From d8ac83d2628f82386639a865610db5a37652ceeb Mon Sep 17 00:00:00 2001 From: MemberPress Date: Thu, 26 Sep 2024 10:16:26 +0000 Subject: [PATCH] Updates to 1.11.35 --- app/controllers/MeprAccountCtrl.php | 1417 +-- app/controllers/MeprAccountLoginCtrl.php | 48 +- .../MeprActiveInactiveHooksCtrl.php | 191 +- app/controllers/MeprAddonsCtrl.php | 535 +- app/controllers/MeprAntiCardTestingCtrl.php | 464 +- app/controllers/MeprApiCtrl.php | 395 +- app/controllers/MeprAppCtrl.php | 2281 ++-- app/controllers/MeprAuthenticatorCtrl.php | 478 +- app/controllers/MeprBlocksCtrl.php | 1198 +- app/controllers/MeprCheckoutCtrl.php | 1829 +-- app/controllers/MeprCouponsCtrl.php | 658 +- app/controllers/MeprCoursesCtrl.php | 321 +- app/controllers/MeprDbCtrl.php | 403 +- .../MeprDeactivationSurveyCtrl.php | 185 +- app/controllers/MeprDrmCtrl.php | 533 +- app/controllers/MeprEmailsCtrl.php | 273 +- app/controllers/MeprEventsCtrl.php | 91 +- app/controllers/MeprExportCtrl.php | 384 +- app/controllers/MeprGroupsCtrl.php | 798 +- app/controllers/MeprGrowthToolsCtrl.php | 38 +- app/controllers/MeprLoginCtrl.php | 759 +- app/controllers/MeprMembersCtrl.php | 675 +- app/controllers/MeprMigratorCtrl.php | 183 +- app/controllers/MeprNotificationsCtrl.php | 44 +- app/controllers/MeprOnboardingCtrl.php | 2206 ++-- app/controllers/MeprOptionsCtrl.php | 1022 +- app/controllers/MeprPayPalConnectCtrl.php | 1412 ++- app/controllers/MeprPayWallCtrl.php | 361 +- app/controllers/MeprPopupCtrl.php | 438 +- app/controllers/MeprPostStatesCtrl.php | 44 +- app/controllers/MeprProductsCtrl.php | 1774 +-- app/controllers/MeprReadyLaunchCtrl.php | 1533 +-- app/controllers/MeprRemindersCtrl.php | 981 +- app/controllers/MeprReportsCtrl.php | 923 +- app/controllers/MeprRulesCtrl.php | 1780 +-- app/controllers/MeprStripeConnectCtrl.php | 970 +- app/controllers/MeprStripeCtrl.php | 1204 +- app/controllers/MeprSubscriptionsCtrl.php | 1068 +- app/controllers/MeprSummaryEmailCtrl.php | 386 +- app/controllers/MeprTaxesCtrl.php | 229 +- app/controllers/MeprTransactionsCtrl.php | 1153 +- app/controllers/MeprUpdateCtrl.php | 1481 +-- app/controllers/MeprUsageCtrl.php | 172 +- app/controllers/MeprUsersCtrl.php | 1340 +- app/controllers/MeprVatTaxCtrl.php | 969 +- app/controllers/MeprZxcvbnCtrl.php | 202 +- app/controllers/index.php | 4 +- app/data/events.php | 300 +- app/data/features/editions.php | 255 +- app/data/minimum_charge_amounts.php | 55 +- app/data/stripe_payment_methods.php | 354 +- app/data/taxes/vat_countries.php | 255 +- app/emails/MeprAdminCancelledSubEmail.php | 38 +- .../MeprAdminCcExpiresReminderEmail.php | 66 +- app/emails/MeprAdminCcExpiringEmail.php | 38 +- app/emails/MeprAdminDowngradedSubEmail.php | 38 +- app/emails/MeprAdminFailedTxnEmail.php | 38 +- .../MeprAdminMemberSignupReminderEmail.php | 66 +- app/emails/MeprAdminNewOneOffEmail.php | 38 +- app/emails/MeprAdminNewSubEmail.php | 38 +- app/emails/MeprAdminReceiptEmail.php | 38 +- app/emails/MeprAdminRefundedTxnEmail.php | 38 +- app/emails/MeprAdminResumedSubEmail.php | 38 +- .../MeprAdminSignupAbandonedReminderEmail.php | 66 +- app/emails/MeprAdminSignupEmail.php | 38 +- .../MeprAdminSubExpiresReminderEmail.php | 66 +- .../MeprAdminSubRenewsReminderEmail.php | 66 +- .../MeprAdminSubTrialEndsReminderEmail.php | 66 +- app/emails/MeprAdminSuspendedSubEmail.php | 38 +- app/emails/MeprAdminUpgradedSubEmail.php | 38 +- app/emails/MeprUserCancelledSubEmail.php | 34 +- app/emails/MeprUserCcExpiresReminderEmail.php | 62 +- app/emails/MeprUserCcExpiringEmail.php | 34 +- app/emails/MeprUserDowngradedSubEmail.php | 34 +- app/emails/MeprUserFailedTxnEmail.php | 34 +- .../MeprUserMemberSignupReminderEmail.php | 60 +- app/emails/MeprUserProductWelcomeEmail.php | 38 +- app/emails/MeprUserReceiptEmail.php | 34 +- app/emails/MeprUserRefundedTxnEmail.php | 34 +- app/emails/MeprUserResumedSubEmail.php | 34 +- .../MeprUserSignupAbandonedReminderEmail.php | 62 +- app/emails/MeprUserStripeInvoiceEmail.php | 34 +- .../MeprUserSubExpiresReminderEmail.php | 62 +- app/emails/MeprUserSubRenewsReminderEmail.php | 62 +- .../MeprUserSubTrialEndsReminderEmail.php | 62 +- app/emails/MeprUserSuspendedSubEmail.php | 34 +- app/emails/MeprUserUpgradedSubEmail.php | 34 +- app/emails/MeprUserWelcomeEmail.php | 34 +- app/emails/index.php | 4 +- app/gateways/MeprArtificialGateway.php | 1002 +- app/gateways/MeprAuthorizeAPI.php | 126 +- app/gateways/MeprAuthorizeGateway.php | 2470 ++-- app/gateways/MeprAuthorizeProfileGateway.php | 1486 +-- app/gateways/MeprAuthorizeWebhooks.php | 444 +- app/gateways/MeprPayPalCommerceGateway.php | 3375 ++--- app/gateways/MeprPayPalGateway.php | 2157 ++-- app/gateways/MeprPayPalProGateway.php | 2134 ++-- app/gateways/MeprPayPalStandardGateway.php | 2655 ++-- app/gateways/MeprStripeGateway.php | 9075 +++++++------- app/gateways/authorizenet/client.php | 833 +- app/gateways/index.php | 4 +- app/gateways/stripe/index.php | 4 +- app/helpers/MeprAccountHelper.php | 76 +- app/helpers/MeprAddonsHelper.php | 50 +- app/helpers/MeprAppHelper.php | 1393 ++- app/helpers/MeprCouponsHelper.php | 346 +- app/helpers/MeprDrmHelper.php | 864 +- app/helpers/MeprGroupsHelper.php | 249 +- app/helpers/MeprMigratorHelper.php | 84 +- app/helpers/MeprOnboardingHelper.php | 932 +- app/helpers/MeprOptionsHelper.php | 794 +- app/helpers/MeprProductsHelper.php | 447 +- app/helpers/MeprRemindersHelper.php | 296 +- app/helpers/MeprReportsHelper.php | 165 +- app/helpers/MeprRulesHelper.php | 435 +- app/helpers/MeprSubscriptionsHelper.php | 339 +- app/helpers/MeprTransactionsHelper.php | 1306 +- app/helpers/MeprUsersHelper.php | 893 +- app/helpers/index.php | 4 +- app/index.php | 4 +- app/integrations/avalara/Integration.php | 113 +- app/integrations/bbpress/Integration.php | 268 +- .../google-captcha/Integration.php | 70 +- app/integrations/ifmenu/Integration.php | 100 +- app/integrations/polylang/Integration.php | 55 +- app/integrations/powerpress/Integration.php | 53 +- app/integrations/quaderno/Integration.php | 185 +- app/integrations/stripe-tax/Integration.php | 1168 +- app/integrations/taxjar/Integration.php | 516 +- app/integrations/two-factor/Integration.php | 424 +- app/integrations/user-roles/Integration.php | 12 +- app/integrations/user-roles/MeprUserRoles.php | 386 +- app/jobs/MeprAuthorizeRetryJob.php | 57 +- app/jobs/MeprEmailJob.php | 91 +- app/jobs/MeprUpdateStripeMetadataJob.php | 58 +- app/jobs/index.php | 4 +- app/lib/MeprAccountLinksWidget.php | 144 +- app/lib/MeprAddonInstallSkin.php | 99 +- app/lib/MeprAddonUpdates.php | 396 +- app/lib/MeprBaseCtrl.php | 30 +- app/lib/MeprBaseDrm.php | 377 +- app/lib/MeprBaseEmail.php | 388 +- app/lib/MeprBaseExclusiveRecurringGateway.php | 31 +- app/lib/MeprBaseGateway.php | 1192 +- app/lib/MeprBaseJob.php | 198 +- app/lib/MeprBaseMetaModel.php | 161 +- app/lib/MeprBaseModel.php | 488 +- app/lib/MeprBaseOptionsAdminEmail.php | 9 +- app/lib/MeprBaseOptionsEmail.php | 45 +- app/lib/MeprBaseOptionsUserEmail.php | 9 +- app/lib/MeprBasePayPalGateway.php | 111 +- app/lib/MeprBaseProductEmail.php | 63 +- app/lib/MeprBaseRealGateway.php | 228 +- app/lib/MeprBaseReminderEmail.php | 85 +- app/lib/MeprBaseStaticGateway.php | 454 +- app/lib/MeprBatchMigrator.php | 294 +- app/lib/MeprCptCtrl.php | 347 +- app/lib/MeprCptModel.php | 430 +- app/lib/MeprCtrlFactory.php | 98 +- app/lib/MeprDb.php | 1672 +-- app/lib/MeprDbMigrations.php | 1103 +- app/lib/MeprDrmAppFee.php | 246 +- app/lib/MeprDrmInvalid.php | 78 +- app/lib/MeprDrmNokey.php | 87 +- app/lib/MeprEmailFactory.php | 104 +- app/lib/MeprExceptions.php | 101 +- app/lib/MeprExpiringOption.php | 83 +- app/lib/MeprGatewayFactory.php | 89 +- app/lib/MeprHooks.php | 109 +- app/lib/MeprJobFactory.php | 44 +- app/lib/MeprJobs.php | 579 +- app/lib/MeprLoginWidget.php | 169 +- app/lib/MeprMembersTable.php | 310 +- app/lib/MeprMigrator.php | 253 +- app/lib/MeprNotifications.php | 1184 +- app/lib/MeprQueueEmail.php | 17 +- app/lib/MeprReports.php | 1334 +- app/lib/MeprSubscriptionsTable.php | 427 +- app/lib/MeprSubscriptionsWidget.php | 174 +- app/lib/MeprTransactionsTable.php | 318 +- app/lib/MeprUsage.php | 955 +- app/lib/MeprUtils.php | 4712 +++---- app/lib/MeprView.php | 135 +- app/lib/activation.php | 69 +- .../class-rollback-memberpress-upgrader.php | 104 +- app/lib/deactivation.php | 10 +- app/lib/index.php | 4 +- app/lib/interfaces/MeprMigratorInterface.php | 15 +- app/lib/interfaces/MeprProductInterface.php | 15 +- .../interfaces/MeprTransactionInterface.php | 25 +- app/lib/migrators/MeprMigratorLearnDash.php | 2574 ++-- app/models/MeprCoupon.php | 884 +- app/models/MeprDrm.php | 29 +- app/models/MeprEvent.php | 500 +- app/models/MeprGroup.php | 637 +- app/models/MeprOptions.php | 2379 ++-- app/models/MeprOrder.php | 223 +- app/models/MeprProduct.php | 1872 +-- app/models/MeprReminder.php | 1062 +- app/models/MeprRule.php | 2504 ++-- app/models/MeprRuleAccessCondition.php | 205 +- app/models/MeprSubscription.php | 3586 +++--- app/models/MeprTaxRate.php | 510 +- app/models/MeprTransaction.php | 2115 ++-- app/models/MeprUser.php | 4587 +++---- app/models/index.php | 4 +- app/views/account/home.php | 30 +- app/views/account/index.php | 4 +- app/views/account/logged_in_template.php | 8 +- app/views/account/logged_in_widget.php | 4 +- app/views/account/logged_out_template.php | 4 +- app/views/account/logged_out_widget.php | 4 +- app/views/account/nav.php | 28 +- app/views/account/password.php | 10 +- app/views/account/payments.php | 41 +- app/views/account/subscriptions.php | 257 +- app/views/account/subscriptions_widget.php | 104 +- app/views/admin/about/1-2-4.php | 12 +- app/views/admin/about/index.php | 4 +- app/views/admin/account-login/index.php | 4 +- app/views/admin/account-login/ui.php | 32 +- app/views/admin/addons/affiliates.php | 12 +- app/views/admin/addons/ui.php | 72 +- app/views/admin/admin-notification.php | 6 +- app/views/admin/auto-updates/option.php | 25 +- app/views/admin/coupons/form.php | 102 +- app/views/admin/coupons/index.php | 4 +- app/views/admin/courses/ui.php | 14 +- app/views/admin/db/upgrade_error.php | 14 +- app/views/admin/db/upgrade_needed.php | 32 +- app/views/admin/db/upgrade_not_needed.php | 12 +- app/views/admin/db/upgrade_success.php | 12 +- app/views/admin/drm/email.php | 6 +- app/views/admin/drm/index.php | 4 +- app/views/admin/drm/modal.php | 48 +- app/views/admin/drm/modal_fee.php | 22 +- app/views/admin/drm/notices/fee_notice.php | 8 +- app/views/admin/drm/notices/index.php | 4 +- .../admin/drm/notices/locked_warning.php | 8 +- app/views/admin/drm/notices/low_warning.php | 22 +- .../admin/drm/notices/medium_warning.php | 22 +- app/views/admin/drm/notices/notice.php | 12 +- app/views/admin/emails/options.php | 33 +- app/views/admin/errors.php | 10 +- .../paypal/connect-migrate-prompt.php | 202 +- .../admin/gateways/stripe/checkboxes.php | 50 +- .../stripe/connect-migrate-prompt.php | 104 +- app/views/admin/gateways/stripe/keys.php | 42 +- app/views/admin/get_started.php | 44 +- .../groups/custom_page_template_form.php | 6 +- app/views/admin/groups/form.php | 72 +- app/views/admin/members/index.php | 4 +- app/views/admin/members/list.php | 14 +- app/views/admin/members/new_member.php | 98 +- app/views/admin/members/row.php | 384 +- app/views/admin/members/search_box.php | 16 +- app/views/admin/must_configure.php | 6 +- app/views/admin/onboarding/active_license.php | 41 +- app/views/admin/onboarding/complete.php | 16 +- .../onboarding/content-search-results.php | 8 +- app/views/admin/onboarding/content.php | 8 +- app/views/admin/onboarding/features.php | 44 +- app/views/admin/onboarding/finish.php | 6 +- app/views/admin/onboarding/index.php | 4 +- app/views/admin/onboarding/license.php | 18 +- app/views/admin/onboarding/membership.php | 18 +- app/views/admin/onboarding/nav/complete.php | 12 +- app/views/admin/onboarding/nav/content.php | 12 +- app/views/admin/onboarding/nav/features.php | 4 +- app/views/admin/onboarding/nav/finish.php | 4 +- app/views/admin/onboarding/nav/index.php | 4 +- app/views/admin/onboarding/nav/license.php | 4 +- app/views/admin/onboarding/nav/membership.php | 12 +- app/views/admin/onboarding/nav/payments.php | 14 +- app/views/admin/onboarding/nav/rules.php | 12 +- .../admin/onboarding/parts/content_popup.php | 8 +- app/views/admin/onboarding/parts/finish.php | 314 +- app/views/admin/onboarding/payments.php | 42 +- app/views/admin/onboarding/rules.php | 20 +- app/views/admin/onboarding/video.php | 4 +- app/views/admin/onboarding/welcome.php | 58 +- app/views/admin/onboarding/wizard.php | 83 +- app/views/admin/options/account_login.php | 4 +- app/views/admin/options/active_license.php | 44 +- app/views/admin/options/courses_migrator.php | 10 +- app/views/admin/options/custom_fields_row.php | 48 +- app/views/admin/options/edge_updates.php | 4 +- app/views/admin/options/form.php | 328 +- app/views/admin/options/gateway.php | 23 +- app/views/admin/options/inactive_license.php | 12 +- app/views/admin/options/index.php | 4 +- app/views/admin/options/options_saved.php | 4 +- .../admin/popups/deactivation_survey.php | 2 +- app/views/admin/popups/senddata.php | 18 - app/views/admin/privacy/privacy_policy.php | 6 +- app/views/admin/products/advanced.php | 70 +- .../products/custom_page_template_form.php | 6 +- app/views/admin/products/form.php | 69 +- app/views/admin/products/index.php | 4 +- app/views/admin/products/order_bumps.php | 20 +- app/views/admin/products/permissions.php | 16 +- app/views/admin/products/price_box.php | 41 +- .../products/product_options_meta_box.php | 10 +- app/views/admin/products/registration.php | 193 +- app/views/admin/products/stripe_tax.php | 28 +- app/views/admin/readylaunch/account.php | 2 +- app/views/admin/readylaunch/checkout.php | 4 +- app/views/admin/readylaunch/coaching.php | 2 +- app/views/admin/readylaunch/login.php | 2 +- app/views/admin/readylaunch/options.php | 4 +- app/views/admin/readylaunch/pricing.php | 42 +- app/views/admin/readylaunch/thankyou.php | 22 +- app/views/admin/reminders/emails.php | 6 +- app/views/admin/reminders/index.php | 4 +- app/views/admin/reminders/trigger.php | 42 +- .../admin/reports/all_time_info_blocks.php | 4 +- app/views/admin/reports/index.php | 4 +- app/views/admin/reports/main.php | 38 +- app/views/admin/reports/month_info_blocks.php | 4 +- app/views/admin/reports/month_table.php | 95 +- .../admin/reports/overall_info_blocks.php | 13 +- .../admin/reports/skeleton_info_blocks.php | 6 +- app/views/admin/reports/skeleton_pie.php | 6 +- app/views/admin/reports/skeleton_table.php | 6 +- app/views/admin/reports/summary_email.php | 30 +- app/views/admin/reports/svg_loader.php | 6 +- app/views/admin/reports/year_info_blocks.php | 4 +- app/views/admin/reports/year_table.php | 99 +- app/views/admin/rules/access_row.php | 34 +- app/views/admin/rules/drip_form.php | 4 +- app/views/admin/rules/form.php | 44 +- app/views/admin/rules/index.php | 4 +- app/views/admin/rules/rules_meta_box.php | 26 +- app/views/admin/rules/unauth_meta_box.php | 22 +- .../admin/stripe_checkout_deprecated.php | 12 +- app/views/admin/subscriptions/edit.php | 8 +- app/views/admin/subscriptions/form.php | 27 +- app/views/admin/subscriptions/index.php | 4 +- app/views/admin/subscriptions/list.php | 6 +- app/views/admin/subscriptions/new.php | 8 +- app/views/admin/subscriptions/row.php | 414 +- app/views/admin/subscriptions/search.php | 6 +- app/views/admin/subscriptions/search_box.php | 20 +- app/views/admin/subscriptions/tabs.php | 8 +- app/views/admin/support/index.php | 4 +- app/views/admin/support/view.php | 4 +- app/views/admin/table_controls.php | 24 +- app/views/admin/taxes/avalara_options.php | 26 +- app/views/admin/taxes/options.php | 106 +- app/views/admin/taxes/stripe_tax_options.php | 37 +- app/views/admin/taxes/taxjar_options.php | 40 +- app/views/admin/taxes/vat_options.php | 56 +- app/views/admin/taxes/vat_profile_fields.php | 57 +- app/views/admin/transactions/edit_trans.php | 22 +- app/views/admin/transactions/index.php | 4 +- app/views/admin/transactions/list.php | 16 +- app/views/admin/transactions/new_trans.php | 8 +- app/views/admin/transactions/row.php | 304 +- app/views/admin/transactions/search_box.php | 20 +- app/views/admin/transactions/trans_form.php | 35 +- app/views/admin/unauthorized.php | 4 +- app/views/admin/unauthorized_meta_box.php | 30 +- app/views/admin/update/activation_warning.php | 16 +- app/views/admin/update/index.php | 4 +- app/views/admin/usage/option.php | 72 +- .../admin/users/extra_profile_fields.php | 49 +- app/views/admin/users/index.php | 4 +- app/views/admin/users/login_page_meta_box.php | 16 +- app/views/admin/users/search.php | 6 +- .../admin/widgets/admin_stats_widget.php | 20 +- app/views/admin/widgets/index.php | 4 +- .../payment_gateway_fields.php | 4 +- .../payment_gateway_fields.php | 4 +- .../payment_form.php | 14 +- .../MeprStripeGateway/payment_form.php | 22 +- .../payment_gateway_fields.php | 38 +- app/views/checkout/form.php | 100 +- app/views/checkout/invoice.php | 46 +- app/views/checkout/invoice_order_bumps.php | 42 +- app/views/checkout/payment_form.php | 4 +- app/views/checkout/signup_row.php | 10 +- app/views/checkout/spc_form.php | 116 +- app/views/emails/admin_cancelled_sub.php | 4 +- .../emails/admin_cc_expires_reminder.php | 4 +- app/views/emails/admin_cc_expiring.php | 4 +- app/views/emails/admin_downgraded_sub.php | 4 +- app/views/emails/admin_failed_txn.php | 4 +- .../emails/admin_member_signup_reminder.php | 4 +- app/views/emails/admin_new_one_off.php | 4 +- app/views/emails/admin_new_sub.php | 4 +- app/views/emails/admin_password_reset.php | 4 +- app/views/emails/admin_receipt.php | 4 +- app/views/emails/admin_refunded_txn.php | 4 +- app/views/emails/admin_resumed_sub.php | 4 +- app/views/emails/admin_signup.php | 4 +- .../admin_signup_abandoned_reminder.php | 4 +- .../emails/admin_sub_expires_reminder.php | 4 +- .../emails/admin_sub_renews_reminder.php | 4 +- .../emails/admin_sub_trial_ends_reminder.php | 6 +- app/views/emails/admin_suspended_sub.php | 4 +- app/views/emails/admin_upgraded_sub.php | 4 +- app/views/emails/index.php | 4 +- app/views/emails/template.php | 4 +- app/views/emails/user_cancelled_sub.php | 4 +- app/views/emails/user_cc_expires_reminder.php | 4 +- app/views/emails/user_cc_expiring.php | 6 +- app/views/emails/user_confirm_payment.php | 18 +- app/views/emails/user_downgraded_sub.php | 4 +- app/views/emails/user_failed_txn.php | 4 +- .../emails/user_member_signup_reminder.php | 4 +- app/views/emails/user_password_was_reset.php | 14 +- app/views/emails/user_product_welcome.php | 4 +- app/views/emails/user_receipt.php | 4 +- app/views/emails/user_refunded_txn.php | 4 +- app/views/emails/user_reset_password.php | 8 +- app/views/emails/user_resumed_sub.php | 4 +- app/views/emails/user_set_password.php | 8 +- .../emails/user_signup_abandoned_reminder.php | 4 +- app/views/emails/user_stripe_invoice.php | 4 +- .../emails/user_sub_expires_reminder.php | 4 +- app/views/emails/user_sub_renews_reminder.php | 4 +- .../emails/user_sub_trial_ends_reminder.php | 6 +- app/views/emails/user_suspended_sub.php | 4 +- app/views/emails/user_upgraded_sub.php | 4 +- app/views/emails/user_welcome.php | 4 +- app/views/groups/front_groups_page.php | 14 +- app/views/groups/index.php | 4 +- app/views/index.php | 4 +- app/views/login/already_logged_in.php | 12 +- app/views/login/forgot_password.php | 6 +- app/views/login/forgot_password_requested.php | 18 +- app/views/login/form.php | 43 +- app/views/login/reset_password.php | 8 +- app/views/readylaunch/account/home.php | 102 +- app/views/readylaunch/account/nav.php | 18 +- app/views/readylaunch/account/password.php | 10 +- app/views/readylaunch/account/payments.php | 104 +- .../readylaunch/account/subscriptions.php | 243 +- app/views/readylaunch/checkout/form.php | 243 +- app/views/readylaunch/checkout/invoice.php | 75 +- .../checkout/invoice_order_bumps.php | 75 +- .../readylaunch/groups/front_groups_page.php | 136 +- app/views/readylaunch/layout/app.php | 41 +- app/views/readylaunch/layout/guest.php | 23 +- .../readylaunch/login/forgot_password.php | 2 +- .../login/forgot_password_requested.php | 20 +- app/views/readylaunch/login/form.php | 57 +- .../readylaunch/login/reset_password.php | 8 +- app/views/readylaunch/shared/errors.php | 2 +- .../shared/expired_password_reset.php | 6 +- app/views/readylaunch/shared/unauthorized.php | 2 +- .../shared/unauthorized_message.php | 26 +- app/views/readylaunch/thankyou.php | 73 +- app/views/shared/errors.php | 10 +- app/views/shared/expired_password_reset.php | 6 +- app/views/shared/has_errors.php | 4 +- app/views/shared/index.php | 4 +- app/views/shared/unauthorized.php | 6 +- app/views/shared/unauthorized_comments.php | 6 +- app/views/shared/unauthorized_message.php | 16 +- .../unauthorized_message_modern_paywall.php | 16 +- app/views/shared/unknown_error.php | 4 +- .../shortcodes/list_users_subscriptions.php | 53 +- app/views/shortcodes/user_files.php | 4 +- app/views/taxes/vat_signup.php | 8 +- css/index.php | 4 +- css/plan_templates/index.php | 4 +- css/plans/index.php | 4 +- i18n/countries.php | 499 +- i18n/index.php | 4 +- i18n/memberpress.pot | 10298 ++++++++-------- i18n/namespace.php | 311 +- i18n/states/AT.php | 24 +- i18n/states/AU.php | 21 +- i18n/states/BD.php | 133 +- i18n/states/BG.php | 61 +- i18n/states/BR.php | 59 +- i18n/states/CA.php | 31 +- i18n/states/CH.php | 58 +- i18n/states/CN.php | 69 +- i18n/states/DE.php | 38 +- i18n/states/ES.php | 109 +- i18n/states/HK.php | 11 +- i18n/states/HU.php | 45 +- i18n/states/ID.php | 73 +- i18n/states/IN.php | 77 +- i18n/states/IR.php | 67 +- i18n/states/IT.php | 225 +- i18n/states/JP.php | 99 +- i18n/states/MX.php | 69 +- i18n/states/MY.php | 37 +- i18n/states/NP.php | 211 +- i18n/states/NZ.php | 35 +- i18n/states/PE.php | 57 +- i18n/states/PT.php | 19 +- i18n/states/TH.php | 159 +- i18n/states/TR.php | 167 +- i18n/states/US.php | 125 +- i18n/states/ZA.php | 23 +- i18n/states/index.php | 4 +- images/index.php | 4 +- images/onboarding/index.php | 4 +- index.php | 4 +- js/admin_usage.js | 20 - js/build/blocks.asset.php | 7 +- js/index.php | 4 +- js/mphelpers.js | 15 +- js/readylaunch/account.js | 49 +- lock.php | 163 +- memberpress.php | 240 +- 510 files changed, 74504 insertions(+), 67829 deletions(-) delete mode 100644 app/views/admin/popups/senddata.php delete mode 100644 js/admin_usage.js diff --git a/app/controllers/MeprAccountCtrl.php b/app/controllers/MeprAccountCtrl.php index 29bf8cb..fd5094f 100644 --- a/app/controllers/MeprAccountCtrl.php +++ b/app/controllers/MeprAccountCtrl.php @@ -1,798 +1,837 @@ get_enabled_product_ids($prd_id); - - if(!empty($enabled_prd_ids)) { //If it's not empty, then the user already has an Enabled subscription for this membership - $prd = new MeprProduct($prd_id); - if(!$prd->simultaneous_subscriptions && apply_filters( 'maybe_show_broken_sub_message_override', true, $prd )) { - $errors[] = sprintf(_x('You already have a subscription to this Membership. Please %1$supdate your payment details%2$s on the existing subscription instead of purchasing again.', 'ui', 'memberpress'), '', ''); - MeprView::render('/shared/errors', get_defined_vars()); - ?> + +if (!defined('ABSPATH')) { + die('You are not allowed to call this page directly.'); +} + +class MeprAccountCtrl extends MeprBaseCtrl +{ + public function load_hooks() + { + add_action('wp_enqueue_scripts', [$this, 'enqueue_scripts']); + add_action('init', [$this, 'maybe_update_username']); // Need to use init for cookie stuff and to get old and new emails + add_action('mepr-above-checkout-form', [$this, 'maybe_show_broken_sub_message']); // Show message on checkout form with link to update broken sub + add_action('wp_ajax_save_profile_changes', [$this, 'save_profile_fields']); + + // These are dependent on the the theme/template supporting them. + // To Turn On: + /* + add_filter('mepr-account-nav-page-titles', 'mepr_cust_on_switch'); + add_filter('mepr-account-nav-broswer-titles', 'mepr_cust_on_switch'); + function mepr_cust_on_switch($on) { + return true; + } + */ + add_filter('the_title', [$this, 'account_page_the_title'], 10, 2); + add_filter('wp_title', [$this, 'account_page_browser_title']); + + // Shortcodes + MeprHooks::add_shortcode('mepr-account-form', [$this, 'account_form_shortcode']); + MeprHooks::add_shortcode('mepr-account-link', [$this, 'get_account_links']); + MeprHooks::add_shortcode('mepr-account-info', [$this, 'output_account_meta']); + MeprHooks::add_shortcode('mepr-offline-instructions', [$this, 'offline_gateway_instructions']); // Offline gateway instructions shortcode + } + + // What if a user has a failed payment on a subscription, and instead of updating their CC + // They accidentally go to purchase a new subscription instead? This could lead to a double billing + // So let's warn them here + public function maybe_show_broken_sub_message($prd_id) + { + global $pagenow; + + if ($pagenow == 'post.php') { + return; + } + + $mepr_options = MeprOptions::fetch(); + $user = MeprUtils::get_currentuserinfo(); + $errors = []; + + if ($user !== false) { + $enabled_prd_ids = $user->get_enabled_product_ids($prd_id); + + if (!empty($enabled_prd_ids)) { // If it's not empty, then the user already has an Enabled subscription for this membership + $prd = new MeprProduct($prd_id); + if (!$prd->simultaneous_subscriptions && apply_filters('maybe_show_broken_sub_message_override', true, $prd)) { + $errors[] = sprintf(_x('You already have a subscription to this Membership. Please %1$supdate your payment details%2$s on the existing subscription instead of purchasing again.', 'ui', 'memberpress'), '', ''); + MeprView::render('/shared/errors', get_defined_vars()); + ?> - user_email; - $new_email = sanitize_email($_POST['user_email']); - - //Make sure no one else has this email as their username - if(is_email($new_email) && username_exists($new_email)) { return; } //BAIL - - if( $mepr_user !== false && - $mepr_options->username_is_email && - is_email($new_email) && //make sure this isn't sql injected or something - is_email($mepr_user->user_login) && //make sure we're not overriding a non-email username - $old_email == $mepr_user->user_login && //Make sure old email and old username match up - $old_email != $new_email ) { - //Some trickery here to keep the user logged in - $wpdb->query($wpdb->prepare("UPDATE {$wpdb->users} SET user_login = %s WHERE ID = %d", $new_email, $mepr_user->ID)); - clean_user_cache($mepr_user->ID); //Get rid of the user cache - wp_clear_auth_cookie(); //Clear their old cookie - wp_set_current_user($mepr_user->ID); //Set the current user again - wp_set_auth_cookie($mepr_user->ID, true, false); //Log the user back in w/out knowing their password - update_user_caches(new WP_User($mepr_user->ID)); - } } - } - - public function enqueue_scripts($force = false) { - global $post; - $mepr_options = MeprOptions::fetch(); - if($force || MeprUser::is_account_page($post)) { - $popup_ctrl = new MeprPopupCtrl(); - - $has_phone = false; - - if ( ! empty( $mepr_options->custom_fields ) ) { - foreach ( $mepr_options->custom_fields as $field ) { - if ( 'tel' === $field->field_type && $field->show_in_account ) { - $has_phone = true; - break; - } - } - } - - wp_register_style('jquery-magnific-popup', $popup_ctrl->popup_css); - wp_enqueue_style('mp-account', MEPR_CSS_URL.'/account.css',array('jquery-magnific-popup'), MEPR_VERSION); - - wp_register_script('jquery-magnific-popup', $popup_ctrl->popup_js, array('jquery')); - wp_enqueue_script('mp-account', MEPR_JS_URL.'/account.js', array('jquery','jquery-magnific-popup'), MEPR_VERSION); - - $pms = $mepr_options->payment_methods(); - - if($pms) { - wp_register_script('mepr-checkout-js', MEPR_JS_URL . '/checkout.js', array('jquery', 'jquery.payment'), MEPR_VERSION); - wp_register_script('mepr-default-gateway-checkout-js', MEPR_JS_URL . '/gateway/checkout.js', array('mepr-checkout-js'), MEPR_VERSION); - foreach($pms as $pm) { - if($pm instanceof MeprBaseRealGateway) { - $pm->enqueue_user_account_scripts(); - } - } - } - - // Check if there's a phone field - if ( $has_phone ) { - wp_enqueue_style( 'mepr-phone-css', MEPR_CSS_URL . '/intlTelInput.min.css', '', '16.0.0' ); - wp_enqueue_style( 'mepr-tel-config-css', MEPR_CSS_URL . '/tel_input.css', '', MEPR_VERSION ); - wp_enqueue_script( 'mepr-phone-js', MEPR_JS_URL . '/intlTelInput.js', '', '16.0.0', true ); - wp_enqueue_script( 'mepr-tel-config-js', MEPR_JS_URL . '/tel_input.js', array( 'mepr-phone-js', 'mp-account' ), MEPR_VERSION, true ); - wp_localize_script( 'mepr-tel-config-js', 'meprTel', MeprHooks::apply_filters( 'mepr-phone-input-config', array( - 'defaultCountry' => strtolower( get_option( 'mepr_biz_country' ) ), - 'utilsUrl' => MEPR_JS_URL . '/intlTelInputUtils.js', - 'onlyCountries' => '' - ) ) ); - } - } - } - - public function render($atts = array()) { - global $post; - - $mepr_current_user = MeprUtils::get_currentuserinfo(); - $expired_subs = $mepr_current_user->subscription_expirations('expired',true); - $mepr_options = MeprOptions::fetch(); - - // When this option is empty, the "Plain" permalink structure is in use. - - $account_url = MeprUtils::get_account_url(); - - $delim = MeprAppCtrl::get_param_delimiter_char($account_url); - - if(MeprReadyLaunchCtrl::template_enabled( 'account' ) || MeprAppHelper::has_block('memberpress/pro-account-tabs' )){ - MeprView::render('/readylaunch/account/nav', get_defined_vars()); - } else { - MeprView::render('/account/nav', get_defined_vars()); + // Update username if username is email address and email address is changing + public function maybe_update_username() + { + if ( + MeprUtils::is_post_request() && + isset($_POST['mepr-process-account']) && $_POST['mepr-process-account'] == 'Y' && + isset($_POST['mepr_account_nonce']) && wp_verify_nonce($_POST['mepr_account_nonce'], 'update_account') + ) { + global $wpdb; + $mepr_options = MeprOptions::fetch(); + + $mepr_user = MeprUtils::get_currentuserinfo(); + $old_email = $mepr_user->user_email; + $new_email = sanitize_email($_POST['user_email']); + + // Make sure no one else has this email as their username + if (is_email($new_email) && username_exists($new_email)) { + return; + } //BAIL + + if ( + $mepr_user !== false && + $mepr_options->username_is_email && + is_email($new_email) && // make sure this isn't sql injected or something + is_email($mepr_user->user_login) && // make sure we're not overriding a non-email username + $old_email == $mepr_user->user_login && // Make sure old email and old username match up + $old_email != $new_email + ) { + // Some trickery here to keep the user logged in + $wpdb->query($wpdb->prepare("UPDATE {$wpdb->users} SET user_login = %s WHERE ID = %d", $new_email, $mepr_user->ID)); + clean_user_cache($mepr_user->ID); // Get rid of the user cache + wp_clear_auth_cookie(); // Clear their old cookie + wp_set_current_user($mepr_user->ID); // Set the current user again + wp_set_auth_cookie($mepr_user->ID, true, false); // Log the user back in w/out knowing their password + update_user_caches(new WP_User($mepr_user->ID)); + } + } } - $action = MeprHooks::apply_filters('mepr-account-action', (isset($_REQUEST['action']))?$_REQUEST['action']:false); - - - switch($action) { - case 'payments': - $this->payments(); - break; - case 'subscriptions': - $this->subscriptions(); - break; - case 'newpassword': - $this->password(); - break; - case 'cancel': - $this->cancel(); - break; - case 'suspend': - $this->suspend(); - break; - case 'resume': - $this->resume(); - break; - case 'update': - $this->update(); - break; - case 'upgrade': - $this->upgrade(); - break; - default: - // Allows you to override the content for a nav tab - ob_start(); - MeprHooks::do_action( 'mepr_account_nav_content', $action, $atts ); - $custom_content = ob_get_clean(); - - if(empty($custom_content)) { - $this->home($atts); - } - else { - echo '
' . $custom_content . '
'; + public function enqueue_scripts($force = false) + { + global $post; + $mepr_options = MeprOptions::fetch(); + + if ($force || MeprUser::is_account_page($post)) { + $popup_ctrl = new MeprPopupCtrl(); + + $has_phone = false; + + if (! empty($mepr_options->custom_fields)) { + foreach ($mepr_options->custom_fields as $field) { + if ('tel' === $field->field_type && $field->show_in_account) { + $has_phone = true; + break; + } + } + } + + wp_register_style('jquery-magnific-popup', $popup_ctrl->popup_css); + wp_enqueue_style('mp-account', MEPR_CSS_URL . '/account.css', ['jquery-magnific-popup'], MEPR_VERSION); + + wp_register_script('jquery-magnific-popup', $popup_ctrl->popup_js, ['jquery']); + wp_enqueue_script('mp-account', MEPR_JS_URL . '/account.js', ['jquery','jquery-magnific-popup'], MEPR_VERSION); + + $pms = $mepr_options->payment_methods(); + + if ($pms) { + wp_register_script('mepr-checkout-js', MEPR_JS_URL . '/checkout.js', ['jquery', 'jquery.payment'], MEPR_VERSION); + wp_register_script('mepr-default-gateway-checkout-js', MEPR_JS_URL . '/gateway/checkout.js', ['mepr-checkout-js'], MEPR_VERSION); + foreach ($pms as $pm) { + if ($pm instanceof MeprBaseRealGateway) { + $pm->enqueue_user_account_scripts(); + } + } + } + + // Check if there's a phone field + if ($has_phone) { + wp_enqueue_style('mepr-phone-css', MEPR_CSS_URL . '/intlTelInput.min.css', '', '16.0.0'); + wp_enqueue_style('mepr-tel-config-css', MEPR_CSS_URL . '/tel_input.css', '', MEPR_VERSION); + wp_enqueue_script('mepr-phone-js', MEPR_JS_URL . '/intlTelInput.js', '', '16.0.0', true); + wp_enqueue_script('mepr-tel-config-js', MEPR_JS_URL . '/tel_input.js', ['mepr-phone-js', 'mp-account'], MEPR_VERSION, true); + wp_localize_script('mepr-tel-config-js', 'meprTel', MeprHooks::apply_filters('mepr-phone-input-config', [ + 'defaultCountry' => strtolower(get_option('mepr_biz_country')), + 'utilsUrl' => MEPR_JS_URL . '/intlTelInputUtils.js', + 'onlyCountries' => '', + ])); + } } } - MeprHooks::do_action( 'mepr_after_account_render'); - } + public function render($atts = []) + { + global $post; - public function home($atts = array()) { - $mepr_current_user = MeprUtils::get_currentuserinfo(); - $mepr_options = MeprOptions::fetch(); - $account_url = $mepr_options->account_page_url(); - $delim = MeprAppCtrl::get_param_delimiter_char($account_url); - $errors = array(); - $saved = false; - $welcome_message = do_shortcode(wp_kses_post(wpautop(stripslashes($mepr_options->custom_message)))); + $mepr_current_user = MeprUtils::get_currentuserinfo(); + $expired_subs = $mepr_current_user->subscription_expirations('expired', true); + $mepr_options = MeprOptions::fetch(); - if( MeprUtils::is_post_request() && - isset($_POST['mepr-process-account']) && $_POST['mepr-process-account'] == 'Y' ) { - check_admin_referer( 'update_account', 'mepr_account_nonce' ); - $errors = MeprUsersCtrl::validate_extra_profile_fields(null, null, $mepr_current_user); - $errors = MeprUser::validate_account($_POST, $errors); - $errors = MeprHooks::apply_filters('mepr-validate-account', $errors, $mepr_current_user); + // When this option is empty, the "Plain" permalink structure is in use. + $account_url = MeprUtils::get_account_url(); - if(empty($errors)) { - //Need to find a better way to do this eventually but for now update the user's email - $new_email = sanitize_email($_POST['user_email']); + $delim = MeprAppCtrl::get_param_delimiter_char($account_url); - if($mepr_current_user->user_email != $new_email) { - $mepr_current_user->user_email = $new_email; - $mepr_current_user->store(); - MeprHooks::do_action('mepr-update-new-user-email', $mepr_current_user); + if (MeprReadyLaunchCtrl::template_enabled('account') || MeprAppHelper::has_block('memberpress/pro-account-tabs')) { + MeprView::render('/readylaunch/account/nav', get_defined_vars()); + } else { + MeprView::render('/account/nav', get_defined_vars()); } - //Save the usermeta - if(($saved = MeprUsersCtrl::save_extra_profile_fields($mepr_current_user->ID, true))) { - $message = __('Your account has been saved.', 'memberpress'); + $action = MeprHooks::apply_filters('mepr-account-action', (isset($_REQUEST['action'])) ? $_REQUEST['action'] : false); + + + switch ($action) { + case 'payments': + $this->payments(); + break; + case 'subscriptions': + $this->subscriptions(); + break; + case 'newpassword': + $this->password(); + break; + case 'cancel': + $this->cancel(); + break; + case 'suspend': + $this->suspend(); + break; + case 'resume': + $this->resume(); + break; + case 'update': + $this->update(); + break; + case 'upgrade': + $this->upgrade(); + break; + default: + // Allows you to override the content for a nav tab + ob_start(); + MeprHooks::do_action('mepr_account_nav_content', $action, $atts); + $custom_content = ob_get_clean(); + + if (empty($custom_content)) { + $this->home($atts); + } else { + echo '
' . $custom_content . '
'; + } } - // Reload the user now that we've updated it's info - $mepr_current_user = new MeprUser($mepr_current_user->ID); + MeprHooks::do_action('mepr_after_account_render'); + } + + public function home($atts = []) + { + $mepr_current_user = MeprUtils::get_currentuserinfo(); + $mepr_options = MeprOptions::fetch(); + $account_url = $mepr_options->account_page_url(); + $delim = MeprAppCtrl::get_param_delimiter_char($account_url); + $errors = []; + $saved = false; + $welcome_message = do_shortcode(wp_kses_post(wpautop(stripslashes($mepr_options->custom_message)))); + + if ( + MeprUtils::is_post_request() && + isset($_POST['mepr-process-account']) && $_POST['mepr-process-account'] == 'Y' + ) { + check_admin_referer('update_account', 'mepr_account_nonce'); + $errors = MeprUsersCtrl::validate_extra_profile_fields(null, null, $mepr_current_user); + $errors = MeprUser::validate_account($_POST, $errors); + $errors = MeprHooks::apply_filters('mepr-validate-account', $errors, $mepr_current_user); + + if (empty($errors)) { + // Need to find a better way to do this eventually but for now update the user's email + $new_email = sanitize_email($_POST['user_email']); + + if ($mepr_current_user->user_email != $new_email) { + $mepr_current_user->user_email = $new_email; + $mepr_current_user->store(); + MeprHooks::do_action('mepr-update-new-user-email', $mepr_current_user); + } + + // Save the usermeta + if (($saved = MeprUsersCtrl::save_extra_profile_fields($mepr_current_user->ID, true))) { + $message = __('Your account has been saved.', 'memberpress'); + } + + // Reload the user now that we've updated it's info + $mepr_current_user = new MeprUser($mepr_current_user->ID); + + MeprHooks::do_action('mepr-save-account', $mepr_current_user); + // Do not call mepr-account-updated here - it's already called in save_extra_profile_fields() above + // MeprEvent::record('member-account-updated', $mepr_current_user); + } + } elseif (isset($_REQUEST['message']) && $_REQUEST['message'] == 'password_updated') { + $message = __('Your password was successfully updated.', 'memberpress'); + } - MeprHooks::do_action('mepr-save-account', $mepr_current_user); - // Do not call mepr-account-updated here - it's already called in save_extra_profile_fields() above - //MeprEvent::record('member-account-updated', $mepr_current_user); - } - } - elseif(isset($_REQUEST['message']) && $_REQUEST['message']=='password_updated') { - $message = __('Your password was successfully updated.', 'memberpress'); - } + // Load user last in case we saved above, we want the saved info to show up! + $mepr_current_user = new MeprUser($mepr_current_user->ID); - //Load user last in case we saved above, we want the saved info to show up - $mepr_current_user = new MeprUser($mepr_current_user->ID); - - if ( ( MeprReadyLaunchCtrl::template_enabled( 'account' ) || MeprAppHelper::has_block('memberpress/pro-account-tabs' ) )) { - if( is_array($atts) ){ - extract( $atts, EXTR_SKIP ); - } - $mepr_options = MeprOptions::fetch(); - $account_url = $mepr_options->account_page_url(); - $delim = MeprAppCtrl::get_param_delimiter_char( $account_url ); - - $mepr_current_user = MeprUtils::get_currentuserinfo(); - - $address_fields = MeprUsersHelper::get_address_fields( $mepr_current_user ); - $address_values = array(); - foreach ( $address_fields as $address_field ) { - $value = $mepr_current_user ? get_user_meta( $mepr_current_user->ID, $address_field->field_key, true ) : ''; - $address_values[] = $value; - } - - static $unique_suffix = 0; - $unique_suffix++; - - $first_name_value = ''; - if ( isset( $user_first_name ) ) { - $first_name_value = esc_attr( stripslashes( $user_first_name ) ); - } elseif ( MeprUtils::is_user_logged_in() ) { - $first_name_value = (string) $mepr_current_user->first_name; - } - - $last_name_value = ''; - if ( isset( $user_last_name ) ) { - $last_name_value = esc_attr( stripslashes( $user_last_name ) ); - } elseif ( MeprUtils::is_user_logged_in() ) { - $last_name_value = (string) $mepr_current_user->last_name; - } - - $custom_fields = MeprUsersHelper::get_custom_fields(); - ob_start(); - foreach ( $custom_fields as $custom_field ) { - if ( isset( $custom_field->show_in_account ) && ! $custom_field->show_in_account ) { - continue; } - MeprUsersHelper::render_pro_templates_custom_field_values( $custom_field, $mepr_current_user ); - } - $custom_fields_values = ob_get_clean(); - - $has_welcome_image = $welcome_image = null; - - // Has welcome image? Priority given to shortcode. - if ( isset( $atts['show_welcome_image'] ) ) { - $show_welcome_image = filter_var( $atts['show_welcome_image'], FILTER_VALIDATE_BOOLEAN ); - } elseif ( isset( $mepr_options->design_account_welcome_img ) ) { - $show_welcome_image = $mepr_options->design_account_welcome_img; - } - - // Get welcome image? Priority given to shortcode. - if ( isset( $atts['welcome_image'] ) && $atts['welcome_image'] > 0 ) { - $welcome_image = wp_get_attachment_url( $atts['welcome_image'] ); - } elseif ( isset( $mepr_options->design_account_welcome_img ) ) { - $welcome_image = wp_get_attachment_url( $mepr_options->design_account_welcome_img ); - } - MeprView::render('/readylaunch/account/home', get_defined_vars()); - } else { - MeprView::render('/account/home', get_defined_vars()); - } - } - - public function password() { - $mepr_current_user = MeprUtils::get_currentuserinfo(); - $mepr_options = MeprOptions::fetch(); - $account_url = $mepr_options->account_page_url(); - $delim = MeprAppCtrl::get_param_delimiter_char($account_url); - - if(isset($_REQUEST['error'])) { - if($_REQUEST['error'] == 'weak') { - $errors = array(__('Password update failed, please check that your password meets the minimum strength requirement.', 'memberpress')); - } - else { - $errors = array(__('Password update failed, please be sure your passwords match and try again.', 'memberpress')); - } + if (( MeprReadyLaunchCtrl::template_enabled('account') || MeprAppHelper::has_block('memberpress/pro-account-tabs') )) { + if (is_array($atts)) { + extract($atts, EXTR_SKIP); + } + $mepr_options = MeprOptions::fetch(); + $account_url = $mepr_options->account_page_url(); + $delim = MeprAppCtrl::get_param_delimiter_char($account_url); + + $mepr_current_user = MeprUtils::get_currentuserinfo(); + + $address_fields = MeprUsersHelper::get_address_fields($mepr_current_user); + $address_values = []; + foreach ($address_fields as $address_field) { + $value = $mepr_current_user ? get_user_meta($mepr_current_user->ID, $address_field->field_key, true) : ''; + $address_values[] = $value; + } + + static $unique_suffix = 0; + $unique_suffix++; + + $first_name_value = ''; + if (isset($user_first_name)) { + $first_name_value = esc_attr(stripslashes($user_first_name)); + } elseif (MeprUtils::is_user_logged_in()) { + $first_name_value = (string) $mepr_current_user->first_name; + } + + $last_name_value = ''; + if (isset($user_last_name)) { + $last_name_value = esc_attr(stripslashes($user_last_name)); + } elseif (MeprUtils::is_user_logged_in()) { + $last_name_value = (string) $mepr_current_user->last_name; + } + + $custom_fields = MeprUsersHelper::get_custom_fields(); + ob_start(); + foreach ($custom_fields as $custom_field) { + if (isset($custom_field->show_in_account) && ! $custom_field->show_in_account) { + continue; + } + MeprUsersHelper::render_pro_templates_custom_field_values($custom_field, $mepr_current_user); + } + $custom_fields_values = ob_get_clean(); + + $has_welcome_image = $welcome_image = null; + + // Has welcome image? Priority given to shortcode. + if (isset($atts['show_welcome_image'])) { + $show_welcome_image = filter_var($atts['show_welcome_image'], FILTER_VALIDATE_BOOLEAN); + } elseif (isset($mepr_options->design_account_welcome_img)) { + $show_welcome_image = $mepr_options->design_account_welcome_img; + } + + // Get welcome image? Priority given to shortcode. + if (isset($atts['welcome_image']) && $atts['welcome_image'] > 0) { + $welcome_image = wp_get_attachment_url($atts['welcome_image']); + } elseif (isset($mepr_options->design_account_welcome_img)) { + $welcome_image = wp_get_attachment_url($mepr_options->design_account_welcome_img); + } + MeprView::render('/readylaunch/account/home', get_defined_vars()); + } else { + MeprView::render('/account/home', get_defined_vars()); + } } - if(MeprReadyLaunchCtrl::template_enabled( 'account' )){ - MeprView::render('/readylaunch/account/password', get_defined_vars()); - } else { - MeprView::render('/account/password', get_defined_vars()); - } - } - - public function payments($args = array()) { - global $wpdb; - $mepr_current_user = MeprUtils::get_currentuserinfo(); - $mepr_options = MeprOptions::fetch(); - $account_url = $_SERVER['REQUEST_URI']; //Use URI for BuddyPress compatibility - $delim = MeprAppCtrl::get_param_delimiter_char($account_url); - $perpage = MeprHooks::apply_filters('mepr_payments_per_page', 10); - $curr_page = (isset($_GET['currpage']) && is_numeric($_GET['currpage']))?$_GET['currpage']:1; - $start = ($curr_page - 1) * $perpage; - $end = $start + $perpage; - - if(isset($args['mode']) && 'readylaunch' == $args['mode']){ - $perpage = isset($args['count']) ? $args['count'] + $perpage : $perpage; - } + public function password() + { + $mepr_current_user = MeprUtils::get_currentuserinfo(); + $mepr_options = MeprOptions::fetch(); + $account_url = $mepr_options->account_page_url(); + $delim = MeprAppCtrl::get_param_delimiter_char($account_url); - $list_table = MeprTransaction::list_table( - 'created_at', 'DESC', $curr_page, - '', 'any', $perpage, - array( - 'member' => $mepr_current_user->user_login, - 'statuses' => array( MeprTransaction::$complete_str ) - ) - ); - - $payments = $list_table['results']; - $all = $list_table['count']; - $next_page = (($curr_page * $perpage) >= $all)?false:$curr_page+1; - $prev_page = ($curr_page > 1)?$curr_page - 1:false; - - if($mepr_options->design_enable_account_template){ - MeprView::render('/readylaunch/account/payments', get_defined_vars()); - } else { - MeprView::render('/account/payments', get_defined_vars()); - } - } - - public function subscriptions($message='',$errors=array(), $args = array()) { - global $wpdb; - $mepr_current_user = MeprUtils::get_currentuserinfo(); - $mepr_options = MeprOptions::fetch(); - $account_url = $_SERVER['REQUEST_URI']; //Use URI for BuddyPress compatibility - $delim = MeprAppCtrl::get_param_delimiter_char($account_url); - $perpage = MeprHooks::apply_filters('mepr_subscriptions_per_page', 10); - $curr_page = (isset($_GET['currpage']) && is_numeric($_GET['currpage']))?$_GET['currpage']:1; - $start = ($curr_page - 1) * $perpage; - $end = $start + $perpage; - - // This is necessary to optimize the queries ... only query what we need - $sub_cols = array('id','user_id','product_id','subscr_id','status','created_at','expires_at','active'); - - if(isset($args['mode']) && 'readylaunch' == $args['mode']){ - $perpage = isset($args['count']) ? $args['count'] + $perpage : $perpage; - } + if (isset($_REQUEST['error'])) { + if ($_REQUEST['error'] == 'weak') { + $errors = [__('Password update failed, please check that your password meets the minimum strength requirement.', 'memberpress')]; + } else { + $errors = [__('Password update failed, please be sure your passwords match and try again.', 'memberpress')]; + } + } - $table = MeprSubscription::account_subscr_table( - 'created_at', 'DESC', - $curr_page, '', 'any', $perpage, false, - array( - 'member' => $mepr_current_user->user_login, - 'statuses' => array( - MeprSubscription::$active_str, - MeprSubscription::$suspended_str, - MeprSubscription::$cancelled_str - ) - ), - $sub_cols - ); - - $subscriptions = $table['results']; - $all = $table['count']; - $next_page = (($curr_page * $perpage) >= $all)?false:$curr_page + 1; - $prev_page = ($curr_page > 1)?$curr_page - 1:false; - - if($mepr_options->design_enable_account_template){ - MeprView::render('/readylaunch/shared/errors', get_defined_vars()); - MeprView::render('/readylaunch/account/subscriptions', get_defined_vars()); - } else { - MeprView::render('/shared/errors', get_defined_vars()); - MeprView::render('/account/subscriptions', get_defined_vars()); + if (MeprReadyLaunchCtrl::template_enabled('account')) { + MeprView::render('/readylaunch/account/password', get_defined_vars()); + } else { + MeprView::render('/account/password', get_defined_vars()); + } } - } - public function suspend() { - $mepr_current_user = MeprUtils::get_currentuserinfo(); - $sub = new MeprSubscription($_GET['sub']); - $errors = array(); - $message = ''; - - if($sub->user_id == $mepr_current_user->ID) { - $pm = $sub->payment_method(); - - if($pm->can('suspend-subscriptions')) { - try { - $pm->process_suspend_subscription($sub->id); - $message = __('Your subscription was successfully paused.', 'memberpress'); + public function payments($args = []) + { + global $wpdb; + $mepr_current_user = MeprUtils::get_currentuserinfo(); + $mepr_options = MeprOptions::fetch(); + $account_url = $_SERVER['REQUEST_URI']; // Use URI for BuddyPress compatibility + $delim = MeprAppCtrl::get_param_delimiter_char($account_url); + $perpage = MeprHooks::apply_filters('mepr_payments_per_page', 10); + $curr_page = (isset($_GET['currpage']) && is_numeric($_GET['currpage'])) ? $_GET['currpage'] : 1; + $start = ($curr_page - 1) * $perpage; + $end = $start + $perpage; + + if (isset($args['mode']) && 'readylaunch' == $args['mode']) { + $perpage = isset($args['count']) ? $args['count'] + $perpage : $perpage; } - catch( Exception $e ) { - $errors[] = $e->getMessage(); + + $list_table = MeprTransaction::list_table( + 'created_at', + 'DESC', + $curr_page, + '', + 'any', + $perpage, + [ + 'member' => $mepr_current_user->user_login, + 'statuses' => [MeprTransaction::$complete_str], + ] + ); + + $payments = $list_table['results']; + $all = $list_table['count']; + $next_page = (($curr_page * $perpage) >= $all) ? false : $curr_page + 1; + $prev_page = ($curr_page > 1) ? $curr_page - 1 : false; + + if ($mepr_options->design_enable_account_template) { + MeprView::render('/readylaunch/account/payments', get_defined_vars()); + } else { + MeprView::render('/account/payments', get_defined_vars()); } - } } - $this->subscriptions($message, $errors); - } + public function subscriptions($message = '', $errors = [], $args = []) + { + global $wpdb; + $mepr_current_user = MeprUtils::get_currentuserinfo(); + $mepr_options = MeprOptions::fetch(); + $account_url = $_SERVER['REQUEST_URI']; // Use URI for BuddyPress compatibility + $delim = MeprAppCtrl::get_param_delimiter_char($account_url); + $perpage = MeprHooks::apply_filters('mepr_subscriptions_per_page', 10); + $curr_page = (isset($_GET['currpage']) && is_numeric($_GET['currpage'])) ? $_GET['currpage'] : 1; + $start = ($curr_page - 1) * $perpage; + $end = $start + $perpage; - public function resume() { - $mepr_current_user = MeprUtils::get_currentuserinfo(); - $sub = new MeprSubscription($_GET['sub']); - $errors = array(); - $message = ''; + // This is necessary to optimize the queries ... only query what we need + $sub_cols = ['id','user_id','product_id','subscr_id','status','created_at','expires_at','active']; - if($sub->user_id == $mepr_current_user->ID) { - $pm = $sub->payment_method(); - - if($pm->can('suspend-subscriptions')) { - try { - $pm->process_resume_subscription($sub->id); - $message = __('You successfully resumed your subscription.', 'memberpress'); + if (isset($args['mode']) && 'readylaunch' == $args['mode']) { + $perpage = isset($args['count']) ? $args['count'] + $perpage : $perpage; } - catch(Exception $e) { - $errors[] = $e->getMessage(); + + $table = MeprSubscription::account_subscr_table( + 'created_at', + 'DESC', + $curr_page, + '', + 'any', + $perpage, + false, + [ + 'member' => $mepr_current_user->user_login, + 'statuses' => [ + MeprSubscription::$active_str, + MeprSubscription::$suspended_str, + MeprSubscription::$cancelled_str, + ], + ], + $sub_cols + ); + + $subscriptions = $table['results']; + $all = $table['count']; + $next_page = (($curr_page * $perpage) >= $all) ? false : $curr_page + 1; + $prev_page = ($curr_page > 1) ? $curr_page - 1 : false; + + if ($mepr_options->design_enable_account_template) { + MeprView::render('/readylaunch/shared/errors', get_defined_vars()); + MeprView::render('/readylaunch/account/subscriptions', get_defined_vars()); + } else { + MeprView::render('/shared/errors', get_defined_vars()); + MeprView::render('/account/subscriptions', get_defined_vars()); } - } } - $this->subscriptions($message, $errors); - } + public function suspend() + { + $mepr_current_user = MeprUtils::get_currentuserinfo(); + $sub = new MeprSubscription($_GET['sub']); + $errors = []; + $message = ''; + + if ($sub->user_id == $mepr_current_user->ID) { + $pm = $sub->payment_method(); + + if ($pm->can('suspend-subscriptions')) { + try { + $pm->process_suspend_subscription($sub->id); + $message = __('Your subscription was successfully paused.', 'memberpress'); + } catch (Exception $e) { + $errors[] = $e->getMessage(); + } + } + } - public function cancel() { - $mepr_current_user = MeprUtils::get_currentuserinfo(); - $sub = new MeprSubscription($_GET['sub']); - $errors = array(); - $message = ''; - $success_message = __('Your subscription was successfully cancelled.', 'memberpress'); + $this->subscriptions($message, $errors); + } - static $already_cancelled; + public function resume() + { + $mepr_current_user = MeprUtils::get_currentuserinfo(); + $sub = new MeprSubscription($_GET['sub']); + $errors = []; + $message = ''; - if($already_cancelled === true) { - $message = $success_message; - } - elseif($sub->user_id == $mepr_current_user->ID) { - $already_cancelled = true; - $pm = $sub->payment_method(); + if ($sub->user_id == $mepr_current_user->ID) { + $pm = $sub->payment_method(); - if($pm->can('cancel-subscriptions')) { - try { - $pm->process_cancel_subscription($sub->id); - $message = $success_message; + if ($pm->can('suspend-subscriptions')) { + try { + $pm->process_resume_subscription($sub->id); + $message = __('You successfully resumed your subscription.', 'memberpress'); + } catch (Exception $e) { + $errors[] = $e->getMessage(); + } + } } - catch(Exception $e) { - $errors[] = $e->getMessage(); + + $this->subscriptions($message, $errors); + } + + public function cancel() + { + $mepr_current_user = MeprUtils::get_currentuserinfo(); + $sub = new MeprSubscription($_GET['sub']); + $errors = []; + $message = ''; + $success_message = __('Your subscription was successfully cancelled.', 'memberpress'); + + static $already_cancelled; + + if ($already_cancelled === true) { + $message = $success_message; + } elseif ($sub->user_id == $mepr_current_user->ID) { + $already_cancelled = true; + $pm = $sub->payment_method(); + + if ($pm->can('cancel-subscriptions')) { + try { + $pm->process_cancel_subscription($sub->id); + $message = $success_message; + } catch (Exception $e) { + $errors[] = $e->getMessage(); + } + } } - } + + $this->subscriptions($message, $errors); } - $this->subscriptions($message, $errors); - } + public function update() + { + $mepr_current_user = MeprUtils::get_currentuserinfo(); + $sub = new MeprSubscription($_REQUEST['sub']); - public function update() { - $mepr_current_user = MeprUtils::get_currentuserinfo(); - $sub = new MeprSubscription($_REQUEST['sub']); + if ($sub->user_id == $mepr_current_user->ID) { + $pm = $sub->payment_method(); - if($sub->user_id == $mepr_current_user->ID) { - $pm = $sub->payment_method(); + if (strtoupper($_SERVER['REQUEST_METHOD'] == 'GET')) { // DISPLAY FORM + $pm->display_update_account_form($sub->id, []); + } elseif (strtoupper($_SERVER['REQUEST_METHOD'] == 'POST')) { // PROCESS FORM + $errors = $pm->validate_update_account_form([]); + $message = ''; - if(strtoupper($_SERVER['REQUEST_METHOD'] == 'GET')) // DISPLAY FORM - $pm->display_update_account_form($sub->id, array()); - elseif(strtoupper($_SERVER['REQUEST_METHOD'] == 'POST')) { // PROCESS FORM - $errors = $pm->validate_update_account_form(array()); - $message=''; + if (empty($errors)) { + try { + $pm->process_update_account_form($sub->id); + $message = __('Your account information was successfully updated.', 'memberpress'); + } catch (Exception $e) { + $errors[] = $e->getMessage(); + } + } - if(empty($errors)) { - try { - $pm->process_update_account_form($sub->id); - $message = __('Your account information was successfully updated.', 'memberpress'); - } - catch(Exception $e) { - $errors[] = $e->getMessage(); - } + $pm->display_update_account_form($sub->id, $errors, $message); + } } - - $pm->display_update_account_form($sub->id, $errors, $message); - } } - } - public function upgrade() { - $sub = new MeprSubscription($_GET['sub']); - $prd = $sub->product(); - $grp = $prd->group(); + public function upgrade() + { + $sub = new MeprSubscription($_GET['sub']); + $prd = $sub->product(); + $grp = $prd->group(); - // TODO: Uyeah, we may want to come up with a more elegant solution here - // for now we have to do a js redirect because we're in mid-page render - ?> + // TODO: Uyeah, we may want to come up with a more elegant solution here + // for now we have to do a js redirect because we're in mid-page render + ?> - display_account_form($content, $atts); - } - - public function display_account_form($content = '', $atts= []) { - global $post; - - //Static var to prevent duplicate token issues with Stripe - static $new_content; - static $content_length; - - //Init this posts static values - if(!isset($new_content) || empty($new_content) || !isset($content_length)) { - $new_content = ''; - $content_length = -1; + display_account_form($content, $atts); } - $content_length = strlen($content); - - if(MeprUtils::is_user_logged_in()) { - ob_start(); - MeprAccountCtrl::render($atts); - $content .= ob_get_clean(); - } - else { - $content = do_shortcode(MeprRulesCtrl::unauthorized_message($post)); - } + public function display_account_form($content = '', $atts = []) + { + global $post; - $new_content = $content; + // Static var to prevent duplicate token issues with Stripe + static $new_content; + static $content_length; - return $new_content; - } + // Init this posts static values + if (!isset($new_content) || empty($new_content) || !isset($content_length)) { + $new_content = ''; + $content_length = -1; + } - public function get_account_links() { - $mepr_options = MeprOptions::fetch(); - ob_start(); + if ($new_content && strlen($content) == $content_length) { + return $new_content; + } - if(MeprUtils::is_user_logged_in()) { - $account_url = $mepr_options->account_page_url(); - $logout_url = MeprUtils::logout_url(); - if(MeprReadyLaunchCtrl::template_enabled( 'account' )){ - MeprView::render('/readylaunch/account/logged_in_template', get_defined_vars()); - } else { - MeprView::render('/account/logged_in_template', get_defined_vars()); - } - } - else { - $login_url = MeprUtils::login_url(); - if(MeprReadyLaunchCtrl::template_enabled( 'account' )){ - MeprView::render('/readylaunch/account/logged_out_template', get_defined_vars()); - } else { - MeprView::render('/account/logged_out_template', get_defined_vars()); - } - } + $content_length = strlen($content); - return ob_get_clean(); - } + if (MeprUtils::is_user_logged_in()) { + ob_start(); + MeprAccountCtrl::render($atts); + $content .= ob_get_clean(); + } else { + $content = do_shortcode(MeprRulesCtrl::unauthorized_message($post)); + } - public function output_account_meta($atts=array(), $content='') { - global $mepr_options, $user_ID; + $new_content = $content; - if((int)$user_ID < 1 || !isset($atts['field'])) { - return ''; + return $new_content; } - $ums = MeprUtils::get_formatted_usermeta($user_ID); - $usermeta = array(); + public function get_account_links() + { + $mepr_options = MeprOptions::fetch(); + ob_start(); + + if (MeprUtils::is_user_logged_in()) { + $account_url = $mepr_options->account_page_url(); + $logout_url = MeprUtils::logout_url(); + if (MeprReadyLaunchCtrl::template_enabled('account')) { + MeprView::render('/readylaunch/account/logged_in_template', get_defined_vars()); + } else { + MeprView::render('/account/logged_in_template', get_defined_vars()); + } + } else { + $login_url = MeprUtils::login_url(); + if (MeprReadyLaunchCtrl::template_enabled('account')) { + MeprView::render('/readylaunch/account/logged_out_template', get_defined_vars()); + } else { + MeprView::render('/account/logged_out_template', get_defined_vars()); + } + } - if(!empty($ums)) { - foreach($ums as $umkey => $umval) { - $usermeta["{$umkey}"] = $umval; - } + return ob_get_clean(); } - //Get some additional params yo - $userdata = get_userdata($user_ID); + public function output_account_meta($atts = [], $content = '') + { + global $mepr_options, $user_ID; - foreach($userdata->data as $key => $value) { - $usermeta[$key] = $value; - } + if ((int)$user_ID < 1 || !isset($atts['field'])) { + return ''; + } - //We can begin to define more custom return cases in here... - switch($atts['field']) { - case 'full_name': - return ucfirst($usermeta['first_name']) . ' ' . ucfirst($usermeta['last_name']); - break; - case 'full_name_last_first': - return ucfirst($usermeta['last_name']) . ', ' . ucfirst($usermeta['first_name']); - break; - case 'first_name_last_initial': - return ucfirst($usermeta['first_name']) . ' ' . ucfirst($usermeta['last_name'][0]) . '.'; - break; - case 'last_name_first_initial': - return ucfirst($usermeta['last_name']) . ', ' . ucfirst($usermeta['first_name'][0]) . '.'; - break; - case 'user_registered': - return MeprAppHelper::format_date($usermeta[$atts['field']]); - break; - case 'mepr_user_message': - return wpautop(stripslashes(do_shortcode($usermeta[$atts['field']]))); - break; - default: - //Make sure field actually exists - if(isset($usermeta[$atts['field']]) && !empty($usermeta[$atts['field']])) { - return $usermeta[$atts['field']]; - } - break; - } - } + $ums = MeprUtils::get_formatted_usermeta($user_ID); + $usermeta = []; - public function save_new_password($user_id, $new_pass, $new_pass_confirm) { - $mepr_options = MeprOptions::fetch(); - $account_url = $mepr_options->account_page_url(); - $delim = MeprAppCtrl::get_param_delimiter_char($account_url); + if (!empty($ums)) { + foreach ($ums as $umkey => $umval) { + $usermeta["{$umkey}"] = $umval; + } + } - $user = MeprUtils::get_currentuserinfo(); + // Get some additional params yo + $userdata = get_userdata($user_ID); - //Check password strength first - if($mepr_options->enforce_strong_password && isset($_POST['mp-pass-strength']) && (int)$_POST['mp-pass-strength'] < MeprZxcvbnCtrl::get_required_int()) { - MeprUtils::wp_redirect($account_url.$delim.'action=newpassword&error=weak'); - } + foreach ($userdata->data as $key => $value) { + $usermeta[$key] = $value; + } - if($user_id && $user && ($user->ID==$user_id)) { - if(($new_pass == $new_pass_confirm) && !empty($new_pass)) { - $user->set_password($new_pass); - $user->store(); - MeprUtils::wp_redirect($account_url.$delim.'action=home&message=password_updated'); - } + // We can begin to define more custom return cases in here... + switch ($atts['field']) { + case 'full_name': + return ucfirst($usermeta['first_name']) . ' ' . ucfirst($usermeta['last_name']); + break; + case 'full_name_last_first': + return ucfirst($usermeta['last_name']) . ', ' . ucfirst($usermeta['first_name']); + break; + case 'first_name_last_initial': + return ucfirst($usermeta['first_name']) . ' ' . ucfirst($usermeta['last_name'][0]) . '.'; + break; + case 'last_name_first_initial': + return ucfirst($usermeta['last_name']) . ', ' . ucfirst($usermeta['first_name'][0]) . '.'; + break; + case 'user_registered': + return MeprAppHelper::format_date($usermeta[$atts['field']]); + break; + case 'mepr_user_message': + return wpautop(stripslashes(do_shortcode($usermeta[$atts['field']]))); + break; + default: + // Make sure field actually exists + if (isset($usermeta[$atts['field']]) && !empty($usermeta[$atts['field']])) { + return $usermeta[$atts['field']]; + } + break; + } } - MeprUtils::wp_redirect($account_url.$delim.'action=newpassword&error=failed'); - } - - /** - * Save account profile fields for Ready Launch account template - * - * @return void - */ - public function save_profile_fields() { - // Check for nonce security - if ( isset( $_POST['nonce'] ) && ! wp_verify_nonce( $_POST['nonce'], 'mepr_account_update' ) ) { - die( 'Busted!' ); - } + public function save_new_password($user_id, $new_pass, $new_pass_confirm) + { + $mepr_options = MeprOptions::fetch(); + $account_url = $mepr_options->account_page_url(); + $delim = MeprAppCtrl::get_param_delimiter_char($account_url); - //Since we use user_* for these, we need to artifically set the $_POST keys correctly for this to work - if(isset($_POST['user_first_name']) && ( !isset($_POST['first_name']) || empty($_POST['first_name']))) { - $_POST['first_name'] = (!empty($_POST['user_first_name']))?sanitize_text_field(wp_unslash($_POST['user_first_name'])):''; - unset($_POST['user_first_name']); - } + $user = MeprUtils::get_currentuserinfo(); - if(isset($_POST['user_last_name']) && (!isset($_POST['last_name']) || empty($_POST['last_name']))) { - $_POST['last_name'] = (!empty($_POST['user_last_name']))?sanitize_text_field(wp_unslash($_POST['user_last_name'])):''; - unset($_POST['user_last_name']); - } + // Check password strength first + if ($mepr_options->enforce_strong_password && isset($_POST['mp-pass-strength']) && (int)$_POST['mp-pass-strength'] < MeprZxcvbnCtrl::get_required_int()) { + MeprUtils::wp_redirect($account_url . $delim . 'action=newpassword&error=weak'); + } - $field_key = array_map( 'sanitize_key', array_merge( array_keys( $_POST ), array_keys( $_FILES ) ) ); - $current_user = MeprUtils::get_currentuserinfo(); + if ($user_id && $user && ($user->ID == $user_id)) { + if (($new_pass == $new_pass_confirm) && !empty($new_pass)) { + $user->set_password($new_pass); + $user->store(); + MeprUtils::wp_redirect($account_url . $delim . 'action=home&message=password_updated'); + } + } - $errors = MeprHooks::apply_filters( 'mepr-validate-account-ajax', array(), $current_user, $field_key ); + MeprUtils::wp_redirect($account_url . $delim . 'action=newpassword&error=failed'); + } - if ( empty( $errors ) ) { - if ( isset( $_POST['user_email'] ) && ! empty( $_POST['user_email'] ) ) { - $new_email = sanitize_email( $_POST['user_email'] ); + /** + * Save account profile fields for Ready Launch account template + * + * @return void + */ + public function save_profile_fields() + { + // Check for nonce security + if (isset($_POST['nonce']) && ! wp_verify_nonce($_POST['nonce'], 'mepr_account_update')) { + die('Busted!'); + } - if ( $current_user->user_email != $new_email ) { - $current_user->user_email = $new_email; - $current_user->store(); - MeprHooks::do_action( 'mepr-update-new-user-email', $current_user ); + // Since we use user_* for these, we need to artifically set the $_POST keys correctly for this to work + if (isset($_POST['user_first_name']) && ( !isset($_POST['first_name']) || empty($_POST['first_name']))) { + $_POST['first_name'] = (!empty($_POST['user_first_name'])) ? sanitize_text_field(wp_unslash($_POST['user_first_name'])) : ''; + unset($_POST['user_first_name']); } - } - MeprUsersCtrl::save_extra_profile_fields( $current_user->ID, true, false, false, $field_key ); - wp_send_json_success(); - } else { - wp_send_json_error( $errors ); - } - } + if (isset($_POST['user_last_name']) && (!isset($_POST['last_name']) || empty($_POST['last_name']))) { + $_POST['last_name'] = (!empty($_POST['user_last_name'])) ? sanitize_text_field(wp_unslash($_POST['user_last_name'])) : ''; + unset($_POST['user_last_name']); + } + + $field_key = array_map('sanitize_key', array_merge(array_keys($_POST), array_keys($_FILES))); + $current_user = MeprUtils::get_currentuserinfo(); + + $errors = MeprHooks::apply_filters('mepr-validate-account-ajax', [], $current_user, $field_key); + if (empty($errors)) { + if (isset($_POST['user_email']) && !empty($_POST['user_email'])) { + $new_email = sanitize_email($_POST['user_email']); - //Shortcode is meant to be placed on the thank you page or per-membership thank you page messages - public function offline_gateway_instructions($atts = array(), $content = '') { - if(!isset($atts['gateway_id']) || empty($atts['gateway_id'])) { return ''; } + if ($current_user->user_email != $new_email) { + $current_user->user_email = $new_email; + $current_user->store(); + MeprHooks::do_action('mepr-update-new-user-email', $current_user); + } + } - if(isset($_GET['trans_num']) && !empty($_GET['trans_num'])) { - $txn = MeprTransaction::get_one_by_trans_num($_GET['trans_num']); - } elseif(isset($_GET['transaction_id']) && !empty($_GET['transaction_id'])) { - $txn = MeprTransaction::get_one($_GET['transaction_id']); - } else { - return ''; + MeprUsersCtrl::save_extra_profile_fields($current_user->ID, true, false, false, $field_key); + wp_send_json_success(); + } else { + wp_send_json_error($errors); + } } - if(!isset($txn->gateway) || $txn->gateway != $atts['gateway_id']) { return ''; } - return do_shortcode($content); - } + // Shortcode is meant to be placed on the thank you page or per-membership thank you page messages + public function offline_gateway_instructions($atts = [], $content = '') + { + if (!isset($atts['gateway_id']) || empty($atts['gateway_id'])) { + return ''; + } - public function account_page_the_title($title) { - if (!in_the_loop() || !MeprHooks::apply_filters('mepr-account-nav-page-titles', false)) { - return $title; - } + if (isset($_GET['trans_num']) && !empty($_GET['trans_num'])) { + $txn = MeprTransaction::get_one_by_trans_num($_GET['trans_num']); + } elseif (isset($_GET['transaction_id']) && !empty($_GET['transaction_id'])) { + $txn = MeprTransaction::get_one($_GET['transaction_id']); + } else { + return ''; + } + + if (!isset($txn->gateway) || $txn->gateway != $atts['gateway_id']) { + return ''; + } - return $this->account_page_title($title); - } - public function account_page_browser_title($title) { - if (!MeprHooks::apply_filters('mepr-account-nav-broswer-titles', false)) { - return $title; + return do_shortcode($content); } - return $this->account_page_title($title); - } - public function account_page_title($title) { - global $post; + public function account_page_the_title($title) + { + if (!in_the_loop() || !MeprHooks::apply_filters('mepr-account-nav-page-titles', false)) { + return $title; + } - //Only apply the title changes on the account nave pages if it is turned on - //and buddy press intregration is not installed. - if (class_exists('MpBuddyPress')) { - return $title; + return $this->account_page_title($title); } + public function account_page_browser_title($title) + { + if (!MeprHooks::apply_filters('mepr-account-nav-broswer-titles', false)) { + return $title; + } - //If we don't have a post, just return the title - if (!isset($post) || !isset($post->ID) || $post->ID <= 0) { - return $title; + return $this->account_page_title($title); } + public function account_page_title($title) + { + global $post; - $action = isset($_REQUEST['action']) ? $_REQUEST['action'] : ''; - $title_after = ''; - $sep = ' ' . apply_filters( 'document_title_separator', '-' ) . ' '; - - if (MeprUser::is_account_page($post)) { - switch($action) { - case 'subscriptions': - $title_after = MeprHooks::apply_filters('mepr-account-subscriptions-title',_x('Subscriptions', 'ui', 'memberpress')); - break; - case 'payments': - $title_after = MeprHooks::apply_filters('mepr-account-payments-title',_x('Payments', 'ui', 'memberpress')); - break; - case 'courses': - $title_after = MeprHooks::apply_filters('mepr-account-courses-title',_x('Courses', 'ui', 'memberpress')); - break; - default: - //For custom tabs on account page. - $title_after = MeprHooks::apply_filters('mepr-custom-account-nav-title', '', $action); - break; - } - } + // Only apply the title changes on the account nave pages if it is turned on + // and buddy press intregration is not installed. + if (class_exists('MpBuddyPress')) { + return $title; + } + + // If we don't have a post, just return the title + if (!isset($post) || !isset($post->ID) || $post->ID <= 0) { + return $title; + } - return !empty($title_after) ? $title . $sep . $title_after : $title; - } + $action = isset($_REQUEST['action']) ? $_REQUEST['action'] : ''; + $title_after = ''; + $sep = ' ' . apply_filters('document_title_separator', '-') . ' '; + + if (MeprUser::is_account_page($post)) { + switch ($action) { + case 'subscriptions': + $title_after = MeprHooks::apply_filters('mepr-account-subscriptions-title', _x('Subscriptions', 'ui', 'memberpress')); + break; + case 'payments': + $title_after = MeprHooks::apply_filters('mepr-account-payments-title', _x('Payments', 'ui', 'memberpress')); + break; + case 'courses': + $title_after = MeprHooks::apply_filters('mepr-account-courses-title', _x('Courses', 'ui', 'memberpress')); + break; + default: + // For custom tabs on account page. + $title_after = MeprHooks::apply_filters('mepr-custom-account-nav-title', '', $action); + break; + } + } + + return !empty($title_after) ? $title . $sep . $title_after : $title; + } } diff --git a/app/controllers/MeprAccountLoginCtrl.php b/app/controllers/MeprAccountLoginCtrl.php index 162a570..8bc8e1c 100644 --- a/app/controllers/MeprAccountLoginCtrl.php +++ b/app/controllers/MeprAccountLoginCtrl.php @@ -1,31 +1,37 @@ id == 'memberpress_page_memberpress-account-login') { - $submenu_file = 'memberpress-options'; - } + // Remove the "Account Login" menu item on all pages + remove_submenu_page('memberpress', 'memberpress-account-login'); - return $submenu_file; - } + // Set the highlighted menu item to "Settings" + if ($current_screen instanceof WP_Screen && $current_screen->id == 'memberpress_page_memberpress-account-login') { + $submenu_file = 'memberpress-options'; + } - public static function route() { - $account_email = get_option('mepr_authenticator_account_email'); - $secret = get_option('mepr_authenticator_secret_token'); - $site_uuid = get_option('mepr_authenticator_site_uuid'); + return $submenu_file; + } - MeprView::render('/admin/account-login/ui', get_defined_vars()); - } + public static function route() + { + $account_email = get_option('mepr_authenticator_account_email'); + $secret = get_option('mepr_authenticator_secret_token'); + $site_uuid = get_option('mepr_authenticator_site_uuid'); + MeprView::render('/admin/account-login/ui', get_defined_vars()); + } } //End class diff --git a/app/controllers/MeprActiveInactiveHooksCtrl.php b/app/controllers/MeprActiveInactiveHooksCtrl.php index cbe14c1..ee2c1d6 100644 --- a/app/controllers/MeprActiveInactiveHooksCtrl.php +++ b/app/controllers/MeprActiveInactiveHooksCtrl.php @@ -1,99 +1,112 @@ status == $txn->status) { return; } - - // Allow third party plugins to stop the running of the method - if(MeprHooks::apply_filters('mepr-active-inactive-hooks-skip', false, $txn)){ - return; - } - - // Bail if no id's - if(!isset($txn->id) || $txn->id <= 0 || !isset($txn->user_id) || $txn->user_id <= 0) { return; } - - // Ignore "pending" txns - if(!isset($txn->status) || empty($txn->status) || $txn->status == MeprTransaction::$pending_str) { return; } - - $active_status = array(MeprTransaction::$complete_str, MeprTransaction::$confirmed_str); - $now = time(); - $expires = 0; // Lifetime - - if ( ! empty( $txn->expires_at ) && $txn->expires_at != MeprUtils::db_lifetime() ) { - $expires = strtotime($txn->expires_at); - } - - if(in_array($txn->status, $active_status)) { - if($expires === 0 || $expires >= $now) { - MeprHooks::do_action('mepr-account-is-active', $txn); - } - else { - MeprHooks::do_action('mepr-account-is-inactive', $txn); - } - } - else { - MeprHooks::do_action('mepr-account-is-inactive', $txn); - } - } - - public function handle_txn_expired($txn, $sub_status = false) { - global $wpdb; - // Part of an Enabled subscription, so let's bail - if($sub_status == MeprSubscription::$active_str) { - return; +if (!defined('ABSPATH')) { + die('You are not allowed to call this page directly.'); +} +/* + ** Added in MP 1.7.3 + ** USED FOR OUR AUTORESPONDER ADD-ONS - So we can have the active/inactive logic all in one place + */ +class MeprActiveInactiveHooksCtrl extends MeprBaseCtrl +{ + public function load_hooks() + { + add_action('mepr-txn-store', [$this, 'handle_txn_store'], 99, 2); + add_action('mepr-txn-expired', [$this, 'handle_txn_expired'], 11, 2); + add_action('delete_user', [$this, 'handle_delete_user']); } - // Allow third party plugins to stop the running of the method - if(MeprHooks::apply_filters('mepr-active-inactive-hooks-skip', false, $txn)){ - return; + public function handle_txn_store($txn, $old_txn) + { + // Already been here? + if ($old_txn->status == $txn->status) { + return; + } + + // Allow third party plugins to stop the running of the method + if (MeprHooks::apply_filters('mepr-active-inactive-hooks-skip', false, $txn)) { + return; + } + + // Bail if no id's + if (!isset($txn->id) || $txn->id <= 0 || !isset($txn->user_id) || $txn->user_id <= 0) { + return; + } + + // Ignore "pending" txns + if (!isset($txn->status) || empty($txn->status) || $txn->status == MeprTransaction::$pending_str) { + return; + } + + $active_status = [MeprTransaction::$complete_str, MeprTransaction::$confirmed_str]; + $now = time(); + $expires = 0; // Lifetime + + if (! empty($txn->expires_at) && $txn->expires_at != MeprUtils::db_lifetime()) { + $expires = strtotime($txn->expires_at); + } + + if (in_array($txn->status, $active_status)) { + if ($expires === 0 || $expires >= $now) { + MeprHooks::do_action('mepr-account-is-active', $txn); + } else { + MeprHooks::do_action('mepr-account-is-inactive', $txn); + } + } else { + MeprHooks::do_action('mepr-account-is-inactive', $txn); + } } - // Bail if no id's - if(!isset($txn->id) || $txn->id <= 0 || !isset($txn->user_id) || $txn->user_id <= 0) { return; } - - // Go directly to the database and maybe flush caches beforehand - if(MeprHooks::apply_filters('mepr-autoresponder-flush-caches', true)) { - wp_cache_flush(); - $wpdb->flush(); - } - - $query = $wpdb->prepare( - "SELECT count(*) FROM {$wpdb->prefix}mepr_transactions WHERE user_id = %d AND product_id = %d AND status IN (%s, %s) AND (expires_at >= %s OR expires_at = %s)", - $txn->user_id, - $txn->product_id, - MeprTransaction::$complete_str, - MeprTransaction::$confirmed_str, - MeprUtils::db_now(), - MeprUtils::db_lifetime() - ); - - $active_on_membership = $wpdb->get_var($query); - - if($active_on_membership) { - MeprHooks::do_action('mepr-account-is-active', $txn); - } - else { - MeprHooks::do_action('mepr-account-is-inactive', $txn); + public function handle_txn_expired($txn, $sub_status = false) + { + global $wpdb; + + // Part of an Enabled subscription, so let's bail + if ($sub_status == MeprSubscription::$active_str) { + return; + } + + // Allow third party plugins to stop the running of the method + if (MeprHooks::apply_filters('mepr-active-inactive-hooks-skip', false, $txn)) { + return; + } + + // Bail if no id's + if (!isset($txn->id) || $txn->id <= 0 || !isset($txn->user_id) || $txn->user_id <= 0) { + return; + } + + // Go directly to the database and maybe flush caches beforehand + if (MeprHooks::apply_filters('mepr-autoresponder-flush-caches', true)) { + wp_cache_flush(); + $wpdb->flush(); + } + + $query = $wpdb->prepare( + "SELECT count(*) FROM {$wpdb->prefix}mepr_transactions WHERE user_id = %d AND product_id = %d AND status IN (%s, %s) AND (expires_at >= %s OR expires_at = %s)", + $txn->user_id, + $txn->product_id, + MeprTransaction::$complete_str, + MeprTransaction::$confirmed_str, + MeprUtils::db_now(), + MeprUtils::db_lifetime() + ); + + $active_on_membership = $wpdb->get_var($query); + + if ($active_on_membership) { + MeprHooks::do_action('mepr-account-is-active', $txn); + } else { + MeprHooks::do_action('mepr-account-is-inactive', $txn); + } } - } - public function handle_delete_user($user_id) { - $user = new MeprUser( $user_id ); - $transactions = (array) $user->active_product_subscriptions( 'transactions', true, true ); - foreach ($transactions as $transaction) { - do_action('mepr-account-is-inactive', $transaction); + public function handle_delete_user($user_id) + { + $user = new MeprUser($user_id); + $transactions = (array) $user->active_product_subscriptions('transactions', true, true); + foreach ($transactions as $transaction) { + do_action('mepr-account-is-inactive', $transaction); + } } - } } // End Class diff --git a/app/controllers/MeprAddonsCtrl.php b/app/controllers/MeprAddonsCtrl.php index 9c729cf..5070ae6 100644 --- a/app/controllers/MeprAddonsCtrl.php +++ b/app/controllers/MeprAddonsCtrl.php @@ -1,315 +1,328 @@ admin_url('admin-ajax.php'), - 'nonce' => wp_create_nonce('mepr_addons'), - 'active' => __('Active', 'memberpress'), - 'inactive' => __('Inactive', 'memberpress'), - 'activate' => __('Activate', 'memberpress'), - 'deactivate' => __('Deactivate', 'memberpress'), - 'install_failed' => __('Could not install add-on. Please download from memberpress.com and install manually.', 'memberpress'), - 'plugin_install_failed' => __('Could not install plugin. Please download and install manually.', 'memberpress'), - )); - } - if(preg_match('/_page_memberpress-(analytics|smtp|affiliates)$/', $hook)) { - wp_enqueue_style('mepr-sister-plugin-css', MEPR_CSS_URL . '/admin-sister-plugin.css', array(), MEPR_VERSION); - wp_enqueue_script('mepr-sister-plugin-js', MEPR_JS_URL . '/admin_sister_plugin.js', array(), MEPR_VERSION); +if (!defined('ABSPATH')) { + die('You are not allowed to call this page directly.'); +} - wp_localize_script('mepr-sister-plugin-js', 'MeprSisterPlugin', array( - 'ajax_url' => admin_url('admin-ajax.php'), - 'nonce' => wp_create_nonce('mepr_addons'), - 'install_failed' => __('Could not install plugin. Please download and install manually.', 'memberpress'), - 'installed_and_activated' => __('Installed & Activated', 'memberpress') - )); +class MeprAddonsCtrl extends MeprBaseCtrl +{ + public function load_hooks() + { + add_action('admin_enqueue_scripts', [$this, 'enqueue_scripts']); + add_action('wp_ajax_mepr_addon_activate', [$this, 'ajax_addon_activate']); + add_action('wp_ajax_mepr_addon_deactivate', [$this, 'ajax_addon_deactivate']); + add_action('wp_ajax_mepr_addon_install', [$this, 'ajax_addon_install']); + add_filter('wp_mail_smtp_core_get_upgrade_link', [$this, 'smtp_affiliate_link']); + add_filter('monsterinsights_shareasale_id', [$this, 'monsterinsights_shareasale_id']); } - } - public function ajax_addon_activate() { - if(!isset($_POST['plugin'])) { - wp_send_json_error(__('Bad request.', 'memberpress')); - } + public static function route() + { + $force = isset($_GET['refresh']) && $_GET['refresh'] == 'true'; + $addons = MeprUpdateCtrl::addons(true, $force, true); + $plugins = get_plugins(); + wp_cache_delete('plugins', 'plugins'); - if(!current_user_can('activate_plugins')) { - wp_send_json_error(__('Sorry, you don\'t have permission to do this.', 'memberpress')); + MeprView::render('/admin/addons/ui', get_defined_vars()); } - if(!check_ajax_referer('mepr_addons', false, false)) { - wp_send_json_error(__('Security check failed.', 'memberpress')); - } + public function enqueue_scripts($hook) + { + if (preg_match('/_page_memberpress-addons$/', $hook)) { + wp_enqueue_style('mepr-addons-css', MEPR_CSS_URL . '/admin-addons.css', [], MEPR_VERSION); + wp_enqueue_script('list-js', MEPR_JS_URL . '/list.min.js', [], '1.5.0'); + wp_enqueue_script('jquery-match-height', MEPR_JS_URL . '/jquery.matchHeight-min.js', [], '0.7.2'); + wp_enqueue_script('mepr-addons-js', MEPR_JS_URL . '/admin_addons.js', ['list-js', 'jquery-match-height'], MEPR_VERSION); + + wp_localize_script('mepr-addons-js', 'MeprAddons', [ + 'ajax_url' => admin_url('admin-ajax.php'), + 'nonce' => wp_create_nonce('mepr_addons'), + 'active' => __('Active', 'memberpress'), + 'inactive' => __('Inactive', 'memberpress'), + 'activate' => __('Activate', 'memberpress'), + 'deactivate' => __('Deactivate', 'memberpress'), + 'install_failed' => __('Could not install add-on. Please download from memberpress.com and install manually.', 'memberpress'), + 'plugin_install_failed' => __('Could not install plugin. Please download and install manually.', 'memberpress'), + ]); + } - $result = activate_plugins(wp_unslash($_POST['plugin'])); - $type = isset($_POST['type']) ? sanitize_key($_POST['type']) : 'add-on'; + if (preg_match('/_page_memberpress-(analytics|smtp|affiliates)$/', $hook)) { + wp_enqueue_style('mepr-sister-plugin-css', MEPR_CSS_URL . '/admin-sister-plugin.css', [], MEPR_VERSION); + wp_enqueue_script('mepr-sister-plugin-js', MEPR_JS_URL . '/admin_sister_plugin.js', [], MEPR_VERSION); - if(is_wp_error($result)) { - if($type == 'plugin') { - wp_send_json_error(__('Could not activate plugin. Please activate from the Plugins page manually.', 'memberpress')); - } else { - wp_send_json_error(__('Could not activate add-on. Please activate from the Plugins page manually.', 'memberpress')); - } + wp_localize_script('mepr-sister-plugin-js', 'MeprSisterPlugin', [ + 'ajax_url' => admin_url('admin-ajax.php'), + 'nonce' => wp_create_nonce('mepr_addons'), + 'install_failed' => __('Could not install plugin. Please download and install manually.', 'memberpress'), + 'installed_and_activated' => __('Installed & Activated', 'memberpress'), + ]); + } } - if($type == 'plugin') { - wp_send_json_success(__('Plugin activated.', 'memberpress')); - } else { - wp_send_json_success(__('Add-on activated.', 'memberpress')); - } - } + public function ajax_addon_activate() + { + if (!isset($_POST['plugin'])) { + wp_send_json_error(__('Bad request.', 'memberpress')); + } - public function ajax_addon_deactivate() { - if(!isset($_POST['plugin'])) { - wp_send_json_error(__('Bad request.', 'memberpress')); - } + if (!current_user_can('activate_plugins')) { + wp_send_json_error(__('Sorry, you don\'t have permission to do this.', 'memberpress')); + } - if(!current_user_can('deactivate_plugins')) { - wp_send_json_error(__('Sorry, you don\'t have permission to do this.', 'memberpress')); - } + if (!check_ajax_referer('mepr_addons', false, false)) { + wp_send_json_error(__('Security check failed.', 'memberpress')); + } - if(!check_ajax_referer('mepr_addons', false, false)) { - wp_send_json_error(__('Security check failed.', 'memberpress')); - } + $result = activate_plugins(wp_unslash($_POST['plugin'])); + $type = isset($_POST['type']) ? sanitize_key($_POST['type']) : 'add-on'; - deactivate_plugins(wp_unslash($_POST['plugin'])); - $type = isset($_POST['type']) ? sanitize_key($_POST['type']) : 'add-on'; + if (is_wp_error($result)) { + if ($type == 'plugin') { + wp_send_json_error(__('Could not activate plugin. Please activate from the Plugins page manually.', 'memberpress')); + } else { + wp_send_json_error(__('Could not activate add-on. Please activate from the Plugins page manually.', 'memberpress')); + } + } - if($type == 'plugin') { - wp_send_json_success(__('Plugin deactivated.', 'memberpress')); - } else { - wp_send_json_success(__('Add-on deactivated.', 'memberpress')); + if ($type == 'plugin') { + wp_send_json_success(__('Plugin activated.', 'memberpress')); + } else { + wp_send_json_success(__('Add-on activated.', 'memberpress')); + } } - } - public function ajax_addon_install() { - if(!isset($_POST['plugin'])) { - wp_send_json_error(__('Bad request.', 'memberpress')); - } + public function ajax_addon_deactivate() + { + if (!isset($_POST['plugin'])) { + wp_send_json_error(__('Bad request.', 'memberpress')); + } - if(!current_user_can('install_plugins') || !current_user_can('activate_plugins')) { - wp_send_json_error(__('Sorry, you don\'t have permission to do this.', 'memberpress')); - } + if (!current_user_can('deactivate_plugins')) { + wp_send_json_error(__('Sorry, you don\'t have permission to do this.', 'memberpress')); + } - if(!check_ajax_referer('mepr_addons', false, false)) { - wp_send_json_error(__('Security check failed.', 'memberpress')); - } + if (!check_ajax_referer('mepr_addons', false, false)) { + wp_send_json_error(__('Security check failed.', 'memberpress')); + } - $type = isset($_POST['type']) ? sanitize_key($_POST['type']) : 'add-on'; + deactivate_plugins(wp_unslash($_POST['plugin'])); + $type = isset($_POST['type']) ? sanitize_key($_POST['type']) : 'add-on'; - if($type == 'plugin') { - $error = esc_html__('Could not install plugin. Please download and install manually.', 'memberpress'); - } else { - $error = esc_html__('Could not install add-on. Please download from memberpress.com and install manually.', 'memberpress'); + if ($type == 'plugin') { + wp_send_json_success(__('Plugin deactivated.', 'memberpress')); + } else { + wp_send_json_success(__('Add-on deactivated.', 'memberpress')); + } } - // Set the current screen to avoid undefined notices - set_current_screen('memberpress_page_memberpress-addons'); + public function ajax_addon_install() + { + if (!isset($_POST['plugin'])) { + wp_send_json_error(__('Bad request.', 'memberpress')); + } - // Prepare variables - $url = esc_url_raw( - add_query_arg( - array( - 'page' => 'memberpress-addons', - ), - admin_url('admin.php') - ) - ); + if (!current_user_can('install_plugins') || !current_user_can('activate_plugins')) { + wp_send_json_error(__('Sorry, you don\'t have permission to do this.', 'memberpress')); + } - $creds = request_filesystem_credentials($url, '', false, false, null); + if (!check_ajax_referer('mepr_addons', false, false)) { + wp_send_json_error(__('Security check failed.', 'memberpress')); + } - // Check for file system permissions - if(false === $creds) { - wp_send_json_error($error); - } + $type = isset($_POST['type']) ? sanitize_key($_POST['type']) : 'add-on'; - if(!WP_Filesystem($creds)) { - wp_send_json_error($error); - } + if ($type == 'plugin') { + $error = esc_html__('Could not install plugin. Please download and install manually.', 'memberpress'); + } else { + $error = esc_html__('Could not install add-on. Please download from memberpress.com and install manually.', 'memberpress'); + } - // We do not need any extra credentials if we have gotten this far, so let's install the plugin - require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php'; + // Set the current screen to avoid undefined notices + set_current_screen('memberpress_page_memberpress-addons'); + + // Prepare variables + $url = esc_url_raw( + add_query_arg( + [ + 'page' => 'memberpress-addons', + ], + admin_url('admin.php') + ) + ); - // Do not allow WordPress to search/download translations, as this will break JS output - remove_action('upgrader_process_complete', array('Language_Pack_Upgrader', 'async_upgrade'), 20); + $creds = request_filesystem_credentials($url, '', false, false, null); - // Create the plugin upgrader with our custom skin - $installer = new Plugin_Upgrader(new MeprAddonInstallSkin()); + // Check for file system permissions + if (false === $creds) { + wp_send_json_error($error); + } - $plugin = wp_unslash($_POST['plugin']); - $installer->install($plugin); + if (!WP_Filesystem($creds)) { + wp_send_json_error($error); + } - if($plugin == 'https://downloads.wordpress.org/plugin/google-analytics-for-wordpress.latest-stable.zip') { - update_option('memberpress_installed_monsterinsights', true); - } + // We do not need any extra credentials if we have gotten this far, so let's install the plugin + require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php'; - if($plugin == 'https://downloads.wordpress.org/plugin/wp-mail-smtp.latest-stable.zip') { - update_option('memberpress_installed_wp_mail_smtp', true); - } + // Do not allow WordPress to search/download translations, as this will break JS output + remove_action('upgrader_process_complete', ['Language_Pack_Upgrader', 'async_upgrade'], 20); - // Flush the cache and return the newly installed plugin basename - wp_cache_flush(); - - if($installer->plugin_info()) { - $plugin_basename = $installer->plugin_info(); - - // Activate the plugin silently - $activated = activate_plugin($plugin_basename); - - if(!is_wp_error($activated)) { - if(isset($_POST['config']) && is_array($_POST['config'])) { - $slug = isset($_POST['config']['slug']) && is_string($_POST['config']['slug']) ? wp_unslash($_POST['config']['slug']) : ''; - $license_key = isset($_POST['config']['license_key']) && is_string($_POST['config']['license_key']) ? sanitize_text_field(wp_unslash($_POST['config']['license_key'])) : ''; - - if( - $slug == 'easy-affiliate/easy-affiliate.php' && - !empty($license_key) && - class_exists('EasyAffiliate\\Models\\Options') && - class_exists('EasyAffiliate\\Controllers\\UpdateCtrl') && - class_exists('EasyAffiliate\\Lib\\Utils') - ) { - try { - $options = \EasyAffiliate\Models\Options::fetch(); - $options->mothership_license = $license_key; - $domain = urlencode(\EasyAffiliate\Lib\Utils::site_domain()); - $args = compact('domain'); - \EasyAffiliate\Controllers\UpdateCtrl::send_mothership_request("/license_keys/activate/{$options->mothership_license}", $args, 'post'); - $options->store(); - \EasyAffiliate\Controllers\UpdateCtrl::manually_queue_update(); - - // Clear the add-ons cache - delete_site_transient('esaf_addons'); - delete_site_transient('esaf_all_addons'); - } - catch(Exception $e) { - // Ignore license activation failure + // Create the plugin upgrader with our custom skin + $installer = new Plugin_Upgrader(new MeprAddonInstallSkin()); + + $plugin = wp_unslash($_POST['plugin']); + $installer->install($plugin); + + if ($plugin == 'https://downloads.wordpress.org/plugin/google-analytics-for-wordpress.latest-stable.zip') { + update_option('memberpress_installed_monsterinsights', true); + } + + if ($plugin == 'https://downloads.wordpress.org/plugin/wp-mail-smtp.latest-stable.zip') { + update_option('memberpress_installed_wp_mail_smtp', true); + } + + // Flush the cache and return the newly installed plugin basename + wp_cache_flush(); + + if ($installer->plugin_info()) { + $plugin_basename = $installer->plugin_info(); + + // Activate the plugin silently + $activated = activate_plugin($plugin_basename); + + if (!is_wp_error($activated)) { + if (isset($_POST['config']) && is_array($_POST['config'])) { + $slug = isset($_POST['config']['slug']) && is_string($_POST['config']['slug']) ? wp_unslash($_POST['config']['slug']) : ''; + $license_key = isset($_POST['config']['license_key']) && is_string($_POST['config']['license_key']) ? sanitize_text_field(wp_unslash($_POST['config']['license_key'])) : ''; + + if ( + $slug == 'easy-affiliate/easy-affiliate.php' && + !empty($license_key) && + class_exists('EasyAffiliate\\Models\\Options') && + class_exists('EasyAffiliate\\Controllers\\UpdateCtrl') && + class_exists('EasyAffiliate\\Lib\\Utils') + ) { + try { + $options = \EasyAffiliate\Models\Options::fetch(); + $options->mothership_license = $license_key; + $domain = urlencode(\EasyAffiliate\Lib\Utils::site_domain()); + $args = compact('domain'); + \EasyAffiliate\Controllers\UpdateCtrl::send_mothership_request("/license_keys/activate/{$options->mothership_license}", $args, 'post'); + $options->store(); + \EasyAffiliate\Controllers\UpdateCtrl::manually_queue_update(); + + // Clear the add-ons cache + delete_site_transient('esaf_addons'); + delete_site_transient('esaf_all_addons'); + } catch (Exception $e) { + // Ignore license activation failure + } + } + } + + wp_send_json_success( + [ + 'message' => $type == 'plugin' ? __('Plugin installed & activated.', 'memberpress') : __('Add-on installed & activated.', 'memberpress'), + 'activated' => true, + 'basename' => $plugin_basename, + ] + ); + } else { + wp_send_json_success( + [ + 'message' => $type == 'plugin' ? __('Plugin installed.', 'memberpress') : __('Add-on installed.', 'memberpress'), + 'activated' => false, + 'basename' => $plugin_basename, + ] + ); } - } } - wp_send_json_success( - array( - 'message' => $type == 'plugin' ? __('Plugin installed & activated.', 'memberpress') : __('Add-on installed & activated.', 'memberpress'), - 'activated' => true, - 'basename' => $plugin_basename - ) - ); - } else { - wp_send_json_success( - array( - 'message' => $type == 'plugin' ? __('Plugin installed.', 'memberpress') : __('Add-on installed.', 'memberpress'), - 'activated' => false, - 'basename' => $plugin_basename - ) - ); - } + wp_send_json_error($error); } - wp_send_json_error($error); - } - - /** - * Returns current plugin info. - * - * @return string Plugin info - */ - public function curr_plugin_info($main_file) { - static $curr_plugins; + /** + * Returns current plugin info. + * + * @return string Plugin info + */ + public function curr_plugin_info($main_file) + { + static $curr_plugins; + + if (!isset($curr_plugins)) { + if (!function_exists('get_plugins')) { + require_once(ABSPATH . '/wp-admin/includes/plugin.php'); + } - if(!isset($curr_plugins)) { - if(!function_exists('get_plugins')) { - require_once( ABSPATH . '/wp-admin/includes/plugin.php' ); - } + $curr_plugins = get_plugins(); + wp_cache_delete('plugins', 'plugins'); + } - $curr_plugins = get_plugins(); - wp_cache_delete('plugins', 'plugins'); - } + if (isset($curr_plugins[$main_file])) { + return $curr_plugins[$main_file]; + } - if(isset($curr_plugins[$main_file])) { - return $curr_plugins[$main_file]; + return ''; } - return ''; - } + public function monsterinsights_shareasale_id($id) + { + if (get_option('memberpress_installed_monsterinsights')) { + $id = '409876'; + } - public function monsterinsights_shareasale_id($id) { - if(get_option('memberpress_installed_monsterinsights')) { - $id = '409876'; + return $id; } - return $id; - } + public function smtp_affiliate_link($link) + { + if (get_option('memberpress_installed_wp_mail_smtp')) { + $link = 'https://shareasale.com/r.cfm?b=834775&u=409876&m=64312&urllink=wpmailsmtp%2Ecom%2Flite%2Dupgrade%2F&afftrack=MP%2DAnalytics%2DMenu%2DItem'; + } - public function smtp_affiliate_link($link) { - if(get_option('memberpress_installed_wp_mail_smtp')) { - $link = 'https://shareasale.com/r.cfm?b=834775&u=409876&m=64312&urllink=wpmailsmtp%2Ecom%2Flite%2Dupgrade%2F&afftrack=MP%2DAnalytics%2DMenu%2DItem'; + return $link; } - return $link; - } - - public static function affiliates() { - $installer_data = array( - 'return_url' => admin_url('admin.php?page=memberpress-affiliates'), - 'nonce' => wp_create_nonce('mepr_easy_affiliate_installer'), - ); - - $installer_data = wp_json_encode($installer_data); - $installer_data = rtrim(strtr(base64_encode($installer_data), '+/', '-_'), '='); - $installer_url = 'https://easyaffiliate.com/installer/' . $installer_data; - - $plugin = array( - 'active' => defined('ESAF_VERSION'), - 'installed' => is_dir(WP_PLUGIN_DIR . '/easy-affiliate'), - 'installer_url' => $installer_url, - 'auto_install' => false, - 'url' => '', - 'license_key' => '', - 'slug' => 'easy-affiliate/easy-affiliate.php', - 'activate_button_text' => __('Activate Easy Affiliate', 'memberpress'), - 'next_step_button_html' => sprintf( - '%s', - esc_url(admin_url('admin.php?page=easy-affiliate-onboarding')), - esc_html__('Run Setup Wizard', 'memberpress') - ) - ); - - if(isset($_GET['data']) && is_string($_GET['data'])) { - $data = wp_unslash($_GET['data']); - $data = base64_decode(strtr($data, '-_', '+/') . str_repeat('=', 3 - (3 + strlen($data)) % 4)); - $data = json_decode($data, true); - - if(is_array($data) && isset($data['license_key'], $data['download_url'], $data['nonce']) && wp_verify_nonce($data['nonce'], 'mepr_easy_affiliate_installer')) { - $plugin['auto_install'] = true; - $plugin['url'] = $data['download_url']; - $plugin['license_key'] = $data['license_key']; - } - } + public static function affiliates() + { + $installer_data = [ + 'return_url' => admin_url('admin.php?page=memberpress-affiliates'), + 'nonce' => wp_create_nonce('mepr_easy_affiliate_installer'), + ]; + + $installer_data = wp_json_encode($installer_data); + $installer_data = rtrim(strtr(base64_encode($installer_data), '+/', '-_'), '='); + $installer_url = 'https://easyaffiliate.com/installer/' . $installer_data; + + $plugin = [ + 'active' => defined('ESAF_VERSION'), + 'installed' => is_dir(WP_PLUGIN_DIR . '/easy-affiliate'), + 'installer_url' => $installer_url, + 'auto_install' => false, + 'url' => '', + 'license_key' => '', + 'slug' => 'easy-affiliate/easy-affiliate.php', + 'activate_button_text' => __('Activate Easy Affiliate', 'memberpress'), + 'next_step_button_html' => sprintf( + '%s', + esc_url(admin_url('admin.php?page=easy-affiliate-onboarding')), + esc_html__('Run Setup Wizard', 'memberpress') + ), + ]; + + if (isset($_GET['data']) && is_string($_GET['data'])) { + $data = wp_unslash($_GET['data']); + $data = base64_decode(strtr($data, '-_', '+/') . str_repeat('=', 3 - (3 + strlen($data)) % 4)); + $data = json_decode($data, true); + + if (is_array($data) && isset($data['license_key'], $data['download_url'], $data['nonce']) && wp_verify_nonce($data['nonce'], 'mepr_easy_affiliate_installer')) { + $plugin['auto_install'] = true; + $plugin['url'] = $data['download_url']; + $plugin['license_key'] = $data['license_key']; + } + } - MeprView::render('/admin/addons/affiliates', get_defined_vars()); - } + MeprView::render('/admin/addons/affiliates', get_defined_vars()); + } } diff --git a/app/controllers/MeprAntiCardTestingCtrl.php b/app/controllers/MeprAntiCardTestingCtrl.php index 94b5f4f..41355e2 100644 --- a/app/controllers/MeprAntiCardTestingCtrl.php +++ b/app/controllers/MeprAntiCardTestingCtrl.php @@ -1,17 +1,20 @@ +class MeprAntiCardTestingCtrl extends MeprBaseCtrl +{ + public function load_hooks() + { + add_action('mepr_display_general_options', [$this, 'display_options']); + add_action('mepr_stripe_payment_failed', [$this, 'record_payment_failure']); + add_action('mepr_stripe_before_confirm_payment', [$this, 'maybe_block_confirm_payment']); + add_action('mepr_stripe_before_create_checkout_session', [$this, 'maybe_block_create_checkout_session']); + add_action('wp_ajax_mepr_anti_card_testing_get_ip', [$this, 'get_detected_ip_ajax']); + } + + public function display_options() + { + $mepr_options = MeprOptions::fetch(); + ?>

@@ -20,13 +23,13 @@ public function display_options() { anti_card_testing_enabled_str, - __('Enable Card Testing Protection', 'memberpress'), - sprintf( + $mepr_options->anti_card_testing_enabled_str, + __('Enable Card Testing Protection', 'memberpress'), + sprintf( // translators: %1$s: br tag - __('Card testing is a type of fraudulent activity where someone tries to determine if stolen card information can be used to make purchases, by repeatedly attempting a purchase with different card numbers until one succeeds.%1$s%1$sBy enabling this protection, MemberPress will permanently block any further payment attempts by any user that has had 5 failed payments in a 2 hour window.', 'memberpress'), - '
' - ) + __('Card testing is a type of fraudulent activity where someone tries to determine if stolen card information can be used to make purchases, by repeatedly attempting a purchase with different card numbers until one succeeds.%1$s%1$sBy enabling this protection, MemberPress will permanently block any further payment attempts by any user that has had 5 failed payments in a 2 hour window.', 'memberpress'), + '
' + ) ); ?> @@ -42,20 +45,20 @@ public function display_options() {
- + anti_card_testing_ip_method_str, - __('How To Get Visitor IP?', 'memberpress'), - sprintf( + $mepr_options->anti_card_testing_ip_method_str, + __('How To Get Visitor IP?', 'memberpress'), + sprintf( // translators: %1$s: br tag, %2$s: open link tag, %3$s: close link tag - __('Which method should MemberPress use to retrieve the visitor\'s IP address?%1$s%1$sIt\'s important to use a method that is compatible with your site. The REMOTE_ADDR method is the most secure but may not be correct if your site is using a front-end proxy.%1$s%1$sCompare the displayed detected IP address with what is displayed on %2$sthis site%3$s to find the correct method for your site.', 'memberpress'), - '
', - '', - '' - ) + __('Which method should MemberPress use to retrieve the visitor\'s IP address?%1$s%1$sIt\'s important to use a method that is compatible with your site. The REMOTE_ADDR method is the most secure but may not be correct if your site is using a front-end proxy.%1$s%1$sCompare the displayed detected IP address with what is displayed on %2$sthis site%3$s to find the correct method for your site.', 'memberpress'), + '
', + '', + '' + ) ); - ?> + ?>

@@ -64,63 +67,63 @@ public function display_options() { ', - '' + esc_html__('%1$sDefault%2$s - Compatible with most sites, but not as secure as the methods below.', 'memberpress'), + '', + '' ); - ?> + ?>

anti_card_testing_ip_method, 'REMOTE_ADDR'); ?>>

anti_card_testing_ip_method, 'HTTP_X_FORWARDED_FOR'); ?>>

anti_card_testing_ip_method, 'HTTP_X_REAL_IP'); ?>>

anti_card_testing_ip_method, 'HTTP_CF_CONNECTING_IP'); ?>>

@@ -129,17 +132,17 @@ public function display_options() {
- anti_card_testing_blocked_str, - __('Blocked IP Addresses', 'memberpress'), - sprintf( + $mepr_options->anti_card_testing_blocked_str, + __('Blocked IP Addresses', 'memberpress'), + sprintf( // translators: %1$s: br tag - __('The IP addresses listed here are currently banned from making purchases.%1$s%1$sYou can add a new IP address (one per line) to block it, or remove an IP address to unblock it.', 'memberpress'), - '
' - ) + __('The IP addresses listed here are currently banned from making purchases.%1$s%1$sYou can add a new IP address (one per line) to block it, or remove an IP address to unblock it.', 'memberpress'), + '
' + ) ); - ?> + ?>
@@ -148,210 +151,219 @@ public function display_options() {
- anti_card_testing_ip_method; + anti_card_testing_ip_method; + } - foreach($headers as $header) { - if(isset($_SERVER[$header])) { - $ips[] = $_SERVER[$header]; + if (empty($method)) { + $headers = [ + 'HTTP_X_FORWARDED_FOR', + 'HTTP_X_REAL_IP', + 'HTTP_CF_CONNECTING_IP', + ]; + + foreach ($headers as $header) { + if (isset($_SERVER[$header])) { + $ips[] = $_SERVER[$header]; + } + } + } elseif (isset($_SERVER[$method])) { + $ips[] = $_SERVER[$method]; } - } - } - elseif(isset($_SERVER[$method])) { - $ips[] = $_SERVER[$method]; - } - $ips[] = $connection_ip; + $ips[] = $connection_ip; - $ip = self::get_client_ip_from_ips($ips); + $ip = self::get_client_ip_from_ips($ips); - if(is_null($ip)) { - $ip = $connection_ip; + if (is_null($ip)) { + $ip = $connection_ip; + } + + return apply_filters('mepr_anti_card_testing_ip', $ip); } - return apply_filters('mepr_anti_card_testing_ip', $ip); - } - - /** - * Get the first valid public IP from the given array - * - * @param array $ips The array of IP addresses to check - * @return string|null - */ - private static function get_client_ip_from_ips($ips) { - foreach($ips as $ip) { - $skip_to_next = false; - - foreach(array(',', ' ', "\t") as $char) { - if(strpos($ip, $char) !== false) { - $parts = explode($char, $ip); - $parts = array_reverse($parts); - - foreach($parts as $part) { - $part = trim($part); - - if(self::is_valid_ip_address($part) && !self::is_private_ip_address($part)) { - return $part; + /** + * Get the first valid public IP from the given array + * + * @param array $ips The array of IP addresses to check + * @return string|null + */ + private static function get_client_ip_from_ips($ips) + { + foreach ($ips as $ip) { + $skip_to_next = false; + + foreach ([',', ' ', "\t"] as $char) { + if (strpos($ip, $char) !== false) { + $parts = explode($char, $ip); + $parts = array_reverse($parts); + + foreach ($parts as $part) { + $part = trim($part); + + if (self::is_valid_ip_address($part) && !self::is_private_ip_address($part)) { + return $part; + } + } + + $skip_to_next = true; + break; + } } - } - $skip_to_next = true; - break; - } - } + if ($skip_to_next) { + continue; // this one had a delimiter and we didn't find anything + } - if($skip_to_next) { - continue; // this one had a delimiter and we didn't find anything - } + if (self::is_valid_ip_address($ip) && !self::is_private_ip_address($ip)) { + return $ip; + } + } - if(self::is_valid_ip_address($ip) && !self::is_private_ip_address($ip)) { - return $ip; - } + return null; } - return null; - } - - /** - * Is the given IP address valid? - * - * @param string $ip - * @return bool - */ - private static function is_valid_ip_address($ip) { - return filter_var($ip, FILTER_VALIDATE_IP) !== false; - } - - /** - * Is the given IP address private? - * - * @param string $ip - * @return bool - */ - private static function is_private_ip_address($ip) { - return filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_IPV6) !== false - && filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_IPV6 | FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) === false; - } - - public function record_payment_failure($ip) { - $mepr_options = MeprOptions::fetch(); - - if(!$mepr_options->anti_card_testing_enabled) { - return; + /** + * Is the given IP address valid? + * + * @param string $ip + * @return boolean + */ + private static function is_valid_ip_address($ip) + { + return filter_var($ip, FILTER_VALIDATE_IP) !== false; } - if(self::is_valid_ip_address($ip) && !self::is_private_ip_address($ip)) { - $failed = (int) get_transient("mepr_failed_payments_$ip"); - set_transient("mepr_failed_payments_$ip", $failed + 1, MeprHooks::apply_filters('mepr_card_testing_timeframe', 2 * HOUR_IN_SECONDS)); + /** + * Is the given IP address private? + * + * @param string $ip + * @return boolean + */ + private static function is_private_ip_address($ip) + { + return filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_IPV6) !== false + && filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_IPV6 | FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) === false; } - } - public function maybe_block_confirm_payment() { - $this->maybe_block_ip(); + public function record_payment_failure($ip) + { + $mepr_options = MeprOptions::fetch(); + + if (!$mepr_options->anti_card_testing_enabled) { + return; + } - if($this->is_ip_blocked()) { - wp_send_json(array( - 'error' => __('We are not able to complete your purchase at this time. Please contact us for more information.', 'memberpress') - )); + if (self::is_valid_ip_address($ip) && !self::is_private_ip_address($ip)) { + $failed = (int) get_transient("mepr_failed_payments_$ip"); + set_transient("mepr_failed_payments_$ip", $failed + 1, MeprHooks::apply_filters('mepr_card_testing_timeframe', 2 * HOUR_IN_SECONDS)); + } } - } - public function maybe_block_create_checkout_session() { - $this->maybe_block_ip(); + public function maybe_block_confirm_payment() + { + $this->maybe_block_ip(); - if($this->is_ip_blocked()) { - wp_send_json(array( - 'error' => __('We are not able to complete your purchase at this time. Please contact us for more information.', 'memberpress') - )); - } - } - - /** - * Is the current IP address blocked? - * - * @return bool - */ - public function is_ip_blocked() { - $mepr_options = MeprOptions::fetch(); - - if(!$mepr_options->anti_card_testing_enabled) { - return false; + if ($this->is_ip_blocked()) { + wp_send_json([ + 'error' => __('We are not able to complete your purchase at this time. Please contact us for more information.', 'memberpress'), + ]); + } } - $ip = self::get_ip(); + public function maybe_block_create_checkout_session() + { + $this->maybe_block_ip(); - if($ip && !self::is_private_ip_address($ip)) { - $blocked_ips = $mepr_options->anti_card_testing_blocked; + if ($this->is_ip_blocked()) { + wp_send_json([ + 'error' => __('We are not able to complete your purchase at this time. Please contact us for more information.', 'memberpress'), + ]); + } + } - if(!is_array($blocked_ips)) { - $blocked_ips = array(); - } + /** + * Is the current IP address blocked? + * + * @return boolean + */ + public function is_ip_blocked() + { + $mepr_options = MeprOptions::fetch(); + + if (!$mepr_options->anti_card_testing_enabled) { + return false; + } - return in_array($ip, $blocked_ips, true); - } + $ip = self::get_ip(); + + if ($ip && !self::is_private_ip_address($ip)) { + $blocked_ips = $mepr_options->anti_card_testing_blocked; - return false; - } + if (!is_array($blocked_ips)) { + $blocked_ips = []; + } - /** - * Block the current IP address if there have been too many failed payment attempts - */ - protected function maybe_block_ip() { - $mepr_options = MeprOptions::fetch(); + return in_array($ip, $blocked_ips, true); + } - if(!$mepr_options->anti_card_testing_enabled) { - return; + return false; } - $ip = self::get_ip(); + /** + * Block the current IP address if there have been too many failed payment attempts + */ + protected function maybe_block_ip() + { + $mepr_options = MeprOptions::fetch(); + + if (!$mepr_options->anti_card_testing_enabled) { + return; + } - if($ip && !self::is_private_ip_address($ip)) { - $failed = (int) get_transient("mepr_failed_payments_$ip"); - $blocked_ips = $mepr_options->anti_card_testing_blocked; + $ip = self::get_ip(); - if(!is_array($blocked_ips)) { - $blocked_ips = array(); - } + if ($ip && !self::is_private_ip_address($ip)) { + $failed = (int) get_transient("mepr_failed_payments_$ip"); + $blocked_ips = $mepr_options->anti_card_testing_blocked; - // If there have been 5 or more failed payments, add to permanently banned IPs - if($failed >= MeprHooks::apply_filters('mepr_card_testing_failure_limit', 5) && !in_array($ip, $blocked_ips, true)) { - $blocked_ips[] = $ip; - $mepr_options->anti_card_testing_blocked = $blocked_ips; - $mepr_options->store(false); - } - } - } + if (!is_array($blocked_ips)) { + $blocked_ips = []; + } - public function get_detected_ip_ajax() { - if(!MeprUtils::is_logged_in_and_an_admin() || !isset($_GET['method']) || !is_string($_GET['method'])) { - wp_send_json_error(); + // If there have been 5 or more failed payments, add to permanently banned IPs + if ($failed >= MeprHooks::apply_filters('mepr_card_testing_failure_limit', 5) && !in_array($ip, $blocked_ips, true)) { + $blocked_ips[] = $ip; + $mepr_options->anti_card_testing_blocked = $blocked_ips; + $mepr_options->store(false); + } + } } - $valid_methods = array('', 'REMOTE_ADDR', 'HTTP_X_FORWARDED_FOR', 'HTTP_X_REAL_IP', 'HTTP_CF_CONNECTING_IP'); - $method = in_array($_GET['method'], $valid_methods, true) ? $_GET['method'] : ''; + public function get_detected_ip_ajax() + { + if (!MeprUtils::is_logged_in_and_an_admin() || !isset($_GET['method']) || !is_string($_GET['method'])) { + wp_send_json_error(); + } - wp_send_json_success(self::get_ip($method)); - } + $valid_methods = ['', 'REMOTE_ADDR', 'HTTP_X_FORWARDED_FOR', 'HTTP_X_REAL_IP', 'HTTP_CF_CONNECTING_IP']; + $method = in_array($_GET['method'], $valid_methods, true) ? $_GET['method'] : ''; + + wp_send_json_success(self::get_ip($method)); + } } diff --git a/app/controllers/MeprApiCtrl.php b/app/controllers/MeprApiCtrl.php index 5ddfe09..38fcc5b 100644 --- a/app/controllers/MeprApiCtrl.php +++ b/app/controllers/MeprApiCtrl.php @@ -1,218 +1,245 @@ user_auth(); - $mepr_user = new MeprUser($user->ID); - - $txns = $mepr_user->active_product_subscriptions('transactions'); - - $filename = "{$mepr_user->user_login}-" . gmdate('Ymd'); - - $struct = array(); - $struct['user'] = (array)$mepr_user->rec; - $struct['transactions'] = array(); - $struct['timestamp'] = gmdate('Y-m-d H:i:s'); - - foreach($txns as $txn) { - $txn_struct = (array)$txn->rec; - unset($txn_struct['response']); // exclude for security - unset($txn_struct['gateway']); // exclude for security - unset($txn_struct['user_id']); // redundant - $struct['transactions'][] = $txn_struct; + public function load_hooks() + { + add_action('wp_ajax_nopriv_mepr_user', [$this, 'user']); + add_action('wp_ajax_mepr_user', [$this, 'user']); } - if(!isset($_REQUEST['fmt']) or $_REQUEST['fmt']=='json') - $this->render_json($struct,$filename); - else if($_REQUEST['fmt']=='xml') - $this->render_xml($struct,$filename); - else if($_REQUEST['fmt']=='csv') { - $csv_struct = array(); - - if(empty($txns)) { - $csv_struct[] = array_keys($struct['user']); - $csv_struct[] = array_values($struct['user']); - } + /** + * GET user information + */ + public function user() + { + $user = $this->user_auth(); + $mepr_user = new MeprUser($user->ID); + + $txns = $mepr_user->active_product_subscriptions('transactions'); + + $filename = "{$mepr_user->user_login}-" . gmdate('Ymd'); + + $struct = []; + $struct['user'] = (array)$mepr_user->rec; + $struct['transactions'] = []; + $struct['timestamp'] = gmdate('Y-m-d H:i:s'); + + foreach ($txns as $txn) { + $txn_struct = (array)$txn->rec; + unset($txn_struct['response']); // exclude for security + unset($txn_struct['gateway']); // exclude for security + unset($txn_struct['user_id']); // redundant + $struct['transactions'][] = $txn_struct; + } - $txn_headers = array_keys( $struct['transactions'][0] ); + if (!isset($_REQUEST['fmt']) or $_REQUEST['fmt'] == 'json') { + $this->render_json($struct, $filename); + } elseif ($_REQUEST['fmt'] == 'xml') { + $this->render_xml($struct, $filename); + } elseif ($_REQUEST['fmt'] == 'csv') { + $csv_struct = []; + + if (empty($txns)) { + $csv_struct[] = array_keys($struct['user']); + $csv_struct[] = array_values($struct['user']); + } + + $txn_headers = array_keys($struct['transactions'][0]); + + foreach ($txn_headers as $i => $val) { + $txn_headers[$i] = "transaction_{$val}"; + } + + $csv_struct[] = array_merge( + array_keys($struct['user']), + $txn_headers, + ['timestamp'] + ); + + foreach ($struct['transactions'] as $txn) { + $csv_struct[] = array_merge( + array_values($struct['user']), + array_values($txn), + [$struct['timestamp']] + ); + } + + $this->render_csv($csv_struct, $filename); + } + } - foreach($txn_headers as $i => $val) - $txn_headers[$i] = "transaction_{$val}"; + protected function user_auth() + { + if (!isset($_SERVER['PHP_AUTH_USER'])) { + $this->unauthorized(__('No credentials have been provided.', 'memberpress')); + } else { + $user = wp_authenticate($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW']); - $csv_struct[] = array_merge( array_keys( $struct['user'] ), - $txn_headers, - array( 'timestamp' ) ); + if (is_wp_error($user)) { + $this->unauthorized($user->get_error_message()); + } - foreach($struct['transactions'] as $txn) - $csv_struct[] = array_merge( array_values( $struct['user'] ), - array_values( $txn ), - array( $struct['timestamp'] ) ); + return $user; + } + } - $this->render_csv($csv_struct,$filename); + protected function unauthorized($message) + { + header('WWW-Authenticate: Basic realm="' . MeprUtils::blogname() . '"'); + header('HTTP/1.0 401 Unauthorized'); + die(sprintf(__('UNAUTHORIZED: %s', 'memberpress'), $message)); } - } - protected function user_auth() { - if(!isset($_SERVER['PHP_AUTH_USER'])) - $this->unauthorized(__('No credentials have been provided.', 'memberpress')); - else { - $user = wp_authenticate($_SERVER['PHP_AUTH_USER'],$_SERVER['PHP_AUTH_PW']); + protected function render_json($struct, $filename = '') + { + header('Content-Type: text/json'); - if(is_wp_error($user)) - $this->unauthorized( $user->get_error_message() ); + if (!$this->is_debug() and !empty($filename)) { + header("Content-Disposition: attachment; filename=\"{$filename}.json\""); + } - return $user; + die(json_encode($struct)); } - } - protected function unauthorized($message) { - header('WWW-Authenticate: Basic realm="' . MeprUtils::blogname() . '"'); - header('HTTP/1.0 401 Unauthorized'); - die(sprintf(__('UNAUTHORIZED: %s', 'memberpress'),$message)); - } + protected function render_xml($struct, $filename = '') + { + header('Content-Type: text/xml'); - protected function render_json($struct,$filename='') { - header('Content-Type: text/json'); + if (!$this->is_debug() and !empty($filename)) { + header("Content-Disposition: attachment; filename=\"{$filename}.xml\""); + } - if(!$this->is_debug() and !empty($filename)) - header("Content-Disposition: attachment; filename=\"{$filename}.json\""); + die($this->to_xml($struct)); + } - die(json_encode($struct)); - } + protected function render_csv($struct, $filename = '') + { + if (!$this->is_debug()) { + header('Content-Type: text/csv'); - protected function render_xml($struct,$filename='') { - header('Content-Type: text/xml'); + if (!empty($filename)) { + header("Content-Disposition: attachment; filename=\"{$filename}.csv\""); + } + } - if(!$this->is_debug() and !empty($filename)) - header("Content-Disposition: attachment; filename=\"{$filename}.xml\""); + header('Content-Type: text/plain'); - die($this->to_xml($struct)); - } + die($this->to_csv($struct)); + } - protected function render_csv($struct,$filename='') { - if(!$this->is_debug()) { - header('Content-Type: text/csv'); + /** + * The main function for converting to an XML document. + * Pass in a multi dimensional array and this recrusively loops through and builds up an XML document. + * + * @param array $data + * @param string $root_node_name - what you want the root node to be - defaultsto data. + * @param SimpleXMLElement $xml - should only be used recursively + * @return string XML + */ + protected function to_xml($data, $root_node_name = 'memberpressData', $xml = null, $parent_node_name = '') + { + // turn off compatibility mode as simple xml throws a wobbly if you don't. + // DEPRECATED IN PHP 5.3 + // if(ini_get('zend.ze1_compatibility_mode') == 1) + // ini_set('zend.ze1_compatibility_mode', 0); + if (is_null($xml)) { + $xml = simplexml_load_string("<{$root_node_name} />"); + } - if(!empty($filename)) - header("Content-Disposition: attachment; filename=\"{$filename}.csv\""); - } + // loop through the data passed in. + foreach ($data as $key => $value) { + // no numeric keys in our xml please! + if (is_numeric($key)) { + if (empty($parent_node_name)) { + $key = 'unknownNode_' . (string)$key; // make string key... + } else { + $key = preg_replace('/s$/', '', $parent_node_name); // We assume that there's an 's' at the end of the string? + } + } + + // replace anything not alpha numeric + // $key = preg_replace('/[^a-z]/i', '', $key); + $key = $this->camelize($key); + + // if there is another array found recrusively call this function + if (is_array($value)) { + $node = $xml->addChild($key); + // recrusive call. + $this->to_xml($value, $root_node_name, $node, $key); + } else { + // add single node. + $value = htmlentities($value); + $xml->addChild($key, $value); + } + } - header('Content-Type: text/plain'); - - die($this->to_csv($struct)); - } - - /** - * The main function for converting to an XML document. - * Pass in a multi dimensional array and this recrusively loops through and builds up an XML document. - * - * @param array $data - * @param string $root_node_name - what you want the root node to be - defaultsto data. - * @param SimpleXMLElement $xml - should only be used recursively - * @return string XML - */ - protected function to_xml($data, $root_node_name='memberpressData', $xml=null, $parent_node_name='') { - // turn off compatibility mode as simple xml throws a wobbly if you don't. - //DEPRECATED IN PHP 5.3 - // if(ini_get('zend.ze1_compatibility_mode') == 1) - // ini_set('zend.ze1_compatibility_mode', 0); - - if(is_null($xml)) - $xml = simplexml_load_string("<{$root_node_name} />"); - - // loop through the data passed in. - foreach( $data as $key => $value ) { - // no numeric keys in our xml please! - if( is_numeric( $key ) ) { - if( empty( $parent_node_name ) ) - $key = "unknownNode_". (string)$key; // make string key... - else - $key = preg_replace( '/s$/', '', $parent_node_name ); // We assume that there's an 's' at the end of the string? - } - - // replace anything not alpha numeric - //$key = preg_replace('/[^a-z]/i', '', $key); - $key = $this->camelize( $key ); - - // if there is another array found recrusively call this function - if(is_array($value)) { - $node = $xml->addChild($key); - // recrusive call. - $this->to_xml($value, $root_node_name, $node, $key); - } - else { - // add single node. - $value = htmlentities($value); - $xml->addChild($key,$value); - } + // pass back as string. or simple xml object if you want! + return $xml->asXML(); } - // pass back as string. or simple xml object if you want! - return $xml->asXML(); - } - - /** - * Formats a line (passed as a fields array) as CSV and returns the CSV as a string. - * Adapted from http://us3.php.net/manual/en/function.fputcsv.php#87120 - */ - public function to_csv( $struct, - $delimiter = ',', - $enclosure = '"', - $enclose_all = false, - $null_to_mysql_null = false ) { - $delimiter_esc = preg_quote($delimiter, '/'); - $enclosure_esc = preg_quote($enclosure, '/'); - - $csv = ''; - $line_num = 0; - foreach( $struct as $line ) { - $output = array(); - - foreach( $line as $field ) { - if( is_null($field) and $null_to_mysql_null ) { - $output[] = 'NULL'; - continue; + /** + * Formats a line (passed as a fields array) as CSV and returns the CSV as a string. + * Adapted from http://us3.php.net/manual/en/function.fputcsv.php#87120 + */ + public function to_csv( + $struct, + $delimiter = ',', + $enclosure = '"', + $enclose_all = false, + $null_to_mysql_null = false + ) { + $delimiter_esc = preg_quote($delimiter, '/'); + $enclosure_esc = preg_quote($enclosure, '/'); + + $csv = ''; + $line_num = 0; + foreach ($struct as $line) { + $output = []; + + foreach ($line as $field) { + if (is_null($field) and $null_to_mysql_null) { + $output[] = 'NULL'; + continue; + } + + // Enclose fields containing $delimiter, $enclosure or whitespace + if ($enclose_all or preg_match("/(?:{$delimiter_esc}|{$enclosure_esc}|\s)/", $field)) { + $output[] = $enclosure . str_replace($enclosure, $enclosure . $enclosure, $field) . $enclosure; + } else { + $output[] = $field; + } + } + + $csv .= implode($delimiter, $output) . "\n"; + $line_num++; } - // Enclose fields containing $delimiter, $enclosure or whitespace - if( $enclose_all or preg_match( "/(?:{$delimiter_esc}|{$enclosure_esc}|\s)/", $field ) ) - $output[] = $enclosure . str_replace($enclosure, $enclosure . $enclosure, $field) . $enclosure; - else - $output[] = $field; - } + return $csv; + } - $csv .= implode( $delimiter, $output ) . "\n"; - $line_num++; + protected function camelize($str) + { + // Level the playing field + $str = strtolower($str); + // Replace dashes and/or underscores with spaces to prepare for ucwords + $str = preg_replace('/[-_]/', ' ', $str); + // Ucwords bro ... uppercase the first letter of every word + $str = ucwords($str); + // Now get rid of the spaces + $str = preg_replace('/ /', '', $str); + // Lowercase the first character of the string + $str[0] = strtolower($str[0]); + + return $str; } - return $csv; - } - - protected function camelize($str) { - // Level the playing field - $str = strtolower($str); - // Replace dashes and/or underscores with spaces to prepare for ucwords - $str = preg_replace('/[-_]/', ' ', $str); - // Ucwords bro ... uppercase the first letter of every word - $str = ucwords($str); - // Now get rid of the spaces - $str = preg_replace('/ /', '', $str); - // Lowercase the first character of the string - $str[0] = strtolower($str[0]); - - return $str; - } - - protected function is_debug() { - return (isset($_REQUEST['debug']) and (int)$_REQUEST['debug']=1); - } + protected function is_debug() + { + return (isset($_REQUEST['debug']) and (int)$_REQUEST['debug'] = 1); + } } - diff --git a/app/controllers/MeprAppCtrl.php b/app/controllers/MeprAppCtrl.php index 76bae7f..eeb58f8 100644 --- a/app/controllers/MeprAppCtrl.php +++ b/app/controllers/MeprAppCtrl.php @@ -1,1282 +1,1427 @@ 0 ) { - foreach( $notifications as $key => $notification ) { - if ( - ( ! empty( $notification['start'] ) && strtotime( $notification['start'] . ' America/New_York' ) > strtotime( date('F j, Y') . ' America/New_York' ) ) || - ( ! empty( $notification['end'] ) && strtotime( $notification['end'] . ' America/New_York' ) < strtotime( date('F j, Y') . ' America/New_York' ) ) - ) { - unset( $notifications[ $key ] ); - } - } - } - ?> + +if (!defined('ABSPATH')) { + die('You are not allowed to call this page directly.'); +} + +class MeprAppCtrl extends MeprBaseCtrl +{ + public function load_hooks() + { + add_action('manage_posts_custom_column', 'MeprAppCtrl::custom_columns', 100, 2); + add_action('manage_pages_custom_column', 'MeprAppCtrl::custom_columns', 100, 2); + add_action('registered_post_type', 'MeprAppCtrl::setup_columns', 10, 2); + add_filter('the_content', 'MeprAppCtrl::page_route', 100); + add_action('wp_enqueue_scripts', 'MeprAppCtrl::load_scripts', 1); + add_action('admin_enqueue_scripts', 'MeprAppCtrl::load_admin_scripts', 1); + add_action('init', 'MeprAppCtrl::parse_standalone_request', 10); + add_action('wp_dashboard_setup', 'MeprAppCtrl::add_dashboard_widgets'); + add_filter('custom_menu_order', '__return_true'); + add_filter('menu_order', 'MeprAppCtrl::admin_menu_order'); + add_filter('menu_order', 'MeprAppCtrl::admin_submenu_order'); + add_action('widgets_init', 'MeprAppCtrl::register_global_widget_area'); + add_action('widgets_init', 'MeprAccountLinksWidget::register_widget'); + add_action('widgets_init', 'MeprLoginWidget::register_widget'); + add_action('widgets_init', 'MeprSubscriptionsWidget::register_widget'); + add_action('add_meta_boxes', 'MeprAppCtrl::add_meta_boxes', 10, 2); + add_action('save_post', 'MeprAppCtrl::save_meta_boxes'); + add_action('admin_notices', 'MeprAppCtrl::protected_notice'); + add_action('admin_notices', 'MeprAppCtrl::php_min_version_check'); + add_action('admin_notices', 'MeprAppCtrl::maybe_show_get_started_notice'); + add_action('wp_ajax_mepr_dismiss_notice', 'MeprAppCtrl::dismiss_notice'); + add_action('wp_ajax_mepr_dismiss_global_notice', 'MeprAppCtrl::dismiss_global_notice'); + add_action('wp_ajax_mepr_dismiss_daily_notice', 'MeprAppCtrl::dismiss_daily_notice'); + add_action('wp_ajax_mepr_dismiss_weekly_notice', 'MeprAppCtrl::dismiss_weekly_notice'); + add_action('wp_ajax_mepr_todays_date', 'MeprAppCtrl::todays_date'); + add_action('wp_ajax_mepr_close_about_notice', 'MeprAppCtrl::close_about_notice'); + add_action('admin_init', 'MeprAppCtrl::append_mp_privacy_policy'); + add_filter('embed_oembed_html', 'MeprAppCtrl::wrap_oembed_html', 99); + add_action('in_admin_header', 'MeprAppCtrl::mp_admin_header', 0); + + // add_action('wp_ajax_mepr_load_css', 'MeprAppCtrl::load_css'); + // add_action('wp_ajax_nopriv_mepr_load_css', 'MeprAppCtrl::load_css'); + add_action('plugins_loaded', 'MeprAppCtrl::load_css'); + + // Load language - must be done after plugins are loaded to work with PolyLang/WPML + add_action('plugins_loaded', 'MeprAppCtrl::load_language'); + add_action('init', [$this, 'load_translations']); + + add_filter('months_dropdown_results', [$this, 'cleanup_list_table_month_dropdown'], 10, 2); + + // Integrate with WP Debugging plugin - https://github.com/afragen/wp-debugging/issues/6 + add_filter('wp_debugging_add_constants', 'MeprAppCtrl::integrate_wp_debugging'); + + // show MemberPress as active menu item when support admin page is selected + add_filter('mp_hidden_submenu', 'MeprAppCtrl::highlight_parent_menu'); + + add_action('activated_plugin', 'MeprAppCtrl::activated_plugin'); + } + + public static function mp_admin_header() + { + global $current_screen; + + if (MeprUtils::is_memberpress_admin_page()) { + $option = get_option('mepr_notifications'); + $notifications = (is_array($option) && ! empty($option)) ? $option['feed'] : []; + if (MeprNotifications::has_access() && ! empty($notifications) && count($notifications) > 0) { + foreach ($notifications as $key => $notification) { + if ( + ( ! empty($notification['start']) && strtotime($notification['start'] . ' America/New_York') > strtotime(date('F j, Y') . ' America/New_York') ) || + ( ! empty($notification['end']) && strtotime($notification['end'] . ' America/New_York') < strtotime(date('F j, Y') . ' America/New_York') ) + ) { + unset($notifications[ $key ]); + } + } + } + ?>
- +
- ' . $cached_html . ''; - } - - public static function add_meta_boxes($post_type, $post) { - $mepr_options = MeprOptions::fetch(); - - if(!isset($post->ID) || $post->ID == $mepr_options->login_page_id) { return; } - - $screens = array_merge( array_keys(get_post_types(array("public" => true, "_builtin" => false))), - array('post', 'page') ); - - // This meta box shouldn't appear on the new/edit membership screen - $pos = array_search(MeprProduct::$cpt, $screens); - if(isset($screens[$pos])) { unset($screens[$pos]); } - - $rules = MeprRule::get_rules($post); - - foreach($screens as $screen) { - if( MeprGroup::$cpt == $screen ) { - add_meta_box( 'mepr_unauthorized_message', - __('MemberPress Unauthorized Access on the Group Pricing Page', 'memberpress'), - 'MeprAppCtrl::unauthorized_meta_box', - $screen ); - if(!empty($rules)) { - add_meta_box( 'mepr_rules', - __('This Group Pricing Page is Protected', 'memberpress'), - 'MeprAppCtrl::rules_meta_box', - $screen, 'normal', 'high' ); - } - } - elseif( in_array( $screen, array( 'post', 'page' ) ) ) { - add_meta_box( 'mepr_unauthorized_message', - __('MemberPress Unauthorized Access', 'memberpress'), - 'MeprAppCtrl::unauthorized_meta_box', - $screen ); - if(!empty($rules)) { - $obj = get_post_type_object( $screen ); - add_meta_box( 'mepr_rules', - sprintf( __('This %s is Protected', 'memberpress'), - $obj->labels->singular_name ), + + // Fix for Elementor page builder and our static the_content caching + // Elementor runs the_content filter on each video embed, our the_content static caching + // caused the same video to load for all instances of a video on a page as a result + public static function wrap_oembed_html($cached_html) + { + $length = rand(1, 100); // Random length, this is the key to all of this + $class = substr(str_shuffle(str_repeat($x = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ', ceil($length / strlen($x)))), 1, $length); + return '' . $cached_html . ''; + } + + public static function add_meta_boxes($post_type, $post) + { + $mepr_options = MeprOptions::fetch(); + + if (!isset($post->ID) || $post->ID == $mepr_options->login_page_id) { + return; + } + + $screens = array_merge( + array_keys(get_post_types([ + 'public' => true, + '_builtin' => false, + ])), + ['post', 'page'] + ); + + // This meta box shouldn't appear on the new/edit membership screen + $pos = array_search(MeprProduct::$cpt, $screens); + if (isset($screens[$pos])) { + unset($screens[$pos]); + } + + $rules = MeprRule::get_rules($post); + + foreach ($screens as $screen) { + if (MeprGroup::$cpt == $screen) { + add_meta_box( + 'mepr_unauthorized_message', + __('MemberPress Unauthorized Access on the Group Pricing Page', 'memberpress'), + 'MeprAppCtrl::unauthorized_meta_box', + $screen + ); + if (!empty($rules)) { + add_meta_box( + 'mepr_rules', + __('This Group Pricing Page is Protected', 'memberpress'), + 'MeprAppCtrl::rules_meta_box', + $screen, + 'normal', + 'high' + ); + } + } elseif (in_array($screen, ['post', 'page'])) { + add_meta_box( + 'mepr_unauthorized_message', + __('MemberPress Unauthorized Access', 'memberpress'), + 'MeprAppCtrl::unauthorized_meta_box', + $screen + ); + if (!empty($rules)) { + $obj = get_post_type_object($screen); + add_meta_box( + 'mepr_rules', + sprintf( + __('This %s is Protected', 'memberpress'), + $obj->labels->singular_name + ), 'MeprAppCtrl::rules_meta_box', - $screen, 'normal', 'high' ); - } - } - else { - $obj = get_post_type_object( $screen ); - add_meta_box( 'mepr_unauthorized_message', - sprintf( __('MemberPress Unauthorized Access to this %s', 'memberpress'), - $obj->labels->singular_name ), - 'MeprAppCtrl::unauthorized_meta_box', - $screen ); - if(!empty($rules)) { - add_meta_box( 'mepr_rules', - sprintf( __('This %s is Protected', 'memberpress'), - $obj->labels->singular_name ), + $screen, + 'normal', + 'high' + ); + } + } else { + $obj = get_post_type_object($screen); + add_meta_box( + 'mepr_unauthorized_message', + sprintf( + __('MemberPress Unauthorized Access to this %s', 'memberpress'), + $obj->labels->singular_name + ), + 'MeprAppCtrl::unauthorized_meta_box', + $screen + ); + if (!empty($rules)) { + add_meta_box( + 'mepr_rules', + sprintf( + __('This %s is Protected', 'memberpress'), + $obj->labels->singular_name + ), 'MeprAppCtrl::rules_meta_box', - $screen, 'normal', 'high' ); - } - } - } - } - - public static function custom_columns($column, $post_id) { - $post = get_post($post_id); - if( $column=="mepr-access" ) { - $access_list = MeprRule::get_access_list($post); - if(empty($access_list)) { - ?>
$access_values ) { - if($access_key == 'membership') { - foreach($access_values as $product_id) { - $product = new MeprProduct($product_id); - if(!is_null($product->ID)) { - $display_access_list[] = stripslashes($product->post_title); - } + $screen, + 'normal', + 'high' + ); + } } - } - else { - $display_access_list = array_merge($display_access_list, $access_values); - } } - ?> + } + + public static function custom_columns($column, $post_id) + { + $post = get_post($post_id); + if ($column == 'mepr-access') { + $access_list = MeprRule::get_access_list($post); + if (empty($access_list)) { + ?>
$access_values) { + if ($access_key == 'membership') { + foreach ($access_values as $product_id) { + $product = new MeprProduct($product_id); + if (!is_null($product->ID)) { + $display_access_list[] = stripslashes($product->post_title); + } + } + } else { + $display_access_list = array_merge($display_access_list, $access_values); + } + } + ?>
- +
- post_type) && !empty($post->post_type))) { - if(!empty($_GET['post_type'])) { - $cpt = get_post_type_object($_GET['post_type']); - } - elseif(!empty($post_type)) { - $cpt = get_post_type_object($post_type); - } - elseif(!empty($post->post_type)) { // Try individual post last - $cpt = get_post_type_object($post->post_type); - } - else { - return $columns; // Just give up trying - } - - if(in_array($cpt->name, $except) || !$cpt->public) { - return $columns; - } + + public static function setup_columns($post_type, $args) + { + add_filter('manage_posts_columns', 'MeprAppCtrl::columns'); + add_filter('manage_pages_columns', 'MeprAppCtrl::columns'); + add_filter("manage_edit-{$post_type}_columns", 'MeprAppCtrl::columns'); } - $ak = array_keys($columns); + public static function columns($columns) + { + global $post_type, $post; + + $except = ['attachment', 'memberpressproduct']; + $except = MeprHooks::apply_filters('mepr-hide-cpt-access-column', $except); + + if (isset($_GET['post_type']) || (isset($post_type) && !empty($post_type)) || (isset($post->post_type) && !empty($post->post_type))) { + if (!empty($_GET['post_type'])) { + $cpt = get_post_type_object($_GET['post_type']); + } elseif (!empty($post_type)) { + $cpt = get_post_type_object($post_type); + } elseif (!empty($post->post_type)) { // Try individual post last + $cpt = get_post_type_object($post->post_type); + } else { + return $columns; // Just give up trying + } + + if (in_array($cpt->name, $except) || !$cpt->public) { + return $columns; + } + } + + $ak = array_keys($columns); - MeprUtils::array_splice_assoc($columns, $ak[2], $ak[2], - array("mepr-access" => __("Access", 'memberpress'))); + MeprUtils::array_splice_assoc( + $columns, + $ak[2], + $ak[2], + ['mepr-access' => __('Access', 'memberpress')] + ); - return $columns; - } + return $columns; + } - public static function rules_meta_box() { - global $post; + public static function rules_meta_box() + { + global $post; - $rules = MeprRule::get_rules($post); - $access_list = MeprRule::get_access_list($post); - $product_ids = (isset($access_list['membership']) && !empty($access_list['membership'])) ? $access_list['membership'] : array(); - $members = (isset($access_list['member']) && !empty($access_list['member'])) ? $access_list['member'] : array(); + $rules = MeprRule::get_rules($post); + $access_list = MeprRule::get_access_list($post); + $product_ids = (isset($access_list['membership']) && !empty($access_list['membership'])) ? $access_list['membership'] : []; + $members = (isset($access_list['member']) && !empty($access_list['member'])) ? $access_list['member'] : []; - MeprView::render('/admin/rules/rules_meta_box', get_defined_vars()); - } + MeprView::render('/admin/rules/rules_meta_box', get_defined_vars()); + } - public static function protected_notice() { - global $post, $pagenow; + public static function protected_notice() + { + global $post, $pagenow; - $public_post_types = MeprRule::public_post_types(); + $public_post_types = MeprRule::public_post_types(); - if( 'post.php' != $pagenow or !isset($_REQUEST['action']) or - $_REQUEST['action']!='edit' or !in_array($post->post_type,$public_post_types) ) - { return; } + if ( + 'post.php' != $pagenow or !isset($_REQUEST['action']) or + $_REQUEST['action'] != 'edit' or !in_array($post->post_type, $public_post_types) + ) { + return; + } - $rules = MeprRule::get_rules($post); - $rule_count = count($rules); + $rules = MeprRule::get_rules($post); + $rule_count = count($rules); - $message = '' . - sprintf( _n( 'This Content is Protected by %s MemberPress Access Rule', - 'This Content is Protected by %s MemberPress Access Rules', - $rule_count , 'memberpress'), $rule_count ) . + $message = '' . + sprintf(_n( + 'This Content is Protected by %s MemberPress Access Rule', + 'This Content is Protected by %s MemberPress Access Rules', + $rule_count, + 'memberpress' + ), $rule_count) . '' . ' – ' . __('Click here to view', 'memberpress') . ''; - if(!empty($rules)) { - MeprView::render('/admin/errors', get_defined_vars()); + if (!empty($rules)) { + MeprView::render('/admin/errors', get_defined_vars()); + } } - } - public static function php_min_version_check() { - $current_php_version = phpversion(); - if (version_compare($current_php_version, MEPR_MIN_PHP_VERSION, '<')) { - $message = __('MemberPress: Your PHP version (%s) is out of date! ' . + public static function php_min_version_check() + { + $current_php_version = phpversion(); + if (version_compare($current_php_version, MEPR_MIN_PHP_VERSION, '<')) { + $message = __('MemberPress: Your PHP version (%s) is out of date! ' . 'This version has reached official End Of Life and as such may expose your site to security vulnerabilities. ' . 'Please contact your web hosting provider to update to %s or newer', 'memberpress'); - ?> + ?>

- integrations) > 0; - $has_product = MeprProduct::count() > 0; - $has_rule = MeprRule::count() > 0; + $has_payment_method = count($mepr_options->integrations) > 0; + $has_product = MeprProduct::count() > 0; + $has_rule = MeprRule::count() > 0; - // Don't show if a payment method, membership and rule already exist - if($has_payment_method && $has_product && $has_rule) { - return; + // Don't show if a payment method, membership and rule already exist + if ($has_payment_method && $has_product && $has_rule) { + return; + } + + MeprView::render('/admin/get_started', compact('has_payment_method', 'has_product', 'has_rule')); } - MeprView::render('/admin/get_started', compact('has_payment_method', 'has_product', 'has_rule')); - } + public static function dismiss_notice() + { + if (check_ajax_referer('mepr_dismiss_notice', false, false) && isset($_POST['notice']) && is_string($_POST['notice'])) { + $notice = sanitize_key($_POST['notice']); + update_user_meta(get_current_user_id(), "mepr_dismiss_notice_{$notice}", true); + } - public static function dismiss_notice() { - if(check_ajax_referer('mepr_dismiss_notice', false, false) && isset($_POST['notice']) && is_string($_POST['notice'])) { - $notice = sanitize_key($_POST['notice']); - update_user_meta(get_current_user_id(), "mepr_dismiss_notice_{$notice}", true); + wp_send_json_success(); } - wp_send_json_success(); - } + public static function dismiss_global_notice() + { + if (check_ajax_referer('mepr_dismiss_notice', false, false) && isset($_POST['notice']) && is_string($_POST['notice'])) { + $notice = sanitize_key($_POST['notice']); + update_option("mepr_dismiss_notice_{$notice}", true); + } - public static function dismiss_global_notice() { - if(check_ajax_referer('mepr_dismiss_notice', false, false) && isset($_POST['notice']) && is_string($_POST['notice'])) { - $notice = sanitize_key($_POST['notice']); - update_option("mepr_dismiss_notice_{$notice}", true); + wp_send_json_success(); } - wp_send_json_success(); - } + public static function dismiss_daily_notice() + { + if (check_ajax_referer('mepr_dismiss_notice', false, false) && isset($_POST['notice']) && is_string($_POST['notice'])) { + $notice = sanitize_key($_POST['notice']); + set_transient("mepr_dismiss_notice_{$notice}", true, DAY_IN_SECONDS); + } - public static function dismiss_daily_notice() { - if(check_ajax_referer('mepr_dismiss_notice', false, false) && isset($_POST['notice']) && is_string($_POST['notice'])) { - $notice = sanitize_key($_POST['notice']); - set_transient( "mepr_dismiss_notice_{$notice}", true, DAY_IN_SECONDS); + wp_send_json_success(); } - wp_send_json_success(); - } + public static function dismiss_weekly_notice() + { + if (check_ajax_referer('mepr_dismiss_notice', false, false) && isset($_POST['notice']) && is_string($_POST['notice'])) { + $notice = sanitize_key($_POST['notice']); + set_transient("mepr_dismiss_notice_{$notice}", true, WEEK_IN_SECONDS); + } - public static function dismiss_weekly_notice() { - if(check_ajax_referer('mepr_dismiss_notice', false, false) && isset($_POST['notice']) && is_string($_POST['notice'])) { - $notice = sanitize_key($_POST['notice']); - set_transient( "mepr_dismiss_notice_{$notice}", true, WEEK_IN_SECONDS); + wp_send_json_success(); } - wp_send_json_success(); - } + public static function unauthorized_meta_box() + { + global $post; - public static function unauthorized_meta_box() - { - global $post; + $mepr_options = MeprOptions::fetch(); - $mepr_options = MeprOptions::fetch(); + $_wpnonce = wp_create_nonce('mepr_unauthorized'); - $_wpnonce = wp_create_nonce('mepr_unauthorized'); + if (!($unauthorized_message_type = get_post_meta($post->ID, '_mepr_unauthorized_message_type', true))) { + $unauthorized_message_type = 'default'; + } - if(!($unauthorized_message_type = get_post_meta($post->ID, '_mepr_unauthorized_message_type', true))) - $unauthorized_message_type = 'default'; + if (!($unauthorized_message = get_post_meta($post->ID, '_mepr_unauthorized_message', true))) { + $unauthorized_message = ''; + } - if(!($unauthorized_message = get_post_meta($post->ID, '_mepr_unauthorized_message', true))) - $unauthorized_message = ''; + $unauth_excerpt_type = get_post_meta($post->ID, '_mepr_unauth_excerpt_type', true); - $unauth_excerpt_type = get_post_meta($post->ID, '_mepr_unauth_excerpt_type', true); + // Backwards compatibility here people + if ($unauthorized_message_type == 'excerpt') { + $unauthorized_message_type = 'hide'; + if (empty($unauth_excerpt_type)) { + $unauth_excerpt_type = 'show'; + } + } - // Backwards compatibility here people - if($unauthorized_message_type=='excerpt') { - $unauthorized_message_type = 'hide'; - if(empty($unauth_excerpt_type)) { - $unauth_excerpt_type = 'show'; - } - } + if (empty($unauth_excerpt_type)) { + $unauth_excerpt_type = 'default'; + } - if(empty($unauth_excerpt_type)) { - $unauth_excerpt_type = 'default'; - } + $unauth_excerpt_size = get_post_meta($post->ID, '_mepr_unauth_excerpt_size', true); - $unauth_excerpt_size = get_post_meta($post->ID, '_mepr_unauth_excerpt_size', true); + if ($unauth_excerpt_size === '' or !is_numeric($unauth_excerpt_size)) { + $unauth_excerpt_size = 100; + } - if($unauth_excerpt_size === '' or !is_numeric($unauth_excerpt_size)) { - $unauth_excerpt_size = 100; - } + $unauth_login = get_post_meta($post->ID, '_mepr_unauth_login', true); - $unauth_login = get_post_meta($post->ID, '_mepr_unauth_login', true); + if ($unauth_login == '') { + // backwards compatibility + $hide_login = get_post_meta($post->ID, '_mepr_hide_login_form', true); + $unauth_login = (empty($hide_login) ? 'default' : 'show'); + } - if($unauth_login=='') { - // backwards compatibility - $hide_login = get_post_meta($post->ID, '_mepr_hide_login_form', true); - $unauth_login = (empty($hide_login)?'default':'show'); + MeprView::render('/admin/unauthorized_meta_box', get_defined_vars()); } - MeprView::render('/admin/unauthorized_meta_box', get_defined_vars()); - } - - public static function save_meta_boxes($post_id) { - //Verify the Nonce First - if(!isset($_REQUEST['mepr_custom_unauthorized_nonce']) || !wp_verify_nonce($_REQUEST['mepr_custom_unauthorized_nonce'], 'mepr_unauthorized')) { - return $post_id; - } + public static function save_meta_boxes($post_id) + { + // Verify the Nonce First + if (!isset($_REQUEST['mepr_custom_unauthorized_nonce']) || !wp_verify_nonce($_REQUEST['mepr_custom_unauthorized_nonce'], 'mepr_unauthorized')) { + return $post_id; + } - if((defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) || defined('DOING_AJAX')) { - return $post_id; - } + if ((defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) || defined('DOING_AJAX')) { + return $post_id; + } - // First we need to check if the current user is authorized to do this action. - if('page' == $_POST['post_type']) { - if(!current_user_can('edit_page', $post_id)) { return; } - } - else { - if(!current_user_can('edit_post', $post_id)) { return; } - } + // First we need to check if the current user is authorized to do this action. + if ('page' == $_POST['post_type']) { + if (!current_user_can('edit_page', $post_id)) { + return; + } + } else { + if (!current_user_can('edit_post', $post_id)) { + return; + } + } - //if saving in a custom table, get post_ID - $post_ID = $_REQUEST['post_ID']; + // if saving in a custom table, get post_ID + $post_ID = $_REQUEST['post_ID']; - update_post_meta( $post_ID, '_mepr_unauthorized_message_type', $_REQUEST['_mepr_unauthorized_message_type'] ); - update_post_meta( $post_ID, '_mepr_unauthorized_message', $_REQUEST['_mepr_unauthorized_message'] ); - update_post_meta( $post_ID, '_mepr_unauth_login', $_REQUEST['_mepr_unauth_login'] ); - update_post_meta( $post_ID, '_mepr_unauth_excerpt_type', $_REQUEST['_mepr_unauth_excerpt_type'] ); - update_post_meta( $post_ID, '_mepr_unauth_excerpt_size', $_REQUEST['_mepr_unauth_excerpt_size'] ); - } + update_post_meta($post_ID, '_mepr_unauthorized_message_type', $_REQUEST['_mepr_unauthorized_message_type']); + update_post_meta($post_ID, '_mepr_unauthorized_message', $_REQUEST['_mepr_unauthorized_message']); + update_post_meta($post_ID, '_mepr_unauth_login', $_REQUEST['_mepr_unauth_login']); + update_post_meta($post_ID, '_mepr_unauth_excerpt_type', $_REQUEST['_mepr_unauth_excerpt_type']); + update_post_meta($post_ID, '_mepr_unauth_excerpt_size', $_REQUEST['_mepr_unauth_excerpt_size']); + } - public static function highlight_parent_menu($submenu) { - $screen = get_current_screen(); + public static function highlight_parent_menu($submenu) + { + $screen = get_current_screen(); - if($screen->id === 'memberpress-support') { - $submenu = 'memberpress'; + if ($screen->id === 'memberpress-support') { + $submenu = 'memberpress'; + } + return $submenu; } - return $submenu; - } - public static function setup_menus() { - add_action('admin_menu', 'MeprAppCtrl::menu'); - } + public static function setup_menus() + { + add_action('admin_menu', 'MeprAppCtrl::menu'); + } - public static function toplevel_menu_route() { - ?> + public static function toplevel_menu_route() + { + ?> - + public static function toplevel_menu_drm_route() + { + ?> - _x('ReadyLaunch™️ General Footer', 'ui', 'memberpress'), - 'description' => __( 'Widgets in this area will be shown at the bottom of all ReadyLaunch pages.', 'memberpress' ), - 'id' => 'mepr_rl_global_footer', - 'before_widget' => '
', - 'after_widget' => '
', - 'before_title' => '

', - 'after_title' => '

', - ) ); - - register_sidebar( array( - 'name' => _x('ReadyLaunch™️ Account Footer', 'ui', 'memberpress'), - 'description' => __( 'Widgets in this area will be shown at the bottom of ReadyLaunch Account page.', 'memberpress' ), - 'id' => 'mepr_rl_account_footer', - 'before_widget' => '
', - 'after_widget' => '
', - 'before_title' => '

', - 'after_title' => '

', - ) ); - - register_sidebar( array( - 'name' => _x('ReadyLaunch™️ Login Footer', 'ui', 'memberpress'), - 'description' => __( 'Widgets in this area will be shown at the bottom of ReadyLaunch Login page.', 'memberpress' ), - 'id' => 'mepr_rl_login_footer', - 'before_widget' => '
', - 'after_widget' => '
', - 'before_title' => '

', - 'after_title' => '

', - ) ); - - register_sidebar( array( - 'name' => _x('ReadyLaunch™️ Registration Footer', 'ui', 'memberpress'), - 'description' => __( 'Widgets in this area will be shown at the bottom of ReadyLaunch Registration pages.', 'memberpress' ), - 'id' => 'mepr_rl_registration_footer', - 'before_widget' => '
', - 'after_widget' => '
', - 'before_title' => '

', - 'after_title' => '

', - ) ); - } - - // Routes for wordpress pages -- we're just replacing content here folks. - public static function page_route($content) { - $current_post = MeprUtils::get_current_post(); - - //This isn't a post? Just return the content then - if($current_post === false) { return $content; } - - //WARNING the_content CAN be run more than once per page load - //so this static var prevents stuff from happening twice - //like cancelling a subscr or resuming etc... - static $already_run = array(); - static $new_content = array(); - static $content_length = array(); - - //Init this posts static values - if(!isset($new_content[$current_post->ID]) || empty($new_content[$current_post->ID])) { - $already_run[$current_post->ID] = false; - $new_content[$current_post->ID] = ''; - $content_length[$current_post->ID] = -1; + return $menu_order; } - if($already_run[$current_post->ID] && strlen($content) == $content_length[$current_post->ID]) { - return $new_content[$current_post->ID]; + public static function register_global_widget_area() + { + register_sidebar([ + 'name' => _x('ReadyLaunch™️ General Footer', 'ui', 'memberpress'), + 'description' => __('Widgets in this area will be shown at the bottom of all ReadyLaunch pages.', 'memberpress'), + 'id' => 'mepr_rl_global_footer', + 'before_widget' => '
', + 'after_widget' => '
', + 'before_title' => '

', + 'after_title' => '

', + ]); + + register_sidebar([ + 'name' => _x('ReadyLaunch™️ Account Footer', 'ui', 'memberpress'), + 'description' => __('Widgets in this area will be shown at the bottom of ReadyLaunch Account page.', 'memberpress'), + 'id' => 'mepr_rl_account_footer', + 'before_widget' => '
', + 'after_widget' => '
', + 'before_title' => '

', + 'after_title' => '

', + ]); + + register_sidebar([ + 'name' => _x('ReadyLaunch™️ Login Footer', 'ui', 'memberpress'), + 'description' => __('Widgets in this area will be shown at the bottom of ReadyLaunch Login page.', 'memberpress'), + 'id' => 'mepr_rl_login_footer', + 'before_widget' => '
', + 'after_widget' => '
', + 'before_title' => '

', + 'after_title' => '

', + ]); + + register_sidebar([ + 'name' => _x('ReadyLaunch™️ Registration Footer', 'ui', 'memberpress'), + 'description' => __('Widgets in this area will be shown at the bottom of ReadyLaunch Registration pages.', 'memberpress'), + 'id' => 'mepr_rl_registration_footer', + 'before_widget' => '
', + 'after_widget' => '
', + 'before_title' => '

', + 'after_title' => '

', + ]); } - $content_length[$current_post->ID] = strlen($content); - $already_run[$current_post->ID] = true; - - $mepr_options = MeprOptions::fetch(); + // Routes for WordPress pages -- we're just replacing content here folks. + public static function page_route($content) + { + $current_post = MeprUtils::get_current_post(); - switch($current_post->ID) { - case $mepr_options->account_page_id: - if(!MeprUser::manually_place_account_form($current_post)) { - try { - $account_ctrl = MeprCtrlFactory::fetch('account'); - $content = $account_ctrl->display_account_form($content); - } - catch(Exception $e) { - ob_start(); - ?> -
- login_page_id: - ob_start(); - - $action = self::get_param('action'); - $manual_login_form = get_post_meta($current_post->ID, '_mepr_manual_login_form', true); - try { - $login_ctrl = MeprCtrlFactory::fetch('login'); - - if($action and $action == 'forgot_password') { - $login_ctrl->display_forgot_password_form(); - } - else if($action and $action == 'mepr_process_forgot_password') { - $login_ctrl->process_forgot_password_form(); - } - else if($action and $action == 'reset_password') { - $login_ctrl->display_reset_password_form(self::get_param('mkey',''),self::get_param('u','')); - } - else if($action and $action === 'mepr_process_reset_password_form' && isset($_POST['errors']) && !empty($_POST['errors'])) { - $login_ctrl->display_reset_password_form_errors($_POST['errors']); - } - else if(!$manual_login_form || ($manual_login_form && $action == 'mepr_unauthorized')) { - $message = ''; - - if($action and $action == 'mepr_unauthorized') { - $resource = isset($_REQUEST['redirect_to']) ? esc_url(urldecode($_REQUEST['redirect_to'])) : __('the requested resource.','memberpress'); - $unauth_message = $mepr_options->unauthorized_message; - - //Maybe override the message if a page id is set - if(isset($_GET['mepr-unauth-page'])) { - $unauth_post = get_post((int)$_GET['mepr-unauth-page']); - $unauth = MeprRule::get_unauth_settings_for($unauth_post); - $unauth_message = $unauth->message; - } - - $unauth_message = wpautop(MeprHooks::apply_filters('mepr-unauthorized-message', do_shortcode($unauth_message), $current_post)); - - $message = '

' . __('Unauthorized for', 'memberpress') . ': ' . $resource . '

' . $unauth_message; - } - $login_ctrl->display_login_form(false, false, $message); - } + // WARNING the_content CAN be run more than once per page load + // so this static var prevents stuff from happening twice + // like cancelling a subscr or resuming etc... + static $already_run = []; + static $new_content = []; + static $content_length = []; + + // Init this posts static values + if (!isset($new_content[$current_post->ID]) || empty($new_content[$current_post->ID])) { + $already_run[$current_post->ID] = false; + $new_content[$current_post->ID] = ''; + $content_length[$current_post->ID] = -1; } - catch(Exception $e) { - $login_actions = array( - 'forgot_password', - 'mepr_process_forgot_password', - 'reset_password', - 'mepr_process_reset_password_form', - 'mepr_unauthorized' - ); - if($action && in_array($action,$login_actions)) { - ?> -
- ID] && strlen($content) == $content_length[$current_post->ID]) { + return $new_content[$current_post->ID]; } - //Some crazy trickery here to prevent from having to completely rewrite a lot of crap - //This is a fix for https://github.com/Caseproof/memberpress/issues/609 - if(!$manual_login_form || ($action && $action == 'bpnoaccess')) { //BuddyPress fix - $content .= ob_get_clean(); - } - elseif($action) { - $match_str = '#' . preg_quote('') . '.*' . preg_quote('') . '#s'; - //preg_quote below helps fix an issue with the math captcha add-on when using a shortcode for login - $content = stripslashes(preg_replace($match_str, ob_get_clean(), preg_quote($content))); - } - else { //do nothing really - ob_end_clean(); + $content_length[$current_post->ID] = strlen($content); + $already_run[$current_post->ID] = true; + + $mepr_options = MeprOptions::fetch(); + + switch ($current_post->ID) { + case $mepr_options->account_page_id: + if (!MeprUser::manually_place_account_form($current_post)) { + try { + $account_ctrl = MeprCtrlFactory::fetch('account'); + $content = $account_ctrl->display_account_form($content); + } catch (Exception $e) { + ob_start(); + ?> +
+ login_page_id: + ob_start(); + + $action = self::get_param('action'); + $manual_login_form = get_post_meta($current_post->ID, '_mepr_manual_login_form', true); + try { + $login_ctrl = MeprCtrlFactory::fetch('login'); + + if ($action and $action == 'forgot_password') { + $login_ctrl->display_forgot_password_form(); + } elseif ($action and $action == 'mepr_process_forgot_password') { + $login_ctrl->process_forgot_password_form(); + } elseif ($action and $action == 'reset_password') { + $login_ctrl->display_reset_password_form(self::get_param('mkey', ''), self::get_param('u', '')); + } elseif ($action and $action === 'mepr_process_reset_password_form' && isset($_POST['errors']) && !empty($_POST['errors'])) { + $login_ctrl->display_reset_password_form_errors($_POST['errors']); + } elseif (!$manual_login_form || ($manual_login_form && $action == 'mepr_unauthorized')) { + $message = ''; + + if ($action and $action == 'mepr_unauthorized') { + $resource = isset($_REQUEST['redirect_to']) ? esc_url(urldecode($_REQUEST['redirect_to'])) : __('the requested resource.', 'memberpress'); + $unauth_message = $mepr_options->unauthorized_message; + + // Maybe override the message if a page id is set + if (isset($_GET['mepr-unauth-page'])) { + $unauth_post = get_post((int)$_GET['mepr-unauth-page']); + $unauth = MeprRule::get_unauth_settings_for($unauth_post); + $unauth_message = $unauth->message; + } + + $unauth_message = wpautop(MeprHooks::apply_filters('mepr-unauthorized-message', do_shortcode($unauth_message), $current_post)); + + $message = '

' . __('Unauthorized for', 'memberpress') . ': ' . $resource . '

' . $unauth_message; + } + + $login_ctrl->display_login_form(false, false, $message); + } + } catch (Exception $e) { + $login_actions = [ + 'forgot_password', + 'mepr_process_forgot_password', + 'reset_password', + 'mepr_process_reset_password_form', + 'mepr_unauthorized', + ]; + + if ($action && in_array($action, $login_actions)) { + ?> +
+ ') . '.*' . preg_quote('') . '#s'; + // preg_quote below helps fix an issue with the math captcha add-on when using a shortcode for login + $content = stripslashes(preg_replace($match_str, ob_get_clean(), preg_quote($content))); + } else { // do nothing really + ob_end_clean(); + } + break; + case $mepr_options->thankyou_page_id: + $message = MeprProductsCtrl::maybe_get_thank_you_page_message(); + + // If a custom message is set, only show that message + if ($message != '') { + $content = $message; + } + break; } - break; - case $mepr_options->thankyou_page_id: - $message = MeprProductsCtrl::maybe_get_thank_you_page_message(); - // If a custom message is set, only show that message - if($message != '') { $content = $message; } - break; + // See above notes + $new_content[$current_post->ID] = $content; + return $new_content[$current_post->ID]; } - // See above notes - $new_content[$current_post->ID] = $content; - return $new_content[$current_post->ID]; - } + public static function load_scripts() + { + global $post; - public static function load_scripts() { - global $post; + $mepr_options = MeprOptions::fetch(); - $mepr_options = MeprOptions::fetch(); + $is_product_page = ( false !== ( $prd = MeprProduct::is_product_page($post) ) ); + $is_group_page = ( false !== ( $grp = MeprGroup::is_group_page($post) ) ); + $is_login_page = MeprUser::is_login_page($post); + $is_account_page = MeprUser::is_account_page($post); + $global_styles = $mepr_options->global_styles; - $is_product_page = ( false !== ( $prd = MeprProduct::is_product_page($post) ) ); - $is_group_page = ( false !== ( $grp = MeprGroup::is_group_page($post) ) ); - $is_login_page = MeprUser::is_login_page($post); - $is_account_page = MeprUser::is_account_page($post); - $global_styles = $mepr_options->global_styles; + MeprHooks::do_action('mepr_enqueue_scripts', $is_product_page, $is_group_page, $is_account_page); - MeprHooks::do_action('mepr_enqueue_scripts', $is_product_page, $is_group_page, $is_account_page); + // Yeah we enqueue this globally all the time so the login form will work on any page + wp_enqueue_style('mp-theme', MEPR_CSS_URL . '/ui/theme.css', null, MEPR_VERSION); - // Yeah we enqueue this globally all the time so the login form will work on any page - wp_enqueue_style('mp-theme', MEPR_CSS_URL . '/ui/theme.css', null, MEPR_VERSION); + if (($global_styles || $is_account_page) && ! has_block('memberpress/pro-account-tabs')) { + wp_enqueue_style('mp-account-css', MEPR_CSS_URL . '/ui/account.css', null, MEPR_VERSION); + } - if(($global_styles || $is_account_page) && ! has_block('memberpress/pro-account-tabs')) { - wp_enqueue_style('mp-account-css', MEPR_CSS_URL.'/ui/account.css', null, MEPR_VERSION); - } + if ( + $global_styles || + $is_login_page || + has_shortcode(get_the_content(null, false, $post), 'mepr-login-form') || + is_active_widget(false, false, 'mepr_login_widget') || + (!$mepr_options->redirect_on_unauthorized && $mepr_options->unauth_show_login && (isset($post) && is_a($post, 'WP_Post') && MeprRule::is_locked($post) || MeprRule::is_uri_locked(esc_url($_SERVER['REQUEST_URI'])))) + ) { + wp_enqueue_style('dashicons'); + wp_enqueue_style('mp-login-css', MEPR_CSS_URL . '/ui/login.css', null, MEPR_VERSION); - if($global_styles || - $is_login_page || - has_shortcode(get_the_content(null, false, $post) , 'mepr-login-form') || - is_active_widget(false, false, 'mepr_login_widget') || - (!$mepr_options->redirect_on_unauthorized && $mepr_options->unauth_show_login && (isset($post) && is_a($post, 'WP_Post') && MeprRule::is_locked($post) || MeprRule::is_uri_locked(esc_url($_SERVER['REQUEST_URI']))))) { - wp_enqueue_style( 'dashicons' ); - wp_enqueue_style( 'mp-login-css', MEPR_CSS_URL.'/ui/login.css', null, MEPR_VERSION); + wp_register_script('mepr-login-js', MEPR_JS_URL . '/login.js', ['jquery', 'underscore', 'wp-i18n'], MEPR_VERSION); - wp_register_script('mepr-login-js', MEPR_JS_URL.'/login.js', array('jquery', 'underscore', 'wp-i18n'), MEPR_VERSION); + wp_enqueue_script('mepr-login-i18n'); + wp_enqueue_script('mepr-login-js'); + } + + if ($global_styles || $is_product_page || $is_account_page) { + $wp_scripts = new WP_Scripts(); + $ui = $wp_scripts->query('jquery-ui-core'); + $url = "//ajax.googleapis.com/ajax/libs/jqueryui/{$ui->ver}/themes/smoothness/jquery-ui.css"; + wp_enqueue_style('mepr-jquery-ui-smoothness', $url); + wp_enqueue_style('jquery-ui-timepicker-addon', MEPR_CSS_URL . '/jquery-ui-timepicker-addon.css', ['mepr-jquery-ui-smoothness']); + + $popup_ctrl = new MeprPopupCtrl(); + wp_enqueue_style('jquery-magnific-popup', $popup_ctrl->popup_css); + wp_enqueue_script('jquery-magnific-popup', $popup_ctrl->popup_js, ['jquery']); + + $prereqs = MeprHooks::apply_filters('mepr-signup-styles', []); + wp_enqueue_style('mp-signup', MEPR_CSS_URL . '/signup.css', $prereqs, MEPR_VERSION); + + wp_register_script('mepr-timepicker-js', MEPR_JS_URL . '/jquery-ui-timepicker-addon.js', ['jquery-ui-datepicker'], MEPR_VERSION); + wp_register_script('mp-datepicker', MEPR_JS_URL . '/date_picker.js', ['mepr-timepicker-js'], MEPR_VERSION); + + $date_picker_frontend = [ + 'translations' => self::get_datepicker_strings(), + 'timeFormat' => (is_admin()) ? 'HH:mm:ss' : '', + 'dateFormat' => MeprUtils::datepicker_format(get_option('date_format')), + 'showTime' => (is_admin()) ? true : false, + ]; + wp_localize_script('mp-datepicker', 'MeprDatePicker', $date_picker_frontend); + + wp_register_script('jquery.payment', MEPR_JS_URL . '/jquery.payment.js', [], MEPR_VERSION); + wp_register_script('mp-validate', MEPR_JS_URL . '/validate.js', [], MEPR_VERSION); + wp_register_script('mp-i18n', MEPR_JS_URL . '/i18n.js', [], MEPR_VERSION); + + $i18n = [ + 'states' => MeprUtils::states(), + 'ajaxurl' => admin_url('admin-ajax.php'), + ]; + $i18n['please_select_state'] = __('-- Select State --', 'memberpress'); + wp_localize_script('mp-i18n', 'MeprI18n', $i18n); + + $prereqs = MeprHooks::apply_filters( + 'mepr-signup-scripts', + ['jquery','jquery.payment','mp-validate','mp-i18n','mp-datepicker'], + $is_product_page, + $is_account_page + ); + + wp_enqueue_script('mp-signup', MEPR_JS_URL . '/signup.js', $prereqs, MEPR_VERSION); + + $local_data = [ + 'coupon_nonce' => wp_create_nonce('mepr_coupons'), + 'spc_enabled' => ( $mepr_options->enable_spc || $mepr_options->design_enable_checkout_template ), + 'spc_invoice' => ( $mepr_options->enable_spc_invoice || $mepr_options->design_enable_checkout_template ), + 'no_compatible_pms' => __('There are no payment methods available that can purchase this product, please contact the site administrator or purchase it separately.', 'memberpress'), + 'switch_pm_prompt' => __('It looks like your purchase requires %s. No problem! Just click below to switch.', 'memberpress'), + 'switch_pm' => __('Switch to %s', 'memberpress'), + 'cancel' => __('Cancel', 'memberpress'), + 'warning_icon_url' => MEPR_IMAGES_URL . '/mepr-notice-icon-error.png', + ]; + + wp_localize_script('mp-signup', 'MeprSignup', $local_data); + + // For Show hide password + wp_enqueue_style('dashicons'); + wp_enqueue_style('mp-login-css', MEPR_CSS_URL . '/ui/login.css', null, MEPR_VERSION); + + wp_register_script('mepr-login-js', MEPR_JS_URL . '/login.js', ['jquery', 'underscore', 'wp-i18n'], MEPR_VERSION); + + wp_enqueue_script('mepr-login-i18n'); + wp_enqueue_script('mepr-login-js'); + } - wp_enqueue_script('mepr-login-i18n'); - wp_enqueue_script('mepr-login-js'); + if ($global_styles || $is_group_page) { + wp_enqueue_style('mp-plans-css', MEPR_CSS_URL . '/plans.min.css', [], MEPR_VERSION); + } } - if($global_styles || $is_product_page || $is_account_page) { - $wp_scripts = new WP_Scripts(); - $ui = $wp_scripts->query('jquery-ui-core'); - $url = "//ajax.googleapis.com/ajax/libs/jqueryui/{$ui->ver}/themes/smoothness/jquery-ui.css"; - wp_enqueue_style('mepr-jquery-ui-smoothness', $url); - wp_enqueue_style('jquery-ui-timepicker-addon', MEPR_CSS_URL.'/jquery-ui-timepicker-addon.css', array('mepr-jquery-ui-smoothness')); + public static function get_datepicker_strings() + { + return [ + 'closeText' => _x('Done', 'ui', 'memberpress'), + 'currentText' => _x('Today', 'ui', 'memberpress'), + 'monthNamesShort' => [ + _x('Jan', 'ui', 'memberpress'), + _x('Feb', 'ui', 'memberpress'), + _x('Mar', 'ui', 'memberpress'), + _x('Apr', 'ui', 'memberpress'), + _x('May', 'ui', 'memberpress'), + _x('Jun', 'ui', 'memberpress'), + _x('Jul', 'ui', 'memberpress'), + _x('Aug', 'ui', 'memberpress'), + _x('Sep', 'ui', 'memberpress'), + _x('Oct', 'ui', 'memberpress'), + _x('Nov', 'ui', 'memberpress'), + _x('Dec', 'ui', 'memberpress'), + ], + 'dayNamesMin' => [_x('Su', 'ui', 'memberpress'),_x('Mo', 'ui', 'memberpress'),_x('Tu', 'ui', 'memberpress'),_x('We', 'ui', 'memberpress'),_x('Th', 'ui', 'memberpress'),_x('Fr', 'ui', 'memberpress'),_x('Sa', 'ui', 'memberpress')], + ]; + } - $popup_ctrl = new MeprPopupCtrl(); - wp_enqueue_style('jquery-magnific-popup', $popup_ctrl->popup_css); - wp_enqueue_script('jquery-magnific-popup', $popup_ctrl->popup_js, array('jquery')); + public static function load_admin_scripts($hook) + { + global $wp_version; + + $popup_ctrl = new MeprPopupCtrl(); + wp_enqueue_style('jquery-magnific-popup', $popup_ctrl->popup_css); + + wp_register_style( + 'mepr-settings-table-css', + MEPR_CSS_URL . '/settings_table.css', + [], + MEPR_VERSION + ); + wp_enqueue_style( + 'mepr-admin-shared-css', + MEPR_CSS_URL . '/admin-shared.css', + ['wp-pointer','jquery-magnific-popup','mepr-settings-table-css'], + MEPR_VERSION + ); + wp_enqueue_style( + 'mepr-fontello-animation', + MEPR_VENDOR_LIB_URL . '/fontello/css/animation.css', + [], + MEPR_VERSION + ); + wp_enqueue_style( + 'mepr-fontello-memberpress', + MEPR_VENDOR_LIB_URL . '/fontello/css/memberpress.css', + ['mepr-fontello-animation'], + MEPR_VERSION + ); + + // If we're in 3.8 now then use a font for the admin image + if (version_compare($wp_version, '3.8', '>=')) { + wp_enqueue_style( + 'mepr-menu-styles', + MEPR_CSS_URL . '/menu-styles.css', + ['mepr-fontello-memberpress'], + MEPR_VERSION + ); + } - $prereqs = MeprHooks::apply_filters('mepr-signup-styles', array()); - wp_enqueue_style('mp-signup', MEPR_CSS_URL.'/signup.css', $prereqs, MEPR_VERSION); + wp_register_script('jquery-magnific-popup', $popup_ctrl->popup_js, ['jquery']); + wp_enqueue_script('mepr-tooltip', MEPR_JS_URL . '/tooltip.js', ['jquery','wp-pointer','jquery-magnific-popup'], MEPR_VERSION); + wp_localize_script('mepr-tooltip', 'MeprTooltip', [ + 'show_about_notice' => self::show_about_notice(), + 'about_notice' => self::about_notice(), + ]); + wp_register_script('mepr-settings-table-js', MEPR_JS_URL . '/settings_table.js', ['jquery'], MEPR_VERSION); + wp_register_script('mepr-cookie-js', MEPR_JS_URL . '/js.cookie.min.js', [], '2.2.1'); + wp_enqueue_script('mepr-admin-shared-js', MEPR_JS_URL . '/admin_shared.js', ['jquery', 'jquery-magnific-popup', 'mepr-settings-table-js', 'mepr-cookie-js'], MEPR_VERSION); + wp_localize_script('mepr-admin-shared-js', 'MeprAdminShared', [ + 'ajax_url' => admin_url('admin-ajax.php'), + 'dismiss_notice_nonce' => wp_create_nonce('mepr_dismiss_notice'), + 'enable_stripe_tax_nonce' => wp_create_nonce('mepr_enable_stripe_tax'), + ]); + + // Widget in the dashboard stuff + if ($hook == 'index.php') { + $local_data = [ + 'report_nonce' => wp_create_nonce('mepr_reports'), + ]; + wp_enqueue_script('mepr-google-jsapi', 'https://www.gstatic.com/charts/loader.js', [], MEPR_VERSION); + wp_enqueue_script('mepr-widgets-js', MEPR_JS_URL . '/admin_widgets.js', ['jquery', 'mepr-google-jsapi'], MEPR_VERSION, true); + wp_localize_script('mepr-widgets-js', 'MeprWidgetData', $local_data); + wp_enqueue_style('mepr-widgets-css', MEPR_CSS_URL . '/admin-widgets.css', [], MEPR_VERSION); + } + } - wp_register_script('mepr-timepicker-js', MEPR_JS_URL.'/jquery-ui-timepicker-addon.js', array('jquery-ui-datepicker'), MEPR_VERSION); - wp_register_script('mp-datepicker', MEPR_JS_URL.'/date_picker.js', array('mepr-timepicker-js'), MEPR_VERSION); + // The tight way to process standalone requests dogg... + public static function parse_standalone_request() + { + global $user_ID; - $date_picker_frontend = array('translations' => self::get_datepicker_strings(), 'timeFormat' => (is_admin())?'HH:mm:ss':'', 'dateFormat' => MeprUtils::datepicker_format(get_option('date_format')), 'showTime' => (is_admin())?true:false); - wp_localize_script('mp-datepicker', 'MeprDatePicker', $date_picker_frontend); + $plugin = (isset($_REQUEST['plugin'])) ? $_REQUEST['plugin'] : ''; + $action = (isset($_REQUEST['action'])) ? $_REQUEST['action'] : ''; + $controller = (isset($_REQUEST['controller'])) ? $_REQUEST['controller'] : ''; - wp_register_script('jquery.payment', MEPR_JS_URL.'/jquery.payment.js', array(), MEPR_VERSION); - wp_register_script('mp-validate', MEPR_JS_URL.'/validate.js', array(), MEPR_VERSION); - wp_register_script('mp-i18n', MEPR_JS_URL.'/i18n.js', array(), MEPR_VERSION); + $request_uri = $_SERVER['REQUEST_URI']; - $i18n = array('states' => MeprUtils::states(), 'ajaxurl' => admin_url('admin-ajax.php')); - $i18n['please_select_state'] = __('-- Select State --', 'memberpress'); - wp_localize_script('mp-i18n', 'MeprI18n', $i18n); + // Pretty Mepr Notifier ... prevents POST vars from being mangled + $notify_url_pattern = MeprUtils::gateway_notify_url_regex_pattern(); + if (MeprUtils::match_uri($notify_url_pattern, $request_uri, $m)) { + $plugin = 'mepr'; + $_REQUEST['pmt'] = $m[1]; + $action = $m[2]; + } - $prereqs = MeprHooks::apply_filters( - 'mepr-signup-scripts', - array('jquery','jquery.payment','mp-validate','mp-i18n','mp-datepicker'), - $is_product_page, - $is_account_page - ); + try { + if (MeprUtils::is_post_request() && isset($_POST['mepr_process_signup_form'])) { + if ( + MeprUtils::is_user_logged_in() && + isset($_POST['logged_in_purchase']) && + $_POST['logged_in_purchase'] == 1 + ) { + check_admin_referer('logged_in_purchase', 'mepr_checkout_nonce'); + } + + $checkout_ctrl = MeprCtrlFactory::fetch('checkout'); + $checkout_ctrl->process_signup_form(); + } elseif (isset($_POST) && isset($_POST['mepr_process_payment_form'])) { + $checkout_ctrl = MeprCtrlFactory::fetch('checkout'); + $checkout_ctrl->process_payment_form(); + } elseif ($action === 'checkout' && isset($_REQUEST['txn'])) { + $_REQUEST['txn'] = MeprUtils::base36_decode($_REQUEST['txn']); + + // Back button fix + $txn = new MeprTransaction((int)$_REQUEST['txn']); + if (strpos($txn->trans_num, 'mp-txn-') === false || $txn->status != MeprTransaction::$pending_str) { + $prd = new MeprProduct($txn->product_id); + MeprUtils::wp_redirect($prd->url()); + } + + $checkout_ctrl = MeprCtrlFactory::fetch('checkout'); + $checkout_ctrl->display_payment_page(); + } elseif (isset($_POST) && isset($_POST['mepr_process_login_form'])) { + $login_ctrl = MeprCtrlFactory::fetch('login'); + $login_ctrl->process_login_form(); + } elseif ( + MeprUtils::is_post_request() && $plugin == 'mepr' && $action == 'updatepassword' && + isset($_POST['mepr-new-password']) && isset($_POST['mepr-confirm-password']) + ) { + check_admin_referer('update_password', 'mepr_account_nonce'); + $account_ctrl = MeprCtrlFactory::fetch('account'); + $account_ctrl->save_new_password($user_ID, $_POST['mepr-new-password'], $_POST['mepr-confirm-password']); + } elseif (!empty($plugin) && $plugin == 'mepr' && !empty($controller) && !empty($action)) { + self::standalone_route($controller, $action); + exit; + } elseif ( + !empty($plugin) && $plugin == 'mepr' && isset($_REQUEST['pmt']) && + !empty($_REQUEST['pmt']) && !empty($action) + ) { + $mepr_options = MeprOptions::fetch(); + $obj = MeprHooks::apply_filters('mepr_gateway_notifier_obj', $mepr_options->payment_method($_REQUEST['pmt']), $action, $_REQUEST['pmt']); + if ($obj && ( $obj instanceof MeprBaseRealGateway )) { + $notifiers = $obj->notifiers(); + if (isset($notifiers[$action])) { + call_user_func([$obj,$notifiers[$action]]); + exit; + } + } + } + } catch (Exception $e) { + ?> +
getMessage()); ?>
+ wp_create_nonce('mepr_coupons'), - 'spc_enabled' => ( $mepr_options->enable_spc || $mepr_options->design_enable_checkout_template ), - 'spc_invoice' => ( $mepr_options->enable_spc_invoice || $mepr_options->design_enable_checkout_template ), - 'no_compatible_pms' => __('There are no payment methods available that can purchase this product, please contact the site administrator or purchase it separately.', 'memberpress'), - 'switch_pm_prompt' => __('It looks like your purchase requires %s. No problem! Just click below to switch.', 'memberpress'), - 'switch_pm' => __('Switch to %s', 'memberpress'), - 'cancel' => __('Cancel', 'memberpress'), - 'warning_icon_url' => MEPR_IMAGES_URL . '/mepr-notice-icon-error.png', - ); + public static function load_language() + { + /* + * Allow add-ons and such to load .po/mo files from outside directories using this filter hook + * WordPress will merge transalations if the textdomain is the same from multiple locations + * so we should be good to do it this way + */ + $paths = []; + + // Have to use WP_PLUGIN_DIR because load_plugin_textdomain doesn't accept abs paths + if (!file_exists(WP_PLUGIN_DIR . '/' . 'mepr-i18n')) { + @mkdir(WP_PLUGIN_DIR . '/' . 'mepr-i18n'); + + if (file_exists(WP_PLUGIN_DIR . '/' . 'mepr-i18n')) { + $paths[] = '/mepr-i18n'; + } + } else { + $paths[] = '/mepr-i18n'; + } - wp_localize_script('mp-signup', 'MeprSignup', $local_data); + // /wp-content/mepr-i18n should have priority over wp-content/memberpress/i18n/ + $paths[] = str_replace(WP_PLUGIN_DIR, '', MEPR_I18N_PATH); - //For Show hide password - wp_enqueue_style( 'dashicons' ); - wp_enqueue_style( 'mp-login-css', MEPR_CSS_URL.'/ui/login.css', null, MEPR_VERSION); + $paths = MeprHooks::apply_filters('mepr-textdomain-paths', $paths); - wp_register_script('mepr-login-js', MEPR_JS_URL.'/login.js', array('jquery', 'underscore', 'wp-i18n'), MEPR_VERSION); + foreach ($paths as $path) { + load_plugin_textdomain('memberpress', false, $path); + } - wp_enqueue_script('mepr-login-i18n'); - wp_enqueue_script('mepr-login-js'); + // Force a refresh of the $mepr_options so those strings can be marked as translatable in WPML/Polylang type plugins + MeprOptions::fetch(true); } - if($global_styles || $is_group_page) { - wp_enqueue_style('mp-plans-css', MEPR_CSS_URL . '/plans.min.css', array(), MEPR_VERSION); + public static function load_translations() + { + if (MeprHooks::apply_filters('mepr-remove-traduttore', false)) { + return; + } + // Load Traduttore + require_once(MEPR_I18N_PATH . '/namespace.php'); + + \MP_Required\Traduttore_Registry\add_project( + 'plugin', + 'memberpress', + 'https://translate.memberpress.com/wp-content/uploads/api/translations/memberpress.json' + ); } - } - - public static function get_datepicker_strings() { - return array( - 'closeText' => _x( 'Done', 'ui', 'memberpress' ), - 'currentText' => _x( 'Today', 'ui', 'memberpress' ), - 'monthNamesShort' => [ _x( 'Jan', 'ui', 'memberpress' ), _x( 'Feb', 'ui', 'memberpress' ), _x( 'Mar', 'ui', 'memberpress' ), _x( 'Apr', 'ui', 'memberpress' ), _x( 'May', 'ui', 'memberpress' ), _x( 'Jun', 'ui', 'memberpress' ), - _x( 'Jul', 'ui', 'memberpress' ), _x( 'Aug', 'ui', 'memberpress' ), _x( 'Sep', 'ui', 'memberpress' ), _x( 'Oct', 'ui', 'memberpress' ), _x( 'Nov', 'ui', 'memberpress' ), _x( 'Dec', 'ui', 'memberpress' ) ], - 'dayNamesMin' => [ _x( 'Su', 'ui', 'memberpress' ),_x( 'Mo', 'ui', 'memberpress' ),_x( 'Tu', 'ui', 'memberpress' ),_x( 'We', 'ui', 'memberpress' ),_x( 'Th', 'ui', 'memberpress' ),_x( 'Fr', 'ui', 'memberpress' ),_x( 'Sa', 'ui', 'memberpress' ) ] - ); - } - - public static function load_admin_scripts($hook) - { - global $wp_version; - - $popup_ctrl = new MeprPopupCtrl(); - wp_enqueue_style('jquery-magnific-popup', $popup_ctrl->popup_css); - - wp_register_style( 'mepr-settings-table-css', - MEPR_CSS_URL.'/settings_table.css', - array(), MEPR_VERSION ); - wp_enqueue_style( 'mepr-admin-shared-css', - MEPR_CSS_URL.'/admin-shared.css', - array('wp-pointer','jquery-magnific-popup','mepr-settings-table-css'), MEPR_VERSION ); - wp_enqueue_style( 'mepr-fontello-animation', - MEPR_VENDOR_LIB_URL.'/fontello/css/animation.css', - array(), MEPR_VERSION ); - wp_enqueue_style( 'mepr-fontello-memberpress', - MEPR_VENDOR_LIB_URL.'/fontello/css/memberpress.css', - array('mepr-fontello-animation'), MEPR_VERSION ); - - // If we're in 3.8 now then use a font for the admin image - if( version_compare( $wp_version, '3.8', '>=' ) ) { - wp_enqueue_style( 'mepr-menu-styles', MEPR_CSS_URL.'/menu-styles.css', - array('mepr-fontello-memberpress'), MEPR_VERSION ); + + // Utility function to grab the parameter whether it's a get or post + public static function get_param($param, $default = '') + { + return (isset($_REQUEST[$param]) ? $_REQUEST[$param] : $default); } - wp_register_script('jquery-magnific-popup', $popup_ctrl->popup_js, array('jquery')); - wp_enqueue_script('mepr-tooltip', MEPR_JS_URL.'/tooltip.js', array('jquery','wp-pointer','jquery-magnific-popup'), MEPR_VERSION); - wp_localize_script('mepr-tooltip', 'MeprTooltip', array( 'show_about_notice' => self::show_about_notice(), - 'about_notice' => self::about_notice() )); - wp_register_script('mepr-settings-table-js', MEPR_JS_URL.'/settings_table.js', array('jquery'), MEPR_VERSION); - wp_register_script('mepr-cookie-js', MEPR_JS_URL.'/js.cookie.min.js', array(), '2.2.1'); - wp_enqueue_script('mepr-admin-shared-js', MEPR_JS_URL.'/admin_shared.js', array('jquery', 'jquery-magnific-popup', 'mepr-settings-table-js', 'mepr-cookie-js'), MEPR_VERSION); - wp_localize_script('mepr-admin-shared-js', 'MeprAdminShared', array( - 'ajax_url' => admin_url('admin-ajax.php'), - 'dismiss_notice_nonce' => wp_create_nonce('mepr_dismiss_notice'), - 'enable_stripe_tax_nonce' => wp_create_nonce('mepr_enable_stripe_tax') - )); - - //Widget in the dashboard stuff - if($hook == 'index.php') { - $local_data = array( - 'report_nonce' => wp_create_nonce('mepr_reports') - ); - wp_enqueue_script('mepr-google-jsapi', 'https://www.gstatic.com/charts/loader.js', array(), MEPR_VERSION); - wp_enqueue_script('mepr-widgets-js', MEPR_JS_URL.'/admin_widgets.js', array('jquery', 'mepr-google-jsapi'), MEPR_VERSION, true); - wp_localize_script('mepr-widgets-js', 'MeprWidgetData', $local_data); - wp_enqueue_style('mepr-widgets-css', MEPR_CSS_URL.'/admin-widgets.css', array(), MEPR_VERSION); + public static function get_param_delimiter_char($link) + { + return ((preg_match('#\?#', $link)) ? '&' : '?'); } - } - // The tight way to process standalone requests dogg... - public static function parse_standalone_request() { - global $user_ID; + public static function add_dashboard_widgets() + { + if (!MeprUtils::is_mepr_admin()) { + return; + } + + wp_add_dashboard_widget('mepr_weekly_stats_widget', 'MemberPress Weekly Stats', 'MeprAppCtrl::weekly_stats_widget'); - $plugin = (isset($_REQUEST['plugin']))?$_REQUEST['plugin']:''; - $action = (isset($_REQUEST['action']))?$_REQUEST['action']:''; - $controller = (isset($_REQUEST['controller']))?$_REQUEST['controller']:''; + // Globalize the metaboxes array, this holds all the widgets for wp-admin + global $wp_meta_boxes; - $request_uri = $_SERVER['REQUEST_URI']; + // Get the regular dashboard widgets array + // (which has our new widget already but at the end) + $normal_dashboard = $wp_meta_boxes['dashboard']['normal']['core']; - // Pretty Mepr Notifier ... prevents POST vars from being mangled - $notify_url_pattern = MeprUtils::gateway_notify_url_regex_pattern(); - if(MeprUtils::match_uri($notify_url_pattern,$request_uri,$m)) { - $plugin = 'mepr'; - $_REQUEST['pmt'] = $m[1]; - $action = $m[2]; + // Backup and delete our new dashbaord widget from the end of the array + $mepr_weekly_stats_widget_backup = ['mepr_weekly_stats_widget' => $normal_dashboard['mepr_weekly_stats_widget']]; + unset($normal_dashboard['mepr_weekly_stats_widget']); + + // Merge the two arrays together so our widget is at the beginning + $sorted_dashboard = array_merge($mepr_weekly_stats_widget_backup, $normal_dashboard); + + // Save the sorted array back into the original metaboxes + $wp_meta_boxes['dashboard']['normal']['core'] = $sorted_dashboard; } - try { - if(MeprUtils::is_post_request() && isset($_POST['mepr_process_signup_form'])) { - if( MeprUtils::is_user_logged_in() && - isset($_POST['logged_in_purchase']) && - $_POST['logged_in_purchase'] == 1 ) { - check_admin_referer( 'logged_in_purchase', 'mepr_checkout_nonce' ); - } - - $checkout_ctrl = MeprCtrlFactory::fetch('checkout'); - $checkout_ctrl->process_signup_form(); - } - else if(isset($_POST) && isset($_POST['mepr_process_payment_form'])) { - $checkout_ctrl = MeprCtrlFactory::fetch('checkout'); - $checkout_ctrl->process_payment_form(); - } - else if($action==='checkout' && isset($_REQUEST['txn'])) { - $_REQUEST['txn'] = MeprUtils::base36_decode($_REQUEST['txn']); - - //Back button fix - $txn = new MeprTransaction((int)$_REQUEST['txn']); - if(strpos($txn->trans_num, 'mp-txn-') === false || $txn->status != MeprTransaction::$pending_str) { - $prd = new MeprProduct($txn->product_id); - MeprUtils::wp_redirect($prd->url()); - } - - $checkout_ctrl = MeprCtrlFactory::fetch('checkout'); - $checkout_ctrl->display_payment_page(); - } - else if(isset($_POST) && isset($_POST['mepr_process_login_form'])) { - $login_ctrl = MeprCtrlFactory::fetch('login'); - $login_ctrl->process_login_form(); - } - else if( MeprUtils::is_post_request() && $plugin=='mepr' && $action=='updatepassword' && - isset($_POST['mepr-new-password']) && isset($_POST['mepr-confirm-password']) ) { - check_admin_referer( 'update_password', 'mepr_account_nonce' ); - $account_ctrl = MeprCtrlFactory::fetch('account'); - $account_ctrl->save_new_password($user_ID, $_POST['mepr-new-password'], $_POST['mepr-confirm-password']); - } - else if( !empty($plugin) && $plugin == 'mepr' && !empty($controller) && !empty($action) ) { - self::standalone_route($controller, $action); - exit; - } - else if(!empty($plugin) && $plugin == 'mepr' && isset($_REQUEST['pmt']) && - !empty($_REQUEST['pmt']) && !empty($action)) { + public static function weekly_stats_widget() + { $mepr_options = MeprOptions::fetch(); - $obj = MeprHooks::apply_filters('mepr_gateway_notifier_obj', $mepr_options->payment_method($_REQUEST['pmt']), $action, $_REQUEST['pmt']); - if( $obj && ( $obj instanceof MeprBaseRealGateway ) ) { - $notifiers = $obj->notifiers(); - if( isset($notifiers[$action]) ) { - nocache_headers(); - call_user_func(array($obj,$notifiers[$action])); - exit; - } - } - } - } - catch(Exception $e) { - ?> -
getMessage()); ?>
- $normal_dashboard['mepr_weekly_stats_widget']); - unset($normal_dashboard['mepr_weekly_stats_widget']); - - // Merge the two arrays together so our widget is at the beginning - $sorted_dashboard = array_merge($mepr_weekly_stats_widget_backup, $normal_dashboard); - - // Save the sorted array back into the original metaboxes - $wp_meta_boxes['dashboard']['normal']['core'] = $sorted_dashboard; - } - - public static function weekly_stats_widget() { - $mepr_options = MeprOptions::fetch(); - - $status_collection = array( - MeprTransaction::$pending_str, - MeprTransaction::$failed_str, - MeprTransaction::$refunded_str, - MeprTransaction::$complete_str - ); - - $start_date = new \DateTimeImmutable('-6 days', new \DateTimeZone('UTC')); - $end_date = new \DateTimeImmutable('now', new \DateTimeZone('UTC')); - - $status_data = MeprReports::get_date_range_transactions_counts($status_collection, $start_date, $end_date); - $pending_transactions = $status_data[MeprTransaction::$pending_str]; - $failed_transactions = $status_data[MeprTransaction::$failed_str]; - $refunded_transactions = $status_data[MeprTransaction::$refunded_str]; - $completed_transactions = $status_data[MeprTransaction::$complete_str]; - - $revenue = MeprReports::get_date_range_revenue($start_date, $end_date); - $refunds = MeprReports::get_date_range_refunds($start_date, $end_date); - - if( empty($revenue) || is_null($revenue) ) { - $revenue = 0; + public static function todays_date() + { + if (isset($_REQUEST['datetime'])) { + echo date_i18n('Y-m-d H:i:s', time(), true); + } else { + echo date_i18n('Y-m-d', time(), true); + } + + die; } - if( empty($refunds) || is_null($refunds) ) { - $refunds = 0; + public static function show_about_notice() + { + $last_shown_notice = get_option('mepr_about_notice_version'); + $version_str = preg_replace('/\./', '-', MEPR_VERSION); + return ( $last_shown_notice != MEPR_VERSION && + file_exists(MeprView::file("/admin/about/{$version_str}")) ); } - MeprView::render('/admin/widgets/admin_stats_widget', get_defined_vars()); - } + public static function about_notice() + { + $version_str = preg_replace('/\./', '-', MEPR_VERSION); + $version_file = MeprView::file("/admin/about/{$version_str}"); + if (file_exists($version_file)) { + ob_start(); + require_once($version_file); + return ob_get_clean(); + } - public static function todays_date() { - if(isset($_REQUEST['datetime'])) { - echo date_i18n('Y-m-d H:i:s', time(), true); + return ''; } - else { - echo date_i18n('Y-m-d', time(), true); + + public static function close_about_notice() + { + update_option('mepr_about_notice_version', MEPR_VERSION); } - die; - } - - public static function show_about_notice() { - $last_shown_notice = get_option('mepr_about_notice_version'); - $version_str = preg_replace('/\./','-',MEPR_VERSION); - return ( $last_shown_notice != MEPR_VERSION && - file_exists( MeprView::file("/admin/about/{$version_str}") ) ); - } - - public static function about_notice() { - $version_str = preg_replace('/\./','-',MEPR_VERSION); - $version_file = MeprView::file("/admin/about/{$version_str}"); - if( file_exists( $version_file ) ) { - ob_start(); - require_once($version_file); - return ob_get_clean(); + public static function cleanup_list_view($views) + { + if (isset($views['draft'])) { + unset($views['draft']); + } + if (isset($views['publish'])) { + unset($views['publish']); + } + return $views; } - return ''; - } - - public static function close_about_notice() { - update_option('mepr_about_notice_version',MEPR_VERSION); - } - - public static function cleanup_list_view($views) { - if(isset($views['draft'])) { unset($views['draft']); } - if(isset($views['publish'])) { unset($views['publish']); } - return $views; - } - - public function cleanup_list_table_month_dropdown( $months, $post_type ) { - $ours = array( MeprProduct::$cpt, MeprRule::$cpt, MeprGroup::$cpt, MeprCoupon::$cpt ); - if( in_array( $post_type, $ours ) ) { $months = array(); } - return $months; - } - - // TODO: We want to eliminate this when we get css compilation / compression in place - public static function load_css() { - //IF WE MOVE BACK TO admin-ajax.php method, then this conditional needs to go - if( !isset($_GET['plugin']) || - $_GET['plugin'] != 'mepr' || - !isset($_GET['action']) || - $_GET['action'] != 'mepr_load_css' ) { - return; + public function cleanup_list_table_month_dropdown($months, $post_type) + { + $ours = [MeprProduct::$cpt, MeprRule::$cpt, MeprGroup::$cpt, MeprCoupon::$cpt]; + if (in_array($post_type, $ours)) { + $months = []; + } + return $months; } - header('Content-Type: text/css'); - header('Cache-Control: max-age=2629000, public'); //1 month - header('Expires: '.gmdate('D, d M Y H:i:s', (int)(time() + 2629000)).' GMT'); //1 month? + // TODO: We want to eliminate this when we get css compilation / compression in place + public static function load_css() + { + // IF WE MOVE BACK TO admin-ajax.php method, then this conditional needs to go + if ( + !isset($_GET['plugin']) || + $_GET['plugin'] != 'mepr' || + !isset($_GET['action']) || + $_GET['action'] != 'mepr_load_css' + ) { + return; + } - $css = ''; + header('Content-Type: text/css'); + header('Cache-Control: max-age=2629000, public'); // 1 month + header('Expires: ' . gmdate('D, d M Y H:i:s', (int)(time() + 2629000)) . ' GMT'); // 1 month? - if(isset($_REQUEST['t']) && $_REQUEST['t']=='price_table') { - $csskey = 'mp-css-' . md5(MEPR_VERSION); - $css_files = get_transient($csskey); + $css = ''; - //$css_files = false; - if(!$css_files) { - $css_files = array(); + if (isset($_REQUEST['t']) && $_REQUEST['t'] == 'price_table') { + $csskey = 'mp-css-' . md5(MEPR_VERSION); + $css_files = get_transient($csskey); - // Enqueue plan templates - $css_files = array_merge($css_files, MeprGroup::group_theme_templates(true)); + // $css_files = false; + if (!$css_files) { + $css_files = []; - // Enqueue plans - $css_files = array_merge($css_files, MeprGroup::group_themes(true)); + // Enqueue plan templates + $css_files = array_merge($css_files, MeprGroup::group_theme_templates(true)); - set_transient($csskey, $css_files, DAY_IN_SECONDS); - } - } + // Enqueue plans + $css_files = array_merge($css_files, MeprGroup::group_themes(true)); - if(isset($css_files) && !empty($css_files)) { - $csskey = 'mp-load-css-' . md5(MEPR_VERSION) . '-' . md5(implode(',',$css_files)); - $css = get_transient($csskey); + set_transient($csskey, $css_files, DAY_IN_SECONDS); + } + } - if(!$css) { - ob_start(); + if (isset($css_files) && !empty($css_files)) { + $csskey = 'mp-load-css-' . md5(MEPR_VERSION) . '-' . md5(implode(',', $css_files)); + $css = get_transient($csskey); - foreach($css_files as $f) { - if(file_exists($f)) { echo file_get_contents($f); } - } + if (!$css) { + ob_start(); - $css = MeprUtils::compress_css(ob_get_clean()); - set_transient($csskey, $css, DAY_IN_SECONDS); - } - } + foreach ($css_files as $f) { + if (file_exists($f)) { + echo file_get_contents($f); + } + } - exit($css); - } + $css = MeprUtils::compress_css(ob_get_clean()); + set_transient($csskey, $css, DAY_IN_SECONDS); + } + } - public static function append_mp_privacy_policy() { - if(!function_exists('wp_add_privacy_policy_content')) { return; } + exit($css); + } - ob_start(); - MeprView::render('/admin/privacy/privacy_policy', get_defined_vars()); - $privacy_policy = ob_get_clean(); + public static function append_mp_privacy_policy() + { + if (!function_exists('wp_add_privacy_policy_content')) { + return; + } - wp_add_privacy_policy_content('MemberPress', $privacy_policy); - } + ob_start(); + MeprView::render('/admin/privacy/privacy_policy', get_defined_vars()); + $privacy_policy = ob_get_clean(); - public static function integrate_wp_debugging($user_defined_constants) { - if(!defined('WP_MEPR_DEBUG') || WP_MEPR_DEBUG === false) { - // if raw is true, then value will be converted to boolean - $user_defined_constants['WP_MEPR_DEBUG'] = array('value' => 'true', 'raw' => true); + wp_add_privacy_policy_content('MemberPress', $privacy_policy); } - return $user_defined_constants; - } + public static function integrate_wp_debugging($user_defined_constants) + { + if (!defined('WP_MEPR_DEBUG') || WP_MEPR_DEBUG === false) { + // if raw is true, then value will be converted to boolean + $user_defined_constants['WP_MEPR_DEBUG'] = [ + 'value' => 'true', + 'raw' => true, + ]; + } - public static function render_admin_support() { - MeprView::render('admin/support/view'); - } + return $user_defined_constants; + } - public static function mepr_menu(){ - global $submenu; + public static function render_admin_support() + { + MeprView::render('admin/support/view'); + } - $capability = MeprUtils::get_mepr_admin_capability(); - $mbr_ctrl = new MeprMembersCtrl(); - $txn_ctrl = new MeprTransactionsCtrl(); - $sub_ctrl = new MeprSubscriptionsCtrl(); - $menu_title = 'MemberPress'; + public static function mepr_menu() + { + global $submenu; - add_menu_page('MemberPress', $menu_title, $capability, 'memberpress', 'MeprAppCtrl::toplevel_menu_route', MEPR_IMAGES_URL."/memberpress-16@2x.png", 775677); + $capability = MeprUtils::get_mepr_admin_capability(); + $mbr_ctrl = new MeprMembersCtrl(); + $txn_ctrl = new MeprTransactionsCtrl(); + $sub_ctrl = new MeprSubscriptionsCtrl(); + $menu_title = 'MemberPress'; - add_submenu_page('memberpress', __('Members', 'memberpress'), __('Members', 'memberpress'), $capability, 'memberpress-members', array($mbr_ctrl,'listing')); + add_menu_page('MemberPress', $menu_title, $capability, 'memberpress', 'MeprAppCtrl::toplevel_menu_route', MEPR_IMAGES_URL . '/memberpress-16@2x.png', 775677); - if(!get_option('mepr_disable_affiliates_menu_item')) { - if(defined('ESAF_VERSION')) { - add_submenu_page('memberpress', __('Affiliates', 'memberpress'), __('Affiliates', 'memberpress'), $capability, admin_url('admin.php?page=easy-affiliate')); - } - else { - add_submenu_page('memberpress', __('Affiliates', 'memberpress'), __('Affiliates', 'memberpress'), $capability, 'memberpress-affiliates', 'MeprAddonsCtrl::affiliates'); - } - } + add_submenu_page('memberpress', __('Members', 'memberpress'), __('Members', 'memberpress'), $capability, 'memberpress-members', [$mbr_ctrl,'listing']); - add_submenu_page('memberpress', __('Subscriptions', 'memberpress'), __('Subscriptions', 'memberpress'), $capability, 'memberpress-subscriptions', array( $sub_ctrl, 'listing' )); - // Specifically for subscriptions listing - add_submenu_page('', __('Subscriptions', 'memberpress'), __('Subscriptions', 'memberpress'), $capability, 'memberpress-lifetimes', array( $sub_ctrl, 'listing' )); - add_submenu_page('memberpress', __('Transactions', 'memberpress'), __('Transactions', 'memberpress'), $capability, 'memberpress-trans', array( $txn_ctrl, 'listing' )); - add_submenu_page('memberpress', __('Reports', 'memberpress'), __('Reports', 'memberpress'), $capability, 'memberpress-reports', 'MeprReportsCtrl::main'); - add_dashboard_page(__('MemberPress', 'memberpress'), __('MemberPress', 'memberpress'), $capability, 'memberpress-reports', 'MeprReportsCtrl::main'); - add_submenu_page('memberpress', __('Settings', 'memberpress'), __('Settings', 'memberpress') . MeprUtils::new_badge(), $capability, 'memberpress-options', 'MeprOptionsCtrl::route'); - add_submenu_page('memberpress', __('Onboarding', 'memberpress'), __('Onboarding', 'memberpress'), $capability, 'memberpress-onboarding', 'MeprOnboardingCtrl::route'); - add_submenu_page('memberpress', __('Account Login', 'memberpress'), __('Account Login', 'memberpress'), $capability, 'memberpress-account-login', 'MeprAccountLoginCtrl::route'); - add_submenu_page('memberpress', __('Add-ons', 'memberpress'), '' . __('Add-ons', 'memberpress') . '', $capability, 'memberpress-addons', 'MeprAddonsCtrl::route'); - - if ( ! is_plugin_active( 'memberpress-courses/main.php' ) ) { - $menu_title = __('Courses', 'memberpress'); - //$menu_title .= sprintf( '%s', __('NEW', 'memberpress') ); - add_submenu_page('memberpress', __('MemberPress Courses', 'memberpress'), $menu_title, $capability, 'memberpress-courses', 'MeprCoursesCtrl::route'); - } + if (!get_option('mepr_disable_affiliates_menu_item')) { + if (defined('ESAF_VERSION')) { + add_submenu_page('memberpress', __('Affiliates', 'memberpress'), __('Affiliates', 'memberpress'), $capability, admin_url('admin.php?page=easy-affiliate')); + } else { + add_submenu_page('memberpress', __('Affiliates', 'memberpress'), __('Affiliates', 'memberpress'), $capability, 'memberpress-affiliates', 'MeprAddonsCtrl::affiliates'); + } + } - add_submenu_page('', __('Support', 'memberpress'), __('Support', 'memberpress'), $capability, 'memberpress-support', 'MeprAppCtrl::render_admin_support'); + add_submenu_page('memberpress', __('Subscriptions', 'memberpress'), __('Subscriptions', 'memberpress'), $capability, 'memberpress-subscriptions', [$sub_ctrl, 'listing']); + // Specifically for subscriptions listing + add_submenu_page('', __('Subscriptions', 'memberpress'), __('Subscriptions', 'memberpress'), $capability, 'memberpress-lifetimes', [$sub_ctrl, 'listing']); + add_submenu_page('memberpress', __('Transactions', 'memberpress'), __('Transactions', 'memberpress'), $capability, 'memberpress-trans', [$txn_ctrl, 'listing']); + add_submenu_page('memberpress', __('Reports', 'memberpress'), __('Reports', 'memberpress'), $capability, 'memberpress-reports', 'MeprReportsCtrl::main'); + add_dashboard_page(__('MemberPress', 'memberpress'), __('MemberPress', 'memberpress'), $capability, 'memberpress-reports', 'MeprReportsCtrl::main'); + add_submenu_page('memberpress', __('Settings', 'memberpress'), __('Settings', 'memberpress') . MeprUtils::new_badge(), $capability, 'memberpress-options', 'MeprOptionsCtrl::route'); + add_submenu_page('memberpress', __('Onboarding', 'memberpress'), __('Onboarding', 'memberpress'), $capability, 'memberpress-onboarding', 'MeprOnboardingCtrl::route'); + add_submenu_page('memberpress', __('Account Login', 'memberpress'), __('Account Login', 'memberpress'), $capability, 'memberpress-account-login', 'MeprAccountLoginCtrl::route'); + add_submenu_page('memberpress', __('Add-ons', 'memberpress'), '' . __('Add-ons', 'memberpress') . '', $capability, 'memberpress-addons', 'MeprAddonsCtrl::route'); + + if (! is_plugin_active('memberpress-courses/main.php')) { + $menu_title = __('Courses', 'memberpress'); + // $menu_title .= sprintf( '%s', __('NEW', 'memberpress') ); + add_submenu_page('memberpress', __('MemberPress Courses', 'memberpress'), $menu_title, $capability, 'memberpress-courses', 'MeprCoursesCtrl::route'); + } - MeprHooks::do_action('mepr_menu'); - } + add_submenu_page('', __('Support', 'memberpress'), __('Support', 'memberpress'), $capability, 'memberpress-support', 'MeprAppCtrl::render_admin_support'); - public static function mepr_drm_menu(){ - global $submenu; + MeprHooks::do_action('mepr_menu'); + } - $capability = MeprUtils::get_mepr_admin_capability(); - $mbr_ctrl = new MeprMembersCtrl(); - $menu_title = 'MemberPress'; + public static function mepr_drm_menu() + { + global $submenu; - add_menu_page('MemberPress', $menu_title, $capability, 'memberpress-drm', 'MeprAppCtrl::toplevel_menu_drm_route', MEPR_IMAGES_URL."/memberpress-16@2x.png", 775677); + $capability = MeprUtils::get_mepr_admin_capability(); + $mbr_ctrl = new MeprMembersCtrl(); + $menu_title = 'MemberPress'; - add_submenu_page('memberpress-drm', __('Members', 'memberpress'), __('Members', 'memberpress'), $capability, 'memberpress-members', array($mbr_ctrl,'listing_drm')); + add_menu_page('MemberPress', $menu_title, $capability, 'memberpress-drm', 'MeprAppCtrl::toplevel_menu_drm_route', MEPR_IMAGES_URL . '/memberpress-16@2x.png', 775677); - MeprHooks::do_action('mepr_drm_menu'); - } + add_submenu_page('memberpress-drm', __('Members', 'memberpress'), __('Members', 'memberpress'), $capability, 'memberpress-members', [$mbr_ctrl,'listing_drm']); - public static function activated_plugin($plugin) { - if($plugin != MEPR_PLUGIN_SLUG) { - return; + MeprHooks::do_action('mepr_drm_menu'); } - if(!is_user_logged_in() || wp_doing_ajax() || !is_admin() || is_network_admin() || !MeprUtils::is_mepr_admin()) { - return; - } + public static function activated_plugin($plugin) + { + if ($plugin != MEPR_PLUGIN_SLUG) { + return; + } - if(MeprUtils::is_post_request() && (isset($_POST['action']) || isset($_POST['action2']))) { - return; // don't redirect on bulk activation - } + if (!is_user_logged_in() || wp_doing_ajax() || !is_admin() || is_network_admin() || !MeprUtils::is_mepr_admin()) { + return; + } + + if (MeprUtils::is_post_request() && (isset($_POST['action']) || isset($_POST['action2']))) { + return; // don't redirect on bulk activation + } - global $wpdb; + global $wpdb; - wp_cache_flush(); - $wpdb->flush(); + wp_cache_flush(); + $wpdb->flush(); - $onboarded = $wpdb->get_var("SELECT option_value FROM {$wpdb->options} WHERE option_name = 'mepr_onboarded'"); + $onboarded = $wpdb->get_var("SELECT option_value FROM {$wpdb->options} WHERE option_name = 'mepr_onboarded'"); - if($onboarded === null) { - nocache_headers(); - wp_redirect(admin_url('admin.php?page=memberpress-onboarding'), 307); - exit; + if ($onboarded === null) { + nocache_headers(); + wp_redirect(admin_url('admin.php?page=memberpress-onboarding'), 307); + exit; + } } - } } //End class diff --git a/app/controllers/MeprAuthenticatorCtrl.php b/app/controllers/MeprAuthenticatorCtrl.php index fbaa0ad..e6fdcb2 100644 --- a/app/controllers/MeprAuthenticatorCtrl.php +++ b/app/controllers/MeprAuthenticatorCtrl.php @@ -1,241 +1,249 @@ false, - 'headers' => array( - 'accept' => 'application/json' - ), - 'body' => array( - 'auth_code' => $auth_code - ) - ) ); - - $body = json_decode( wp_remote_retrieve_body( $response ), true ); - - if ( isset( $body['account_email'] ) && ! empty( $body['account_email'] ) ) { - $email_saved = update_option( 'mepr_authenticator_account_email', sanitize_text_field( $body['account_email'] ) ); + public function load_hooks() + { + if (!defined('MEPR_AUTH_SERVICE_DOMAIN')) { + define('MEPR_AUTH_SERVICE_DOMAIN', 'auth.caseproof.com'); + } + define('MEPR_AUTH_SERVICE_URL', 'https://' . MEPR_AUTH_SERVICE_DOMAIN); + + add_action('admin_init', [$this, 'clear_connection_data']); + add_action('init', [$this, 'process_connect']); + add_action('init', [$this, 'process_disconnect']); + } + + public function clear_connection_data() + { + if (isset($_GET['mp-clear-connection-data'])) { + // Admins only + if (current_user_can('manage_options')) { + delete_option('mepr_authenticator_site_uuid'); + delete_option('mepr_authenticator_account_email'); + delete_option('mepr_authenticator_secret_token'); + } + } + } + + /** + * Process a Connect + * + * @return void + */ + public function process_connect() + { + + // Make sure we've entered our Authenticator process + if (! isset($_GET['mepr-connect']) || 'true' !== $_GET['mepr-connect']) { + return; + } + + // Validate the nonce on the WP side of things + if (! isset($_GET['nonce']) || ! wp_verify_nonce($_GET['nonce'], 'mepr-connect')) { + return; + } + + // Make sure the user is authorized + if (! MeprUtils::is_mepr_admin()) { + return; + } + + $site_uuid = sanitize_text_field($_GET['site_uuid']); + $auth_code = sanitize_text_field($_GET['auth_code']); + + // GET request to obtain token + $response = wp_remote_get(MEPR_AUTH_SERVICE_URL . "/api/tokens/{$site_uuid}", [ + 'sslverify' => false, + 'headers' => [ + 'accept' => 'application/json', + ], + 'body' => [ + 'auth_code' => $auth_code, + ], + ]); + + $body = json_decode(wp_remote_retrieve_body($response), true); + + if (isset($body['account_email']) && ! empty($body['account_email'])) { + $email_saved = update_option('mepr_authenticator_account_email', sanitize_text_field($body['account_email'])); + } + + if (isset($body['secret_token']) && ! empty($body['secret_token'])) { + $token_saved = update_option('mepr_authenticator_secret_token', sanitize_text_field($body['secret_token'])); + } + + if (isset($body['user_uuid']) && ! empty($body['user_uuid'])) { + $user_uuid_saved = update_option('mepr_authenticator_user_uuid', sanitize_text_field($body['user_uuid'])); + } + + if ($site_uuid) { + update_option('mepr_authenticator_site_uuid', $site_uuid); + } + + if (isset($_GET['stripe_connect']) && 'true' === $_GET['stripe_connect'] && isset($_GET['method_id']) && ! empty($_GET['method_id'])) { + wp_redirect(MeprStripeGateway::get_stripe_connect_url($_GET['method_id'])); + exit; + } + + $redirect_url = remove_query_arg(['mepr-connect', 'nonce', 'site_uuid', 'user_uuid', 'auth_code', 'license_key']); + + $license_key = isset($_GET['license_key']) ? sanitize_text_field(wp_unslash($_GET['license_key'])) : ''; + + if (! empty($license_key)) { + try { + MeprUpdateCtrl::activate_license($license_key); + } catch (Exception $e) { + $redirect_url = add_query_arg('license_error', urlencode($e->getMessage()), $redirect_url); + } + } + + wp_redirect($redirect_url); + exit; + } + + /** + * Process a Disconnect + * + * @return void + */ + public function process_disconnect() + { + + // Make sure we've entered our Authenticator process + if (! isset($_GET['mepr-disconnect']) || 'true' !== $_GET['mepr-disconnect']) { + return; + } + + // Validate the nonce on the WP side of things + if (! isset($_GET['nonce']) || ! wp_verify_nonce($_GET['nonce'], 'mepr-disconnect')) { + return; + } + + // Make sure the user is authorized + if (! MeprUtils::is_mepr_admin()) { + return; + } + + $site_email = get_option('mepr_authenticator_account_email'); + $site_uuid = get_option('mepr_authenticator_site_uuid'); + + MeprHooks::do_action('mepr_memberpress_com_pre_disconnect', $site_uuid, $site_email); + + // Create token payload + $payload = [ + 'email' => $site_email, + 'site_uuid' => $site_uuid, + ]; + + // Create JWT + $jwt = self::generate_jwt($payload); + + // DELETE request to obtain token + $response = wp_remote_request(MEPR_AUTH_SERVICE_URL . '/api/disconnect/memberpress', [ + 'method' => 'DELETE', + 'sslverify' => false, + 'headers' => MeprUtils::jwt_header($jwt, MEPR_AUTH_SERVICE_DOMAIN), + ]); + + $body = json_decode(wp_remote_retrieve_body($response), true); + + if (isset($body['disconnected']) && true === $body['disconnected']) { + delete_option('mepr_authenticator_account_email'); + delete_option('mepr_authenticator_secret_token'); + delete_option('mepr_authenticator_site_uuid', $site_uuid); + } + + wp_redirect(remove_query_arg(['mepr-disconnect', 'nonce'])); + exit; + } + + /** + * Generates a JWT, signed by the stored secret token + * + * @param array $payload Payload data + * @param sring $secret Used to sign the JWT + * + * @return string + */ + public static function generate_jwt($payload, $secret = false) + { + + if (false === $secret) { + $secret = get_option('mepr_authenticator_secret_token'); + } + + // Create token header + $header = [ + 'typ' => 'JWT', + 'alg' => 'HS256', + ]; + $header = json_encode($header); + $header = self::base64url_encode($header); + + // Create token payload + $payload = json_encode($payload); + $payload = self::base64url_encode($payload); + + // Create Signature Hash + $signature = hash_hmac('sha256', "{$header}.{$payload}", $secret); + $signature = json_encode($signature); + $signature = self::base64url_encode($signature); + + // Create JWT + $jwt = "{$header}.{$payload}.{$signature}"; + return $jwt; + } + + /** + * Ensure that the Base64 string is passed within URLs without any URL encoding + * + * @param string $value + * + * @return string + */ + public static function base64url_encode($value) + { + return rtrim(strtr(base64_encode($value), '+/', '-_'), '='); + } + + /** + * Assembles a URL for connecting to our Authentication service + * + * @param boolean $stripe_connect Will add a query string that is used to redirect to Stripe Connect after returning from Auth service + * @param array $additional_params + * @param string|null $return_url + * + * @return string + */ + public static function get_auth_connect_url($stripe_connect = false, $payment_method_id = false, $additional_params = [], $return_url = null) + { + $return_url = is_null($return_url) ? admin_url('admin.php?page=memberpress-account-login', false) : $return_url; + + $connect_params = [ + 'return_url' => urlencode(add_query_arg('mepr-connect', 'true', $return_url)), + 'nonce' => wp_create_nonce('mepr-connect'), + ]; + + $site_uuid = get_option('mepr_authenticator_site_uuid'); + + if ($site_uuid) { + $connect_params['site_uuid'] = $site_uuid; + } + + if (true === $stripe_connect && ! empty($payment_method_id)) { + $connect_params['stripe_connect'] = 'true'; + $connect_params['method_id'] = $payment_method_id; + } + + if (! empty($additional_params)) { + $connect_params = array_merge($connect_params, $additional_params); + } + + return add_query_arg($connect_params, MEPR_AUTH_SERVICE_URL . '/connect/memberpress'); } - - if ( isset( $body['secret_token'] ) && ! empty( $body['secret_token'] ) ) { - $token_saved = update_option( 'mepr_authenticator_secret_token', sanitize_text_field( $body['secret_token'] ) ); - } - - if ( isset( $body['user_uuid'] ) && ! empty( $body['user_uuid'] ) ) { - $user_uuid_saved = update_option( 'mepr_authenticator_user_uuid', sanitize_text_field( $body['user_uuid'] ) ); - } - - if ( $site_uuid ) { - update_option( 'mepr_authenticator_site_uuid', $site_uuid ); - } - - if ( isset( $_GET['stripe_connect'] ) && 'true' === $_GET['stripe_connect'] && isset( $_GET['method_id'] ) && ! empty( $_GET['method_id'] ) ) { - wp_redirect( MeprStripeGateway::get_stripe_connect_url( $_GET['method_id'] ) ); - exit; - } - - $redirect_url = remove_query_arg( array( 'mepr-connect', 'nonce', 'site_uuid', 'user_uuid', 'auth_code', 'license_key' ) ); - - $license_key = isset( $_GET['license_key'] ) ? sanitize_text_field( wp_unslash( $_GET['license_key'] ) ) : ''; - - if( ! empty( $license_key ) ) { - try { - MeprUpdateCtrl::activate_license( $license_key ); - } - catch( Exception $e ) { - $redirect_url = add_query_arg( 'license_error', urlencode( $e->getMessage() ), $redirect_url ); - } - } - - wp_redirect( $redirect_url ); - exit; - } - - /** - * Process a Disconnect - * - * @return void - */ - public function process_disconnect() { - - // Make sure we've entered our Authenticator process - if ( ! isset( $_GET['mepr-disconnect'] ) || 'true' !== $_GET['mepr-disconnect'] ) { - return; - } - - // Validate the nonce on the WP side of things - if ( ! isset( $_GET['nonce'] ) || ! wp_verify_nonce( $_GET['nonce'], 'mepr-disconnect' ) ) { - return; - } - - // Make sure the user is authorized - if ( ! MeprUtils::is_mepr_admin() ) { - return; - } - - $site_email = get_option( 'mepr_authenticator_account_email' ); - $site_uuid = get_option( 'mepr_authenticator_site_uuid' ); - - MeprHooks::do_action('mepr_memberpress_com_pre_disconnect', $site_uuid, $site_email); - - // Create token payload - $payload = array( - 'email' => $site_email, - 'site_uuid' => $site_uuid - ); - - // Create JWT - $jwt = self::generate_jwt( $payload ); - - // DELETE request to obtain token - $response = wp_remote_request( MEPR_AUTH_SERVICE_URL . "/api/disconnect/memberpress", array( - 'method' => 'DELETE', - 'sslverify' => false, - 'headers' => MeprUtils::jwt_header($jwt, MEPR_AUTH_SERVICE_DOMAIN), - ) ); - - $body = json_decode( wp_remote_retrieve_body( $response ), true ); - - if ( isset( $body['disconnected'] ) && true === $body['disconnected'] ) { - delete_option( 'mepr_authenticator_account_email' ); - delete_option( 'mepr_authenticator_secret_token' ); - delete_option( 'mepr_authenticator_site_uuid', $site_uuid ); - } - - wp_redirect( remove_query_arg( array( 'mepr-disconnect', 'nonce' ) ) ); - exit; - } - - /** - * Generates a JWT, signed by the stored secret token - * - * @param array $payload Payload data - * @param sring $secret Used to sign the JWT - * - * @return string - */ - public static function generate_jwt( $payload, $secret = false ) { - - if ( false === $secret ) { - $secret = get_option( 'mepr_authenticator_secret_token' ); - } - - // Create token header - $header = array( - 'typ' => 'JWT', - 'alg' => 'HS256' - ); - $header = json_encode( $header ); - $header = self::base64url_encode( $header ); - - // Create token payload - $payload = json_encode( $payload ); - $payload = self::base64url_encode( $payload ); - - // Create Signature Hash - $signature = hash_hmac( 'sha256', "{$header}.{$payload}", $secret ); - $signature = json_encode( $signature ); - $signature = self::base64url_encode( $signature ); - - // Create JWT - $jwt = "{$header}.{$payload}.{$signature}"; - return $jwt; - } - - /** - * Ensure that the Base64 string is passed within URLs without any URL encoding - * - * @param string $value - * - * @return string - */ - public static function base64url_encode( $value ) { - return rtrim( strtr( base64_encode( $value ), '+/', '-_' ), '=' ); - } - - /** - * Assembles a URL for connecting to our Authentication service - * - * @param boolean $stripe_connect Will add a query string that is used to redirect to Stripe Connect after returning from Auth service - * @param array $additional_params - * @param string|null $return_url - * - * @return string - */ - public static function get_auth_connect_url( $stripe_connect = false, $payment_method_id = false, $additional_params = [], $return_url = null ) { - $return_url = is_null( $return_url ) ? admin_url( 'admin.php?page=memberpress-account-login', false ) : $return_url; - - $connect_params = array( - 'return_url' => urlencode( add_query_arg( 'mepr-connect', 'true', $return_url ) ), - 'nonce' => wp_create_nonce( 'mepr-connect' ) - ); - - $site_uuid = get_option( 'mepr_authenticator_site_uuid' ); - - if ( $site_uuid ) { - $connect_params['site_uuid'] = $site_uuid; - } - - if ( true === $stripe_connect && ! empty( $payment_method_id ) ) { - $connect_params['stripe_connect'] = 'true'; - $connect_params['method_id'] = $payment_method_id; - } - - if ( ! empty( $additional_params ) ) { - $connect_params = array_merge($connect_params, $additional_params); - } - - return add_query_arg( $connect_params, MEPR_AUTH_SERVICE_URL . '/connect/memberpress' ); - } } - diff --git a/app/controllers/MeprBlocksCtrl.php b/app/controllers/MeprBlocksCtrl.php index 27039b4..d938b6a 100644 --- a/app/controllers/MeprBlocksCtrl.php +++ b/app/controllers/MeprBlocksCtrl.php @@ -1,647 +1,663 @@ array( - 'membership' => array( - 'type' => 'string', - ), - ), - 'render_callback' => array( $this, 'render_membership_signup_block' ), - ) - ); - - // Account form block - register_block_type( - 'memberpress/account-form', - array( - 'attributes' => array(), - 'render_callback' => array( $this, 'render_account_block' ), - ) - ); - - // Login form block - register_block_type( - 'memberpress/login-form', - array( - 'api_version' => 2, - 'attributes' => array( - 'use_redirect' => array( - 'type' => 'boolean', - ), - ), - 'render_callback' => array( $this, 'render_login_block' ), - ) - ); - - // Protected content block - register_block_type( - 'memberpress/protected-content', - array( - 'attributes' => array( - 'rule' => array( - 'type' => 'number', - ), - 'ifallowed' => array( - 'type' => 'string', - ), - 'unauth' => array( - 'type' => 'string', - ), - 'unauth_message' => array( - 'type' => 'string', - ), - ), - 'render_callback' => array( $this, 'render_protected_content_block' ), - ) - ); - - // Pro Login Form - register_block_type( - 'memberpress/pro-login-form', - array( - 'api_version' => 2, - 'attributes' => array( - 'show_welcome_image' => array( - 'type' => 'boolean', - 'default' => $mepr_options->design_show_login_welcome_image - ), - 'welcome_image' => array( - 'type' => 'string', - 'default' => wp_get_attachment_url( $mepr_options->design_login_welcome_img ) - ), - 'admin_view' => array( - 'type' => 'boolean', - ) - ), - 'render_callback' => array( $this, 'render_pro_login_block' ), - 'editor_style' => 'mp-pro-login', - ) - ); - - // Pricing Columns for Pro Template - register_block_type( - 'memberpress/pro-pricing-table', - array( - 'api_version' => 2, - 'attributes' => array( - 'show_title' => array( - 'type' => 'boolean', - ), - 'button_highlight_color' => array( - 'type' => 'string', - 'default' => '#EF1010', - ), - 'group_id' => array( - 'type' => 'string', - 'default' => '', - ), - ), - 'render_callback' => array( $this, 'render_pro_pricing_block' ), - 'editor_style' => 'mp-pro-pricing', - ) - ); - - // Accounts Tab - register_block_type( - 'memberpress/pro-account-tabs', - array( - 'api_version' => 2, - 'attributes' => array( - 'show_welcome_image' => array( - 'type' => 'boolean', - 'default' => $mepr_options->design_show_account_welcome_image - ), - 'welcome_image' => array( - 'type' => 'string', - 'default' => wp_get_attachment_url( $mepr_options->design_account_welcome_img ) - ), - ), - 'editor_style' => 'mp-pro-account', - 'render_callback' => array( $this, 'render_pro_account_block' ), - ) - ); - - // Checkout - register_block_type( - 'memberpress/checkout', - array( - 'api_version' => 2, - 'attributes' => array( - 'show_welcome_image' => array( - 'type' => 'boolean', - ), - 'membership_id' => array( - 'type' => 'string', - 'default' => '', - ), - ), - 'render_callback' => array( $this, 'render_checkout_block' ), - 'editor_style' => 'mp-pro-checkout' - ) - ); - - // Account Links - register_block_type( - 'memberpress/account-links', - array( - 'api_version' => 2, - 'attributes' => array(), - 'render_callback' => array( $this, 'render_account_links_block') - ) - ); - - // Subscriptions - register_block_type( - 'memberpress/subscriptions', - array( - 'api_version' => 2, - 'attributes' => array( - 'order_by' => array( - 'type' => 'string', - 'default' => '' - ), - 'order' => array( - 'type' => 'string', - 'default' => '' - ), - 'not_logged_in_message' => array( - 'type' => 'string', - 'default' => __('You are not logged in.', 'memberpress') - ), - 'no_subscriptions_message' => array( - 'type' => 'string', - 'default' => __('You have no Subscriptions yet.', 'memberpress') - ), - 'top_description' => array( - 'type' => 'string' - ), - 'bottom_description' => array( - 'type' => 'string' - ), - 'use_access_url' => array( - 'type' => 'boolean' - ) - ), - 'render_callback' => array( $this, 'render_subscriptions_block') - ) - ); - - // Accounts Info - register_block_type( - 'memberpress/account-info', - array( - 'api_version' => 2, - 'attributes' => array( - 'field' => array( - 'type' => 'string', - 'default' => 'full_name' - ), - ), - 'render_callback' => array( $this, 'render_account_info' ), - ) - ); - } - - /** - * Renders a membership's signup form - * - * @param array $props Properties/data from the block - * - * @return string - */ - public function render_membership_signup_block( $props ) { - - $membership_id = isset( $props['membership'] ) ? (int) $props['membership'] : 0; - - if ( $membership_id > 0 ) { - ob_start(); - echo do_shortcode( "[mepr-membership-registration-form id='{$membership_id}']" ); - return ob_get_clean(); + + /** + * Render the frontend for the blocks on the server ("save" method must return null) + * + * @return void + */ + public function register_block_types_serverside() + { + $mepr_options = MeprOptions::fetch(); + + // Membership signup form block + register_block_type( + 'memberpress/membership-signup', + [ + 'attributes' => [ + 'membership' => [ + 'type' => 'string', + ], + ], + 'render_callback' => [$this, 'render_membership_signup_block'], + ] + ); + + // Account form block + register_block_type( + 'memberpress/account-form', + [ + 'attributes' => [], + 'render_callback' => [$this, 'render_account_block'], + ] + ); + + // Login form block + register_block_type( + 'memberpress/login-form', + [ + 'api_version' => 2, + 'attributes' => [ + 'use_redirect' => [ + 'type' => 'boolean', + ], + ], + 'render_callback' => [$this, 'render_login_block'], + ] + ); + + // Protected content block + register_block_type( + 'memberpress/protected-content', + [ + 'attributes' => [ + 'rule' => [ + 'type' => 'number', + ], + 'ifallowed' => [ + 'type' => 'string', + ], + 'unauth' => [ + 'type' => 'string', + ], + 'unauth_message' => [ + 'type' => 'string', + ], + ], + 'render_callback' => [$this, 'render_protected_content_block'], + ] + ); + + // Pro Login Form + register_block_type( + 'memberpress/pro-login-form', + [ + 'api_version' => 2, + 'attributes' => [ + 'show_welcome_image' => [ + 'type' => 'boolean', + 'default' => $mepr_options->design_show_login_welcome_image, + ], + 'welcome_image' => [ + 'type' => 'string', + 'default' => wp_get_attachment_url($mepr_options->design_login_welcome_img), + ], + 'admin_view' => [ + 'type' => 'boolean', + ], + ], + 'render_callback' => [$this, 'render_pro_login_block'], + 'editor_style' => 'mp-pro-login', + ] + ); + + // Pricing Columns for Pro Template + register_block_type( + 'memberpress/pro-pricing-table', + [ + 'api_version' => 2, + 'attributes' => [ + 'show_title' => [ + 'type' => 'boolean', + ], + 'button_highlight_color' => [ + 'type' => 'string', + 'default' => '#EF1010', + ], + 'group_id' => [ + 'type' => 'string', + 'default' => '', + ], + ], + 'render_callback' => [$this, 'render_pro_pricing_block'], + 'editor_style' => 'mp-pro-pricing', + ] + ); + + // Accounts Tab + register_block_type( + 'memberpress/pro-account-tabs', + [ + 'api_version' => 2, + 'attributes' => [ + 'show_welcome_image' => [ + 'type' => 'boolean', + 'default' => $mepr_options->design_show_account_welcome_image, + ], + 'welcome_image' => [ + 'type' => 'string', + 'default' => wp_get_attachment_url($mepr_options->design_account_welcome_img), + ], + ], + 'editor_style' => 'mp-pro-account', + 'render_callback' => [$this, 'render_pro_account_block'], + ] + ); + + // Checkout + register_block_type( + 'memberpress/checkout', + [ + 'api_version' => 2, + 'attributes' => [ + 'show_welcome_image' => [ + 'type' => 'boolean', + ], + 'membership_id' => [ + 'type' => 'string', + 'default' => '', + ], + ], + 'render_callback' => [$this, 'render_checkout_block'], + 'editor_style' => 'mp-pro-checkout', + ] + ); + + // Account Links + register_block_type( + 'memberpress/account-links', + [ + 'api_version' => 2, + 'attributes' => [], + 'render_callback' => [$this, 'render_account_links_block'], + ] + ); + + // Subscriptions + register_block_type( + 'memberpress/subscriptions', + [ + 'api_version' => 2, + 'attributes' => [ + 'order_by' => [ + 'type' => 'string', + 'default' => '', + ], + 'order' => [ + 'type' => 'string', + 'default' => '', + ], + 'not_logged_in_message' => [ + 'type' => 'string', + 'default' => __('You are not logged in.', 'memberpress'), + ], + 'no_subscriptions_message' => [ + 'type' => 'string', + 'default' => __('You have no Subscriptions yet.', 'memberpress'), + ], + 'top_description' => [ + 'type' => 'string', + ], + 'bottom_description' => [ + 'type' => 'string', + ], + 'use_access_url' => [ + 'type' => 'boolean', + ], + ], + 'render_callback' => [$this, 'render_subscriptions_block'], + ] + ); + + // Accounts Info + register_block_type( + 'memberpress/account-info', + [ + 'api_version' => 2, + 'attributes' => [ + 'field' => [ + 'type' => 'string', + 'default' => 'full_name', + ], + ], + 'render_callback' => [$this, 'render_account_info'], + ] + ); + } + + /** + * Renders a membership's signup form + * + * @param array $props Properties/data from the block + * + * @return string + */ + public function render_membership_signup_block($props) + { + + $membership_id = isset($props['membership']) ? (int) $props['membership'] : 0; + + if ($membership_id > 0) { + ob_start(); + echo do_shortcode("[mepr-membership-registration-form id='{$membership_id}']"); + return ob_get_clean(); + } + + return _x('Uh oh, something went wrong. Not a valid Membership form.', 'ui', 'memberpress'); } - return _x( 'Uh oh, something went wrong. Not a valid Membership form.', 'ui', 'memberpress' ); - } - - /** - * Renders the MP account form - * - * @return string - */ - public function render_account_block() { - ob_start(); - echo do_shortcode( '[mepr-account-form]' ); - return ob_get_clean(); - } - - /** - * Renders the MP login form - * - * @param array $props Properties/data from the block - * - * @return string - */ - public function render_login_block( $props ) { - $shortcode = isset( $props['use_redirect'] ) && true === $props['use_redirect'] ? "[mepr-login-form use_redirect='true']" : '[mepr-login-form]'; - ob_start(); - echo do_shortcode( $shortcode ); - return ob_get_clean(); - } - - /** - * Render the "dynamic" block - * - * @param array $attributes Properties/data from the block - * @param string $content Block content - * - * @return string - */ - public function render_protected_content_block( $attributes, $content ) { - - $attributes['ifallowed'] = ! empty( $attributes['ifallowed'] ) ? $attributes['ifallowed'] : 'show'; - - if ( ! isset( $attributes['unauth_message'] ) || '' === $attributes['unauth_message'] ) { - $attributes['unauth_message'] = __( 'You are unauthorized to view this content.', 'memberpress' ); + /** + * Renders the MP account form + * + * @return string + */ + public function render_account_block() + { + ob_start(); + echo do_shortcode('[mepr-account-form]'); + return ob_get_clean(); } - $content = MeprRulesCtrl::protect_shortcode_content( $attributes, $content ); + /** + * Renders the MP login form + * + * @param array $props Properties/data from the block + * + * @return string + */ + public function render_login_block($props) + { + $shortcode = isset($props['use_redirect']) && true === $props['use_redirect'] ? "[mepr-login-form use_redirect='true']" : '[mepr-login-form]'; + ob_start(); + echo do_shortcode($shortcode); + return ob_get_clean(); + } + + /** + * Render the "dynamic" block + * + * @param array $attributes Properties/data from the block + * @param string $content Block content + * + * @return string + */ + public function render_protected_content_block($attributes, $content) + { - return $content; - } + $attributes['ifallowed'] = ! empty($attributes['ifallowed']) ? $attributes['ifallowed'] : 'show'; - /** - * Renders the MP login form - * - * @param array $props Properties/data from the block - * - * @return string - */ - public function render_pro_login_block( $atts ) { - wp_enqueue_style( 'mp-pro-login' ); + if (! isset($attributes['unauth_message']) || '' === $attributes['unauth_message']) { + $attributes['unauth_message'] = __('You are unauthorized to view this content.', 'memberpress'); + } - $show_welcome_image = filter_var( $atts['show_welcome_image'], FILTER_VALIDATE_BOOLEAN ); + $content = MeprRulesCtrl::protect_shortcode_content($attributes, $content); - $admin_view = isset($atts['admin_view']) ? filter_var( $atts['admin_view'], FILTER_VALIDATE_BOOLEAN ) : false; + return $content; + } - $welcome_image = isset( $atts['welcome_image'] ) ? - esc_url_raw( $atts['welcome_image'] ) : ''; + /** + * Renders the MP login form + * + * @param array $props Properties/data from the block + * + * @return string + */ + public function render_pro_login_block($atts) + { + wp_enqueue_style('mp-pro-login'); - $shortcode = "[mepr-pro-login-form + $show_welcome_image = filter_var($atts['show_welcome_image'], FILTER_VALIDATE_BOOLEAN); + + $admin_view = isset($atts['admin_view']) ? filter_var($atts['admin_view'], FILTER_VALIDATE_BOOLEAN) : false; + + $welcome_image = isset($atts['welcome_image']) ? + esc_url_raw($atts['welcome_image']) : ''; + + $shortcode = "[mepr-pro-login-form show_welcome_image='$show_welcome_image' welcome_image='$welcome_image' admin_view='$admin_view']"; - ob_start(); - echo do_shortcode( $shortcode ); - return ob_get_clean(); - } + ob_start(); + echo do_shortcode($shortcode); + return ob_get_clean(); + } - /** - * Renders the MP login form - * - * @param array $props Properties/data from the block - * - * @return string - */ - public function render_pro_pricing_block( $atts ) { - wp_enqueue_style( 'mp-pro-pricing' ); + /** + * Renders the MP login form + * + * @param array $props Properties/data from the block + * + * @return string + */ + public function render_pro_pricing_block($atts) + { + wp_enqueue_style('mp-pro-pricing'); - $show_title = isset( $atts['show_title'] ) && - filter_var( $atts['show_title'], FILTER_VALIDATE_BOOLEAN ) ? - true : false; + $show_title = isset($atts['show_title']) && + filter_var($atts['show_title'], FILTER_VALIDATE_BOOLEAN) ? + true : false; - $button_highlight_color = isset( $atts['button_highlight_color'] ) ? - sanitize_text_field( $atts['button_highlight_color'] ) : ''; + $button_highlight_color = isset($atts['button_highlight_color']) ? + sanitize_text_field($atts['button_highlight_color']) : ''; - $group_id = isset( $atts['group_id'] ) ? - absint( $atts['group_id'] ) : ''; + $group_id = isset($atts['group_id']) ? + absint($atts['group_id']) : ''; - $shortcode = "[mepr-pro-pricing-table + $shortcode = "[mepr-pro-pricing-table show_title='$show_title' button_highlight_color='$button_highlight_color' group_id='$group_id']"; - ob_start(); - echo do_shortcode( $shortcode ); - return ob_get_clean(); - } + ob_start(); + echo do_shortcode($shortcode); + return ob_get_clean(); + } - /** - * Renders the MP login form - * - * @param array $props Properties/data from the block - * - * @return string - */ - public function render_pro_account_block( $atts ) { - wp_enqueue_style( 'mp-pro-account' ); + /** + * Renders the MP login form + * + * @param array $props Properties/data from the block + * + * @return string + */ + public function render_pro_account_block($atts) + { + wp_enqueue_style('mp-pro-account'); - $show_welcome_image = isset( $atts['show_welcome_image'] ) && - filter_var( $atts['show_welcome_image'], FILTER_VALIDATE_BOOLEAN ) ? - true : false; + $show_welcome_image = isset($atts['show_welcome_image']) && + filter_var($atts['show_welcome_image'], FILTER_VALIDATE_BOOLEAN) ? + true : false; - $welcome_image = isset( $atts['welcome_image'] ) ? - esc_url_raw( $atts['welcome_image'] ) : ''; + $welcome_image = isset($atts['welcome_image']) ? + esc_url_raw($atts['welcome_image']) : ''; - $shortcode = "[mepr-pro-account-tabs + $shortcode = "[mepr-pro-account-tabs show_welcome_image='$show_welcome_image' welcome_image='$welcome_image']"; - ob_start(); - echo do_shortcode( $shortcode ); - return ob_get_clean(); - } - - /** - * Renders the MP login form - * - * @param array $props Properties/data from the block - * - * @return string - */ - public function render_checkout_block( $atts ) { - wp_enqueue_style( 'mp-pro-checkout' ); - - $membership_id = isset( $atts['membership_id'] ) ? - absint( $atts['membership_id'] ) : ''; - - $shortcode = "[mepr-pro-checkout membership_id='$membership_id']"; - ob_start(); - echo do_shortcode( $shortcode ); - return ob_get_clean(); - } - - /** - * Renders the MP account links - * - * @param array $atts Properties/data from the block - * - * @return string - */ - public function render_account_links_block(array $atts ) { - ob_start(); - $mepr_options = MeprOptions::fetch(); - if(MeprUtils::is_user_logged_in()) { - $account_url = $mepr_options->account_page_url(); - $logout_url = MeprUtils::logout_url(); - MeprView::render('/account/logged_in_widget', get_defined_vars()); + ob_start(); + echo do_shortcode($shortcode); + return ob_get_clean(); } - else { - $login_url = MeprUtils::login_url(); - MeprView::render('/account/logged_out_widget', get_defined_vars()); - } - return ob_get_clean(); - } - - /** - * Renders the MP subscriptions - * - * @param array $atts Properties/data from the block - * - * @return string - */ - public function render_subscriptions_block(array $atts ) { - ob_start(); - $user = MeprUtils::get_currentuserinfo(); - $mepr_options = MeprOptions::fetch(); - - $order_by = isset( $atts['order_by'] ) ? - sanitize_text_field( $atts['order_by'] ) : ''; - $order = isset( $atts['order'] ) ? - sanitize_text_field( $atts['order'] ) : ''; - $not_logged_in_message = isset( $atts['not_logged_in_message'] ) ? - sanitize_text_field( $atts['not_logged_in_message'] ) : ''; - $no_subscriptions_message = isset( $atts['no_subscriptions_message'] ) ? - sanitize_text_field( $atts['no_subscriptions_message'] ) : ''; - $top_desc = isset( $atts['top_description'] ) ? - sanitize_text_field( $atts['top_description'] ) : ''; - $bottom_desc = isset( $atts['bottom_description'] ) ? - sanitize_text_field( $atts['bottom_description'] ) : ''; - $use_access_url = isset( $atts['use_access_url'] ) && - filter_var( $atts['use_access_url'], FILTER_VALIDATE_BOOLEAN ) ? - true : false; - - MeprView::render('/account/subscriptions_widget', get_defined_vars()); - return ob_get_clean(); - } - - /** - * Renders the MP account info - * - * @param array $props Properties/data from the block - * - * @return string - */ - public function render_account_info(array $props ) { - $shortcode = isset( $props['field'] ) - ? '[mepr-account-info field="' . sanitize_text_field($props['field']) . '"]' - : '[mepr-account-info field="full_name"]'; - ob_start(); - echo '

' . do_shortcode( $shortcode ) . '

'; - return ob_get_clean(); - } - - /** - * Enqueue the necessary scripts/styles in the editor - * - * @return void - */ - public function enqueue_editor_block_scripts() { - $asset_file = include MEPR_JS_PATH . '/build/blocks.asset.php'; - - $dependencies = array_unique( - array_merge( - array( - 'wp-blocks', - 'wp-i18n', - 'wp-editor', - ), // legacy dependencies - (array) $asset_file['dependencies'] - ) - ); - - wp_enqueue_script( - 'memberpress/blocks', - MEPR_JS_URL . '/build/blocks.js', - $dependencies, - $asset_file['version'], - true - ); - - $membership_options = array(); - $rule_options = array(); - - // Assemble MP Products into an options array - foreach ( MeprCptModel::all( 'MeprProduct' ) as $membership ) { - $membership_options[] = array( - 'label' => $membership->post_title, - 'value' => $membership->ID, - ); + + /** + * Renders the MP login form + * + * @param array $props Properties/data from the block + * + * @return string + */ + public function render_checkout_block($atts) + { + wp_enqueue_style('mp-pro-checkout'); + + $membership_id = isset($atts['membership_id']) ? + absint($atts['membership_id']) : ''; + + $shortcode = "[mepr-pro-checkout membership_id='$membership_id']"; + ob_start(); + echo do_shortcode($shortcode); + return ob_get_clean(); } - // Assemble MP Rules into an options array - foreach ( MeprCptModel::all( 'MeprRule' ) as $rule ) { + /** + * Renders the MP account links + * + * @param array $atts Properties/data from the block + * + * @return string + */ + public function render_account_links_block(array $atts) + { + ob_start(); + $mepr_options = MeprOptions::fetch(); + if (MeprUtils::is_user_logged_in()) { + $account_url = $mepr_options->account_page_url(); + $logout_url = MeprUtils::logout_url(); + MeprView::render('/account/logged_in_widget', get_defined_vars()); + } else { + $login_url = MeprUtils::login_url(); + MeprView::render('/account/logged_out_widget', get_defined_vars()); + } + return ob_get_clean(); + } - $rule_options[] = array( - 'label' => $rule->post_title, - 'value' => $rule->ID, - 'ruleLink' => get_edit_post_link( $rule->ID, '&' ), - ); + /** + * Renders the MP subscriptions + * + * @param array $atts Properties/data from the block + * + * @return string + */ + public function render_subscriptions_block(array $atts) + { + ob_start(); + $user = MeprUtils::get_currentuserinfo(); + $mepr_options = MeprOptions::fetch(); + + $order_by = isset($atts['order_by']) ? + sanitize_text_field($atts['order_by']) : ''; + $order = isset($atts['order']) ? + sanitize_text_field($atts['order']) : ''; + $not_logged_in_message = isset($atts['not_logged_in_message']) ? + sanitize_text_field($atts['not_logged_in_message']) : ''; + $no_subscriptions_message = isset($atts['no_subscriptions_message']) ? + sanitize_text_field($atts['no_subscriptions_message']) : ''; + $top_desc = isset($atts['top_description']) ? + sanitize_text_field($atts['top_description']) : ''; + $bottom_desc = isset($atts['bottom_description']) ? + sanitize_text_field($atts['bottom_description']) : ''; + $use_access_url = isset($atts['use_access_url']) && + filter_var($atts['use_access_url'], FILTER_VALIDATE_BOOLEAN) ? + true : false; + + MeprView::render('/account/subscriptions_widget', get_defined_vars()); + return ob_get_clean(); } - // Assemble MP Groups into an options array - $groups = array(); - foreach ( MeprCptModel::all( 'MeprGroup' ) as $group ) { - $groups[] = array( - 'label' => $group->post_title, - 'value' => $group->ID, - ); + /** + * Renders the MP account info + * + * @param array $props Properties/data from the block + * + * @return string + */ + public function render_account_info(array $props) + { + $shortcode = isset($props['field']) + ? '[mepr-account-info field="' . sanitize_text_field($props['field']) . '"]' + : '[mepr-account-info field="full_name"]'; + ob_start(); + echo '

' . do_shortcode($shortcode) . '

'; + return ob_get_clean(); } - // Assemble custom fields into an options array - $mepr_options = MeprOptions::fetch(); - $custom_fields = array(); - if(!empty($mepr_options->custom_fields)) { - foreach ( $mepr_options->custom_fields as $field ) { - $custom_fields[] = array( - 'label' => $field->field_key, - 'value' => $field->field_key, + /** + * Enqueue the necessary scripts/styles in the editor + * + * @return void + */ + public function enqueue_editor_block_scripts() + { + $asset_file = include MEPR_JS_PATH . '/build/blocks.asset.php'; + + $dependencies = array_unique( + array_merge( + [ + 'wp-blocks', + 'wp-i18n', + 'wp-editor', + ], // legacy dependencies + (array) $asset_file['dependencies'] + ) ); - } - } - // Make the data available to the script - wp_localize_script( - 'memberpress/blocks', - 'memberpressBlocks', - array( - 'memberships' => $membership_options, - 'rules' => $rule_options, - 'groups' => $groups, - 'custom_fields' => $custom_fields, - 'redirect_url_setting_url' => menu_page_url( 'memberpress-options', false ) . '#mepr-accounts', - ) - ); - - wp_enqueue_style('mp-theme', MEPR_CSS_URL . '/ui/theme.css', null, MEPR_VERSION); - - } - - /** - * Enqueue the necessary scripts / styles for each block - * - * @return void - */ - public function enqueue_block_scripts() { - - // Register account scripts - wp_register_style( 'mp-pro-fonts', MEPR_CSS_URL . '/readylaunch/fonts.css', null, MEPR_VERSION ); - wp_register_style( 'mp-pro-login', MEPR_CSS_URL . '/readylaunch/login.css', null, MEPR_VERSION ); - wp_register_style( 'mp-pro-account', MEPR_CSS_URL . '/readylaunch/account.css', array( 'mp-pro-fonts', 'mp-pro-login' ), MEPR_VERSION ); - - // Register pricing scripts - wp_register_style( 'mp-pro-pricing', MEPR_CSS_URL . '/readylaunch/pricing.css', null, MEPR_VERSION ); - - // Register checkout scripts - $prereqs = MeprHooks::apply_filters( 'mepr-signup-styles', array() ); - wp_register_style( 'mp-signup', MEPR_CSS_URL . '/signup.css', $prereqs, MEPR_VERSION ); - wp_register_style( 'mp-pro-checkout', MEPR_CSS_URL . '/readylaunch/checkout.css', array( 'mp-signup' ), MEPR_VERSION ); - } - - /** - * Filter to add the necessary frontend enqueues for Membership Signup block - * - * @param mixed $return MeprProduct object if scripts will be enqueued, else false - * @param object $post WP_Post - * - * @return boolean - */ - public function signup_block_enqueues( $return, $post ) { - - if ( ! isset( $post->post_content ) ) { - return $return; - } + wp_enqueue_script( + 'memberpress/blocks', + MEPR_JS_URL . '/build/blocks.js', + $dependencies, + $asset_file['version'], + true + ); - // We don't want to mess with enqueues on MemberPress products since the files are already properly enqueued there - if ( ! is_object( $return ) || ! is_a( $return, 'MeprProduct' ) ) { - $membership = false; - - // Check that the signup form block is added - $match = preg_match( '/(?:wp:memberpress\/membership-signup\s)(\{(?:[^{}]|(?R))*\})/', $post->post_content, $matches ); - - if ( 1 === $match && isset( $matches[1] ) && isset( json_decode( $matches[1], true )['membership'] ) ) { - $membership = new MeprProduct( json_decode( $matches[1], true )['membership'] ); - } elseif ( preg_match( - '~(?:wp:memberpress\/checkout\s)+{\"membership_id\"\:[\"\\\'](\d+)[\"\\\']~', - $post->post_content, - $m - ) ) { - $membership = new MeprProduct( $m[1] ); - } - - // Valid membership - if ( isset( $membership->ID ) && $membership->ID > 0 ) { - $return = $membership; // Return the MeprProduct instead of just boolean true (backward compatibility) - } - } + $membership_options = []; + $rule_options = []; + + // Assemble MP Products into an options array + foreach (MeprCptModel::all('MeprProduct') as $membership) { + $membership_options[] = [ + 'label' => $membership->post_title, + 'value' => $membership->ID, + ]; + } + + // Assemble MP Rules into an options array + foreach (MeprCptModel::all('MeprRule') as $rule) { + $rule_options[] = [ + 'label' => $rule->post_title, + 'value' => $rule->ID, + 'ruleLink' => get_edit_post_link($rule->ID, '&'), + ]; + } + + // Assemble MP Groups into an options array + $groups = []; + foreach (MeprCptModel::all('MeprGroup') as $group) { + $groups[] = [ + 'label' => $group->post_title, + 'value' => $group->ID, + ]; + } + + // Assemble custom fields into an options array + $mepr_options = MeprOptions::fetch(); + $custom_fields = []; + if (!empty($mepr_options->custom_fields)) { + foreach ($mepr_options->custom_fields as $field) { + $custom_fields[] = [ + 'label' => $field->field_key, + 'value' => $field->field_key, + ]; + } + } + + // Make the data available to the script + wp_localize_script( + 'memberpress/blocks', + 'memberpressBlocks', + [ + 'memberships' => $membership_options, + 'rules' => $rule_options, + 'groups' => $groups, + 'custom_fields' => $custom_fields, + 'redirect_url_setting_url' => menu_page_url('memberpress-options', false) . '#mepr-accounts', + ] + ); - return $return; - } - - /** - * Filter to add the necessary frontend enqueues for the Account Form block - * - * @param boolean $return Whether the page is an "Account" page - * @param object $post WP_Post - * - * @return boolean - */ - public function account_block_enqueues( $return, $post ) { - - if ( ! isset( $post->post_content ) ) { - return $return; + wp_enqueue_style('mp-theme', MEPR_CSS_URL . '/ui/theme.css', null, MEPR_VERSION); } - // Post is an "Account" page if it has the Account Form block - if ( has_block( 'memberpress/account-form', $post ) || MeprAppHelper::block_template_has_block('account-form') ) { - $return = true; + /** + * Enqueue the necessary scripts / styles for each block + * + * @return void + */ + public function enqueue_block_scripts() + { + + // Register account scripts + wp_register_style('mp-pro-fonts', MEPR_CSS_URL . '/readylaunch/fonts.css', null, MEPR_VERSION); + wp_register_style('mp-pro-login', MEPR_CSS_URL . '/readylaunch/login.css', null, MEPR_VERSION); + wp_register_style('mp-pro-account', MEPR_CSS_URL . '/readylaunch/account.css', ['mp-pro-fonts', 'mp-pro-login'], MEPR_VERSION); + + // Register pricing scripts + wp_register_style('mp-pro-pricing', MEPR_CSS_URL . '/readylaunch/pricing.css', null, MEPR_VERSION); + + // Register checkout scripts + $prereqs = MeprHooks::apply_filters('mepr-signup-styles', []); + wp_register_style('mp-signup', MEPR_CSS_URL . '/signup.css', $prereqs, MEPR_VERSION); + wp_register_style('mp-pro-checkout', MEPR_CSS_URL . '/readylaunch/checkout.css', ['mp-signup'], MEPR_VERSION); } - return $return; - } + /** + * Filter to add the necessary frontend enqueues for Membership Signup block + * + * @param mixed $return MeprProduct object if scripts will be enqueued, else false + * @param object $post WP_Post + * + * @return boolean + */ + public function signup_block_enqueues($return, $post) + { + + if (! isset($post->post_content)) { + return $return; + } + + // We don't want to mess with enqueues on MemberPress products since the files are already properly enqueued there + if (! is_object($return) || ! is_a($return, 'MeprProduct')) { + $membership = false; + + // Check that the signup form block is added + $match = preg_match('/(?:wp:memberpress\/membership-signup\s)(\{(?:[^{}]|(?R))*\})/', $post->post_content, $matches); + + if (1 === $match && isset($matches[1]) && isset(json_decode($matches[1], true)['membership'])) { + $membership = new MeprProduct(json_decode($matches[1], true)['membership']); + } elseif ( + preg_match( + '~(?:wp:memberpress\/checkout\s)+{\"membership_id\"\:[\"\\\'](\d+)[\"\\\']~', + $post->post_content, + $m + ) + ) { + $membership = new MeprProduct($m[1]); + } + + // Valid membership + if (isset($membership->ID) && $membership->ID > 0) { + $return = $membership; // Return the MeprProduct instead of just boolean true (backward compatibility) + } + } + + return $return; + } + /** + * Filter to add the necessary frontend enqueues for the Account Form block + * + * @param boolean $return Whether the page is an "Account" page + * @param object $post WP_Post + * + * @return boolean + */ + public function account_block_enqueues($return, $post) + { + + if (! isset($post->post_content)) { + return $return; + } + + // Post is an "Account" page if it has the Account Form block + if (has_block('memberpress/account-form', $post) || MeprAppHelper::block_template_has_block('account-form')) { + $return = true; + } + + return $return; + } } // End MeprBlocksCtrl diff --git a/app/controllers/MeprCheckoutCtrl.php b/app/controllers/MeprCheckoutCtrl.php index ebcc993..286a533 100644 --- a/app/controllers/MeprCheckoutCtrl.php +++ b/app/controllers/MeprCheckoutCtrl.php @@ -1,1055 +1,1078 @@ -id) && $original_txn->id > 0) { - $original_txn = new MeprTransaction($original_txn->id); - } - else { - return; +class MeprCheckoutCtrl extends MeprBaseCtrl +{ + public function load_hooks() + { + add_action('wp_enqueue_scripts', [$this,'enqueue_scripts']); + add_action('mepr-signup', [$this, 'process_spc_payment_form'], 100); // 100 priority to give other things a chance to hook in before SPC takes over the world + add_filter('mepr_signup_form_payment_description', [$this, 'maybe_render_payment_form'], 10, 4); + MeprHooks::add_shortcode('mepr-ecommerce-tracking', [$this, 'replace_tracking_codes']); + add_filter('mepr-signup-checkout-url', [$this, 'handle_spc_checkout_url'], 10, 2); + add_action('mepr_readylaunch_thank_you_page_after_content', [$this, 'maybe_show_order_bumps_error_message_in_readylaunch']); + add_filter('mepr_options_helper_payment_methods', [$this, 'exclude_disconnected_gateways'], 10, 2); + add_filter('the_content', [$this, 'maybe_show_order_bumps_error_message'], 1); + add_action('wp_ajax_mepr_get_checkout_state', [$this, 'get_checkout_state']); + add_action('wp_ajax_nopriv_mepr_get_checkout_state', [$this, 'get_checkout_state']); } - $order = $original_txn->order(); + public function maybe_show_order_bumps_error_message_in_readylaunch() + { + if (!isset($_GET['trans_num'])) { + return; + } - if ( !$order ) { - return; - } + $trans_num = sanitize_text_field(wp_unslash($_GET['trans_num'])); + $original_txn = MeprTransaction::get_one_by_trans_num($trans_num); + + if (isset($original_txn->id) && $original_txn->id > 0) { + $original_txn = new MeprTransaction($original_txn->id); + } else { + return; + } - $bumps = MeprTransaction::get_all_by_order_id( $order->id ); - $errors = []; + $order = $original_txn->order(); - foreach ( $bumps as $txn ) { - $product = $txn->product(); - $meta = $txn->get_meta( '_authorizenet_txn_error_', true ); + if (!$order) { + return; + } - if (empty($meta)) { - continue; - } + $bumps = MeprTransaction::get_all_by_order_id($order->id); + $errors = []; - $error = (explode('|', $meta)); - $error_message = sprintf( __( 'Notice: %s purchase failed. Click here to purchase it separately.', 'memberpress' ), $product->post_title, get_permalink($product->ID) ); + foreach ($bumps as $txn) { + $product = $txn->product(); + $meta = $txn->get_meta('_authorizenet_txn_error_', true); + if (empty($meta)) { + continue; + } + + $error = (explode('|', $meta)); + $error_message = sprintf(__('Notice: %1$s purchase failed. Click here to purchase it separately.', 'memberpress'), $product->post_title, get_permalink($product->ID)); - if ( ! empty( $error ) ) { - $errors[] = $error_message; - } - } - if ( !empty($errors) ) { - MeprView::render('/shared/errors', get_defined_vars()); - echo "
"; + if (! empty($error)) { + $errors[] = $error_message; + } + } + + if (!empty($errors)) { + MeprView::render('/shared/errors', get_defined_vars()); + echo '
'; + } } - } - public function maybe_show_order_bumps_error_message( $content ) { - if ( is_singular() && in_the_loop() && is_main_query() ) { - $mepr_options = MeprOptions::fetch(); + public function maybe_show_order_bumps_error_message($content) + { + if (is_singular() && in_the_loop() && is_main_query()) { + $mepr_options = MeprOptions::fetch(); - if ( $mepr_options->thankyou_page_id != get_the_ID() ) { - return $content; - } + if ($mepr_options->thankyou_page_id != get_the_ID()) { + return $content; + } - if (!isset($_GET['trans_num'])) { - return $content; - } + if (!isset($_GET['trans_num'])) { + return $content; + } - $trans_num = sanitize_text_field( wp_unslash( $_GET['trans_num'] ) ); - $original_txn = MeprTransaction::get_one_by_trans_num( $trans_num ); + $trans_num = sanitize_text_field(wp_unslash($_GET['trans_num'])); + $original_txn = MeprTransaction::get_one_by_trans_num($trans_num); - if (isset($original_txn->id) && $original_txn->id > 0) { - $original_txn = new MeprTransaction($original_txn->id); - } - else { - return $content; - } + if (isset($original_txn->id) && $original_txn->id > 0) { + $original_txn = new MeprTransaction($original_txn->id); + } else { + return $content; + } - $order = $original_txn->order(); + $order = $original_txn->order(); - if ( !$order ) { - return $content; - } + if (!$order) { + return $content; + } - $bumps = MeprTransaction::get_all_by_order_id( $order->id ); - $errors = []; + $bumps = MeprTransaction::get_all_by_order_id($order->id); + $errors = []; - foreach ( $bumps as $txn ) { - $product = $txn->product(); - $meta = $txn->get_meta( '_authorizenet_txn_error_', true ); + foreach ($bumps as $txn) { + $product = $txn->product(); + $meta = $txn->get_meta('_authorizenet_txn_error_', true); - if (empty($meta)) { - continue; - } + if (empty($meta)) { + continue; + } + + $error = (explode('|', $meta)); + $error_message = sprintf(__('Notice: %1$s purchase failed. Click here to purchase it separately.', 'memberpress'), $product->post_title, get_permalink($product->ID)); - $error = (explode('|', $meta)); - $error_message = sprintf( __( 'Notice: %s purchase failed. Click here to purchase it separately.', 'memberpress' ), $product->post_title, get_permalink($product->ID) ); + if (! empty($error)) { + $errors[] = $error_message; + } + } - if ( ! empty( $error ) ) { - $errors[] = $error_message; + if (!empty($errors)) { + ob_start(); + MeprView::render('/shared/errors', get_defined_vars()); + $error_section = ob_get_contents(); + ob_end_clean(); + $content .= $error_section; + } } - } - if ( !empty($errors) ) { - ob_start(); - MeprView::render('/shared/errors', get_defined_vars()); - $error_section = ob_get_contents(); - ob_end_clean(); - $content .= $error_section; - } + return $content; } - return $content; - } - - public function replace_tracking_codes($atts, $content='') { - $atts = shortcode_atts( - array( - 'membership' => null, - ), - $atts, - 'mepr-ecommerce-tracking' - ); - - if(!($this->request_has_valid_thank_you_params($_GET) && - $this->request_has_valid_thank_you_membership_id($_GET) && - $this->request_has_valid_thank_you_trans_num($_GET) && - $this->request_has_valid_thank_you_membership($atts, $_GET))) { - return ''; - } + public function replace_tracking_codes($atts, $content = '') + { + $atts = shortcode_atts( + [ + 'membership' => null, + ], + $atts, + 'mepr-ecommerce-tracking' + ); - $tracking_codes = array( - '%%subtotal%%' => array('MeprTransaction' => 'tracking_subtotal'), - '%%tax_amount%%' => array('MeprTransaction' => 'tracking_tax_amount'), - '%%tax_rate%%' => array('MeprTransaction' => 'tracking_tax_rate'), - '%%total%%' => array('MeprTransaction' => 'tracking_total'), - '%%txn_num%%' => array('MeprTransaction' => 'trans_num'), - '%%sub_id%%' => array('MeprTransaction' => 'subscription_id'), - '%%txn_id%%' => array('MeprTransaction' => 'id'), - '%%sub_num%%' => array('MeprSubscription' => 'subscr_id'), - '%%membership_amount%%' => array('MeprSubscription' => 'price'), - '%%trial_days%%' => array('MeprSubscription' => 'trial_days'), - '%%trial_amount%%' => array('MeprSubscription' => 'trial_amount'), - '%%username%%' => array('MeprUser' => 'user_login'), - '%%user_email%%' => array('MeprUser' => 'user_email'), - '%%user_id%%' => array('MeprUser' => 'ID'), - '%%membership_name%%' => array('MeprProduct' => 'post_title'), - '%%membership_id%%' => array('MeprProduct' => 'ID'), - ); - - foreach($tracking_codes as $code => $mapping) { - // Make sure the content has a code to replace - if(strpos($content, $code) !== false) { - foreach($mapping as $model => $attr) { - switch($model) { - case 'MeprTransaction': - // Only fetch the object once! - if(!isset($txn)) { - if(isset($_GET['trans_num']) && !empty($_GET['trans_num'])) { - $rec = $model::get_one_by_trans_num($_GET['trans_num']); - $txn = $obj = new MeprTransaction($rec->id); - } - elseif(isset($_GET['transaction_id']) && !empty($_GET['transaction_id'])) { - $txn = $obj = new MeprTransaction((int) $_GET['transaction_id']); - } - } - break; - case 'MeprSubscription': - if(!isset($sub)) { - if(isset($_GET['subscr_id']) && !empty($_GET['subscr_id'])) { - $sub = $obj = $model::get_one_by_subscr_id($_GET['subscr_id']); - } - elseif(isset($_GET['subscription_id']) && !empty($_GET['subscription_id'])) { - $sub = $obj = $model::get_one((int) $_GET['subscription_id']); + if ( + !($this->request_has_valid_thank_you_params($_GET) && + $this->request_has_valid_thank_you_membership_id($_GET) && + $this->request_has_valid_thank_you_trans_num($_GET) && + $this->request_has_valid_thank_you_membership($atts, $_GET)) + ) { + return ''; + } + + $tracking_codes = [ + '%%subtotal%%' => ['MeprTransaction' => 'tracking_subtotal'], + '%%tax_amount%%' => ['MeprTransaction' => 'tracking_tax_amount'], + '%%tax_rate%%' => ['MeprTransaction' => 'tracking_tax_rate'], + '%%total%%' => ['MeprTransaction' => 'tracking_total'], + '%%txn_num%%' => ['MeprTransaction' => 'trans_num'], + '%%sub_id%%' => ['MeprTransaction' => 'subscription_id'], + '%%txn_id%%' => ['MeprTransaction' => 'id'], + '%%sub_num%%' => ['MeprSubscription' => 'subscr_id'], + '%%membership_amount%%' => ['MeprSubscription' => 'price'], + '%%trial_days%%' => ['MeprSubscription' => 'trial_days'], + '%%trial_amount%%' => ['MeprSubscription' => 'trial_amount'], + '%%username%%' => ['MeprUser' => 'user_login'], + '%%user_email%%' => ['MeprUser' => 'user_email'], + '%%user_id%%' => ['MeprUser' => 'ID'], + '%%membership_name%%' => ['MeprProduct' => 'post_title'], + '%%membership_id%%' => ['MeprProduct' => 'ID'], + ]; + + foreach ($tracking_codes as $code => $mapping) { + // Make sure the content has a code to replace + if (strpos($content, $code) !== false) { + foreach ($mapping as $model => $attr) { + switch ($model) { + case 'MeprTransaction': + // Only fetch the object once! + if (!isset($txn)) { + if (isset($_GET['trans_num']) && !empty($_GET['trans_num'])) { + $rec = $model::get_one_by_trans_num($_GET['trans_num']); + $txn = $obj = new MeprTransaction($rec->id); + } elseif (isset($_GET['transaction_id']) && !empty($_GET['transaction_id'])) { + $txn = $obj = new MeprTransaction((int) $_GET['transaction_id']); + } + } + break; + case 'MeprSubscription': + if (!isset($sub)) { + if (isset($_GET['subscr_id']) && !empty($_GET['subscr_id'])) { + $sub = $obj = $model::get_one_by_subscr_id($_GET['subscr_id']); + } elseif (isset($_GET['subscription_id']) && !empty($_GET['subscription_id'])) { + $sub = $obj = $model::get_one((int) $_GET['subscription_id']); + } + } + break; + case 'MeprUser': + if (!isset($user)) { + $user = $obj = MeprUtils::get_currentuserinfo(); + } + break; + case 'MeprProduct': + if (!isset($prod) && isset($_GET['membership_id']) && !empty($_GET['membership_id'])) { + $prod = $obj = new $model($_GET['membership_id']); + } + break; + default: + unset($obj); + } + if (isset($obj) && (isset($obj->id) && (int) $obj->id > 0) || (isset($obj->ID) && (int) $obj->ID > 0)) { + $content = str_replace($code, $obj->$attr, $content); + break; // once we've replaced the code time to move on + } } - } - break; - case 'MeprUser': - if(!isset($user)) { - $user = $obj = MeprUtils::get_currentuserinfo(); - } - break; - case 'MeprProduct': - if(!isset($prod) && isset($_GET['membership_id']) && !empty($_GET['membership_id'])) { - $prod = $obj = new $model($_GET['membership_id']); - } - break; - default: - unset($obj); - } - if(isset($obj) && (isset($obj->id) && (int) $obj->id > 0) || (isset($obj->ID) && (int) $obj->ID > 0)) { - $content = str_replace($code, $obj->$attr, $content); - break; // once we've replaced the code time to move on - } + // Blank out the code if it isn't found + $content = str_replace($code, '', $content); + } } - // Blank out the code if it isn't found - $content = str_replace($code, '', $content); - } + return $content; } - return $content; - } - /** Enqueue gateway specific js/css if required */ - public function enqueue_scripts() { - global $post; - $mepr_options = MeprOptions::fetch(); + /** + * Enqueue gateway specific js/css if required + */ + public function enqueue_scripts() + { + global $post; + $mepr_options = MeprOptions::fetch(); - if(MeprProduct::is_product_page($post)) { + if (MeprProduct::is_product_page($post)) { + $has_phone = false; - $has_phone = false; + if (! empty($mepr_options->custom_fields)) { + foreach ($mepr_options->custom_fields as $field) { + if ('tel' === $field->field_type && $field->show_on_signup) { + $has_phone = true; + break; + } + } + } - if ( ! empty( $mepr_options->custom_fields ) ) { - foreach ( $mepr_options->custom_fields as $field ) { - if ( 'tel' === $field->field_type && $field->show_on_signup ) { - $has_phone = true; - break; - } + // Check if there's a phone field + if ($has_phone) { + wp_enqueue_style('mepr-phone-css', MEPR_CSS_URL . '/intlTelInput.min.css', '', '16.0.0'); + wp_enqueue_style('mepr-tel-config-css', MEPR_CSS_URL . '/tel_input.css', '', MEPR_VERSION); + wp_enqueue_script('mepr-phone-js', MEPR_JS_URL . '/intlTelInput.js', '', '16.0.0', true); + wp_enqueue_script('mepr-tel-config-js', MEPR_JS_URL . '/tel_input.js', ['mepr-phone-js', 'mp-signup'], MEPR_VERSION, true); + wp_localize_script('mepr-tel-config-js', 'meprTel', MeprHooks::apply_filters('mepr-phone-input-config', [ + 'defaultCountry' => strtolower(get_option('mepr_biz_country')), + 'utilsUrl' => MEPR_JS_URL . '/intlTelInputUtils.js', + 'onlyCountries' => '', + ])); + } + + if ( + ((isset($_REQUEST['action']) && + $_REQUEST['action'] === 'checkout' && + ( (isset($_REQUEST['mepr_transaction_id']) && + ($txn = new MeprTransaction($_REQUEST['mepr_transaction_id']))) || + (isset($_REQUEST['txn']) && + ($txn = new MeprTransaction($_REQUEST['txn']))) + ) && + $txn->id > 0 && + ($pm = $txn->payment_method())) || + (MeprUtils::is_user_logged_in() && + isset($_REQUEST['action']) && + $_REQUEST['action'] === 'update' && + isset($_REQUEST['sub']) && + ($sub = new MeprSubscription($_REQUEST['sub'])) && + $sub->id > 0 && + ($pm = $sub->payment_method()))) && + ($pm instanceof MeprBaseRealGateway) + ) { + wp_register_script('mepr-checkout-js', MEPR_JS_URL . '/checkout.js', ['jquery', 'jquery.payment'], MEPR_VERSION); + $pm->enqueue_payment_form_scripts(); + } } - } - - // Check if there's a phone field - if ( $has_phone ) { - wp_enqueue_style( 'mepr-phone-css', MEPR_CSS_URL . '/intlTelInput.min.css', '', '16.0.0' ); - wp_enqueue_style( 'mepr-tel-config-css', MEPR_CSS_URL . '/tel_input.css', '', MEPR_VERSION ); - wp_enqueue_script( 'mepr-phone-js', MEPR_JS_URL . '/intlTelInput.js', '', '16.0.0', true ); - wp_enqueue_script( 'mepr-tel-config-js', MEPR_JS_URL . '/tel_input.js', array( 'mepr-phone-js', 'mp-signup' ), MEPR_VERSION, true ); - wp_localize_script( 'mepr-tel-config-js', 'meprTel', MeprHooks::apply_filters( 'mepr-phone-input-config', array( - 'defaultCountry' => strtolower( get_option( 'mepr_biz_country' ) ), - 'utilsUrl' => MEPR_JS_URL . '/intlTelInputUtils.js', - 'onlyCountries' => '' - ) ) ); - } - - if(((isset($_REQUEST['action']) && - $_REQUEST['action'] === 'checkout' && - ( (isset($_REQUEST['mepr_transaction_id']) && - ($txn = new MeprTransaction($_REQUEST['mepr_transaction_id']))) || - (isset($_REQUEST['txn']) && - ($txn = new MeprTransaction($_REQUEST['txn']))) - ) && - $txn->id > 0 && - ($pm = $txn->payment_method())) || - (MeprUtils::is_user_logged_in() && - isset($_REQUEST['action']) && - $_REQUEST['action'] === 'update' && - isset($_REQUEST['sub']) && - ($sub = new MeprSubscription($_REQUEST['sub'])) && - $sub->id > 0 && - ($pm = $sub->payment_method()))) && - ($pm instanceof MeprBaseRealGateway)) { - wp_register_script('mepr-checkout-js', MEPR_JS_URL . '/checkout.js', array('jquery', 'jquery.payment'), MEPR_VERSION); - $pm->enqueue_payment_form_scripts(); - } - } - } - - /** - * Renders the payment form if SPC is enabled and supported by the payment method - * Called from: mepr_signup_form_payment_description filter - * Returns: description includding form for SPC if enabled - */ - public function maybe_render_payment_form($description, $payment_method, $first, $product = null) { - $mepr_options = MeprOptions::fetch(); - if( ($mepr_options->enable_spc || $mepr_options->design_enable_checkout_template) && $payment_method->has_spc_form) { - // TODO: Maybe we queue these up from wp_enqueue_scripts? - wp_register_script('mepr-checkout-js', MEPR_JS_URL . '/checkout.js', array('jquery', 'jquery.payment'), MEPR_VERSION); - wp_enqueue_script('mepr-checkout-js'); - $payment_method->enqueue_payment_form_scripts(); - $description = $payment_method->spc_payment_fields($product); - } - return $description; - } - - public function display_signup_form($product) { - $mepr_options = MeprOptions::fetch(); - $mepr_blogurl = home_url(); - $mepr_coupon_code = ''; - - extract($_REQUEST, EXTR_SKIP); - if ( isset( $_REQUEST['errors'] ) ) { - if ( is_array( $_REQUEST['errors'] ) ) { - $errors = array_map( 'wp_kses_post', $_REQUEST['errors'] ); // Use kses here so our error HTML isn't stripped - } else { - $errors = [ wp_kses_post( $_REQUEST['errors'] ) ]; - } - } - //See if Coupon was passed via GET - if(isset($_GET['coupon']) && !empty($_GET['coupon'])) { - if(MeprCoupon::is_valid_coupon_code($_GET['coupon'], $product->ID)) { - $mepr_coupon_code = htmlentities( sanitize_text_field( $_GET['coupon'] ) ); - } } - if(MeprUtils::is_user_logged_in()) { - $mepr_current_user = MeprUtils::get_currentuserinfo(); + /** + * Renders the payment form if SPC is enabled and supported by the payment method + * Called from: mepr_signup_form_payment_description filter + * Returns: description includding form for SPC if enabled + */ + public function maybe_render_payment_form($description, $payment_method, $first, $product = null) + { + $mepr_options = MeprOptions::fetch(); + if (($mepr_options->enable_spc || $mepr_options->design_enable_checkout_template) && $payment_method->has_spc_form) { + // TODO: Maybe we queue these up from wp_enqueue_scripts? + wp_register_script('mepr-checkout-js', MEPR_JS_URL . '/checkout.js', ['jquery', 'jquery.payment'], MEPR_VERSION); + wp_enqueue_script('mepr-checkout-js'); + $payment_method->enqueue_payment_form_scripts(); + $description = $payment_method->spc_payment_fields($product); + } + return $description; } - $first_name_value = ''; - if(isset($user_first_name)) { - $first_name_value = esc_attr(stripslashes($user_first_name)); - } - elseif(MeprUtils::is_user_logged_in()) { - $first_name_value = (string)$mepr_current_user->first_name; - } + public function display_signup_form($product) + { + $mepr_options = MeprOptions::fetch(); + $mepr_blogurl = home_url(); + $mepr_coupon_code = ''; + + extract($_REQUEST, EXTR_SKIP); + if (isset($_REQUEST['errors'])) { + if (is_array($_REQUEST['errors'])) { + $errors = array_map('wp_kses_post', $_REQUEST['errors']); // Use kses here so our error HTML isn't stripped + } else { + $errors = [wp_kses_post($_REQUEST['errors'])]; + } + } + // See if Coupon was passed via GET + if (isset($_GET['coupon']) && !empty($_GET['coupon'])) { + if (MeprCoupon::is_valid_coupon_code($_GET['coupon'], $product->ID)) { + $mepr_coupon_code = htmlentities(sanitize_text_field($_GET['coupon'])); + } + } - $last_name_value = ''; - if(isset($user_last_name)) { - $last_name_value = esc_attr(stripslashes($user_last_name)); - } - elseif(MeprUtils::is_user_logged_in()) { - $last_name_value = (string)$mepr_current_user->last_name; - } + if (MeprUtils::is_user_logged_in()) { + $mepr_current_user = MeprUtils::get_currentuserinfo(); + } - if(isset($errors) and !empty($errors)) { - MeprView::render("/shared/errors", get_defined_vars()); - } + $first_name_value = ''; + if (isset($user_first_name)) { + $first_name_value = esc_attr(stripslashes($user_first_name)); + } elseif (MeprUtils::is_user_logged_in()) { + $first_name_value = (string)$mepr_current_user->first_name; + } - // Gather payment methods for checkout - $payment_methods = $product->payment_methods(); - if(empty($payment_methods)) { - $payment_methods = array_keys($mepr_options->integrations); - } - $payment_methods = MeprHooks::apply_filters('mepr_options_helper_payment_methods', $payment_methods, 'mepr_payment_method', $product); - $payment_methods = array_map(function($pm_id) use($mepr_options) { - return $mepr_options->payment_method($pm_id); - }, $payment_methods); - - static $unique_suffix = 0; - $unique_suffix++; - - $payment_required = MeprHooks::apply_filters('mepr_signup_payment_required', $product->is_payment_required($mepr_coupon_code), $product); - - if($mepr_options->enable_spc) { - if(MeprReadyLaunchCtrl::template_enabled( 'checkout' ) || MeprAppHelper::has_block( 'memberpress/checkout' )){ - $is_rl_widget = ( is_active_sidebar( 'mepr_rl_registration_footer' ) || is_active_sidebar( 'mepr_rl_global_footer' ) ); - MeprView::render('/readylaunch/checkout/form', get_defined_vars()); - } else { - MeprView::render('/checkout/spc_form', get_defined_vars()); - } - } - else { - if(MeprReadyLaunchCtrl::template_enabled( 'checkout' ) || MeprAppHelper::has_block( 'memberpress/checkout' )){ - $is_rl_widget = ( is_active_sidebar( 'mepr_rl_registration_footer' ) || is_active_sidebar( 'mepr_rl_global_footer' ) ); - MeprView::render('/readylaunch/checkout/form', get_defined_vars()); - } else { - MeprView::render('/checkout/form', get_defined_vars()); - } + $last_name_value = ''; + if (isset($user_last_name)) { + $last_name_value = esc_attr(stripslashes($user_last_name)); + } elseif (MeprUtils::is_user_logged_in()) { + $last_name_value = (string)$mepr_current_user->last_name; + } + + if (isset($errors) and !empty($errors)) { + MeprView::render('/shared/errors', get_defined_vars()); + } + + // Gather payment methods for checkout + $payment_methods = $product->payment_methods(); + if (empty($payment_methods)) { + $payment_methods = array_keys($mepr_options->integrations); + } + $payment_methods = MeprHooks::apply_filters('mepr_options_helper_payment_methods', $payment_methods, 'mepr_payment_method', $product); + $payment_methods = array_map(function ($pm_id) use ($mepr_options) { + return $mepr_options->payment_method($pm_id); + }, $payment_methods); + + static $unique_suffix = 0; + $unique_suffix++; + + $payment_required = MeprHooks::apply_filters('mepr_signup_payment_required', $product->is_payment_required($mepr_coupon_code), $product); + + if ($mepr_options->enable_spc) { + if (MeprReadyLaunchCtrl::template_enabled('checkout') || MeprAppHelper::has_block('memberpress/checkout')) { + $is_rl_widget = ( is_active_sidebar('mepr_rl_registration_footer') || is_active_sidebar('mepr_rl_global_footer') ); + MeprView::render('/readylaunch/checkout/form', get_defined_vars()); + } else { + MeprView::render('/checkout/spc_form', get_defined_vars()); + } + } else { + if (MeprReadyLaunchCtrl::template_enabled('checkout') || MeprAppHelper::has_block('memberpress/checkout')) { + $is_rl_widget = ( is_active_sidebar('mepr_rl_registration_footer') || is_active_sidebar('mepr_rl_global_footer') ); + MeprView::render('/readylaunch/checkout/form', get_defined_vars()); + } else { + MeprView::render('/checkout/form', get_defined_vars()); + } + } } - } - /** Gets called on the 'init' hook ... used for processing aspects of the signup - * form before the logic progresses on to 'the_content' ... - */ - public function process_signup_form() { - $mepr_options = MeprOptions::fetch(); + /** + * Gets called on the 'init' hook ... used for processing aspects of the signup + * form before the logic progresses on to 'the_content' ... + */ + public function process_signup_form() + { + $mepr_options = MeprOptions::fetch(); - if(isset($_POST['mepr_transaction_id']) && is_numeric($_POST['mepr_transaction_id'])) { - // With the new Stripe SCA changes a transaction already exists, just grab the vars we need for the hooks - $txn = new MeprTransaction((int) $_POST['mepr_transaction_id']); + if (isset($_POST['mepr_transaction_id']) && is_numeric($_POST['mepr_transaction_id'])) { + // With the new Stripe SCA changes a transaction already exists, just grab the vars we need for the hooks + $txn = new MeprTransaction((int) $_POST['mepr_transaction_id']); - if(empty($txn->id)) { - $_POST['errors'] = array(__('Sorry, we were unable to find the transaction.', 'memberpress')); - return; - } + if (empty($txn->id)) { + $_POST['errors'] = [__('Sorry, we were unable to find the transaction.', 'memberpress')]; + return; + } - $usr = $txn->user(); + $usr = $txn->user(); - if (empty($usr->ID)) { - $_POST['errors'] = array(__('Sorry, we were unable to find the user.', 'memberpress')); - return; - } + if (empty($usr->ID)) { + $_POST['errors'] = [__('Sorry, we were unable to find the user.', 'memberpress')]; + return; + } - $is_existing_user = true; + $is_existing_user = true; - $product = $txn->product(); + $product = $txn->product(); - if($product->is_one_time_payment()) { - $signup_type = 'non-recurring'; - } - else { - $signup_type = 'recurring'; - } - } - else { - // Validate the form post - $errors = MeprHooks::apply_filters('mepr-validate-signup', MeprUser::validate_signup($_POST, array())); - if(!empty($errors)) { - $_POST['errors'] = $errors; //Deprecated? - $_REQUEST['errors'] = $errors; - - return; - } - - // Check if the user is logged in already - $is_existing_user = MeprUtils::is_user_logged_in(); - - if($is_existing_user) { - $usr = MeprUtils::get_currentuserinfo(); - } - else { // If new user we've got to create them and sign them in - $usr = new MeprUser(); - $usr->user_login = ($mepr_options->username_is_email)?sanitize_email($_POST['user_email']):sanitize_user($_POST['user_login']); - $usr->user_email = sanitize_email($_POST['user_email']); - $usr->first_name = isset($_POST['user_first_name']) && !empty($_POST['user_first_name']) ? sanitize_text_field(wp_unslash($_POST['user_first_name'])) : ''; - $usr->last_name = isset($_POST['user_last_name']) && !empty($_POST['user_last_name']) ? sanitize_text_field(wp_unslash($_POST['user_last_name'])) : ''; - - $password = ($mepr_options->disable_checkout_password_fields === true) ? wp_generate_password() : $_POST['mepr_user_password']; - //Have to use rec here because we unset user_pass on __construct - $usr->set_password($password); + if ($product->is_one_time_payment()) { + $signup_type = 'non-recurring'; + } else { + $signup_type = 'recurring'; + } + } else { + // Validate the form post + $errors = MeprHooks::apply_filters('mepr-validate-signup', MeprUser::validate_signup($_POST, [])); + if (!empty($errors)) { + $_POST['errors'] = $errors; // Deprecated? + $_REQUEST['errors'] = $errors; + + return; + } - try { - $usr->store(); - - // We need to refresh the user object. In the case where emails are used as - // usernames, the email & username could differ after the user is saved. - $usr = new MeprUser($usr->ID); - - // Log the new user in - if(MeprHooks::apply_filters('mepr-auto-login', true, $_POST['mepr_product_id'], $usr)) { - wp_signon( - array( - 'user_login' => $usr->user_login, - 'user_password' => $password - ), - MeprUtils::is_ssl() //May help with the users getting logged out when going between http and https - ); - } - - MeprEvent::record('login', $usr); //Record the first login here - } - catch(MeprCreateException $e) { - $_POST['errors'] = array(__( 'The user was unable to be saved.', 'memberpress')); //Deprecated? - $_REQUEST['errors'] = array(__( 'The user was unable to be saved.', 'memberpress')); - return; - } - } + // Check if the user is logged in already + $is_existing_user = MeprUtils::is_user_logged_in(); + + if ($is_existing_user) { + $usr = MeprUtils::get_currentuserinfo(); + } else { // If new user we've got to create them and sign them in + $usr = new MeprUser(); + $usr->user_login = ($mepr_options->username_is_email) ? sanitize_email($_POST['user_email']) : sanitize_user($_POST['user_login']); + $usr->user_email = sanitize_email($_POST['user_email']); + $usr->first_name = isset($_POST['user_first_name']) && !empty($_POST['user_first_name']) ? sanitize_text_field(wp_unslash($_POST['user_first_name'])) : ''; + $usr->last_name = isset($_POST['user_last_name']) && !empty($_POST['user_last_name']) ? sanitize_text_field(wp_unslash($_POST['user_last_name'])) : ''; + + $password = ($mepr_options->disable_checkout_password_fields === true) ? wp_generate_password() : $_POST['mepr_user_password']; + // Have to use rec here because we unset user_pass on __construct + $usr->set_password($password); + + try { + $usr->store(); + + // We need to refresh the user object. In the case where emails are used as + // usernames, the email & username could differ after the user is saved. + $usr = new MeprUser($usr->ID); + + // Log the new user in + if (MeprHooks::apply_filters('mepr-auto-login', true, $_POST['mepr_product_id'], $usr)) { + wp_signon( + [ + 'user_login' => $usr->user_login, + 'user_password' => $password, + ], + MeprUtils::is_ssl() // May help with the users getting logged out when going between http and https + ); + } + + MeprEvent::record('login', $usr); // Record the first login here + } catch (MeprCreateException $e) { + $_POST['errors'] = [__('The user was unable to be saved.', 'memberpress')]; // Deprecated? + $_REQUEST['errors'] = [__('The user was unable to be saved.', 'memberpress')]; + return; + } + } - // Create a new transaction and set our new membership details - $txn = new MeprTransaction(); - $txn->user_id = $usr->ID; + // Create a new transaction and set our new membership details + $txn = new MeprTransaction(); + $txn->user_id = $usr->ID; - // Get the membership in place - $txn->product_id = sanitize_text_field($_POST['mepr_product_id']); - $product = $txn->product(); + // Get the membership in place + $txn->product_id = sanitize_text_field($_POST['mepr_product_id']); + $product = $txn->product(); - if(empty($product->ID)) { - $_POST['errors'] = array(__('Sorry, we were unable to find the membership.', 'memberpress')); - $_REQUEST['errors'] = array(__('Sorry, we were unable to find the membership.', 'memberpress')); - return; - } + if (empty($product->ID)) { + $_POST['errors'] = [__('Sorry, we were unable to find the membership.', 'memberpress')]; + $_REQUEST['errors'] = [__('Sorry, we were unable to find the membership.', 'memberpress')]; + return; + } - // If we're showing the fields on logged in purchases, let's save them here - if(!$is_existing_user || ($is_existing_user && $mepr_options->show_fields_logged_in_purchases)) { - MeprUsersCtrl::save_extra_profile_fields($usr->ID, true, $product, true); - $usr = new MeprUser($usr->ID); //Re-load the user object with the metadata now (helps with first name last name missing from hooks below) - } + // If we're showing the fields on logged in purchases, let's save them here + if (!$is_existing_user || ($is_existing_user && $mepr_options->show_fields_logged_in_purchases)) { + MeprUsersCtrl::save_extra_profile_fields($usr->ID, true, $product, true); + $usr = new MeprUser($usr->ID); // Re-load the user object with the metadata now (helps with first name last name missing from hooks below) + } - // Needed for autoresponders (SPC + Stripe + Free Trial issue) - MeprHooks::do_action('mepr-signup-user-loaded', $usr); + // Needed for autoresponders (SPC + Stripe + Free Trial issue) + MeprHooks::do_action('mepr-signup-user-loaded', $usr); - // Set default price, adjust it later if coupon applies - $price = $product->adjusted_price(); + // Set default price, adjust it later if coupon applies + $price = $product->adjusted_price(); - // Default coupon object - $cpn = (object)array('ID' => 0, 'post_title' => null); + // Default coupon object + $cpn = (object)[ + 'ID' => 0, + 'post_title' => null, + ]; - // Adjust membership price from the coupon code - if(isset($_POST['mepr_coupon_code']) && !empty($_POST['mepr_coupon_code'])) { - // Coupon object has to be loaded here or else txn create will record a 0 for coupon_id - $mepr_coupon_code = htmlentities(sanitize_text_field($_POST['mepr_coupon_code'])); - $cpn = MeprCoupon::get_one_from_code(sanitize_text_field($_POST['mepr_coupon_code'])); + // Adjust membership price from the coupon code + if (isset($_POST['mepr_coupon_code']) && !empty($_POST['mepr_coupon_code'])) { + // Coupon object has to be loaded here or else txn create will record a 0 for coupon_id + $mepr_coupon_code = htmlentities(sanitize_text_field($_POST['mepr_coupon_code'])); + $cpn = MeprCoupon::get_one_from_code(sanitize_text_field($_POST['mepr_coupon_code'])); - if(($cpn !== false) || ($cpn instanceof MeprCoupon)) { - $price = $product->adjusted_price($cpn->post_title); - } - } - - $txn->set_subtotal(MeprUtils::maybe_round_to_minimum_amount($price)); - - // Set the coupon id of the transaction - $txn->coupon_id = $cpn->ID; - - // Figure out the Payment Method - if(isset($_POST['mepr_payment_method']) && !empty($_POST['mepr_payment_method'])) { - $txn->gateway = sanitize_text_field($_POST['mepr_payment_method']); - } - else { - $txn->gateway = MeprTransaction::$free_gateway_str; - } - - // Let's checkout now - if($txn->gateway === MeprTransaction::$free_gateway_str) { - $signup_type = 'free'; - } - elseif(($pm = $txn->payment_method()) && $pm instanceof MeprBaseExclusiveRecurringGateway) { - $sub_attrs = $pm->subscription_attributes($product->plan_code); - if($pm->is_one_time_payment($product->plan_code)) { - $signup_type = 'non-recurring'; - $price = $sub_attrs['one_time_amount']; - } - else { - $signup_type = 'recurring'; - - // Create the subscription from the gateway plan - $sub = new MeprSubscription($sub_attrs); - $sub->user_id = $usr->ID; - $sub->gateway = $pm->id; - $sub->product_id = $product->ID; - $sub->maybe_prorate(); // sub to sub - $sub->store(); - - // Update the transaction with subscription id - $txn->subscription_id = $sub->id; - $price = $sub->price; - } - // Update subtotal - $txn->amount = $price; - } - elseif(($pm = $txn->payment_method()) && ($pm instanceof MeprBaseRealGateway)) { - // Set default price, adjust it later if coupon applies - $price = $product->adjusted_price(); - // Default coupon object - $cpn = (object)array('ID' => 0, 'post_title' => null); - // Adjust membership price from the coupon code - if(isset($_POST['mepr_coupon_code']) && !empty($_POST['mepr_coupon_code'])) { - // Coupon object has to be loaded here or else txn create will record a 0 for coupon_id - $cpn = MeprCoupon::get_one_from_code(sanitize_text_field($_POST['mepr_coupon_code'])); - if(($cpn !== false) || ($cpn instanceof MeprCoupon)) { - $price = $product->adjusted_price($cpn->post_title); - } - } - $txn->set_subtotal(MeprUtils::maybe_round_to_minimum_amount($price)); + if (($cpn !== false) || ($cpn instanceof MeprCoupon)) { + $price = $product->adjusted_price($cpn->post_title); + } + } - // Set the coupon id of the transaction - $txn->coupon_id = $cpn->ID; - // Create a new subscription - if($product->is_one_time_payment()) { - $signup_type = 'non-recurring'; - } - else { - $signup_type = 'recurring'; + $txn->set_subtotal(MeprUtils::maybe_round_to_minimum_amount($price)); - $sub = new MeprSubscription(); - $sub->user_id = $usr->ID; - $sub->gateway = $pm->id; - $sub->load_product_vars($product, $cpn->post_title, true); - $sub->maybe_prorate(); // sub to sub - $sub->store(); + // Set the coupon id of the transaction + $txn->coupon_id = $cpn->ID; - $txn->subscription_id = $sub->id; - } - } - else { - $_POST['errors'] = array(__('Invalid payment method', 'memberpress')); - return; - } - - $txn->store(); - - if(empty($txn->id)) { - // Don't want any loose ends here if the $txn didn't save for some reason - if($signup_type === 'recurring' && ($sub instanceof MeprSubscription)) { - $sub->destroy(); - } - $_POST['errors'] = array(__('Sorry, we were unable to create a transaction.', 'memberpress')); - return; - } - } + // Figure out the Payment Method + if (isset($_POST['mepr_payment_method']) && !empty($_POST['mepr_payment_method'])) { + $txn->gateway = sanitize_text_field($_POST['mepr_payment_method']); + } else { + $txn->gateway = MeprTransaction::$free_gateway_str; + } - try { - if(! $is_existing_user) { - if($mepr_options->disable_checkout_password_fields === true) { - $usr->send_password_notification('new'); + // Let's checkout now + if ($txn->gateway === MeprTransaction::$free_gateway_str) { + $signup_type = 'free'; + } elseif (($pm = $txn->payment_method()) && $pm instanceof MeprBaseExclusiveRecurringGateway) { + $sub_attrs = $pm->subscription_attributes($product->plan_code); + if ($pm->is_one_time_payment($product->plan_code)) { + $signup_type = 'non-recurring'; + $price = $sub_attrs['one_time_amount']; + } else { + $signup_type = 'recurring'; + + // Create the subscription from the gateway plan + $sub = new MeprSubscription($sub_attrs); + $sub->user_id = $usr->ID; + $sub->gateway = $pm->id; + $sub->product_id = $product->ID; + $sub->maybe_prorate(); // sub to sub + $sub->store(); + + // Update the transaction with subscription id + $txn->subscription_id = $sub->id; + $price = $sub->price; + } + // Update subtotal + $txn->amount = $price; + } elseif (($pm = $txn->payment_method()) && ($pm instanceof MeprBaseRealGateway)) { + // Set default price, adjust it later if coupon applies + $price = $product->adjusted_price(); + // Default coupon object + $cpn = (object)[ + 'ID' => 0, + 'post_title' => null, + ]; + // Adjust membership price from the coupon code + if (isset($_POST['mepr_coupon_code']) && !empty($_POST['mepr_coupon_code'])) { + // Coupon object has to be loaded here or else txn create will record a 0 for coupon_id + $cpn = MeprCoupon::get_one_from_code(sanitize_text_field($_POST['mepr_coupon_code'])); + if (($cpn !== false) || ($cpn instanceof MeprCoupon)) { + $price = $product->adjusted_price($cpn->post_title); + } + } + $txn->set_subtotal(MeprUtils::maybe_round_to_minimum_amount($price)); + + // Set the coupon id of the transaction + $txn->coupon_id = $cpn->ID; + // Create a new subscription + if ($product->is_one_time_payment()) { + $signup_type = 'non-recurring'; + } else { + $signup_type = 'recurring'; + + $sub = new MeprSubscription(); + $sub->user_id = $usr->ID; + $sub->gateway = $pm->id; + $sub->load_product_vars($product, $cpn->post_title, true); + $sub->maybe_prorate(); // sub to sub + $sub->store(); + + $txn->subscription_id = $sub->id; + } + } else { + $_POST['errors'] = [__('Invalid payment method', 'memberpress')]; + return; + } + + $txn->store(); + + if (empty($txn->id)) { + // Don't want any loose ends here if the $txn didn't save for some reason + if ($signup_type === 'recurring' && ($sub instanceof MeprSubscription)) { + $sub->destroy(); + } + $_POST['errors'] = [__('Sorry, we were unable to create a transaction.', 'memberpress')]; + return; + } } - } - // DEPRECATED: These 2 actions here for backwards compatibility ... use mepr-signup instead - MeprHooks::do_action('mepr-track-signup', $txn->amount, $usr, $product->ID, $txn->id); - MeprHooks::do_action('mepr-process-signup', $txn->amount, $usr, $product->ID, $txn->id); + try { + if (! $is_existing_user) { + if ($mepr_options->disable_checkout_password_fields === true) { + $usr->send_password_notification('new'); + } + } - if(('free' !== $signup_type) && isset($pm) && ($pm instanceof MeprBaseRealGateway)) { - $pm->process_signup_form($txn); - } + // DEPRECATED: These 2 actions here for backwards compatibility ... use mepr-signup instead + MeprHooks::do_action('mepr-track-signup', $txn->amount, $usr, $product->ID, $txn->id); + MeprHooks::do_action('mepr-process-signup', $txn->amount, $usr, $product->ID, $txn->id); - // Signup type can be 'free', 'non-recurring' or 'recurring' - MeprHooks::do_action("mepr-{$signup_type}-signup", $txn); - MeprHooks::do_action('mepr-signup', $txn); + if (('free' !== $signup_type) && isset($pm) && ($pm instanceof MeprBaseRealGateway)) { + $pm->process_signup_form($txn); + } - // Pass order bump product IDs to the checkout second page - $obs = isset($_POST['mepr_order_bumps']) && is_array($_POST['mepr_order_bumps']) ? array_filter(array_map('intval', $_POST['mepr_order_bumps'])) : []; - $args = count($obs) ? ['obs' => $obs] : []; + // Signup type can be 'free', 'non-recurring' or 'recurring' + MeprHooks::do_action("mepr-{$signup_type}-signup", $txn); + MeprHooks::do_action('mepr-signup', $txn); - MeprUtils::wp_redirect(MeprHooks::apply_filters('mepr-signup-checkout-url', $txn->checkout_url($args), $txn)); - } - catch(Exception $e) { - $_POST['errors'] = $_REQUEST['errors'] = array($e->getMessage()); - } - } - - /** - * Called from filter mepr-signup-checkout-url - * Used to handle redirection when there are errors on SPC - * Returns: redirection URL - */ - public function handle_spc_checkout_url($checkout_url, $txn) { - $mepr_options = MeprOptions::fetch(); - if(isset($_POST['mepr_payment_method'])) { - $payment_method = $mepr_options->payment_method($_POST['mepr_payment_method']); - if($mepr_options->enable_spc && $payment_method->has_spc_form && !empty($_POST['errors'])) { - $errors = $_POST['errors']; - $errors = array_map('urlencode', $errors); - $query_params = array( - 'errors' => $errors, - 'mepr_transaction_id' => $txn->id, - 'mepr_process_signup_form' => 0, - 'mepr_process_payment_form' => 1, - 'mepr_payment_method' => sanitize_text_field($_POST['mepr_payment_method']), - ); - if(!empty($_POST['mepr_coupon_code'])) { - $query_params = array_merge(array('mepr_coupon_code' => htmlentities( sanitize_text_field( $_POST['mepr_coupon_code'] ) )), $query_params); + // Pass order bump product IDs to the checkout second page + $obs = isset($_POST['mepr_order_bumps']) && is_array($_POST['mepr_order_bumps']) ? array_filter(array_map('intval', $_POST['mepr_order_bumps'])) : []; + $args = count($obs) ? ['obs' => $obs] : []; + + MeprUtils::wp_redirect(MeprHooks::apply_filters('mepr-signup-checkout-url', $txn->checkout_url($args), $txn)); + } catch (Exception $e) { + $_POST['errors'] = $_REQUEST['errors'] = [$e->getMessage()]; } - $product = $txn->product(); - $checkout_url = add_query_arg($query_params, $product->url()); - } - } - return $checkout_url; - } - - /** - * Called from mepr-signup action - * Processes the payment for SPC - */ - public function process_spc_payment_form($txn) { - if(did_action('mepr_stripe_payment_pending') || did_action('mepr_stripe_checkout_pending')) { - return; } - if(isset($_POST['smart-payment-button']) && $_POST['smart-payment-button']) { - return; + /** + * Called from filter mepr-signup-checkout-url + * Used to handle redirection when there are errors on SPC + * Returns: redirection URL + */ + public function handle_spc_checkout_url($checkout_url, $txn) + { + $mepr_options = MeprOptions::fetch(); + if (isset($_POST['mepr_payment_method'])) { + $payment_method = $mepr_options->payment_method($_POST['mepr_payment_method']); + if ($mepr_options->enable_spc && $payment_method->has_spc_form && !empty($_POST['errors'])) { + $errors = $_POST['errors']; + $errors = array_map('urlencode', $errors); + $query_params = [ + 'errors' => $errors, + 'mepr_transaction_id' => $txn->id, + 'mepr_process_signup_form' => 0, + 'mepr_process_payment_form' => 1, + 'mepr_payment_method' => sanitize_text_field($_POST['mepr_payment_method']), + ]; + if (!empty($_POST['mepr_coupon_code'])) { + $query_params = array_merge(['mepr_coupon_code' => htmlentities(sanitize_text_field($_POST['mepr_coupon_code']))], $query_params); + } + $product = $txn->product(); + $checkout_url = add_query_arg($query_params, $product->url()); + } + } + return $checkout_url; } - $mepr_options = MeprOptions::fetch(); - if(isset($_POST['mepr_payment_method'])) { - $payment_method = $mepr_options->payment_method($_POST['mepr_payment_method']); - if($mepr_options->enable_spc && $payment_method->has_spc_form || ($mepr_options->design_enable_checkout_template)) { - $_POST = array_merge( - $_POST, - array( - 'mepr_process_payment_form' => 1, - 'mepr_transaction_id' => $txn->id, - ) - ); - $this->process_payment_form(); - } + /** + * Called from mepr-signup action + * Processes the payment for SPC + */ + public function process_spc_payment_form($txn) + { + if (did_action('mepr_stripe_payment_pending') || did_action('mepr_stripe_checkout_pending')) { + return; + } + + if (isset($_POST['smart-payment-button']) && $_POST['smart-payment-button']) { + return; + } + + $mepr_options = MeprOptions::fetch(); + if (isset($_POST['mepr_payment_method'])) { + $payment_method = $mepr_options->payment_method($_POST['mepr_payment_method']); + if ($mepr_options->enable_spc && $payment_method->has_spc_form || ($mepr_options->design_enable_checkout_template)) { + $_POST = array_merge( + $_POST, + [ + 'mepr_process_payment_form' => 1, + 'mepr_transaction_id' => $txn->id, + ] + ); + $this->process_payment_form(); + } + } } - } - public function display_payment_page() { - $mepr_options = MeprOptions::fetch(); + public function display_payment_page() + { + $mepr_options = MeprOptions::fetch(); - $txn_id = $_REQUEST['txn']; - $txn = new MeprTransaction($txn_id); + $txn_id = $_REQUEST['txn']; + $txn = new MeprTransaction($txn_id); - if(!isset($txn->id) || $txn->id <= 0) { - wp_die(__('ERROR: Invalid Transaction ID. Use your browser back button and try registering again.', 'memberpress')); - } + if (!isset($txn->id) || $txn->id <= 0) { + wp_die(__('ERROR: Invalid Transaction ID. Use your browser back button and try registering again.', 'memberpress')); + } - if($txn->gateway === MeprTransaction::$free_gateway_str || $txn->amount <= 0.00) { - MeprTransaction::create_free_transaction($txn); - } - else if(($pm = $mepr_options->payment_method($txn->gateway)) && $pm instanceof MeprBaseRealGateway) { - $pm->display_payment_page($txn); - } + if ($txn->gateway === MeprTransaction::$free_gateway_str || $txn->amount <= 0.00) { + MeprTransaction::create_free_transaction($txn); + } elseif (($pm = $mepr_options->payment_method($txn->gateway)) && $pm instanceof MeprBaseRealGateway) { + $pm->display_payment_page($txn); + } - // Artificially set the payment method params so we can use them downstream - // when display_payment_form is called in the 'the_content' action. - $_REQUEST['payment_method_params'] = array( - 'method' => $txn->gateway, - 'amount' => $txn->amount, - 'user' => $txn->user(), - 'product_id' => $txn->product_id, - 'transaction_id' => $txn->id - ); - } - - // Called in the 'the_content' hook ... used to display a signup form - public function display_payment_form() { - $mepr_options = MeprOptions::fetch(); - - if(isset($_REQUEST['payment_method_params'])) { - extract($_REQUEST['payment_method_params'], EXTR_SKIP); - - if(isset($_REQUEST['errors']) && !empty($_REQUEST['errors'])) { - $errors = $_REQUEST['errors']; - MeprView::render('/shared/errors', get_defined_vars()); - } - - if(($pm = $mepr_options->payment_method($method)) && - ($pm instanceof MeprBaseRealGateway)) { - $pm->display_payment_form($amount, $user, $product_id, $transaction_id); - } + // Artificially set the payment method params so we can use them downstream + // when display_payment_form is called in the 'the_content' action. + $_REQUEST['payment_method_params'] = [ + 'method' => $txn->gateway, + 'amount' => $txn->amount, + 'user' => $txn->user(), + 'product_id' => $txn->product_id, + 'transaction_id' => $txn->id, + ]; } - } - - public function process_payment_form() { - if(isset($_POST['mepr_process_payment_form']) && isset($_POST['mepr_transaction_id']) && is_numeric($_POST['mepr_transaction_id'])) { - $txn = new MeprTransaction($_POST['mepr_transaction_id']); - if($txn->rec != false) { + // Called in the 'the_content' hook ... used to display a signup form + public function display_payment_form() + { $mepr_options = MeprOptions::fetch(); - if(($pm = $mepr_options->payment_method($txn->gateway)) && $pm instanceof MeprBaseRealGateway) { - $errors = $pm->validate_payment_form(array()); - - if(empty($errors)) { - // process_payment_form either returns true - // for success or an array of $errors on failure - try { - $pm->process_payment_form($txn); + + if (isset($_REQUEST['payment_method_params'])) { + extract($_REQUEST['payment_method_params'], EXTR_SKIP); + + if (isset($_REQUEST['errors']) && !empty($_REQUEST['errors'])) { + $errors = $_REQUEST['errors']; + MeprView::render('/shared/errors', get_defined_vars()); } - catch(Exception $e) { - MeprHooks::do_action('mepr_payment_failure', $txn); - $errors = array($e->getMessage()); + + if ( + ($pm = $mepr_options->payment_method($method)) && + ($pm instanceof MeprBaseRealGateway) + ) { + $pm->display_payment_form($amount, $user, $product_id, $transaction_id); } - } - - if(empty($errors)) { - //Reload the txn now that it should have a proper trans_num set - $txn = new MeprTransaction($txn->id); - $product = new MeprProduct($txn->product_id); - $sanitized_title = sanitize_title($product->post_title); - $query_params = array('membership' => $sanitized_title, 'trans_num' => $txn->trans_num, 'membership_id' => $product->ID); - if($txn->subscription_id > 0) { - $sub = $txn->subscription(); - $query_params = array_merge($query_params, array('subscr_id' => $sub->subscr_id)); + } + } + + public function process_payment_form() + { + if (isset($_POST['mepr_process_payment_form']) && isset($_POST['mepr_transaction_id']) && is_numeric($_POST['mepr_transaction_id'])) { + $txn = new MeprTransaction($_POST['mepr_transaction_id']); + + if ($txn->rec != false) { + $mepr_options = MeprOptions::fetch(); + if (($pm = $mepr_options->payment_method($txn->gateway)) && $pm instanceof MeprBaseRealGateway) { + $errors = $pm->validate_payment_form([]); + + if (empty($errors)) { + // process_payment_form either returns true + // for success or an array of $errors on failure + try { + $pm->process_payment_form($txn); + } catch (Exception $e) { + MeprHooks::do_action('mepr_payment_failure', $txn); + $errors = [$e->getMessage()]; + } + } + + if (empty($errors)) { + // Reload the txn now that it should have a proper trans_num set + $txn = new MeprTransaction($txn->id); + $product = new MeprProduct($txn->product_id); + $sanitized_title = sanitize_title($product->post_title); + $query_params = [ + 'membership' => $sanitized_title, + 'trans_num' => $txn->trans_num, + 'membership_id' => $product->ID, + ]; + if ($txn->subscription_id > 0) { + $sub = $txn->subscription(); + $query_params = array_merge($query_params, ['subscr_id' => $sub->subscr_id]); + } + MeprUtils::wp_redirect($mepr_options->thankyou_page_url(build_query($query_params))); + } else { + // Artificially set the payment method params so we can use them downstream + // when display_payment_form is called in the 'the_content' action. + $_REQUEST['payment_method_params'] = [ + 'method' => $pm->id, + 'amount' => $txn->amount, + 'user' => new MeprUser($txn->user_id), + 'product_id' => $txn->product_id, + 'transaction_id' => $txn->id, + ]; + $_REQUEST['mepr_payment_method'] = $pm->id; + $_POST['errors'] = $errors; + return; + } + } } - MeprUtils::wp_redirect($mepr_options->thankyou_page_url(build_query($query_params))); - } - else { - // Artificially set the payment method params so we can use them downstream - // when display_payment_form is called in the 'the_content' action. - $_REQUEST['payment_method_params'] = array( - 'method' => $pm->id, - 'amount' => $txn->amount, - 'user' => new MeprUser($txn->user_id), - 'product_id' => $txn->product_id, - 'transaction_id' => $txn->id - ); - $_REQUEST['mepr_payment_method'] = $pm->id; - $_POST['errors'] = $errors; - return; - } } - } + + $_POST['errors'] = [__('Sorry, an unknown error occurred.', 'memberpress')]; } - $_POST['errors'] = array(__('Sorry, an unknown error occurred.', 'memberpress')); - } + public function exclude_disconnected_gateways($pm_ids, $field_name) + { + $mepr_options = MeprOptions::fetch(); + $connected_pm_ids = []; - public function exclude_disconnected_gateways($pm_ids, $field_name) { - $mepr_options = MeprOptions::fetch(); - $connected_pm_ids = array(); + foreach ($pm_ids as $pm_id) { + $obj = $mepr_options->payment_method($pm_id); - foreach($pm_ids as $pm_id) { - $obj = $mepr_options->payment_method($pm_id); + if (MeprUtils::is_gateway_connected($obj)) { + $connected_pm_ids[] = $pm_id; + } + } - if(MeprUtils::is_gateway_connected($obj)) { - $connected_pm_ids[] = $pm_id; - } + return $connected_pm_ids; } - return $connected_pm_ids; - } + private function request_has_valid_thank_you_params($req) + { + // If these aren't set as parameters then this isn't actually a real checkout + if ( + !isset($req['membership']) || + !isset($req['membership_id']) || + (!isset($req['trans_num']) && !isset($req['transaction_id'])) + ) { + return false; + } - private function request_has_valid_thank_you_params($req) { - // If these aren't set as parameters then this isn't actually a real checkout - if( !isset($req['membership']) || - !isset($req['membership_id']) || - (!isset($req['trans_num']) && !isset($req['transaction_id']))) { - return false; + return true; } - return true; - } + private function request_has_valid_thank_you_membership_id($req) + { + // If this is an invalid membership then let's bail, yo + $membership = new MeprProduct($req['membership_id']); + if (!$membership || empty($membership->ID)) { + return false; + } - private function request_has_valid_thank_you_membership_id($req) { - // If this is an invalid membership then let's bail, yo - $membership = new MeprProduct($req['membership_id']); - if( !$membership || empty($membership->ID) ) { - return false; + return true; } - return true; - } + private function request_has_valid_thank_you_trans_num($req) + { + // If this is an invalid transaction then let's bail, yo + $transaction = $this->get_transaction_from_request($req); - private function request_has_valid_thank_you_trans_num($req) { - // If this is an invalid transaction then let's bail, yo - $transaction = $this->get_transaction_from_request($req); + return !empty($transaction); + } - return !empty($transaction); - } + private function request_has_valid_thank_you_membership($atts, $req) + { + $membership = new MeprProduct($req['membership_id']); + $transaction = $this->get_transaction_from_request($req); - private function request_has_valid_thank_you_membership($atts, $req) { - $membership = new MeprProduct($req['membership_id']); - $transaction = $this->get_transaction_from_request($req); + // If this transaction doesn't match the membership then something fishy is going on here bro + if (empty($transaction) || $transaction->product_id != $membership->ID) { + return false; + } - // If this transaction doesn't match the membership then something fishy is going on here bro - if(empty($transaction) || $transaction->product_id != $membership->ID) { - return false; - } + // If the shortcode is tied to a specific membership then only show + // it when this is the thank you page for the specified membership + if ( + !is_null($atts['membership']) && isset($req['membership_id']) && + $req['membership_id'] != $atts['membership'] + ) { + return false; + } - // If the shortcode is tied to a specific membership then only show - // it when this is the thank you page for the specified membership - if( !is_null($atts['membership']) && isset($req['membership_id']) && - $req['membership_id'] != $atts['membership'] ) { - return false; + return true; } - return true; - } + private function get_transaction_from_request($req) + { + if (isset($req['trans_num'])) { + return MeprTransaction::get_one_by_trans_num($req['trans_num']); + } elseif (isset($req['transaction_id'])) { + return MeprTransaction::get_one((int) $req['transaction_id']); + } - private function get_transaction_from_request($req) { - if(isset($req['trans_num'])) { - return MeprTransaction::get_one_by_trans_num($req['trans_num']); - } - elseif(isset($req['transaction_id'])) { - return MeprTransaction::get_one((int) $req['transaction_id']); + return false; } - return false; - } - - /** - * Prepare transaction variables - * - * Instantiates and returns a new transaction and subscription (if recurring), based on the given parameters. - * - * @param MeprProduct $product The product being purchased - * @param int $order_id The order ID - * @param int $user_id The user ID - * @param string $gateway_id The payment gateway ID - * @param MeprCoupon|false $cpn Optional coupon code - * @param bool $store Whether to store the transaction & subscription, default true - * @return array{0: MeprTransaction, 1: MeprSubscription|null} - * @throws Exception If unable to store a transaction or subscription - */ - public static function prepare_transaction(MeprProduct $product, $order_id, $user_id, $gateway_id, $cpn = false, $store = true) { - $txn = new MeprTransaction(); - $txn->order_id = $order_id; - $txn->user_id = $user_id; - $txn->gateway = $gateway_id; - $txn->product_id = $product->ID; - $txn->coupon_id = 0; - - // Set default price, adjust it later if coupon applies - $price = $product->adjusted_price(); - - // Adjust membership price from the coupon code - if($cpn instanceof MeprCoupon && MeprCoupon::is_valid_coupon_code($cpn->post_title, $product->ID)) { - $price = $product->adjusted_price($cpn->post_title); - $txn->coupon_id = $cpn->ID; - } + /** + * Prepare transaction variables + * + * Instantiates and returns a new transaction and subscription (if recurring), based on the given parameters. + * + * @param MeprProduct $product The product being purchased + * @param integer $order_id The order ID + * @param integer $user_id The user ID + * @param string $gateway_id The payment gateway ID + * @param MeprCoupon|false $cpn Optional coupon code + * @param boolean $store Whether to store the transaction & subscription, default true + * @return array{0: MeprTransaction, 1: MeprSubscription|null} + * @throws Exception If unable to store a transaction or subscription + */ + public static function prepare_transaction(MeprProduct $product, $order_id, $user_id, $gateway_id, $cpn = false, $store = true) + { + $txn = new MeprTransaction(); + $txn->order_id = $order_id; + $txn->user_id = $user_id; + $txn->gateway = $gateway_id; + $txn->product_id = $product->ID; + $txn->coupon_id = 0; - $txn->set_subtotal(MeprUtils::maybe_round_to_minimum_amount($price)); + // Set default price, adjust it later if coupon applies + $price = $product->adjusted_price(); - if($product->is_one_time_payment()) { - $sub = null; - } - else { - $sub = new MeprSubscription(); - $sub->order_id = $order_id; - $sub->user_id = $user_id; - $sub->gateway = $gateway_id; - $sub->load_product_vars($product, $cpn instanceof MeprCoupon ? $cpn->post_title : null, true); - $sub->maybe_prorate(); // sub to sub - } + // Adjust membership price from the coupon code + if ($cpn instanceof MeprCoupon && MeprCoupon::is_valid_coupon_code($cpn->post_title, $product->ID)) { + $price = $product->adjusted_price($cpn->post_title); + $txn->coupon_id = $cpn->ID; + } - if($store) { - if($sub instanceof MeprSubscription) { - $sub->store(); + $txn->set_subtotal(MeprUtils::maybe_round_to_minimum_amount($price)); - if(empty($sub->id)) { - throw new Exception(__('Sorry, we were unable to create a subscription.', 'memberpress')); + if ($product->is_one_time_payment()) { + $sub = null; + } else { + $sub = new MeprSubscription(); + $sub->order_id = $order_id; + $sub->user_id = $user_id; + $sub->gateway = $gateway_id; + $sub->load_product_vars($product, $cpn instanceof MeprCoupon ? $cpn->post_title : null, true); + $sub->maybe_prorate(); // sub to sub } - $txn->subscription_id = $sub->id; - } + if ($store) { + if ($sub instanceof MeprSubscription) { + $sub->store(); - $txn->store(); + if (empty($sub->id)) { + throw new Exception(__('Sorry, we were unable to create a subscription.', 'memberpress')); + } - if(empty($txn->id)) { - // Don't want any loose ends here if the transaction didn't save for some reason - if($sub instanceof MeprSubscription) { - $sub->destroy(); + $txn->subscription_id = $sub->id; + } + + $txn->store(); + + if (empty($txn->id)) { + // Don't want any loose ends here if the transaction didn't save for some reason + if ($sub instanceof MeprSubscription) { + $sub->destroy(); + } + + throw new Exception(__('Sorry, we were unable to create a transaction.', 'memberpress')); + } } - throw new Exception(__('Sorry, we were unable to create a transaction.', 'memberpress')); - } + return [$txn, $sub]; } - return [$txn, $sub]; - } + /** + * Get order bump products + * + * Gets an array that is filled with a MeprProduct for each order bump in the $_POST data. + * + * @param integer $product_id The main product for the purchase, order bumps with this ID will be ignored + * @return MeprProduct[] + * @throws Exception If a product was not found + */ + public static function get_order_bump_products($product_id, array $order_bump_product_ids) + { + $order_bump_products = []; + + foreach ($order_bump_product_ids as $order_bump_product_id) { + $product = new MeprProduct($order_bump_product_id); + + if (empty($product->ID)) { + throw new Exception(__('Product not found', 'memberpress')); + } - /** - * Get order bump products - * - * Gets an array that is filled with a MeprProduct for each order bump in the $_POST data. - * - * @param int $product_id The main product for the purchase, order bumps with this ID will be ignored - * @return MeprProduct[] - * @throws Exception If a product was not found - */ - public static function get_order_bump_products($product_id, array $order_bump_product_ids) { - $order_bump_products = []; + if ($product_id == $product->ID) { + continue; + } - foreach($order_bump_product_ids as $order_bump_product_id) { - $product = new MeprProduct($order_bump_product_id); + if (!$product->can_you_buy_me()) { + throw new Exception(sprintf(__("You don't have access to purchase %s.", 'memberpress'), $product->post_title)); + } - if(empty($product->ID)) { - throw new Exception(__('Product not found', 'memberpress')); - } + $group = $product->group(); - if($product_id == $product->ID) { - continue; - } + if ($group instanceof MeprGroup && $group->is_upgrade_path) { + throw new Exception(sprintf(__('The product %s cannot be purchased at this time.', 'memberpress'), $product->post_title)); + } - if(!$product->can_you_buy_me()) { - throw new Exception(sprintf(__("You don't have access to purchase %s.", 'memberpress'), $product->post_title)); - } + $order_bump_products[] = $product; + } - $group = $product->group(); + return $order_bump_products; + } - if($group instanceof MeprGroup && $group->is_upgrade_path) { - throw new Exception(sprintf(__("The product %s cannot be purchased at this time.", 'memberpress'), $product->post_title)); - } + /** + * Get dynamic updates when the checkout state changes + */ + public function get_checkout_state() + { + $mepr_options = MeprOptions::fetch(); + $product_id = isset($_POST['mepr_product_id']) ? (int) sanitize_text_field(wp_unslash($_POST['mepr_product_id'])) : 0; + $coupon_code = isset($_POST['mepr_coupon_code']) ? sanitize_text_field(wp_unslash($_POST['mepr_coupon_code'])) : ''; - $order_bump_products[] = $product; - } + if (empty($product_id)) { + wp_send_json_error(); + } - return $order_bump_products; - } + $prd = new MeprProduct($product_id); - /** - * Get dynamic updates when the checkout state changes - */ - public function get_checkout_state() { - $mepr_options = MeprOptions::fetch(); - $product_id = isset($_POST['mepr_product_id']) ? (int) sanitize_text_field(wp_unslash($_POST['mepr_product_id'])) : 0; - $coupon_code = isset($_POST['mepr_coupon_code']) ? sanitize_text_field(wp_unslash($_POST['mepr_coupon_code'])) : ''; + if (empty($prd->ID)) { + wp_send_json_error(); + } - if(empty($product_id)) { - wp_send_json_error(); - } + if (!empty($coupon_code) && !check_ajax_referer('mepr_coupons', 'mepr_coupon_nonce', false)) { + wp_send_json_error(); + } - $prd = new MeprProduct($product_id); + $is_gift = isset($_POST['mpgft_gift_checkbox']) ? sanitize_text_field(wp_unslash($_POST['mpgft_gift_checkbox'])) : 'false'; + $payment_required = $prd->is_payment_required($coupon_code); + $order_bump_products = []; - if(empty($prd->ID)) { - wp_send_json_error(); - } + if ($is_gift == 'true') { + $prd->allow_renewal = false; + } - if(!empty($coupon_code) && !check_ajax_referer('mepr_coupons', 'mepr_coupon_nonce', false)) { - wp_send_json_error(); - } + try { + $order_bump_product_ids = isset($_POST['mepr_order_bumps']) && is_array($_POST['mepr_order_bumps']) ? array_map('intval', $_POST['mepr_order_bumps']) : []; + $order_bump_products = self::get_order_bump_products($prd->ID, $order_bump_product_ids); - $is_gift = isset($_POST['mpgft_gift_checkbox']) ? sanitize_text_field(wp_unslash($_POST['mpgft_gift_checkbox'])) : 'false'; - $payment_required = $prd->is_payment_required($coupon_code); - $order_bump_products = []; + foreach ($order_bump_products as $product) { + if ($product->is_payment_required()) { + $payment_required = true; + } + } + } catch (Exception $e) { + // ignore exception + } - if($is_gift == 'true') { - $prd->allow_renewal = false; - } + $payment_required = MeprHooks::apply_filters('mepr_signup_payment_required', $payment_required, $prd); - try { - $order_bump_product_ids = isset($_POST['mepr_order_bumps']) && is_array($_POST['mepr_order_bumps']) ? array_map('intval', $_POST['mepr_order_bumps']) : []; - $order_bump_products = self::get_order_bump_products($prd->ID, $order_bump_product_ids); + ob_start(); + MeprProductsHelper::display_invoice($prd, $coupon_code); + $price_string = ob_get_clean(); - foreach($order_bump_products as $product) { - if($product->is_payment_required()) { - $payment_required = true; - } - } - } - catch(Exception $e) { - // ignore exception - } + ob_start(); + MeprProductsHelper::display_spc_invoice($prd, $coupon_code, $order_bump_products); + $invoice_html = ob_get_clean(); + + $data = [ + 'payment_required' => $payment_required, + 'price_string' => $price_string, + 'invoice_html' => $invoice_html, + 'is_gift' => MeprHooks::apply_filters('mepr_signup_product_is_gift', false, $prd), + ]; + + if ($payment_required) { + $payment_method_ids = isset($_POST['mepr_payment_methods']) && is_array($_POST['mepr_payment_methods']) ? array_map('sanitize_text_field', array_map('wp_unslash', $_POST['mepr_payment_methods'])) : []; + + if (count($payment_method_ids)) { + $payment_methods = $mepr_options->payment_methods(false); + $coupon = MeprCoupon::get_one_from_code($coupon_code); + $coupon_code = $coupon instanceof MeprCoupon ? $coupon->post_title : ''; + $elements_options = []; + + foreach ($payment_methods as $pm) { + if ($pm instanceof MeprStripeGateway && $pm->settings->stripe_checkout_enabled != 'on' && in_array($pm->id, $payment_method_ids, true)) { + try { + list($txn, $sub) = MeprCheckoutCtrl::prepare_transaction( + $prd, + 0, + get_current_user_id(), + $pm->id, + $coupon, + false + ); + + $elements_options[$pm->id] = $pm->get_elements_options( + $prd, + $txn, + $sub, + $coupon_code, + $order_bump_products + ); + } catch (Exception $e) { + // ignore exception + } + } + } - $payment_required = MeprHooks::apply_filters('mepr_signup_payment_required', $payment_required, $prd); - - ob_start(); - MeprProductsHelper::display_invoice($prd, $coupon_code); - $price_string = ob_get_clean(); - - ob_start(); - MeprProductsHelper::display_spc_invoice($prd, $coupon_code, $order_bump_products); - $invoice_html = ob_get_clean(); - - $data = [ - 'payment_required' => $payment_required, - 'price_string' => $price_string, - 'invoice_html' => $invoice_html, - 'is_gift' => MeprHooks::apply_filters('mepr_signup_product_is_gift', false, $prd), - ]; - - if($payment_required) { - $payment_method_ids = isset($_POST['mepr_payment_methods']) && is_array($_POST['mepr_payment_methods']) ? array_map('sanitize_text_field', array_map('wp_unslash', $_POST['mepr_payment_methods'])) : []; - - if(count($payment_method_ids)) { - $payment_methods = $mepr_options->payment_methods(false); - $coupon = MeprCoupon::get_one_from_code($coupon_code); - $coupon_code = $coupon instanceof MeprCoupon ? $coupon->post_title : ''; - $elements_options = []; - - foreach($payment_methods as $pm) { - if($pm instanceof MeprStripeGateway && $pm->settings->stripe_checkout_enabled != 'on' && in_array($pm->id, $payment_method_ids, true)) { - try { - list($txn, $sub) = MeprCheckoutCtrl::prepare_transaction( - $prd, - 0, - get_current_user_id(), - $pm->id, - $coupon, - false - ); - - $elements_options[$pm->id] = $pm->get_elements_options( - $prd, - $txn, - $sub, - $coupon_code, - $order_bump_products - ); - } - catch(Exception $e) { - // ignore exception + if (count($elements_options)) { + $data['elements_options'] = $elements_options; + } } - } } - if(count($elements_options)) { - $data['elements_options'] = $elements_options; - } - } + wp_send_json_success($data); } - - wp_send_json_success($data); - } } diff --git a/app/controllers/MeprCouponsCtrl.php b/app/controllers/MeprCouponsCtrl.php index feb30c5..6ba48ea 100644 --- a/app/controllers/MeprCouponsCtrl.php +++ b/app/controllers/MeprCouponsCtrl.php @@ -1,377 +1,389 @@ array( - 'name' => __('Coupons', 'memberpress'), - 'singular_name' => __('Coupon', 'memberpress'), - 'add_new' => __('Add New', 'memberpress'), - 'add_new_item' => __('Add New Coupon', 'memberpress'), - 'edit_item' => __('Edit Coupon', 'memberpress'), - 'new_item' => __('New Coupon', 'memberpress'), - 'view_item' => __('View Coupon', 'memberpress'), - 'search_items' => __('Search Coupons', 'memberpress'), - 'not_found' => __('No Coupons found', 'memberpress'), - 'not_found_in_trash' => __('No Coupons found in Trash', 'memberpress'), - 'parent_item_colon' => __('Parent Coupon:', 'memberpress') - ), - 'public' => false, - 'show_ui' => true, //MeprUpdateCtrl::is_activated(), - 'show_in_menu' => 'memberpress', - 'capability_type' => 'page', - 'hierarchical' => false, - 'register_meta_box_cb' => 'MeprCouponsCtrl::add_meta_boxes', - 'rewrite' => false, - 'supports' => array('title') - ) - ); - } - - public static function columns($columns) { - $columns = array( - "cb" => '', - "title" => __('Code', 'memberpress'), - "coupon-description" => __('Description', 'memberpress'), - "date" => __('Created', 'memberpress'), - "coupon-discount" => __('Discount', 'memberpress'), - "coupon-dm" => __('Mode', 'memberpress'), - "coupon-starts" => __('Starts', 'memberpress'), - "coupon-expires" => __('Expires', 'memberpress'), - "coupon-count" => __('Usage Count', 'memberpress'), - "coupon-products" => __('Applies To', 'memberpress') - ); - - return $columns; - } - - public static function custom_columns($column, $coupon_id) { - $mepr_options = MeprOptions::fetch(); - $coupon = new MeprCoupon($coupon_id); - - if($coupon->ID !== null) { - switch($column) { - case 'coupon-description': - echo strip_tags($coupon->post_content); - break; - case 'coupon-discount': - if($coupon->discount_mode=='first-payment') { - echo $coupon->first_payment_discount_amount; //Update this to show proper currency symbol later - echo ($coupon->first_payment_discount_type == 'percent')?__('%', 'memberpress'):$mepr_options->currency_code; - echo ' → '; - } - - echo $coupon->discount_amount; //Update this to show proper currency symbol later - echo ($coupon->discount_type == 'percent')?__('%', 'memberpress'):$mepr_options->currency_code; - break; - case 'coupon-starts': - if($coupon->post_status != 'trash') { - if($coupon->should_start) { - echo MeprUtils::get_date_from_ts($coupon->starts_on); - } - else { - _e('Immediately', 'memberpress'); - } - } - else { - _e('Expired', 'memberpress'); //They've moved this to trash so show it as expired - } - break; - case 'coupon-expires': - if($coupon->post_status != 'trash') { - if($coupon->should_expire) { - echo MeprUtils::get_date_from_ts($coupon->expires_on); - } - else { - _e('Never', 'memberpress'); + +if (!defined('ABSPATH')) { + die('You are not allowed to call this page directly.'); +} + +class MeprCouponsCtrl extends MeprCptCtrl +{ + public function load_hooks() + { + add_filter('bulk_actions-edit-memberpresscoupon', 'MeprCouponsCtrl::disable_bulk'); + add_filter('post_row_actions', 'MeprCouponsCtrl::disable_row', 10, 2); + add_action('admin_enqueue_scripts', 'MeprCouponsCtrl::admin_enqueue_scripts'); + add_action('manage_posts_custom_column', 'MeprCouponsCtrl::custom_columns', 10, 2); + add_filter('manage_edit-memberpresscoupon_columns', 'MeprCouponsCtrl::columns'); + add_action('admin_init', 'MeprCoupon::expire_old_coupons_and_cleanup_db'); + add_action('save_post', 'MeprCouponsCtrl::save_postdata'); + add_action('wp_insert_post_data', 'MeprCouponsCtrl::sanitize_coupon_title', 99, 2); + add_filter('default_title', 'MeprCouponsCtrl::get_page_title_code'); + add_action('mepr-txn-store', 'MeprCouponsCtrl::update_coupon_usage_count'); + add_action('mepr-subscr-store', 'MeprCouponsCtrl::update_coupon_usage_count'); + + // Cleanup list view + add_filter('views_edit-' . MeprCoupon::$cpt, 'MeprAppCtrl::cleanup_list_view'); + + // Ajax coupon validation + add_action('wp_ajax_mepr_validate_coupon', 'MeprCouponsCtrl::validate_coupon_ajax'); + add_action('wp_ajax_nopriv_mepr_validate_coupon', 'MeprCouponsCtrl::validate_coupon_ajax'); + } + + public function register_post_type() + { + register_post_type(MeprCoupon::$cpt, [ + 'labels' => [ + 'name' => __('Coupons', 'memberpress'), + 'singular_name' => __('Coupon', 'memberpress'), + 'add_new' => __('Add New', 'memberpress'), + 'add_new_item' => __('Add New Coupon', 'memberpress'), + 'edit_item' => __('Edit Coupon', 'memberpress'), + 'new_item' => __('New Coupon', 'memberpress'), + 'view_item' => __('View Coupon', 'memberpress'), + 'search_items' => __('Search Coupons', 'memberpress'), + 'not_found' => __('No Coupons found', 'memberpress'), + 'not_found_in_trash' => __('No Coupons found in Trash', 'memberpress'), + 'parent_item_colon' => __('Parent Coupon:', 'memberpress'), + ], + 'public' => false, + 'show_ui' => true, // MeprUpdateCtrl::is_activated(), + 'show_in_menu' => 'memberpress', + 'capability_type' => 'page', + 'hierarchical' => false, + 'register_meta_box_cb' => 'MeprCouponsCtrl::add_meta_boxes', + 'rewrite' => false, + 'supports' => ['title'], + ]); + } + + public static function columns($columns) + { + $columns = [ + 'cb' => '', + 'title' => __('Code', 'memberpress'), + 'coupon-description' => __('Description', 'memberpress'), + 'date' => __('Created', 'memberpress'), + 'coupon-discount' => __('Discount', 'memberpress'), + 'coupon-dm' => __('Mode', 'memberpress'), + 'coupon-starts' => __('Starts', 'memberpress'), + 'coupon-expires' => __('Expires', 'memberpress'), + 'coupon-count' => __('Usage Count', 'memberpress'), + 'coupon-products' => __('Applies To', 'memberpress'), + ]; + + return $columns; + } + + public static function custom_columns($column, $coupon_id) + { + $mepr_options = MeprOptions::fetch(); + $coupon = new MeprCoupon($coupon_id); + + if ($coupon->ID !== null) { + switch ($column) { + case 'coupon-description': + echo strip_tags($coupon->post_content); + break; + case 'coupon-discount': + if ($coupon->discount_mode == 'first-payment') { + echo $coupon->first_payment_discount_amount; // Update this to show proper currency symbol later + echo ($coupon->first_payment_discount_type == 'percent') ? __('%', 'memberpress') : $mepr_options->currency_code; + echo ' → '; + } + + echo $coupon->discount_amount; // Update this to show proper currency symbol later + echo ($coupon->discount_type == 'percent') ? __('%', 'memberpress') : $mepr_options->currency_code; + break; + case 'coupon-starts': + if ($coupon->post_status != 'trash') { + if ($coupon->should_start) { + echo MeprUtils::get_date_from_ts($coupon->starts_on); + } else { + _e('Immediately', 'memberpress'); + } + } else { + _e('Expired', 'memberpress'); // They've moved this to trash so show it as expired + } + break; + case 'coupon-expires': + if ($coupon->post_status != 'trash') { + if ($coupon->should_expire) { + echo MeprUtils::get_date_from_ts($coupon->expires_on); + } else { + _e('Never', 'memberpress'); + } + } else { + _e('Expired', 'memberpress'); // They've moved this to trash so show it as expired + } + break; + case 'coupon-count': + echo ''; + if ($coupon->usage_amount) { + echo (int)$coupon->usage_count . ' / ' . $coupon->usage_amount; + } else { + echo (int)$coupon->usage_count . ' / ' . __('Unlimited', 'memberpress'); + } + echo ''; + break; + case 'coupon-dm': + if ($coupon->discount_mode == 'trial-override') { + printf(__('Trial: %1$s days for %2$s', 'memberpress'), $coupon->trial_days, MeprAppHelper::format_currency($coupon->trial_amount)); + } elseif ($coupon->discount_mode == 'first-payment') { + echo esc_html_x('First Payment', 'ui', 'memberpress'); + } elseif ($coupon->discount_mode == 'standard') { + echo esc_html_x('Standard', 'ui', 'memberpress'); + } else { + echo esc_html_x('None', 'ui', 'memberpress'); + } + break; + case 'coupon-products': + echo implode(', ', $coupon->get_formatted_products()); } - } - else { - _e('Expired', 'memberpress'); //They've moved this to trash so show it as expired - } - break; - case 'coupon-count': - echo ''; - if($coupon->usage_amount) { - echo (int)$coupon->usage_count.' / '.$coupon->usage_amount; - } - else { - echo (int)$coupon->usage_count.' / '.__('Unlimited', 'memberpress'); - } - echo ''; - break; - case 'coupon-dm': - if($coupon->discount_mode=='trial-override') { - printf(__('Trial: %1$s days for %2$s','memberpress'), $coupon->trial_days, MeprAppHelper::format_currency($coupon->trial_amount)); - } - else if($coupon->discount_mode=='first-payment') { - echo esc_html_x('First Payment', 'ui', 'memberpress'); - } - else if($coupon->discount_mode=='standard') { - echo esc_html_x('Standard', 'ui', 'memberpress'); - } - else { - echo esc_html_x('None', 'ui', 'memberpress'); - } - break; - case 'coupon-products': - echo implode(', ', $coupon->get_formatted_products()); - } + } } - } - public static function add_meta_boxes() { - global $post_id; - $c = new MeprCoupon($post_id); + public static function add_meta_boxes() + { + global $post_id; + $c = new MeprCoupon($post_id); - add_meta_box("memberpress-coupon-meta", __("Coupon Options", 'memberpress'), "MeprCouponsCtrl::coupon_meta_box", MeprCoupon::$cpt, "normal", "high"); - add_meta_box("memberpress-coupon-description", __("Description", 'memberpress'), "MeprCouponsCtrl::coupon_description_box", MeprCoupon::$cpt, "normal", "high"); + add_meta_box('memberpress-coupon-meta', __('Coupon Options', 'memberpress'), 'MeprCouponsCtrl::coupon_meta_box', MeprCoupon::$cpt, 'normal', 'high'); + add_meta_box('memberpress-coupon-description', __('Description', 'memberpress'), 'MeprCouponsCtrl::coupon_description_box', MeprCoupon::$cpt, 'normal', 'high'); - MeprHooks::do_action('mepr-coupon-meta-boxes', $c); - } + MeprHooks::do_action('mepr-coupon-meta-boxes', $c); + } - public static function coupon_meta_box() { - global $post_id; - $mepr_options = MeprOptions::fetch(); - $c = new MeprCoupon($post_id); + public static function coupon_meta_box() + { + global $post_id; + $mepr_options = MeprOptions::fetch(); + $c = new MeprCoupon($post_id); - MeprView::render('/admin/coupons/form', get_defined_vars()); - } + MeprView::render('/admin/coupons/form', get_defined_vars()); + } - public static function coupon_description_box() { - global $post_id; - $c = new MeprCoupon($post_id); + public static function coupon_description_box() + { + global $post_id; + $c = new MeprCoupon($post_id); - ?> + ?> - post_type == MeprCoupon::$cpt) { + $coupon = new MeprCoupon($post_id); + + if (isset($_POST[MeprCoupon::$should_start_str])) { + $coupon->should_start = true; + $month = isset($_POST[MeprCoupon::$starts_on_month_str]) ? $_POST[MeprCoupon::$starts_on_month_str] : 1; + $day = isset($_POST[MeprCoupon::$starts_on_day_str]) ? $_POST[MeprCoupon::$starts_on_day_str] : 1; + $year = isset($_POST[MeprCoupon::$starts_on_year_str]) ? $_POST[MeprCoupon::$starts_on_year_str] : 1970; + $coupon->start_timezone = isset($_POST[MeprCoupon::$start_on_timezone_str]) ? $_POST[MeprCoupon::$start_on_timezone_str] : 0; + $coupon->starts_on = MeprUtils::make_ts_date($month, $day, $year, true); + + if (!empty($coupon->starts_on)) { + $minimum_start_date = new DateTime(); + + // get datetime object of coupon starts_on : DateTime + $coupon_start_date = new DateTime(); + $coupon_start_ts = MeprCouponsHelper::convert_timestamp_to_tz($coupon->starts_on, $coupon->start_timezone); // Convert UTC timestamp to selected timezone timestamp. + $coupon_start_date->setTimestamp($coupon_start_ts); + + if ($minimum_start_date > $coupon_start_date) { + $coupon->should_start = false; + $coupon->starts_on = 0; + } + } + } else { + $coupon->should_start = false; + $coupon->starts_on = 0; + } + + if (isset($_POST[MeprCoupon::$should_expire_str])) { + $coupon->should_expire = true; + $month = isset($_POST[MeprCoupon::$expires_on_month_str]) ? $_POST[MeprCoupon::$expires_on_month_str] : 1; + $day = isset($_POST[MeprCoupon::$expires_on_day_str]) ? $_POST[MeprCoupon::$expires_on_day_str] : 1; + $year = isset($_POST[MeprCoupon::$expires_on_year_str]) ? $_POST[MeprCoupon::$expires_on_year_str] : 1970; + $coupon->expire_timezone = isset($_POST[MeprCoupon::$expires_on_timezone_str]) ? $_POST[MeprCoupon::$expires_on_timezone_str] : 0; + $coupon->expires_on = MeprUtils::make_ts_date($month, $day, $year); // 23:59:59 of the chosen day + } else { + $coupon->should_expire = false; + $coupon->expires_on = 0; + } + + if (isset($_POST[MeprCoupon::$usage_amount_str]) and is_numeric($_POST[MeprCoupon::$usage_amount_str])) { + $coupon->usage_amount = sanitize_text_field($_POST[MeprCoupon::$usage_amount_str]); + } else { + $coupon->usage_amount = 0; + } - if(defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) { return $post_id; } + $coupon->discount_type = isset($_POST[MeprCoupon::$discount_type_str]) ? sanitize_text_field($_POST[MeprCoupon::$discount_type_str]) : 'percent'; + $coupon->discount_amount = isset($_POST[MeprCoupon::$discount_amount_str]) ? (float)sanitize_text_field($_POST[MeprCoupon::$discount_amount_str]) : 0; - if(defined('DOING_AJAX')) { return; } + if ($coupon->discount_type == 'percent' && $coupon->discount_amount > 100) { + $coupon->discount_amount = 100; // Make sure percent is never > 100 + } - if(!empty($post) && $post->post_type == MeprCoupon::$cpt) { - $coupon = new MeprCoupon($post_id); + $coupon->first_payment_discount_type = isset($_POST[MeprCoupon::$first_payment_discount_type_str]) ? sanitize_text_field($_POST[MeprCoupon::$first_payment_discount_type_str]) : 'percent'; + $coupon->first_payment_discount_amount = isset($_POST[MeprCoupon::$first_payment_discount_amount_str]) ? (float)sanitize_text_field($_POST[MeprCoupon::$first_payment_discount_amount_str]) : 0; - if(isset($_POST[MeprCoupon::$should_start_str])) { - $coupon->should_start = true; - $month = isset($_POST[MeprCoupon::$starts_on_month_str])?$_POST[MeprCoupon::$starts_on_month_str]:1; - $day = isset($_POST[MeprCoupon::$starts_on_day_str])?$_POST[MeprCoupon::$starts_on_day_str]:1; - $year = isset($_POST[MeprCoupon::$starts_on_year_str])?$_POST[MeprCoupon::$starts_on_year_str]:1970; - $coupon->start_timezone = isset($_POST[MeprCoupon::$start_on_timezone_str])?$_POST[MeprCoupon::$start_on_timezone_str]:0; - $coupon->starts_on = MeprUtils::make_ts_date($month, $day, $year, true); + if ($coupon->first_payment_discount_type == 'percent' && $coupon->first_payment_discount_amount > 100) { + $coupon->first_payment_discount_amount = 100; // Make sure percent is never > 100 + } - if(!empty($coupon->starts_on)) { - $minimum_start_date = new DateTime(); + $coupon->use_on_upgrades = isset($_POST[MeprCoupon::$use_on_upgrades_str]); - // get datetime object of coupon starts_on : DateTime - $coupon_start_date = new DateTime(); - $coupon_start_ts = MeprCouponsHelper::convert_timestamp_to_tz($coupon->starts_on,$coupon->start_timezone); // Convert UTC timestamp to selected timezone timestamp. - $coupon_start_date->setTimestamp($coupon_start_ts); + $coupon->valid_products = isset($_POST[MeprCoupon::$valid_products_str]) ? $_POST[MeprCoupon::$valid_products_str] : []; + $coupon->discount_mode = sanitize_text_field($_POST[MeprCoupon::$discount_mode_str]); + $coupon->trial_days = isset($_POST[MeprCoupon::$trial_days_str]) ? (int)sanitize_text_field($_POST[MeprCoupon::$trial_days_str]) : 0; + $coupon->trial_amount = isset($_POST[MeprCoupon::$trial_amount_str]) ? (float) sanitize_text_field($_POST[MeprCoupon::$trial_amount_str]) : 0.00; - if($minimum_start_date > $coupon_start_date){ - $coupon->should_start = false; - $coupon->starts_on = 0; - } + if (isset($_POST[MeprCoupon::$usage_per_user_count_str]) and is_numeric($_POST[MeprCoupon::$usage_per_user_count_str])) { + $coupon->usage_per_user_count = $coupon->usage_amount <= 0 || $_POST[MeprCoupon::$usage_per_user_count_str] <= $coupon->usage_amount ? sanitize_text_field($_POST[MeprCoupon::$usage_per_user_count_str]) : $coupon->usage_amount; + } else { + $coupon->usage_per_user_count = 0; + } + $coupon->usage_per_user_count_timeframe = isset($_POST[MeprCoupon::$usage_per_user_count_timeframe_str]) ? sanitize_text_field($_POST[MeprCoupon::$usage_per_user_count_timeframe_str]) : 'lifetime'; + $coupon->store_meta(); + + MeprHooks::do_action('mepr-coupon-save-meta', $coupon); } - } - else { - $coupon->should_start = false; - $coupon->starts_on = 0; - } - - if(isset($_POST[MeprCoupon::$should_expire_str])) { - $coupon->should_expire = true; - $month = isset($_POST[MeprCoupon::$expires_on_month_str])?$_POST[MeprCoupon::$expires_on_month_str]:1; - $day = isset($_POST[MeprCoupon::$expires_on_day_str])?$_POST[MeprCoupon::$expires_on_day_str]:1; - $year = isset($_POST[MeprCoupon::$expires_on_year_str])?$_POST[MeprCoupon::$expires_on_year_str]:1970; - $coupon->expire_timezone = isset($_POST[MeprCoupon::$expires_on_timezone_str])?$_POST[MeprCoupon::$expires_on_timezone_str]:0; - $coupon->expires_on = MeprUtils::make_ts_date($month, $day, $year); //23:59:59 of the chosen day - } - else { - $coupon->should_expire = false; - $coupon->expires_on = 0; - } - - if( isset($_POST[MeprCoupon::$usage_amount_str]) and is_numeric($_POST[MeprCoupon::$usage_amount_str]) ) { - $coupon->usage_amount = sanitize_text_field($_POST[MeprCoupon::$usage_amount_str]); - } - else { - $coupon->usage_amount = 0; - } - - $coupon->discount_type = isset($_POST[MeprCoupon::$discount_type_str])?sanitize_text_field($_POST[MeprCoupon::$discount_type_str]):'percent'; - $coupon->discount_amount = isset($_POST[MeprCoupon::$discount_amount_str])?(float)sanitize_text_field($_POST[MeprCoupon::$discount_amount_str]):0; - - if($coupon->discount_type == 'percent' && $coupon->discount_amount > 100) { - $coupon->discount_amount = 100; //Make sure percent is never > 100 - } - - $coupon->first_payment_discount_type = isset($_POST[MeprCoupon::$first_payment_discount_type_str])?sanitize_text_field($_POST[MeprCoupon::$first_payment_discount_type_str]):'percent'; - $coupon->first_payment_discount_amount = isset($_POST[MeprCoupon::$first_payment_discount_amount_str])?(float)sanitize_text_field($_POST[MeprCoupon::$first_payment_discount_amount_str]):0; - - if($coupon->first_payment_discount_type == 'percent' && $coupon->first_payment_discount_amount > 100) { - $coupon->first_payment_discount_amount = 100; //Make sure percent is never > 100 - } - - $coupon->use_on_upgrades = isset( $_POST[MeprCoupon::$use_on_upgrades_str] ); - - $coupon->valid_products = isset($_POST[MeprCoupon::$valid_products_str])?$_POST[MeprCoupon::$valid_products_str]:array(); - $coupon->discount_mode = sanitize_text_field($_POST[MeprCoupon::$discount_mode_str]); - $coupon->trial_days = isset($_POST[MeprCoupon::$trial_days_str])?(int)sanitize_text_field($_POST[MeprCoupon::$trial_days_str]):0; - $coupon->trial_amount = isset($_POST[MeprCoupon::$trial_amount_str]) ? (float) sanitize_text_field( $_POST[MeprCoupon::$trial_amount_str] ) : 0.00; - - if(isset($_POST[MeprCoupon::$usage_per_user_count_str]) and is_numeric($_POST[MeprCoupon::$usage_per_user_count_str])){ - $coupon->usage_per_user_count = $coupon->usage_amount <= 0 || $_POST[MeprCoupon::$usage_per_user_count_str] <= $coupon->usage_amount ? sanitize_text_field( $_POST[MeprCoupon::$usage_per_user_count_str] ) : $coupon->usage_amount; - } else { - $coupon->usage_per_user_count = 0; - } - $coupon->usage_per_user_count_timeframe = isset($_POST[MeprCoupon::$usage_per_user_count_timeframe_str])?sanitize_text_field($_POST[MeprCoupon::$usage_per_user_count_timeframe_str]):'lifetime'; - $coupon->store_meta(); - - MeprHooks::do_action('mepr-coupon-save-meta', $coupon); } - } - public static function sanitize_coupon_title($data, $postarr) { - global $wpdb; + public static function sanitize_coupon_title($data, $postarr) + { + global $wpdb; + + if ($data['post_type'] == MeprCoupon::$cpt) { + // Get rid of invalid chars + $data['post_title'] = preg_replace(['/ +/', '/[^A-Za-z0-9_-]/'], ['-', ''], $data['post_title']); - if($data['post_type'] == MeprCoupon::$cpt) { - //Get rid of invalid chars - $data['post_title'] = preg_replace(array('/ +/', '/[^A-Za-z0-9_-]/'), array('-', ''), $data['post_title']); + // Begin duplicate titles handling + $q1 = "SELECT ID FROM {$wpdb->posts} WHERE post_title = %s AND post_type = %s AND ID <> %d LIMIT 1"; + $q2 = $wpdb->prepare($q1, $data['post_title'], MeprCoupon::$cpt, $postarr['ID']); + $count = 0; - //Begin duplicate titles handling - $q1 = "SELECT ID FROM {$wpdb->posts} WHERE post_title = %s AND post_type = %s AND ID <> %d LIMIT 1"; - $q2 = $wpdb->prepare($q1, $data['post_title'], MeprCoupon::$cpt, $postarr['ID']); - $count = 0; + if (is_admin()) { + while (($id = $wpdb->get_var($q2))) { + ++$count; // Want to increment before running the query, so when we exit the loop $data['post_title'] . "-{$count}" is stil valid + $q2 = $wpdb->prepare($q1, $data['post_title'] . "-{$count}", MeprCoupon::$cpt, $postarr['ID']); + } + } - if(is_admin()) { - while( ($id = $wpdb->get_var($q2)) ) { - ++$count; //Want to increment before running the query, so when we exit the loop $data['post_title'] . "-{$count}" is stil valid - $q2 = $wpdb->prepare($q1, $data['post_title'] . "-{$count}", MeprCoupon::$cpt, $postarr['ID']); + if ($count > 0) { + $data['post_title'] .= "-{$count}"; + } + // End duplicate titles handling } - } - if($count > 0) { - $data['post_title'] .= "-{$count}"; - } - //End duplicate titles handling + return $data; } - return $data; - } - - public static function disable_row($actions, $post) { - global $current_screen; + public static function disable_row($actions, $post) + { + global $current_screen; - if(!isset($current_screen->post_type) || $current_screen->post_type != MeprCoupon::$cpt) { return $actions; } + if (!isset($current_screen->post_type) || $current_screen->post_type != MeprCoupon::$cpt) { + return $actions; + } - unset($actions['inline hide-if-no-js']); //Hides quick-edit - unset($actions['delete']); //Hides permanantely delete + unset($actions['inline hide-if-no-js']); // Hides quick-edit + unset($actions['delete']); // Hides permanantely delete - return $actions; - } + return $actions; + } - public static function disable_bulk($actions) { - unset($actions['delete']); //disables permanent delete bulk action - unset($actions['edit']); //disables bulk edit + public static function disable_bulk($actions) + { + unset($actions['delete']); // disables permanent delete bulk action + unset($actions['edit']); // disables bulk edit - return $actions; - } + return $actions; + } - public static function admin_enqueue_scripts($hook) { - global $current_screen; + public static function admin_enqueue_scripts($hook) + { + global $current_screen; - $l10n = array('mepr_no_products_message' => __('Please select at least one Membership before saving.', 'memberpress')); + $l10n = ['mepr_no_products_message' => __('Please select at least one Membership before saving.', 'memberpress')]; - if($current_screen->post_type == MeprCoupon::$cpt) { - wp_register_style( 'mepr-settings-table-css', MEPR_CSS_URL.'/settings_table.css', array(), MEPR_VERSION ); - wp_enqueue_style('mepr-coupons-css', MEPR_CSS_URL.'/admin-coupons.css', array('mepr-settings-table-css'), MEPR_VERSION); + if ($current_screen->post_type == MeprCoupon::$cpt) { + wp_register_style('mepr-settings-table-css', MEPR_CSS_URL . '/settings_table.css', [], MEPR_VERSION); + wp_enqueue_style('mepr-coupons-css', MEPR_CSS_URL . '/admin-coupons.css', ['mepr-settings-table-css'], MEPR_VERSION); - wp_register_script('mepr-settings-table-js', MEPR_JS_URL.'/settings_table.js', array('jquery'), MEPR_VERSION); - wp_dequeue_script('autosave'); //Disable auto-saving - wp_enqueue_script('mepr-coupons-js', MEPR_JS_URL.'/admin_coupons.js', array('jquery','mepr-settings-table-js'), MEPR_VERSION); - wp_localize_script('mepr-coupons-js', 'MeprCoupon', $l10n); + wp_register_script('mepr-settings-table-js', MEPR_JS_URL . '/settings_table.js', ['jquery'], MEPR_VERSION); + wp_dequeue_script('autosave'); // Disable auto-saving + wp_enqueue_script('mepr-coupons-js', MEPR_JS_URL . '/admin_coupons.js', ['jquery','mepr-settings-table-js'], MEPR_VERSION); + wp_localize_script('mepr-coupons-js', 'MeprCoupon', $l10n); - do_action('mepr-coupon-admin-enqueue-script', $hook); + do_action('mepr-coupon-admin-enqueue-script', $hook); + } } - } - public static function get_page_title_code($title) { - global $current_screen; + public static function get_page_title_code($title) + { + global $current_screen; - if(empty($title) && isset($current_screen->post_type) && $current_screen->post_type == MeprCoupon::$cpt) { - return MeprUtils::random_string(10,false,true); - } - else { - return $title; + if (empty($title) && isset($current_screen->post_type) && $current_screen->post_type == MeprCoupon::$cpt) { + return MeprUtils::random_string(10, false, true); + } else { + return $title; + } } - } - public static function validate_coupon_ajax($code = null, $product_id = null) { - check_ajax_referer('mepr_coupons', 'coupon_nonce'); + public static function validate_coupon_ajax($code = null, $product_id = null) + { + check_ajax_referer('mepr_coupons', 'coupon_nonce'); + + if (empty($code) || empty($product_id)) { + if (!isset($_POST['code']) || empty($_POST['code']) || !isset($_POST['prd_id']) || empty($_POST['prd_id'])) { + echo 'false'; + die(); + } else { + $code = wp_unslash($_POST['code']); + $product_id = wp_unslash($_POST['prd_id']); + } + } - if(empty($code) || empty($product_id)) { - if(!isset($_POST['code']) || empty($_POST['code']) || !isset($_POST['prd_id']) || empty($_POST['prd_id'])) { - echo 'false'; - die(); - } - else { - $code = wp_unslash($_POST['code']); - $product_id = wp_unslash($_POST['prd_id']); - } - } + $is_valid = MeprCoupon::is_valid_coupon_code($code, $product_id); + $output = apply_filters('mepr-validate-coupon', $is_valid, $code, $product_id); - $is_valid = MeprCoupon::is_valid_coupon_code($code, $product_id); - $output = apply_filters( 'mepr-validate-coupon', $is_valid, $code, $product_id ); + if ($output) { + echo 'true'; + } else { + echo 'false'; + } - if($output) { - echo 'true'; - } - else { - echo 'false'; + die(); } - die(); - } - - public static function update_coupon_usage_count($object) { - if(!isset($object->coupon_id) || empty($object->coupon_id)) { return; } + public static function update_coupon_usage_count($object) + { + if (!isset($object->coupon_id) || empty($object->coupon_id)) { + return; + } - $coupon = new MeprCoupon($object->coupon_id); + $coupon = new MeprCoupon($object->coupon_id); - if($coupon->ID) { - $coupon->update_usage_count(); + if ($coupon->ID) { + $coupon->update_usage_count(); + } } - } } //End class diff --git a/app/controllers/MeprCoursesCtrl.php b/app/controllers/MeprCoursesCtrl.php index a31e44a..985b40b 100644 --- a/app/controllers/MeprCoursesCtrl.php +++ b/app/controllers/MeprCoursesCtrl.php @@ -1,173 +1,182 @@ courses_slug ) ) { - add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_scripts' ) ); - add_action( 'wp_ajax_mepr_courses_action', array( $this, 'ajax_courses_action' ) ); - add_action( 'mepr_display_options_tabs', array( $this, 'courses_tab' ), 99 ); - add_action( 'mepr_display_options', array( $this, 'courses_tab_content' ) ); +if (!defined('ABSPATH')) { + die('You are not allowed to call this page directly.'); +} + +class MeprCoursesCtrl extends MeprBaseCtrl +{ + private $courses_slug = 'memberpress-courses/main.php'; + + public function load_hooks() + { + if (! is_plugin_active($this->courses_slug)) { + add_action('admin_enqueue_scripts', [$this, 'enqueue_scripts']); + add_action('wp_ajax_mepr_courses_action', [$this, 'ajax_courses_action']); + add_action('mepr_display_options_tabs', [$this, 'courses_tab'], 99); + add_action('mepr_display_options', [$this, 'courses_tab_content']); + } } - } - - public static function route() { - $plugins = get_plugins(); - MeprView::render('/admin/courses/ui', get_defined_vars()); - } - public function enqueue_scripts($hook) { - if(preg_match('/_page_memberpress-(courses|options)$/', $hook)) { - wp_enqueue_style('mepr-sister-plugin-css', MEPR_CSS_URL . '/admin-sister-plugin.css', array(), MEPR_VERSION); - } - } - - /** - * Adds the "Courses" tab to the MemberPress settings page. - * - * @return void - */ - public function courses_tab() { - ?> - - -
- -
- install_courses( true ); - $activated = $installed ? $installed : $activated; - $result = $installed ? 'success' : 'error'; - $message = $installed ? esc_html__( 'Courses has been installed and activated successfully. Enjoy!', 'memberpress' ) : esc_html__( 'Courses could not be installed. Please check your license settings, or contact MemberPress support for help.', 'memberpress' ); - break; - case 'activate' : // Just activate (already installed) - $activated = is_null( activate_plugin( $this->courses_slug ) ); - $result = 'success'; - $message = esc_html__( 'Courses has been activated successfully. Enjoy!', 'memberpress' ); - break; - default: - break; + /** + * Adds the "Courses" tab to the MemberPress settings page. + * + * @return void + */ + public function courses_tab() + { + ?> + + 'mpcs-course', - 'courses_activated' => 'true' - ), admin_url( 'edit.php' ) ); + /** + * Renders the "Courses" tab content. + * + * @return void + */ + public function courses_tab_content() + { + ?> +
+ +
+ $installed, - 'activated' => $activated, - 'result' => $result, - 'message' => $message, - 'redirect' => $redirect - ) ); - } - - /** - * Install the MemberPress Courses addon - * - * @param boolean $activate Whether to activate after installing - * - * @return boolean Whether the plugin was installed - */ - public function install_courses( $activate = false ) { - - $force = isset($_GET['refresh']) && $_GET['refresh'] == 'true'; - $addons = (array) MeprUpdateCtrl::addons(true, $force, true); - $courses_addon = ! empty( $addons['memberpress-courses'] ) ? $addons['memberpress-courses'] : array(); - $plugins = get_plugins(); - wp_cache_delete('plugins', 'plugins'); - - if ( empty( $courses_addon ) ) { - return false; + /** + * Handle actions for MemberPress Courses + * + * @return void + */ + public function ajax_courses_action() + { + + if (empty($_POST['nonce']) || ! wp_verify_nonce($_POST['nonce'], 'mepr_courses_action')) { + die(); + } + + if (! current_user_can('activate_plugins')) { + wp_send_json_error(__('Sorry, you don\'t have permission to do this.', 'memberpress')); + } + + $type = sanitize_text_field($_POST['type']); + $installed = false; + $activated = false; + $message = ''; + $result = 'error'; + switch ($type) { + case 'install-activate': // Install and activate courses + $installed = $this->install_courses(true); + $activated = $installed ? $installed : $activated; + $result = $installed ? 'success' : 'error'; + $message = $installed ? esc_html__('Courses has been installed and activated successfully. Enjoy!', 'memberpress') : esc_html__('Courses could not be installed. Please check your license settings, or contact MemberPress support for help.', 'memberpress'); + break; + case 'activate': // Just activate (already installed) + $activated = is_null(activate_plugin($this->courses_slug)); + $result = 'success'; + $message = esc_html__('Courses has been activated successfully. Enjoy!', 'memberpress'); + break; + default: + break; + } + + delete_option('mepr_courses_flushed_rewrite_rules'); + + $redirect = ''; + + if ($activated) { + $redirect = add_query_arg([ + 'post_type' => 'mpcs-course', + 'courses_activated' => 'true', + ], admin_url('edit.php')); + } + + wp_send_json_success([ + 'installed' => $installed, + 'activated' => $activated, + 'result' => $result, + 'message' => $message, + 'redirect' => $redirect, + ]); } - // Set the current screen to avoid undefined notices - set_current_screen( "memberpress_page_{$this->courses_slug}" ); - - // Prepare variables - $url = esc_url_raw( - add_query_arg( - array( - 'page' => $this->courses_slug, - ), - admin_url('admin.php') - ) - ); - - $creds = request_filesystem_credentials( $url, '', false, false, null ); - - // Check for file system permissions - if ( false === $creds ) { - wp_send_json_error( esc_html( 'File system credentials failed.', 'memberpress' ) ); - } - if ( ! WP_Filesystem( $creds ) ) { - wp_send_json_error( esc_html( 'File system credentials failed.', 'memberpress' ) ); + /** + * Install the MemberPress Courses addon + * + * @param boolean $activate Whether to activate after installing + * + * @return boolean Whether the plugin was installed + */ + public function install_courses($activate = false) + { + + $force = isset($_GET['refresh']) && $_GET['refresh'] == 'true'; + $addons = (array) MeprUpdateCtrl::addons(true, $force, true); + $courses_addon = ! empty($addons['memberpress-courses']) ? $addons['memberpress-courses'] : []; + $plugins = get_plugins(); + wp_cache_delete('plugins', 'plugins'); + + if (empty($courses_addon)) { + return false; + } + + // Set the current screen to avoid undefined notices + set_current_screen("memberpress_page_{$this->courses_slug}"); + + // Prepare variables + $url = esc_url_raw( + add_query_arg( + [ + 'page' => $this->courses_slug, + ], + admin_url('admin.php') + ) + ); + + $creds = request_filesystem_credentials($url, '', false, false, null); + + // Check for file system permissions + if (false === $creds) { + wp_send_json_error(esc_html('File system credentials failed.', 'memberpress')); + } + if (! WP_Filesystem($creds)) { + wp_send_json_error(esc_html('File system credentials failed.', 'memberpress')); + } + + // We do not need any extra credentials if we have gotten this far, so let's install the plugin + require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php'; + + // Do not allow WordPress to search/download translations, as this will break JS output + remove_action('upgrader_process_complete', ['Language_Pack_Upgrader', 'async_upgrade'], 20); + + // Create the plugin upgrader with our custom skin + $installer = new Plugin_Upgrader(new MeprAddonInstallSkin()); + + $plugin = wp_unslash($courses_addon->url); + $installer->install($plugin); + + // Flush the cache and return the newly installed plugin basename + wp_cache_flush(); + + if ($installer->plugin_info() && true === $activate) { + activate_plugin($installer->plugin_info()); + } + + return $installer->plugin_info(); } - - // We do not need any extra credentials if we have gotten this far, so let's install the plugin - require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php'; - - // Do not allow WordPress to search/download translations, as this will break JS output - remove_action( 'upgrader_process_complete', array( 'Language_Pack_Upgrader', 'async_upgrade' ), 20 ); - - // Create the plugin upgrader with our custom skin - $installer = new Plugin_Upgrader( new MeprAddonInstallSkin() ); - - $plugin = wp_unslash( $courses_addon->url ); - $installer->install( $plugin ); - - // Flush the cache and return the newly installed plugin basename - wp_cache_flush(); - - if ( $installer->plugin_info() && true === $activate ) { - activate_plugin( $installer->plugin_info() ); - } - - return $installer->plugin_info(); - } - } //End class diff --git a/app/controllers/MeprDbCtrl.php b/app/controllers/MeprDbCtrl.php index d8a6bc3..4ff3bc6 100644 --- a/app/controllers/MeprDbCtrl.php +++ b/app/controllers/MeprDbCtrl.php @@ -1,225 +1,251 @@ MeprUtils::minutes(10), // Run every 10 minutes - 'display' => __('MemberPress Member Data Migrate Interval', 'memberpress'), - ); - - return $schedules; - } - - public function upgrade_needed() { - if(defined('DOING_AJAX')) { return; } - if(isset($_GET['page']) && $_GET['page']=='mepr-rollback') { return; } - if(isset($_GET['mepraction']) && $_GET['mepraction']=='cancel-migration') { - delete_transient('mepr_migrating'); - delete_transient('mepr_migration_error'); - delete_transient('mepr_current_migration'); - update_option('mepr_db_version',MEPR_VERSION); - //wp_redirect(admin_url('index.php')); - return; - } - $mepr_db = new MeprDb(); +if (!defined('ABSPATH')) { + die('You are not allowed to call this page directly.'); +} + +class MeprDbCtrl extends MeprBaseCtrl +{ + public function load_hooks() + { + // DB upgrades will happen here, as a non-blocking process hopefully + add_action('init', [$this,'upgrade_needed'], 1); + add_action('wp_ajax_mepr_db_upgrade', [$this,'ajax_db_upgrade']); + add_action('wp_ajax_mepr_db_upgrade_success', [$this,'ajax_db_upgrade_success']); + add_action('wp_ajax_mepr_db_upgrade_not_needed', [$this,'ajax_db_upgrade_not_needed']); + add_action('wp_ajax_mepr_db_upgrade_in_progress', [$this,'ajax_db_upgrade_in_progress']); + add_action('wp_ajax_mepr_db_upgrade_error', [$this,'ajax_db_upgrade_error']); + + add_filter('cron_schedules', [$this,'intervals']); + add_action('mepr_migrate_members_table_015', 'MeprDbMigrations::populate_inactive_memberships_col_015'); + + // Cleanup soft db migrate for now + // TODO: Remove soon + if (($timestamp = wp_next_scheduled('mepr_migration_worker'))) { + wp_unschedule_event($timestamp, 'mepr_migration_worker'); + } - if(is_admin() && MeprUtils::is_mepr_admin() && $mepr_db->show_upgrade_ui()) { - MeprView::render('admin/db/upgrade_needed'); - exit; + // Small UI to check on some table records + add_action('mepr_activate_license_page', [$this, 'activate_license_page']); } - else { - try { - delete_transient('mepr_migration_error'); // Reset migration error transient - $this->upgrade(); - } - catch(MeprDbMigrationException $e) { - // We just log the error? - } - } - } - public function ajax_db_upgrade() { - check_ajax_referer('db_upgrade', 'mepr_db_upgrade_nonce'); + public function intervals($schedules) + { + $schedules['mepr_migrate_members_table_015_interval'] = [ + 'interval' => MeprUtils::minutes(10), // Run every 10 minutes + 'display' => __('MemberPress Member Data Migrate Interval', 'memberpress'), + ]; - // Network super admin and non-network admins will be the only ones dealing with upgrades - if(!MeprUtils::is_mepr_admin()) { - header('HTTP/1.1 403 Forbidden'); - exit(json_encode(array('error'=>__('You\'re unauthorized to access this resource.', 'memberpress')))); + return $schedules; } - $mepr_db = MeprDb::fetch(); - - header('Content-Type: application/json'); + public function upgrade_needed() + { + if (defined('DOING_AJAX')) { + return; + } + if (isset($_GET['page']) && $_GET['page'] == 'mepr-rollback') { + return; + } + if (isset($_GET['mepraction']) && $_GET['mepraction'] == 'cancel-migration') { + delete_transient('mepr_migrating'); + delete_transient('mepr_migration_error'); + delete_transient('mepr_current_migration'); + update_option('mepr_db_version', MEPR_VERSION); + // wp_redirect(admin_url('index.php')); + return; + } - if($mepr_db->do_upgrade()) { - try { - delete_transient('mepr_migration_error'); // Reset migration error transient - $this->upgrade(); - exit(json_encode(array('status'=>'complete', 'message'=>__('Your Upgrade has completed successfully', 'memberpress')))); - } - catch(MeprDbMigrationException $e) { - header('HTTP/1.1 500 Internal Error'); - exit(json_encode(array('status' => 'error', 'message' => $e->getMessage()))); - } + $mepr_db = new MeprDb(); + + if (is_admin() && MeprUtils::is_mepr_admin() && $mepr_db->show_upgrade_ui()) { + MeprView::render('admin/db/upgrade_needed'); + exit; + } else { + try { + delete_transient('mepr_migration_error'); // Reset migration error transient + $this->upgrade(); + } catch (MeprDbMigrationException $e) { + // We just log the error? + } + } } - exit(json_encode(array('status'=>'already_migrated', 'message'=>__('No need to upgrade your database', 'memberpress')))); - } + public function ajax_db_upgrade() + { + check_ajax_referer('db_upgrade', 'mepr_db_upgrade_nonce'); - public function ajax_db_upgrade_in_progress() { - check_ajax_referer('db_upgrade_in_progress', 'mepr_db_upgrade_nonce'); + // Network super admin and non-network admins will be the only ones dealing with upgrades + if (!MeprUtils::is_mepr_admin()) { + header('HTTP/1.1 403 Forbidden'); + exit(json_encode(['error' => __('You\'re unauthorized to access this resource.', 'memberpress')])); + } - $mig = new MeprDbMigrations(); + $mepr_db = MeprDb::fetch(); + + header('Content-Type: application/json'); + + if ($mepr_db->do_upgrade()) { + try { + delete_transient('mepr_migration_error'); // Reset migration error transient + $this->upgrade(); + exit(json_encode([ + 'status' => 'complete', + 'message' => __('Your Upgrade has completed successfully', 'memberpress'), + ])); + } catch (MeprDbMigrationException $e) { + header('HTTP/1.1 500 Internal Error'); + exit(json_encode([ + 'status' => 'error', + 'message' => $e->getMessage(), + ])); + } + } - // Network super admin and non-network admins will be the only ones dealing with upgrades - if(!MeprUtils::is_mepr_admin()) { - header('HTTP/1.1 403 Forbidden'); - exit(json_encode(array('error'=>__('You\'re unauthorized to access this resource.', 'memberpress')))); + exit(json_encode([ + 'status' => 'already_migrated', + 'message' => __('No need to upgrade your database', 'memberpress'), + ])); } - $version = get_transient('mepr_migrating'); + public function ajax_db_upgrade_in_progress() + { + check_ajax_referer('db_upgrade_in_progress', 'mepr_db_upgrade_nonce'); - if(!empty($version)) { - $current_migration = get_transient('mepr_current_migration'); + $mig = new MeprDbMigrations(); - if(!isset($current_migration['check']) || $current_migration['check'] == false) { - $check = array('completed' => 0, 'total' => 0, 'progress' => 0); - } - else { - $check = call_user_func(array($mig, $current_migration['check'])); - } - - $message = ((!isset($current_migration['message']) || empty($current_migration['message'])) ? __('MemberPress is currently upgrading your database', 'memberpress') : $current_migration['message']); + // Network super admin and non-network admins will be the only ones dealing with upgrades + if (!MeprUtils::is_mepr_admin()) { + header('HTTP/1.1 403 Forbidden'); + exit(json_encode(['error' => __('You\'re unauthorized to access this resource.', 'memberpress')])); + } - extract($check); + $version = get_transient('mepr_migrating'); + + if (!empty($version)) { + $current_migration = get_transient('mepr_current_migration'); + + if (!isset($current_migration['check']) || $current_migration['check'] == false) { + $check = [ + 'completed' => 0, + 'total' => 0, + 'progress' => 0, + ]; + } else { + $check = call_user_func([$mig, $current_migration['check']]); + } + + $message = ((!isset($current_migration['message']) || empty($current_migration['message'])) ? __('MemberPress is currently upgrading your database', 'memberpress') : $current_migration['message']); + + extract($check); + + header('HTTP/1.1 200 OK'); + $status = 'in_progress'; + exit( + json_encode( + compact('status', 'version', 'progress', 'message', 'completed', 'total') + ) + ); + } else { + $error = get_transient('mepr_migration_error'); + if ($error) { + header('HTTP/1.1 500 Internal Error'); + exit(json_encode(['error' => $error])); + } else { + header('HTTP/1.1 200 OK'); + exit(json_encode([ + 'status' => 'not_in_progress', + 'message' => __('No MemberPress database upgrade is in progress', 'memberpress'), + ])); + } + } - header('HTTP/1.1 200 OK'); - $status = 'in_progress'; - exit( - json_encode( - compact('status','version','progress','message','completed','total') - ) - ); - } - else { - $error = get_transient('mepr_migration_error'); - if($error) { - header('HTTP/1.1 500 Internal Error'); - exit(json_encode(array('error'=>$error))); - } - else { - header('HTTP/1.1 200 OK'); - exit(json_encode(array('status'=>'not_in_progress','message'=>__('No MemberPress database upgrade is in progress', 'memberpress')))); - } + exit; } - exit; - } + public function ajax_db_upgrade_success() + { + check_ajax_referer('db_upgrade_success', 'mepr_db_upgrade_nonce'); - public function ajax_db_upgrade_success() { - check_ajax_referer('db_upgrade_success', 'mepr_db_upgrade_nonce'); - - $error = get_transient('mepr_migration_error'); - if($error) { - MeprView::render('admin/db/upgrade_error',compact('error')); - } - else { - MeprView::render('admin/db/upgrade_success'); + $error = get_transient('mepr_migration_error'); + if ($error) { + MeprView::render('admin/db/upgrade_error', compact('error')); + } else { + MeprView::render('admin/db/upgrade_success'); + } + exit; } - exit; - } - - public function ajax_db_upgrade_not_needed() { - check_ajax_referer('db_upgrade_not_needed', 'mepr_db_upgrade_nonce'); - - MeprView::render('admin/db/upgrade_not_needed'); - exit; - } - - public function ajax_db_upgrade_error() { - check_ajax_referer('db_upgrade_error', 'mepr_db_upgrade_nonce'); - - $error = get_transient('mepr_migration_error'); - MeprView::render('admin/db/upgrade_error',compact('error')); - exit; - } - - /** INSTALL PLUGIN - * Handled in the same way wp-cron does it ... - * fast, non-blocking post with an ignore_user_abort - */ - public function upgrade() { - global $wpdb; - $mepr_db = MeprDb::fetch(); - // This is Async now so if we're already migrating this version then let's bail - $already_migrating = get_transient('mepr_migrating'); - if(!empty($already_migrating)) { return; } + public function ajax_db_upgrade_not_needed() + { + check_ajax_referer('db_upgrade_not_needed', 'mepr_db_upgrade_nonce'); - if($mepr_db->do_upgrade()) { - @ignore_user_abort(true); - @set_time_limit(0); - - set_transient('mepr_migrating', MEPR_VERSION, MeprUtils::hours(4)); + MeprView::render('admin/db/upgrade_not_needed'); + exit; + } - // Leave this up to the individual migrations now - //$wpdb->query('START TRANSACTION'); + public function ajax_db_upgrade_error() + { + check_ajax_referer('db_upgrade_error', 'mepr_db_upgrade_nonce'); - if(is_multisite()) { - global $blog_id; + $error = get_transient('mepr_migration_error'); + MeprView::render('admin/db/upgrade_error', compact('error')); + exit; + } - // If we're on the root blog then let's upgrade every site on the network - if($blog_id==1) { - $mepr_db->upgrade_multisite(); - } - else { - $mepr_db->upgrade(); + /** + * INSTALL PLUGIN + * Handled in the same way wp-cron does it ... + * fast, non-blocking post with an ignore_user_abort + */ + public function upgrade() + { + global $wpdb; + $mepr_db = MeprDb::fetch(); + + // This is Async now so if we're already migrating this version then let's bail + $already_migrating = get_transient('mepr_migrating'); + if (!empty($already_migrating)) { + return; } - } - else { - $mepr_db->upgrade(); - } - //$wpdb->query('COMMIT'); - - delete_transient('mepr_migrating'); + if ($mepr_db->do_upgrade()) { + @ignore_user_abort(true); + @set_time_limit(0); + + set_transient('mepr_migrating', MEPR_VERSION, MeprUtils::hours(4)); + + // Leave this up to the individual migrations now + // $wpdb->query('START TRANSACTION'); + if (is_multisite()) { + global $blog_id; + + // If we're on the root blog then let's upgrade every site on the network + if ($blog_id == 1) { + $mepr_db->upgrade_multisite(); + } else { + $mepr_db->upgrade(); + } + } else { + $mepr_db->upgrade(); + } + + // $wpdb->query('COMMIT'); + delete_transient('mepr_migrating'); + } } - } - public function activate_license_page() { - $mepr_db = MeprDb::fetch(); + public function activate_license_page() + { + $mepr_db = MeprDb::fetch(); - if(isset($_GET['db-status']) && $mepr_db->table_exists($mepr_db->members) && $mepr_db->table_exists($mepr_db->subscriptions)) { - $mepr_db_migration = new MeprDbMigrations(); + if (isset($_GET['db-status']) && $mepr_db->table_exists($mepr_db->members) && $mepr_db->table_exists($mepr_db->subscriptions)) { + $mepr_db_migration = new MeprDbMigrations(); - $subs = (object)$mepr_db_migration->check_create_and_migrate_subscriptions_table_001(); - $mbrs = (object)$mepr_db_migration->check_create_and_migrate_members_table_002(); + $subs = (object)$mepr_db_migration->check_create_and_migrate_subscriptions_table_001(); + $mbrs = (object)$mepr_db_migration->check_create_and_migrate_members_table_002(); - ?> + ?>

@@ -227,8 +253,7 @@ public function activate_license_page() {
progress}% ({$subs->completed} / {$subs->total})"; ?>

- is_dev_url())) { - return; - } +class MeprDeactivationSurveyCtrl extends MeprBaseCtrl +{ + public function load_hooks() + { + if (apply_filters('mepr_deactivation_survey_skip', $this->is_dev_url())) { + return; + } - add_action('admin_enqueue_scripts', array($this, 'enqueue')); - add_action('admin_footer', array($this, 'popup')); - } + add_action('admin_enqueue_scripts', [$this, 'enqueue']); + add_action('admin_footer', [$this, 'popup']); + } - protected function is_dev_url() { - $url = network_site_url( '/' ); - $is_local_url = false; + protected function is_dev_url() + { + $url = network_site_url('/'); + $is_local_url = false; - // Trim it up - $url = strtolower( trim( $url ) ); + // Trim it up + $url = strtolower(trim($url)); - // Need to get the host...so let's add the scheme so we can use parse_url - if ( false === strpos( $url, 'http://' ) && false === strpos( $url, 'https://' ) ) { - $url = 'http://' . $url; - } - $url_parts = parse_url( $url ); - $host = ! empty( $url_parts['host'] ) ? $url_parts['host'] : false; - if ( ! empty( $url ) && ! empty( $host ) ) { - if ( false !== ip2long( $host ) ) { - if ( ! filter_var( $host, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE ) ) { - $is_local_url = true; + // Need to get the host...so let's add the scheme so we can use parse_url + if (false === strpos($url, 'http://') && false === strpos($url, 'https://')) { + $url = 'http://' . $url; } - } else if ( 'localhost' === $host ) { - $is_local_url = true; - } - - $tlds_to_check = array( '.dev', '.local', ':8888' ); - foreach ( $tlds_to_check as $tld ) { - if ( false !== strpos( $host, $tld ) ) { - $is_local_url = true; - continue; - } - - } - if ( substr_count( $host, '.' ) > 1 ) { - $subdomains_to_check = array( 'dev.', '*.staging.', 'beta.', 'test.' ); - foreach ( $subdomains_to_check as $subdomain ) { - $subdomain = str_replace( '.', '(.)', $subdomain ); - $subdomain = str_replace( array( '*', '(.)' ), '(.*)', $subdomain ); - if ( preg_match( '/^(' . $subdomain . ')/', $host ) ) { - $is_local_url = true; - continue; - } + $url_parts = parse_url($url); + $host = ! empty($url_parts['host']) ? $url_parts['host'] : false; + if (! empty($url) && ! empty($host)) { + if (false !== ip2long($host)) { + if (! filter_var($host, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) { + $is_local_url = true; + } + } elseif ('localhost' === $host) { + $is_local_url = true; + } + + $tlds_to_check = ['.dev', '.local', ':8888']; + foreach ($tlds_to_check as $tld) { + if (false !== strpos($host, $tld)) { + $is_local_url = true; + continue; + } + } + if (substr_count($host, '.') > 1) { + $subdomains_to_check = ['dev.', '*.staging.', 'beta.', 'test.']; + foreach ($subdomains_to_check as $subdomain) { + $subdomain = str_replace('.', '(.)', $subdomain); + $subdomain = str_replace(['*', '(.)'], '(.*)', $subdomain); + if (preg_match('/^(' . $subdomain . ')/', $host)) { + $is_local_url = true; + continue; + } + } + } } - } - } - - return $is_local_url; - } - public function enqueue() { - if(!$this->is_plugin_page()) { - return; + return $is_local_url; } - wp_enqueue_style('mepr-deactivation-survey', MEPR_CSS_URL . '/admin-deactivation-survey.css', array(), MEPR_VERSION); - wp_enqueue_script('mepr-deactivation-survey', MEPR_JS_URL . '/admin_deactivation_survey.js', array('jquery'), MEPR_VERSION, true); + public function enqueue() + { + if (!$this->is_plugin_page()) { + return; + } - wp_localize_script('mepr-deactivation-survey', 'MeprDeactivationSurvey', array( - 'slug' => MEPR_PLUGIN_NAME, - 'pleaseSelectAnOption' => __('Please select an option', 'memberpress'), - 'siteUrl' => site_url(), - 'apiUrl' => 'https://hooks.zapier.com/hooks/catch/43914/otu86c9/silent/' - )); - } + wp_enqueue_style('mepr-deactivation-survey', MEPR_CSS_URL . '/admin-deactivation-survey.css', [], MEPR_VERSION); + wp_enqueue_script('mepr-deactivation-survey', MEPR_JS_URL . '/admin_deactivation_survey.js', ['jquery'], MEPR_VERSION, true); - public function popup() { - if(!$this->is_plugin_page()) { - return; + wp_localize_script('mepr-deactivation-survey', 'MeprDeactivationSurvey', [ + 'slug' => MEPR_PLUGIN_NAME, + 'pleaseSelectAnOption' => __('Please select an option', 'memberpress'), + 'siteUrl' => site_url(), + 'apiUrl' => 'https://hooks.zapier.com/hooks/catch/43914/otu86c9/silent/', + ]); } - $plugin = MEPR_PLUGIN_NAME; - - $options = array( - 1 => array( - 'label' => __('I no longer need the plugin', 'memberpress'), - ), - 2 => array( - 'label' => __('I\'m switching to a different plugin', 'memberpress'), - 'details' => __('Please share which plugin', 'memberpress'), - ), - 3 => array( - 'label' => __('I couldn\'t get the plugin to work', 'memberpress'), - ), - 4 => array( - 'label' => __('It\'s a temporary deactivation', 'memberpress'), - ), - 5 => array( - 'label' => __('Other', 'memberpress'), - 'details' => __('Please share the reason', 'memberpress'), - ) - ); + public function popup() + { + if (!$this->is_plugin_page()) { + return; + } - MeprView::render('/admin/popups/deactivation_survey', compact('plugin', 'options')); - } + $plugin = MEPR_PLUGIN_NAME; + + $options = [ + 1 => [ + 'label' => __('I no longer need the plugin', 'memberpress'), + ], + 2 => [ + 'label' => __('I\'m switching to a different plugin', 'memberpress'), + 'details' => __('Please share which plugin', 'memberpress'), + ], + 3 => [ + 'label' => __('I couldn\'t get the plugin to work', 'memberpress'), + ], + 4 => [ + 'label' => __('It\'s a temporary deactivation', 'memberpress'), + ], + 5 => [ + 'label' => __('Other', 'memberpress'), + 'details' => __('Please share the reason', 'memberpress'), + ], + ]; + + MeprView::render('/admin/popups/deactivation_survey', compact('plugin', 'options')); + } - protected function is_plugin_page() { - return in_array(MeprUtils::get_current_screen_id(), array('plugins', 'plugins-network')); - } + protected function is_plugin_page() + { + return in_array(MeprUtils::get_current_screen_id(), ['plugins', 'plugins-network']); + } } diff --git a/app/controllers/MeprDrmCtrl.php b/app/controllers/MeprDrmCtrl.php index 6b7ddf5..52cb463 100644 --- a/app/controllers/MeprDrmCtrl.php +++ b/app/controllers/MeprDrmCtrl.php @@ -1,349 +1,364 @@ dismiss_events( 'mepr-drm' ); - - // Undo DRM Fee - $drm_app_fee = new MeprDrmAppFee(); - $drm_app_fee->undo_app_fee(); - } - - public function drm_license_deactivated() { - wp_clear_scheduled_hook( 'mepr_drm_app_fee_mapper' ); - wp_clear_scheduled_hook( 'mepr_drm_app_fee_reversal' ); - wp_clear_scheduled_hook( 'mepr_drm_app_fee_revision' ); - - $drm_no_license = get_option( 'mepr_drm_no_license', false ); - - if ( ! $drm_no_license ) { - delete_option( 'mepr_drm_invalid_license' ); - - // set no license. - update_option( 'mepr_drm_no_license', true ); - - $drm = new MeprDrmNokey(); - $drm->create_event(); + +if (! defined('ABSPATH')) { + die('You are not allowed to call this page directly.'); +} + +class MeprDrmCtrl extends MeprBaseCtrl +{ + /** + * Load the hooks for this controller + */ + public function load_hooks() + { + add_action('mepr_license_activated', [$this, 'drm_license_activated']); + add_action('mepr_license_deactivated', [$this, 'drm_license_deactivated']); + add_action('mepr_license_expired', [$this, 'drm_license_invalid_expired']); + add_action('mepr_license_invalidated', [$this, 'drm_license_invalid_expired']); + add_action('mepr_drm_set_status_locked', [$this, 'drm_set_status_locked'], 10, 3); + add_action('wp_ajax_mepr_dismiss_notice_drm', [$this, 'drm_dismiss_notice']); + add_action('wp_ajax_mepr_dismiss_fee_notice_drm', [$this, 'drm_dismiss_fee_notice']); + add_action('wp_ajax_mepr_drm_activate_license', [$this, 'ajax_drm_activate_license']); + add_action('wp_ajax_mepr_drm_use_without_license', [$this, 'ajax_drm_use_without_license']); + add_action('admin_menu', [$this, 'drm_init'], 1); + add_action('admin_init', [$this, 'drm_throttle'], 20); + add_action('admin_footer', [$this, 'drm_menu_append_alert']); + add_filter('cron_schedules', [$this, 'drm_cron_schedules']); + add_action('mepr_drm_app_fee_mapper', [$this, 'drm_app_fee_mapper']); + add_action('mepr_drm_app_fee_reversal', [$this, 'drm_app_fee_reversal']); + add_action('mepr_drm_app_fee_revision', [$this, 'drm_app_fee_percentage_revision']); } - } - public function drm_license_invalid_expired() { - $drm_invalid_license = get_option( 'mepr_drm_invalid_license', false ); + public function drm_license_activated() + { + delete_option('mepr_drm_no_license'); + delete_option('mepr_drm_invalid_license'); + delete_option('mepr_drm_app_fee_notice_dimissed'); + wp_clear_scheduled_hook('mepr_drm_app_fee_mapper'); + wp_clear_scheduled_hook('mepr_drm_app_fee_reversal'); + wp_clear_scheduled_hook('mepr_drm_app_fee_revision'); + + // delete DRM notices + $notiications = new MeprNotifications(); + $notiications->dismiss_events('mepr-drm'); + + // Undo DRM Fee + $drm_app_fee = new MeprDrmAppFee(); + $drm_app_fee->undo_app_fee(); + } - if ( ! $drm_invalid_license ) { - delete_option( 'mepr_drm_no_license' ); + public function drm_license_deactivated() + { + wp_clear_scheduled_hook('mepr_drm_app_fee_mapper'); + wp_clear_scheduled_hook('mepr_drm_app_fee_reversal'); + wp_clear_scheduled_hook('mepr_drm_app_fee_revision'); - // set invalid license. - update_option( 'mepr_drm_invalid_license', true ); + $drm_no_license = get_option('mepr_drm_no_license', false); - $drm = new MeprDrmInvalid(); - $drm->create_event(); - } - } - - public static function drm_dismiss_notice() { - - if ( check_ajax_referer( 'mepr_dismiss_notice', false, false ) && isset( $_POST['notice'] ) && is_string( $_POST['notice'] ) ) { - $notice = sanitize_key( $_POST['notice'] ); - $secret = sanitize_key( $_POST['secret'] ); - $secret_parts = explode( '-', $secret ); - $notice_hash = $secret_parts[0]; - $event_hash = $secret_parts[1]; - $notice_key = MeprDrmHelper::prepare_dismissable_notice_key( $notice ); - - if ( $notice_hash == sha1( $notice ) ) { - $event = null; - if ( sha1( MeprDrmHelper::NO_LICENSE_EVENT ) == $event_hash ) { - $event = MeprEvent::latest( MeprDrmHelper::NO_LICENSE_EVENT ); - } elseif ( sha1( MeprDrmHelper::INVALID_LICENSE_EVENT ) == $event_hash ) { - $event = MeprEvent::latest( MeprDrmHelper::INVALID_LICENSE_EVENT ); - } + if (! $drm_no_license) { + delete_option('mepr_drm_invalid_license'); + + // set no license. + update_option('mepr_drm_no_license', true); - if ( $event && is_object( $event ) ) { - if ( $event->rec->id > 0 ) { - $event_data = MeprDrmHelper::parse_event_args( $event->args ); - $event_data[ $notice_key ] = time(); - $event->args = json_encode( $event_data ); - $event->store(); - } + $drm = new MeprDrmNokey(); + $drm->create_event(); } - } } - wp_send_json_success(); - } + public function drm_license_invalid_expired() + { + $drm_invalid_license = get_option('mepr_drm_invalid_license', false); + if (! $drm_invalid_license) { + delete_option('mepr_drm_no_license'); - public function drm_init() { + // set invalid license. + update_option('mepr_drm_invalid_license', true); - if ( MeprDrmHelper::is_valid() ) { - return; // bail. + $drm = new MeprDrmInvalid(); + $drm->create_event(); + } } - if ( MeprDrmHelper::is_app_fee_enabled() ) { - add_action( 'admin_notices', array( $this, 'app_fee_admin_notices' ), 20 ); - add_action( 'admin_footer', array( $this, 'app_fee_modal_footer' ), 99 ); - - $drm_app_fee = new MeprDrmAppFee(); - $drm_app_fee->init_crons(); + public static function drm_dismiss_notice() + { + + if (check_ajax_referer('mepr_dismiss_notice', false, false) && isset($_POST['notice']) && is_string($_POST['notice'])) { + $notice = sanitize_key($_POST['notice']); + $secret = sanitize_key($_POST['secret']); + $secret_parts = explode('-', $secret); + $notice_hash = $secret_parts[0]; + $event_hash = $secret_parts[1]; + $notice_key = MeprDrmHelper::prepare_dismissable_notice_key($notice); + + if ($notice_hash == sha1($notice)) { + $event = null; + if (sha1(MeprDrmHelper::NO_LICENSE_EVENT) == $event_hash) { + $event = MeprEvent::latest(MeprDrmHelper::NO_LICENSE_EVENT); + } elseif (sha1(MeprDrmHelper::INVALID_LICENSE_EVENT) == $event_hash) { + $event = MeprEvent::latest(MeprDrmHelper::INVALID_LICENSE_EVENT); + } + + if ($event && is_object($event)) { + if ($event->rec->id > 0) { + $event_data = MeprDrmHelper::parse_event_args($event->args); + $event_data[ $notice_key ] = time(); + $event->args = json_encode($event_data); + $event->store(); + } + } + } + } - return; // bail. + wp_send_json_success(); } - $drm_no_license = get_option( 'mepr_drm_no_license', false ); - $drm_invalid_license = get_option( 'mepr_drm_invalid_license', false ); - if ( $drm_no_license ) { - $drm = new MeprDrmNokey(); - $drm->run(); - } elseif ( $drm_invalid_license ) { - $drm = new MeprDrmInvalid(); - $drm->run(); - } - } + public function drm_init() + { - public function drm_throttle() { + if (MeprDrmHelper::is_valid()) { + return; // bail. + } - if ( wp_doing_ajax() ) { - return; - } + if (MeprDrmHelper::is_app_fee_enabled()) { + add_action('admin_notices', [$this, 'app_fee_admin_notices'], 20); + add_action('admin_footer', [$this, 'app_fee_modal_footer'], 99); - if ( MeprDrmHelper::is_locked() ) { + $drm_app_fee = new MeprDrmAppFee(); + $drm_app_fee->init_crons(); - if ( MeprDrmHelper::is_app_fee_enabled() ) { - return; // bail. - } + return; // bail. + } - $page = isset( $_GET['page'] ) ? $_GET['page'] : ''; // phpcs:ignore WordPress.Security.NonceVerification + $drm_no_license = get_option('mepr_drm_no_license', false); + $drm_invalid_license = get_option('mepr_drm_invalid_license', false); - if ( 'memberpress-members' === $page ) { + if ($drm_no_license) { + $drm = new MeprDrmNokey(); + $drm->run(); + } elseif ($drm_invalid_license) { + $drm = new MeprDrmInvalid(); + $drm->run(); + } + } - $action = isset( $_GET['action'] ) ? $_GET['action'] : ''; // phpcs:ignore WordPress.Security.NonceVerification + public function drm_throttle() + { - if ( 'new' == $action ) { - wp_die( __( 'Sorry, you are not allowed to access this page.', 'memberpress' ) ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + if (wp_doing_ajax()) { + return; } - if ( MeprUtils::is_post_request() && 'create' == $action ) { - wp_die( __( 'Sorry, you are not allowed to access this page.', 'memberpress' ) ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + if (MeprDrmHelper::is_locked()) { + if (MeprDrmHelper::is_app_fee_enabled()) { + return; // bail. + } + + $page = isset($_GET['page']) ? $_GET['page'] : ''; // phpcs:ignore WordPress.Security.NonceVerification + + if ('memberpress-members' === $page) { + $action = isset($_GET['action']) ? $_GET['action'] : ''; // phpcs:ignore WordPress.Security.NonceVerification + + if ('new' == $action) { + wp_die(__('Sorry, you are not allowed to access this page.', 'memberpress')); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + } + + if (MeprUtils::is_post_request() && 'create' == $action) { + wp_die(__('Sorry, you are not allowed to access this page.', 'memberpress')); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + } + } } - } } - } - public function ajax_drm_activate_license() { - if ( ! MeprUtils::is_post_request() || ! isset( $_POST['key'] ) || ! is_string( $_POST['key'] ) ) { - wp_send_json_error( sprintf( __( 'An error occurred during activation: %s', 'memberpress' ), __( 'Bad request.', 'memberpress' ) ) ); - } + public function ajax_drm_activate_license() + { + if (! MeprUtils::is_post_request() || ! isset($_POST['key']) || ! is_string($_POST['key'])) { + wp_send_json_error(sprintf(__('An error occurred during activation: %s', 'memberpress'), __('Bad request.', 'memberpress'))); + } - if ( ! MeprUtils::is_logged_in_and_an_admin() ) { - wp_send_json_error( __( 'Sorry, you don\'t have permission to do this.', 'memberpress' ) ); - } + if (! MeprUtils::is_logged_in_and_an_admin()) { + wp_send_json_error(__('Sorry, you don\'t have permission to do this.', 'memberpress')); + } - if ( ! check_ajax_referer( 'mepr_drm_activate_license', false, false ) ) { - wp_send_json_error( sprintf( __( 'An error occurred during activation: %s', 'memberpress' ), __( 'Security check failed.', 'memberpress' ) ) ); - } + if (! check_ajax_referer('mepr_drm_activate_license', false, false)) { + wp_send_json_error(sprintf(__('An error occurred during activation: %s', 'memberpress'), __('Security check failed.', 'memberpress'))); + } - $mepr_options = MeprOptions::fetch(); - $license_key = sanitize_text_field( wp_unslash( $_POST['key'] ) ); + $mepr_options = MeprOptions::fetch(); + $license_key = sanitize_text_field(wp_unslash($_POST['key'])); - try { - $act = MeprUpdateCtrl::activate_license( $license_key ); + try { + $act = MeprUpdateCtrl::activate_license($license_key); - $output = esc_html( $act['message'] ); + $output = esc_html($act['message']); - wp_send_json_success( $output ); - } catch ( Exception $e ) { - wp_send_json_error( $e->getMessage() ); + wp_send_json_success($output); + } catch (Exception $e) { + wp_send_json_error($e->getMessage()); + } } - } - public function drm_menu_append_alert() { + public function drm_menu_append_alert() + { - if ( ! MeprDrmHelper::is_locked() ) { - return; - } + if (! MeprDrmHelper::is_locked()) { + return; + } - ob_start(); - ?> + ob_start(); + ?> - + - + - admin_url( 'admin.php?page=memberpress-members' )) ); - return; - } + // is it already enabled? + if (MeprDrmHelper::is_app_fee_enabled()) { + wp_send_json_success(['redirect_to' => admin_url('admin.php?page=memberpress-members')]); + return; + } - if( true !== MeprHooks::apply_filters('mepr_do_app_fee', true) ) { - wp_send_json_error( __( 'Not allowed.', 'memberpress' ) ); - } + if (true !== MeprHooks::apply_filters('mepr_do_app_fee', true)) { + wp_send_json_error(__('Not allowed.', 'memberpress')); + } - $drm_app_fee = new MeprDrmAppFee(); - $drm_app_fee->do_app_fee(); + $drm_app_fee = new MeprDrmAppFee(); + $drm_app_fee->do_app_fee(); - wp_send_json_success( array('redirect_to' => admin_url( 'admin.php?page=memberpress-members' )) ); - } catch ( Exception $e ) { - wp_send_json_error( $e->getMessage() ); + wp_send_json_success(['redirect_to' => admin_url('admin.php?page=memberpress-members')]); + } catch (Exception $e) { + wp_send_json_error($e->getMessage()); + } } - } - public function drm_set_status_locked( $status, $days, $event_name ){ + public function drm_set_status_locked($status, $days, $event_name) + { - if( ! MeprDrmHelper::is_locked($status) ) { - return; // bail. - } + if (! MeprDrmHelper::is_locked($status)) { + return; // bail. + } - if( ! MeprStripeGateway::has_method_with_connect_status() ) { - return; - } + if (! MeprStripeGateway::has_method_with_connect_status()) { + return; + } - $drm_app_fee = new MeprDrmAppFee(); - $drm_app_fee->do_app_fee(); + $drm_app_fee = new MeprDrmAppFee(); + $drm_app_fee->do_app_fee(); - if ( ! wp_doing_ajax() ) { - wp_safe_redirect( admin_url( 'admin.php?page=memberpress-members' ) ); - exit; + if (! wp_doing_ajax()) { + wp_safe_redirect(admin_url('admin.php?page=memberpress-members')); + exit; + } } - } - - public function app_fee_admin_notices() { + public function app_fee_admin_notices() + { - if ( ! MeprDrmHelper::is_app_fee_enabled() ) { - return; - } + if (! MeprDrmHelper::is_app_fee_enabled()) { + return; + } - $is_dismissed = (bool) MeprDrmHelper::is_app_fee_notice_dismissed(); - if ( false === $is_dismissed ) { - echo''; - MeprView::render( '/admin/drm/notices/fee_notice', get_defined_vars() ); + $is_dismissed = (bool) MeprDrmHelper::is_app_fee_notice_dismissed(); + if (false === $is_dismissed) { + echo''; + MeprView::render('/admin/drm/notices/fee_notice', get_defined_vars()); + } } - } + public static function drm_dismiss_fee_notice() + { - public static function drm_dismiss_fee_notice() { + if (check_ajax_referer('mepr_dismiss_notice', false, false)) { + MeprDrmHelper::dismiss_app_fee_notice(); + } - if ( check_ajax_referer( 'mepr_dismiss_notice', false, false ) ) { - MeprDrmHelper::dismiss_app_fee_notice(); + wp_send_json_success(); } - wp_send_json_success(); - } + public function app_fee_modal_footer() + { + MeprView::render('/admin/drm/modal_fee'); + } - public function app_fee_modal_footer() { - MeprView::render( '/admin/drm/modal_fee' ); - } + public function drm_cron_schedules($array) + { - public function drm_cron_schedules( $array ) { + $array['mepr_drm_ten_minutes'] = [ + 'interval' => 300, + 'display' => __('Every 10 minutes', 'memberpress'), + ]; - $array['mepr_drm_ten_minutes'] = array( - 'interval' => 300, - 'display' => __( 'Every 10 minutes', 'memberpress' ), - ); + return $array; + } - return $array; - } + public function drm_app_fee_mapper() + { + if (! MeprDrmHelper::is_app_fee_enabled()) { + return; // bail. + } - public function drm_app_fee_mapper() { - if( ! MeprDrmHelper::is_app_fee_enabled() ) { - return; // bail. - } + $api_version = MeprDrmHelper::get_drm_app_fee_version(); + $current_percentage = MeprDrmHelper::get_application_fee_percentage(); + $meprdrm = new MeprDrmAppFee(); - $api_version = MeprDrmHelper::get_drm_app_fee_version(); - $current_percentage = MeprDrmHelper::get_application_fee_percentage(); - $meprdrm = new MeprDrmAppFee(); + // --Add fee-- + $subscriptions = $meprdrm->get_all_active_subs(['mepr_app_fee_not_applied' => true]); + $meprdrm->process_subscriptions_fee($subscriptions, $api_version, $current_percentage); - // --Add fee-- - $subscriptions = $meprdrm->get_all_active_subs(array('mepr_app_fee_not_applied' => true)); - $meprdrm->process_subscriptions_fee($subscriptions, $api_version, $current_percentage); + // --Update fee-- + $subscriptions = $meprdrm->get_all_active_subs(['mepr_app_not_fee_version' => $api_version]); + $meprdrm->process_subscriptions_fee($subscriptions, $api_version, $current_percentage); + } - // --Update fee-- - $subscriptions = $meprdrm->get_all_active_subs(array('mepr_app_not_fee_version' => $api_version)); - $meprdrm->process_subscriptions_fee($subscriptions, $api_version, $current_percentage); - } + public function drm_app_fee_reversal() + { - public function drm_app_fee_reversal() { + if (! MeprDrmHelper::is_valid()) { + return; // bail. + } - if( ! MeprDrmHelper::is_valid() ) { - return; // bail. + $meprdrm = new MeprDrmAppFee(); + $api_version = MeprDrmHelper::get_drm_app_fee_version(); + $current_percentage = MeprDrmHelper::get_application_fee_percentage(); + $subscriptions = $meprdrm->get_all_active_subs(['mepr_app_fee_applied' => MeprDrmHelper::get_drm_app_fee_version()]); + $meprdrm->process_subscriptions_fee($subscriptions, $api_version, 0, true); } - $meprdrm = new MeprDrmAppFee(); - $api_version = MeprDrmHelper::get_drm_app_fee_version(); - $current_percentage = MeprDrmHelper::get_application_fee_percentage(); - $subscriptions = $meprdrm->get_all_active_subs(array('mepr_app_fee_applied' => MeprDrmHelper::get_drm_app_fee_version())); - $meprdrm->process_subscriptions_fee($subscriptions, $api_version, 0, true); - } - - public function drm_app_fee_percentage_revision() { - $current_version = MeprDrmHelper::get_drm_app_fee_version(); - $current_percentage = MeprDrmHelper::get_application_fee_percentage(); - MeprDrmHelper::get_application_fee_percentage(true); - } + public function drm_app_fee_percentage_revision() + { + $current_version = MeprDrmHelper::get_drm_app_fee_version(); + $current_percentage = MeprDrmHelper::get_application_fee_percentage(); + MeprDrmHelper::get_application_fee_percentage(true); + } } diff --git a/app/controllers/MeprEmailsCtrl.php b/app/controllers/MeprEmailsCtrl.php index 653c0ff..09c0eca 100644 --- a/app/controllers/MeprEmailsCtrl.php +++ b/app/controllers/MeprEmailsCtrl.php @@ -1,139 +1,164 @@ $e->getMessage()))); - } + public static function set_email_defaults() + { + check_ajax_referer('set_email_defaults', 'set_email_defaults_nonce'); - die(json_encode(array('subject' => $email->default_subject(), 'body' => $email->default_body()))); - } + if (!MeprUtils::is_mepr_admin()) { + die(__('You do not have access.', 'memberpress')); + } - public static function send_test_email() { - check_ajax_referer('send_test_email', 'send_test_email_nonce'); + if (!isset($_POST['e'])) { + die(__('Email couldn\'t be set to default', 'memberpress')); + } - $mepr_options = MeprOptions::fetch(); + if (!isset($_POST['a'])) { + $_POST['a'] = []; + } - if(!MeprUtils::is_mepr_admin()) { - die(__('You do not have access to send a test email.', 'memberpress')); - } + try { + $email = MeprEmailFactory::fetch(wp_unslash($_POST['e']), 'MeprBaseEmail', $_POST['a']); + } catch (Exception $e) { + die(json_encode(['error' => $e->getMessage()])); + } - if(!isset($_POST['e']) or !isset($_POST['s']) or !isset($_POST['b']) or !isset($_POST['t'])) { - die(__('Can\'t send your email ... refresh the page and try it again.', 'memberpress')); + die(json_encode([ + 'subject' => $email->default_subject(), + 'body' => $email->default_body(), + ])); } - if(!isset($_POST['a'])) { $_POST['a']=array(); } - - try { - $email = MeprEmailFactory::fetch( wp_unslash( $_POST['e'] ), 'MeprBaseEmail', $_POST['a'] ); - } - catch( Exception $e ) { - die(json_encode(array('error' => $e->getMessage()))); + public static function send_test_email() + { + check_ajax_referer('send_test_email', 'send_test_email_nonce'); + + $mepr_options = MeprOptions::fetch(); + + if (!MeprUtils::is_mepr_admin()) { + die(__('You do not have access to send a test email.', 'memberpress')); + } + + if (!isset($_POST['e']) or !isset($_POST['s']) or !isset($_POST['b']) or !isset($_POST['t'])) { + die(__('Can\'t send your email ... refresh the page and try it again.', 'memberpress')); + } + + if (!isset($_POST['a'])) { + $_POST['a'] = []; + } + + try { + $email = MeprEmailFactory::fetch(wp_unslash($_POST['e']), 'MeprBaseEmail', $_POST['a']); + } catch (Exception $e) { + die(json_encode(['error' => $e->getMessage()])); + } + + $email->to = $mepr_options->admin_email_addresses; + + $amount = preg_replace( + '~\$~', + '\\\$', + sprintf( + '%s' . MeprUtils::format_float(15.15), + stripslashes($mepr_options->currency_symbol) + ) + ); + $subtotal = preg_replace( + '~\$~', + '\\\$', + sprintf( + '%s' . MeprUtils::format_float(15.00), + stripslashes($mepr_options->currency_symbol) + ) + ); + $tax_amount = preg_replace( + '~\$~', + '\\\$', + sprintf( + '%s' . MeprUtils::format_float(0.15), + stripslashes($mepr_options->currency_symbol) + ) + ); + + $params = array_merge( + [ + 'user_id' => 481, + 'user_login' => 'johndoe', + 'username' => 'johndoe', + 'user_email' => 'johndoe@example.com', + 'user_first_name' => __('John', 'memberpress'), + 'user_last_name' => __('Doe', 'memberpress'), + 'user_full_name' => __('John Doe', 'memberpress'), + 'user_address' => '
' . + __('111 Cool Avenue', 'memberpress') . '
' . + __('New York, NY 10005', 'memberpress') . '
' . + __('United States', 'memberpress') . '
', + 'usermeta:(.*)' => __('User Meta Field: $1', 'memberpress'), + 'membership_type' => __('Bronze Edition', 'memberpress'), + 'signup_url' => home_url(), + 'product_name' => __('Bronze Edition', 'memberpress'), + 'invoice_num' => 718, + 'trans_num' => '9i8h7g6f5e', + 'trans_date' => MeprAppHelper::format_date(gmdate('c', time())), + 'trans_expires_at' => MeprAppHelper::format_date(gmdate('c', time() + MeprUtils::months(1))), + 'trans_gateway' => __('Credit Card (Stripe)', 'memberpress'), + 'user_remote_addr' => $_SERVER['REMOTE_ADDR'], + 'payment_amount' => $amount, + 'subscr_num' => '1a2b3c4d5e', + 'subscr_date' => MeprAppHelper::format_date(gmdate('c', time())), + 'subscr_next_billing_at' => MeprAppHelper::format_date(gmdate('c', time() + MeprUtils::months(1))), + 'subscr_expires_at' => MeprAppHelper::format_date(gmdate('c', time() + MeprUtils::months(1))), + 'subscr_gateway' => __('Credit Card (Stripe)', 'memberpress'), + 'subscr_terms' => sprintf(__('%s / month', 'memberpress'), $amount), + 'subscr_cc_num' => MeprUtils::cc_num('6710'), + 'subscr_cc_month_exp' => gmdate('m'), + 'subscr_cc_year_exp' => (gmdate('Y') + 2), + 'subscr_update_url' => $mepr_options->login_page_url(), + 'subscr_upgrade_url' => $mepr_options->login_page_url(), + 'subscr_renew_url' => $mepr_options->account_page_url() . '?action=subscriptions', + 'subscr_trial_end_date' => MeprAppHelper::format_date(gmdate('c', time() + MeprUtils::months(1))), + 'subscr_next_billing_amount' => $amount, + 'reminder_id' => 28, + 'reminder_trigger_length' => 2, + 'reminder_trigger_interval' => 'days', + 'reminder_trigger_timing' => 'before', + 'reminder_trigger_event' => 'sub-expires', + 'reminder_name' => __('Subscription Expiring', 'memberpress'), + 'reminder_description' => __('Subscription Expiring in 2 Days', 'memberpress'), + 'blog_name' => MeprUtils::blogname(), + 'payment_subtotal' => $subtotal, + 'tax_rate' => '10%', + 'tax_amount' => $tax_amount, + 'tax_desc' => __('Tax', 'memberpress'), + 'business_name' => $mepr_options->attr('biz_name'), + 'biz_name' => $mepr_options->attr('biz_name'), + 'biz_address1' => $mepr_options->attr('biz_address1'), + 'biz_address2' => $mepr_options->attr('biz_address2'), + 'biz_city' => $mepr_options->attr('biz_city'), + 'biz_state' => $mepr_options->attr('biz_state'), + 'biz_postcode' => $mepr_options->attr('biz_postcode'), + 'biz_country' => $mepr_options->attr('biz_country'), + 'login_page' => $mepr_options->login_page_url(), + 'account_url' => $mepr_options->account_page_url(), + 'login_url' => $mepr_options->login_page_url(), + ], + $email->test_vars + ); + + $use_template = ( $_POST['t'] == 'true' ); + $email->send($params, sanitize_text_field(wp_unslash($_POST['s'])), wp_kses_post(wp_unslash($_POST['b'])), $use_template); + + die(json_encode(['message' => __('Your test email was successfully sent.', 'memberpress')])); } - - $email->to = $mepr_options->admin_email_addresses; - - $amount = preg_replace( '~\$~', '\\\$', - sprintf( '%s'.MeprUtils::format_float(15.15), - stripslashes( $mepr_options->currency_symbol ) ) ); - $subtotal = preg_replace( '~\$~', '\\\$', - sprintf( '%s'.MeprUtils::format_float(15.00), - stripslashes( $mepr_options->currency_symbol ) ) ); - $tax_amount = preg_replace( '~\$~', '\\\$', - sprintf( '%s'.MeprUtils::format_float(0.15), - stripslashes( $mepr_options->currency_symbol ) ) ); - - $params = array_merge( - array( - 'user_id' => 481, - 'user_login' => 'johndoe', - 'username' => 'johndoe', - 'user_email' => 'johndoe@example.com', - 'user_first_name' => __('John', 'memberpress'), - 'user_last_name' => __('Doe', 'memberpress'), - 'user_full_name' => __('John Doe', 'memberpress'), - 'user_address' => '
' . - __('111 Cool Avenue', 'memberpress') .'
' . - __('New York, NY 10005', 'memberpress') . '
' . - __('United States', 'memberpress') . '
', - 'usermeta:(.*)' => __('User Meta Field: $1', 'memberpress'), - 'membership_type' => __('Bronze Edition', 'memberpress'), - 'signup_url' => home_url(), - 'product_name' => __('Bronze Edition', 'memberpress'), - 'invoice_num' => 718, - 'trans_num' => '9i8h7g6f5e', - 'trans_date' => MeprAppHelper::format_date(gmdate('c', time())), - 'trans_expires_at' => MeprAppHelper::format_date(gmdate('c', time() + MeprUtils::months(1))), - 'trans_gateway' => __("Credit Card (Stripe)", 'memberpress'), - 'user_remote_addr' => $_SERVER['REMOTE_ADDR'], - 'payment_amount' => $amount, - 'subscr_num' => '1a2b3c4d5e', - 'subscr_date' => MeprAppHelper::format_date(gmdate('c', time())), - 'subscr_next_billing_at' => MeprAppHelper::format_date(gmdate('c', time() + MeprUtils::months(1))), - 'subscr_expires_at' => MeprAppHelper::format_date(gmdate('c', time() + MeprUtils::months(1))), - 'subscr_gateway' => __("Credit Card (Stripe)", 'memberpress'), - 'subscr_terms' => sprintf(__("%s / month", 'memberpress'), $amount), - 'subscr_cc_num' => MeprUtils::cc_num('6710'), - 'subscr_cc_month_exp' => gmdate('m'), - 'subscr_cc_year_exp' => (gmdate('Y') + 2), - 'subscr_update_url' => $mepr_options->login_page_url(), - 'subscr_upgrade_url' => $mepr_options->login_page_url(), - 'subscr_renew_url' => $mepr_options->account_page_url() . '?action=subscriptions', - 'subscr_trial_end_date' => MeprAppHelper::format_date(gmdate('c', time() + MeprUtils::months(1))), - 'subscr_next_billing_amount' => $amount, - 'reminder_id' => 28, - 'reminder_trigger_length' => 2, - 'reminder_trigger_interval' => 'days', - 'reminder_trigger_timing' => 'before', - 'reminder_trigger_event' => 'sub-expires', - 'reminder_name' => __('Subscription Expiring', 'memberpress'), - 'reminder_description' => __('Subscription Expiring in 2 Days', 'memberpress'), - 'blog_name' => MeprUtils::blogname(), - 'payment_subtotal' => $subtotal, - 'tax_rate' => '10%', - 'tax_amount' => $tax_amount, - 'tax_desc' => __('Tax', 'memberpress'), - 'business_name' => $mepr_options->attr('biz_name'), - 'biz_name' => $mepr_options->attr('biz_name'), - 'biz_address1' => $mepr_options->attr('biz_address1'), - 'biz_address2' => $mepr_options->attr('biz_address2'), - 'biz_city' => $mepr_options->attr('biz_city'), - 'biz_state' => $mepr_options->attr('biz_state'), - 'biz_postcode' => $mepr_options->attr('biz_postcode'), - 'biz_country' => $mepr_options->attr('biz_country'), - 'login_page' => $mepr_options->login_page_url(), - 'account_url' => $mepr_options->account_page_url(), - 'login_url' => $mepr_options->login_page_url() - ), - $email->test_vars - ); - - $use_template = ( $_POST['t']=='true' ); - $email->send($params, sanitize_text_field(wp_unslash($_POST['s'])), wp_kses_post(wp_unslash($_POST['b'])), $use_template); - - die(json_encode(array('message' => __('Your test email was successfully sent.', 'memberpress')))); - } } //End class diff --git a/app/controllers/MeprEventsCtrl.php b/app/controllers/MeprEventsCtrl.php index 6ef9c83..6b0e0ff 100644 --- a/app/controllers/MeprEventsCtrl.php +++ b/app/controllers/MeprEventsCtrl.php @@ -1,47 +1,62 @@ -subscription_id > 0 && - ($sub = $txn->subscription()) && - $sub->status == MeprSubscription::$cancelled_str && - $sub->is_expired() ) { - MeprEvent::record('subscription-expired', $sub, $txn); + public function delete_user($user_id) + { + if (!empty($user_id)) { + // Since the 'delete_user' action fires just before the user is deleted + // we should still have access to the full MeprUser object for them + MeprEvent::record('member-deleted', (new MeprUser($user_id))); + } } - } - public function sub_created($sub_id) { - $sub = new MeprSubscription($sub_id); - MeprEvent::record('subscription-created', $sub); - return $sub_id; - } + /** + * Let's figure some stuff out from the txn-expired hook yo ... and send some proper events + */ + public function txn_expired($txn, $sub_status) + { + // Assume the txn is expired (otherwise this action wouldn't fire) + // Then ensure the subscription is expired before sending a sub expired event + if ( + !empty($txn) && + $txn instanceof MeprTransaction && + (int)$txn->subscription_id > 0 && + ($sub = $txn->subscription()) && + $sub->status == MeprSubscription::$cancelled_str && + $sub->is_expired() + ) { + MeprEvent::record('subscription-expired', $sub, $txn); + } + } + + public function sub_created($sub_id) + { + $sub = new MeprSubscription($sub_id); + MeprEvent::record('subscription-created', $sub); + return $sub_id; + } } //End class diff --git a/app/controllers/MeprExportCtrl.php b/app/controllers/MeprExportCtrl.php index 4a17936..d395a10 100644 --- a/app/controllers/MeprExportCtrl.php +++ b/app/controllers/MeprExportCtrl.php @@ -1,105 +1,115 @@ MEPR_PLUGIN_NAME, - 'callback' => array('MeprExportCtrl', 'export_pii'), - ); - - return $exporters; - } - - /** - * Gathers PII data stored by MemberPress for export - * @param string $email Email address of requested user to export - * @return array ['data' => array, 'done' => bool] - */ - public static function export_pii($email, $page = 1) { - $user = get_user_by('email', $email); - $mepr_user = new MeprUser($user->ID); - $mepr_data = array(); - - // Export address fields - if($mepr_user->address_is_set()) { - $address = $mepr_user->formatted_address(); - $mepr_data[] = array( - 'group_id' => MEPR_PLUGIN_NAME, - 'group_label' => __('Membership Data', 'memberpress'), - 'item_id' => 'mepr-user-data', - 'data' => array( - array( - 'name' => __('Address', 'memberpress'), - 'value' => $address, - ), - ), - ); - } - // Export VAT data - $vat_number = get_user_meta($user->ID, 'mepr_vat_number', true); - if(!empty($vat_number)) { - $mepr_data[] = array( - 'group_id' => MEPR_PLUGIN_NAME, - 'group_label' => __('Membership Data', 'memberpress'), - 'item_id' => 'mepr-user-data', - 'data' => array( - array( - 'name' => __('VAT Number', 'memberpress'), - 'value' => $vat_number, - ), - ), - ); +if (!defined('ABSPATH')) { + die('You are not allowed to call this page directly.'); +} + +class MeprExportCtrl extends MeprBaseCtrl +{ + public function load_hooks() + { + add_action('init', 'MeprExportCtrl::export_users_csv'); + add_action('init', 'MeprExportCtrl::export_inactive_users_csv'); + add_filter('wp_privacy_personal_data_exporters', [$this, 'register_pii_exporter'], 10); } - // Export Geo located country - $geo_country = get_user_meta($user->ID, 'mepr-geo-country', true); - if(!empty($geo_country)) { - $mepr_data[] = array( - 'group_id' => MEPR_PLUGIN_NAME, - 'group_label' => __('Membership Data', 'memberpress'), - 'item_id' => 'mepr-user-data', - 'data' => array( - array( - 'name' => __('Geo Location Country', 'memberpress'), - 'value' => $geo_country, - ), - ), - ); + /** + * Registers callback for PII exporter + * + * @param array $exporters + * @return array $exporters + */ + public static function register_pii_exporter($exporters) + { + $exporters[MEPR_PLUGIN_SLUG] = [ + 'exporter_friendly_name' => MEPR_PLUGIN_NAME, + 'callback' => ['MeprExportCtrl', 'export_pii'], + ]; + + return $exporters; } - return array( - 'data' => $mepr_data, - 'done' => true, - ); - } - - // This will need a major overhaul eventually, but we needed a quick fix for some clients - // So this is the quick fix. It can only be accessed via - // /wp-admin/admin.php?page=memberpress-options&mepr-export-users-csv currently. - // If we change this, we definitely need to let the folks at theballstonjournal.com know - public static function export_users_csv() { - global $wpdb; - $mepr_db = new MeprDb(); - - if(!MeprUtils::is_mepr_admin()) { //Make sure we're an admin - return; + /** + * Gathers PII data stored by MemberPress for export + * + * @param string $email Email address of requested user to export + * @return array ['data' => array, 'done' => bool] + */ + public static function export_pii($email, $page = 1) + { + $user = get_user_by('email', $email); + $mepr_user = new MeprUser($user->ID); + $mepr_data = []; + + // Export address fields + if ($mepr_user->address_is_set()) { + $address = $mepr_user->formatted_address(); + $mepr_data[] = [ + 'group_id' => MEPR_PLUGIN_NAME, + 'group_label' => __('Membership Data', 'memberpress'), + 'item_id' => 'mepr-user-data', + 'data' => [ + [ + 'name' => __('Address', 'memberpress'), + 'value' => $address, + ], + ], + ]; + } + + // Export VAT data + $vat_number = get_user_meta($user->ID, 'mepr_vat_number', true); + if (!empty($vat_number)) { + $mepr_data[] = [ + 'group_id' => MEPR_PLUGIN_NAME, + 'group_label' => __('Membership Data', 'memberpress'), + 'item_id' => 'mepr-user-data', + 'data' => [ + [ + 'name' => __('VAT Number', 'memberpress'), + 'value' => $vat_number, + ], + ], + ]; + } + + // Export Geo located country + $geo_country = get_user_meta($user->ID, 'mepr-geo-country', true); + if (!empty($geo_country)) { + $mepr_data[] = [ + 'group_id' => MEPR_PLUGIN_NAME, + 'group_label' => __('Membership Data', 'memberpress'), + 'item_id' => 'mepr-user-data', + 'data' => [ + [ + 'name' => __('Geo Location Country', 'memberpress'), + 'value' => $geo_country, + ], + ], + ]; + } + + return [ + 'data' => $mepr_data, + 'done' => true, + ]; } - if(isset($_GET['page']) && $_GET['page'] == 'memberpress-options' && isset($_GET['mepr-export-users-csv'])) { - $q = "SELECT u.ID AS user_ID, u.user_login AS username, u.user_email AS email, f.meta_value AS first_name, l.meta_value AS last_name, a1.meta_value AS address1, a2.meta_value AS address2, c.meta_value AS city, s.meta_value AS state, z.meta_value AS zip, u.user_registered AS start_date, t.expires_at AS end_date, p.post_title AS membership, t.gateway, cp.post_title AS coupon + // This will need a major overhaul eventually, but we needed a quick fix for some clients + // So this is the quick fix. It can only be accessed via + // /wp-admin/admin.php?page=memberpress-options&mepr-export-users-csv currently. + // If we change this, we definitely need to let the folks at theballstonjournal.com know + public static function export_users_csv() + { + global $wpdb; + $mepr_db = new MeprDb(); + + if (!MeprUtils::is_mepr_admin()) { // Make sure we're an admin + return; + } + + if (isset($_GET['page']) && $_GET['page'] == 'memberpress-options' && isset($_GET['mepr-export-users-csv'])) { + $q = "SELECT u.ID AS user_ID, u.user_login AS username, u.user_email AS email, f.meta_value AS first_name, l.meta_value AS last_name, a1.meta_value AS address1, a2.meta_value AS address2, c.meta_value AS city, s.meta_value AS state, z.meta_value AS zip, u.user_registered AS start_date, t.expires_at AS end_date, p.post_title AS membership, t.gateway, cp.post_title AS coupon FROM {$wpdb->users} AS u LEFT JOIN {$wpdb->usermeta} AS f ON u.ID = f.user_id AND f.meta_key = 'first_name' @@ -116,67 +126,70 @@ public static function export_users_csv() { LEFT JOIN {$wpdb->usermeta} AS z ON u.ID = z.user_id AND z.meta_key = 'mepr-address-zip' INNER JOIN {$mepr_db->transactions} AS t - ON u.ID = t.user_id AND (t.status = 'complete' OR t.status = 'confirmed') AND (t.expires_at IS NULL OR t.expires_at = 0 OR t.expires_at >= '".date('c')."') + ON u.ID = t.user_id AND (t.status = 'complete' OR t.status = 'confirmed') AND (t.expires_at IS NULL OR t.expires_at = 0 OR t.expires_at >= '" . date('c') . "') LEFT JOIN {$wpdb->posts} AS p ON t.product_id = p.ID LEFT JOIN {$wpdb->posts} AS cp ON t.coupon_id = cp.ID"; - // output headers so that the file is downloaded rather than displayed - header('Content-Type: text/csv; charset=utf-8'); - header('Content-Disposition: attachment; filename=active_customers.csv'); - - // create a file pointer connected to the output stream - $output = fopen('php://output', 'w'); - - // output the column headings - fputcsv($output, array( 'User_ID', - 'Username', - 'Email', - 'First Name', - 'Last Name', - 'Address 1', - 'Address 2', - 'City', - 'State', - 'Zip', - 'Start Date', - 'End Date', - 'Subscription', - 'Gateway', - 'Coupon' )); - - // fetch the data - $wpdb->query("SET SQL_BIG_SELECTS=1"); - $rows = $wpdb->get_results($q, ARRAY_A); - - // loop over the rows, outputting them - foreach($rows as $row) { - fputcsv($output, $row); - } - - // close the file and exit - fclose($output); - exit; - } - } - - // This will need a major overhaul eventually, but we needed a quick fix for some clients - // So this is the quick fix. It can only be accessed via - // /wp-admin/admin.php?page=memberpress-options&mepr-export-inactive-users-csv currently. - // If we change this, we definitely need to let the folks at theballstonjournal.com know - public static function export_inactive_users_csv() { - global $wpdb; - $mepr_db = new MeprDb(); - - if(!MeprUtils::is_mepr_admin()) { //Make sure we're an admin - return; + // output headers so that the file is downloaded rather than displayed + header('Content-Type: text/csv; charset=utf-8'); + header('Content-Disposition: attachment; filename=active_customers.csv'); + + // create a file pointer connected to the output stream + $output = fopen('php://output', 'w'); + + // output the column headings + fputcsv($output, [ + 'User_ID', + 'Username', + 'Email', + 'First Name', + 'Last Name', + 'Address 1', + 'Address 2', + 'City', + 'State', + 'Zip', + 'Start Date', + 'End Date', + 'Subscription', + 'Gateway', + 'Coupon', + ]); + + // fetch the data + $wpdb->query('SET SQL_BIG_SELECTS=1'); + $rows = $wpdb->get_results($q, ARRAY_A); + + // loop over the rows, outputting them + foreach ($rows as $row) { + fputcsv($output, $row); + } + + // close the file and exit + fclose($output); + exit; + } } - if(isset($_GET['page']) && $_GET['page'] == 'memberpress-options' && isset($_GET['mepr-export-inactive-users-csv'])) { - $db_now = $wpdb->prepare('%s',MeprUtils::db_now()); - $db_lifetime = $wpdb->prepare('%s',MeprUtils::db_lifetime()); - $q = "SELECT u.ID AS user_ID, u.user_login AS username, u.user_email AS email, f.meta_value AS first_name, l.meta_value AS last_name, a1.meta_value AS address1, a2.meta_value AS address2, c.meta_value AS city, s.meta_value AS state, z.meta_value AS zip, u.user_registered AS start_date, t.expires_at AS end_date, p.post_title AS membership, t.gateway, cp.post_title AS coupon + // This will need a major overhaul eventually, but we needed a quick fix for some clients + // So this is the quick fix. It can only be accessed via + // /wp-admin/admin.php?page=memberpress-options&mepr-export-inactive-users-csv currently. + // If we change this, we definitely need to let the folks at theballstonjournal.com know + public static function export_inactive_users_csv() + { + global $wpdb; + $mepr_db = new MeprDb(); + + if (!MeprUtils::is_mepr_admin()) { // Make sure we're an admin + return; + } + + if (isset($_GET['page']) && $_GET['page'] == 'memberpress-options' && isset($_GET['mepr-export-inactive-users-csv'])) { + $db_now = $wpdb->prepare('%s', MeprUtils::db_now()); + $db_lifetime = $wpdb->prepare('%s', MeprUtils::db_lifetime()); + $q = "SELECT u.ID AS user_ID, u.user_login AS username, u.user_email AS email, f.meta_value AS first_name, l.meta_value AS last_name, a1.meta_value AS address1, a2.meta_value AS address2, c.meta_value AS city, s.meta_value AS state, z.meta_value AS zip, u.user_registered AS start_date, t.expires_at AS end_date, p.post_title AS membership, t.gateway, cp.post_title AS coupon FROM {$wpdb->users} AS u LEFT JOIN {$wpdb->usermeta} AS f ON u.ID = f.user_id AND f.meta_key = 'first_name' @@ -208,41 +221,44 @@ public static function export_inactive_users_csv() { ON t.coupon_id = cp.ID GROUP BY u.ID"; - // output headers so that the file is downloaded rather than displayed - header('Content-Type: text/csv; charset=utf-8'); - header('Content-Disposition: attachment; filename=inactive_customers.csv'); - - // create a file pointer connected to the output stream - $output = fopen('php://output', 'w'); - - // output the column headings - fputcsv($output, array( 'User_ID', - 'Username', - 'Email', - 'First Name', - 'Last Name', - 'Address 1', - 'Address 2', - 'City', - 'State', - 'Zip', - 'Start Date', - 'End Date', - 'Subscription', - 'Gateway', - 'Coupon' ) ); - - // fetch the data - $wpdb->query("SET SQL_BIG_SELECTS=1"); - $rows = $wpdb->get_results($q, ARRAY_A); - - // loop over the rows, outputting them - foreach($rows as $row) - fputcsv($output, $row); - - // close the file and exit - fclose($output); - exit; + // output headers so that the file is downloaded rather than displayed + header('Content-Type: text/csv; charset=utf-8'); + header('Content-Disposition: attachment; filename=inactive_customers.csv'); + + // create a file pointer connected to the output stream + $output = fopen('php://output', 'w'); + + // output the column headings + fputcsv($output, [ + 'User_ID', + 'Username', + 'Email', + 'First Name', + 'Last Name', + 'Address 1', + 'Address 2', + 'City', + 'State', + 'Zip', + 'Start Date', + 'End Date', + 'Subscription', + 'Gateway', + 'Coupon', + ]); + + // fetch the data + $wpdb->query('SET SQL_BIG_SELECTS=1'); + $rows = $wpdb->get_results($q, ARRAY_A); + + // loop over the rows, outputting them + foreach ($rows as $row) { + fputcsv($output, $row); + } + + // close the file and exit + fclose($output); + exit; + } } - } } //End class diff --git a/app/controllers/MeprGroupsCtrl.php b/app/controllers/MeprGroupsCtrl.php index d687f0b..daa0f4a 100644 --- a/app/controllers/MeprGroupsCtrl.php +++ b/app/controllers/MeprGroupsCtrl.php @@ -1,250 +1,271 @@ cpt = (object)array( - 'slug' => MeprGroup::$cpt, - 'config' => array( - 'labels' => array( - 'name' => __('Groups', 'memberpress'), - 'singular_name' => __('Group', 'memberpress'), - 'add_new' => __('Add New', 'memberpress'), - 'add_new_item' => __('Add New Group', 'memberpress'), - 'edit_item' => __('Edit Group', 'memberpress'), - 'new_item' => __('New Group', 'memberpress'), - 'view_item' => __('View Group', 'memberpress'), - 'search_items' => __('Search Group', 'memberpress'), - 'not_found' => __('No Group found', 'memberpress'), - 'not_found_in_trash' => __('No Group found in Trash', 'memberpress'), - 'parent_item_colon' => __('Parent Group:', 'memberpress') - ), - 'public' => true, - 'show_ui' => true, //MeprUpdateCtrl::is_activated(), - 'show_in_menu' => 'memberpress', - 'capability_type' => 'page', - 'hierarchical' => true, - 'register_meta_box_cb' => 'MeprGroupsCtrl::add_meta_boxes', - 'rewrite' => array("slug" => $mepr_options->group_pages_slug, "with_front" => false), - 'supports' => array('title', 'editor', 'page-attributes', 'comments', 'thumbnail') - ) - ); - register_post_type( $this->cpt->slug, $this->cpt->config ); - } - - public static function render_pricing_boxes($content, $manual = false) { - $current_post = MeprUtils::get_current_post(); - - //This isn't a post? Just return the content then - if($current_post === false) { return $content; } - - //WARNING the_content CAN be run more than once per page load - //so this static var prevents stuff from happening twice - //like cancelling a subscr or resuming etc... - static $already_run = array(); - static $new_content = array(); - static $content_length = array(); - //Init this posts static values - if(!isset($new_content[$current_post->ID]) || empty($new_content[$current_post->ID])) { - $already_run[$current_post->ID] = false; - $new_content[$current_post->ID] = ''; - $content_length[$current_post->ID] = -1; + +if (!defined('ABSPATH')) { + die('You are not allowed to call this page directly.'); +} + +class MeprGroupsCtrl extends MeprCptCtrl +{ + public function load_hooks() + { + add_action('admin_init', 'MeprGroup::cleanup_db'); + add_action('admin_enqueue_scripts', 'MeprGroupsCtrl::enqueue_scripts'); + add_action('manage_pages_custom_column', 'MeprGroupsCtrl::custom_columns', 10, 2); + add_action('save_post', 'MeprGroupsCtrl::save_postdata'); + add_action('wp_trash_post', 'MeprGroupsCtrl::trash_group_remove_memberships'); + add_action('wp_ajax_mepr_is_product_already_in_group', 'MeprGroupsCtrl::is_product_already_in_group'); + add_action('mepr-group-fallback-membership-deleted', 'MeprGroupsCtrl::remove_fallback_memberships', 10, 2); + add_action('mepr-group-fallback-membership-changed', 'MeprGroupsCtrl::update_fallback_memberships', 10, 2); + add_action('mepr-txn-status-complete', [$this, 'expire_fallback']); + add_action('mepr-txn-status-confirmed', [$this, 'expire_fallback']); + add_action('mepr-txn-status-refunded', [$this, 'create_fallback']); + add_action('mepr-transaction-expired', [$this, 'create_fallback'], 10, 2); + add_filter('the_content', 'MeprGroupsCtrl::render_pricing_boxes', 10); + add_filter('manage_edit-memberpressgroup_columns', 'MeprGroupsCtrl::columns'); + add_filter('template_include', 'MeprGroupsCtrl::template_include'); + add_action('mepr-txn-status-failed', [$this, 'create_fallback']); + + MeprHooks::add_shortcode('mepr-group-price-boxes', 'MeprGroupsCtrl::shortcode_group_price_boxes'); + + // Cleanup list view + add_filter('views_edit-' . MeprGroup::$cpt, 'MeprAppCtrl::cleanup_list_view'); } - if($already_run[$current_post->ID] && strlen($content) == $content_length[$current_post->ID] && !$manual) //shortcode may pass - return $new_content[$current_post->ID]; + public function register_post_type() + { + $mepr_options = MeprOptions::fetch(); + $this->cpt = (object)[ + 'slug' => MeprGroup::$cpt, + 'config' => [ + 'labels' => [ + 'name' => __('Groups', 'memberpress'), + 'singular_name' => __('Group', 'memberpress'), + 'add_new' => __('Add New', 'memberpress'), + 'add_new_item' => __('Add New Group', 'memberpress'), + 'edit_item' => __('Edit Group', 'memberpress'), + 'new_item' => __('New Group', 'memberpress'), + 'view_item' => __('View Group', 'memberpress'), + 'search_items' => __('Search Group', 'memberpress'), + 'not_found' => __('No Group found', 'memberpress'), + 'not_found_in_trash' => __('No Group found in Trash', 'memberpress'), + 'parent_item_colon' => __('Parent Group:', 'memberpress'), + ], + 'public' => true, + 'show_ui' => true, // MeprUpdateCtrl::is_activated(), + 'show_in_menu' => 'memberpress', + 'capability_type' => 'page', + 'hierarchical' => true, + 'register_meta_box_cb' => 'MeprGroupsCtrl::add_meta_boxes', + 'rewrite' => [ + 'slug' => $mepr_options->group_pages_slug, + 'with_front' => false, + ], + 'supports' => ['title', 'editor', 'page-attributes', 'comments', 'thumbnail'], + ], + ]; + register_post_type($this->cpt->slug, $this->cpt->config); + } - $content_length[$current_post->ID] = strlen($content); - $already_run[$current_post->ID] = true; + public static function render_pricing_boxes($content, $manual = false) + { + $current_post = MeprUtils::get_current_post(); - if(isset($current_post) && is_a($current_post, 'WP_Post') && $current_post->post_type == MeprGroup::$cpt) { - $group = new MeprGroup($current_post->ID); + // This isn't a post? Just return the content then + if ($current_post === false) { + return $content; + } - //Short circuiting for any of the following reasons - if( $group->ID === null || //Bad group for some reason - (!$manual && $group->manual_append_price_boxes()) || //the_content filter and show manually is enabled - ($manual && !$group->manual_append_price_boxes()) ) { //do_shortcode and show manually is disabled - //See notes above - $new_content[$current_post->ID] = $content; - return $new_content[$current_post->ID]; - } + // WARNING the_content CAN be run more than once per page load + // so this static var prevents stuff from happening twice + // like cancelling a subscr or resuming etc... + static $already_run = []; + static $new_content = []; + static $content_length = []; + // Init this posts static values + if (!isset($new_content[$current_post->ID]) || empty($new_content[$current_post->ID])) { + $already_run[$current_post->ID] = false; + $new_content[$current_post->ID] = ''; + $content_length[$current_post->ID] = -1; + } - ob_start(); - self::display_pricing_boxes($group); - $content .= ob_get_clean(); - } + if ($already_run[$current_post->ID] && strlen($content) == $content_length[$current_post->ID] && !$manual) { // shortcode may pass + return $new_content[$current_post->ID]; + } - //See notes above - $new_content[$current_post->ID] = $content; - return $new_content[$current_post->ID]; - } + $content_length[$current_post->ID] = strlen($content); + $already_run[$current_post->ID] = true; + + if (isset($current_post) && is_a($current_post, 'WP_Post') && $current_post->post_type == MeprGroup::$cpt) { + $group = new MeprGroup($current_post->ID); + + // Short circuiting for any of the following reasons + if ( + $group->ID === null || // Bad group for some reason + (!$manual && $group->manual_append_price_boxes()) || // the_content filter and show manually is enabled + ($manual && !$group->manual_append_price_boxes()) + ) { // do_shortcode and show manually is disabled + // See notes above + $new_content[$current_post->ID] = $content; + return $new_content[$current_post->ID]; + } + + ob_start(); + self::display_pricing_boxes($group); + $content .= ob_get_clean(); + } - public static function display_pricing_boxes($group, $theme=null, $args = []) { - if(MeprReadyLaunchCtrl::template_enabled( 'pricing' ) || MeprAppHelper::has_block( 'memberpress/pro-pricing-table' )){ - MeprView::render('/readylaunch/groups/front_groups_page', get_defined_vars()); - } else { - MeprView::render('/groups/front_groups_page', get_defined_vars()); + // See notes above + $new_content[$current_post->ID] = $content; + return $new_content[$current_post->ID]; } - } - - public static function columns($columns) { - $columns = array( - "cb" => "", - "ID" => __("ID", 'memberpress'), - "title" => __("Group Title", "memberpress"), - "url" => __("URL", "memberpress"), - "group-products" => __("Memberships in Group", "memberpress") - ); - return $columns; - } - - public static function custom_columns($column, $post_id) { - $group = new MeprGroup($post_id); - - if($group->ID !== null) { - if("ID" == $column) { - echo $group->ID; - } - elseif("group-products" == $column) { - echo implode(', ', $group->products('titles')); - } - elseif("url" == $column) { - echo $group->url(); - } + + public static function display_pricing_boxes($group, $theme = null, $args = []) + { + if (MeprReadyLaunchCtrl::template_enabled('pricing') || MeprAppHelper::has_block('memberpress/pro-pricing-table')) { + MeprView::render('/readylaunch/groups/front_groups_page', get_defined_vars()); + } else { + MeprView::render('/groups/front_groups_page', get_defined_vars()); + } } - } - - // Template selection - public static function template_include($template) { - global $post, $wp_query; - - if(!is_singular()) { return $template; } - - if(isset($post) && is_a($post, 'WP_Post') && $post->post_type == MeprGroup::$cpt) { - $group = new MeprGroup($post->ID); - - if(!$group->pricing_page_disabled) { - $new_template = $group->get_page_template(); - } - elseif($group->pricing_page_disabled && !empty($group->alternate_group_url)) { - MeprUtils::wp_redirect($group->alternate_group_url); - } - else { - $wp_query->is_404 = true; - $new_template = locate_template(array('404.php')); - } + + public static function columns($columns) + { + $columns = [ + 'cb' => '', + 'ID' => __('ID', 'memberpress'), + 'title' => __('Group Title', 'memberpress'), + 'url' => __('URL', 'memberpress'), + 'group-products' => __('Memberships in Group', 'memberpress'), + ]; + return $columns; } - if(isset($new_template) && !empty($new_template)) { return $new_template; } + public static function custom_columns($column, $post_id) + { + $group = new MeprGroup($post_id); + + if ($group->ID !== null) { + if ('ID' == $column) { + echo $group->ID; + } elseif ('group-products' == $column) { + echo implode(', ', $group->products('titles')); + } elseif ('url' == $column) { + echo $group->url(); + } + } + } - return $template; - } + // Template selection + public static function template_include($template) + { + global $post, $wp_query; - public static function add_meta_boxes() { - global $post_id; - $group = new MeprGroup($post_id); + if (!is_singular()) { + return $template; + } - add_meta_box("memberpress-group-meta", __("Group Options", 'memberpress'), "MeprGroupsCtrl::group_meta_box", MeprGroup::$cpt, "normal", "high", array('group' => $group)); - add_meta_box("memberpress-custom-template", __('Custom Page Template', 'memberpress'), "MeprGroupsCtrl::custom_page_template", MeprGroup::$cpt, "side", "default", array('group' => $group)); - } + if (isset($post) && is_a($post, 'WP_Post') && $post->post_type == MeprGroup::$cpt) { + $group = new MeprGroup($post->ID); + + if (!$group->pricing_page_disabled) { + $new_template = $group->get_page_template(); + } elseif ($group->pricing_page_disabled && !empty($group->alternate_group_url)) { + MeprUtils::wp_redirect($group->alternate_group_url); + } else { + $wp_query->is_404 = true; + $new_template = locate_template(['404.php']); + } + } - public static function save_postdata($post_id) { - $post = get_post($post_id); - $fallback_state = 'unchanged'; + if (isset($new_template) && !empty($new_template)) { + return $new_template; + } - if(!wp_verify_nonce((isset($_POST[MeprGroup::$nonce_str]))?$_POST[MeprGroup::$nonce_str]:'', MeprGroup::$nonce_str.wp_salt())) { - return $post_id; //Nonce prevents meta data from being wiped on move to trash + return $template; } - if(defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) { - return $post_id; - } + public static function add_meta_boxes() + { + global $post_id; + $group = new MeprGroup($post_id); - if(defined('DOING_AJAX')) { - return; + add_meta_box('memberpress-group-meta', __('Group Options', 'memberpress'), 'MeprGroupsCtrl::group_meta_box', MeprGroup::$cpt, 'normal', 'high', ['group' => $group]); + add_meta_box('memberpress-custom-template', __('Custom Page Template', 'memberpress'), 'MeprGroupsCtrl::custom_page_template', MeprGroup::$cpt, 'side', 'default', ['group' => $group]); } - if(!empty($post) && $post->post_type == MeprGroup::$cpt) { - $group = new MeprGroup($post_id); - $group->pricing_page_disabled = isset($_POST[MeprGroup::$pricing_page_disabled_str]); - $group->disable_change_plan_popup = isset($_POST[MeprGroup::$disable_change_plan_popup_str]); - $group->is_upgrade_path = isset($_POST[MeprGroup::$is_upgrade_path_str]); - $group->upgrade_path_reset_period = isset($_POST[MeprGroup::$upgrade_path_reset_period_str]); - //$group->group_page_style_options = self::get_style_options_array(); - $group->group_theme = sanitize_text_field($_POST[MeprGroup::$group_theme_str]); - $group->page_button_class = sanitize_text_field(trim($_POST[MeprGroup::$page_button_class_str])); - $group->page_button_highlighted_class = sanitize_text_field(trim($_POST[MeprGroup::$page_button_highlighted_class_str])); - $group->page_button_disabled_class = sanitize_text_field(trim($_POST[MeprGroup::$page_button_disabled_class_str])); - $group->alternate_group_url = sanitize_text_field(wp_unslash($_POST[MeprGroup::$alternate_group_url_str])); - self::store_chosen_products($group->ID); - $group->use_custom_template = isset($_POST['_mepr_use_custom_template']); - $group->custom_template = isset($_POST['_mepr_custom_template'])?sanitize_text_field($_POST['_mepr_custom_template']):''; - - $orig_fallback_membership = $group->fallback_membership; - $fallback_membership = sanitize_text_field($_POST[MeprGroup::$fallback_membership_str]); - - if(empty($_POST[MeprGroup::$fallback_membership_str])) { - if(!empty($orig_fallback_membership)) { - // Fallback changed to default or none - $fallback_state = 'deleted'; + public static function save_postdata($post_id) + { + $post = get_post($post_id); + $fallback_state = 'unchanged'; + + if (!wp_verify_nonce((isset($_POST[MeprGroup::$nonce_str])) ? $_POST[MeprGroup::$nonce_str] : '', MeprGroup::$nonce_str . wp_salt())) { + return $post_id; // Nonce prevents meta data from being wiped on move to trash + } + + if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) { + return $post_id; + } + + if (defined('DOING_AJAX')) { + return; } - } - else { - if(!empty($orig_fallback_membership) && $orig_fallback_membership != $fallback_membership) { - // Fallback changed to a new product/membership - $fallback_state = 'changed'; + + if (!empty($post) && $post->post_type == MeprGroup::$cpt) { + $group = new MeprGroup($post_id); + $group->pricing_page_disabled = isset($_POST[MeprGroup::$pricing_page_disabled_str]); + $group->disable_change_plan_popup = isset($_POST[MeprGroup::$disable_change_plan_popup_str]); + $group->is_upgrade_path = isset($_POST[MeprGroup::$is_upgrade_path_str]); + $group->upgrade_path_reset_period = isset($_POST[MeprGroup::$upgrade_path_reset_period_str]); + // $group->group_page_style_options = self::get_style_options_array(); + $group->group_theme = sanitize_text_field($_POST[MeprGroup::$group_theme_str]); + $group->page_button_class = sanitize_text_field(trim($_POST[MeprGroup::$page_button_class_str])); + $group->page_button_highlighted_class = sanitize_text_field(trim($_POST[MeprGroup::$page_button_highlighted_class_str])); + $group->page_button_disabled_class = sanitize_text_field(trim($_POST[MeprGroup::$page_button_disabled_class_str])); + $group->alternate_group_url = sanitize_text_field(wp_unslash($_POST[MeprGroup::$alternate_group_url_str])); + self::store_chosen_products($group->ID); + $group->use_custom_template = isset($_POST['_mepr_use_custom_template']); + $group->custom_template = isset($_POST['_mepr_custom_template']) ? sanitize_text_field($_POST['_mepr_custom_template']) : ''; + + $orig_fallback_membership = $group->fallback_membership; + $fallback_membership = sanitize_text_field($_POST[MeprGroup::$fallback_membership_str]); + + if (empty($_POST[MeprGroup::$fallback_membership_str])) { + if (!empty($orig_fallback_membership)) { + // Fallback changed to default or none + $fallback_state = 'deleted'; + } + } else { + if (!empty($orig_fallback_membership) && $orig_fallback_membership != $fallback_membership) { + // Fallback changed to a new product/membership + $fallback_state = 'changed'; + } + } + + $group->fallback_membership = $fallback_membership; + $group->store_meta(); + // Let's handle the $fallback_state changes through hooks + MeprHooks::do_action("mepr-group-fallback-membership-{$fallback_state}", $orig_fallback_membership, $group); + + // Some themes rely on this meta key to be set to use the custom template, and they don't use locate_template + if ($group->use_custom_template && !empty($group->custom_template)) { + update_post_meta($group->ID, '_wp_page_template', $group->custom_template); + } else { + update_post_meta($group->ID, '_wp_page_template', ''); + } } - } - - $group->fallback_membership = $fallback_membership; - $group->store_meta(); - // Let's handle the $fallback_state changes through hooks - MeprHooks::do_action("mepr-group-fallback-membership-{$fallback_state}", $orig_fallback_membership, $group); - - //Some themes rely on this meta key to be set to use the custom template, and they don't use locate_template - if($group->use_custom_template && !empty($group->custom_template)) { - update_post_meta($group->ID, '_wp_page_template', $group->custom_template); - } - else { - update_post_meta($group->ID, '_wp_page_template', ''); - } } - } - public static function trash_group_remove_memberships($id) { - global $post_type, $wpdb; - if($post_type != MeprGroup::$cpt) { return; } - $wpdb->query("DELETE FROM {$wpdb->postmeta} WHERE meta_key = '" . MeprProduct::$group_id_str . "' AND meta_value = '{$id}'"); - } + public static function trash_group_remove_memberships($id) + { + global $post_type, $wpdb; + if ($post_type != MeprGroup::$cpt) { + return; + } + $wpdb->query("DELETE FROM {$wpdb->postmeta} WHERE meta_key = '" . MeprProduct::$group_id_str . "' AND meta_value = '{$id}'"); + } - // public static function get_style_options_array() { + // public static function get_style_options_array() { // $styles = array(); - // $styles['layout'] = $_POST[MeprGroup::$group_page_layout_str]; // $styles['style'] = $_POST[MeprGroup::$group_page_style_str]; // $styles['button_size'] = $_POST[MeprGroup::$group_page_button_size_str]; @@ -252,199 +273,218 @@ public static function trash_group_remove_memberships($id) { // $styles['font_style'] = $_POST[MeprGroup::$group_page_font_style_str]; // $styles['font_size'] = $_POST[MeprGroup::$group_page_font_size_str]; // $styles['button_color'] = $_POST[MeprGroup::$group_page_button_color_str]; - // return $styles; - // } - - public static function store_chosen_products($group_id) { - if(isset($_POST[MeprGroup::$products_str]['product'])) { - //Zero out old memberships before assigning the new ones - self::zero_out_old_products($group_id); - - for($index=0; $index < (count($_POST[MeprGroup::$products_str]['product']) - 1); $index++) { - $product_id = (int)sanitize_key($_POST[MeprGroup::$products_str]['product'][$index]); - $prd = new MeprProduct($product_id); - - if($prd->ID) { - $prd->group_id = $group_id; - $prd->group_order = $index; - $prd->store_meta(); + // } + public static function store_chosen_products($group_id) + { + if (isset($_POST[MeprGroup::$products_str]['product'])) { + // Zero out old memberships before assigning the new ones + self::zero_out_old_products($group_id); + + for ($index = 0; $index < (count($_POST[MeprGroup::$products_str]['product']) - 1); $index++) { + $product_id = (int)sanitize_key($_POST[MeprGroup::$products_str]['product'][$index]); + $prd = new MeprProduct($product_id); + + if ($prd->ID) { + $prd->group_id = $group_id; + $prd->group_order = $index; + $prd->store_meta(); + } + } } - } } - } - - //Deletes all memberships from the given group. We purge memberships before saving the new one's. - public static function zero_out_old_products($group_id) { - $group = new MeprGroup($group_id); - $products = $group->products(); - - if(!empty($products)) { - foreach($products as $product) { - $product->group_id = 0; - $product->store_meta(); - } + + // Deletes all memberships from the given group. We purge memberships before saving the new one's. + public static function zero_out_old_products($group_id) + { + $group = new MeprGroup($group_id); + $products = $group->products(); + + if (!empty($products)) { + foreach ($products as $product) { + $product->group_id = 0; + $product->store_meta(); + } + } } - } - public static function group_meta_box($post, $args) { //Don't use $post here, it is null on new group - use args instead - $group = $args['args']['group']; - $mepr_options = MeprOptions::fetch(); + public static function group_meta_box($post, $args) + { + // Don't use $post here, it is null on new group - use args instead + $group = $args['args']['group']; + $mepr_options = MeprOptions::fetch(); - MeprView::render('/admin/groups/form', get_defined_vars()); - } + MeprView::render('/admin/groups/form', get_defined_vars()); + } - public static function custom_page_template($post, $args) { - $group = $args['args']['group']; + public static function custom_page_template($post, $args) + { + $group = $args['args']['group']; - MeprView::render('/admin/groups/custom_page_template_form', get_defined_vars()); - } + MeprView::render('/admin/groups/custom_page_template_form', get_defined_vars()); + } - public static function enqueue_scripts($hook) { - global $current_screen; + public static function enqueue_scripts($hook) + { + global $current_screen; - if($current_screen->post_type == MeprGroup::$cpt) { - wp_enqueue_style('mepr-groups-css', MEPR_CSS_URL.'/admin-groups.css', array('mepr-settings-table-css'), MEPR_VERSION); + if ($current_screen->post_type == MeprGroup::$cpt) { + wp_enqueue_style('mepr-groups-css', MEPR_CSS_URL . '/admin-groups.css', ['mepr-settings-table-css'], MEPR_VERSION); - wp_dequeue_script('autosave'); //Disable auto-saving + wp_dequeue_script('autosave'); // Disable auto-saving - wp_enqueue_script('mepr-groups-js', MEPR_JS_URL.'/admin_groups.js', array('jquery','jquery-ui-sortable','mepr-settings-table-js'), MEPR_VERSION); + wp_enqueue_script('mepr-groups-js', MEPR_JS_URL . '/admin_groups.js', ['jquery','jquery-ui-sortable','mepr-settings-table-js'], MEPR_VERSION); - wp_localize_script('mepr-groups-js', 'MeprAdminGroups', array('readylaunch_enabled' => MeprReadyLaunchCtrl::template_enabled('pricing'))); + wp_localize_script('mepr-groups-js', 'MeprAdminGroups', ['readylaunch_enabled' => MeprReadyLaunchCtrl::template_enabled('pricing')]); + } } - } - public static function is_product_already_in_group() { - if(!isset($_POST['product_id'])) { - _e('Unknown error has occured.', 'memberpress'); - die(); - } + public static function is_product_already_in_group() + { + if (!isset($_POST['product_id'])) { + _e('Unknown error has occured.', 'memberpress'); + die(); + } - $groups = MeprCptModel::all('MeprGroup'); + $groups = MeprCptModel::all('MeprGroup'); - if(empty($groups)) { die(); } + if (empty($groups)) { + die(); + } - foreach($groups as $g) { - $group = new MeprGroup($g->ID); - $products = $group->products(); + foreach ($groups as $g) { + $group = new MeprGroup($g->ID); + $products = $group->products(); - if(empty($products)) { continue; } + if (empty($products)) { + continue; + } - foreach($products as $p) { - if($p->ID == $_POST['product_id']) { - _e('This membership already belongs to another group. If you assign it to this group, it will be removed from the other.', 'memberpress'); - die(); + foreach ($products as $p) { + if ($p->ID == $_POST['product_id']) { + _e('This membership already belongs to another group. If you assign it to this group, it will be removed from the other.', 'memberpress'); + die(); + } + } } - } - } - die(); //No matches so let's kill this thing - } - - /** - * Removes all fallback transactions matching the product and gateway - * Hook: mepr-group-fallback-membership-deleted - */ - public static function remove_fallback_memberships($fallback_membership, $group) { - global $wpdb; - $mepr_db = MeprDb::fetch(); + die(); // No matches so let's kill this thing + } - $query = $wpdb->prepare(" + /** + * Removes all fallback transactions matching the product and gateway + * Hook: mepr-group-fallback-membership-deleted + */ + public static function remove_fallback_memberships($fallback_membership, $group) + { + global $wpdb; + $mepr_db = MeprDb::fetch(); + + $query = $wpdb->prepare( + " DELETE FROM {$mepr_db->transactions} WHERE product_id = %d AND gateway = %s ", - (int)$fallback_membership, - MeprTransaction::$fallback_gateway_str - ); - - MeprUtils::debug_log("Removing fallback memberships for {$fallback_membership}"); - $wpdb->query($query); - } - - /** - * Updates all fallback transactions matching the product and gateway to the new product/membership - * Hook: mepr-group-fallback-membership-changed - */ - public static function update_fallback_memberships($fallback_membership, $group) { - global $wpdb; - $mepr_db = MeprDb::fetch(); - - $query = $wpdb->prepare(" + (int)$fallback_membership, + MeprTransaction::$fallback_gateway_str + ); + + MeprUtils::debug_log("Removing fallback memberships for {$fallback_membership}"); + $wpdb->query($query); + } + + /** + * Updates all fallback transactions matching the product and gateway to the new product/membership + * Hook: mepr-group-fallback-membership-changed + */ + public static function update_fallback_memberships($fallback_membership, $group) + { + global $wpdb; + $mepr_db = MeprDb::fetch(); + + $query = $wpdb->prepare( + " UPDATE {$mepr_db->transactions} SET product_id = %d WHERE product_id = %d AND gateway = %s ", - (int)$group->fallback_membership, - (int)$fallback_membership, - MeprTransaction::$fallback_gateway_str - ); - - MeprUtils::debug_log("Updating fallback memberships for {$fallback_membership} to {$group->fallback_membership}"); - $wpdb->query($query); - } - - /** - * Used to expire the active fallback transaction - * Hooks: mepr-txn-status-complete, mepr-txn-status-confirmed - */ - public static function expire_fallback($txn) { - $product = $txn->product(); - $user = $txn->user(); - $group = $product->group(); - - // Return if product doesn't belong to a group - if($group === false) { return; } - - $fallback_membership = $group->fallback_membership(); - - if($fallback_membership !== false && $product->ID != $fallback_membership->ID) { - if(($user->subscription_in_group($group)) || ($user->lifetime_subscription_in_group($group, array(MeprTransaction::$fallback_str)))) { - $fallback_txn = $user->fallback_txn($fallback_membership->ID); - if($fallback_txn !== false) { - $fallback_txn->expire(); - } // No fallback transaction exists - } // No active subscription found for the group - } // No fallback for product or the transaction product is the fallback - } - - /** - * Used to expire create a fallback transaction - * Hooks: mepr-transaction-expired, mepr-txn-status-refunded, mepr-txn-status-failed - */ - public static function create_fallback($txn, $sub_status=false) { - $user = $txn->user(); - $product = $txn->product(); - $group = $product->group(); - - // Return if product doesn't belong to a group - if($group === false) { return; } - - $fallback_membership = $group->fallback_membership(); - - if($fallback_membership !== false && $product->ID != $fallback_membership->ID) { - if((!$user->is_already_subscribed_to($fallback_membership->ID)) && (!$user->subscription_in_group($group)) && (!$user->lifetime_subscription_in_group($group, array(MeprTransaction::$fallback_str)))) { - $fallback_txn_id = $txn->create_fallback_transaction(); - MeprEvent::record('subscription-changed', $txn, $fallback_txn_id); - } // User still has an active subscription in the group - } // No fallback for product or the transaction product is the fallback - } - - public static function shortcode_group_price_boxes($atts, $content = '') { - if(isset($atts['group_id']) and $group = new MeprGroup($atts['group_id'])) { - $theme = (isset($atts['theme']) ? $atts['theme'] : null); - - ob_start(); - - self::display_pricing_boxes($group, $theme, $atts); - - return ob_get_clean(); + (int)$group->fallback_membership, + (int)$fallback_membership, + MeprTransaction::$fallback_gateway_str + ); + + MeprUtils::debug_log("Updating fallback memberships for {$fallback_membership} to {$group->fallback_membership}"); + $wpdb->query($query); } - else { - //No validation needed here as the below function does it all - //This is just a wrapper - return self::render_pricing_boxes('', true); + + /** + * Used to expire the active fallback transaction + * Hooks: mepr-txn-status-complete, mepr-txn-status-confirmed + */ + public static function expire_fallback($txn) + { + $product = $txn->product(); + $user = $txn->user(); + $group = $product->group(); + + // Return if product doesn't belong to a group + if ($group === false) { + return; + } + + $fallback_membership = $group->fallback_membership(); + + if ($fallback_membership !== false && $product->ID != $fallback_membership->ID) { + if (($user->subscription_in_group($group)) || ($user->lifetime_subscription_in_group($group, [MeprTransaction::$fallback_str]))) { + $fallback_txn = $user->fallback_txn($fallback_membership->ID); + if ($fallback_txn !== false) { + $fallback_txn->expire(); + } // No fallback transaction exists + } // No active subscription found for the group + } // No fallback for product or the transaction product is the fallback + } + + /** + * Used to expire create a fallback transaction + * Hooks: mepr-transaction-expired, mepr-txn-status-refunded, mepr-txn-status-failed + */ + public static function create_fallback($txn, $sub_status = false) + { + $user = $txn->user(); + $product = $txn->product(); + $group = $product->group(); + + // Return if product doesn't belong to a group + if ($group === false) { + return; + } + + $fallback_membership = $group->fallback_membership(); + + if ($fallback_membership !== false && $product->ID != $fallback_membership->ID) { + if ((!$user->is_already_subscribed_to($fallback_membership->ID)) && (!$user->subscription_in_group($group)) && (!$user->lifetime_subscription_in_group($group, [MeprTransaction::$fallback_str]))) { + $fallback_txn_id = $txn->create_fallback_transaction(); + MeprEvent::record('subscription-changed', $txn, $fallback_txn_id); + } // User still has an active subscription in the group + } // No fallback for product or the transaction product is the fallback + } + + public static function shortcode_group_price_boxes($atts, $content = '') + { + if (isset($atts['group_id']) and $group = new MeprGroup($atts['group_id'])) { + $theme = (isset($atts['theme']) ? $atts['theme'] : null); + + ob_start(); + + self::display_pricing_boxes($group, $theme, $atts); + + return ob_get_clean(); + } else { + // No validation needed here as the below function does it all + // This is just a wrapper + return self::render_pricing_boxes('', true); + } } - } } //End class diff --git a/app/controllers/MeprGrowthToolsCtrl.php b/app/controllers/MeprGrowthToolsCtrl.php index a85d0d2..0cddd5e 100644 --- a/app/controllers/MeprGrowthToolsCtrl.php +++ b/app/controllers/MeprGrowthToolsCtrl.php @@ -1,25 +1,27 @@ -=') && class_exists('\MemberPress\Caseproof\GrowthTools\App')) { - add_action('admin_enqueue_scripts', function () { - $screen = get_current_screen(); - if ($screen->id == 'memberpress_page_memberpress-growth-tools') { - wp_enqueue_style('memberpress-onboarding', MEPR_CSS_URL . '/admin-onboarding.css', [], MEPR_VERSION); + public function load_hooks() + { + if (version_compare(phpversion(), '7.4', '>=') && class_exists('\MemberPress\Caseproof\GrowthTools\App')) { + add_action('admin_enqueue_scripts', function () { + $screen = get_current_screen(); + if ($screen->id == 'memberpress_page_memberpress-growth-tools') { + wp_enqueue_style('memberpress-onboarding', MEPR_CSS_URL . '/admin-onboarding.css', [], MEPR_VERSION); + } + }); + $config = new \MemberPress\Caseproof\GrowthTools\Config([ + 'parentMenuSlug' => 'memberpress', + 'instanceId' => 'memberpress', + 'menuSlug' => 'memberpress-growth-tools', + 'buttonCSSClasses' => ['mepr-wizard-button-blue'], + ]); + new \MemberPress\Caseproof\GrowthTools\App($config); } - }); - $config = new \MemberPress\Caseproof\GrowthTools\Config([ - 'parentMenuSlug' => 'memberpress', - 'instanceId' => 'memberpress', - 'menuSlug' => 'memberpress-growth-tools', - 'buttonCSSClasses' => ['mepr-wizard-button-blue'], - ]); - new \MemberPress\Caseproof\GrowthTools\App($config); } - } } diff --git a/app/controllers/MeprLoginCtrl.php b/app/controllers/MeprLoginCtrl.php index 2979c60..0f28243 100644 --- a/app/controllers/MeprLoginCtrl.php +++ b/app/controllers/MeprLoginCtrl.php @@ -1,444 +1,447 @@ -ID) ? MeprUtils::get_permalink($current_post->ID) : ''; - - ob_start(); - - if(MeprUtils::is_user_logged_in()) { - ?> - - - - ID) ? MeprUtils::get_permalink($current_post->ID) : ''; + + ob_start(); + + if (MeprUtils::is_user_logged_in()) { + ?> + + + + display_reset_password_form_errors($_POST['errors']); - } - elseif($_REQUEST['action'] == 'forgot_password') { - $this->display_forgot_password_form(); - } - elseif($_REQUEST['action'] == 'reset_password') { - $this->display_reset_password_form($_REQUEST['mkey'], $_REQUEST['u']); - } - else{ - $this->display_login_form( - $shortcode, - (isset($atts['use_redirect']) && $atts['use_redirect']=='true'), - '', - $atts - ); - } - } - //END TEMP WPML FIX - else { - if ( ! is_user_logged_in() || ! isset( $atts['show_logged_in'] ) || $atts['show_logged_in'] !== 'false' ) { - $this->display_login_form( - $shortcode, - (isset($atts['use_redirect']) && $atts['use_redirect']=='true'), - '', - $atts - ); - } + // Grabs a string of the login form + public function render_login_form($atts = [], $content = '', $shortcode = true) + { + global $post; + $mepr_options = MeprOptions::fetch(); + + if (isset($atts['redirect_to']) && !empty($atts['redirect_to'])) { + // Security fix. Restrict redirect_to param to safe URLs PT#154812459 + $_REQUEST['redirect_to'] = wp_validate_redirect($atts['redirect_to'], apply_filters('wp_safe_redirect_fallback', home_url(), 302)); + } + + ob_start(); + + if ( + $shortcode && isset($_REQUEST['action']) && + $_REQUEST['action'] != 'mepr_unauthorized' && + $_REQUEST['action'] != 'bpnoaccess' && // BuddyPress fix + !defined('DOING_AJAX') + ) { + // Don't do this if it's an ajax request. Probably loading up the form shortcode via AJAX + // Need to check for this POST first + if ($_REQUEST['action'] == 'mepr_process_reset_password_form' && isset($_POST['errors']) && !empty($_POST['errors'])) { + $this->display_reset_password_form_errors($_POST['errors']); + } elseif ($_REQUEST['action'] == 'forgot_password') { + $this->display_forgot_password_form(); + } elseif ($_REQUEST['action'] == 'reset_password') { + $this->display_reset_password_form($_REQUEST['mkey'], $_REQUEST['u']); + } else { + $this->display_login_form( + $shortcode, + (isset($atts['use_redirect']) && $atts['use_redirect'] == 'true'), + '', + $atts + ); + } + } else { + if (! is_user_logged_in() || ! isset($atts['show_logged_in']) || $atts['show_logged_in'] !== 'false') { + $this->display_login_form( + $shortcode, + (isset($atts['use_redirect']) && $atts['use_redirect'] == 'true'), + '', + $atts + ); + } + } + + return ob_get_clean(); } - return ob_get_clean(); - } + // Outputs the login form + public function display_login_form($shortcode = false, $widget_use_redirect_urls = false, $message = '', $atts = []) + { + $current_post = MeprUtils::get_current_post(); + $mepr_options = MeprOptions::fetch(); + $login_page_id = (!empty($mepr_options->login_page_id) && $mepr_options->login_page_id > 0) ? $mepr_options->login_page_id : 0; + $is_login_page = (is_page($login_page_id) || $widget_use_redirect_urls); - // Outputs the login form - public function display_login_form($shortcode=false, $widget_use_redirect_urls = false, $message = '', $atts = []) { - $current_post = MeprUtils::get_current_post(); - $mepr_options = MeprOptions::fetch(); - $login_page_id = (!empty($mepr_options->login_page_id) && $mepr_options->login_page_id > 0)?$mepr_options->login_page_id:0; - $is_login_page = (is_page($login_page_id) || $widget_use_redirect_urls); + // Initially set redirect_to to the default + $redirect_to = $mepr_options->login_redirect_url; - // Initially set redirect_to to the default - $redirect_to = $mepr_options->login_redirect_url; + // if redirect_to isset then set it to the query param + if (isset($_REQUEST['redirect_to']) && !empty($_REQUEST['redirect_to'])) { + $redirect_to = urldecode($_REQUEST['redirect_to']); + // Security fix. Restrict redirect_to param to safe URLs PT#154812459 + $redirect_to = wp_validate_redirect($redirect_to, apply_filters('wp_safe_redirect_fallback', home_url(), 302)); + } - // if redirect_to isset then set it to the query param - if(isset($_REQUEST['redirect_to']) && !empty($_REQUEST['redirect_to'])) { - $redirect_to = urldecode($_REQUEST['redirect_to']); - // Security fix. Restrict redirect_to param to safe URLs PT#154812459 - $redirect_to = wp_validate_redirect($redirect_to, apply_filters( 'wp_safe_redirect_fallback', home_url(), 302)); - } + // if we're on a page other than the login page and we're in a shortcode + if ( + (!isset($_REQUEST['redirect_to']) || empty($_REQUEST['redirect_to'])) && + false !== $shortcode && !is_page($login_page_id) && false === $widget_use_redirect_urls + ) { + // $redirect_to = MeprUtils::get_permalink($current_post->ID); + $redirect_to = esc_url($_SERVER['REQUEST_URI']); + } - // if we're on a page other than the login page and we're in a shortcode - if((!isset($_REQUEST['redirect_to']) || empty($_REQUEST['redirect_to'])) && - false!==$shortcode && !is_page($login_page_id) && false===$widget_use_redirect_urls) { - // $redirect_to = MeprUtils::get_permalink($current_post->ID); - $redirect_to = esc_url($_SERVER['REQUEST_URI']); - } + // Check if we've got an unauth page set here + // Is this even used here??? I don't think so, but leaving it here just in case + if (isset($_REQUEST['mepr-unauth-page']) && !isset($_REQUEST['redirect_to'])) { + $redirect_to = MeprUtils::get_permalink($_REQUEST['mepr-unauth-page']); + } - // Check if we've got an unauth page set here - // Is this even used here??? I don't think so, but leaving it here just in case - if(isset($_REQUEST['mepr-unauth-page']) && !isset($_REQUEST['redirect_to'])) { - $redirect_to = MeprUtils::get_permalink($_REQUEST['mepr-unauth-page']); - } + $redirect_to = MeprHooks::apply_filters('mepr-login-redirect-url', $redirect_to); - $redirect_to = MeprHooks::apply_filters('mepr-login-redirect-url', $redirect_to); + if ($login_page_id) { + $login_url = $mepr_options->login_page_url(); + $login_delim = MeprAppCtrl::get_param_delimiter_char($login_url); + $forgot_password_url = "{$login_url}{$login_delim}action=forgot_password"; + } else { + $login_url = home_url('/wp-login.php'); + $forgot_password_url = home_url('/wp-login.php?action=lostpassword'); + } - if($login_page_id) { - $login_url = $mepr_options->login_page_url(); - $login_delim = MeprAppCtrl::get_param_delimiter_char($login_url); - $forgot_password_url = "{$login_url}{$login_delim}action=forgot_password"; - } - else { - $login_url = home_url('/wp-login.php'); - $forgot_password_url = home_url('/wp-login.php?action=lostpassword'); - } + if (MeprUtils::is_user_logged_in()) { + global $user_ID; - if(MeprUtils::is_user_logged_in()) { - global $user_ID; + $wp_user = get_user_by('id', $user_ID); - $wp_user = get_user_by('id', $user_ID); + // Need to override $redirect_to here if a per-membership login redirect URL is set (but do not track a login event) + $redirect_to = MeprProductsCtrl::track_and_override_login_redirect_mepr($redirect_to, $wp_user, true, false); + $redirect_to = urlencode($redirect_to); - //Need to override $redirect_to here if a per-membership login redirect URL is set (but do not track a login event) - $redirect_to = MeprProductsCtrl::track_and_override_login_redirect_mepr($redirect_to, $wp_user, true, false); - $redirect_to = urlencode($redirect_to); + if (MeprReadyLaunchCtrl::template_enabled('login')) { + MeprView::render('/readylaunch/login/form', get_defined_vars()); + } else { + MeprView::render('/login/form', get_defined_vars()); + } + return; + } - if(MeprReadyLaunchCtrl::template_enabled( 'login' )){ - MeprView::render('/readylaunch/login/form', get_defined_vars()); - } else { - MeprView::render('/login/form', get_defined_vars()); - } - return; - } + if (!empty($_REQUEST['mepr_process_login_form']) && !empty($_REQUEST['errors'])) { + $errors = array_map('wp_kses_post', $_REQUEST['errors']); + if (MeprReadyLaunchCtrl::template_enabled('login')) { + MeprView::render('/readylaunch/shared/errors', get_defined_vars()); + } else { + MeprView::render('/shared/errors', get_defined_vars()); + } + } - if(!empty($_REQUEST['mepr_process_login_form']) && !empty($_REQUEST['errors'])) { - $errors = array_map( 'wp_kses_post', $_REQUEST['errors'] ); - if(MeprReadyLaunchCtrl::template_enabled( 'login' )){ - MeprView::render('/readylaunch/shared/errors', get_defined_vars()); - } else { - MeprView::render('/shared/errors', get_defined_vars()); - } + if (MeprReadyLaunchCtrl::template_enabled('login') || MeprReadyLaunchCtrl::template_enabled('account') || MeprAppHelper::has_block('memberpress/pro-login-form')) { + MeprView::render('/readylaunch/login/form', get_defined_vars()); + } else { + MeprView::render('/login/form', get_defined_vars()); + } } - if(MeprReadyLaunchCtrl::template_enabled( 'login' ) || MeprReadyLaunchCtrl::template_enabled( 'account' ) || MeprAppHelper::has_block('memberpress/pro-login-form' )){ - MeprView::render('/readylaunch/login/form', get_defined_vars()); - } else { - MeprView::render('/login/form', get_defined_vars()); - } - } + // Processes the login form + public function process_login_form() + { + $mepr_options = MeprOptions::fetch(); - // Processes the login form - public function process_login_form() { - $mepr_options = MeprOptions::fetch(); + $errors = MeprHooks::apply_filters( + 'mepr-validate-login', + MeprUser::validate_login($_POST, []) + ); - $errors = MeprHooks::apply_filters( 'mepr-validate-login', - MeprUser::validate_login($_POST, array()) - ); + $login = stripcslashes(sanitize_text_field($_POST['log'])); // Have to do this for apostrophes in emails, cuz apparently that is a thing. - $login = stripcslashes(sanitize_text_field( $_POST['log'] )); //Have to do this for apostrophes in emails, cuz apparently that is a thing. + if (is_email($login)) { + $user = get_user_by('email', $login); - if(is_email($login)) { - $user = get_user_by('email', $login); + if ($user !== false) { + $login = $user->user_login; + } + } - if($user !== false) { - $login = $user->user_login; - } - } + if (!empty($errors)) { + $login_error = new WP_Error('mepr_login_failed', $errors[0]); + do_action('wp_login_failed', $login, $login_error); + $_REQUEST['errors'] = $errors; + return; + } - if(!empty($errors)) { - $login_error = new WP_Error('mepr_login_failed', $errors[0]); - do_action('wp_login_failed', $login, $login_error); - $_REQUEST['errors'] = $errors; - return; - } + if (!function_exists('wp_signon')) { + require_once(ABSPATH . WPINC . '/user.php'); + } - if(!function_exists('wp_signon')) { - require_once(ABSPATH . WPINC . '/user.php'); - } + $wp_user = wp_signon( + [ + 'user_login' => $login, + 'user_password' => $_POST['pwd'], // Do not need to sanitize here - it causes issues with passwords like test%12test (the %12 is stripped out) + 'remember' => isset($_POST['rememberme']), + ], + MeprUtils::is_ssl() // May help with the users getting logged out when going between http and https + ); - $wp_user = wp_signon( - array( - 'user_login' => $login, - 'user_password' => $_POST['pwd'], // Do not need to sanitize here - it causes issues with passwords like test%12test (the %12 is stripped out) - 'remember' => isset($_POST['rememberme']) - ), - MeprUtils::is_ssl() //May help with the users getting logged out when going between http and https - ); - - if(is_wp_error($wp_user)) { - $_REQUEST['errors'] = $wp_user->get_error_messages(); - return; - } + if (is_wp_error($wp_user)) { + $_REQUEST['errors'] = $wp_user->get_error_messages(); + return; + } - if(isset($_POST['redirect_to'])) { - $redirect_to = wp_sanitize_redirect( urldecode( $_POST['redirect_to'] ) ); - // Security fix. Restrict redirect_to param to safe URLs PT#154812459 - $redirect_to = wp_validate_redirect($redirect_to, apply_filters( 'wp_safe_redirect_fallback', home_url(), 302)); + if (isset($_POST['redirect_to'])) { + $redirect_to = wp_sanitize_redirect(urldecode($_POST['redirect_to'])); + // Security fix. Restrict redirect_to param to safe URLs PT#154812459 + $redirect_to = wp_validate_redirect($redirect_to, apply_filters('wp_safe_redirect_fallback', home_url(), 302)); + } else { + $redirect_to = $mepr_options->login_redirect_url; + } + $redirect_to = MeprHooks::apply_filters( + 'mepr-process-login-redirect-url', + $redirect_to, + $wp_user + ); + + MeprUtils::wp_redirect($redirect_to); } - else { - $redirect_to = $mepr_options->login_redirect_url; + + // Alters the default logout redirect + public function logout_redirect_override() + { + $mepr_options = MeprOptions::fetch(); + + if (isset($mepr_options->logout_redirect_url) && !empty($mepr_options->logout_redirect_url)) { + MeprUtils::wp_redirect(MeprHooks::apply_filters('mepr-process-logout-redirect-url', $mepr_options->logout_redirect_url)); + exit; + } } - $redirect_to = MeprHooks::apply_filters( - 'mepr-process-login-redirect-url', - $redirect_to, - $wp_user - ); - - MeprUtils::wp_redirect($redirect_to); - } - - // Alters the default logout redirect - public function logout_redirect_override() { - $mepr_options = MeprOptions::fetch(); - - if(isset($mepr_options->logout_redirect_url) && !empty($mepr_options->logout_redirect_url)) { - MeprUtils::wp_redirect(MeprHooks::apply_filters('mepr-process-logout-redirect-url', $mepr_options->logout_redirect_url)); - exit; + + // This needs to be done in init as before then it seems to cause conflicts with Shield Security plugin + public function override_wp_login_url_init() + { + add_filter('login_url', [$this, 'override_wp_login_url'], 999999, 2); } - } - //This needs to be done in init as before then it seems to cause conflicts with Shield Security plugin - public function override_wp_login_url_init() { - add_filter('login_url', array($this, 'override_wp_login_url'), 999999, 2); - } + // Override the default WordPress login url + public function override_wp_login_url($url, $redirect_to) + { + $mepr_options = MeprOptions::fetch(); + $redirect_to = urldecode($redirect_to); // might not be urlencoded, but let's do this just in case before we call urlencode below - // Override the default wordpress login url - public function override_wp_login_url($url, $redirect_to) { - $mepr_options = MeprOptions::fetch(); - $redirect_to = urldecode($redirect_to); // might not be urlencoded, but let's do this just in case before we call urlencode below + if (is_admin() || !$mepr_options->force_login_page_url || strpos($_SERVER['REQUEST_URI'], 'wp-login.php') !== false) { + return $url; + } - if(is_admin() || !$mepr_options->force_login_page_url || strpos($_SERVER['REQUEST_URI'], 'wp-login.php') !== false) { - return $url; - } + if (!empty($redirect_to)) { + $new_login_url = $mepr_options->login_page_url('redirect_to=' . urlencode($redirect_to)); + } else { + $new_login_url = $mepr_options->login_page_url(); + } - if(!empty($redirect_to)) { - $new_login_url = $mepr_options->login_page_url('redirect_to=' . urlencode($redirect_to)); - } - else { - $new_login_url = $mepr_options->login_page_url(); + return $new_login_url; } - return $new_login_url; - } + public function display_forgot_password_form() + { + $mepr_options = MeprOptions::fetch(); + $mepr_blogurl = home_url(); - public function display_forgot_password_form() { - $mepr_options = MeprOptions::fetch(); - $mepr_blogurl = home_url(); + $mepr_user_or_email = (isset($_REQUEST['user_or_email'])) ? sanitize_text_field(urldecode($_REQUEST['user_or_email'])) : ''; - $mepr_user_or_email = (isset($_REQUEST['user_or_email'])) ? sanitize_text_field(urldecode($_REQUEST['user_or_email'])) : ''; + $process = MeprAppCtrl::get_param('mepr_process_forgot_password_form', ''); - $process = MeprAppCtrl::get_param('mepr_process_forgot_password_form',''); - - if(empty($process)) { - if(MeprReadyLaunchCtrl::template_enabled( 'login' )){ - MeprView::render('/readylaunch/login/forgot_password', get_defined_vars()); - } else { - MeprView::render('/login/forgot_password', get_defined_vars()); - } - } - else { - $this->process_forgot_password_form(); + if (empty($process)) { + if (MeprReadyLaunchCtrl::template_enabled('login')) { + MeprView::render('/readylaunch/login/forgot_password', get_defined_vars()); + } else { + MeprView::render('/login/forgot_password', get_defined_vars()); + } + } else { + $this->process_forgot_password_form(); + } } - } - public function process_forgot_password_form() { - $mepr_options = MeprOptions::fetch(); - $errors = MeprHooks::apply_filters('mepr-validate-forgot-password', MeprUser::validate_forgot_password($_POST, array())); + public function process_forgot_password_form() + { + $mepr_options = MeprOptions::fetch(); + $errors = MeprHooks::apply_filters('mepr-validate-forgot-password', MeprUser::validate_forgot_password($_POST, [])); - extract($_POST, EXTR_SKIP); + extract($_POST, EXTR_SKIP); - $mepr_user_or_email = wp_unslash($mepr_user_or_email); + $mepr_user_or_email = wp_unslash($mepr_user_or_email); - if(empty($errors)) { - $is_email = (is_email($mepr_user_or_email) and email_exists($mepr_user_or_email)); - $is_username = username_exists($mepr_user_or_email); + if (empty($errors)) { + $is_email = (is_email($mepr_user_or_email) and email_exists($mepr_user_or_email)); + $is_username = username_exists($mepr_user_or_email); - // If the username & email are not found then let's display a generic message. - if(!$is_email && !$is_username) { - if(MeprReadyLaunchCtrl::template_enabled( 'login' )){ - MeprView::render('/readylaunch/login/forgot_password_requested', get_defined_vars()); - } else { - MeprView::render('/login/forgot_password_requested', get_defined_vars()); - } - return; - } + // If the username & email are not found then let's display a generic message. + if (!$is_email && !$is_username) { + if (MeprReadyLaunchCtrl::template_enabled('login')) { + MeprView::render('/readylaunch/login/forgot_password_requested', get_defined_vars()); + } else { + MeprView::render('/login/forgot_password_requested', get_defined_vars()); + } + return; + } - $user = new MeprUser(); + $user = new MeprUser(); - // If the username & email are identical then let's rely on it as a username first and foremost - if($is_username) { - $user->load_user_data_by_login($mepr_user_or_email); - } - else if($is_email) { - $user->load_user_data_by_email($mepr_user_or_email); - } + // If the username & email are identical then let's rely on it as a username first and foremost + if ($is_username) { + $user->load_user_data_by_login($mepr_user_or_email); + } elseif ($is_email) { + $user->load_user_data_by_email($mepr_user_or_email); + } - if($user->ID) { - $user->send_password_notification('reset'); + if ($user->ID) { + $user->send_password_notification('reset'); - if(MeprReadyLaunchCtrl::template_enabled( 'login' )){ - MeprView::render('/readylaunch/login/forgot_password_requested', get_defined_vars()); + if (MeprReadyLaunchCtrl::template_enabled('login')) { + MeprView::render('/readylaunch/login/forgot_password_requested', get_defined_vars()); + } else { + MeprView::render('/login/forgot_password_requested', get_defined_vars()); + } + } else { + MeprView::render('/shared/unknown_error', get_defined_vars()); + } } else { - MeprView::render('/login/forgot_password_requested', get_defined_vars()); + if (MeprReadyLaunchCtrl::template_enabled('login')) { + MeprView::render('/readylaunch/shared/errors', get_defined_vars()); + MeprView::render('/readylaunch/login/forgot_password', get_defined_vars()); + } else { + MeprView::render('/shared/errors', get_defined_vars()); + MeprView::render('/login/forgot_password', get_defined_vars()); + } } - } - else { - MeprView::render('/shared/unknown_error', get_defined_vars()); - } - } - else { - if(MeprReadyLaunchCtrl::template_enabled( 'login' )){ - MeprView::render('/readylaunch/shared/errors', get_defined_vars()); - MeprView::render('/readylaunch/login/forgot_password', get_defined_vars()); - } else { - MeprView::render('/shared/errors', get_defined_vars()); - MeprView::render('/login/forgot_password', get_defined_vars()); - } } - } - - public function display_reset_password_form($mepr_key, $mepr_screenname) { - $user = new MeprUser(); - $user->load_user_data_by_login($mepr_screenname); - - $is_key_valid = $user->reset_form_key_is_valid($mepr_key); - if($user->ID && $is_key_valid) { - if(MeprReadyLaunchCtrl::template_enabled( 'login' )){ - MeprView::render('/readylaunch/login/reset_password', get_defined_vars()); - } else { - MeprView::render('/login/reset_password', get_defined_vars()); - } - } - elseif($user->ID && ($user->reset_form_key_has_expired($mepr_key) || ! $is_key_valid)) { - if(MeprReadyLaunchCtrl::template_enabled( 'login' )){ - MeprView::render('/readylaunch/shared/expired_password_reset', get_defined_vars()); - } else { - MeprView::render('/shared/expired_password_reset', get_defined_vars()); - } - } - else { - $mepr_options = MeprOptions::fetch(); - if(MeprReadyLaunchCtrl::template_enabled( 'login' )){ - MeprView::render('/readylaunch/shared/unauthorized', get_defined_vars()); - } else { - MeprView::render('/shared/unauthorized', get_defined_vars()); - } - } - } - - public function display_reset_password_form_errors($errors) { - if(!empty($errors)) { - $mepr_screenname = isset($_POST['mepr_screenname']) ? sanitize_user(wp_unslash($_POST['mepr_screenname'])) : ''; - $mepr_key = isset($_POST['mepr_key']) ? wp_unslash($_POST['mepr_key']) : ''; - - if(MeprReadyLaunchCtrl::template_enabled('login')) { - MeprView::render('/readylaunch/shared/errors', get_defined_vars()); - MeprView::render('/readylaunch/login/reset_password', get_defined_vars()); - } - else { - MeprView::render('/shared/errors', get_defined_vars()); - MeprView::render('/login/reset_password', get_defined_vars()); - } - } - } - - public function process_reset_password_form() { - // Log user out when clicking reset password link - if(MeprUtils::is_user_logged_in()) { - if(isset($_GET['action']) && $_GET['action'] == 'reset_password' && isset($_GET['mkey']) && !isset($_GET['loggedout'])) { - wp_destroy_current_session(); - wp_clear_auth_cookie(); - MeprUtils::wp_redirect($_SERVER['REQUEST_URI'] . '&loggedout=true'); // redirect to same page to flush login cookies - } + + public function display_reset_password_form($mepr_key, $mepr_screenname) + { + $user = new MeprUser(); + $user->load_user_data_by_login($mepr_screenname); + + $is_key_valid = $user->reset_form_key_is_valid($mepr_key); + if ($user->ID && $is_key_valid) { + if (MeprReadyLaunchCtrl::template_enabled('login')) { + MeprView::render('/readylaunch/login/reset_password', get_defined_vars()); + } else { + MeprView::render('/login/reset_password', get_defined_vars()); + } + } elseif ($user->ID && ($user->reset_form_key_has_expired($mepr_key) || ! $is_key_valid)) { + if (MeprReadyLaunchCtrl::template_enabled('login')) { + MeprView::render('/readylaunch/shared/expired_password_reset', get_defined_vars()); + } else { + MeprView::render('/shared/expired_password_reset', get_defined_vars()); + } + } else { + $mepr_options = MeprOptions::fetch(); + if (MeprReadyLaunchCtrl::template_enabled('login')) { + MeprView::render('/readylaunch/shared/unauthorized', get_defined_vars()); + } else { + MeprView::render('/shared/unauthorized', get_defined_vars()); + } + } } - if(isset($_POST['action']) && $_POST['action'] === 'mepr_process_reset_password_form') { - $mepr_options = MeprOptions::fetch(); + public function display_reset_password_form_errors($errors) + { + if (!empty($errors)) { + $mepr_screenname = isset($_POST['mepr_screenname']) ? sanitize_user(wp_unslash($_POST['mepr_screenname'])) : ''; + $mepr_key = isset($_POST['mepr_key']) ? wp_unslash($_POST['mepr_key']) : ''; - if(isset($_POST['errors'])) { - $errors = $_POST['errors']; - } - else { - $errors = $_POST['errors'] = MeprHooks::apply_filters('mepr-validate-reset-password', MeprUser::validate_reset_password($_POST, array())); - } + if (MeprReadyLaunchCtrl::template_enabled('login')) { + MeprView::render('/readylaunch/shared/errors', get_defined_vars()); + MeprView::render('/readylaunch/login/reset_password', get_defined_vars()); + } else { + MeprView::render('/shared/errors', get_defined_vars()); + MeprView::render('/login/reset_password', get_defined_vars()); + } + } + } - if(empty($errors)) { - $mepr_screenname = isset($_POST['mepr_screenname']) ? sanitize_user(wp_unslash($_POST['mepr_screenname'])) : ''; - $mepr_user_password = isset($_POST['mepr_user_password']) ? $_POST['mepr_user_password'] : ''; - $mepr_key = isset($_POST['mepr_key']) ? wp_unslash($_POST['mepr_key']) : ''; + public function process_reset_password_form() + { + // Log user out when clicking reset password link + if (MeprUtils::is_user_logged_in()) { + if (isset($_GET['action']) && $_GET['action'] == 'reset_password' && isset($_GET['mkey']) && !isset($_GET['loggedout'])) { + wp_destroy_current_session(); + wp_clear_auth_cookie(); + MeprUtils::wp_redirect($_SERVER['REQUEST_URI'] . '&loggedout=true'); // redirect to same page to flush login cookies + } + } - $user = new MeprUser(); - $user->load_user_data_by_login($mepr_screenname); + if (isset($_POST['action']) && $_POST['action'] === 'mepr_process_reset_password_form') { + $mepr_options = MeprOptions::fetch(); - if($user->ID) { - $user->set_password_and_send_notifications($mepr_key, $mepr_user_password); - - if(MeprHooks::apply_filters('mepr-auto-login', true, null, $user)) { - if(!MeprUtils::is_user_logged_in()) { - $wp_user = wp_signon( - array( - 'user_login' => $mepr_screenname, - 'user_password' => $mepr_user_password, - ), - MeprUtils::is_ssl() - ); - - if(!is_wp_error($wp_user)) { - $redirect_to = $mepr_options->login_redirect_url; - $redirect_to = MeprHooks::apply_filters( - 'mepr-process-login-redirect-url', - $redirect_to, - $wp_user - ); + if (isset($_POST['errors'])) { + $errors = $_POST['errors']; + } else { + $errors = $_POST['errors'] = MeprHooks::apply_filters('mepr-validate-reset-password', MeprUser::validate_reset_password($_POST, [])); + } - MeprUtils::wp_redirect($redirect_to); - } - else { - $_POST['errors'] = array($wp_user->get_error_message()); - } + if (empty($errors)) { + $mepr_screenname = isset($_POST['mepr_screenname']) ? sanitize_user(wp_unslash($_POST['mepr_screenname'])) : ''; + $mepr_user_password = isset($_POST['mepr_user_password']) ? $_POST['mepr_user_password'] : ''; + $mepr_key = isset($_POST['mepr_key']) ? wp_unslash($_POST['mepr_key']) : ''; + + $user = new MeprUser(); + $user->load_user_data_by_login($mepr_screenname); + + if ($user->ID) { + $user->set_password_and_send_notifications($mepr_key, $mepr_user_password); + + if (MeprHooks::apply_filters('mepr-auto-login', true, null, $user)) { + if (!MeprUtils::is_user_logged_in()) { + $wp_user = wp_signon( + [ + 'user_login' => $mepr_screenname, + 'user_password' => $mepr_user_password, + ], + MeprUtils::is_ssl() + ); + + if (!is_wp_error($wp_user)) { + $redirect_to = $mepr_options->login_redirect_url; + $redirect_to = MeprHooks::apply_filters( + 'mepr-process-login-redirect-url', + $redirect_to, + $wp_user + ); + + MeprUtils::wp_redirect($redirect_to); + } else { + $_POST['errors'] = [$wp_user->get_error_message()]; + } + } + } + } else { + $_POST['errors'] = [__('An Unknown Error Occurred', 'memberpress')]; + } + } else { + $_POST['errors'] = $errors; } - } - } - else { - $_POST['errors'] = array(__('An Unknown Error Occurred', 'memberpress')); } - } - else { - $_POST['errors'] = $errors; - } } - } } diff --git a/app/controllers/MeprMembersCtrl.php b/app/controllers/MeprMembersCtrl.php index ef29305..fb81560 100644 --- a/app/controllers/MeprMembersCtrl.php +++ b/app/controllers/MeprMembersCtrl.php @@ -1,404 +1,425 @@ MeprUtils::hours(6), // Run four times a day - 'display' => __('MemberPress Member Data Update Interval', 'memberpress'), - ); - - return $schedules; - } - - public function updater() { - MeprUtils::debug_log('Start Updating Missing Members'); - MeprUser::update_all_member_data(true,100); - MeprUtils::debug_log('End Updating Missing Members'); - - //MeprUtils::debug_log('Start Updating Existing Member Data'); - //MeprUser::update_existing_member_data(100); - //MeprUtils::debug_log('End Updating Existing Member Data'); - } - - public function enqueue_scripts($hook) { - $wp_scripts = new WP_Scripts(); - $ui = $wp_scripts->query('jquery-ui-core'); - $url = "//ajax.googleapis.com/ajax/libs/jqueryui/{$ui->ver}/themes/smoothness/jquery-ui.css"; - - if($hook == 'memberpress_page_memberpress-members' || $hook == 'memberpress_page_memberpress-new-member') { - wp_register_script('mepr-table-controls-js', MEPR_JS_URL.'/table_controls.js', array('jquery'), MEPR_VERSION); - wp_register_script('mepr-timepicker-js', MEPR_JS_URL.'/jquery-ui-timepicker-addon.js', array('jquery-ui-datepicker')); - wp_register_script('mepr-date-picker-js', MEPR_JS_URL.'/date_picker.js', array('mepr-timepicker-js'), MEPR_VERSION); - wp_register_script('mphelpers', MEPR_JS_URL.'/mphelpers.js', array('suggest'), MEPR_VERSION); - wp_enqueue_script( - 'mepr-members-js', - MEPR_JS_URL.'/admin_members.js', - array('mepr-table-controls-js','jquery','mphelpers','mepr-date-picker-js','mepr-settings-table-js'), - MEPR_VERSION - ); - - wp_register_style('mepr-jquery-ui-smoothness', $url); - wp_register_style('jquery-ui-timepicker-addon', MEPR_CSS_URL.'/jquery-ui-timepicker-addon.css', array('mepr-jquery-ui-smoothness')); - wp_enqueue_style('mepr-members-css', MEPR_CSS_URL.'/admin-members.css', array('mepr-settings-table-css','jquery-ui-timepicker-addon'), MEPR_VERSION); + if (!($member_data_timestamp = wp_next_scheduled('mepr_member_data_updater_worker'))) { + wp_schedule_event(time() + MeprUtils::hours(6), 'mepr_member_data_updater_interval', 'mepr_member_data_updater_worker'); + } + // else { + // wp_unschedule_event($member_data_timestamp, 'mepr_member_data_updater_worker'); + // } } - } - public function listing() { - $action = (isset($_REQUEST['action']) && !empty($_REQUEST['action']))?$_REQUEST['action']:false; - if($action == 'new') { - $this->new_member(); - } - else if(MeprUtils::is_post_request() && $action == 'create') { - $this->create_member(); - } - else { - $this->display_list(); - } - } - - /* This is here to use wherever we want. */ - public function get_columns() { - $cols = array( - 'col_id' => __('Id', 'memberpress'), - //'col_photo' => __('Photo'), - 'col_username' => __('Username', 'memberpress'), - 'col_email' => __('Email', 'memberpress'), - 'col_status' => __('Status', 'memberpress'), - 'col_name' => __('Name', 'memberpress'), - 'col_sub_info' => __('Subscriptions', 'memberpress'), - 'col_txn_info' => __('Transactions', 'memberpress'), - // 'col_info' => __('Info', 'memberpress'), - // 'col_txn_count' => __('Transactions', 'memberpress'), - //'col_expired_txn_count' => __('Expired Transactions'), - //'col_active_txn_count' => __('Active Transactions'), - //'col_sub_count' => __('Subscriptions', 'memberpress'), - //'col_pending_sub_count' => __('Pending Subscriptions'), - //'col_active_sub_count' => __('Enabled Subscriptions'), - //'col_suspended_sub_count' => __('Paused Subscriptions'), - //'col_cancelled_sub_count' => __('Stopped Subscriptions'), - 'col_memberships' => __('Memberships', 'memberpress'), - 'col_inactive_memberships' => __('Inactive Memberships', 'memberpress'), - 'col_last_login_date' => __('Last Login', 'memberpress'), - 'col_login_count' => __('Logins', 'memberpress'), - 'col_total_spent' => __('Value', 'memberpress'), - 'col_registered' => __('Registered', 'memberpress') - ); - - return MeprHooks::apply_filters('mepr-admin-members-cols', $cols); - } - - public function display_list($message='', $errors=array()) { - $screen = get_current_screen(); - - $list_table = new MeprMembersTable( $screen, $this->get_columns() ); - $list_table->prepare_items(); - - MeprView::render('/admin/members/list', compact('message','list_table')); - } - - public function new_member($member=null,$transaction=null,$errors='',$message='') { - $mepr_options = MeprOptions::fetch(); - - if(empty($member)) { - $member = new MeprUser(); - $member->send_notification = true; - $member->password = wp_generate_password(24); + public function intervals($schedules) + { + $schedules['mepr_member_data_updater_interval'] = [ + 'interval' => MeprUtils::hours(6), // Run four times a day + 'display' => __('MemberPress Member Data Update Interval', 'memberpress'), + ]; + + return $schedules; } - if(empty($transaction)) { - $transaction = new MeprTransaction(); - $transaction->status = MeprTransaction::$complete_str; // Default this to complete in this case - $transaction->send_welcome = true; + public function updater() + { + MeprUtils::debug_log('Start Updating Missing Members'); + MeprUser::update_all_member_data(true, 100); + MeprUtils::debug_log('End Updating Missing Members'); + + // MeprUtils::debug_log('Start Updating Existing Member Data'); + // MeprUser::update_existing_member_data(100); + // MeprUtils::debug_log('End Updating Existing Member Data'); } - MeprView::render('/admin/members/new_member', compact('mepr_options','member','transaction','errors','message')); - } + public function enqueue_scripts($hook) + { + $wp_scripts = new WP_Scripts(); + $ui = $wp_scripts->query('jquery-ui-core'); + $url = "//ajax.googleapis.com/ajax/libs/jqueryui/{$ui->ver}/themes/smoothness/jquery-ui.css"; + + if ($hook == 'memberpress_page_memberpress-members' || $hook == 'memberpress_page_memberpress-new-member') { + wp_register_script('mepr-table-controls-js', MEPR_JS_URL . '/table_controls.js', ['jquery'], MEPR_VERSION); + wp_register_script('mepr-timepicker-js', MEPR_JS_URL . '/jquery-ui-timepicker-addon.js', ['jquery-ui-datepicker']); + wp_register_script('mepr-date-picker-js', MEPR_JS_URL . '/date_picker.js', ['mepr-timepicker-js'], MEPR_VERSION); + wp_register_script('mphelpers', MEPR_JS_URL . '/mphelpers.js', ['suggest'], MEPR_VERSION); + wp_enqueue_script( + 'mepr-members-js', + MEPR_JS_URL . '/admin_members.js', + ['mepr-table-controls-js','jquery','mphelpers','mepr-date-picker-js','mepr-settings-table-js'], + MEPR_VERSION + ); + + wp_register_style('mepr-jquery-ui-smoothness', $url); + wp_register_style('jquery-ui-timepicker-addon', MEPR_CSS_URL . '/jquery-ui-timepicker-addon.css', ['mepr-jquery-ui-smoothness']); + wp_enqueue_style('mepr-members-css', MEPR_CSS_URL . '/admin-members.css', ['mepr-settings-table-css','jquery-ui-timepicker-addon'], MEPR_VERSION); + } + } - public function create_member() { - check_admin_referer('mepr_create_member', 'mepr_members_nonce'); + public function listing() + { + $action = (isset($_REQUEST['action']) && !empty($_REQUEST['action'])) ? $_REQUEST['action'] : false; + if ($action == 'new') { + $this->new_member(); + } elseif (MeprUtils::is_post_request() && $action == 'create') { + $this->create_member(); + } else { + $this->display_list(); + } + } - $mepr_options = MeprOptions::fetch(); - $errors = $this->validate_new_member(); - $message = ''; + /* This is here to use wherever we want. */ + public function get_columns() + { + $cols = [ + 'col_id' => __('Id', 'memberpress'), + // 'col_photo' => __('Photo'), + 'col_username' => __('Username', 'memberpress'), + 'col_email' => __('Email', 'memberpress'), + 'col_status' => __('Status', 'memberpress'), + 'col_name' => __('Name', 'memberpress'), + 'col_sub_info' => __('Subscriptions', 'memberpress'), + 'col_txn_info' => __('Transactions', 'memberpress'), + // 'col_info' => __('Info', 'memberpress'), + // 'col_txn_count' => __('Transactions', 'memberpress'), + // 'col_expired_txn_count' => __('Expired Transactions'), + // 'col_active_txn_count' => __('Active Transactions'), + // 'col_sub_count' => __('Subscriptions', 'memberpress'), + // 'col_pending_sub_count' => __('Pending Subscriptions'), + // 'col_active_sub_count' => __('Enabled Subscriptions'), + // 'col_suspended_sub_count' => __('Paused Subscriptions'), + // 'col_cancelled_sub_count' => __('Stopped Subscriptions'), + 'col_memberships' => __('Memberships', 'memberpress'), + 'col_inactive_memberships' => __('Inactive Memberships', 'memberpress'), + 'col_last_login_date' => __('Last Login', 'memberpress'), + 'col_login_count' => __('Logins', 'memberpress'), + 'col_total_spent' => __('Value', 'memberpress'), + 'col_registered' => __('Registered', 'memberpress'), + ]; + + return MeprHooks::apply_filters('mepr-admin-members-cols', $cols); + } - $member = new MeprUser(); - $member->load_from_array($_POST['member']); - $member->send_notification = isset($_POST['member']['send_notification']); + public function display_list($message = '', $errors = []) + { + $screen = get_current_screen(); - // Just here in case things fail so we can show the same password when the new_member page is re-displayed - $member->password = $_POST['member']['user_pass']; - $member->user_email = sanitize_email($_POST['member']['user_email']); + $list_table = new MeprMembersTable($screen, $this->get_columns()); + $list_table->prepare_items(); - $transaction = new MeprTransaction(); - $transaction->load_from_array($_POST['transaction']); - $transaction->send_welcome = isset($_POST['transaction']['send_welcome']); - $_POST['transaction']['amount'] = MeprUtils::format_currency_us_float($_POST['transaction']['amount']); //Don't forget this, or the members page and emails will have $0.00 for amounts - if($transaction->total <= 0) { - $transaction->total = $_POST['transaction']['amount']; //Don't forget this, or the members page and emails will have $0.00 for amounts + MeprView::render('/admin/members/list', compact('message', 'list_table')); } - if(count($errors) <= 0) { - try { - $member->set_password($_POST['member']['user_pass']); - $member->store(); + public function new_member($member = null, $transaction = null, $errors = '', $message = '') + { + $mepr_options = MeprOptions::fetch(); - // Needed for autoresponders - call before storing txn - MeprHooks::do_action('mepr-signup-user-loaded', $member); + if (empty($member)) { + $member = new MeprUser(); + $member->send_notification = true; + $member->password = wp_generate_password(24); + } - if($member->send_notification) { - $member->send_password_notification('new'); + if (empty($transaction)) { + $transaction = new MeprTransaction(); + $transaction->status = MeprTransaction::$complete_str; // Default this to complete in this case + $transaction->send_welcome = true; } - $transaction->user_id = $member->ID; - $transaction->store(); + MeprView::render('/admin/members/new_member', compact('mepr_options', 'member', 'transaction', 'errors', 'message')); + } - //Trigger the right events here yo - MeprEvent::record('transaction-completed', $transaction); - MeprEvent::record('non-recurring-transaction-completed', $transaction); + public function create_member() + { + check_admin_referer('mepr_create_member', 'mepr_members_nonce'); - //Run the signup hooks - MeprHooks::do_action("mepr-non-recurring-signup", $transaction); - MeprHooks::do_action('mepr-signup', $transaction); + $mepr_options = MeprOptions::fetch(); + $errors = $this->validate_new_member(); + $message = ''; - if($transaction->send_welcome) { - MeprUtils::send_signup_notices($transaction); - } - else { //Trigger the event for this yo, as it's normally triggered in send_signup_notices - MeprEvent::record('member-signup-completed', $member, (object)$transaction->rec); //have to use ->rec here for some reason - } + $member = new MeprUser(); + $member->load_from_array($_POST['member']); + $member->send_notification = isset($_POST['member']['send_notification']); - $message = __('Your new member was created successfully.', 'memberpress'); + // Just here in case things fail so we can show the same password when the new_member page is re-displayed + $member->password = $_POST['member']['user_pass']; + $member->user_email = sanitize_email($_POST['member']['user_email']); - return $this->display_list($message); - } - catch(Exception $e) { - $errors[] = $e->getMessage(); - } - } + $transaction = new MeprTransaction(); + $transaction->load_from_array($_POST['transaction']); + $transaction->send_welcome = isset($_POST['transaction']['send_welcome']); + $_POST['transaction']['amount'] = MeprUtils::format_currency_us_float($_POST['transaction']['amount']); // Don't forget this, or the members page and emails will have $0.00 for amounts + if ($transaction->total <= 0) { + $transaction->total = $_POST['transaction']['amount']; // Don't forget this, or the members page and emails will have $0.00 for amounts + } - $this->new_member($member,$transaction,$errors,$message); - } + if (count($errors) <= 0) { + try { + $member->set_password($_POST['member']['user_pass']); + $member->store(); - public function add_screen_options() { - add_screen_option('layout_columns'); + // Needed for autoresponders - call before storing txn + MeprHooks::do_action('mepr-signup-user-loaded', $member); - $option = 'per_page'; + if ($member->send_notification) { + $member->send_password_notification('new'); + } - $args = array( - 'label' => __('Members', 'memberpress'), - 'default' => 10, - 'option' => 'mp_members_perpage' - ); + $transaction->user_id = $member->ID; + $transaction->store(); - add_screen_option( $option, $args ); - } + // Trigger the right events here yo + MeprEvent::record('transaction-completed', $transaction); + MeprEvent::record('non-recurring-transaction-completed', $transaction); - public function setup_screen_options($status, $option, $value) { - if('mp_members_perpage' === $option) { - return $value; - } + // Run the signup hooks + MeprHooks::do_action('mepr-non-recurring-signup', $transaction); + MeprHooks::do_action('mepr-signup', $transaction); - return $status; - } + if ($transaction->send_welcome) { + MeprUtils::send_signup_notices($transaction); + } else { // Trigger the event for this yo, as it's normally triggered in send_signup_notices + MeprEvent::record('member-signup-completed', $member, (object)$transaction->rec); // have to use ->rec here for some reason + } - // This is purely for performance ... we don't want to do these queries during a listing - public function update_txn_meta($txn, $sub_status = false) { - $u = $txn->user(); - $u->update_member_data(); - } + $message = __('Your new member was created successfully.', 'memberpress'); - public function update_event_meta($evt) { - if($evt->evt_id_type === MeprEvent::$users_str && $evt->event === MeprEvent::$login_event_str) { - $u = $evt->get_data(); - $u->update_member_data(); - } - } + return $this->display_list($message); + } catch (Exception $e) { + $errors[] = $e->getMessage(); + } + } - public function update_member_meta($user_id) { - $u = new MeprUser($user_id); - $u->update_member_data(); - } + $this->new_member($member, $transaction, $errors, $message); + } - public function update_member_data_from_subscription($subscription) { - $member = $subscription->user(); - $member->update_member_data(); - } + public function add_screen_options() + { + add_screen_option('layout_columns'); - public function delete_member_meta($user_id) { - $u = new MeprUser($user_id); - $u->delete_member_data(); - } + $option = 'per_page'; - public function validate_new_member() { - $errors = array(); - $usr = new MeprUser(); + $args = [ + 'label' => __('Members', 'memberpress'), + 'default' => 10, + 'option' => 'mp_members_perpage', + ]; - if( !isset($_POST['member']['user_login']) || empty($_POST['member']['user_login']) ) { - $errors[] = __('The username field can\'t be blank.', 'memberpress'); + add_screen_option($option, $args); } - if( username_exists($_POST['member']['user_login']) ) { - $errors[] = __('This username is already taken.', 'memberpress'); - } + public function setup_screen_options($status, $option, $value) + { + if ('mp_members_perpage' === $option) { + return $value; + } - if( !validate_username($_POST['member']['user_login']) ) { - $errors[] = __('The username must be valid.', 'memberpress'); + return $status; } - if( !isset($_POST['member']['user_email']) || empty($_POST['member']['user_email']) ) { - $errors[] = __('The email field can\'t be blank.', 'memberpress'); + // This is purely for performance ... we don't want to do these queries during a listing + public function update_txn_meta($txn, $sub_status = false) + { + $u = $txn->user(); + $u->update_member_data(); } - if( email_exists($_POST['member']['user_email']) ) { - $errors[] = __('This email is already being used by another user.', 'memberpress'); + public function update_event_meta($evt) + { + if ($evt->evt_id_type === MeprEvent::$users_str && $evt->event === MeprEvent::$login_event_str) { + $u = $evt->get_data(); + $u->update_member_data(); + } } - if( !is_email(stripslashes($_POST['member']['user_email'])) ) { - $errors[] = __('A valid email must be entered.', 'memberpress'); + public function update_member_meta($user_id) + { + $u = new MeprUser($user_id); + $u->update_member_data(); } - // Simple validation here - if(!isset($_POST['transaction']['amount']) || empty($_POST['transaction']['amount'])) { - $errors[] = __('The transaction amount must be set.', 'memberpress'); + public function update_member_data_from_subscription($subscription) + { + $member = $subscription->user(); + $member->update_member_data(); } - if( preg_match("/[^0-9., ]/", $_POST['transaction']['amount']) ) { - $errors[] = __('The transaction amount must be a number.', 'memberpress'); + public function delete_member_meta($user_id) + { + $u = new MeprUser($user_id); + $u->delete_member_data(); } - if(empty($_POST['transaction']['trans_num']) || preg_match('#[^a-zA-z0-9_\-]#', $_POST['transaction']['trans_num'])) { - $errors[] = __('The Transaction Number is required, and must contain only letters, numbers, underscores and hyphens.', 'memberpress'); - } + public function validate_new_member() + { + $errors = []; + $usr = new MeprUser(); - return $errors; - } + if (!isset($_POST['member']['user_login']) || empty($_POST['member']['user_login'])) { + $errors[] = __('The username field can\'t be blank.', 'memberpress'); + } - public function table_search_box() { - if(isset($_REQUEST['page']) && $_REQUEST['page']=='memberpress-members') { - $membership = (isset($_REQUEST['membership'])?$_REQUEST['membership']:false); - $status = (isset($_REQUEST['status'])?$_REQUEST['status']:'all'); - $prds = MeprCptModel::all('MeprProduct', false, array('orderby' => 'title', 'order' => 'ASC')); - MeprView::render('/admin/members/search_box', compact('membership','status','prds')); - } - } + if (username_exists($_POST['member']['user_login'])) { + $errors[] = __('This username is already taken.', 'memberpress'); + } + + if (!validate_username($_POST['member']['user_login'])) { + $errors[] = __('The username must be valid.', 'memberpress'); + } - public function csv() { - check_ajax_referer('export_members', 'mepr_members_nonce'); + if (!isset($_POST['member']['user_email']) || empty($_POST['member']['user_email'])) { + $errors[] = __('The email field can\'t be blank.', 'memberpress'); + } - $filename = 'members-'.time(); + if (email_exists($_POST['member']['user_email'])) { + $errors[] = __('This email is already being used by another user.', 'memberpress'); + } - // Since we're running WP_List_Table headless we need to do this - $GLOBALS['hook_suffix'] = false; + if (!is_email(stripslashes($_POST['member']['user_email']))) { + $errors[] = __('A valid email must be entered.', 'memberpress'); + } - $screen = get_current_screen(); - $tab = new MeprMembersTable( $screen, $this->get_columns() ); + // Simple validation here + if (!isset($_POST['transaction']['amount']) || empty($_POST['transaction']['amount'])) { + $errors[] = __('The transaction amount must be set.', 'memberpress'); + } - if(isset($_REQUEST['all']) && !empty($_REQUEST['all'])) { - $search = isset($_REQUEST["search"]) && !empty($_REQUEST["search"]) ? esc_sql($_REQUEST["search"]) : ''; - $search_field = isset($_REQUEST["search"]) && !empty($_REQUEST["search-field"]) ? esc_sql($_REQUEST["search-field"]) : 'any'; - $search_field = isset($tab->db_search_cols[$search_field]) ? $tab->db_search_cols[$search_field] : 'any'; + if (preg_match('/[^0-9., ]/', $_POST['transaction']['amount'])) { + $errors[] = __('The transaction amount must be a number.', 'memberpress'); + } - $all = MeprUser::list_table( - /* $order_by */ 'user_login', - /* $order */ 'ASC', - /* $paged */ '', - /* $search */ $search, - /* $search_field */ $search_field, - /* $perpage */ '', - /* $params */ $_REQUEST, - /* $include_fields */ true - ); + if (empty($_POST['transaction']['trans_num']) || preg_match('#[^a-zA-z0-9_\-]#', $_POST['transaction']['trans_num'])) { + $errors[] = __('The Transaction Number is required, and must contain only letters, numbers, underscores and hyphens.', 'memberpress'); + } - add_filter('mepr_process_csv_cell', array($this,'process_custom_field'), 10, 2); - MeprUtils::render_csv($all['results'], $filename); + return $errors; } - else { - $tab->prepare_items(); - MeprUtils::render_csv( $tab->get_items(), $filename ); + + public function table_search_box() + { + if (isset($_REQUEST['page']) && $_REQUEST['page'] == 'memberpress-members') { + $membership = (isset($_REQUEST['membership']) ? $_REQUEST['membership'] : false); + $status = (isset($_REQUEST['status']) ? $_REQUEST['status'] : 'all'); + $prds = MeprCptModel::all('MeprProduct', false, [ + 'orderby' => 'title', + 'order' => 'ASC', + ]); + MeprView::render('/admin/members/search_box', compact('membership', 'status', 'prds')); + } } - } - public function process_custom_field($field, $label) { - $mepr_options = MeprOptions::fetch(); + public function csv() + { + check_ajax_referer('export_members', 'mepr_members_nonce'); + + $filename = 'members-' . time(); + + // Since we're running WP_List_Table headless we need to do this + $GLOBALS['hook_suffix'] = false; + + $screen = get_current_screen(); + $tab = new MeprMembersTable($screen, $this->get_columns()); + + if (isset($_REQUEST['all']) && !empty($_REQUEST['all'])) { + $search = isset($_REQUEST['search']) && !empty($_REQUEST['search']) ? esc_sql($_REQUEST['search']) : ''; + $search_field = isset($_REQUEST['search']) && !empty($_REQUEST['search-field']) ? esc_sql($_REQUEST['search-field']) : 'any'; + $search_field = isset($tab->db_search_cols[$search_field]) ? $tab->db_search_cols[$search_field] : 'any'; + + $all = MeprUser::list_table( + /* $order_by */ 'user_login', + /* $order */ 'ASC', + /* $paged */ '', + /* $search */ $search, + /* $search_field */ $search_field, + /* $perpage */ '', + /* $params */ $_REQUEST, + /* $include_fields */ true + ); + + add_filter('mepr_process_csv_cell', [$this,'process_custom_field'], 10, 2); + MeprUtils::render_csv($all['results'], $filename); + } else { + $tab->prepare_items(); + MeprUtils::render_csv($tab->get_items(), $filename); + } + } - // Pull out our serialized custom field values - if(is_serialized($field)) { - $field_settings = $mepr_options->get_custom_field($label); + public function process_custom_field($field, $label) + { + $mepr_options = MeprOptions::fetch(); + + // Pull out our serialized custom field values + if (is_serialized($field)) { + $field_settings = $mepr_options->get_custom_field($label); + + if (empty($field_settings)) { + return $field; + } + + if ($field_settings->field_type == 'multiselect') { + $field = unserialize($field); + return implode(',', $field); + } elseif ($field_settings->field_type == 'checkboxes') { + $field = unserialize($field); + return implode(',', array_keys($field)); + } + } - if(empty($field_settings)) { return $field; - } - - if($field_settings->field_type == 'multiselect') { - $field = unserialize($field); - return implode(',',$field); - } - else if($field_settings->field_type == 'checkboxes') { - $field = unserialize($field); - return implode(',',array_keys($field)); - } } - return $field; - } - - public function export_footer_link($action, $totalitems, $itemcount) { - if($action=='mepr_members') { - MeprAppHelper::export_table_link($action, 'export_members', 'mepr_members_nonce', $itemcount); - ?> | | display_list(); - } + public function listing_drm() + { + $this->display_list(); + } } //End clas diff --git a/app/controllers/MeprMigratorCtrl.php b/app/controllers/MeprMigratorCtrl.php index 8728adc..1d9569c 100644 --- a/app/controllers/MeprMigratorCtrl.php +++ b/app/controllers/MeprMigratorCtrl.php @@ -1,106 +1,113 @@ admin_url('admin-ajax.php'), - 'migrate_nonce' => wp_create_nonce('mepr_migrator_migrate'), - 'leave_are_you_sure' => __('The migration has not yet completed, are you sure you want to leave this page?', 'memberpress'), - 'migration_complete' => __('Migration complete', 'memberpress'), - ]); + wp_localize_script('memberpress-migrator', 'MeprMigratorL10n', [ + 'ajax_url' => admin_url('admin-ajax.php'), + 'migrate_nonce' => wp_create_nonce('mepr_migrator_migrate'), + 'leave_are_you_sure' => __('The migration has not yet completed, are you sure you want to leave this page?', 'memberpress'), + 'migration_complete' => __('Migration complete', 'memberpress'), + ]); + } } - } - /** - * Adds the Migrator tab to the Courses Settings within MP Settings. - */ - public static function add_options_tab() { - ?> -
  • - +
  • + migrate($data); - } - catch(Exception $e) { - wp_send_json_error($e->getMessage()); + try { + $migrator->migrate($data); + } catch (Exception $e) { + wp_send_json_error($e->getMessage()); + } } - } - /** - * Display admin notices. - */ - public static function admin_notices() { - global $pagenow, $typenow; + /** + * Display admin notices. + */ + public static function admin_notices() + { + global $pagenow, $typenow; - if( - !MeprUtils::is_logged_in_and_an_admin() || - get_option('mepr_dismiss_notice_learndash_migrator') || - $pagenow != 'edit.php' || - $typenow != 'mpcs-course' - ) { - return; - } + if ( + !MeprUtils::is_logged_in_and_an_admin() || + get_option('mepr_dismiss_notice_learndash_migrator') || + $pagenow != 'edit.php' || + $typenow != 'mpcs-course' + ) { + return; + } - if( - MeprMigratorHelper::has_completed_migration(MeprMigratorLearnDash::KEY) || - !MeprMigratorLearnDash::is_migration_possible() - ) { - update_option('mepr_dismiss_notice_learndash_migrator', true); - return; - } - ?> + if ( + MeprMigratorHelper::has_completed_migration(MeprMigratorLearnDash::KEY) || + !MeprMigratorLearnDash::is_migration_possible() + ) { + update_option('mepr_dismiss_notice_learndash_migrator', true); + return; + } + ?>
    @@ -114,6 +121,6 @@ public static function admin_notices() {
    - init(); // loads hooks +if (!defined('ABSPATH')) { + die('You are not allowed to call this page directly.'); +} - add_action('admin_enqueue_scripts', function () { - if(MeprUtils::is_memberpress_admin_page()) { - do_action('mepr_overview_enqueue'); - } - }); +/** + * This is just a front-end Controller adapter for MeprNotifications. + */ +class MeprNotificationsCtrl extends MeprBaseCtrl +{ + public function load_hooks() + { + $notifications = new MeprNotifications(); + $notifications->init(); // loads hooks - //add_action('admin_notices', function() { - // if(MeprUtils::is_memberpress_admin_page()) { - // do_action('mepr_admin_overview_before_table'); - // } - //}); - } -} + add_action('admin_enqueue_scripts', function () { + if (MeprUtils::is_memberpress_admin_page()) { + do_action('mepr_overview_enqueue'); + } + }); + // add_action('admin_notices', function() { + // if(MeprUtils::is_memberpress_admin_page()) { + // do_action('mepr_admin_overview_before_table'); + // } + // }); + } +} diff --git a/app/controllers/MeprOnboardingCtrl.php b/app/controllers/MeprOnboardingCtrl.php index 31dbaaa..1a8e2ee 100644 --- a/app/controllers/MeprOnboardingCtrl.php +++ b/app/controllers/MeprOnboardingCtrl.php @@ -1,1342 +1,1386 @@ query("INSERT INTO {$wpdb->options} (option_name, option_value) VALUES('mepr_onboarded', '1') ON DUPLICATE KEY UPDATE option_value = VALUES(option_value);"); - - $step = isset($_GET['step']) ? (int) $_GET['step'] : 0; - - if($step) { - $steps = [ - [ - 'title' => __('Activate License', 'memberpress'), - 'content' => MEPR_VIEWS_PATH . '/admin/onboarding/license.php', - 'nav' => MEPR_VIEWS_PATH . '/admin/onboarding/nav/license.php', - 'step' => 1, - ], - [ - 'title' => __('Enable Features', 'memberpress'), - 'content' => MEPR_VIEWS_PATH . '/admin/onboarding/features.php', - 'nav' => MEPR_VIEWS_PATH . '/admin/onboarding/nav/features.php', - 'step' => 2, - ], - [ - 'title' => __('Create or Select Content', 'memberpress'), - 'content' => MEPR_VIEWS_PATH . '/admin/onboarding/content.php', - 'nav' => MEPR_VIEWS_PATH . '/admin/onboarding/nav/content.php', - 'step' => 3, - ], - [ - 'title' => __('Create Membership', 'memberpress'), - 'content' => MEPR_VIEWS_PATH . '/admin/onboarding/membership.php', - 'nav' => MEPR_VIEWS_PATH . '/admin/onboarding/nav/membership.php', - 'step' => 4, - ], - [ - 'title' => __('Protect Content', 'memberpress'), - 'content' => MEPR_VIEWS_PATH . '/admin/onboarding/rules.php', - 'nav' => MEPR_VIEWS_PATH . '/admin/onboarding/nav/rules.php', - 'step' => 5, - ], - [ - 'title' => __('Payment Options', 'memberpress'), - 'content' => MEPR_VIEWS_PATH . '/admin/onboarding/payments.php', - 'nav' => MEPR_VIEWS_PATH . '/admin/onboarding/nav/payments.php', - 'step' => 6, - ], - [ - 'title' => __('Finish Setup', 'memberpress'), - 'content' => MEPR_VIEWS_PATH . '/admin/onboarding/finish.php', - 'nav' => MEPR_VIEWS_PATH . '/admin/onboarding/nav/finish.php', - 'step' => 7, - ], - [ - 'title' => __('Complete', 'memberpress'), - 'content' => MEPR_VIEWS_PATH . '/admin/onboarding/complete.php', - 'nav' => MEPR_VIEWS_PATH . '/admin/onboarding/nav/complete.php', - 'step' => 8, - ], - ]; - - MeprView::render('/admin/onboarding/wizard', compact('step', 'steps')); - } - else { - MeprView::render('/admin/onboarding/welcome'); - } - } - - public static function admin_enqueue_scripts() { - if(self::is_onboarding_page()) { - wp_enqueue_style('memberpress-onboarding', MEPR_CSS_URL . '/admin-onboarding.css', [], MEPR_VERSION); - wp_enqueue_script('memberpress-onboarding', MEPR_JS_URL . '/admin_onboarding.js', ['jquery'], MEPR_VERSION, true); - wp_localize_script('memberpress-onboarding', 'MeprOnboardingL10n', [ - 'step' => isset($_GET['step']) ? (int) $_GET['step'] : 0, - 'ajax_url' => admin_url('admin-ajax.php'), - 'onboarding_url' => admin_url('admin.php?page=memberpress-onboarding'), - 'features' => self::get_features(), - 'save_features_nonce' => wp_create_nonce('mepr_onboarding_save_features'), - 'save_new_content_nonce' => wp_create_nonce('mepr_onboarding_save_new_content'), - 'save_new_rule_nonce' => wp_create_nonce('mepr_onboarding_save_new_rule'), - 'save_new_membership_nonce' => wp_create_nonce('mepr_onboarding_save_new_membership'), - 'get_membership_nonce' => wp_create_nonce('mepr_onboarding_get_membership'), - 'get_rule_nonce' => wp_create_nonce('mepr_onboarding_get_rule'), - 'install_correct_edition' => wp_create_nonce('mepr_onboarding_install_correct_edition'), - 'install_addons' => wp_create_nonce('mepr_onboarding_install_addons'), - 'load_complete_step' => wp_create_nonce('mepr_onboarding_load_complete_step'), - 'load_create_new_content' => wp_create_nonce('mepr_onboarding_load_create_new_content'), - 'load_finish_step' => wp_create_nonce('mepr_onboarding_load_finish_step'), - 'set_content_nonce' => wp_create_nonce('mepr_onboarding_set_content'), - 'unset_content_nonce' => wp_create_nonce('mepr_onboarding_unset_content'), - 'unset_rule_nonce' => wp_create_nonce('mepr_onboarding_unset_rule'), - 'unset_membership_nonce' => wp_create_nonce('mepr_onboarding_unset_membership'), - 'mark_content_steps_skipped_nonce' => wp_create_nonce('mepr_onboarding_mark_content_steps_skipped'), - 'mark_steps_complete_nonce' => wp_create_nonce('mepr_onboarding_mark_steps_complete'), - 'search_content_nonce' => wp_create_nonce('mepr_onboarding_search_content'), - 'add_payment_method_nonce' => wp_create_nonce('mepr_add_payment_method'), - 'remove_payment_method_nonce' => wp_create_nonce('mepr_remove_payment_method'), - 'save_authorize_config_nonce' => wp_create_nonce('mepr_save_authorize_config'), - 'deactivate_confirm' => __('Are you sure? MemberPress will not be functional if this License Key is deactivated.', 'memberpress'), - 'activate_license_nonce' => wp_create_nonce('mepr_activate_license'), - 'deactivate_license_nonce' => wp_create_nonce('mepr_deactivate_license'), - 'an_error_occurred' => __('An error occurred', 'memberpress'), - 'content_id' => MeprOnboardingHelper::get_content_post_id(), - 'membership_id' => MeprOnboardingHelper::get_membership_post_id(), - 'membership_rule_id' => MeprOnboardingHelper::get_rule_post_id(), - 'course_name' => __('Course Name', 'memberpress'), - 'page_title' => __('Page Title', 'memberpress'), - 'course' => __('Course', 'memberpress'), - 'page' => __('Page', 'memberpress'), - 'may_take_couple_minutes' => __('This may take a couple of minutes', 'memberpress'), - 'enable_stripe_tax_nonce' => wp_create_nonce('mepr_enable_stripe_tax'), - 'finish_nonce' => wp_create_nonce('mepr_onboarding_finish'), - 'memberships_url' => admin_url('edit.php?post_type=memberpressproduct'), - 'error_installing_addon' => __('An error occurred when installing an add-on, please download and install the add-ons manually.', 'memberpress'), - 'edition_url_param' => isset($_GET['edition']) ? sanitize_text_field(wp_unslash($_GET['edition'])) : '', - ]); - } - } - - private static function get_features() { - return [ - 'memberpress-courses' => 'MemberPress Courses', - 'memberpress-downloads' => 'MemberPress Downloads', - 'memberpress-buddypress' => 'MemberPress BuddyPress', - 'memberpress-developer-tools' => 'MemberPress Developer Tools', - 'memberpress-gifting' => 'MemberPress Gifting', - 'memberpress-corporate' => 'MemberPress Corporate Accounts', - 'easy-affiliate' => 'Easy Affiliate', - 'memberpress-coachkit' => 'CoachKit™', - ]; - } - - public static function remove_all_admin_notices() { - if(self::is_onboarding_page()) { - remove_all_actions('admin_notices'); - } - } - public static function highlight_menu_item($submenu_file) { - remove_submenu_page('memberpress', 'memberpress-onboarding'); +if (!defined('ABSPATH')) { + die('You are not allowed to call this page directly.'); +} + +class MeprOnboardingCtrl extends MeprBaseCtrl +{ + public function load_hooks() + { + add_filter('submenu_file', 'MeprOnboardingCtrl::highlight_menu_item'); + add_action('admin_enqueue_scripts', 'MeprOnboardingCtrl::admin_enqueue_scripts'); + add_action('admin_notices', 'MeprOnboardingCtrl::remove_all_admin_notices', 0); + add_action('wp_ajax_mepr_onboarding_save_features', 'MeprOnboardingCtrl::save_features'); + add_action('wp_ajax_mepr_onboarding_save_new_content', 'MeprOnboardingCtrl::save_new_content'); + add_action('wp_ajax_mepr_onboarding_save_new_membership', 'MeprOnboardingCtrl::save_new_membership'); + add_action('wp_ajax_mepr_onboarding_get_membership', 'MeprOnboardingCtrl::get_membership'); + add_action('wp_ajax_mepr_onboarding_search_content', 'MeprOnboardingCtrl::search_content'); + add_action('wp_ajax_mepr_onboarding_set_content', 'MeprOnboardingCtrl::set_content'); + add_action('wp_ajax_mepr_onboarding_unset_content', 'MeprOnboardingCtrl::unset_content'); + add_action('wp_ajax_mepr_onboarding_mark_content_steps_skipped', 'MeprOnboardingCtrl::mark_content_steps_skipped'); + add_action('wp_ajax_mepr_onboarding_mark_steps_complete', 'MeprOnboardingCtrl::mark_steps_complete'); + add_action('wp_ajax_mepr_onboarding_add_stripe_payment_method', 'MeprOnboardingCtrl::add_stripe_payment_method'); + add_action('wp_ajax_mepr_onboarding_add_paypal_payment_method', 'MeprOnboardingCtrl::add_paypal_payment_method'); + add_action('wp_ajax_mepr_onboarding_add_authorize_payment_method', 'MeprOnboardingCtrl::add_authorize_payment_method'); + add_action('wp_ajax_mepr_onboarding_save_authorize_config', 'MeprOnboardingCtrl::save_authorize_config'); + add_action('wp_ajax_mepr_onboarding_add_offline_payment_method', 'MeprOnboardingCtrl::add_offline_payment_method'); + add_action('wp_ajax_mepr_onboarding_remove_payment_method', 'MeprOnboardingCtrl::remove_payment_method'); + add_action('wp_ajax_mepr_onboarding_save_new_rule', 'MeprOnboardingCtrl::save_new_rule'); + add_action('wp_ajax_mepr_onboarding_get_rule', 'MeprOnboardingCtrl::get_rule'); + add_action('wp_ajax_mepr_onboarding_unset_rule', 'MeprOnboardingCtrl::unset_rule'); + add_action('wp_ajax_mepr_onboarding_unset_membership', 'MeprOnboardingCtrl::unset_membership'); + add_action('wp_ajax_mepr_onboarding_install_correct_edition', 'MeprOnboardingCtrl::install_correct_edition'); + add_action('wp_ajax_mepr_onboarding_install_addons', 'MeprOnboardingCtrl::install_addons'); + add_action('wp_ajax_mepr_onboarding_load_complete_step', 'MeprOnboardingCtrl::load_complete_step'); + add_action('wp_ajax_mepr_onboarding_load_create_new_content', 'MeprOnboardingCtrl::load_create_new_content'); + add_action('wp_ajax_mepr_onboarding_enable_stripe_tax', 'MeprOnboardingCtrl::enable_stripe_tax'); + add_action('wp_ajax_mepr_onboarding_load_finish_step', 'MeprOnboardingCtrl::load_finish_step'); + add_action('wp_ajax_mepr_onboarding_finish', 'MeprOnboardingCtrl::finish'); + add_action('mepr_license_activated', 'MeprOnboardingCtrl::license_activated'); + add_action('mepr_license_deactivated', 'MeprOnboardingCtrl::license_deactivated'); + add_action('admin_menu', 'MeprOnboardingCtrl::validate_step'); + add_action('load-memberpress_page_memberpress-onboarding', 'MeprOnboardingCtrl::settings_redirect'); + add_action('admin_notices', 'MeprOnboardingCtrl::admin_notice'); + } + + public static function route() + { + global $wpdb; + + $wpdb->query("INSERT INTO {$wpdb->options} (option_name, option_value) VALUES('mepr_onboarded', '1') ON DUPLICATE KEY UPDATE option_value = VALUES(option_value);"); + + $step = isset($_GET['step']) ? (int) $_GET['step'] : 0; + + if ($step) { + $steps = [ + [ + 'title' => __('Activate License', 'memberpress'), + 'content' => MEPR_VIEWS_PATH . '/admin/onboarding/license.php', + 'nav' => MEPR_VIEWS_PATH . '/admin/onboarding/nav/license.php', + 'step' => 1, + ], + [ + 'title' => __('Enable Features', 'memberpress'), + 'content' => MEPR_VIEWS_PATH . '/admin/onboarding/features.php', + 'nav' => MEPR_VIEWS_PATH . '/admin/onboarding/nav/features.php', + 'step' => 2, + ], + [ + 'title' => __('Create or Select Content', 'memberpress'), + 'content' => MEPR_VIEWS_PATH . '/admin/onboarding/content.php', + 'nav' => MEPR_VIEWS_PATH . '/admin/onboarding/nav/content.php', + 'step' => 3, + ], + [ + 'title' => __('Create Membership', 'memberpress'), + 'content' => MEPR_VIEWS_PATH . '/admin/onboarding/membership.php', + 'nav' => MEPR_VIEWS_PATH . '/admin/onboarding/nav/membership.php', + 'step' => 4, + ], + [ + 'title' => __('Protect Content', 'memberpress'), + 'content' => MEPR_VIEWS_PATH . '/admin/onboarding/rules.php', + 'nav' => MEPR_VIEWS_PATH . '/admin/onboarding/nav/rules.php', + 'step' => 5, + ], + [ + 'title' => __('Payment Options', 'memberpress'), + 'content' => MEPR_VIEWS_PATH . '/admin/onboarding/payments.php', + 'nav' => MEPR_VIEWS_PATH . '/admin/onboarding/nav/payments.php', + 'step' => 6, + ], + [ + 'title' => __('Finish Setup', 'memberpress'), + 'content' => MEPR_VIEWS_PATH . '/admin/onboarding/finish.php', + 'nav' => MEPR_VIEWS_PATH . '/admin/onboarding/nav/finish.php', + 'step' => 7, + ], + [ + 'title' => __('Complete', 'memberpress'), + 'content' => MEPR_VIEWS_PATH . '/admin/onboarding/complete.php', + 'nav' => MEPR_VIEWS_PATH . '/admin/onboarding/nav/complete.php', + 'step' => 8, + ], + ]; + + MeprView::render('/admin/onboarding/wizard', compact('step', 'steps')); + } else { + MeprView::render('/admin/onboarding/welcome'); + } + } - if(self::is_onboarding_page()) { - $submenu_file = 'edit.php?post_type=memberpressproduct'; + public static function admin_enqueue_scripts() + { + if (self::is_onboarding_page()) { + wp_enqueue_style('memberpress-onboarding', MEPR_CSS_URL . '/admin-onboarding.css', [], MEPR_VERSION); + wp_enqueue_script('memberpress-onboarding', MEPR_JS_URL . '/admin_onboarding.js', ['jquery'], MEPR_VERSION, true); + wp_localize_script('memberpress-onboarding', 'MeprOnboardingL10n', [ + 'step' => isset($_GET['step']) ? (int) $_GET['step'] : 0, + 'ajax_url' => admin_url('admin-ajax.php'), + 'onboarding_url' => admin_url('admin.php?page=memberpress-onboarding'), + 'features' => self::get_features(), + 'save_features_nonce' => wp_create_nonce('mepr_onboarding_save_features'), + 'save_new_content_nonce' => wp_create_nonce('mepr_onboarding_save_new_content'), + 'save_new_rule_nonce' => wp_create_nonce('mepr_onboarding_save_new_rule'), + 'save_new_membership_nonce' => wp_create_nonce('mepr_onboarding_save_new_membership'), + 'get_membership_nonce' => wp_create_nonce('mepr_onboarding_get_membership'), + 'get_rule_nonce' => wp_create_nonce('mepr_onboarding_get_rule'), + 'install_correct_edition' => wp_create_nonce('mepr_onboarding_install_correct_edition'), + 'install_addons' => wp_create_nonce('mepr_onboarding_install_addons'), + 'load_complete_step' => wp_create_nonce('mepr_onboarding_load_complete_step'), + 'load_create_new_content' => wp_create_nonce('mepr_onboarding_load_create_new_content'), + 'load_finish_step' => wp_create_nonce('mepr_onboarding_load_finish_step'), + 'set_content_nonce' => wp_create_nonce('mepr_onboarding_set_content'), + 'unset_content_nonce' => wp_create_nonce('mepr_onboarding_unset_content'), + 'unset_rule_nonce' => wp_create_nonce('mepr_onboarding_unset_rule'), + 'unset_membership_nonce' => wp_create_nonce('mepr_onboarding_unset_membership'), + 'mark_content_steps_skipped_nonce' => wp_create_nonce('mepr_onboarding_mark_content_steps_skipped'), + 'mark_steps_complete_nonce' => wp_create_nonce('mepr_onboarding_mark_steps_complete'), + 'search_content_nonce' => wp_create_nonce('mepr_onboarding_search_content'), + 'add_payment_method_nonce' => wp_create_nonce('mepr_add_payment_method'), + 'remove_payment_method_nonce' => wp_create_nonce('mepr_remove_payment_method'), + 'save_authorize_config_nonce' => wp_create_nonce('mepr_save_authorize_config'), + 'deactivate_confirm' => __('Are you sure? MemberPress will not be functional if this License Key is deactivated.', 'memberpress'), + 'activate_license_nonce' => wp_create_nonce('mepr_activate_license'), + 'deactivate_license_nonce' => wp_create_nonce('mepr_deactivate_license'), + 'an_error_occurred' => __('An error occurred', 'memberpress'), + 'content_id' => MeprOnboardingHelper::get_content_post_id(), + 'membership_id' => MeprOnboardingHelper::get_membership_post_id(), + 'membership_rule_id' => MeprOnboardingHelper::get_rule_post_id(), + 'course_name' => __('Course Name', 'memberpress'), + 'page_title' => __('Page Title', 'memberpress'), + 'course' => __('Course', 'memberpress'), + 'page' => __('Page', 'memberpress'), + 'may_take_couple_minutes' => __('This may take a couple of minutes', 'memberpress'), + 'enable_stripe_tax_nonce' => wp_create_nonce('mepr_enable_stripe_tax'), + 'finish_nonce' => wp_create_nonce('mepr_onboarding_finish'), + 'memberships_url' => admin_url('edit.php?post_type=memberpressproduct'), + 'error_installing_addon' => __('An error occurred when installing an add-on, please download and install the add-ons manually.', 'memberpress'), + 'edition_url_param' => isset($_GET['edition']) ? sanitize_text_field(wp_unslash($_GET['edition'])) : '', + ]); + } } - return $submenu_file; - } + private static function get_features() + { + return [ + 'memberpress-courses' => 'MemberPress Courses', + 'memberpress-downloads' => 'MemberPress Downloads', + 'memberpress-buddypress' => 'MemberPress BuddyPress', + 'memberpress-developer-tools' => 'MemberPress Developer Tools', + 'memberpress-gifting' => 'MemberPress Gifting', + 'memberpress-corporate' => 'MemberPress Corporate Accounts', + 'easy-affiliate' => 'Easy Affiliate', + 'memberpress-coachkit' => 'CoachKit™', + ]; + } + + public static function remove_all_admin_notices() + { + if (self::is_onboarding_page()) { + remove_all_actions('admin_notices'); + } + } - public static function is_onboarding_page() { - $id = MeprUtils::get_current_screen_id(); + public static function highlight_menu_item($submenu_file) + { + remove_submenu_page('memberpress', 'memberpress-onboarding'); - return !empty($id) && is_string($id) && preg_match('/_page_memberpress-onboarding$/', $id); - } + if (self::is_onboarding_page()) { + $submenu_file = 'edit.php?post_type=memberpressproduct'; + } - public static function save_features() { - $data = MeprUtils::get_json_request_data('mepr_onboarding_save_features'); + return $submenu_file; + } - $valid_features = self::get_features(); - $features = []; + public static function is_onboarding_page() + { + $id = MeprUtils::get_current_screen_id(); - foreach($data as $feature) { - if(array_key_exists($feature, $valid_features)) { - $features[] = $feature; - } + return !empty($id) && is_string($id) && preg_match('/_page_memberpress-onboarding$/', $id); } - $addons_installed = array(); - $data = array(); - $data['features'] = $features; - $data['addons_not_installed'] = array(); + public static function save_features() + { + $data = MeprUtils::get_json_request_data('mepr_onboarding_save_features'); - if(!empty($features)){ - $license_addons = MeprUpdateCtrl::addons(true, true, true); + $valid_features = self::get_features(); + $features = []; - // lets try to install and activate add-on. - foreach( $features as $addon_slug ){ - $response = self::maybe_install_activate_addons($license_addons, $addon_slug); - if( -1 === (int) $response ){ - $data['addons_not_installed'][] = $addon_slug; + foreach ($data as $feature) { + if (array_key_exists($feature, $valid_features)) { + $features[] = $feature; + } } - } - } - MeprOnboardingHelper::set_selected_features($data); - MeprOnboardingHelper::maybe_set_steps_completed(2); + $addons_installed = []; + $data = []; + $data['features'] = $features; + $data['addons_not_installed'] = []; - wp_send_json_success($data); - } + if (!empty($features)) { + $license_addons = MeprUpdateCtrl::addons(true, true, true); - public static function maybe_install_activate_addons($license_addons, $addon_slug) { - $return_value = -1; + // lets try to install and activate add-on. + foreach ($features as $addon_slug) { + $response = self::maybe_install_activate_addons($license_addons, $addon_slug); + if (-1 === (int) $response) { + $data['addons_not_installed'][] = $addon_slug; + } + } + } - if(isset($license_addons->$addon_slug)) { - $addon_info = $license_addons->$addon_slug; + MeprOnboardingHelper::set_selected_features($data); + MeprOnboardingHelper::maybe_set_steps_completed(2); - $plugin_url = $addon_info->url; + wp_send_json_success($data); + } - $installed = isset($addon_info->extra_info->directory) && is_dir(WP_PLUGIN_DIR . '/' . $addon_info->extra_info->directory); - $active = isset($addon_info->extra_info->main_file) && is_plugin_active($addon_info->extra_info->main_file); + public static function maybe_install_activate_addons($license_addons, $addon_slug) + { + $return_value = -1; - if($installed && $active) { // already installed and active. - return 1; - } - elseif($installed && !$active) { // already installed and inactive. + if (isset($license_addons->$addon_slug)) { + $addon_info = $license_addons->$addon_slug; - if(isset($addon_info->extra_info->main_file)) { - self::maybe_install_dependent_plugin($addon_slug); - $result = activate_plugins(wp_unslash($addon_info->extra_info->main_file)); - return (int) is_wp_error($result); - } - else { - return 0; + $plugin_url = $addon_info->url; + + $installed = isset($addon_info->extra_info->directory) && is_dir(WP_PLUGIN_DIR . '/' . $addon_info->extra_info->directory); + $active = isset($addon_info->extra_info->main_file) && is_plugin_active($addon_info->extra_info->main_file); + + if ($installed && $active) { // already installed and active. + return 1; + } elseif ($installed && !$active) { // already installed and inactive. + if (isset($addon_info->extra_info->main_file)) { + self::maybe_install_dependent_plugin($addon_slug); + $result = activate_plugins(wp_unslash($addon_info->extra_info->main_file)); + return (int) is_wp_error($result); + } else { + return 0; + } + } else { + return (int) self::download_and_activate_addon($addon_info, $plugin_url, $addon_slug); + } } - } - else { - return (int) self::download_and_activate_addon($addon_info, $plugin_url, $addon_slug); - } - } - // Check if EA is installed or active. - if('easy-affiliate' == $addon_slug) { - $installed = is_dir(WP_PLUGIN_DIR . '/easy-affiliate'); - $active = is_plugin_active('easy-affiliate/easy-affiliate.php'); - - if($installed && $active) { // already installed and active. - return 1; - } - elseif($installed && !$active) { // already installed and inactive. - $result = activate_plugins('easy-affiliate/easy-affiliate.php'); - return (int) is_wp_error($result); - } - else { - $mepr_options = MeprOptions::fetch(); + // Check if EA is installed or active. + if ('easy-affiliate' == $addon_slug) { + $installed = is_dir(WP_PLUGIN_DIR . '/easy-affiliate'); + $active = is_plugin_active('easy-affiliate/easy-affiliate.php'); + + if ($installed && $active) { // already installed and active. + return 1; + } elseif ($installed && !$active) { // already installed and inactive. + $result = activate_plugins('easy-affiliate/easy-affiliate.php'); + return (int) is_wp_error($result); + } else { + $mepr_options = MeprOptions::fetch(); + + if (empty($mepr_options->mothership_license)) { + return 0; + } - if(empty($mepr_options->mothership_license)) { - return 0; + $domain = defined('MEPR_ONBOARDING_MP_URL') ? MEPR_ONBOARDING_MP_URL : 'https://memberpress.com'; + $url = $domain . '/wp-admin/admin-ajax.php?action=mepr_onboarding_get_ea_license'; + + $response = wp_remote_post( + $url, + [ + 'body' => [ + 'key' => $mepr_options->mothership_license, + ], + ] + ); + + $code = wp_remote_retrieve_response_code($response); + + if ($code == 200) { + $data = json_decode(wp_remote_retrieve_body($response), true); + + if (isset($data['success']) && is_bool($data['success'])) { + if ($data['success']) { + // Install Easy Affiliate + $result = self::download_and_activate_plugin($data['data']['download_url']); + + if ($result && class_exists('EasyAffiliate\\Lib\\CtrlFactory')) { + try { + $ctrl = EasyAffiliate\Lib\CtrlFactory::fetch('UpdateCtrl'); + $ctrl->activate_license($data['data']['license_key']); + } catch (Exception $e) { + // ignore + } + } + + return (int) $result; + } + } + } + } } - $domain = defined('MEPR_ONBOARDING_MP_URL') ? MEPR_ONBOARDING_MP_URL : 'https://memberpress.com'; - $url = $domain . '/wp-admin/admin-ajax.php?action=mepr_onboarding_get_ea_license'; + return $return_value; + } - $response = wp_remote_post( - $url, - [ - 'body' => [ - 'key' => $mepr_options->mothership_license - ] - ] - ); + public static function maybe_install_dependent_plugin($addon_slug) + { + if ('memberpress-buddypress' === (string)$addon_slug) { + $buddypress_plugin = 'https://downloads.wordpress.org/plugin/buddypress.latest-stable.zip'; + $buddypress_main_file = 'buddypress/bp-loader.php'; + $buddyboss_main_file = 'buddyboss-platform/bp-loader.php'; - $code = wp_remote_retrieve_response_code($response); + $bboss_installed = is_dir(WP_PLUGIN_DIR . '/' . 'buddyboss-platform'); + $bboss_active = is_plugin_active($buddyboss_main_file); - if($code == 200) { - $data = json_decode(wp_remote_retrieve_body($response), true); + if ($bboss_installed && $bboss_active) { + return 1; + } - if(isset($data['success']) && is_bool($data['success'])) { - if($data['success']) { - // Install Easy Affiliate - $result = self::download_and_activate_plugin($data['data']['download_url']); + // if buddyboss is installed but not active, let's activate. + if ($bboss_installed && !$bboss_active) { + $result = activate_plugins(wp_unslash($buddyboss_main_file)); + delete_transient('_bp_activation_redirect'); + return $result; + } - if($result && class_exists('EasyAffiliate\\Lib\\CtrlFactory')) { - try { - $ctrl = EasyAffiliate\Lib\CtrlFactory::fetch('UpdateCtrl'); - $ctrl->activate_license($data['data']['license_key']); - } - catch(Exception $e) { - // ignore - } - } + $bp_installed = is_dir(WP_PLUGIN_DIR . '/' . 'buddypress'); + $bp_active = is_plugin_active($buddypress_main_file); - return (int) $result; + // If BuddyPress is both installed and active, bailout. + if ($bp_installed && $bp_active) { + return 1; + } + + if ($bp_installed && !$bp_active) { + $result = activate_plugins(wp_unslash($buddypress_main_file)); + delete_transient('_bp_activation_redirect'); + return $result; + } else { + $result = (int) self::download_and_activate_plugin($buddypress_plugin); + delete_transient('_bp_activation_redirect'); + return $result; } - } } - } } - return $return_value; - } - - public static function maybe_install_dependent_plugin($addon_slug) { - if('memberpress-buddypress' === (string)$addon_slug){ - $buddypress_plugin = 'https://downloads.wordpress.org/plugin/buddypress.latest-stable.zip'; - $buddypress_main_file = 'buddypress/bp-loader.php'; - $buddyboss_main_file = 'buddyboss-platform/bp-loader.php'; - - $bboss_installed = is_dir(WP_PLUGIN_DIR . '/' . 'buddyboss-platform'); - $bboss_active = is_plugin_active($buddyboss_main_file); - - if($bboss_installed && $bboss_active) { - return 1; - } - - // if buddyboss is installed but not active, let's activate. - if($bboss_installed && !$bboss_active) { - $result = activate_plugins(wp_unslash($buddyboss_main_file)); - delete_transient('_bp_activation_redirect'); - return $result; - } - - $bp_installed = is_dir(WP_PLUGIN_DIR . '/' . 'buddypress'); - $bp_active = is_plugin_active($buddypress_main_file); - if($bp_installed && !$bp_active) { - $result = activate_plugins(wp_unslash($buddypress_main_file)); - delete_transient('_bp_activation_redirect'); - return $result; - }else{ - $result = (int) self::download_and_activate_plugin($buddypress_plugin); - delete_transient('_bp_activation_redirect'); - return $result; - } - } - } + public static function save_new_content() + { + $data = MeprUtils::get_json_request_data('mepr_onboarding_save_new_content'); - public static function save_new_content() { - $data = MeprUtils::get_json_request_data('mepr_onboarding_save_new_content'); + if (empty($data['type']) || empty($data['title']) || !in_array($data['type'], ['course', 'page'], true)) { + wp_send_json_error(esc_html__('Bad request.', 'memberpress')); + } - if(empty($data['type']) || empty($data['title']) || !in_array($data['type'], ['course', 'page'], true)) { - wp_send_json_error(esc_html__('Bad request.', 'memberpress')); - } + if (!current_user_can('publish_posts')) { + wp_send_json_error(esc_html__('Sorry, you don\'t have permission to do this.', 'memberpress')); + } - if(!current_user_can('publish_posts')) { - wp_send_json_error(esc_html__('Sorry, you don\'t have permission to do this.', 'memberpress')); - } + $type = sanitize_text_field($data['type']); + $title = sanitize_text_field($data['title']); - $type = sanitize_text_field($data['type']); - $title = sanitize_text_field($data['title']); + $post_id = wp_insert_post([ + 'post_type' => $type == 'course' ? 'mpcs-course' : 'page', + 'post_title' => wp_slash($title), // post_title is expected to be slashed + 'post_status' => 'publish', + ], true); - $post_id = wp_insert_post([ - 'post_type' => $type == 'course' ? 'mpcs-course' : 'page', - 'post_title' => wp_slash($title), // post_title is expected to be slashed - 'post_status' => 'publish', - ], true); + if (is_wp_error($post_id)) { + wp_send_json_error($post_id->get_error_message()); + } - if(is_wp_error($post_id)) { - wp_send_json_error($post_id->get_error_message()); - } + $post = get_post($post_id); - $post = get_post($post_id); + if (!$post instanceof WP_Post) { + wp_send_json_error(esc_html__('Post not found.', 'memberpress')); + } - if(!$post instanceof WP_Post) { - wp_send_json_error(esc_html__('Post not found.', 'memberpress')); - } + MeprOnboardingHelper::set_content_post_id($post_id); + MeprOnboardingHelper::set_rule_post_id(0); + MeprOnboardingHelper::maybe_set_steps_completed(2); - MeprOnboardingHelper::set_content_post_id($post_id); - MeprOnboardingHelper::set_rule_post_id(0); - MeprOnboardingHelper::maybe_set_steps_completed(2); - - wp_send_json_success([ - 'heading' => $post->post_type == 'mpcs-course' ? esc_html__('Course Name', 'memberpress') : esc_html__('Page Title', 'memberpress'), - 'post' => $post, - 'rule_data' => MeprOnboardingHelper::get_rules_step_data(), - ]); - } - - public static function get_content_search_results_html($search_query = '') { - $posts = array(); - $post_types = ['page']; - if(MeprOnboardingHelper::is_courses_addon_applicable()){ - $post_types = ['mpcs-course', 'page']; + wp_send_json_success([ + 'heading' => $post->post_type == 'mpcs-course' ? esc_html__('Course Name', 'memberpress') : esc_html__('Page Title', 'memberpress'), + 'post' => $post, + 'rule_data' => MeprOnboardingHelper::get_rules_step_data(), + ]); } - if('' == $search_query){ - $content_id = MeprOnboardingHelper::get_content_post_id(); - - $args = [ - 'post_type' => $post_types, - 'post_status' => 'publish', - 'numberposts' => 6, - 'post__not_in' => array($content_id), - 'orderby' => 'modified', - 'order' => 'DESC', - ]; - - $posts = get_posts($args); - - if($content_id){ - $content_post = get_post($content_id); - $posts[] = $content_post; - } - }else{ - - $args = [ - 'post_type' => $post_types, - 'post_status' => 'publish', - 'numberposts' => 6, - 'orderby' => 'modified', - 'order' => 'DESC', - 's' => $search_query, - ]; - - $posts = get_posts($args); - } - - return MeprView::get_string('/admin/onboarding/content-search-results', compact('posts', 'search_query')); - } - public static function search_content() { - $data = MeprUtils::get_json_request_data('mepr_onboarding_search_content'); + public static function get_content_search_results_html($search_query = '') + { + $posts = []; + $post_types = ['page']; + if (MeprOnboardingHelper::is_courses_addon_applicable()) { + $post_types = ['mpcs-course', 'page']; + } + if ('' == $search_query) { + $content_id = MeprOnboardingHelper::get_content_post_id(); + + $args = [ + 'post_type' => $post_types, + 'post_status' => 'publish', + 'numberposts' => 6, + 'post__not_in' => [$content_id], + 'orderby' => 'modified', + 'order' => 'DESC', + ]; + + $posts = get_posts($args); + + if ($content_id) { + $content_post = get_post($content_id); + $posts[] = $content_post; + } + } else { + $args = [ + 'post_type' => $post_types, + 'post_status' => 'publish', + 'numberposts' => 6, + 'orderby' => 'modified', + 'order' => 'DESC', + 's' => $search_query, + ]; + + $posts = get_posts($args); + } - if(!isset($data['search']) || !is_string($data['search'])) { - wp_send_json_error(__('Bad request.', 'memberpress')); + return MeprView::get_string('/admin/onboarding/content-search-results', compact('posts', 'search_query')); } - $search = sanitize_text_field($data['search']); + public static function search_content() + { + $data = MeprUtils::get_json_request_data('mepr_onboarding_search_content'); - wp_send_json_success(self::get_content_search_results_html($search)); - } + if (!isset($data['search']) || !is_string($data['search'])) { + wp_send_json_error(__('Bad request.', 'memberpress')); + } - public static function license_activated() { + $search = sanitize_text_field($data['search']); - if( ! isset($_GET['page']) || ! isset($_GET['step']) ){ - return; + wp_send_json_success(self::get_content_search_results_html($search)); } - MeprOnboardingHelper::maybe_set_steps_completed(1); + public static function license_activated() + { - if( 'memberpress-onboarding' === (string) $_GET['page'] && 1 === (int) $_GET['step'] ){ + if (! isset($_GET['page']) || ! isset($_GET['step'])) { + return; + } - // to rebuild the mepr_license_info transient. - MeprUpdateCtrl::manually_queue_update(); + MeprOnboardingHelper::maybe_set_steps_completed(1); - $editions = MeprUtils::is_incorrect_edition_installed(); + if ('memberpress-onboarding' === (string) $_GET['page'] && 1 === (int) $_GET['step']) { + // to rebuild the mepr_license_info transient. + MeprUpdateCtrl::manually_queue_update(); - if(is_array($editions) && $editions['license']['index'] > $editions['installed']['index'] ){ - $li = get_site_transient('mepr_license_info'); - $result = MeprOptionsCtrl::install_plugin_silently($li['url'], array('overwrite_package' => true)); - if($result === true) { - do_action('mepr_plugin_edition_changed'); + $editions = MeprUtils::is_incorrect_edition_installed(); + + if (is_array($editions) && $editions['license']['index'] > $editions['installed']['index']) { + $li = get_site_transient('mepr_license_info'); + $result = MeprOptionsCtrl::install_plugin_silently($li['url'], ['overwrite_package' => true]); + if ($result === true) { + do_action('mepr_plugin_edition_changed'); + } + } } - } } - } - public static function license_deactivated() { - MeprOnboardingHelper::set_steps_completed(0); - } + public static function license_deactivated() + { + MeprOnboardingHelper::set_steps_completed(0); + } - public static function validate_step() { + public static function validate_step() + { - if( ! isset($_GET['page']) || ! isset($_GET['step']) ){ - return; - } + if (! isset($_GET['page']) || ! isset($_GET['step'])) { + return; + } - $current_step = (int) $_GET['step']; - if( 'memberpress-onboarding' === (string) $_GET['page'] && 0 < $current_step ){ + $current_step = (int) $_GET['step']; + if ('memberpress-onboarding' === (string) $_GET['page'] && 0 < $current_step) { + if ($current_step == 4) { + $content_id = MeprOnboardingHelper::get_content_post_id(); - if( $current_step == 4 ){ - $content_id = MeprOnboardingHelper::get_content_post_id(); + if (0 === (int) $content_id) { + wp_safe_redirect(admin_url('admin.php?page=memberpress-onboarding&step=3')); + return; + } + } - if( 0 === (int) $content_id ){ - wp_safe_redirect(admin_url('admin.php?page=memberpress-onboarding&step=3')); - return; - } - } + if ($current_step == 5) { + $content_id = MeprOnboardingHelper::get_content_post_id(); + $membership_post_id = MeprOnboardingHelper::get_membership_post_id(); - if( $current_step == 5 ){ - $content_id = MeprOnboardingHelper::get_content_post_id(); - $membership_post_id = MeprOnboardingHelper::get_membership_post_id(); + if (0 === (int) $content_id) { + wp_safe_redirect(admin_url('admin.php?page=memberpress-onboarding&step=3')); + return; + } - if( 0 === (int) $content_id ){ - wp_safe_redirect(admin_url('admin.php?page=memberpress-onboarding&step=3')); - return; - } + if (0 === (int) $membership_post_id) { + wp_safe_redirect(admin_url('admin.php?page=memberpress-onboarding&step=4')); + return; + } + } - if( 0 === (int) $membership_post_id ){ - wp_safe_redirect(admin_url('admin.php?page=memberpress-onboarding&step=4')); - return; + $steps_completed = MeprOnboardingHelper::get_steps_completed(); + $next_applicable_step = $steps_completed + 1; + + if ($current_step > $next_applicable_step) { + $link_step = $steps_completed + 1; + wp_safe_redirect(admin_url('admin.php?page=memberpress-onboarding&step=' . (int)$link_step)); + } } - } + } - $steps_completed = MeprOnboardingHelper::get_steps_completed(); - $next_applicable_step = $steps_completed + 1; + private static function download_and_activate_plugin($plugin_url) + { - if( $current_step > $next_applicable_step ){ - $link_step = $steps_completed + 1; - wp_safe_redirect(admin_url('admin.php?page=memberpress-onboarding&step='.(int)$link_step)); - } - } - } - - private static function download_and_activate_plugin($plugin_url){ - - // Prepare variables - $url = esc_url_raw( - add_query_arg( - array( - 'page' => 'memberpress-addons', - 'onboarding' => '1', - ), - admin_url('admin.php') - ) - ); - - $creds = request_filesystem_credentials($url, '', false, false, null); - - // Check for file system permissions - if(false === $creds) { - return false; - } + // Prepare variables + $url = esc_url_raw( + add_query_arg( + [ + 'page' => 'memberpress-addons', + 'onboarding' => '1', + ], + admin_url('admin.php') + ) + ); - if(!WP_Filesystem($creds)) { - return false; - } + $creds = request_filesystem_credentials($url, '', false, false, null); + + // Check for file system permissions + if (false === $creds) { + return false; + } + + if (!WP_Filesystem($creds)) { + return false; + } + + // We do not need any extra credentials if we have gotten this far, so let's install the plugin + require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php'; - // We do not need any extra credentials if we have gotten this far, so let's install the plugin - require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php'; + // Do not allow WordPress to search/download translations, as this will break JS output + remove_action('upgrader_process_complete', ['Language_Pack_Upgrader', 'async_upgrade'], 20); - // Do not allow WordPress to search/download translations, as this will break JS output - remove_action('upgrader_process_complete', array('Language_Pack_Upgrader', 'async_upgrade'), 20); + // Create the plugin upgrader with our custom skin + $installer = new Plugin_Upgrader(new MeprAddonInstallSkin()); - // Create the plugin upgrader with our custom skin - $installer = new Plugin_Upgrader(new MeprAddonInstallSkin()); + $plugin = wp_unslash($plugin_url); + $installer->install($plugin); - $plugin = wp_unslash($plugin_url); - $installer->install($plugin); + // Flush the cache and return the newly installed plugin basename + wp_cache_flush(); - // Flush the cache and return the newly installed plugin basename - wp_cache_flush(); + if ($installer->plugin_info()) { + $plugin_basename = $installer->plugin_info(); - if($installer->plugin_info()) { - $plugin_basename = $installer->plugin_info(); + // Activate the plugin silently + $activated = activate_plugin($plugin_basename); - // Activate the plugin silently - $activated = activate_plugin($plugin_basename); + if (!is_wp_error($activated)) { + return true; + } else { + return false; + } + } - if(!is_wp_error($activated)) { - return true; - } else { return false; - } } - return false; - } + private static function download_and_activate_addon($addon_info, $plugin_url, $addon_slug = '') + { - private static function download_and_activate_addon($addon_info,$plugin_url, $addon_slug = ''){ + if (!$addon_info->installable) { + return -1; // upgrade required. + } - if(!$addon_info->installable){ - return -1; // upgrade required. - } + // Prepare variables + $url = esc_url_raw( + add_query_arg( + [ + 'page' => 'memberpress-addons', + 'onboarding' => '1', + ], + admin_url('admin.php') + ) + ); - // Prepare variables - $url = esc_url_raw( - add_query_arg( - array( - 'page' => 'memberpress-addons', - 'onboarding' => '1', - ), - admin_url('admin.php') - ) - ); - - $creds = request_filesystem_credentials($url, '', false, false, null); - - // Check for file system permissions - if(false === $creds) { - return false; - } + $creds = request_filesystem_credentials($url, '', false, false, null); - if(!WP_Filesystem($creds)) { - return false; - } + // Check for file system permissions + if (false === $creds) { + return false; + } - // We do not need any extra credentials if we have gotten this far, so let's install the plugin - require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php'; + if (!WP_Filesystem($creds)) { + return false; + } - // Do not allow WordPress to search/download translations, as this will break JS output - remove_action('upgrader_process_complete', array('Language_Pack_Upgrader', 'async_upgrade'), 20); + // We do not need any extra credentials if we have gotten this far, so let's install the plugin + require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php'; - // Create the plugin upgrader with our custom skin - $installer = new Plugin_Upgrader(new MeprAddonInstallSkin()); + // Do not allow WordPress to search/download translations, as this will break JS output + remove_action('upgrader_process_complete', ['Language_Pack_Upgrader', 'async_upgrade'], 20); - $plugin = wp_unslash($plugin_url); - $installer->install($plugin); + // Create the plugin upgrader with our custom skin + $installer = new Plugin_Upgrader(new MeprAddonInstallSkin()); - // Flush the cache and return the newly installed plugin basename - wp_cache_flush(); + $plugin = wp_unslash($plugin_url); + $installer->install($plugin); - if($installer->plugin_info()) { - $plugin_basename = $installer->plugin_info(); + // Flush the cache and return the newly installed plugin basename + wp_cache_flush(); - self::maybe_install_dependent_plugin($addon_slug); + if ($installer->plugin_info()) { + $plugin_basename = $installer->plugin_info(); - // Activate the plugin silently - $activated = activate_plugin($plugin_basename); + self::maybe_install_dependent_plugin($addon_slug); + + // Activate the plugin silently + $activated = activate_plugin($plugin_basename); + + if (!is_wp_error($activated)) { + return true; + } else { + return false; + } + } - if(!is_wp_error($activated)) { - return true; - } else { return false; - } } - return false; - } + public static function set_content() + { + $data = MeprUtils::get_json_request_data('mepr_onboarding_set_content'); - public static function set_content() { - $data = MeprUtils::get_json_request_data('mepr_onboarding_set_content'); + if (!current_user_can('publish_posts')) { + wp_send_json_error(esc_html__('Sorry, you don\'t have permission to do this.', 'memberpress')); + } - if(!current_user_can('publish_posts')) { - wp_send_json_error(esc_html__('Sorry, you don\'t have permission to do this.', 'memberpress')); - } + if (empty($data['content_id'])) { + wp_send_json_error(esc_html__('Bad request.', 'memberpress')); + } + + $content_id = absint($data['content_id']); + $post = get_post($content_id); + + if (!$post instanceof WP_Post) { + wp_send_json_error(esc_html__('Invalid request.', 'memberpress')); + } - if(empty($data['content_id'])) { - wp_send_json_error(esc_html__('Bad request.', 'memberpress')); + if (!in_array($post->post_type, ['page','mpcs-course'])) { + wp_send_json_error(esc_html__('Invalid content.', 'memberpress')); + } + + MeprOnboardingHelper::set_content_post_id($content_id); + MeprOnboardingHelper::set_rule_post_id(0); + MeprOnboardingHelper::maybe_set_steps_completed(3); + + wp_send_json_success([ + 'rule_data' => MeprOnboardingHelper::get_rules_step_data(), + ]); } - $content_id = absint($data['content_id']); - $post = get_post($content_id); + public static function unset_content() + { + $data = MeprUtils::get_json_request_data('mepr_onboarding_unset_content'); + MeprOnboardingHelper::set_content_post_id(0); + MeprOnboardingHelper::set_rule_post_id(0); + } - if(!$post instanceof WP_Post){ - wp_send_json_error(esc_html__('Invalid request.', 'memberpress')); + public static function unset_rule() + { + $data = MeprUtils::get_json_request_data('mepr_onboarding_unset_rule'); + MeprOnboardingHelper::set_rule_post_id(0); } - if(!in_array($post->post_type,array('page','mpcs-course'))){ - wp_send_json_error(esc_html__('Invalid content.', 'memberpress')); + public static function unset_membership() + { + $data = MeprUtils::get_json_request_data('mepr_onboarding_unset_membership'); + MeprOnboardingHelper::set_membership_post_id(0); } - MeprOnboardingHelper::set_content_post_id($content_id); - MeprOnboardingHelper::set_rule_post_id(0); - MeprOnboardingHelper::maybe_set_steps_completed(3); - - wp_send_json_success([ - 'rule_data' => MeprOnboardingHelper::get_rules_step_data(), - ]); - } - - public static function unset_content() { - $data = MeprUtils::get_json_request_data('mepr_onboarding_unset_content'); - MeprOnboardingHelper::set_content_post_id(0); - MeprOnboardingHelper::set_rule_post_id(0); - } - - public static function unset_rule() { - $data = MeprUtils::get_json_request_data('mepr_onboarding_unset_rule'); - MeprOnboardingHelper::set_rule_post_id(0); - } - - public static function unset_membership() { - $data = MeprUtils::get_json_request_data('mepr_onboarding_unset_membership'); - MeprOnboardingHelper::set_membership_post_id(0); - } - - public static function mark_content_steps_skipped() { - $data = MeprUtils::get_json_request_data('mepr_onboarding_mark_content_steps_skipped'); - MeprOnboardingHelper::mark_content_steps_skipped(); - MeprOnboardingHelper::maybe_set_steps_completed(5); - } - - public static function mark_steps_complete() { - $data = MeprUtils::get_json_request_data('mepr_onboarding_mark_steps_complete'); - MeprOnboardingHelper::maybe_set_steps_completed($data['step']); - } - - public static function save_new_membership() { - $data = MeprUtils::get_json_request_data('mepr_onboarding_save_new_membership'); - - if(empty($data['type']) || empty($data['title']) || !in_array($data['type'], ['onetime', 'months','years'], true)) { - wp_send_json_error(esc_html__('Bad request.', 'memberpress')); + public static function mark_content_steps_skipped() + { + $data = MeprUtils::get_json_request_data('mepr_onboarding_mark_content_steps_skipped'); + MeprOnboardingHelper::mark_content_steps_skipped(); + MeprOnboardingHelper::maybe_set_steps_completed(5); } - if(!current_user_can('publish_posts')) { - wp_send_json_error(esc_html__('Sorry, you don\'t have permission to do this.', 'memberpress')); + public static function mark_steps_complete() + { + $data = MeprUtils::get_json_request_data('mepr_onboarding_mark_steps_complete'); + MeprOnboardingHelper::maybe_set_steps_completed($data['step']); } - $type = sanitize_text_field($data['type']); - $title = sanitize_text_field($data['title']); - $price = sanitize_text_field($data['price']); + public static function save_new_membership() + { + $data = MeprUtils::get_json_request_data('mepr_onboarding_save_new_membership'); - $is_recurring = in_array($type, ['months','years'], true); + if (empty($data['type']) || empty($data['title']) || !in_array($data['type'], ['onetime', 'months','years'], true)) { + wp_send_json_error(esc_html__('Bad request.', 'memberpress')); + } - if( $is_recurring && (float) $price <= 0.0 ){ - wp_send_json_error(esc_html__('Price must be greater than zero for the Billing.', 'memberpress')); - } + if (!current_user_can('publish_posts')) { + wp_send_json_error(esc_html__('Sorry, you don\'t have permission to do this.', 'memberpress')); + } - $product_period_type = 'lifetime'; - if( $is_recurring ){ - $product_period_type = $type; - } + $type = sanitize_text_field($data['type']); + $title = sanitize_text_field($data['title']); + $price = sanitize_text_field($data['price']); - $post_id = wp_insert_post([ - 'post_type' => 'memberpressproduct', - 'post_title' => wp_slash($title), // post_title is expected to be slashed - 'post_status' => 'publish', - ], true); + $is_recurring = in_array($type, ['months','years'], true); - if(is_wp_error($post_id)) { - wp_send_json_error($post_id->get_error_message()); - } + if ($is_recurring && (float) $price <= 0.0) { + wp_send_json_error(esc_html__('Price must be greater than zero for the Billing.', 'memberpress')); + } - $post = get_post($post_id); + $product_period_type = 'lifetime'; + if ($is_recurring) { + $product_period_type = $type; + } - if(!$post instanceof WP_Post) { - wp_send_json_error(esc_html__('Post not found.', 'memberpress')); - } + $post_id = wp_insert_post([ + 'post_type' => 'memberpressproduct', + 'post_title' => wp_slash($title), // post_title is expected to be slashed + 'post_status' => 'publish', + ], true); + + if (is_wp_error($post_id)) { + wp_send_json_error($post_id->get_error_message()); + } - $product = new MeprProduct($post_id); + $post = get_post($post_id); - $product->price = MeprUtils::format_currency_us_float($price); - $product->pricing_title = $title; - $product->period = 1; - $product->period_type = $product_period_type; - $product->pricing_display = 'auto'; - $product->tax_class = 'standard'; - $product->pricing_button_txt = esc_html__('Sign Up', 'memberpress'); - $product->store_meta(); + if (!$post instanceof WP_Post) { + wp_send_json_error(esc_html__('Post not found.', 'memberpress')); + } - MeprOnboardingHelper::set_membership_post_id($post_id); - MeprOnboardingHelper::maybe_set_steps_completed(4); + $product = new MeprProduct($post_id); - wp_send_json_success(MeprOnboardingHelper::prepare_product_data($product)); - } + $product->price = MeprUtils::format_currency_us_float($price); + $product->pricing_title = $title; + $product->period = 1; + $product->period_type = $product_period_type; + $product->pricing_display = 'auto'; + $product->tax_class = 'standard'; + $product->pricing_button_txt = esc_html__('Sign Up', 'memberpress'); + $product->store_meta(); - public static function get_membership() { - $data = MeprUtils::get_json_request_data('mepr_onboarding_get_membership'); + MeprOnboardingHelper::set_membership_post_id($post_id); + MeprOnboardingHelper::maybe_set_steps_completed(4); - if(empty($data['membership_id'])) { - wp_send_json_error(esc_html__('Bad request.', 'memberpress')); + wp_send_json_success(MeprOnboardingHelper::prepare_product_data($product)); } - if(!current_user_can('publish_posts')) { - wp_send_json_error(esc_html__('Sorry, you don\'t have permission to do this.', 'memberpress')); - } + public static function get_membership() + { + $data = MeprUtils::get_json_request_data('mepr_onboarding_get_membership'); + + if (empty($data['membership_id'])) { + wp_send_json_error(esc_html__('Bad request.', 'memberpress')); + } + + if (!current_user_can('publish_posts')) { + wp_send_json_error(esc_html__('Sorry, you don\'t have permission to do this.', 'memberpress')); + } - $post_id = sanitize_text_field($data['membership_id']); - $post = get_post($post_id); + $post_id = sanitize_text_field($data['membership_id']); + $post = get_post($post_id); - if(!$post instanceof WP_Post) { - wp_send_json_error(esc_html__('Post not found.', 'memberpress')); + if (!$post instanceof WP_Post) { + wp_send_json_error(esc_html__('Post not found.', 'memberpress')); + } + + $product = new MeprProduct($post_id); + wp_send_json_success(MeprOnboardingHelper::prepare_product_data($product)); } - $product = new MeprProduct($post_id); - wp_send_json_success(MeprOnboardingHelper::prepare_product_data($product)); - } + public static function add_stripe_payment_method() + { + MeprUtils::validate_json_request('mepr_add_payment_method'); - public static function add_stripe_payment_method() { - MeprUtils::validate_json_request('mepr_add_payment_method'); + $mepr_options = MeprOptions::fetch(); + $gateway = new MeprStripeGateway(); - $mepr_options = MeprOptions::fetch(); - $gateway = new MeprStripeGateway(); + if (isset($mepr_options->integrations[$gateway->id])) { + wp_send_json_error('Gateway already exists'); + } - if(isset($mepr_options->integrations[$gateway->id])) { - wp_send_json_error('Gateway already exists'); - } + $integration = [ + $gateway->id => [ + 'id' => $gateway->id, + 'saved' => '1', + 'label' => 'Stripe', + 'gateway' => 'MeprStripeGateway', + 'use_label' => true, + 'use_icon' => true, + 'use_desc' => true, + 'api_keys' => [ + 'test' => [ + 'public' => '', + 'secret' => '', + ], + 'live' => [ + 'public' => '', + 'secret' => '', + ], + ], + 'connect_status' => '', + 'service_account_id' => '', + 'service_account_name' => '', + 'test_mode' => false, + 'stripe_wallet_enabled' => 'on', + ], + ]; + + $mepr_options->integrations = array_merge($mepr_options->integrations, $integration); + $mepr_options->store(false); + + update_option('mepr_onboarding_payment_gateway', $gateway->id); + + $account_email = get_option('mepr_authenticator_account_email'); + $secret = get_option('mepr_authenticator_secret_token'); + $site_uuid = get_option('mepr_authenticator_site_uuid'); + + if ($account_email && $secret && $site_uuid) { + $stripe_connect_url = MeprStripeGateway::get_stripe_connect_url($gateway->id, true); + } else { + $stripe_connect_url = MeprAuthenticatorCtrl::get_auth_connect_url(true, $gateway->id, [], admin_url('admin.php?page=memberpress-onboarding&step=6')); + } - $integration = [ - $gateway->id => [ - 'id' => $gateway->id, - 'saved' => '1', - 'label' => 'Stripe', - 'gateway' => 'MeprStripeGateway', - 'use_label' => true, - 'use_icon' => true, - 'use_desc' => true, - 'api_keys' => [ - 'test' => [ - 'public' => '', - 'secret' => '', - ], - 'live' => [ - 'public' => '', - 'secret' => '', - ], - ], - 'connect_status' => '', - 'service_account_id' => '', - 'service_account_name' => '', - 'test_mode' => false, - 'stripe_wallet_enabled' => 'on', - ] - ]; - - $mepr_options->integrations = array_merge($mepr_options->integrations, $integration); - $mepr_options->store(false); - - update_option('mepr_onboarding_payment_gateway', $gateway->id); - - $account_email = get_option('mepr_authenticator_account_email'); - $secret = get_option('mepr_authenticator_secret_token'); - $site_uuid = get_option('mepr_authenticator_site_uuid'); - - if($account_email && $secret && $site_uuid) { - $stripe_connect_url = MeprStripeGateway::get_stripe_connect_url($gateway->id, true); - } - else { - $stripe_connect_url = MeprAuthenticatorCtrl::get_auth_connect_url(true, $gateway->id, [], admin_url('admin.php?page=memberpress-onboarding&step=6')); + MeprOnboardingHelper::maybe_set_steps_completed(6); + wp_send_json_success($stripe_connect_url); } - MeprOnboardingHelper::maybe_set_steps_completed(6); - wp_send_json_success($stripe_connect_url); - } + public static function add_paypal_payment_method() + { + $data = MeprUtils::get_json_request_data('mepr_add_payment_method'); - public static function add_paypal_payment_method() { - $data = MeprUtils::get_json_request_data('mepr_add_payment_method'); + $sandbox = isset($data['sandbox']) && $data['sandbox']; + $auth_code = isset($data['auth_code']) ? sanitize_text_field($data['auth_code']) : ''; + $shared_id = isset($data['shared_id']) ? sanitize_text_field($data['shared_id']) : ''; + $gateway_id = isset($data['gateway_id']) ? sanitize_text_field($data['gateway_id']) : ''; - $sandbox = isset($data['sandbox']) && $data['sandbox']; - $auth_code = isset($data['auth_code']) ? sanitize_text_field($data['auth_code']) : ''; - $shared_id = isset($data['shared_id']) ? sanitize_text_field($data['shared_id']) : ''; - $gateway_id = isset($data['gateway_id']) ? sanitize_text_field($data['gateway_id']) : ''; + if (empty($auth_code) || empty($shared_id) || empty($gateway_id)) { + wp_send_json_error(__('Bad request.', 'memberpress')); + } + + try { + update_option('mepr_onboarding_payment_gateway', $gateway_id); + + $ctrl = MeprCtrlFactory::fetch('MeprPayPalConnectCtrl'); + $ctrl->handle_update_creds($sandbox, $auth_code, $shared_id, $gateway_id); + + MeprOnboardingHelper::maybe_set_steps_completed(6); - if(empty($auth_code) || empty($shared_id) || empty($gateway_id)) { - wp_send_json_error(__('Bad request.', 'memberpress')); + wp_send_json_success(MeprOnboardingHelper::get_payment_gateway_html()); + } catch (Exception $e) { + wp_send_json_error($e->getMessage()); + } } - try { - update_option('mepr_onboarding_payment_gateway', $gateway_id); + public static function add_authorize_payment_method() + { + $data = MeprUtils::get_json_request_data('mepr_add_payment_method'); - $ctrl = MeprCtrlFactory::fetch('MeprPayPalConnectCtrl'); - $ctrl->handle_update_creds($sandbox, $auth_code, $shared_id, $gateway_id); + $mepr_options = MeprOptions::fetch(); + $gateway = new MeprAuthorizeGateway(); - MeprOnboardingHelper::maybe_set_steps_completed(6); + if (isset($mepr_options->integrations[$gateway->id])) { + wp_send_json_error('Gateway already exists'); + } - wp_send_json_success(MeprOnboardingHelper::get_payment_gateway_html()); - } - catch(Exception $e) { - wp_send_json_error($e->getMessage()); - } - } + if (1 === (int) $data['upgrade_required'] && ! MeprOnboardingHelper::is_pro_license()) { + update_option('mepr_onboarding_payment_gateway', 'MeprAuthorizeGateway'); + MeprOnboardingHelper::maybe_set_steps_completed(6); + wp_send_json_success([ + 'payment_gateway_html' => MeprOnboardingHelper::get_payment_gateway_html(), + 'webhook_url' => $gateway->notify_url('whk'), + ]); + } - public static function add_authorize_payment_method() { - $data = MeprUtils::get_json_request_data('mepr_add_payment_method'); + $integration = [ + $gateway->id => [ + 'id' => $gateway->id, + 'saved' => '1', + 'label' => 'Authorize.net', + 'gateway' => 'MeprAuthorizeGateway', + 'use_label' => true, + 'use_icon' => true, + 'use_desc' => true, + 'login_name' => '', + 'transaction_key' => '', + 'signature_key' => '', + ], + ]; + + $mepr_options->integrations = array_merge($mepr_options->integrations, $integration); + $mepr_options->store(false); + + update_option('mepr_onboarding_payment_gateway', $gateway->id); + MeprOnboardingHelper::maybe_set_steps_completed(6); + + wp_send_json_success([ + 'payment_gateway_html' => MeprOnboardingHelper::get_payment_gateway_html(), + 'webhook_url' => $gateway->notify_url('whk'), + ]); + } + + public static function add_offline_payment_method() + { + MeprUtils::validate_json_request('mepr_add_payment_method'); - $mepr_options = MeprOptions::fetch(); - $gateway = new MeprAuthorizeGateway(); + $mepr_options = MeprOptions::fetch(); - if(isset($mepr_options->integrations[$gateway->id])) { - wp_send_json_error('Gateway already exists'); - } + if (!empty($mepr_options->integrations)) { + // Bail successfully if we already have a payment method + wp_send_json_success(); + } - if( 1 === (int) $data['upgrade_required'] && ! MeprOnboardingHelper::is_pro_license() ){ - update_option('mepr_onboarding_payment_gateway', 'MeprAuthorizeGateway'); - MeprOnboardingHelper::maybe_set_steps_completed(6); - wp_send_json_success([ - 'payment_gateway_html' => MeprOnboardingHelper::get_payment_gateway_html(), - 'webhook_url' => $gateway->notify_url('whk'), - ]); - } + $gateway = new MeprArtificialGateway(); - $integration = [ - $gateway->id => [ - 'id' => $gateway->id, - 'saved' => '1', - 'label' => 'Authorize.net', - 'gateway' => 'MeprAuthorizeGateway', - 'use_label' => true, - 'use_icon' => true, - 'use_desc' => true, - 'login_name' => '', - 'transaction_key' => '', - 'signature_key' => '', - ] - ]; - - $mepr_options->integrations = array_merge($mepr_options->integrations, $integration); - $mepr_options->store(false); - - update_option('mepr_onboarding_payment_gateway', $gateway->id); - MeprOnboardingHelper::maybe_set_steps_completed(6); - - wp_send_json_success([ - 'payment_gateway_html' => MeprOnboardingHelper::get_payment_gateway_html(), - 'webhook_url' => $gateway->notify_url('whk'), - ]); - } - - public static function add_offline_payment_method() { - MeprUtils::validate_json_request('mepr_add_payment_method'); - - $mepr_options = MeprOptions::fetch(); - - if(!empty($mepr_options->integrations)) { - // Bail successfully if we already have a payment method - wp_send_json_success(); - } + if (isset($mepr_options->integrations[$gateway->id])) { + wp_send_json_error('Gateway already exists'); + } - $gateway = new MeprArtificialGateway(); + $integration = [ + $gateway->id => [ + 'id' => $gateway->id, + 'saved' => '1', + 'label' => 'Offline Payment', + 'gateway' => 'MeprArtificialGateway', + 'use_label' => true, + 'use_icon' => true, + 'use_desc' => true, + ], + ]; - if(isset($mepr_options->integrations[$gateway->id])) { - wp_send_json_error('Gateway already exists'); - } + $mepr_options->integrations = array_merge($mepr_options->integrations, $integration); + $mepr_options->store(false); - $integration = [ - $gateway->id => [ - 'id' => $gateway->id, - 'saved' => '1', - 'label' => 'Offline Payment', - 'gateway' => 'MeprArtificialGateway', - 'use_label' => true, - 'use_icon' => true, - 'use_desc' => true, - ] - ]; - - $mepr_options->integrations = array_merge($mepr_options->integrations, $integration); - $mepr_options->store(false); - - MeprOnboardingHelper::maybe_set_steps_completed(6); - - wp_send_json_success(); - } - - public static function remove_payment_method() { - $data = MeprUtils::get_json_request_data('mepr_remove_payment_method'); - - $saved_gateway_id = get_option('mepr_onboarding_payment_gateway'); - $gateway_id = isset($data['gateway_id']) ? sanitize_text_field($data['gateway_id']) : ''; - - if( $gateway_id === 'MeprAuthorizeGateway' ){ - MeprOnboardingHelper::maybe_set_steps_completed(5); - delete_option('mepr_onboarding_payment_gateway'); - wp_send_json_success(1); - return; - } + MeprOnboardingHelper::maybe_set_steps_completed(6); - if(empty($gateway_id) || empty($data['gateway_id']) || $gateway_id != $data['gateway_id']) { - wp_send_json_error(__('Bad request.', 'memberpress')); + wp_send_json_success(); } - $mepr_options = MeprOptions::fetch(); - $gateway = $mepr_options->payment_method($saved_gateway_id); + public static function remove_payment_method() + { + $data = MeprUtils::get_json_request_data('mepr_remove_payment_method'); - if(!$gateway instanceof MeprStripeGateway && !$gateway instanceof MeprPayPalCommerceGateway && !$gateway instanceof MeprAuthorizeGateway) { - wp_send_json_error(__('Bad request.', 'memberpress')); - } + $saved_gateway_id = get_option('mepr_onboarding_payment_gateway'); + $gateway_id = isset($data['gateway_id']) ? sanitize_text_field($data['gateway_id']) : ''; - // Don't delete a gateway that has a transaction or subscription - $mepr_db = MeprDb::fetch(); - $transaction_count = (int) $mepr_db->get_count($mepr_db->transactions, ['gateway' => $gateway_id]); - $subscription_count = (int) $mepr_db->get_count($mepr_db->subscriptions, ['gateway' => $gateway_id]); + if ($gateway_id === 'MeprAuthorizeGateway') { + MeprOnboardingHelper::maybe_set_steps_completed(5); + delete_option('mepr_onboarding_payment_gateway'); + wp_send_json_success(1); + return; + } - if($transaction_count > 0 || $subscription_count > 0) { - wp_send_json_error(__('Bad request.', 'memberpress')); - } + if (empty($gateway_id) || empty($data['gateway_id']) || $gateway_id != $data['gateway_id']) { + wp_send_json_error(__('Bad request.', 'memberpress')); + } - $integrations = $mepr_options->integrations; - unset($integrations[$gateway_id]); - $mepr_options->integrations = $integrations; - $mepr_options->store(false); - - if($gateway instanceof MeprStripeGateway) { - try { - $ctrl = MeprCtrlFactory::fetch('MeprStripConnectCtrl'); - $ctrl->disconnect($gateway->id, 'remote'); - } - catch(Exception $e) { - // ignore - } - } - elseif($gateway instanceof MeprPayPalCommerceGateway) { - $jwt = MeprAuthenticatorCtrl::generate_jwt([ - 'site_uuid' => get_option('mepr_authenticator_site_uuid') - ]); - - $options = [ - 'method' => 'DELETE', - 'headers' => MeprUtils::jwt_header($jwt, MEPR_PAYPAL_SERVICE_DOMAIN), - 'body' => [ - 'method-id' => $gateway->id, - ], - ]; - - if(apply_filters('mepr_onboarding_paypal_sandbox', false)) { - $endpoint = "/sandbox/credentials/{$gateway->id}"; - } - else { - $endpoint = "/credentials/{$gateway->id}"; - } - - wp_remote_request(MEPR_PAYPAL_SERVICE_URL . $endpoint, $options); - } + $mepr_options = MeprOptions::fetch(); + $gateway = $mepr_options->payment_method($saved_gateway_id); - delete_option('mepr_onboarding_payment_gateway'); - MeprOnboardingHelper::set_steps_completed(5); + if (!$gateway instanceof MeprStripeGateway && !$gateway instanceof MeprPayPalCommerceGateway && !$gateway instanceof MeprAuthorizeGateway) { + wp_send_json_error(__('Bad request.', 'memberpress')); + } - wp_send_json_success(); - } + // Don't delete a gateway that has a transaction or subscription + $mepr_db = MeprDb::fetch(); + $transaction_count = (int) $mepr_db->get_count($mepr_db->transactions, ['gateway' => $gateway_id]); + $subscription_count = (int) $mepr_db->get_count($mepr_db->subscriptions, ['gateway' => $gateway_id]); - public static function save_authorize_config() { - $data = MeprUtils::get_json_request_data('mepr_save_authorize_config'); + if ($transaction_count > 0 || $subscription_count > 0) { + wp_send_json_error(__('Bad request.', 'memberpress')); + } - $gateway_id = isset($data['gateway_id']) ? sanitize_text_field($data['gateway_id']) : ''; + $integrations = $mepr_options->integrations; + unset($integrations[$gateway_id]); + $mepr_options->integrations = $integrations; + $mepr_options->store(false); + + if ($gateway instanceof MeprStripeGateway) { + try { + $ctrl = MeprCtrlFactory::fetch('MeprStripConnectCtrl'); + $ctrl->disconnect($gateway->id, 'remote'); + } catch (Exception $e) { + // ignore + } + } elseif ($gateway instanceof MeprPayPalCommerceGateway) { + $jwt = MeprAuthenticatorCtrl::generate_jwt([ + 'site_uuid' => get_option('mepr_authenticator_site_uuid'), + ]); + + $options = [ + 'method' => 'DELETE', + 'headers' => MeprUtils::jwt_header($jwt, MEPR_PAYPAL_SERVICE_DOMAIN), + 'body' => [ + 'method-id' => $gateway->id, + ], + ]; + + if (apply_filters('mepr_onboarding_paypal_sandbox', false)) { + $endpoint = "/sandbox/credentials/{$gateway->id}"; + } else { + $endpoint = "/credentials/{$gateway->id}"; + } - if(empty($gateway_id)) { - wp_send_json_error(__('Bad request.', 'memberpress')); - } + wp_remote_request(MEPR_PAYPAL_SERVICE_URL . $endpoint, $options); + } - $mepr_options = MeprOptions::fetch(); - $gateway = $mepr_options->payment_method($gateway_id); - $integrations = $mepr_options->integrations; + delete_option('mepr_onboarding_payment_gateway'); + MeprOnboardingHelper::set_steps_completed(5); - if(!$gateway instanceof MeprAuthorizeGateway || !isset($integrations[$gateway->id]) || !is_array($integrations[$gateway->id])) { - wp_send_json_error(__('Bad request.', 'memberpress')); + wp_send_json_success(); } - $login_name = isset($data['login_name']) ? sanitize_text_field($data['login_name']) : ''; - $transaction_key = isset($data['transaction_key']) ? sanitize_text_field($data['transaction_key']) : ''; - $signature_key = isset($data['signature_key']) ? sanitize_text_field($data['signature_key']) : ''; + public static function save_authorize_config() + { + $data = MeprUtils::get_json_request_data('mepr_save_authorize_config'); - $errors = []; + $gateway_id = isset($data['gateway_id']) ? sanitize_text_field($data['gateway_id']) : ''; - if(empty($login_name)) { - $errors[] = 'login-name'; - } + if (empty($gateway_id)) { + wp_send_json_error(__('Bad request.', 'memberpress')); + } - if(empty($transaction_key)) { - $errors[] = 'transaction-key'; - } + $mepr_options = MeprOptions::fetch(); + $gateway = $mepr_options->payment_method($gateway_id); + $integrations = $mepr_options->integrations; - if(empty($signature_key)) { - $errors[] = 'signature-key'; - } + if (!$gateway instanceof MeprAuthorizeGateway || !isset($integrations[$gateway->id]) || !is_array($integrations[$gateway->id])) { + wp_send_json_error(__('Bad request.', 'memberpress')); + } - if(!empty($errors)) { - wp_send_json_error(['errors' => $errors]); - } + $login_name = isset($data['login_name']) ? sanitize_text_field($data['login_name']) : ''; + $transaction_key = isset($data['transaction_key']) ? sanitize_text_field($data['transaction_key']) : ''; + $signature_key = isset($data['signature_key']) ? sanitize_text_field($data['signature_key']) : ''; - $integrations[$gateway->id]['login_name'] = $login_name; - $integrations[$gateway->id]['transaction_key'] = $transaction_key; - $integrations[$gateway->id]['signature_key'] = $signature_key; + $errors = []; - $mepr_options->integrations = $integrations; - $mepr_options->store(false); + if (empty($login_name)) { + $errors[] = 'login-name'; + } - wp_send_json_success(MeprOnboardingHelper::get_payment_gateway_html()); - } + if (empty($transaction_key)) { + $errors[] = 'transaction-key'; + } - public static function save_new_rule() { - $data = MeprUtils::get_json_request_data('mepr_onboarding_save_new_rule'); + if (empty($signature_key)) { + $errors[] = 'signature-key'; + } - if(empty($data['content']) || empty($data['membershipname'])) { - wp_send_json_error(esc_html__('Bad request.', 'memberpress')); - } + if (!empty($errors)) { + wp_send_json_error(['errors' => $errors]); + } - if(!current_user_can('publish_posts')) { - wp_send_json_error(esc_html__('Sorry, you don\'t have permission to do this.', 'memberpress')); - } + $integrations[$gateway->id]['login_name'] = $login_name; + $integrations[$gateway->id]['transaction_key'] = $transaction_key; + $integrations[$gateway->id]['signature_key'] = $signature_key; - $rule_data = MeprOnboardingHelper::get_rules_step_data(); + $mepr_options->integrations = $integrations; + $mepr_options->store(false); - if(empty($rule_data['content_title']) || empty($rule_data['membership_title'])) { - wp_send_json_error(esc_html__('Bad request.', 'memberpress')); + wp_send_json_success(MeprOnboardingHelper::get_payment_gateway_html()); } - $content_id = $rule_data['content_id']; - $membership_id = $rule_data['membership_id']; + public static function save_new_rule() + { + $data = MeprUtils::get_json_request_data('mepr_onboarding_save_new_rule'); - $rule_title = sprintf(esc_html__('A Single %s', 'memberpress'),$rule_data['content_type']) . ': ' . $rule_data['content_title']; + if (empty($data['content']) || empty($data['membershipname'])) { + wp_send_json_error(esc_html__('Bad request.', 'memberpress')); + } - $post_id = wp_insert_post([ - 'post_type' => 'memberpressrule', - 'post_title' => wp_slash($rule_title), - 'post_status' => 'publish', - ], true); + if (!current_user_can('publish_posts')) { + wp_send_json_error(esc_html__('Sorry, you don\'t have permission to do this.', 'memberpress')); + } - if(is_wp_error($post_id)) { - wp_send_json_error($post_id->get_error_message()); - } + $rule_data = MeprOnboardingHelper::get_rules_step_data(); - $post = get_post($post_id); + if (empty($rule_data['content_title']) || empty($rule_data['membership_title'])) { + wp_send_json_error(esc_html__('Bad request.', 'memberpress')); + } - if(!$post instanceof WP_Post) { - wp_send_json_error(esc_html__('Post not found.', 'memberpress')); - } + $content_id = $rule_data['content_id']; + $membership_id = $rule_data['membership_id']; - MeprOnboardingHelper::set_rule_post_id($post_id); + $rule_title = sprintf(esc_html__('A Single %s', 'memberpress'), $rule_data['content_type']) . ': ' . $rule_data['content_title']; - $rule = new MeprRule($post_id); - $rule->mepr_type = sanitize_text_field($rule_data['mepr_type']); - $rule->mepr_content = sanitize_text_field($rule_data['content_id']); - $rule->store_meta(); + $post_id = wp_insert_post([ + 'post_type' => 'memberpressrule', + 'post_title' => wp_slash($rule_title), + 'post_status' => 'publish', + ], true); - // Delete rules first then add them back below - MeprRuleAccessCondition::delete_all_by_rule($post_id); + if (is_wp_error($post_id)) { + wp_send_json_error($post_id->get_error_message()); + } - // Let's store the access rules - $rule_access_condition = new MeprRuleAccessCondition(0); - $rule_access_condition->rule_id = $post_id; - $rule_access_condition->access_type = 'membership'; - $rule_access_condition->access_operator = 'is'; - $rule_access_condition->access_condition = $rule_data['membership_id']; - $rule_access_condition->store(); + $post = get_post($post_id); + if (!$post instanceof WP_Post) { + wp_send_json_error(esc_html__('Post not found.', 'memberpress')); + } - MeprOnboardingHelper::maybe_set_steps_completed(5); + MeprOnboardingHelper::set_rule_post_id($post_id); - wp_send_json_success([ - 'rule_data' => $rule_data, - ]); - } + $rule = new MeprRule($post_id); + $rule->mepr_type = sanitize_text_field($rule_data['mepr_type']); + $rule->mepr_content = sanitize_text_field($rule_data['content_id']); + $rule->store_meta(); - public static function get_rule() { - $data = MeprUtils::get_json_request_data('mepr_onboarding_get_rule'); + // Delete rules first then add them back below + MeprRuleAccessCondition::delete_all_by_rule($post_id); - if(empty($data['membership_rule_id'])) { - wp_send_json_error(esc_html__('Bad request.', 'memberpress')); - } + // Let's store the access rules + $rule_access_condition = new MeprRuleAccessCondition(0); + $rule_access_condition->rule_id = $post_id; + $rule_access_condition->access_type = 'membership'; + $rule_access_condition->access_operator = 'is'; + $rule_access_condition->access_condition = $rule_data['membership_id']; + $rule_access_condition->store(); - if(!current_user_can('publish_posts')) { - wp_send_json_error(esc_html__('Sorry, you don\'t have permission to do this.', 'memberpress')); - } - $post_id = sanitize_text_field($data['membership_rule_id']); - $post = get_post($post_id); + MeprOnboardingHelper::maybe_set_steps_completed(5); - if(!$post instanceof WP_Post) { - wp_send_json_error(esc_html__('Post not found.', 'memberpress')); + wp_send_json_success([ + 'rule_data' => $rule_data, + ]); } - wp_send_json_success(MeprOnboardingHelper::get_rules_step_data()); - } - - public static function install_correct_edition() { - MeprUtils::validate_json_request('mepr_onboarding_install_correct_edition'); - $li = get_site_transient('mepr_license_info'); - - if(!empty($li) && is_array($li) && !empty($li['url']) && MeprUtils::is_url($li['url'])) { - $result = self::install_plugin_silently($li['url'], array('overwrite_package' => true)); - - if($result instanceof WP_Error) { - wp_send_json_error($result->get_error_message()); - } - elseif($result === true) { - do_action('mepr_plugin_edition_changed'); - wp_send_json_success(__('The correct edition of MemberPress has been installed successfully.', 'memberpress')); - } - else { - wp_send_json_error(__('Failed to install the correct edition of MemberPress, please download it from memberpress.com and install it manually.', 'memberpress')); - } - } + public static function get_rule() + { + $data = MeprUtils::get_json_request_data('mepr_onboarding_get_rule'); + + if (empty($data['membership_rule_id'])) { + wp_send_json_error(esc_html__('Bad request.', 'memberpress')); + } - wp_send_json_error(__('License data not found', 'memberpress')); - } + if (!current_user_can('publish_posts')) { + wp_send_json_error(esc_html__('Sorry, you don\'t have permission to do this.', 'memberpress')); + } - private static function install_plugin_silently($url, $args) { - require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php'; + $post_id = sanitize_text_field($data['membership_rule_id']); + $post = get_post($post_id); - $skin = new Automatic_Upgrader_Skin(); - $upgrader = new Plugin_Upgrader($skin); + if (!$post instanceof WP_Post) { + wp_send_json_error(esc_html__('Post not found.', 'memberpress')); + } - if(!$skin->request_filesystem_credentials(false, WP_PLUGIN_DIR)) { - return new WP_Error('no_filesystem_access', __('Failed to get filesystem access', 'memberpress')); + wp_send_json_success(MeprOnboardingHelper::get_rules_step_data()); } - return $upgrader->install($url, $args); - } + public static function install_correct_edition() + { + MeprUtils::validate_json_request('mepr_onboarding_install_correct_edition'); + $li = get_site_transient('mepr_license_info'); - public static function install_addons() { - $data = MeprUtils::get_json_request_data('mepr_onboarding_install_addons'); + if (!empty($li) && is_array($li) && !empty($li['url']) && MeprUtils::is_url($li['url'])) { + $result = self::install_plugin_silently($li['url'], ['overwrite_package' => true]); - if(empty($data['addon_slug'])) { - wp_send_json_error(__('Bad request.', 'memberpress')); - } + if ($result instanceof WP_Error) { + wp_send_json_error($result->get_error_message()); + } elseif ($result === true) { + do_action('mepr_plugin_edition_changed'); + wp_send_json_success(__('The correct edition of MemberPress has been installed successfully.', 'memberpress')); + } else { + wp_send_json_error(__('Failed to install the correct edition of MemberPress, please download it from memberpress.com and install it manually.', 'memberpress')); + } + } - if(!current_user_can('publish_posts')) { - wp_send_json_error(__('Sorry, you don\'t have permission to do this.', 'memberpress')); + wp_send_json_error(__('License data not found', 'memberpress')); } - $features_data = MeprOnboardingHelper::get_selected_features_data(); - if(!isset($features_data['addons_installed'])){ - $features_data['addons_installed'] = array(); - } + private static function install_plugin_silently($url, $args) + { + require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php'; - if(!isset($features_data['addons_upgrade_failed'])){ - $features_data['addons_upgrade_failed'] = array(); + $skin = new Automatic_Upgrader_Skin(); + $upgrader = new Plugin_Upgrader($skin); + + if (!$skin->request_filesystem_credentials(false, WP_PLUGIN_DIR)) { + return new WP_Error('no_filesystem_access', __('Failed to get filesystem access', 'memberpress')); + } + + return $upgrader->install($url, $args); } - if(!empty($features_data['addons_not_installed'])) { - if(in_array($data['addon_slug'], $features_data['addons_not_installed'], true)) { - $license_addons = MeprUpdateCtrl::addons(true, true, true); + public static function install_addons() + { + $data = MeprUtils::get_json_request_data('mepr_onboarding_install_addons'); - // lets try to install and activate add-on. - foreach ($features_data['addons_not_installed'] as $i => $addon_slug) { - if($addon_slug == $data['addon_slug']) { - $response = self::maybe_install_activate_addons($license_addons, $addon_slug); - $next_addon = isset($features_data['addons_not_installed'][$i + 1]) ? $features_data['addons_not_installed'][$i + 1] : ''; + if (empty($data['addon_slug'])) { + wp_send_json_error(__('Bad request.', 'memberpress')); + } - if(1 === (int) $response) { - $features_data['addons_installed'][] = $addon_slug; - $features_data['addons_installed'] = array_unique($features_data['addons_installed']); + if (!current_user_can('publish_posts')) { + wp_send_json_error(__('Sorry, you don\'t have permission to do this.', 'memberpress')); + } - MeprOnboardingHelper::set_selected_features($features_data); - wp_send_json_success(array('addon_slug' => $addon_slug, 'message' => '', 'status' => 1, 'next_addon' => $next_addon)); - } - else { - $features_data['addons_upgrade_failed'][] = $addon_slug; - $features_data['addons_upgrade_failed'] = array_unique($features_data['addons_upgrade_failed']); + $features_data = MeprOnboardingHelper::get_selected_features_data(); + if (!isset($features_data['addons_installed'])) { + $features_data['addons_installed'] = []; + } - MeprOnboardingHelper::set_selected_features($features_data); - wp_send_json_success(array('addon_slug' => $addon_slug, 'message' => esc_html__('Unable to install. Please download and install manually.', 'memberpress'), 'status' => 0, 'next_addon' => $next_addon)); + if (!isset($features_data['addons_upgrade_failed'])) { + $features_data['addons_upgrade_failed'] = []; + } + + if (!empty($features_data['addons_not_installed'])) { + if (in_array($data['addon_slug'], $features_data['addons_not_installed'], true)) { + $license_addons = MeprUpdateCtrl::addons(true, true, true); + + // lets try to install and activate add-on. + foreach ($features_data['addons_not_installed'] as $i => $addon_slug) { + if ($addon_slug == $data['addon_slug']) { + $response = self::maybe_install_activate_addons($license_addons, $addon_slug); + $next_addon = isset($features_data['addons_not_installed'][$i + 1]) ? $features_data['addons_not_installed'][$i + 1] : ''; + + if (1 === (int) $response) { + $features_data['addons_installed'][] = $addon_slug; + $features_data['addons_installed'] = array_unique($features_data['addons_installed']); + + MeprOnboardingHelper::set_selected_features($features_data); + wp_send_json_success([ + 'addon_slug' => $addon_slug, + 'message' => '', + 'status' => 1, + 'next_addon' => $next_addon, + ]); + } else { + $features_data['addons_upgrade_failed'][] = $addon_slug; + $features_data['addons_upgrade_failed'] = array_unique($features_data['addons_upgrade_failed']); + + MeprOnboardingHelper::set_selected_features($features_data); + wp_send_json_success([ + 'addon_slug' => $addon_slug, + 'message' => esc_html__('Unable to install. Please download and install manually.', 'memberpress'), + 'status' => 0, + 'next_addon' => $next_addon, + ]); + } + } + } } - } } - } } - } - public static function load_complete_step() { - $data = MeprUtils::get_json_request_data('mepr_onboarding_load_complete_step'); + public static function load_complete_step() + { + $data = MeprUtils::get_json_request_data('mepr_onboarding_load_complete_step'); - wp_send_json_success(['html' => MeprOnboardingHelper::get_completed_step_urls_html()]); - } + wp_send_json_success(['html' => MeprOnboardingHelper::get_completed_step_urls_html()]); + } - public static function load_create_new_content() { - $data = MeprUtils::get_json_request_data('mepr_onboarding_load_create_new_content'); + public static function load_create_new_content() + { + $data = MeprUtils::get_json_request_data('mepr_onboarding_load_create_new_content'); - wp_send_json_success(['html' => MeprView::get_string('/admin/onboarding/parts/content_popup', get_defined_vars())]); - } + wp_send_json_success(['html' => MeprView::get_string('/admin/onboarding/parts/content_popup', get_defined_vars())]); + } - public static function enable_stripe_tax() { - $data = MeprUtils::get_json_request_data('mepr_enable_stripe_tax'); + public static function enable_stripe_tax() + { + $data = MeprUtils::get_json_request_data('mepr_enable_stripe_tax'); - if(!isset($data['gateway_id']) || !is_string($data['gateway_id'])) { - wp_send_json_error(__('Bad request.', 'memberpress')); - } + if (!isset($data['gateway_id']) || !is_string($data['gateway_id'])) { + wp_send_json_error(__('Bad request.', 'memberpress')); + } - $mepr_options = MeprOptions::fetch(); - $pm = $mepr_options->payment_method(sanitize_text_field($data['gateway_id'])); + $mepr_options = MeprOptions::fetch(); + $pm = $mepr_options->payment_method(sanitize_text_field($data['gateway_id'])); - if(!$pm instanceof MeprStripeGateway) { - wp_send_json_error(__('Invalid payment gateway', 'memberpress')); - } + if (!$pm instanceof MeprStripeGateway) { + wp_send_json_error(__('Invalid payment gateway', 'memberpress')); + } - try { - $tax_settings = (object) $pm->send_stripe_request('tax/settings', array(), 'get'); - - if($tax_settings->status != 'active') { - wp_send_json_error(false); - } - - update_option('mepr_calculate_taxes', true); - update_option('mepr_tax_stripe_enabled', true); - update_option('mepr_tax_calc_location', 'customer'); - update_option('mepr_tax_default_address', 'none'); - update_option('mepr_tax_stripe_payment_method', $pm->id); - update_option('mepr_stripe_tax_notice_dismissed', true); - update_option('mepr_tax_avalara_enabled', false); - update_option('mepr_tax_quaderno_enabled', false); - update_option('mepr_tax_taxjar_enabled', false); - delete_option('mepr_tax_stripe_deactivated'); - - wp_send_json_success(); - } - catch(Exception $e) { - wp_send_json_error($e->getMessage()); - } - } + try { + $tax_settings = (object) $pm->send_stripe_request('tax/settings', [], 'get'); - public static function load_finish_step() { - $data = MeprUtils::get_json_request_data('mepr_onboarding_load_finish_step'); - wp_send_json_success(['html' => MeprView::get_string('/admin/onboarding/parts/finish', get_defined_vars())]); - } + if ($tax_settings->status != 'active') { + wp_send_json_error(false); + } - public static function finish() { - MeprUtils::validate_json_request('mepr_onboarding_finish'); + update_option('mepr_calculate_taxes', true); + update_option('mepr_tax_stripe_enabled', true); + update_option('mepr_tax_calc_location', 'customer'); + update_option('mepr_tax_default_address', 'none'); + update_option('mepr_tax_stripe_payment_method', $pm->id); + update_option('mepr_stripe_tax_notice_dismissed', true); + update_option('mepr_tax_avalara_enabled', false); + update_option('mepr_tax_quaderno_enabled', false); + update_option('mepr_tax_taxjar_enabled', false); + delete_option('mepr_tax_stripe_deactivated'); + + wp_send_json_success(); + } catch (Exception $e) { + wp_send_json_error($e->getMessage()); + } + } + + public static function load_finish_step() + { + $data = MeprUtils::get_json_request_data('mepr_onboarding_load_finish_step'); + wp_send_json_success(['html' => MeprView::get_string('/admin/onboarding/parts/finish', get_defined_vars())]); + } - update_option('mepr_onboarding_complete', '1'); + public static function finish() + { + MeprUtils::validate_json_request('mepr_onboarding_finish'); - wp_send_json_success(); - } + update_option('mepr_onboarding_complete', '1'); - public static function settings_redirect() { - if(!is_user_logged_in() || wp_doing_ajax() || !is_admin() || is_network_admin() || !MeprUtils::is_mepr_admin() || MeprUtils::is_post_request()) { - return; + wp_send_json_success(); } - global $wpdb; + public static function settings_redirect() + { + if (!is_user_logged_in() || wp_doing_ajax() || !is_admin() || is_network_admin() || !MeprUtils::is_mepr_admin() || MeprUtils::is_post_request()) { + return; + } - wp_cache_flush(); - $wpdb->flush(); + global $wpdb; - $onboarding_complete = $wpdb->get_var("SELECT option_value FROM {$wpdb->options} WHERE option_name = 'mepr_onboarding_complete'"); + wp_cache_flush(); + $wpdb->flush(); - if($onboarding_complete === '1') { - nocache_headers(); - wp_redirect(admin_url('admin.php?page=memberpress-options'), 307); - exit; - } - } + $onboarding_complete = $wpdb->get_var("SELECT option_value FROM {$wpdb->options} WHERE option_name = 'mepr_onboarding_complete'"); - public static function admin_notice() { - if(!MeprUtils::is_memberpress_admin_page() || !MeprUtils::is_logged_in_and_an_admin()) { - return; + if ($onboarding_complete === '1') { + nocache_headers(); + wp_redirect(admin_url('admin.php?page=memberpress-options'), 307); + exit; + } } - if(!get_option('mepr_onboarded') || get_option('mepr_onboarding_complete') == '1' || get_transient('mepr_dismiss_notice_continue_onboarding')) { - return; - } - ?> + public static function admin_notice() + { + if (!MeprUtils::is_memberpress_admin_page() || !MeprUtils::is_logged_in_and_an_admin()) { + return; + } + + if (!get_option('mepr_onboarded') || get_option('mepr_onboarding_complete') == '1' || get_transient('mepr_dismiss_notice_continue_onboarding')) { + return; + } + ?>

    ', - '' + esc_html__("Hey, it looks like you started setting up MemberPress but didn't finish, %1\$sclick here to continue%2\$s.", 'memberpress'), + '', + '' ); ?>

    - integrations as $integration) { - if (isset($integration['gateway']) && $integration['gateway'] == 'MeprStripeGateway' && isset($integration['use_stripe_checkout'])) { - MeprView::render('/admin/stripe_checkout_deprecated'); - break; - } - } - } - } - - public static function route() { - $action = (isset($_REQUEST['action'])?$_REQUEST['action']:''); - if(MeprUtils::is_post_request() && $action == 'process-form') { - check_admin_referer('mepr_update_options', 'mepr_options_nonce'); - return self::process_form(); - } - else if($action == 'queue' and isset($_REQUEST['_wpnonce']) and - wp_verify_nonce($_REQUEST['_wpnonce'], 'MeprUpdateCtrl::manually_queue_update')) { - MeprUpdateCtrl::manually_queue_update(); - } - else if($action==='upgrade') { // Manually upgrade the database - $mepr_app = new MeprAppCtrl(); - try { - delete_transient('mepr_migration_error'); - $mepr_app->install(); - $message = __('Database Was Upgraded', 'memberpress'); - return self::display_form(array(),$message); - } - catch(MeprDbMigrationException $e) { - return self::display_form(array($e->getMessage()),''); - } - } - else if($action==='clear_tax_rates') { - check_admin_referer('clear_tax_rates', 'mepr_taxes_nonce'); - MeprTaxRate::destroy_all(); - $message = __('Tax rates have been cleared', 'memberpress'); - return self::display_form(array(),$message); +if (!defined('ABSPATH')) { + die('You are not allowed to call this page directly.'); +} + +class MeprOptionsCtrl extends MeprBaseCtrl +{ + public function load_hooks() + { + add_action('wp_ajax_mepr_activate_license', 'MeprOptionsCtrl::ajax_activate_license'); + add_action('wp_ajax_mepr_deactivate_license', 'MeprOptionsCtrl::ajax_deactivate_license'); + add_action('wp_ajax_mepr_install_license_edition', 'MeprOptionsCtrl::ajax_install_license_edition'); + add_action('wp_ajax_mepr_gateway_form', 'MeprOptionsCtrl::gateway_form'); + add_action('admin_enqueue_scripts', 'MeprOptionsCtrl::enqueue_scripts'); + add_action('admin_print_footer_scripts', 'MeprOptionsCtrl::enqueue_footer_scripts'); + add_action('admin_notices', 'MeprOptionsCtrl::maybe_show_stripe_checkout_warning'); + add_action('wp_ajax_mepr_validate_stripe_payment_method_types', 'MeprOptionsCtrl::validate_stripe_payment_method_types'); + add_action('wp_ajax_mepr_activate_stripe_payment_method', 'MeprOptionsCtrl::activate_stripe_payment_method'); + } + + public static function maybe_show_stripe_checkout_warning() + { + if (MeprUtils::is_get_request() && isset($_GET['page']) && $_GET['page'] == 'memberpress-options') { + $mepr_options = MeprOptions::fetch(); + + foreach ($mepr_options->integrations as $integration) { + if (isset($integration['gateway']) && $integration['gateway'] == 'MeprStripeGateway' && isset($integration['use_stripe_checkout'])) { + MeprView::render('/admin/stripe_checkout_deprecated'); + break; + } + } + } } - else { - return self::display_form(); + + public static function route() + { + $action = (isset($_REQUEST['action']) ? $_REQUEST['action'] : ''); + + if (MeprUtils::is_post_request() && $action == 'process-form') { + check_admin_referer('mepr_update_options', 'mepr_options_nonce'); + return self::process_form(); + } elseif ( + $action == 'queue' and isset($_REQUEST['_wpnonce']) and + wp_verify_nonce($_REQUEST['_wpnonce'], 'MeprUpdateCtrl::manually_queue_update') + ) { + MeprUpdateCtrl::manually_queue_update(); + } elseif ($action === 'upgrade') { // Manually upgrade the database + $mepr_app = new MeprAppCtrl(); + try { + delete_transient('mepr_migration_error'); + $mepr_app->install(); + $message = __('Database Was Upgraded', 'memberpress'); + return self::display_form([], $message); + } catch (MeprDbMigrationException $e) { + return self::display_form([$e->getMessage()], ''); + } + } elseif ($action === 'clear_tax_rates') { + check_admin_referer('clear_tax_rates', 'mepr_taxes_nonce'); + MeprTaxRate::destroy_all(); + $message = __('Tax rates have been cleared', 'memberpress'); + return self::display_form([], $message); + } else { + return self::display_form(); + } } - } - public static function display_form($errors=array(), $message='') { - $mepr_options = MeprOptions::fetch(); + public static function display_form($errors = [], $message = '') + { + $mepr_options = MeprOptions::fetch(); - if(MeprUtils::is_logged_in_and_an_admin()) { - if(!empty($mepr_options->mothership_license)) { - $li = get_site_transient('mepr_license_info'); + if (MeprUtils::is_logged_in_and_an_admin()) { + if (!empty($mepr_options->mothership_license)) { + $li = get_site_transient('mepr_license_info'); - if($li === false) { - MeprUpdateCtrl::manually_queue_update(); - $li = get_site_transient('mepr_license_info'); - } - } + if ($li === false) { + MeprUpdateCtrl::manually_queue_update(); + $li = get_site_transient('mepr_license_info'); + } + } - MeprView::render('/admin/options/form', get_defined_vars()); + MeprView::render('/admin/options/form', get_defined_vars()); + } } - } - public static function process_form() { - $mepr_options = MeprOptions::fetch(); + public static function process_form() + { + $mepr_options = MeprOptions::fetch(); - if(MeprUtils::is_logged_in_and_an_admin()) { - $errors = MeprHooks::apply_filters('mepr-validate-options', $mepr_options->validate($_POST, array())); + if (MeprUtils::is_logged_in_and_an_admin()) { + $errors = MeprHooks::apply_filters('mepr-validate-options', $mepr_options->validate($_POST, [])); - if(empty($errors)) { - MeprHooks::do_action('mepr-process-options', $_POST); - $settings = MeprHooks::apply_filters( 'mepr-saved-options', $_POST ); - $mepr_options->update($settings); - $mepr_options->store(); + if (empty($errors)) { + MeprHooks::do_action('mepr-process-options', $_POST); + $settings = MeprHooks::apply_filters('mepr-saved-options', $_POST); + $mepr_options->update($settings); + $mepr_options->store(); - // Ensure that the rewrite rules are flushed & in place - MeprUtils::flush_rewrite_rules(); //Don't call this before running ->update() - it borks stuff + // Ensure that the rewrite rules are flushed & in place + MeprUtils::flush_rewrite_rules(); // Don't call this before running ->update() - it borks stuff - $message = __('Options saved.', 'memberpress'); - } + $message = __('Options saved.', 'memberpress'); + } - if(!empty($mepr_options->mothership_license)) { - $li = get_site_transient('mepr_license_info'); + if (!empty($mepr_options->mothership_license)) { + $li = get_site_transient('mepr_license_info'); - if($li === false) { - MeprUpdateCtrl::manually_queue_update(); - $li = get_site_transient('mepr_license_info'); - } - } + if ($li === false) { + MeprUpdateCtrl::manually_queue_update(); + $li = get_site_transient('mepr_license_info'); + } + } - MeprView::render('/admin/options/form', get_defined_vars()); + MeprView::render('/admin/options/form', get_defined_vars()); + } } - } - public static function enqueue_footer_scripts() { - global $hook_suffix; - if ( $hook_suffix == 'memberpress_page_memberpress-options' ) { - ?> + public static function enqueue_footer_scripts() + { + global $hook_suffix; + if ($hook_suffix == 'memberpress_page_memberpress-options') { + ?> - __('Name:', 'memberpress'), - 'typeLabel' => __('Type:', 'memberpress'), - 'defaultLabel' => __('Default Value(s):', 'memberpress'), - 'signupLabel' => __('Show at Signup', 'memberpress'), - 'accountLabel' => __('Show in Account', 'memberpress'), - 'requiredLabel' => __('Required', 'memberpress'), - 'textOption' => __('Text', 'memberpress'), - 'textareaOption' => __('Textarea', 'memberpress'), - 'checkboxOption' => __('Checkbox', 'memberpress'), - 'dropdownOption' => __('Dropdown', 'memberpress'), - 'multiselectOption' => __('Multi-Select', 'memberpress'), - 'emailOption' => __('Email', 'memberpress'), - 'urlOption' => __('URL', 'memberpress'), - 'phoneOption' => __('Phone', 'memberpress'), - 'radiosOption' => __('Radio Buttons', 'memberpress'), - 'checkboxesOption' => __('Checkboxes', 'memberpress'), - 'fileuploadOption' => __('File Upload', 'memberpress'), - 'dateOption' => __('Date', 'memberpress'), - 'optionNameLabel' => __('Option Name:', 'memberpress'), - 'optionValueLabel' => __('Option Value:', 'memberpress'), - 'addOptionLabel' => __('Add Option', 'memberpress'), - 'show_fname_lname_id' => "#{$mepr_options->show_fname_lname_str}", - 'require_fname_lname_id' => "#{$mepr_options->require_fname_lname_str}", - 'jsUrl' => MEPR_JS_URL, - 'taxRateRemoveStr' => __('Are you sure you want to delete this Tax Rate?', 'memberpress'), - 'confirmPMDelete' => __('WARNING: Do not remove this Payment Method if you have active subscriptions using it. Doing so will prevent you from being notified of recurring payments for those subscriptions, which means your members will lose access to their paid content. Are you sure you want to delete this Payment Method?', 'memberpress'), - 'wpnonce' => wp_create_nonce(MEPR_PLUGIN_SLUG), - 'option_nonce' => wp_create_nonce('mepr_gateway_form_nonce'), - 'tax_nonce' => wp_create_nonce('mepr_taxes'), - 'activate_license_nonce' => wp_create_nonce('mepr_activate_license'), - 'activation_error' => __('An error occurred during activation: %s', 'memberpress'), - 'invalid_response' => __('Invalid response.', 'memberpress'), - 'ajax_error' => __('Ajax error.', 'memberpress'), - 'deactivate_license_nonce' => wp_create_nonce('mepr_deactivate_license'), - 'deactivate_confirm' => sprintf(__('Are you sure? MemberPress will not be functional on %s if this License Key is deactivated.', 'memberpress'), MeprUtils::site_domain()), - 'deactivation_error' => __('An error occurred during deactivation: %s', 'memberpress'), - 'install_license_edition_nonce' => wp_create_nonce('mepr_install_license_edition'), - 'validate_stripe_payment_methods_nonce' => wp_create_nonce('mepr_validate_stripe_payment_method_types'), - 'activate_stripe_payment_method_nonce' => wp_create_nonce('mepr_activate_stripe_payment_method'), - 'validate_stripe_tax_nonce' => wp_create_nonce('mepr_validate_stripe_tax'), - 'unable_to_verify_stripe_tax' => __('Unable to verify Stripe Tax status', 'memberpress') - ); - - wp_register_script('memberpress-i18n', MEPR_JS_URL.'/i18n.js', array('jquery'), MEPR_VERSION); - wp_register_script('mepr-uploader', MEPR_JS_URL.'/uploader.js', array(), MEPR_VERSION); - wp_enqueue_script('alpinejs', MEPR_JS_URL . '/vendor/alpine.min.js', array(), MEPR_VERSION, true); - wp_localize_script('memberpress-i18n', 'MeprI18n', array('states' => MeprUtils::states())); - - wp_register_script( 'mepr-clipboard-js', MEPR_JS_URL . '/clipboard.min.js', array(), MEPR_VERSION ); - wp_register_script( 'mepr-tooltipster', MEPR_JS_URL . '/tooltipster.bundle.min.js', array('jquery'), MEPR_VERSION ); - wp_register_script( 'mepr-copy-to-clipboard', MEPR_JS_URL . '/copy_to_clipboard.js', array('mepr-clipboard-js','mepr-tooltipster'), MEPR_VERSION ); - wp_localize_script( 'mepr-copy-to-clipboard', 'MeprClipboard', array( - 'copy_text' => __('Copy to Clipboard', 'memberpress'), - 'copied_text' => __('Copied!', 'memberpress'), - 'copy_error_text' => __('Oops, Copy Failed!', 'memberpress'), - )); - - wp_enqueue_script('mepr-options-js', MEPR_JS_URL.'/admin_options.js', - array( - 'jquery', - 'mepr-copy-to-clipboard', - 'mepr-settings-table-js', - 'mepr-admin-shared-js', - 'jquery-ui-sortable', - 'memberpress-i18n' - ), - MEPR_VERSION - ); - wp_localize_script('mepr-options-js', 'MeprOptions', $js_helpers); - - $email_locals = array( - 'set_email_defaults_nonce' => wp_create_nonce('set_email_defaults'), - 'send_test_email_nonce' => wp_create_nonce('send_test_email'), - ); - wp_enqueue_script('mepr-emails-js', MEPR_JS_URL.'/admin_emails.js', array('mepr-options-js'), MEPR_VERSION); - wp_localize_script('mepr-emails-js', 'MeprEmail', $email_locals); - MeprHooks::do_action('mepr-options-admin-enqueue-script', $hook); + __('Unauthorized', 'memberpress')))); + public static function enqueue_scripts($hook) + { + if ($hook == 'memberpress_page_memberpress-options') { + $mepr_options = MeprOptions::fetch(); + + wp_register_style('mepr-clipboardtip', MEPR_CSS_URL . '/tooltipster.bundle.min.css', [], MEPR_VERSION); + wp_register_style('mepr-clipboardtip-borderless', MEPR_CSS_URL . '/tooltipster-sideTip-borderless.min.css', ['mepr-clipboardtip'], MEPR_VERSION); + wp_enqueue_style('mp-options', MEPR_CSS_URL . '/admin-options.css', ['mepr-settings-table-css','mepr-clipboardtip','mepr-clipboardtip-borderless'], MEPR_VERSION); + wp_enqueue_style('mp-emails', MEPR_CSS_URL . '/admin-emails.css', ['mp-options'], MEPR_VERSION); + + $js_helpers = [ + 'nameLabel' => __('Name:', 'memberpress'), + 'typeLabel' => __('Type:', 'memberpress'), + 'defaultLabel' => __('Default Value(s):', 'memberpress'), + 'signupLabel' => __('Show at Signup', 'memberpress'), + 'accountLabel' => __('Show in Account', 'memberpress'), + 'requiredLabel' => __('Required', 'memberpress'), + 'textOption' => __('Text', 'memberpress'), + 'textareaOption' => __('Textarea', 'memberpress'), + 'checkboxOption' => __('Checkbox', 'memberpress'), + 'dropdownOption' => __('Dropdown', 'memberpress'), + 'multiselectOption' => __('Multi-Select', 'memberpress'), + 'emailOption' => __('Email', 'memberpress'), + 'urlOption' => __('URL', 'memberpress'), + 'phoneOption' => __('Phone', 'memberpress'), + 'radiosOption' => __('Radio Buttons', 'memberpress'), + 'checkboxesOption' => __('Checkboxes', 'memberpress'), + 'fileuploadOption' => __('File Upload', 'memberpress'), + 'dateOption' => __('Date', 'memberpress'), + 'optionNameLabel' => __('Option Name:', 'memberpress'), + 'optionValueLabel' => __('Option Value:', 'memberpress'), + 'addOptionLabel' => __('Add Option', 'memberpress'), + 'show_fname_lname_id' => "#{$mepr_options->show_fname_lname_str}", + 'require_fname_lname_id' => "#{$mepr_options->require_fname_lname_str}", + 'jsUrl' => MEPR_JS_URL, + 'taxRateRemoveStr' => __('Are you sure you want to delete this Tax Rate?', 'memberpress'), + 'confirmPMDelete' => __('WARNING: Do not remove this Payment Method if you have active subscriptions using it. Doing so will prevent you from being notified of recurring payments for those subscriptions, which means your members will lose access to their paid content. Are you sure you want to delete this Payment Method?', 'memberpress'), + 'wpnonce' => wp_create_nonce(MEPR_PLUGIN_SLUG), + 'option_nonce' => wp_create_nonce('mepr_gateway_form_nonce'), + 'tax_nonce' => wp_create_nonce('mepr_taxes'), + 'activate_license_nonce' => wp_create_nonce('mepr_activate_license'), + 'activation_error' => __('An error occurred during activation: %s', 'memberpress'), + 'invalid_response' => __('Invalid response.', 'memberpress'), + 'ajax_error' => __('Ajax error.', 'memberpress'), + 'deactivate_license_nonce' => wp_create_nonce('mepr_deactivate_license'), + 'deactivate_confirm' => sprintf(__('Are you sure? MemberPress will not be functional on %s if this License Key is deactivated.', 'memberpress'), MeprUtils::site_domain()), + 'deactivation_error' => __('An error occurred during deactivation: %s', 'memberpress'), + 'install_license_edition_nonce' => wp_create_nonce('mepr_install_license_edition'), + 'validate_stripe_payment_methods_nonce' => wp_create_nonce('mepr_validate_stripe_payment_method_types'), + 'activate_stripe_payment_method_nonce' => wp_create_nonce('mepr_activate_stripe_payment_method'), + 'validate_stripe_tax_nonce' => wp_create_nonce('mepr_validate_stripe_tax'), + 'unable_to_verify_stripe_tax' => __('Unable to verify Stripe Tax status', 'memberpress'), + ]; + + wp_register_script('memberpress-i18n', MEPR_JS_URL . '/i18n.js', ['jquery'], MEPR_VERSION); + wp_register_script('mepr-uploader', MEPR_JS_URL . '/uploader.js', [], MEPR_VERSION); + wp_enqueue_script('alpinejs', MEPR_JS_URL . '/vendor/alpine.min.js', [], MEPR_VERSION, true); + wp_localize_script('memberpress-i18n', 'MeprI18n', ['states' => MeprUtils::states()]); + + wp_register_script('mepr-clipboard-js', MEPR_JS_URL . '/clipboard.min.js', [], MEPR_VERSION); + wp_register_script('mepr-tooltipster', MEPR_JS_URL . '/tooltipster.bundle.min.js', ['jquery'], MEPR_VERSION); + wp_register_script('mepr-copy-to-clipboard', MEPR_JS_URL . '/copy_to_clipboard.js', ['mepr-clipboard-js','mepr-tooltipster'], MEPR_VERSION); + wp_localize_script('mepr-copy-to-clipboard', 'MeprClipboard', [ + 'copy_text' => __('Copy to Clipboard', 'memberpress'), + 'copied_text' => __('Copied!', 'memberpress'), + 'copy_error_text' => __('Oops, Copy Failed!', 'memberpress'), + ]); + + wp_enqueue_script( + 'mepr-options-js', + MEPR_JS_URL . '/admin_options.js', + [ + 'jquery', + 'mepr-copy-to-clipboard', + 'mepr-settings-table-js', + 'mepr-admin-shared-js', + 'jquery-ui-sortable', + 'memberpress-i18n', + ], + MEPR_VERSION + ); + wp_localize_script('mepr-options-js', 'MeprOptions', $js_helpers); + + $email_locals = [ + 'set_email_defaults_nonce' => wp_create_nonce('set_email_defaults'), + 'send_test_email_nonce' => wp_create_nonce('send_test_email'), + ]; + wp_enqueue_script('mepr-emails-js', MEPR_JS_URL . '/admin_emails.js', ['mepr-options-js'], MEPR_VERSION); + wp_localize_script('mepr-emails-js', 'MeprEmail', $email_locals); + MeprHooks::do_action('mepr-options-admin-enqueue-script', $hook); + } } - $mepr_options = MeprOptions::fetch(); + public static function gateway_form() + { + check_ajax_referer('mepr_gateway_form_nonce', 'option_nonce'); - if(!isset($_POST['g']) or empty($_POST['g'])) { - $gateways = array_keys(MeprGatewayFactory::all()); + if (!is_admin()) { + die(json_encode(['error' => __('Unauthorized', 'memberpress')])); + } - if(empty($gateways)) { - die(json_encode(array('error'=>__('No gateways were found', 'memberpress')))); - } + $mepr_options = MeprOptions::fetch(); - // Artificially set the gateway to the first available - $gateway = $gateways[0]; - } - else { - $gateway = $_POST['g']; - } + if (!isset($_POST['g']) or empty($_POST['g'])) { + $gateways = array_keys(MeprGatewayFactory::all()); - try { - $obj = MeprGatewayFactory::fetch($gateway); - } - catch(Exception $e) { - die($e->getMessage()); - } - - ob_start(); - MeprView::render("/admin/options/gateway", get_defined_vars()); - $form = ob_get_clean(); + if (empty($gateways)) { + die(json_encode(['error' => __('No gateways were found', 'memberpress')])); + } - die( json_encode( array( 'form' => $form, 'id' => $obj->id ) ) ); - } + // Artificially set the gateway to the first available + $gateway = $gateways[0]; + } else { + $gateway = $_POST['g']; + } - public static function ajax_activate_license() { - if(!MeprUtils::is_post_request() || !isset($_POST['key']) || !is_string($_POST['key'])) { - wp_send_json_error(sprintf(__('An error occurred during activation: %s', 'memberpress'), __('Bad request.', 'memberpress'))); - } + try { + $obj = MeprGatewayFactory::fetch($gateway); + } catch (Exception $e) { + die($e->getMessage()); + } - if(!MeprUtils::is_logged_in_and_an_admin()) { - wp_send_json_error(__('Sorry, you don\'t have permission to do this.', 'memberpress')); - } + ob_start(); + MeprView::render('/admin/options/gateway', get_defined_vars()); + $form = ob_get_clean(); - if(!check_ajax_referer('mepr_activate_license', false, false)) { - wp_send_json_error(sprintf(__('An error occurred during activation: %s', 'memberpress'), __('Security check failed.', 'memberpress'))); + die(json_encode([ + 'form' => $form, + 'id' => $obj->id, + ])); } - $mepr_options = MeprOptions::fetch(); - $license_key = sanitize_text_field(wp_unslash($_POST['key'])); - - try { - $act = MeprUpdateCtrl::activate_license($license_key); - $li = get_site_transient('mepr_license_info'); - $onboarding = isset($_POST['onboarding']) && sanitize_text_field(wp_unslash($_POST['onboarding'])) == '1'; - - if($onboarding) { - $output = ''; - } - else { - $output = sprintf('

    %s

    ', esc_html($act['message'])); - } - - if(is_array($li)) { - $editions = MeprUtils::is_incorrect_edition_installed(); - $automatic_updates = !empty($mepr_options->auto_updates) ? $mepr_options->auto_updates : 'all'; - - if(is_array($editions) && $editions['license']['index'] > $editions['installed']['index'] && $automatic_updates != 'none') { - // The installed plugin is a lower edition, try to upgrade to the higher license edition - if(!empty($li['url']) && MeprUtils::is_url($li['url'])) { - $result = self::install_plugin_silently($li['url'], array('overwrite_package' => true)); - - if($result === true) { - do_action('mepr_plugin_edition_changed'); - wp_send_json_success(true); - } - } + public static function ajax_activate_license() + { + if (!MeprUtils::is_post_request() || !isset($_POST['key']) || !is_string($_POST['key'])) { + wp_send_json_error(sprintf(__('An error occurred during activation: %s', 'memberpress'), __('Bad request.', 'memberpress'))); } - if($onboarding) { - $output .= MeprView::get_string('/admin/onboarding/active_license', get_defined_vars()); + if (!MeprUtils::is_logged_in_and_an_admin()) { + wp_send_json_error(__('Sorry, you don\'t have permission to do this.', 'memberpress')); } - else { - $output .= MeprView::get_string('/admin/options/active_license', get_defined_vars()); + + if (!check_ajax_referer('mepr_activate_license', false, false)) { + wp_send_json_error(sprintf(__('An error occurred during activation: %s', 'memberpress'), __('Security check failed.', 'memberpress'))); } - } - else { - $output .= sprintf('

    %s

    ', esc_html__('The license information is not available, try refreshing the page.', 'memberpress')); - } - wp_send_json_success($output); - } - catch(Exception $e) { - try { - $expires = MeprUpdateCtrl::send_mothership_request("/license_keys/expires_at/$license_key"); + $mepr_options = MeprOptions::fetch(); + $license_key = sanitize_text_field(wp_unslash($_POST['key'])); - if(isset($expires['expires_at'])) { - $expires_at = strtotime($expires['expires_at']); + try { + $act = MeprUpdateCtrl::activate_license($license_key); + $li = get_site_transient('mepr_license_info'); + $onboarding = isset($_POST['onboarding']) && sanitize_text_field(wp_unslash($_POST['onboarding'])) == '1'; - if($expires_at && $expires_at < time()) { - $licenses = MeprUpdateCtrl::send_mothership_request("/license_keys/list_keys/$license_key"); + if ($onboarding) { + $output = ''; + } else { + $output = sprintf('

    %s

    ', esc_html($act['message'])); + } - if(!empty($licenses) && is_array($licenses)) { - $highest_edition_index = -1; - $highest_license = null; + if (is_array($li)) { + $editions = MeprUtils::is_incorrect_edition_installed(); + $automatic_updates = !empty($mepr_options->auto_updates) ? $mepr_options->auto_updates : 'all'; - foreach($licenses as $license) { - $edition = MeprUtils::get_edition($license['product_slug']); + if (is_array($editions) && $editions['license']['index'] > $editions['installed']['index'] && $automatic_updates != 'none') { + // The installed plugin is a lower edition, try to upgrade to the higher license edition + if (!empty($li['url']) && MeprUtils::is_url($li['url'])) { + $result = self::install_plugin_silently($li['url'], ['overwrite_package' => true]); - if(is_array($edition) && $edition['index'] > $highest_edition_index) { - $highest_edition_index = $edition['index']; - $highest_license = $license; + if ($result === true) { + do_action('mepr_plugin_edition_changed'); + wp_send_json_success(true); + } + } } - } - - if(is_array($highest_license)) { - wp_send_json_error( - sprintf( - /* translators: %1$s: the product name, %2$s: open link tag, %3$s: close link tag */ - esc_html__('This License Key has expired, but you have an active license for %1$s, %2$sclick here%3$s to activate using this license instead.', 'memberpress'), - '' . esc_html($highest_license['product_name']) . '', - sprintf('', esc_attr($highest_license['license_key'])), - '' - ) - ); - } - } - } - } - } - catch(Exception $ignore) { - // Nothing we can do, let it fail. - } - wp_send_json_error($e->getMessage()); - } - } + if ($onboarding) { + $output .= MeprView::get_string('/admin/onboarding/active_license', get_defined_vars()); + } else { + $output .= MeprView::get_string('/admin/options/active_license', get_defined_vars()); + } + } else { + $output .= sprintf('

    %s

    ', esc_html__('The license information is not available, try refreshing the page.', 'memberpress')); + } - public static function ajax_deactivate_license() { - if(!MeprUtils::is_post_request()) { - wp_send_json_error(sprintf(__('An error occurred during deactivation: %s', 'memberpress'), __('Bad request.', 'memberpress'))); - } + wp_send_json_success($output); + } catch (Exception $e) { + try { + $expires = MeprUpdateCtrl::send_mothership_request("/license_keys/expires_at/$license_key"); + + if (isset($expires['expires_at'])) { + $expires_at = strtotime($expires['expires_at']); + + if ($expires_at && $expires_at < time()) { + $licenses = MeprUpdateCtrl::send_mothership_request("/license_keys/list_keys/$license_key"); + + if (!empty($licenses) && is_array($licenses)) { + $highest_edition_index = -1; + $highest_license = null; + + foreach ($licenses as $license) { + $edition = MeprUtils::get_edition($license['product_slug']); + + if (is_array($edition) && $edition['index'] > $highest_edition_index) { + $highest_edition_index = $edition['index']; + $highest_license = $license; + } + } + + if (is_array($highest_license)) { + wp_send_json_error( + sprintf( + /* translators: %1$s: the product name, %2$s: open link tag, %3$s: close link tag */ + esc_html__('This License Key has expired, but you have an active license for %1$s, %2$sclick here%3$s to activate using this license instead.', 'memberpress'), + '' . esc_html($highest_license['product_name']) . '', + sprintf('', esc_attr($highest_license['license_key'])), + '' + ) + ); + } + } + } + } + } catch (Exception $ignore) { + // Nothing we can do, let it fail. + } - if(!MeprUtils::is_logged_in_and_an_admin()) { - wp_send_json_error(__('Sorry, you don\'t have permission to do this.', 'memberpress')); + wp_send_json_error($e->getMessage()); + } } - if(!check_ajax_referer('mepr_deactivate_license', false, false)) { - wp_send_json_error(sprintf(__('An error occurred during deactivation: %s', 'memberpress'), __('Security check failed.', 'memberpress'))); - } + public static function ajax_deactivate_license() + { + if (!MeprUtils::is_post_request()) { + wp_send_json_error(sprintf(__('An error occurred during deactivation: %s', 'memberpress'), __('Bad request.', 'memberpress'))); + } - $mepr_options = MeprOptions::fetch(); - $act = MeprUpdateCtrl::deactivate_license(); + if (!MeprUtils::is_logged_in_and_an_admin()) { + wp_send_json_error(__('Sorry, you don\'t have permission to do this.', 'memberpress')); + } - $output = sprintf('

    %s

    ', esc_html($act['message'])); - $output .= MeprView::get_string('/admin/options/inactive_license', get_defined_vars()); + if (!check_ajax_referer('mepr_deactivate_license', false, false)) { + wp_send_json_error(sprintf(__('An error occurred during deactivation: %s', 'memberpress'), __('Security check failed.', 'memberpress'))); + } - wp_send_json_success($output); - } + $mepr_options = MeprOptions::fetch(); + $act = MeprUpdateCtrl::deactivate_license(); - public static function install_plugin_silently($url, $args) { - require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php'; + $output = sprintf('

    %s

    ', esc_html($act['message'])); + $output .= MeprView::get_string('/admin/options/inactive_license', get_defined_vars()); - if(!function_exists('request_filesystem_credentials')) { - require_once ABSPATH . 'wp-admin/includes/file.php'; + wp_send_json_success($output); } - $skin = new Automatic_Upgrader_Skin(); - $upgrader = new Plugin_Upgrader($skin); - - if(!$skin->request_filesystem_credentials(false, WP_PLUGIN_DIR)) { - return new WP_Error('no_filesystem_access', __('Failed to get filesystem access', 'memberpress')); - } + public static function install_plugin_silently($url, $args) + { + require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php'; - return $upgrader->install($url, $args); - } + if (!function_exists('request_filesystem_credentials')) { + require_once ABSPATH . 'wp-admin/includes/file.php'; + } - public static function ajax_install_license_edition() { - if(!MeprUtils::is_post_request()) { - wp_send_json_error(__('Bad request.', 'memberpress')); - } + $skin = new Automatic_Upgrader_Skin(); + $upgrader = new Plugin_Upgrader($skin); - if(!current_user_can('update_plugins')) { - wp_send_json_error(__('Sorry, you don\'t have permission to do this.', 'memberpress')); - } + if (!$skin->request_filesystem_credentials(false, WP_PLUGIN_DIR)) { + return new WP_Error('no_filesystem_access', __('Failed to get filesystem access', 'memberpress')); + } - if(!check_ajax_referer('mepr_install_license_edition', false, false)) { - wp_send_json_error(__('Security check failed.', 'memberpress')); + return $upgrader->install($url, $args); } - $li = get_site_transient('mepr_license_info'); - - if(!empty($li) && is_array($li) && !empty($li['url']) && MeprUtils::is_url($li['url'])) { - $result = self::install_plugin_silently($li['url'], array('overwrite_package' => true)); - - if($result instanceof WP_Error) { - wp_send_json_error($result->get_error_message()); - } - elseif($result === true) { - do_action('mepr_plugin_edition_changed'); - wp_send_json_success(__('The correct edition of MemberPress has been installed successfully.', 'memberpress')); - } - else { - wp_send_json_error(__('Failed to install the correct edition of MemberPress, please download it from memberpress.com and install it manually.', 'memberpress')); - } - } + public static function ajax_install_license_edition() + { + if (!MeprUtils::is_post_request()) { + wp_send_json_error(__('Bad request.', 'memberpress')); + } - wp_send_json_error(__('License data not found', 'memberpress')); - } + if (!current_user_can('update_plugins')) { + wp_send_json_error(__('Sorry, you don\'t have permission to do this.', 'memberpress')); + } - public static function validate_stripe_payment_method_types() { - if(!MeprUtils::is_post_request()) { - wp_send_json_error(__('Bad request.', 'memberpress')); - } + if (!check_ajax_referer('mepr_install_license_edition', false, false)) { + wp_send_json_error(__('Security check failed.', 'memberpress')); + } - if(!MeprUtils::is_logged_in_and_an_admin()) { - wp_send_json_error(__('Sorry, you don\'t have permission to do this.', 'memberpress')); - } + $li = get_site_transient('mepr_license_info'); - if(!check_ajax_referer('mepr_validate_stripe_payment_method_types', false, false)) { - wp_send_json_error(__('Security check failed.', 'memberpress')); - } + if (!empty($li) && is_array($li) && !empty($li['url']) && MeprUtils::is_url($li['url'])) { + $result = self::install_plugin_silently($li['url'], ['overwrite_package' => true]); - $gateway_id = isset($_POST['gateway_id']) ? sanitize_text_field(wp_unslash($_POST['gateway_id'])) : ''; - $payment_method_types = isset($_POST['payment_method_types']) && is_array($_POST['payment_method_types']) ? array_filter(array_map('sanitize_key', array_map('wp_unslash', $_POST['payment_method_types']))) : []; + if ($result instanceof WP_Error) { + wp_send_json_error($result->get_error_message()); + } elseif ($result === true) { + do_action('mepr_plugin_edition_changed'); + wp_send_json_success(__('The correct edition of MemberPress has been installed successfully.', 'memberpress')); + } else { + wp_send_json_error(__('Failed to install the correct edition of MemberPress, please download it from memberpress.com and install it manually.', 'memberpress')); + } + } - if(empty($gateway_id) || empty($payment_method_types)) { - wp_send_json_error(__('Bad request.', 'memberpress')); + wp_send_json_error(__('License data not found', 'memberpress')); } - $mepr_options = MeprOptions::fetch(); - $pm = $mepr_options->payment_method($gateway_id); + public static function validate_stripe_payment_method_types() + { + if (!MeprUtils::is_post_request()) { + wp_send_json_error(__('Bad request.', 'memberpress')); + } - if(!$pm instanceof MeprStripeGateway) { - wp_send_json_error(__('Bad request.', 'memberpress')); - } + if (!MeprUtils::is_logged_in_and_an_admin()) { + wp_send_json_error(__('Sorry, you don\'t have permission to do this.', 'memberpress')); + } - try { - if(in_array('link', $payment_method_types, true) && !self::is_payment_method_active('link', $pm->id)) { - self::activate_payment_method('link', $pm); - } + if (!check_ajax_referer('mepr_validate_stripe_payment_method_types', false, false)) { + wp_send_json_error(__('Security check failed.', 'memberpress')); + } - $pm->create_test_payment_intent($payment_method_types); + $gateway_id = isset($_POST['gateway_id']) ? sanitize_text_field(wp_unslash($_POST['gateway_id'])) : ''; + $payment_method_types = isset($_POST['payment_method_types']) && is_array($_POST['payment_method_types']) ? array_filter(array_map('sanitize_key', array_map('wp_unslash', $_POST['payment_method_types']))) : []; - wp_send_json_success(); - } - catch(Exception $e) { - wp_send_json_error($e->getMessage()); - } - } + if (empty($gateway_id) || empty($payment_method_types)) { + wp_send_json_error(__('Bad request.', 'memberpress')); + } - public static function activate_stripe_payment_method() { - if(!MeprUtils::is_post_request()) { - wp_send_json_error(__('Bad request.', 'memberpress')); - } + $mepr_options = MeprOptions::fetch(); + $pm = $mepr_options->payment_method($gateway_id); - if(!MeprUtils::is_logged_in_and_an_admin()) { - wp_send_json_error(__('Sorry, you don\'t have permission to do this.', 'memberpress')); - } + if (!$pm instanceof MeprStripeGateway) { + wp_send_json_error(__('Bad request.', 'memberpress')); + } - if(!check_ajax_referer('mepr_activate_stripe_payment_method', false, false)) { - wp_send_json_error(__('Security check failed.', 'memberpress')); - } + try { + if (in_array('link', $payment_method_types, true) && !self::is_payment_method_active('link', $pm->id)) { + self::activate_payment_method('link', $pm); + } - $gateway_id = isset($_POST['gateway_id']) ? sanitize_text_field(wp_unslash($_POST['gateway_id'])) : ''; - $payment_method_type = isset($_POST['payment_method_type']) ? sanitize_key(wp_unslash($_POST['payment_method_type'])) : ''; - $supported_types = ['apple_pay', 'google_pay']; + $pm->create_test_payment_intent($payment_method_types); - if(empty($gateway_id) || empty($payment_method_type) || !in_array($payment_method_type, $supported_types, true)) { - wp_send_json_error(__('Bad request.', 'memberpress')); + wp_send_json_success(); + } catch (Exception $e) { + wp_send_json_error($e->getMessage()); + } } - $mepr_options = MeprOptions::fetch(); - $pm = $mepr_options->payment_method($gateway_id); + public static function activate_stripe_payment_method() + { + if (!MeprUtils::is_post_request()) { + wp_send_json_error(__('Bad request.', 'memberpress')); + } - if(!$pm instanceof MeprStripeGateway) { - wp_send_json_error(__('Bad request.', 'memberpress')); - } + if (!MeprUtils::is_logged_in_and_an_admin()) { + wp_send_json_error(__('Sorry, you don\'t have permission to do this.', 'memberpress')); + } - try { - if($payment_method_type == 'apple_pay') { - $root_dir = isset($_SERVER['DOCUMENT_ROOT']) ? wp_unslash($_SERVER['DOCUMENT_ROOT']) : ABSPATH; - $domain_association_file_name = 'apple-developer-merchantid-domain-association'; - $well_known_dir = untrailingslashit($root_dir) . '/.well-known'; - $full_path = "$well_known_dir/$domain_association_file_name"; + if (!check_ajax_referer('mepr_activate_stripe_payment_method', false, false)) { + wp_send_json_error(__('Security check failed.', 'memberpress')); + } - if(!file_exists($full_path)) { - if(!is_dir($well_known_dir) && !@mkdir($well_known_dir, 0755) && !is_dir($well_known_dir)) { - throw new Exception(__('Unable to create domain association folder in domain root', 'memberpress')); - } + $gateway_id = isset($_POST['gateway_id']) ? sanitize_text_field(wp_unslash($_POST['gateway_id'])) : ''; + $payment_method_type = isset($_POST['payment_method_type']) ? sanitize_key(wp_unslash($_POST['payment_method_type'])) : ''; + $supported_types = ['apple_pay', 'google_pay']; - if(!@copy(MEPR_GATEWAYS_PATH . "/stripe/$domain_association_file_name", $full_path)) { - throw new Exception(__('Unable to copy the domain association file', 'memberpress')); - } + if (empty($gateway_id) || empty($payment_method_type) || !in_array($payment_method_type, $supported_types, true)) { + wp_send_json_error(__('Bad request.', 'memberpress')); } - } - self::activate_payment_method($payment_method_type, $pm); + $mepr_options = MeprOptions::fetch(); + $pm = $mepr_options->payment_method($gateway_id); - wp_send_json_success(); - } - catch(Exception $e) { - wp_send_json_error($e->getMessage()); - } - } - - /** - * Is the given payment method active on the given Stripe gateway? - * - * Checks that the current site is registered as a payment method domain, and that the given payment method is active. - * - * @param string $payment_method_type The payment method type: 'apple_pay', 'google_pay' or 'link' - * @param string $gateway_id The ID of the Stripe payment method - * @return bool - */ - public static function is_payment_method_active($payment_method_type, $gateway_id) { - $pmd = get_option("mepr_stripe_payment_method_domain_$gateway_id"); - - if( - is_array($pmd) && - isset($pmd['domain_name']) && $pmd['domain_name'] == self::get_site_domain() && - isset($pmd['enabled']) && $pmd['enabled'] && - isset($pmd[$payment_method_type]['status']) && $pmd[$payment_method_type]['status'] == 'active' - ) { - return true; - } + if (!$pm instanceof MeprStripeGateway) { + wp_send_json_error(__('Bad request.', 'memberpress')); + } - return false; - } + try { + if ($payment_method_type == 'apple_pay') { + $root_dir = isset($_SERVER['DOCUMENT_ROOT']) ? wp_unslash($_SERVER['DOCUMENT_ROOT']) : ABSPATH; + $domain_association_file_name = 'apple-developer-merchantid-domain-association'; + $well_known_dir = untrailingslashit($root_dir) . '/.well-known'; + $full_path = "$well_known_dir/$domain_association_file_name"; + + if (!file_exists($full_path)) { + if (!is_dir($well_known_dir) && !@mkdir($well_known_dir, 0755) && !is_dir($well_known_dir)) { + throw new Exception(__('Unable to create domain association folder in domain root', 'memberpress')); + } + + if (!@copy(MEPR_GATEWAYS_PATH . "/stripe/$domain_association_file_name", $full_path)) { + throw new Exception(__('Unable to copy the domain association file', 'memberpress')); + } + } + } - /** - * Activate the given payment method on the given gateway - * - * Registers the current site as a Stripe payment method domain, in both live and test mode, and ensures that the - * given payment method is active. - * - * @param string $payment_method_type - * @param MeprStripeGateway $pm - * @throws Exception If the payment method couldn't be activated - * @throws MeprHttpException - * @throws MeprRemoteException - */ - public static function activate_payment_method($payment_method_type, MeprStripeGateway $pm) { - $domain = self::get_site_domain(); + self::activate_payment_method($payment_method_type, $pm); - add_filter('mepr_stripe_is_test_mode', '__return_false'); + wp_send_json_success(); + } catch (Exception $e) { + wp_send_json_error($e->getMessage()); + } + } - $live = $pm->send_stripe_request('payment_method_domains', [ - 'domain_name' => $domain, - 'enabled' => 'true' - ], 'post'); + /** + * Is the given payment method active on the given Stripe gateway? + * + * Checks that the current site is registered as a payment method domain, and that the given payment method is active. + * + * @param string $payment_method_type The payment method type: 'apple_pay', 'google_pay' or 'link' + * @param string $gateway_id The ID of the Stripe payment method + * @return boolean + */ + public static function is_payment_method_active($payment_method_type, $gateway_id) + { + $pmd = get_option("mepr_stripe_payment_method_domain_$gateway_id"); + + if ( + is_array($pmd) && + isset($pmd['domain_name']) && $pmd['domain_name'] == self::get_site_domain() && + isset($pmd['enabled']) && $pmd['enabled'] && + isset($pmd[$payment_method_type]['status']) && $pmd[$payment_method_type]['status'] == 'active' + ) { + return true; + } - if($live[$payment_method_type]['status'] != 'active') { - $live = $pm->send_stripe_request("payment_method_domains/{$live['id']}/validate", [], 'post'); + return false; + } + + /** + * Activate the given payment method on the given gateway + * + * Registers the current site as a Stripe payment method domain, in both live and test mode, and ensures that the + * given payment method is active. + * + * @param string $payment_method_type + * @param MeprStripeGateway $pm + * @throws Exception If the payment method couldn't be activated + * @throws MeprHttpException + * @throws MeprRemoteException + */ + public static function activate_payment_method($payment_method_type, MeprStripeGateway $pm) + { + $domain = self::get_site_domain(); + + add_filter('mepr_stripe_is_test_mode', '__return_false'); + + $live = $pm->send_stripe_request('payment_method_domains', [ + 'domain_name' => $domain, + 'enabled' => 'true', + ], 'post'); + + if ($live[$payment_method_type]['status'] != 'active') { + $live = $pm->send_stripe_request("payment_method_domains/{$live['id']}/validate", [], 'post'); + + if ($live[$payment_method_type]['status'] != 'active') { + if (isset($live[$payment_method_type]['status_details']['error_message'])) { + $message = $live[$payment_method_type]['status_details']['error_message']; + } else { + $message = sprintf(__('Unable to activate payment method `%s`', 'memberpress'), $payment_method_type); + } - if($live[$payment_method_type]['status'] != 'active') { - if(isset($live[$payment_method_type]['status_details']['error_message'])) { - $message = $live[$payment_method_type]['status_details']['error_message']; - } - else { - $message = sprintf(__('Unable to activate payment method `%s`', 'memberpress'), $payment_method_type); + throw new Exception($message); + } } - throw new Exception($message); - } - } + remove_filter('mepr_stripe_is_test_mode', '__return_false'); - remove_filter('mepr_stripe_is_test_mode', '__return_false'); + update_option("mepr_stripe_payment_method_domain_$pm->id", $live); - update_option("mepr_stripe_payment_method_domain_$pm->id", $live); + add_filter('mepr_stripe_is_test_mode', '__return_true'); - add_filter('mepr_stripe_is_test_mode', '__return_true'); + try { + $test = $pm->send_stripe_request('payment_method_domains', [ + 'domain_name' => $domain, + 'enabled' => 'true', + ], 'post'); - try { - $test = $pm->send_stripe_request('payment_method_domains', [ - 'domain_name' => $domain, - 'enabled' => 'true' - ], 'post'); + if ($test[$payment_method_type]['status'] != 'active') { + $pm->send_stripe_request("payment_method_domains/{$test['id']}/validate", [], 'post'); + } + } catch (Exception $e) { + // ignore exceptions in Test mode + } - if($test[$payment_method_type]['status'] != 'active') { - $pm->send_stripe_request("payment_method_domains/{$test['id']}/validate", [], 'post'); - } - } - catch(Exception $e) { - // ignore exceptions in Test mode + remove_filter('mepr_stripe_is_test_mode', '__return_true'); } - remove_filter('mepr_stripe_is_test_mode', '__return_true'); - } - - /** - * Get the domain of the current site, to be registered as a payment method domain - * - * @return string - */ - private static function get_site_domain() { - return isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : str_replace(array('https://', 'http://'), '', get_site_url()); - } + /** + * Get the domain of the current site, to be registered as a payment method domain + * + * @return string + */ + private static function get_site_domain() + { + return isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : str_replace(['https://', 'http://'], '', get_site_url()); + } } //End class diff --git a/app/controllers/MeprPayPalConnectCtrl.php b/app/controllers/MeprPayPalConnectCtrl.php index d783cbe..279b6ee 100644 --- a/app/controllers/MeprPayPalConnectCtrl.php +++ b/app/controllers/MeprPayPalConnectCtrl.php @@ -1,799 +1,839 @@ add_ajax_endpoints(); - } - - /** - * @param MeprTransaction $txn - * @param false $sub_status - */ - public function check_for_renewal_transactions($txn, $sub_status=false) - { - $sub = $txn->subscription(); - - if (empty($sub->id)) return; - - $mepr_options = MeprOptions::fetch(); - $subscr_id = $sub->subscr_id; - /** @var MeprPayPalCommerceGateway $gateway */ - $gateway = $mepr_options->payment_method($subscr_id); - - if (!$gateway instanceof MeprPayPalCommerceGateway) { - return; - } + // add_filter( 'site_status_tests', array( $this, 'add_site_health_test' ) ); + add_filter('http_request_timeout', function ($seconds) { + return $seconds + 15; + }); - $date = new DateTime(); - $nextDate = new DateTime(); - - if ($txn->txn_type == MeprTransaction::$subscription_confirmation_str) { - $nextDate->add(new DateInterval('P1D')); - $date->sub(new DateInterval('P1D')); - } elseif ($txn->txn_type == MeprTransaction::$payment_str) { - $nextDate->add(new DateInterval('P1D')); - $date->sub(new DateInterval('P5D')); - } else { - $date = null; - $nextDate = null; + add_action('admin_init', [$this, 'admin_init']); + add_action('mepr-transaction-expired', [$this, 'check_for_renewal_transactions'], 10, 2); + add_action('mepr-saved-options', [$this, 'mepr_saved_options']); + $this->add_ajax_endpoints(); } - // Get transactions from yesterday - $pp_transactions = $gateway->get_paypal_subscription_transactions($subscr_id, $date, $nextDate); + /** + * @param MeprTransaction $txn + * @param false $sub_status + */ + public function check_for_renewal_transactions($txn, $sub_status = false) + { + $sub = $txn->subscription(); - foreach ($pp_transactions as $pp_transaction) { - $_POST['txn_id'] = $pp_transaction['id']; - $_POST['mc_gross'] = $pp_transaction['amount_with_breakdown']['gross_amount']['value']; - $_POST['payment_date'] = $pp_transaction['time']; - $_POST['subscr_id'] = $subscr_id; - $gateway->record_subscription_payment(); - } - } + if (empty($sub->id)) { + return; + } - public function mepr_saved_options($settings) { - $mepr_options = MeprOptions::fetch(); + $mepr_options = MeprOptions::fetch(); + $subscr_id = $sub->subscr_id; + /** + * @var MeprPayPalCommerceGateway $gateway +*/ + $gateway = $mepr_options->payment_method($subscr_id); - if ( ! isset( $settings['mepr-integrations'] ) || empty( $settings['mepr-integrations'] ) || ! is_array( $settings['mepr-integrations'] ) ) { - return $settings; - } + if (!$gateway instanceof MeprPayPalCommerceGateway) { + return; + } + + $date = new DateTime(); + $nextDate = new DateTime(); - foreach ( $settings['mepr-integrations'] as $key => $integration ) { - if ( $integration['gateway'] == MeprPayPalCommerceGateway::class ) { - if ( isset( $mepr_options->legacy_integrations[ $key ] ) ) { - $mepr_options->legacy_integrations[ $key ]['debug'] = isset($integration['enable_paypal_standard_debug_email']); - $mepr_options->store( false ); + if ($txn->txn_type == MeprTransaction::$subscription_confirmation_str) { + $nextDate->add(new DateInterval('P1D')); + $date->sub(new DateInterval('P1D')); + } elseif ($txn->txn_type == MeprTransaction::$payment_str) { + $nextDate->add(new DateInterval('P1D')); + $date->sub(new DateInterval('P5D')); + } else { + $date = null; + $nextDate = null; } - } - } - return $settings; - } + // Get transactions from yesterday + $pp_transactions = $gateway->get_paypal_subscription_transactions($subscr_id, $date, $nextDate); - public function admin_init() { - if ( ! isset( $_GET['page'] ) || $_GET['page'] !== 'memberpress-options' ) { - return; + foreach ($pp_transactions as $pp_transaction) { + $_POST['txn_id'] = $pp_transaction['id']; + $_POST['mc_gross'] = $pp_transaction['amount_with_breakdown']['gross_amount']['value']; + $_POST['payment_date'] = $pp_transaction['time']; + $_POST['subscr_id'] = $subscr_id; + $gateway->record_subscription_payment(); + } } - if ( ! isset( $_GET['paypal'] ) || ! isset( $_GET['method-id'] ) ) { - return; - } + public function mepr_saved_options($settings) + { + $mepr_options = MeprOptions::fetch(); - if ( isset( $_GET['sandbox'] ) & ! empty( $_GET['sandbox'] ) ) { - $sandbox = true; - } else { - $sandbox = false; - } + if (! isset($settings['mepr-integrations']) || empty($settings['mepr-integrations']) || ! is_array($settings['mepr-integrations'])) { + return $settings; + } - $methodId = filter_input(INPUT_GET, 'method-id'); - $mepr_options = MeprOptions::fetch(); - $integrations = $mepr_options->integrations; - - if ( ! isset( $integrations[ $methodId ] ) ) { - $integrations[ $methodId ] = [ - 'label' => esc_html( __( 'PayPal', 'memberpress' ) ), - 'id' => $methodId, - 'gateway' => 'MeprPayPalCommerceGateway', - 'saved' => true, - ]; - $mepr_options->integrations = $integrations; - $mepr_options->store( false ); - } - } - - /** - * Add a site health test callback - * - * @param array $tests Array of tests to be run - * - * @return array - */ - public function add_site_health_test( $tests ) { - $tests['direct']['mepr_paypal_connect_test'] = array( - 'label' => __( 'MemberPress - PayPal Connect Security', 'memberpress' ), - 'test' => array( $this, 'run_site_health_test' ) - ); - - return $tests; - } - - public function check_and_show_upgrade_notices() { - $mepr_options = MeprOptions::fetch(); - $integrations = $mepr_options->integrations; - - if ( ! is_array( $integrations ) ) { - return; + foreach ($settings['mepr-integrations'] as $key => $integration) { + if ($integration['gateway'] == MeprPayPalCommerceGateway::class) { + if (isset($mepr_options->legacy_integrations[ $key ])) { + $mepr_options->legacy_integrations[ $key ]['debug'] = isset($integration['enable_paypal_standard_debug_email']); + $mepr_options->store(false); + } + } + } + + return $settings; } - $has_old_paypal_integration = false; + public function admin_init() + { + if (! isset($_GET['page']) || $_GET['page'] !== 'memberpress-options') { + return; + } + + if (! isset($_GET['paypal']) || ! isset($_GET['method-id'])) { + return; + } - foreach ( $integrations as $integration ) { - if ( isset( $integration['gateway'] ) && $integration['gateway'] === 'MeprPayPalStandardGateway' ) { - $has_old_paypal_integration = true; - break; - } - } + if (isset($_GET['sandbox']) & ! empty($_GET['sandbox'])) { + $sandbox = true; + } else { + $sandbox = false; + } - if ($has_old_paypal_integration === false) { - return; + $methodId = filter_input(INPUT_GET, 'method-id'); + $mepr_options = MeprOptions::fetch(); + $integrations = $mepr_options->integrations; + + if (! isset($integrations[ $methodId ])) { + $integrations[ $methodId ] = [ + 'label' => esc_html(__('PayPal', 'memberpress')), + 'id' => $methodId, + 'gateway' => 'MeprPayPalCommerceGateway', + 'saved' => true, + ]; + $mepr_options->integrations = $integrations; + $mepr_options->store(false); + } } - $has_commerce_gateway = false; + /** + * Add a site health test callback + * + * @param array $tests Array of tests to be run + * + * @return array + */ + public function add_site_health_test($tests) + { + $tests['direct']['mepr_paypal_connect_test'] = [ + 'label' => __('MemberPress - PayPal Connect Security', 'memberpress'), + 'test' => [$this, 'run_site_health_test'], + ]; - foreach ( $mepr_options->integrations as $integration ) { - if ( isset( $integration['gateway'] ) && 'MeprPayPalCommerceGateway' === $integration['gateway'] ) { - $has_commerce_gateway = true; - break; - } + return $tests; } - if ( ! $has_commerce_gateway && ( ! isset( $_COOKIE['mepr_paypal_connect_upgrade_dismissed'] ) || false == $_COOKIE['mepr_paypal_connect_upgrade_dismissed'] ) ) { - ?> + public function check_and_show_upgrade_notices() + { + $mepr_options = MeprOptions::fetch(); + $integrations = $mepr_options->integrations; + + if (! is_array($integrations)) { + return; + } + + $has_old_paypal_integration = false; + + foreach ($integrations as $integration) { + if (isset($integration['gateway']) && $integration['gateway'] === 'MeprPayPalStandardGateway') { + $has_old_paypal_integration = true; + break; + } + } + + if ($has_old_paypal_integration === false) { + return; + } + + $has_commerce_gateway = false; + + foreach ($mepr_options->integrations as $integration) { + if (isset($integration['gateway']) && 'MeprPayPalCommerceGateway' === $integration['gateway']) { + $has_commerce_gateway = true; + break; + } + } + + if (! $has_commerce_gateway && ( ! isset($_COOKIE['mepr_paypal_connect_upgrade_dismissed']) || false == $_COOKIE['mepr_paypal_connect_upgrade_dismissed'] )) { + ?>

    -

    -

    -

    +

    +

    +

    - +
    - integrations as $integration ) { - if ( isset( $integration['gateway'] ) && 'MeprPayPalCommerceGateway' === $integration['gateway'] ) { - $has_commerce_gateway = true; - break; - } - } + foreach ($mepr_options->integrations as $integration) { + if (isset($integration['gateway']) && 'MeprPayPalCommerceGateway' === $integration['gateway']) { + $has_commerce_gateway = true; + break; + } + } - if ( $has_commerce_gateway && ! MeprPayPalCommerceGateway::has_method_with_connect_status( 'not-connected' ) ) { - ?> + if ($has_commerce_gateway && ! MeprPayPalCommerceGateway::has_method_with_connect_status('not-connected')) { + ?>

    -

    -

    -

    +

    +

    +

    -

    %2$s

    ', esc_attr( $class ), esc_html( $message ) ); - } - - protected function add_ajax_endpoints() { - add_action( 'wp_ajax_mepr_paypal_connect_rollback', array( $this, 'rollback_paypal_to_standard' ) ); - add_action( 'wp_ajax_mepr_paypal_connect_upgrade_standard_gateway', array( $this, 'upgrade_standard_gateway' ) ); - add_action( 'wp_ajax_mepr_paypal_connect_update_creds', array( $this, 'process_update_creds' ) ); - add_action( 'wp_ajax_mepr_paypal_connect_update_creds_sandbox', array( $this, 'process_update_creds_sandbox' ) ); - add_action( 'wp_ajax_mepr_paypal_connect_disconnect', array( $this, 'process_remove_creds' ) ); - add_action( 'wp_ajax_mepr_paypal_commerce_get_smart_button_mode', array( $this, 'get_smart_button_mode' ) ); - add_action( 'wp_ajax_nopriv_mepr_paypal_commerce_get_smart_button_mode', array( $this, 'get_smart_button_mode' ) ); - add_action( 'wp_ajax_mepr_paypal_commerce_create_smart_button', array( $this, 'generate_smart_button_object' ) ); - add_action( 'wp_ajax_nopriv_mepr_paypal_commerce_create_smart_button', array( $this, 'generate_smart_button_object' ) ); - add_action( 'admin_init', array( $this, 'onboarding_success' ) ); - // add_action( 'admin_notices', array( $this, 'check_and_show_upgrade_notices' ) ); - // add_action( 'admin_notices', array( $this, 'show_notices_if_commerce_not_connected' ) ); - add_action( 'admin_notices', array( $this, 'admin_notices' ) ); - add_filter('mepr_signup_form_payment_description', array($this, 'maybe_render_payment_form'), 10, 3); - } - - /** - * Renders the payment form if SPC is enabled and supported by the payment method - * Called from: mepr_signup_form_payment_description filter - * Returns: description includding form for SPC if enabled - */ - public function maybe_render_payment_form( $description, $payment_method, $first ) { - $mepr_options = MeprOptions::fetch(); - - if ( ! $payment_method instanceof MeprPayPalCommerceGateway ) { - return $description; - } + if (isset($_REQUEST['paypal-gateway-message-success'])) { + $class = 'notice notice-success'; + $message = sanitize_text_field($_REQUEST['paypal-gateway-message-success']); + } else { + $class = 'notice notice-error'; + $message = sanitize_text_field($_REQUEST['paypal-gateway-message']); + } - if ( ! ( $mepr_options->enable_spc && $payment_method->has_spc_form ) ) { - // Include smart buttons in spc - wp_register_script('mepr-checkout-js', MEPR_JS_URL . '/checkout.js', array('jquery', 'jquery.payment'), MEPR_VERSION); - wp_enqueue_script('mepr-checkout-js'); - $payment_method->enqueue_payment_form_scripts(); - $description = $payment_method->spc_payment_fields(); - } + printf('

    %2$s

    ', esc_attr($class), esc_html($message)); + } + + protected function add_ajax_endpoints() + { + add_action('wp_ajax_mepr_paypal_connect_rollback', [$this, 'rollback_paypal_to_standard']); + add_action('wp_ajax_mepr_paypal_connect_upgrade_standard_gateway', [$this, 'upgrade_standard_gateway']); + add_action('wp_ajax_mepr_paypal_connect_update_creds', [$this, 'process_update_creds']); + add_action('wp_ajax_mepr_paypal_connect_update_creds_sandbox', [$this, 'process_update_creds_sandbox']); + add_action('wp_ajax_mepr_paypal_connect_disconnect', [$this, 'process_remove_creds']); + add_action('wp_ajax_mepr_paypal_commerce_get_smart_button_mode', [$this, 'get_smart_button_mode']); + add_action('wp_ajax_nopriv_mepr_paypal_commerce_get_smart_button_mode', [$this, 'get_smart_button_mode']); + add_action('wp_ajax_mepr_paypal_commerce_create_smart_button', [$this, 'generate_smart_button_object']); + add_action('wp_ajax_nopriv_mepr_paypal_commerce_create_smart_button', [$this, 'generate_smart_button_object']); + add_action('admin_init', [$this, 'onboarding_success']); + // add_action( 'admin_notices', array( $this, 'check_and_show_upgrade_notices' ) ); + // add_action( 'admin_notices', array( $this, 'show_notices_if_commerce_not_connected' ) ); + add_action('admin_notices', [$this, 'admin_notices']); + add_filter('mepr_signup_form_payment_description', [$this, 'maybe_render_payment_form'], 10, 3); + } + + /** + * Renders the payment form if SPC is enabled and supported by the payment method + * Called from: mepr_signup_form_payment_description filter + * Returns: description includding form for SPC if enabled + */ + public function maybe_render_payment_form($description, $payment_method, $first) + { + $mepr_options = MeprOptions::fetch(); + + if (! $payment_method instanceof MeprPayPalCommerceGateway) { + return $description; + } - return $description; - } + if (! ( $mepr_options->enable_spc && $payment_method->has_spc_form )) { + // Include smart buttons in spc + wp_register_script('mepr-checkout-js', MEPR_JS_URL . '/checkout.js', ['jquery', 'jquery.payment'], MEPR_VERSION); + wp_enqueue_script('mepr-checkout-js'); + $payment_method->enqueue_payment_form_scripts(); + $description = $payment_method->spc_payment_fields(); + } - public function onboarding_success() { - if ( ! current_user_can( 'manage_options' ) ) { - return; + return $description; } - if ( isset ( $_GET['mepr-paypal-commerce-confirm-email'] ) && $_GET['mepr-paypal-commerce-confirm-email'] == '1' ) { - $sandbox = isset( $_GET['sandbox'] ) && $_GET['sandbox'] == '1'; - $mepr_options = MeprOptions::fetch(); - $integrations = $mepr_options->integrations; - $methodId = filter_var( $_GET['method-id'] ); - $site_uuid = get_option( 'mepr_authenticator_site_uuid' ); - $buffer_settings = get_option( 'mepr_buff_integrations', [] ); - - if ( isset( $buffer_settings[ $methodId ] ) ) { - foreach ( [ 'test_merchant_id', 'live_merchant_id', 'test_email_confirmed', 'live_email_confirmed' ] as $key ) { - if ( isset( $buffer_settings[ $methodId ][ $key ] ) ) { - $mepr_options->integrations[ $methodId ][ $key ] = $buffer_settings[ $methodId ][ $key ]; - } - } - } - - if ( $sandbox ) { - $endpoint = MEPR_PAYPAL_SERVICE_URL . "/sandbox/credentials/{$methodId}"; - $payload = array( - 'site_uuid' => $site_uuid, - 'merchant_id' => $integrations[ $methodId ]['test_merchant_id'], - ); - } else { - $endpoint = MEPR_PAYPAL_SERVICE_URL . "/credentials/{$methodId}"; - $payload = array( - 'site_uuid' => $site_uuid, - 'merchant_id' => $integrations[ $methodId ]['live_merchant_id'], - ); - } - - $jwt = MeprAuthenticatorCtrl::generate_jwt( $payload ); - - $options = array( - 'headers' => MeprUtils::jwt_header( $jwt, MEPR_PAYPAL_SERVICE_DOMAIN ) - ); - - $response = wp_remote_get( $endpoint, $options ); - $creds = wp_remote_retrieve_body( $response ); - self::debug_log( $endpoint ); - self::debug_log( $options ); - $creds = json_decode( $creds, true ); - self::debug_log( $creds ); - - if ( isset( $creds['primary_email_confirmed'] ) && ! empty( $creds['primary_email_confirmed'] ) ) { - if ( $sandbox ) { - $integrations[ $methodId ]['test_email_confirmed'] = true; - } else { - $integrations[ $methodId ]['live_email_confirmed'] = true; + public function onboarding_success() + { + if (! current_user_can('manage_options')) { + return; } - $mepr_options->integrations = $integrations; - $mepr_options->store( false ); - } + if (isset($_GET['mepr-paypal-commerce-confirm-email']) && $_GET['mepr-paypal-commerce-confirm-email'] == '1') { + $sandbox = isset($_GET['sandbox']) && $_GET['sandbox'] == '1'; + $mepr_options = MeprOptions::fetch(); + $integrations = $mepr_options->integrations; + $methodId = filter_var($_GET['method-id']); + $site_uuid = get_option('mepr_authenticator_site_uuid'); + $buffer_settings = get_option('mepr_buff_integrations', []); + + if (isset($buffer_settings[ $methodId ])) { + foreach (['test_merchant_id', 'live_merchant_id', 'test_email_confirmed', 'live_email_confirmed'] as $key) { + if (isset($buffer_settings[ $methodId ][ $key ])) { + $mepr_options->integrations[ $methodId ][ $key ] = $buffer_settings[ $methodId ][ $key ]; + } + } + } + + if ($sandbox) { + $endpoint = MEPR_PAYPAL_SERVICE_URL . "/sandbox/credentials/{$methodId}"; + $payload = [ + 'site_uuid' => $site_uuid, + 'merchant_id' => $integrations[ $methodId ]['test_merchant_id'], + ]; + } else { + $endpoint = MEPR_PAYPAL_SERVICE_URL . "/credentials/{$methodId}"; + $payload = [ + 'site_uuid' => $site_uuid, + 'merchant_id' => $integrations[ $methodId ]['live_merchant_id'], + ]; + } + + $jwt = MeprAuthenticatorCtrl::generate_jwt($payload); + + $options = [ + 'headers' => MeprUtils::jwt_header($jwt, MEPR_PAYPAL_SERVICE_DOMAIN), + ]; + + $response = wp_remote_get($endpoint, $options); + $creds = wp_remote_retrieve_body($response); + self::debug_log($endpoint); + self::debug_log($options); + $creds = json_decode($creds, true); + self::debug_log($creds); + + if (isset($creds['primary_email_confirmed']) && ! empty($creds['primary_email_confirmed'])) { + if ($sandbox) { + $integrations[ $methodId ]['test_email_confirmed'] = true; + } else { + $integrations[ $methodId ]['live_email_confirmed'] = true; + } + + $mepr_options->integrations = $integrations; + $mepr_options->store(false); + } + } + if (isset($_GET['paypal-connect']) && $_GET['paypal-connect'] == '1') { + $mepr_options = MeprOptions::fetch(); + $methodId = filter_var($_GET['method_id']); + $integrations = $mepr_options->integrations; + self::debug_log($_GET); + if (isset($_GET['merchantIdInPayPal'])) { + if (isset($_GET['sandbox']) && $_GET['sandbox'] == '1') { + $integrations[ $methodId ]['test_merchant_id'] = esc_sql($_GET['merchantIdInPayPal']); + } else { + $integrations[ $methodId ]['live_merchant_id'] = esc_sql($_GET['merchantIdInPayPal']); + } + } + if (isset($_GET['isEmailConfirmed'])) { + $isConfirmed = ! ( $_GET['isEmailConfirmed'] == 'false' ); + + if (isset($_GET['sandbox']) && $_GET['sandbox'] == '1') { + $integrations[ $methodId ]['test_email_confirmed'] = $isConfirmed; + } else { + $integrations[ $methodId ]['live_email_confirmed'] = $isConfirmed; + } + } + self::debug_log($integrations); + $mepr_options->integrations = $integrations; + $buffer = get_option('mepr_buff_integrations'); + + if (empty($buffer)) { + $buffer = []; + } + + $buffer[ $methodId ] = $integrations[ $methodId ]; + update_option('mepr_buff_integrations', $buffer); + + $mepr_options->store(false); + + $onboarding = isset($_GET['onboarding']) ? sanitize_text_field(wp_unslash($_GET['onboarding'])) : ''; + + if ($onboarding == 'true') { + update_option('mepr_onboarding_payment_gateway', $methodId); + + $redirect_url = add_query_arg([ + 'page' => 'memberpress-onboarding', + 'step' => '6', + ], admin_url('admin.php')); + } else { + $redirect_url = admin_url('admin.php?page=memberpress-options#mepr-integration'); + } + + MeprUtils::wp_redirect($redirect_url); + exit; + } } - if ( isset( $_GET['paypal-connect'] ) && $_GET['paypal-connect'] == '1' ) { - $mepr_options = MeprOptions::fetch(); - $methodId = filter_var( $_GET['method_id'] ); - $integrations = $mepr_options->integrations; - self::debug_log( $_GET ); - if ( isset( $_GET['merchantIdInPayPal'] ) ) { - if ( isset ( $_GET['sandbox'] ) && $_GET['sandbox'] == '1' ) { - $integrations[ $methodId ]['test_merchant_id'] = esc_sql( $_GET['merchantIdInPayPal'] ); - } else { - $integrations[ $methodId ]['live_merchant_id'] = esc_sql( $_GET['merchantIdInPayPal'] ); + + public function create_webhook($webhook_url, $client_id, $client_secret, $sandbox = false) + { + self::debug_log('Attempt to create webhook'); + + $webhook_url = str_ireplace('http://', 'https://', $webhook_url); + $url = self::get_base_paypal_endpoint($sandbox); + $payload = [ + 'url' => $webhook_url, + 'event_types' => [ + [ + 'name' => 'INVOICING.INVOICE.PAID', + ], + [ + 'name' => 'CHECKOUT.ORDER.COMPLETED', + ], + [ + 'name' => 'CHECKOUT.ORDER.PROCESSED', + ], + [ + 'name' => 'PAYMENT.SALE.COMPLETED', + ], + [ + 'name' => 'PAYMENT.CAPTURE.REFUNDED', + ], + [ + 'name' => 'PAYMENT.CAPTURE.DENIED', + ], + [ + 'name' => 'PAYMENT.SALE.REFUNDED', + ], + [ + 'name' => 'BILLING.SUBSCRIPTION.ACTIVATED', + ], + [ + 'name' => 'BILLING.SUBSCRIPTION.SUSPENDED', + ], + [ + 'name' => 'BILLING.SUBSCRIPTION.EXPIRED', + ], + [ + 'name' => 'BILLING.SUBSCRIPTION.CANCELLED', + ], + ], + ]; + $json_string = json_encode($payload, JSON_UNESCAPED_SLASHES); + + $response = wp_remote_post($url . '/v1/notifications/webhooks', [ + 'headers' => [ + 'Authorization' => 'Basic ' . base64_encode($client_id . ':' . $client_secret), + 'PayPal-Partner-Attribution-Id' => self::PAYPAL_BN_CODE, + 'Content-Type' => 'application/json', + ], + 'body' => $json_string, + 'method' => 'POST', + ]); + + $raw = wp_remote_retrieve_body($response); + self::debug_log($json_string); + self::debug_log($raw); + $paypal_webhook = json_decode($raw, true); + + if (isset($paypal_webhook['id'])) { + return $paypal_webhook['id']; } - } - if ( isset( $_GET['isEmailConfirmed'] ) ) { - $isConfirmed = ! ( $_GET['isEmailConfirmed'] == 'false' ); + } - if ( isset ( $_GET['sandbox'] ) && $_GET['sandbox'] == '1' ) { - $integrations[ $methodId ]['test_email_confirmed'] = $isConfirmed; - } else { - $integrations[ $methodId ]['live_email_confirmed'] = $isConfirmed; + /** + * @todo this is unused for now, we can't delete webhook because new webhook won't receive + * notifications for payments created from prior webhook + * @param $webhook_id + * @param $token + * + * @return boolean + */ + public static function delete_webhook($webhook_id, $token, $sandbox = false) + { + $url = self::get_base_paypal_endpoint($sandbox) . '/v1/notifications/webhooks/' . $webhook_id; + $options = [ + 'headers' => [ + 'Authorization' => 'Basic ' . $token, + 'PayPal-Partner-Attribution-Id' => self::PAYPAL_BN_CODE, + 'Content-Type' => 'application/json', + ], + 'method' => 'DELETE', + ]; + + $response = wp_remote_request($url, $options); + $response_code = wp_remote_retrieve_response_code($response); + + if ($response_code >= 200 && $response_code < 300) { + return true; } - } - self::debug_log( $integrations ); - $mepr_options->integrations = $integrations; - $buffer = get_option( 'mepr_buff_integrations' ); - if (empty($buffer)) { - $buffer = []; - } + self::debug_log($response); - $buffer[ $methodId ] = $integrations[ $methodId ]; - update_option( 'mepr_buff_integrations', $buffer ); + return false; + } + + public static function debug_log($data) + { + if (! defined('WP_MEPR_DEBUG')) { + return; + } - $mepr_options->store( false ); + file_put_contents(WP_CONTENT_DIR . '/paypal-connect.log', print_r($data, true) . PHP_EOL, FILE_APPEND); + } - $onboarding = isset($_GET['onboarding']) ? sanitize_text_field(wp_unslash($_GET['onboarding'])) : ''; + public function upgrade_standard_gateway() + { + $mepr_options = MeprOptions::fetch(); + $id = filter_input(INPUT_GET, 'method-id', FILTER_SANITIZE_STRING); + $standard_gateway_settings = $mepr_options->integrations[ $id ]; - if( $onboarding == 'true' ) { - update_option('mepr_onboarding_payment_gateway', $methodId); + if (! isset($mepr_options->legacy_integrations)) { + $mepr_options->legacy_integrations = []; + } - $redirect_url = add_query_arg( [ - 'page' => 'memberpress-onboarding', - 'step' => '6', - ], admin_url('admin.php') ); - } - else { - $redirect_url = admin_url('admin.php?page=memberpress-options#mepr-integration'); - } + $mepr_options->legacy_integrations[ $id ] = $standard_gateway_settings; + $mepr_options->integrations[ $id ]['gateway'] = MeprPayPalCommerceGateway::class; + $mepr_options->store(false); + $url = admin_url('admin.php?page=memberpress-options#mepr-integration'); + MeprUtils::wp_redirect($url); + } + + public function process_remove_creds() + { + $mepr_options = MeprOptions::fetch(); + $site_uuid = get_option('mepr_authenticator_site_uuid'); + $methodId = sanitize_text_field($_REQUEST['method-id']); + $payload = [ + 'site_uuid' => $site_uuid, + ]; + + $sandbox = filter_var(isset($_GET['sandbox']) ? $_GET['sandbox'] : 0); + $retry = filter_var(isset($_GET['retry']) ? $_GET['retry'] : 0); + + if ($retry) { + $integrations = $mepr_options->integrations; + $integrations[ $methodId ]['live_auth_code'] = ''; + $integrations[ $methodId ]['test_auth_code'] = ''; + $mepr_options->integrations = $integrations; + $mepr_options->store(false); + $message = esc_html(__('You have disconnected your PayPal. You should login to your PayPal account and go to Developer settings to delete the app created by this gateway unless you have active recurring subscriptions that were created with this gateway', 'memberpress')); + $url = admin_url('admin.php?page=memberpress-options&paypal-gateway-message-success=' . $message . '#mepr-integration'); + MeprUtils::wp_redirect($url); + } - MeprUtils::wp_redirect( $redirect_url ); - exit; - } - } - - public function create_webhook( $webhook_url, $client_id, $client_secret, $sandbox = false ) { - self::debug_log( 'Attempt to create webhook' ); - - $webhook_url = str_ireplace( 'http://', 'https://', $webhook_url ); - $url = self::get_base_paypal_endpoint( $sandbox ); - $payload = [ - "url" => $webhook_url, - "event_types" => [ - [ - "name" => "INVOICING.INVOICE.PAID", - ], - [ - "name" => "CHECKOUT.ORDER.COMPLETED", - ], - [ - "name" => "CHECKOUT.ORDER.PROCESSED", - ], - [ - "name" => "PAYMENT.SALE.COMPLETED", - ], - [ - "name" => "PAYMENT.CAPTURE.REFUNDED", - ], - [ - "name" => "PAYMENT.CAPTURE.DENIED", - ], - [ - "name" => "PAYMENT.SALE.REFUNDED", - ], - [ - "name" => "BILLING.SUBSCRIPTION.ACTIVATED", - ], - [ - "name" => "BILLING.SUBSCRIPTION.SUSPENDED", - ], - [ - "name" => "BILLING.SUBSCRIPTION.EXPIRED", - ], - [ - "name" => "BILLING.SUBSCRIPTION.CANCELLED", - ], - ], - ]; - $json_string = json_encode( $payload, JSON_UNESCAPED_SLASHES ); - - $response = wp_remote_post( $url . '/v1/notifications/webhooks', [ - "headers" => [ - "Authorization" => 'Basic ' . base64_encode( $client_id . ':' . $client_secret ), - "PayPal-Partner-Attribution-Id" => self::PAYPAL_BN_CODE, - "Content-Type" => "application/json" - ], - "body" => $json_string, - "method" => "POST" - ] ); - - $raw = wp_remote_retrieve_body( $response ); - self::debug_log( $json_string ); - self::debug_log( $raw ); - $paypal_webhook = json_decode( $raw, true ); - - if ( isset( $paypal_webhook['id'] ) ) { - return $paypal_webhook['id']; - } - } - - /** - * @todo this is unused for now, we can't delete webhook because new webhook won't receive - * notifications for payments created from prior webhook - * @param $webhook_id - * @param $token - * - * @return bool - */ - public static function delete_webhook( $webhook_id, $token, $sandbox = false ) { - $url = self::get_base_paypal_endpoint($sandbox) . '/v1/notifications/webhooks/' . $webhook_id; - $options = [ - "headers" => [ - "Authorization" => 'Basic ' . $token, - "PayPal-Partner-Attribution-Id" => self::PAYPAL_BN_CODE, - "Content-Type" => "application/json" - ], - 'method' => 'DELETE', - ]; - - $response = wp_remote_request( $url, $options ); - $response_code = wp_remote_retrieve_response_code( $response ); - - if ( $response_code >= 200 && $response_code < 300 ) { - return true; - } + self::debug_log($sandbox); - self::debug_log( $response ); + if (! empty($sandbox)) { + $endpoint = MEPR_PAYPAL_SERVICE_URL . "/sandbox/credentials/{$methodId}"; + } else { + $endpoint = MEPR_PAYPAL_SERVICE_URL . "/credentials/{$methodId}"; + } - return false; - } + $jwt = MeprAuthenticatorCtrl::generate_jwt($payload); - public static function debug_log( $data ) { - if ( ! defined( 'WP_MEPR_DEBUG' ) ) { - return; - } + // Make sure the request came from the Connect service + $options = [ + 'body' => [ + 'method-id' => $methodId, + ], + 'method' => 'DELETE', + 'headers' => MeprUtils::jwt_header($jwt, MEPR_PAYPAL_SERVICE_DOMAIN), + ]; - file_put_contents( WP_CONTENT_DIR . '/paypal-connect.log', print_r( $data, true ) . PHP_EOL, FILE_APPEND ); - } + $response = wp_remote_request($endpoint, $options); + $response_code = wp_remote_retrieve_response_code($response); + $body = wp_remote_retrieve_body($response); + $integrations = $mepr_options->integrations; - public function upgrade_standard_gateway() { - $mepr_options = MeprOptions::fetch(); - $id = filter_input( INPUT_GET, 'method-id', FILTER_SANITIZE_STRING ); - $standard_gateway_settings = $mepr_options->integrations[ $id ]; + if (empty($sandbox)) { + $integrations[ $methodId ]['live_webhook_id'] = ''; + } else { + $integrations[ $methodId ]['test_webhook_id'] = ''; + } - if ( ! isset( $mepr_options->legacy_integrations ) ) { - $mepr_options->legacy_integrations = []; - } + self::debug_log($body); - $mepr_options->legacy_integrations[ $id ] = $standard_gateway_settings; - $mepr_options->integrations[ $id ]['gateway'] = MeprPayPalCommerceGateway::class; - $mepr_options->store( false ); - $url = admin_url( 'admin.php?page=memberpress-options#mepr-integration' ); - MeprUtils::wp_redirect( $url ); - } - - public function process_remove_creds() { - $mepr_options = MeprOptions::fetch(); - $site_uuid = get_option( 'mepr_authenticator_site_uuid' ); - $methodId = sanitize_text_field( $_REQUEST['method-id'] ); - $payload = array( - 'site_uuid' => $site_uuid - ); - - $sandbox = filter_var( isset( $_GET['sandbox'] ) ? $_GET['sandbox'] : 0 ); - $retry = filter_var( isset( $_GET['retry'] ) ? $_GET['retry'] : 0 ); - - if ( $retry ) { - $integrations = $mepr_options->integrations; - $integrations[ $methodId ]['live_auth_code'] = ''; - $integrations[ $methodId ]['test_auth_code'] = ''; - $mepr_options->integrations = $integrations; - $mepr_options->store( false ); - $message = esc_html( __( 'You have disconnected your PayPal. You should login to your PayPal account and go to Developer settings to delete the app created by this gateway unless you have active recurring subscriptions that were created with this gateway', 'memberpress' ) ); - $url = admin_url( 'admin.php?page=memberpress-options&paypal-gateway-message-success=' . $message . '#mepr-integration' ); - MeprUtils::wp_redirect( $url ); - } + if (empty($sandbox)) { + $integrations[$methodId]['live_client_id'] = ''; + $integrations[$methodId]['live_client_secret'] = ''; + $integrations[$methodId]['live_merchant_id'] = ''; + } else { + $integrations[$methodId]['test_client_id'] = ''; + $integrations[$methodId]['test_client_secret'] = ''; + $integrations[$methodId]['test_merchant_id'] = ''; + } - self::debug_log( $sandbox ); + $mepr_options->integrations = $integrations; + $mepr_options->store(false); + $message = esc_html(__('You have disconnected your PayPal. You should login to your PayPal account and go to Developer settings to delete the app created by this gateway unless you have active recurring subscriptions that were created with this gateway', 'memberpress')); + $url = admin_url('admin.php?page=memberpress-options&paypal-gateway-message-success=' . $message . '#mepr-integration'); - if ( ! empty( $sandbox ) ) { - $endpoint = MEPR_PAYPAL_SERVICE_URL . "/sandbox/credentials/{$methodId}"; - } else { - $endpoint = MEPR_PAYPAL_SERVICE_URL . "/credentials/{$methodId}"; + MeprUtils::wp_redirect($url); } - $jwt = MeprAuthenticatorCtrl::generate_jwt( $payload ); - - // Make sure the request came from the Connect service - $options = array( - 'body' => [ - 'method-id' => $methodId, - ], - 'method' => 'DELETE', - 'headers' => MeprUtils::jwt_header( $jwt, MEPR_PAYPAL_SERVICE_DOMAIN ) - ); - - $response = wp_remote_request( $endpoint, $options ); - $response_code = wp_remote_retrieve_response_code( $response ); - $body = wp_remote_retrieve_body( $response ); - $integrations = $mepr_options->integrations; - - if ( empty( $sandbox ) ) { - $integrations[ $methodId ]['live_webhook_id'] = ''; - } else { - $integrations[ $methodId ]['test_webhook_id'] = ''; + public function process_update_creds_sandbox() + { + $this->process_update_creds(true); } - self::debug_log( $body ); + public function process_update_creds($sandbox = false) + { + // Security check + if (! isset($_GET['_wpnonce']) || ! wp_verify_nonce($_GET['_wpnonce'], 'paypal-update-creds')) { + wp_die(__('Sorry, updating your credentials failed. (security)', 'memberpress')); + } - if (empty($sandbox)) { - $integrations[$methodId]['live_client_id'] = ''; - $integrations[$methodId]['live_client_secret'] = ''; - $integrations[$methodId]['live_merchant_id'] = ''; - } else { - $integrations[$methodId]['test_client_id'] = ''; - $integrations[$methodId]['test_client_secret'] = ''; - $integrations[$methodId]['test_merchant_id'] = ''; - } + if (! current_user_can('manage_options')) { + wp_die(__('You do not have sufficient permission to perform this operation', 'memberpress')); + } - $mepr_options->integrations = $integrations; - $mepr_options->store(false); - $message = esc_html(__('You have disconnected your PayPal. You should login to your PayPal account and go to Developer settings to delete the app created by this gateway unless you have active recurring subscriptions that were created with this gateway', 'memberpress')); - $url = admin_url('admin.php?page=memberpress-options&paypal-gateway-message-success=' . $message . '#mepr-integration'); + $posted = json_decode(@file_get_contents('php://input'), true); + self::debug_log($posted); + $authCode = $posted['authCode']; + $sharedId = $posted['sharedId']; + $methodId = $posted['payment_method_id']; + + $this->handle_update_creds($sandbox, $authCode, $sharedId, $methodId); + } + + public function handle_update_creds($sandbox, $authCode, $sharedId, $methodId) + { + $pm = new MeprPayPalCommerceGateway(); + $mepr_options = MeprOptions::fetch(); + $integrations = $mepr_options->integrations; + + if (! isset($integrations[ $methodId ])) { + $integrations[ $methodId ] = [ + 'label' => esc_html(__('PayPal', 'memberpress')), + 'id' => $methodId, + 'gateway' => 'MeprPayPalCommerceGateway', + 'saved' => true, + ]; + + $mepr_options->integrations = $integrations; + $mepr_options->store(false); + $pm->load(['id' => $methodId]); + $pm->id = $methodId; + } - MeprUtils::wp_redirect( $url ); - } + $pm->load($integrations[ $methodId ]); - public function process_update_creds_sandbox() { - $this->process_update_creds( true ); - } + if ($sandbox) { + if (isset($integrations[ $methodId ]['test_auth_code']) && ! empty($integrations[ $methodId ]['test_auth_code'])) { + die('An auth code is being processed'); + } + $integrations[ $methodId ]['test_auth_code'] = $authCode; + $mepr_options->integrations = $integrations; + $mepr_options->store(false); + } else { + if (isset($integrations[ $methodId ]['live_auth_code']) && ! empty($integrations[ $methodId ]['live_auth_code'])) { + die('An auth code is being processed'); + } + $integrations[ $methodId ]['live_auth_code'] = $authCode; + $mepr_options->integrations = $integrations; + $mepr_options->store(false); + } - public function process_update_creds( $sandbox = false ) { - // Security check - if ( ! isset( $_GET['_wpnonce'] ) || ! wp_verify_nonce( $_GET['_wpnonce'], 'paypal-update-creds' ) ) { - wp_die( __( 'Sorry, updating your credentials failed. (security)', 'memberpress' ) ); - } + $site_uuid = get_option('mepr_authenticator_site_uuid'); - if ( ! current_user_can( 'manage_options' ) ) { - wp_die( __( 'You do not have sufficient permission to perform this operation', 'memberpress' ) ); - } + $payload = [ + 'site_uuid' => $site_uuid, + ]; - $posted = json_decode( @file_get_contents( 'php://input' ), true ); - self::debug_log( $posted ); - $authCode = $posted['authCode']; - $sharedId = $posted['sharedId']; - $methodId = $posted['payment_method_id']; - - $this->handle_update_creds( $sandbox, $authCode, $sharedId, $methodId ); - } - - public function handle_update_creds( $sandbox, $authCode, $sharedId, $methodId ) { - $pm = new MeprPayPalCommerceGateway(); - $mepr_options = MeprOptions::fetch(); - $integrations = $mepr_options->integrations; - - if ( ! isset( $integrations[ $methodId ] ) ) { - $integrations[ $methodId ] = [ - 'label' => esc_html( __( 'PayPal', 'memberpress' ) ), - 'id' => $methodId, - 'gateway' => 'MeprPayPalCommerceGateway', - 'saved' => true, - ]; - - $mepr_options->integrations = $integrations; - $mepr_options->store( false ); - $pm->load( array( 'id' => $methodId ) ); - $pm->id = $methodId; - } + $jwt = MeprAuthenticatorCtrl::generate_jwt($payload); + $options = [ + 'body' => [ + 'auth_code' => $authCode, + 'share_id' => $sharedId, + ], + 'headers' => MeprUtils::jwt_header($jwt, MEPR_PAYPAL_SERVICE_DOMAIN), + ]; - $pm->load( $integrations[ $methodId ] ); - - if ( $sandbox ) { - if ( isset( $integrations[ $methodId ]['test_auth_code'] ) && ! empty( $integrations[ $methodId ]['test_auth_code'] ) ) { - die('An auth code is being processed'); - } - $integrations[ $methodId ]['test_auth_code'] = $authCode; - $mepr_options->integrations = $integrations; - $mepr_options->store( false ); - } else { - if ( isset( $integrations[ $methodId ]['live_auth_code'] ) && ! empty( $integrations[ $methodId ]['live_auth_code'] ) ) { - die('An auth code is being processed'); - } - $integrations[ $methodId ]['live_auth_code'] = $authCode; - $mepr_options->integrations = $integrations; - $mepr_options->store( false ); - } + if ($sandbox) { + $endpoint = MEPR_PAYPAL_SERVICE_URL . "/sandbox/credentials/{$methodId}"; + } else { + $endpoint = MEPR_PAYPAL_SERVICE_URL . "/credentials/{$methodId}"; + } - $site_uuid = get_option( 'mepr_authenticator_site_uuid' ); - - $payload = array( - 'site_uuid' => $site_uuid - ); - - $jwt = MeprAuthenticatorCtrl::generate_jwt( $payload ); - $options = array( - 'body' => [ - 'auth_code' => $authCode, - 'share_id' => $sharedId, - ], - 'headers' => MeprUtils::jwt_header( $jwt, MEPR_PAYPAL_SERVICE_DOMAIN ) - ); - - if ( $sandbox ) { - $endpoint = MEPR_PAYPAL_SERVICE_URL . "/sandbox/credentials/{$methodId}"; - } else { - $endpoint = MEPR_PAYPAL_SERVICE_URL . "/credentials/{$methodId}"; + $response = wp_remote_post($endpoint, $options); + $creds = wp_remote_retrieve_body($response); + $creds = json_decode($creds, true); + + if (isset($creds['client_id']) && isset($creds['client_secret'])) { + $webhook_id = self::create_webhook($pm->notify_url('webhook'), $creds['client_id'], $creds['client_secret'], $sandbox); + + if ($sandbox) { + $integrations[ $methodId ]['test_client_id'] = $creds['client_id']; + $integrations[ $methodId ]['test_client_secret'] = $creds['client_secret']; + $integrations[ $methodId ]['test_auth_code'] = ''; + $integrations[ $methodId ]['test_webhook_id'] = $webhook_id; + } else { + $integrations[ $methodId ]['live_client_id'] = $creds['client_id']; + $integrations[ $methodId ]['live_client_secret'] = $creds['client_secret']; + $integrations[ $methodId ]['live_auth_code'] = ''; + $integrations[ $methodId ]['live_webhook_id'] = $webhook_id; + } + + $mepr_options->integrations = $integrations; + $mepr_options->store(false); + } } - $response = wp_remote_post( $endpoint, $options ); - $creds = wp_remote_retrieve_body( $response ); - $creds = json_decode( $creds, true ); - - if ( isset( $creds['client_id'] ) && isset( $creds['client_secret'] ) ) { - $webhook_id = self::create_webhook( $pm->notify_url( 'webhook' ), $creds['client_id'], $creds['client_secret'], $sandbox ); - - if ( $sandbox ) { - $integrations[ $methodId ]['test_client_id'] = $creds['client_id']; - $integrations[ $methodId ]['test_client_secret'] = $creds['client_secret']; - $integrations[ $methodId ]['test_auth_code'] = ''; - $integrations[ $methodId ]['test_webhook_id'] = $webhook_id; - } else { - $integrations[ $methodId ]['live_client_id'] = $creds['client_id']; - $integrations[ $methodId ]['live_client_secret'] = $creds['client_secret']; - $integrations[ $methodId ]['live_auth_code'] = ''; - $integrations[ $methodId ]['live_webhook_id'] = $webhook_id; - } - - $mepr_options->integrations = $integrations; - $mepr_options->store( false ); - } - } + public function get_smart_button_mode() + { + $mepr_options = MeprOptions::fetch(); + $transaction_id = isset($_POST['mepr_transaction_id']) && is_numeric($_POST['mepr_transaction_id']) ? (int) $_POST['mepr_transaction_id'] : 0; - public function get_smart_button_mode() { - $mepr_options = MeprOptions::fetch(); - $transaction_id = isset($_POST['mepr_transaction_id']) && is_numeric($_POST['mepr_transaction_id']) ? (int) $_POST['mepr_transaction_id'] : 0; + if ($transaction_id > 0) { + $txn = new MeprTransaction($transaction_id); - if($transaction_id > 0) { - $txn = new MeprTransaction($transaction_id); + if (!$txn->id) { + wp_send_json_error(__('Transaction not found', 'memberpress')); + } - if(!$txn->id) { - wp_send_json_error(__('Transaction not found', 'memberpress')); - } + $pm = $mepr_options->payment_method($txn->gateway); - $pm = $mepr_options->payment_method($txn->gateway); + if (!($pm instanceof MeprPayPalCommerceGateway)) { + wp_send_json_error(__('Invalid payment gateway', 'memberpress')); + } - if(!($pm instanceof MeprPayPalCommerceGateway)) { - wp_send_json_error(__('Invalid payment gateway', 'memberpress')); - } + $prd = $txn->product(); + } else { + $product_id = isset($_POST['mepr_product_id']) ? (int) $_POST['mepr_product_id'] : 0; + $prd = new MeprProduct($product_id); + } - $prd = $txn->product(); - } - else { - $product_id = isset($_POST['mepr_product_id']) ? (int) $_POST['mepr_product_id'] : 0; - $prd = new MeprProduct($product_id); - } + if (empty($prd->ID)) { + wp_send_json_error(__('Product not found', 'memberpress')); + } - if(empty($prd->ID)) { - wp_send_json_error(__('Product not found', 'memberpress')); - } + $has_subscription = !$prd->is_one_time_payment(); - $has_subscription = !$prd->is_one_time_payment(); + $order_bump_product_ids = isset($_POST['mepr_order_bumps']) && is_array($_POST['mepr_order_bumps']) ? array_map('intval', $_POST['mepr_order_bumps']) : []; - $order_bump_product_ids = isset($_POST['mepr_order_bumps']) && is_array($_POST['mepr_order_bumps']) ? array_map('intval', $_POST['mepr_order_bumps']) : []; + foreach ($order_bump_product_ids as $order_bump_product_id) { + $product = new MeprProduct($order_bump_product_id); - foreach($order_bump_product_ids as $order_bump_product_id) { - $product = new MeprProduct($order_bump_product_id); + if (empty($product->ID)) { + wp_send_json_error(__('Product not found', 'memberpress')); + } - if(empty($product->ID)) { - wp_send_json_error(__('Product not found', 'memberpress')); - } + if (!$product->is_one_time_payment()) { + $has_subscription = true; + } + } - if(!$product->is_one_time_payment()) { - $has_subscription = true; - } + wp_send_json_success($has_subscription ? 'subscription' : 'order'); } - wp_send_json_success($has_subscription ? 'subscription' : 'order'); - } + public function generate_smart_button_object() + { + $mepr_options = MeprOptions::fetch(); + $key = 'mepr_payment_method'; + $payment_method_id = isset($_POST[$key]) ? sanitize_text_field(wp_unslash($_POST[$key])) : ''; + $pm = $mepr_options->payment_method($payment_method_id); - public function generate_smart_button_object() { - $_POST['smart-payment-button'] = true; - $checkout_ctrl = MeprCtrlFactory::fetch( 'checkout' ); - $checkout_ctrl->process_signup_form(); + if (!($pm instanceof MeprPayPalCommerceGateway)) { + wp_send_json_error(['errors' => [__('Invalid payment gateway', 'memberpress')]]); + } - if ( isset( $_REQUEST['errors'] ) ) { - wp_send_json_error( ['errors' => $_REQUEST['errors']] ); + if ($pm->settings->enable_smart_button != 'on') { + wp_send_json_error(['errors' => [__('Bad request', 'memberpress')]]); + } + + $_POST['smart-payment-button'] = true; + $checkout_ctrl = MeprCtrlFactory::fetch('checkout'); + $checkout_ctrl->process_signup_form(); + + if (isset($_REQUEST['errors'])) { + wp_send_json_error(['errors' => $_REQUEST['errors']]); + } + + wp_send_json($_REQUEST); } - wp_send_json( $_REQUEST ); - } + public static function get_base_paypal_endpoint($sandbox = false) + { + if ($sandbox) { + return self::PAYPAL_URL_SANDBOX; + } - public static function get_base_paypal_endpoint( $sandbox = false ) { - if ( $sandbox ) { - return self::PAYPAL_URL_SANDBOX; + return self::PAYPAL_URL_LIVE; } - return self::PAYPAL_URL_LIVE; - } + public function rollback_paypal_to_standard() + { + $mepr_options = MeprOptions::fetch(); + $id = filter_input(INPUT_GET, 'method-id', FILTER_SANITIZE_STRING); - public function rollback_paypal_to_standard() { - $mepr_options = MeprOptions::fetch(); - $id = filter_input( INPUT_GET, 'method-id', FILTER_SANITIZE_STRING ); + if (! isset($mepr_options->legacy_integrations[ $id ])) { + return; + } - if ( ! isset( $mepr_options->legacy_integrations[ $id ] ) ) { - return; - } + $mepr_options->integrations[ $id ] = $mepr_options->legacy_integrations[ $id ]; + $mepr_options->integrations[ $id ]['gateway'] = MeprPayPalStandardGateway::class; + $mepr_options->store(false); + $message = esc_html(__('You have reverted PayPal to legacy gateway', 'memberpress')); + $url = admin_url('admin.php?page=memberpress-options&paypal-gateway-message=' . $message . '#mepr-integration'); + MeprUtils::wp_redirect($url); + } + + /** + * Run a site health check and return the result + * + * @return array + */ + public function run_site_health_test() + { + $result = [ + 'label' => __('MemberPress is securely connected to PayPal', 'memberpress'), + 'status' => 'good', + 'badge' => [ + 'label' => __('Security', 'memberpress'), + 'color' => 'blue', + ], + 'description' => sprintf( + '

    %s

    ', + __('Your MemberPress PayPal connection is complete and secure.', 'memberpress') + ), + 'actions' => '', + 'test' => 'run_site_health_test', + ]; + + if (class_exists('MeprPaypalCommerceGateway') && ! MeprPayPalCommerceGateway::has_method_with_connect_status('not-connected')) { + $result = [ + 'label' => __('MemberPress is not securely connected to PayPal', 'memberpress'), + 'status' => 'critical', + 'badge' => [ + 'label' => __('Security', 'memberpress'), + 'color' => 'red', + ], + 'description' => sprintf( + '

    %s

    ', + __('Your current PayPal payment connection is out of date and may become insecure or stop working. Please click the button below to re-connect your PayPal payment method now.', 'memberpress') + ), + 'actions' => '' . __('Re-connect PayPal Payments to Fix this Error Now', 'memberpress') . '', + 'test' => 'run_site_health_test', + ]; + } - $mepr_options->integrations[ $id ] = $mepr_options->legacy_integrations[ $id ]; - $mepr_options->integrations[ $id ]['gateway'] = MeprPayPalStandardGateway::class; - $mepr_options->store( false ); - $message = esc_html( __( 'You have reverted PayPal to legacy gateway', 'memberpress' ) ); - $url = admin_url( 'admin.php?page=memberpress-options&paypal-gateway-message=' . $message . '#mepr-integration' ); - MeprUtils::wp_redirect( $url ); - } - - /** - * Run a site health check and return the result - * - * @return array - */ - public function run_site_health_test() { - $result = array( - 'label' => __( 'MemberPress is securely connected to PayPal', 'memberpress' ), - 'status' => 'good', - 'badge' => array( - 'label' => __( 'Security', 'memberpress' ), - 'color' => 'blue', - ), - 'description' => sprintf( - '

    %s

    ', - __( 'Your MemberPress PayPal connection is complete and secure.', 'memberpress' ) - ), - 'actions' => '', - 'test' => 'run_site_health_test', - ); - - if ( class_exists( 'MeprPaypalCommerceGateway' ) && ! MeprPayPalCommerceGateway::has_method_with_connect_status( 'not-connected' ) ) { - $result = array( - 'label' => __( 'MemberPress is not securely connected to PayPal', 'memberpress' ), - 'status' => 'critical', - 'badge' => array( - 'label' => __( 'Security', 'memberpress' ), - 'color' => 'red', - ), - 'description' => sprintf( - '

    %s

    ', - __( 'Your current PayPal payment connection is out of date and may become insecure or stop working. Please click the button below to re-connect your PayPal payment method now.', 'memberpress' ) - ), - 'actions' => '' . __( 'Re-connect PayPal Payments to Fix this Error Now', 'memberpress' ) . '', - 'test' => 'run_site_health_test', - ); + return $result; } - - return $result; - } } diff --git a/app/controllers/MeprPayWallCtrl.php b/app/controllers/MeprPayWallCtrl.php index b3af2a7..f25c6bc 100644 --- a/app/controllers/MeprPayWallCtrl.php +++ b/app/controllers/MeprPayWallCtrl.php @@ -1,229 +1,262 @@ ID, $excluded_wp_posts)) { - $excluded = true; - } + if (!empty($excluded_category_slugs) && in_category($excluded_category_slugs, $post)) { + $excluded = true; + } - $excluded = MeprHooks::apply_filters('mepr-paywall-is-excluded', $excluded, $post); + if (!empty($excluded_tag_slugs) && has_tag($excluded_tag_slugs, $post)) { + $excluded = true; + } - return $excluded; - } + if (!empty($excluded_wp_posts) && in_array($post->ID, $excluded_wp_posts)) { + $excluded = true; + } - //Tell search engines NOT to cache this page - public static function add_noarchive_to_wp_head() { - $post = MeprUtils::get_current_post(); - $mepr_options = MeprOptions::fetch(); - $uri = $_SERVER['REQUEST_URI']; + $excluded = MeprHooks::apply_filters('mepr-paywall-is-excluded', $excluded, $post); - //Check the URI and post first to see if this is even a locked page - //TODO: - //Ugh should probably check for non-singular page types and make sure - //none of them are protected here as well eventually - if(!MeprRule::is_uri_locked($uri) && ($post === false || !MeprRule::is_locked($post))) { - return; + return $excluded; } - if($mepr_options->authorize_seo_views && self::verify_bot()): - ?> + // Tell search engines NOT to cache this page + public static function add_noarchive_to_wp_head() + { + $post = MeprUtils::get_current_post(); + $mepr_options = MeprOptions::fetch(); + $uri = $_SERVER['REQUEST_URI']; + + // Check the URI and post first to see if this is even a locked page + // TODO: + // Ugh should probably check for non-singular page types and make sure + // none of them are protected here as well eventually + if (!MeprRule::is_uri_locked($uri) && ($post === false || !MeprRule::is_locked($post))) { + return; + } + + if ($mepr_options->authorize_seo_views && self::verify_bot()) : + ?> - authorize_seo_views && $mepr_options->seo_unauthorized_noindex): - ?> + if (get_option('blog_public') && !$mepr_options->authorize_seo_views && $mepr_options->seo_unauthorized_noindex) : + ?> - paywall_enabled && $mepr_options->paywall_num_free_views > 0) { - $num_views = (isset($_COOKIE[self::$cookie_name]) && !empty($_COOKIE[self::$cookie_name]))?$_COOKIE[self::$cookie_name]:0; + if ($mepr_options->paywall_enabled && $mepr_options->paywall_num_free_views > 0) { + $num_views = (isset($_COOKIE[self::$cookie_name]) && !empty($_COOKIE[self::$cookie_name])) ? $_COOKIE[self::$cookie_name] : 0; - if($num_views !== 0) { - $num_views = base64_decode($num_views); - } + if ($num_views !== 0) { + $num_views = base64_decode($num_views); + } - $cookie_time = MeprHooks::apply_filters('mepr-paywall-cookie-time', (time() + 60 * 60 * 24 * 30), $num_views); ; + $cookie_time = MeprHooks::apply_filters('mepr-paywall-cookie-time', (time() + 60 * 60 * 24 * 30), $num_views); + ; - setcookie(self::$cookie_name, base64_encode(($num_views + 1)), $cookie_time, '/'); - $_COOKIE[self::$cookie_name] = base64_encode(($num_views + 1)); //Update the COOKIE global too for use later downstream + setcookie(self::$cookie_name, base64_encode(($num_views + 1)), $cookie_time, '/'); + $_COOKIE[self::$cookie_name] = base64_encode(($num_views + 1)); // Update the COOKIE global too for use later downstream + } } - } - public static function paywall_allow_through_content($protect, $post, $uri) { - if(self::paywall_allow_through()) - return false; //Need to return false to allow them through the blocks + public static function paywall_allow_through_content($protect, $post, $uri) + { + if (self::paywall_allow_through()) { + return false; // Need to return false to allow them through the blocks + } - return $protect; - } + return $protect; + } - public static function paywall_allow_through_redirection($protect, $uri, $delim) { - if(self::paywall_allow_through('uri')) - return false; //Need to return false to allow them through the blocks + public static function paywall_allow_through_redirection($protect, $uri, $delim) + { + if (self::paywall_allow_through('uri')) { + return false; // Need to return false to allow them through the blocks + } - return $protect; - } + return $protect; + } - public static function paywall_allow_through($type = 'content') { - $post = MeprUtils::get_current_post(); + public static function paywall_allow_through($type = 'content') + { + $post = MeprUtils::get_current_post(); - //Do nothing if the member is logged in, or if this is a bot (bots might be allowed through later down the chain) - if(MeprUtils::is_user_logged_in() || self::verify_bot()) - return false; + // Do nothing if the member is logged in, or if this is a bot (bots might be allowed through later down the chain) + if (MeprUtils::is_user_logged_in() || self::verify_bot()) { + return false; + } - //check if Post is excluded from the PayWall - if so do NOT allow them through - if($post !== false && self::is_excluded($post)) - return false; + // check if Post is excluded from the PayWall - if so do NOT allow them through + if ($post !== false && self::is_excluded($post)) { + return false; + } - $mepr_options = MeprOptions::fetch(); + $mepr_options = MeprOptions::fetch(); - if($mepr_options->paywall_enabled && $mepr_options->paywall_num_free_views > 0) { - $num_views = (isset($_COOKIE[self::$cookie_name]) && !empty($_COOKIE[self::$cookie_name]))?$_COOKIE[self::$cookie_name]:0; + if ($mepr_options->paywall_enabled && $mepr_options->paywall_num_free_views > 0) { + $num_views = (isset($_COOKIE[self::$cookie_name]) && !empty($_COOKIE[self::$cookie_name])) ? $_COOKIE[self::$cookie_name] : 0; - if($num_views !== 0) { - $num_views = base64_decode($num_views); - } + if ($num_views !== 0) { + $num_views = base64_decode($num_views); + } - //There's a race condition happening here, so we need to add one to the uri's - if($type == 'uri') { - $num_views += 1; - } + // There's a race condition happening here, so we need to add one to the uri's + if ($type == 'uri') { + $num_views += 1; + } - if($num_views <= $mepr_options->paywall_num_free_views) - return true; + if ($num_views <= $mepr_options->paywall_num_free_views) { + return true; + } + } } - } - public static function allow_search_engines_content($protect, $post, $uri) { - if(self::allow_search_engines_through()) - return false; //Need to return false here to allow SE through the blocks + public static function allow_search_engines_content($protect, $post, $uri) + { + if (self::allow_search_engines_through()) { + return false; // Need to return false here to allow SE through the blocks + } - $hide_comments = $protect ? '__return_true' : '__return_false'; - add_filter('mepr-rule-comments', $hide_comments); + $hide_comments = $protect ? '__return_true' : '__return_false'; + add_filter('mepr-rule-comments', $hide_comments); - return $protect; - } + return $protect; + } - public static function allow_search_engines_redirection($protect, $uri, $delim) { - if(self::allow_search_engines_through()) - return false; //Need to return false here to allow SE through the blocks + public static function allow_search_engines_redirection($protect, $uri, $delim) + { + if (self::allow_search_engines_through()) { + return false; // Need to return false here to allow SE through the blocks + } - return $protect; - } + return $protect; + } - public static function allow_search_engines_through() { - $post = MeprUtils::get_current_post(); + public static function allow_search_engines_through() + { + $post = MeprUtils::get_current_post(); - //check if Post is excluded from the PayWall - if($post !== false && self::is_excluded($post)) - return false; + // check if Post is excluded from the PayWall + if ($post !== false && self::is_excluded($post)) { + return false; + } - $mepr_options = MeprOptions::fetch(); + $mepr_options = MeprOptions::fetch(); - if($mepr_options->authorize_seo_views) - return self::verify_bot(); + if ($mepr_options->authorize_seo_views) { + return self::verify_bot(); + } - return false; - } + return false; + } - public static function verify_bot() { - //If the user is logged in, then bail - if(MeprUtils::is_user_logged_in()) { return false; } + public static function verify_bot() + { + // If the user is logged in, then bail + if (MeprUtils::is_user_logged_in()) { + return false; + } - $agent = 'no-agent-found'; - if(isset($_SERVER['HTTP_USER_AGENT']) && !empty($_SERVER['HTTP_USER_AGENT'])) { - $agent = strtolower($_SERVER['HTTP_USER_AGENT']); - } + $agent = 'no-agent-found'; + if (isset($_SERVER['HTTP_USER_AGENT']) && !empty($_SERVER['HTTP_USER_AGENT'])) { + $agent = strtolower($_SERVER['HTTP_USER_AGENT']); + } - $known_engines = array('google', 'bing', 'msn', 'yahoo', 'ask'); + $known_engines = ['google', 'bing', 'msn', 'yahoo', 'ask']; - static $returned = null; + static $returned = null; - if(!is_null($returned)) { - return $returned; - } + if (!is_null($returned)) { + return $returned; + } - foreach($known_engines as $engine) { - if(strpos($agent, $engine) !== false) { - $ip_to_check = $_SERVER['REMOTE_ADDR']; + foreach ($known_engines as $engine) { + if (strpos($agent, $engine) !== false) { + $ip_to_check = $_SERVER['REMOTE_ADDR']; - //Lookup the host by this IP address - $hostname = gethostbyaddr($ip_to_check); + // Lookup the host by this IP address + $hostname = gethostbyaddr($ip_to_check); - if($engine == 'google' && !preg_match('#^.*\.googlebot\.com$#', $hostname)) - break; + if ($engine == 'google' && !preg_match('#^.*\.googlebot\.com$#', $hostname)) { + break; + } - if(($engine == 'bing' || $engine == 'msn') && !preg_match('#^.*\.search\.msn\.com$#', $hostname)) - break; + if (($engine == 'bing' || $engine == 'msn') && !preg_match('#^.*\.search\.msn\.com$#', $hostname)) { + break; + } - if($engine == 'ask' && !preg_match('#^.*\.ask\.com$#', $hostname)) - break; + if ($engine == 'ask' && !preg_match('#^.*\.ask\.com$#', $hostname)) { + break; + } - //Even though yahoo is contracted with bingbot, they do still send out slurp to update some entries etc - if(($engine == 'yahoo' || $engine == 'slurp') && !preg_match('#^.*\.crawl\.yahoo\.net$#', $hostname)) - break; + // Even though yahoo is contracted with bingbot, they do still send out slurp to update some entries etc + if (($engine == 'yahoo' || $engine == 'slurp') && !preg_match('#^.*\.crawl\.yahoo\.net$#', $hostname)) { + break; + } - if($hostname !== false && $hostname != $ip_to_check) { - //Do the reverse lookup - $ip_to_verify = gethostbyname($hostname); + if ($hostname !== false && $hostname != $ip_to_check) { + // Do the reverse lookup + $ip_to_verify = gethostbyname($hostname); - if($ip_to_verify != $hostname && $ip_to_verify == $ip_to_check) { - $returned = true; - return $returned; - } + if ($ip_to_verify != $hostname && $ip_to_verify == $ip_to_check) { + $returned = true; + return $returned; + } + } + } } - } - } - //Otherwise return false - $returned = false; - return $returned; - } + // Otherwise return false + $returned = false; + return $returned; + } } //End class diff --git a/app/controllers/MeprPopupCtrl.php b/app/controllers/MeprPopupCtrl.php index 193804c..9ea16ee 100644 --- a/app/controllers/MeprPopupCtrl.php +++ b/app/controllers/MeprPopupCtrl.php @@ -1,237 +1,265 @@ -popup_css = "{$cdn_base}/magnific-popup.min.css"; - $this->popup_js = "{$cdn_base}/jquery.magnific-popup.min.js"; - - // This is an array of the currently defined popups ... - // used to validate that the popup specified actually exists - $this->popups = array( - // XXX: Not showing this anymore since the user has the ability - // to disable Anonymous data reporting from their options - // - //'senddata' => array( - // 'user_popup' => false, - // 'delay' => MONTH_IN_SECONDS, - // 'delay_after_last_popup' => WEEK_IN_SECONDS, - //), - ); - - parent::__construct(); - } - - public function load_hooks() { - // This is a hidden option to help support in case - // there's a problem stopping or delaying a popup - $dap = get_option('mepr_disable_all_popups'); - if($dap) { return; } - - add_action('admin_enqueue_scripts', array($this, 'enqueue_admin_scripts')); - add_action('wp_ajax_mepr_stop_popup', array($this, 'ajax_stop_or_delay_popup')); - add_action('wp_ajax_mepr_delay_popup', array($this, 'ajax_stop_or_delay_popup')); - add_action('admin_notices', array($this,'display_popups')); - } - - public function enqueue_admin_scripts($hook) { - $mepr_cpts = array( - 'memberpressproduct', - 'memberpressgroup', - 'memberpressrule', - 'memberpresscoupon', - 'mp-reminder' - ); - - if( false !== strstr($hook, 'memberpress') || - ( $hook=='edit.php' && - isset($_REQUEST['post_type']) && - in_array($_REQUEST['post_type'], $mepr_cpts) ) ) { - wp_register_style('jquery-magnific-popup', $this->popup_css); - wp_enqueue_style( - 'mepr-admin-popup', - MEPR_CSS_URL.'/admin_popup.css', - array('jquery-magnific-popup'), - MEPR_VERSION - ); - - wp_register_script('jquery-magnific-popup', $this->popup_js, array('jquery')); - wp_enqueue_script( - 'mepr-admin-popup', - MEPR_JS_URL.'/admin_popup.js', - array('jquery','jquery-magnific-popup'), - MEPR_VERSION - ); - $loc = array( - 'security' => wp_create_nonce('mepr-admin-popup'), - 'error' => __('An unknown error occurred.', 'memberpress') - ); - wp_localize_script('mepr-admin-popup','MeprPopup', $loc); - } - } - private function on_memberpress_page() { - $screen = get_current_screen(); - return (isset($screen->parent_base) && $screen->parent_base=='memberpress'); - } - - public function display_popups() { - //if(!$this->on_memberpress_page()) { return; } - - // If this isn't a MemberPress authorized user then bail - if(!MeprUtils::is_mepr_admin()) { return; } - - foreach($this->popups as $popup => $settings) { - $this->maybe_show_popup($popup); - } - } - - public function ajax_stop_or_delay_popup() { - MeprUtils::check_ajax_referer('mepr-admin-popup','security'); - - // If this isn't a MemberPress authorized user then bail - if(!MeprUtils::is_mepr_admin()) { - MeprUtils::exit_with_status(403,json_encode(array('error'=>__('Forbidden', 'memberpress')))); - } - - if(!isset($_POST['popup'])) { - MeprUtils::exit_with_status(400,json_encode(array('error'=>__('Must specify a popup', 'memberpress')))); - } - - $popup = sanitize_text_field($_POST['popup']); - - if(!$this->is_valid_popup($popup)) { - MeprUtils::exit_with_status(400,json_encode(array('error'=>__('Invalid popup', 'memberpress')))); - } - - if($_POST['action']=='mepr_delay_popup') { - $this->delay_popup($popup); - $message = __('The popup was successfully delayed', 'memberpress'); - } - else { - $this->stop_popup($popup); // TODO: Error handling - $message = __('The popup was successfully stopped', 'memberpress'); +popup_css = "{$cdn_base}/magnific-popup.min.css"; + $this->popup_js = "{$cdn_base}/jquery.magnific-popup.min.js"; + + /** + * This is an array of the currently defined popups, used to validate that the popup specified actually exists. + * + * 'example' => [ + * 'user_popup' => false, + * 'delay' => MONTH_IN_SECONDS, + * 'delay_after_last_popup' => WEEK_IN_SECONDS, + * ], + */ + $this->popups = []; + + parent::__construct(); } - MeprUtils::exit_with_status(200,json_encode(compact('message'))); - } + public function load_hooks() + { + // This is a hidden option to help support in case + // there's a problem stopping or delaying a popup + $dap = get_option('mepr_disable_all_popups'); + if ($dap) { + return; + } + + add_action('admin_enqueue_scripts', [$this, 'enqueue_admin_scripts']); + add_action('wp_ajax_mepr_stop_popup', [$this, 'ajax_stop_or_delay_popup']); + add_action('wp_ajax_mepr_delay_popup', [$this, 'ajax_stop_or_delay_popup']); + add_action('admin_notices', [$this,'display_popups']); + } - private function is_valid_popup($popup) { - return in_array($popup,array_keys($this->popups)); - } + public function enqueue_admin_scripts($hook) + { + $mepr_cpts = [ + 'memberpressproduct', + 'memberpressgroup', + 'memberpressrule', + 'memberpresscoupon', + 'mp-reminder', + ]; + + if ( + false !== strstr($hook, 'memberpress') || + ( $hook == 'edit.php' && + isset($_REQUEST['post_type']) && + in_array($_REQUEST['post_type'], $mepr_cpts) ) + ) { + wp_register_style('jquery-magnific-popup', $this->popup_css); + wp_enqueue_style( + 'mepr-admin-popup', + MEPR_CSS_URL . '/admin_popup.css', + ['jquery-magnific-popup'], + MEPR_VERSION + ); + + wp_register_script('jquery-magnific-popup', $this->popup_js, ['jquery']); + wp_enqueue_script( + 'mepr-admin-popup', + MEPR_JS_URL . '/admin_popup.js', + ['jquery','jquery-magnific-popup'], + MEPR_VERSION + ); + $loc = [ + 'security' => wp_create_nonce('mepr-admin-popup'), + 'error' => __('An unknown error occurred.', 'memberpress'), + ]; + wp_localize_script('mepr-admin-popup', 'MeprPopup', $loc); + } + } - private function stop_popup($popup) { - // TODO: Should we add some error handling? - if(!$this->is_valid_popup($popup)) { return; } + public function display_popups() + { + // If this isn't a MemberPress authorized user, then bail. + if (!MeprUtils::is_mepr_admin()) { + return; + } - if($this->popups[$popup]['user_popup']) { - $user_id = MeprUtils::get_current_user_id(); - update_user_meta($user_id, $this->popup_stop_key($popup), 1); - } - else { - update_option($this->popup_stop_key($popup), 1); + foreach ($this->popups as $popup => $settings) { + $this->maybe_show_popup($popup); + } } - } - private function delay_popup($popup) { - // TODO: Should we add some error handling? - if(!$this->is_valid_popup($popup)) { return; } + public function ajax_stop_or_delay_popup() + { + MeprUtils::check_ajax_referer('mepr-admin-popup', 'security'); - set_transient( - $this->popup_delay_key($popup), - 1, - $this->popups[$popup]['delay'] - ); - } + // If this isn't a MemberPress authorized user, then bail. + if (!MeprUtils::is_mepr_admin()) { + MeprUtils::exit_with_status(403, json_encode(['error' => __('Forbidden', 'memberpress')])); + } - private function is_popup_delayed($popup) { - if(!$this->is_valid_popup($popup)) { $return; } + if (!isset($_POST['popup'])) { + MeprUtils::exit_with_status(400, json_encode(['error' => __('Must specify a popup', 'memberpress')])); + } - if($this->popups[$popup]['user_popup']) { - // check if it's been delayed or stopped - $user_id = MeprUtils::get_current_user_id(); - return get_transient($this->popup_delay_key($popup)); - } + $popup = sanitize_text_field($_POST['popup']); - return get_transient($this->popup_delay_key($popup)); - } + if (!$this->is_valid_popup($popup)) { + MeprUtils::exit_with_status(400, json_encode(['error' => __('Invalid popup', 'memberpress')])); + } - private function is_popup_stopped($popup) { - if(!$this->is_valid_popup($popup)) { $return; } + if ($_POST['action'] == 'mepr_delay_popup') { + $this->delay_popup($popup); + $message = __('The popup was successfully delayed', 'memberpress'); + } else { + $this->stop_popup($popup); // TODO: Error handling + $message = __('The popup was successfully stopped', 'memberpress'); + } - if($this->popups[$popup]['user_popup']) { - $user_id = MeprUtils::get_current_user_id(); - return get_user_meta($user_id, $this->popup_stop_key($popup), true); + MeprUtils::exit_with_status(200, json_encode(compact('message'))); } - return get_option($this->popup_stop_key($popup)); - } - - private function set_popup_last_viewed_timestamp($popup) { - $timestamp = time(); - return update_option('mepr-popup-last-viewed', compact('popup','timestamp')); - } + private function is_valid_popup($popup) + { + return in_array($popup, array_keys($this->popups)); + } - private function get_popup_last_viewed_timestamp() { - $default = array('popup'=>false,'timestamp'=>false); - return get_option('mepr-popup-last-viewed',$default); - } + private function stop_popup($popup) + { + // TODO: Should we add some error handling? + if (!$this->is_valid_popup($popup)) { + return; + } + + if ($this->popups[$popup]['user_popup']) { + $user_id = MeprUtils::get_current_user_id(); + update_user_meta($user_id, $this->popup_stop_key($popup), 1); + } else { + update_option($this->popup_stop_key($popup), 1); + } + } - private function maybe_show_popup($popup) { - if($this->popup_visible($popup)) { - $this->increment_popup_display_count($popup); - $this->set_popup_last_viewed_timestamp($popup); - require(MEPR_VIEWS_PATH."/admin/popups/{$popup}.php"); + private function delay_popup($popup) + { + // TODO: Should we add some error handling? + if (!$this->is_valid_popup($popup)) { + return; + } + + set_transient( + $this->popup_delay_key($popup), + 1, + $this->popups[$popup]['delay'] + ); } - } - private function popup_visible($popup) { - $mepr_update = new MeprUpdateCtrl(); - if(!$mepr_update->is_activated() || - !$this->is_valid_popup($popup)) { - return false; + private function is_popup_delayed($popup) + { + if (!$this->is_valid_popup($popup)) { + return; + } + + if ($this->popups[$popup]['user_popup']) { + // check if it's been delayed or stopped + $user_id = MeprUtils::get_current_user_id(); + return get_transient($this->popup_delay_key($popup)); + } + + return get_transient($this->popup_delay_key($popup)); } - // If we're not yet past the delay threshold for the last viewed popup then don't show it - $last_viewed = $this->get_popup_last_viewed_timestamp(); - if( !empty($last_viewed) && - $last_viewed['popup']!=$popup && - ((int)$last_viewed['timestamp'] + (int)$this->popups[$popup]['delay_after_last_popup']) > time() ) { - return false; + private function is_popup_stopped($popup) + { + if (!$this->is_valid_popup($popup)) { + return; + } + + if ($this->popups[$popup]['user_popup']) { + $user_id = MeprUtils::get_current_user_id(); + return get_user_meta($user_id, $this->popup_stop_key($popup), true); + } + + return get_option($this->popup_stop_key($popup)); } - // This is for popups that should be displayed and resolved for each individual admin user - $delayed = $this->is_popup_delayed($popup); + private function set_popup_last_viewed_timestamp($popup) + { + $timestamp = time(); + return update_option('mepr-popup-last-viewed', compact('popup', 'timestamp')); + } - // Popups displayed and resolved for any admin user in the system - $stopped = $this->is_popup_stopped($popup); + private function get_popup_last_viewed_timestamp() + { + $default = [ + 'popup' => false, + 'timestamp' => false, + ]; + return get_option('mepr-popup-last-viewed', $default); + } - return (!$delayed && !$stopped); - } + private function maybe_show_popup($popup) + { + if ($this->popup_visible($popup)) { + $this->increment_popup_display_count($popup); + $this->set_popup_last_viewed_timestamp($popup); + require(MEPR_VIEWS_PATH . "/admin/popups/{$popup}.php"); + } + } - private function increment_popup_display_count($popup) { - $user_id = MeprUtils::get_current_user_id(); - $count = (int)get_user_meta($user_id, $this->popup_display_count_key($popup), true); - update_user_meta($user_id, $this->popup_display_count_key($popup), ++$count); - } + private function popup_visible($popup) + { + $mepr_update = new MeprUpdateCtrl(); + if ( + !$mepr_update->is_activated() || + !$this->is_valid_popup($popup) + ) { + return false; + } + + // If we're not yet past the delay threshold for the last viewed popup then don't show it + $last_viewed = $this->get_popup_last_viewed_timestamp(); + if ( + !empty($last_viewed) && + $last_viewed['popup'] != $popup && + ((int)$last_viewed['timestamp'] + (int)$this->popups[$popup]['delay_after_last_popup']) > time() + ) { + return false; + } + + // This is for popups that should be displayed and resolved for each individual admin user + $delayed = $this->is_popup_delayed($popup); + + // Popups displayed and resolved for any admin user in the system + $stopped = $this->is_popup_stopped($popup); + + return (!$delayed && !$stopped); + } - private function popup_display_count_key($popup) { - return "mepr-{$popup}-popup-display-count"; - } + private function increment_popup_display_count($popup) + { + $user_id = MeprUtils::get_current_user_id(); + $count = (int)get_user_meta($user_id, $this->popup_display_count_key($popup), true); + update_user_meta($user_id, $this->popup_display_count_key($popup), ++$count); + } - private function popup_delay_key($popup) { - if($this->is_valid_popup($popup) && $this->popups[$popup]['user_popup']) { - $user_id = MeprUtils::get_current_user_id(); - return "mepr-delay-{$popup}-popup-for-{$user_id}"; + private function popup_display_count_key($popup) + { + return "mepr-{$popup}-popup-display-count"; } - else { - return "mepr-delay-{$popup}-popup"; + + private function popup_delay_key($popup) + { + if ($this->is_valid_popup($popup) && $this->popups[$popup]['user_popup']) { + $user_id = MeprUtils::get_current_user_id(); + return "mepr-delay-{$popup}-popup-for-{$user_id}"; + } else { + return "mepr-delay-{$popup}-popup"; + } } - } - private function popup_stop_key($popup) { - return "mepr-stop-{$popup}-popup"; - } + private function popup_stop_key($popup) + { + return "mepr-stop-{$popup}-popup"; + } } - diff --git a/app/controllers/MeprPostStatesCtrl.php b/app/controllers/MeprPostStatesCtrl.php index 314f73e..d3097ff 100644 --- a/app/controllers/MeprPostStatesCtrl.php +++ b/app/controllers/MeprPostStatesCtrl.php @@ -1,29 +1,33 @@ thankyou_page_id === $post->ID ) { - $post_states['thankyou_page_id'] = __( 'MemberPress Thank You Page', 'memberpress' ); - } + $mepr_options = MeprOptions::fetch(); - if ( $mepr_options->account_page_id === $post->ID ) { - $post_states['account_page_id'] = __( 'MemberPress Account Page', 'memberpress' ); - } + if ($mepr_options->thankyou_page_id === $post->ID) { + $post_states['thankyou_page_id'] = __('MemberPress Thank You Page', 'memberpress'); + } - if ( $mepr_options->login_page_id === $post->ID ) { - $post_states['login_page_id'] = __( 'MemberPress Login Page', 'memberpress' ); - } + if ($mepr_options->account_page_id === $post->ID) { + $post_states['account_page_id'] = __('MemberPress Account Page', 'memberpress'); + } - return $post_states; + if ($mepr_options->login_page_id === $post->ID) { + $post_states['login_page_id'] = __('MemberPress Login Page', 'memberpress'); + } - } -} \ No newline at end of file + return $post_states; + } +} diff --git a/app/controllers/MeprProductsCtrl.php b/app/controllers/MeprProductsCtrl.php index eb9cae0..92a23fa 100644 --- a/app/controllers/MeprProductsCtrl.php +++ b/app/controllers/MeprProductsCtrl.php @@ -1,1003 +1,1065 @@ cpt = (object)array( - 'slug' => MeprProduct::$cpt, - 'config' => array( - 'labels' => array( - 'name' => __('Memberships', 'memberpress'), - 'singular_name' => __('Membership', 'memberpress'), - 'add_new' => __('Add New', 'memberpress'), - 'add_new_item' => __('Add New Membership', 'memberpress'), - 'edit_item' => __('Edit Membership', 'memberpress'), - 'new_item' => __('New Membership', 'memberpress'), - 'view_item' => __('View Membership', 'memberpress'), - 'search_items' => __('Search Membership', 'memberpress'), - 'not_found' => __('No Membership found', 'memberpress'), - 'not_found_in_trash' => __('No Membership found in Trash', 'memberpress'), - 'parent_item_colon' => __('Parent Membership:', 'memberpress') - ), - 'public' => true, - 'show_ui' => true, //MeprUpdateCtrl::is_activated(), - 'show_in_menu' => 'memberpress', - 'capability_type' => 'page', - 'hierarchical' => true, - 'register_meta_box_cb' => 'MeprProductsCtrl::add_meta_boxes', - 'rewrite' => array("slug" => $mepr_options->product_pages_slug, "with_front" => false), - 'supports' => array('title', 'editor', 'page-attributes', 'comments', 'thumbnail') - ) - ); - register_post_type( $this->cpt->slug, $this->cpt->config ); - } - - public static function register_taxonomy() { - register_taxonomy( - MeprProduct::$taxonomy_product_category, - MeprProduct::$cpt, - array( - 'labels' => array( - 'name' => esc_html_x( 'Categories', 'taxonomy general name', 'memberpress' ), - 'singular_name' => esc_html_x( 'Category', 'taxonomy singular name', 'memberpress' ), - 'search_items' => esc_html__( 'Search Categories', 'memberpress' ), - 'all_items' => esc_html__( 'All Categories', 'memberpress' ), - 'parent_item' => esc_html__( 'Parent Category', 'memberpress' ), - 'parent_item_colon' => esc_html__( 'Parent Category:', 'memberpress' ), - 'edit_item' => esc_html__( 'Edit Category', 'memberpress' ), - 'update_item' => esc_html__( 'Update Category', 'memberpress' ), - 'add_new_item' => esc_html__( 'Add New Category', 'memberpress' ), - 'new_item_name' => esc_html__( 'New Category Name', 'memberpress' ), - 'menu_name' => esc_html__( 'Categories', 'memberpress' ), - 'separate_items_with_commas' => esc_html__( 'Separate Categories with commas', 'memberpress' ), - 'add_or_remove_items' => esc_html__( 'Add or remove Categories', 'memberpress' ), - 'choose_from_most_used' => esc_html__( 'Choose from the most used', 'memberpress' ), - 'popular_items' => esc_html__( 'Popular Categories', 'memberpress' ), - 'not_found' => esc_html__( 'Not Found', 'memberpress' ), - 'no_terms' => esc_html__( 'No Categories', 'memberpress' ), - 'items_list' => esc_html__( 'Categories list', 'memberpress' ), - 'items_list_navigation' => esc_html__( 'Categories list navigation', 'memberpress' ) - ), - 'hierarchical' => false, - 'show_ui' => true, - 'show_admin_column' => true, - 'rewrite' => false, - 'capabilities' => array( - 'manage_terms' => 'manage_options', - 'edit_terms' => 'manage_options', - 'delete_terms' => 'manage_options', - 'assign_terms' => 'manage_options' - ) - ) - ); - } - - public static function columns($columns) { - $columns = array( - "cb" => "", - "ID" => esc_html__("ID", 'memberpress'), - "title" => esc_html__("Membership Title", 'memberpress'), - "terms" => esc_html__("Terms", 'memberpress'), - "url" => esc_html__('URL', 'memberpress') - ); - return MeprHooks::apply_filters('mepr-admin-memberships-columns', $columns); - } - - public static function sortable_columns($columns) { - $columns['ID'] = 'ID'; - return $columns; - } - - public static function custom_columns($column, $post_id) { - $mepr_options = MeprOptions::fetch(); - $product = new MeprProduct($post_id); - - if($product->ID !== null) { - if("ID" == $column) { - echo $product->ID; - } - elseif("terms" == $column) { - echo MeprProductsHelper::format_currency($product, true, null, false); //$product, $show_symbol, $coupon_code, $show_prorated - } - elseif("url" == $column) { - echo $product->url(); - } - } - } - - // Template selection - public static function template_include($template) { - global $post, $wp_query; - - if(!is_singular()) { return $template; } - - if(isset($post) && is_a($post, 'WP_Post') && $post->post_type == MeprProduct::$cpt) { - $product = new MeprProduct($post->ID); - $new_template = $product->get_page_template(); - } - - if(isset($new_template) && !empty($new_template)) { return $new_template; } - - return $template; - } - - public static function add_meta_boxes() { - global $post_id; - - $product = new MeprProduct($post_id); - - add_meta_box("memberpress-product-meta", __('Membership Terms', 'memberpress'), "MeprProductsCtrl::product_meta_box", MeprProduct::$cpt, "side", "high", array('product' => $product)); - - add_meta_box("memberpress-custom-template", __('Custom Page Template', 'memberpress'), "MeprProductsCtrl::custom_page_template", MeprProduct::$cpt, "side", "default", array('product' => $product)); - - add_meta_box("memberpress-product-options", __('Membership Options', 'memberpress'), "MeprProductsCtrl::product_options_meta_box", MeprProduct::$cpt, "normal", "high", array('product' => $product)); - - MeprHooks::do_action('mepr-product-meta-boxes', $product); // DEPRECATED - MeprHooks::do_action('mepr-membership-meta-boxes', $product); - } - - public static function save_postdata($post_id) { - $post = get_post($post_id); - - if(!wp_verify_nonce((isset($_POST[MeprProduct::$nonce_str]))?$_POST[MeprProduct::$nonce_str]:'', MeprProduct::$nonce_str.wp_salt())) { - return $post_id; //Nonce prevents meta data from being wiped on move to trash - } - - if(defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) { - return $post_id; - } - - if(defined('DOING_AJAX')) { - return; - } - - if(!empty($post) && $post->post_type == MeprProduct::$cpt) { - $product = new MeprProduct($post_id); - - extract($_POST, EXTR_SKIP); - - $product->price = (isset($_mepr_product_price))?MeprUtils::format_currency_us_float(sanitize_text_field($_mepr_product_price)):$product->attrs['price']; - $product->period = (isset($_mepr_product_period))?sanitize_text_field($_mepr_product_period):$product->attrs['period']; - $product->period_type = (isset($_mepr_product_period_type))?sanitize_text_field($_mepr_product_period_type):$product->attrs['period_type']; - $product->signup_button_text = (isset($_mepr_product_signup_button_text))?wp_kses_post(trim($_mepr_product_signup_button_text)):$product->attrs['signup_button_text']; - $product->limit_cycles = isset($_mepr_product_limit_cycles); - $product->limit_cycles_num = (isset($_mepr_product_limit_cycles_num))?sanitize_text_field($_mepr_product_limit_cycles_num):$product->attrs['limit_cycles_num']; - $product->limit_cycles_action = (isset($_mepr_product_limit_cycles_action)?sanitize_text_field($_mepr_product_limit_cycles_action):$product->attrs['limit_cycles_action']); - $product->limit_cycles_expires_after = (isset($_mepr_product_limit_cycles_expires_after)?sanitize_text_field($_mepr_product_limit_cycles_expires_after):$product->attrs['limit_cycles_expires_after']); - $product->limit_cycles_expires_type = (isset($_mepr_product_limit_cycles_expires_type)?sanitize_text_field($_mepr_product_limit_cycles_expires_type):$product->attrs['limit_cycles_expires_type']); - $product->trial = isset($_mepr_product_trial); - $product->trial_days = (isset($_mepr_product_trial_days))?sanitize_text_field($_mepr_product_trial_days):$product->attrs['trial_days']; - - // Make sure the number of trial days is always set to at least 1 day. - if($product->trial_days <= 0) { - $product->trial_days = 1; - } - - $product->trial_amount = (isset($_mepr_product_trial_amount))?MeprUtils::format_currency_us_float(sanitize_text_field($_mepr_product_trial_amount)):$product->attrs['trial_amount']; - $product->trial_once = isset($_mepr_product_trial_once); - $product->who_can_purchase = self::get_who_can_purchase_array(); - $product->is_highlighted = isset($_mepr_product_is_highlighted); - $product->pricing_title = (isset($_mepr_product_pricing_title))?wp_kses_post(trim($_mepr_product_pricing_title)):$product->attrs['pricing_title']; - $product->pricing_show_price = isset($_mepr_product_pricing_show_price); - $product->plan_code = isset($_mepr_plan_code) ? sanitize_user($_mepr_plan_code, true) : $product->attrs['plan_code']; - $product->pricing_display = isset($_mepr_product_pricing_display) ? sanitize_text_field($_mepr_product_pricing_display) : $product->attrs['pricing_display']; +if (!defined('ABSPATH')) { + die('You are not allowed to call this page directly.'); +} + +class MeprProductsCtrl extends MeprCptCtrl +{ + public function load_hooks() + { + add_action('admin_enqueue_scripts', 'MeprProductsCtrl::enqueue_scripts'); + add_action('manage_pages_custom_column', 'MeprProductsCtrl::custom_columns', 10, 2); + add_filter('manage_edit-' . MeprProduct::$cpt . '_columns', 'MeprProductsCtrl::columns'); + add_filter('manage_edit-' . MeprProduct::$cpt . '_sortable_columns', 'MeprProductsCtrl::sortable_columns'); + add_filter('template_include', 'MeprProductsCtrl::template_include'); + add_action('save_post', 'MeprProductsCtrl::save_postdata'); + add_filter('the_content', 'MeprProductsCtrl::display_registration_form', 10); + add_action('admin_init', 'MeprProduct::cleanup_db'); + add_action('before_delete_post', 'MeprProductsCtrl::nullify_records_on_delete'); + add_filter('login_redirect', 'MeprProductsCtrl::track_and_override_login_redirect_wp', 999999, 3); + add_filter('mepr-process-login-redirect-url', 'MeprProductsCtrl::track_and_override_login_redirect_mepr', 10, 2); + + MeprHooks::add_shortcode('mepr-product-link', 'MeprProductsCtrl::shortcode_product_link'); // DEPRECATED + MeprHooks::add_shortcode('mepr-product-registration-form', 'MeprProductsCtrl::shortcode_registration_form'); // DEPRECATED + MeprHooks::add_shortcode('mepr-product-purchased', 'MeprProductsCtrl::shortcode_if_product_was_purchased'); // DEPRECATED + MeprHooks::add_shortcode('mepr-product-access-url', 'MeprProductsCtrl::shortcode_access_url_link'); // DEPRECATED + + MeprHooks::add_shortcode('mepr-membership-link', 'MeprProductsCtrl::shortcode_product_link'); + MeprHooks::add_shortcode('mepr-membership-registration-form', 'MeprProductsCtrl::shortcode_registration_form'); + MeprHooks::add_shortcode('mepr-membership-purchased', 'MeprProductsCtrl::shortcode_if_product_was_purchased'); + MeprHooks::add_shortcode('mepr-membership-access-url', 'MeprProductsCtrl::shortcode_access_url_link'); + + MeprHooks::add_shortcode('mepr-membership-price', 'MeprProductsCtrl::shortcode_price'); + + add_action('wp_ajax_mepr_get_product_price_str', 'MeprProductsCtrl::get_price_str_ajax'); + + // Cleanup list view + add_filter('views_edit-' . MeprProduct::$cpt, 'MeprAppCtrl::cleanup_list_view'); + + // Category filter + add_action('init', 'MeprProductsCtrl::register_taxonomy'); + add_action('admin_init', 'MeprProductsCtrl::register_filter_queries'); + add_action('restrict_manage_posts', 'MeprProductsCtrl::render_memberships_filters', 10, 1); + add_action('admin_footer-edit.php', 'MeprProductsCtrl::render_categories_button'); + } - $product->custom_price = isset($_mepr_product_custom_price) ? sanitize_text_field($_mepr_product_custom_price) : $product->attrs['custom_price']; + public function register_post_type() + { + $mepr_options = MeprOptions::fetch(); + $this->cpt = (object)[ + 'slug' => MeprProduct::$cpt, + 'config' => [ + 'labels' => [ + 'name' => __('Memberships', 'memberpress'), + 'singular_name' => __('Membership', 'memberpress'), + 'add_new' => __('Add New', 'memberpress'), + 'add_new_item' => __('Add New Membership', 'memberpress'), + 'edit_item' => __('Edit Membership', 'memberpress'), + 'new_item' => __('New Membership', 'memberpress'), + 'view_item' => __('View Membership', 'memberpress'), + 'search_items' => __('Search Membership', 'memberpress'), + 'not_found' => __('No Membership found', 'memberpress'), + 'not_found_in_trash' => __('No Membership found in Trash', 'memberpress'), + 'parent_item_colon' => __('Parent Membership:', 'memberpress'), + ], + 'public' => true, + 'show_ui' => true, // MeprUpdateCtrl::is_activated(), + 'show_in_menu' => 'memberpress', + 'capability_type' => 'page', + 'hierarchical' => true, + 'register_meta_box_cb' => 'MeprProductsCtrl::add_meta_boxes', + 'rewrite' => [ + 'slug' => $mepr_options->product_pages_slug, + 'with_front' => false, + ], + 'supports' => ['title', 'editor', 'page-attributes', 'comments', 'thumbnail'], + ], + ]; + register_post_type($this->cpt->slug, $this->cpt->config); + } - $product->pricing_heading_txt = (isset($_mepr_product_pricing_heading_text))?wp_kses_post($_mepr_product_pricing_heading_text):$product->attrs['pricing_heading_text']; - $product->pricing_footer_txt = (isset($_mepr_product_pricing_footer_text))?wp_kses_post($_mepr_product_pricing_footer_text):$product->attrs['pricing_footer_txt']; - $product->pricing_button_txt = (isset($_mepr_product_pricing_button_text))?wp_kses_post(trim($_mepr_product_pricing_button_text)):$product->attrs['pricing_button_txt']; - $product->pricing_button_position = (isset($_mepr_product_pricing_button_position))?sanitize_text_field($_mepr_product_pricing_button_position):$product->attrs['pricing_button_position']; - $product->pricing_benefits = (isset($_mepr_product_pricing_benefits))?array_map(function($benefit) { - return trim(sanitize_text_field($benefit)); - }, $_mepr_product_pricing_benefits):$product->attrs['pricing_benefits']; - $product->register_price_action = (isset($_mepr_register_price_action))?sanitize_text_field($_mepr_register_price_action):$product->attrs['register_price_action']; - $product->register_price = (isset($_mepr_register_price))?sanitize_text_field($_mepr_register_price):$product->attrs['register_price']; - $product->thank_you_page_enabled = isset($_mepr_thank_you_page_enabled); - $product->thank_you_message = (isset($meprproductthankyoumessage) && !empty($meprproductthankyoumessage))?wp_kses_post(wp_unslash($meprproductthankyoumessage)):$product->attrs['thank_you_message']; - $product->thank_you_page_type = (isset($_mepr_thank_you_page_type)?sanitize_text_field($_mepr_thank_you_page_type):$product->attrs['thank_you_page_type']); - $product->thank_you_page_id = (isset($_mepr_product_thank_you_page_id) && is_numeric($_mepr_product_thank_you_page_id) && (int)$_mepr_product_thank_you_page_id > 0)?(int)$_mepr_product_thank_you_page_id:$product->attrs['thank_you_page_id']; + public static function register_taxonomy() + { + register_taxonomy( + MeprProduct::$taxonomy_product_category, + MeprProduct::$cpt, + [ + 'labels' => [ + 'name' => esc_html_x('Categories', 'taxonomy general name', 'memberpress'), + 'singular_name' => esc_html_x('Category', 'taxonomy singular name', 'memberpress'), + 'search_items' => esc_html__('Search Categories', 'memberpress'), + 'all_items' => esc_html__('All Categories', 'memberpress'), + 'parent_item' => esc_html__('Parent Category', 'memberpress'), + 'parent_item_colon' => esc_html__('Parent Category:', 'memberpress'), + 'edit_item' => esc_html__('Edit Category', 'memberpress'), + 'update_item' => esc_html__('Update Category', 'memberpress'), + 'add_new_item' => esc_html__('Add New Category', 'memberpress'), + 'new_item_name' => esc_html__('New Category Name', 'memberpress'), + 'menu_name' => esc_html__('Categories', 'memberpress'), + 'separate_items_with_commas' => esc_html__('Separate Categories with commas', 'memberpress'), + 'add_or_remove_items' => esc_html__('Add or remove Categories', 'memberpress'), + 'choose_from_most_used' => esc_html__('Choose from the most used', 'memberpress'), + 'popular_items' => esc_html__('Popular Categories', 'memberpress'), + 'not_found' => esc_html__('Not Found', 'memberpress'), + 'no_terms' => esc_html__('No Categories', 'memberpress'), + 'items_list' => esc_html__('Categories list', 'memberpress'), + 'items_list_navigation' => esc_html__('Categories list navigation', 'memberpress'), + ], + 'hierarchical' => false, + 'show_ui' => true, + 'show_admin_column' => true, + 'rewrite' => false, + 'capabilities' => [ + 'manage_terms' => 'manage_options', + 'edit_terms' => 'manage_options', + 'delete_terms' => 'manage_options', + 'assign_terms' => 'manage_options', + ], + ] + ); + } - /** - * Sets thank_you_page_id to the id from the POST or Adds the new page. - */ - if($product->thank_you_page_type == 'page' && isset($_mepr_product_thank_you_page_id)) { - if(is_numeric($_mepr_product_thank_you_page_id) && (int)$_mepr_product_thank_you_page_id > 0) { - $product->thank_you_page_id = (int)$_mepr_product_thank_you_page_id; - } elseif($product->thank_you_page_enabled && preg_match("#^__auto_page:(.*?)$#", $_mepr_product_thank_you_page_id, $matches)) { - $product->thank_you_page_id = MeprAppHelper::auto_add_page($matches[1], esc_html__('Your subscription has been set up successfully.', 'memberpress')); - } else { - $product->thank_you_page_id = $product->attrs['thank_you_page_id']; - } - } + public static function columns($columns) + { + $columns = [ + 'cb' => '', + 'ID' => esc_html__('ID', 'memberpress'), + 'title' => esc_html__('Membership Title', 'memberpress'), + 'terms' => esc_html__('Terms', 'memberpress'), + 'url' => esc_html__('URL', 'memberpress'), + ]; + return MeprHooks::apply_filters('mepr-admin-memberships-columns', $columns); + } - $product->simultaneous_subscriptions = isset($_mepr_allow_simultaneous_subscriptions); - $product->use_custom_template = isset($_mepr_use_custom_template); - $product->custom_template = isset($_mepr_custom_template)?sanitize_text_field($_mepr_custom_template):$product->attrs['custom_template']; - $product->customize_payment_methods = isset($_mepr_customize_payment_methods); - $product->customize_profile_fields = isset($_mepr_customize_profile_fields); - $product->custom_profile_fields = array(); //We'll populate it below if we need to - $product->custom_payment_methods = json_decode(sanitize_text_field(wp_unslash($_POST['mepr-product-payment-methods-json']))); - $product->custom_login_urls_enabled = isset($_mepr_custom_login_urls_enabled); - $product->expire_type = isset(${MeprProduct::$expire_type_str}) ? sanitize_text_field($_POST[MeprProduct::$expire_type_str]) : $product->attrs['expire_type']; - $product->expire_after = isset(${MeprProduct::$expire_after_str}) ? sanitize_text_field($_POST[MeprProduct::$expire_after_str]) : $product->attrs['expire_after']; - $product->expire_unit = isset(${MeprProduct::$expire_unit_str}) ? sanitize_text_field($_POST[MeprProduct::$expire_unit_str]) : $product->attrs['expire_unit']; - $product->expire_fixed = isset(${MeprProduct::$expire_fixed_str}) ? sanitize_text_field($_POST[MeprProduct::$expire_fixed_str]) : $product->attrs['expire_fixed']; - $product->tax_exempt = isset($_POST[MeprProduct::$tax_exempt_str]); - $product->tax_class = isset(${MeprProduct::$tax_class_str}) ? sanitize_text_field($_POST[MeprProduct::$tax_class_str]) : $product->attrs['tax_class']; - $product->allow_renewal = (($product->period_type == 'lifetime' || $product->price == 0.00) && (($product->expire_type == 'delay' && isset($_POST[MeprProduct::$allow_renewal_str])) || ($product->expire_type == 'fixed' && isset($_POST[MeprProduct::$allow_renewal_str.'-fixed'])))); - $product->access_url = isset($_mepr_access_url)?sanitize_text_field(wp_unslash(trim($_mepr_access_url))):$product->attrs['access_url']; - $product->disable_address_fields = (isset($_mepr_disable_address_fields) && $product->price <= 0.00); - $product->cannot_purchase_message = (!empty($meprcannotpurchasemessage))?wp_kses_post(wp_unslash($meprcannotpurchasemessage)):$product->cannot_purchase_message; + public static function sortable_columns($columns) + { + $columns['ID'] = 'ID'; + return $columns; + } - // Notification Settings - $emails = array(); - foreach( $_POST[MeprProduct::$emails_str] as $email => $vals ) { - $emails[$email] = array( 'enabled' => isset( $vals['enabled'] ), - 'use_template' => isset( $vals['use_template'] ), - 'subject' => sanitize_text_field( wp_unslash( $vals['subject'] ) ), - 'body' => MeprUtils::maybe_wpautop( wp_kses_post( wp_unslash( $vals['body'] ) ) ) ); - } - $product->emails = $emails; + public static function custom_columns($column, $post_id) + { + $mepr_options = MeprOptions::fetch(); + $product = new MeprProduct($post_id); + + if ($product->ID !== null) { + if ('ID' == $column) { + echo $product->ID; + } elseif ('terms' == $column) { + echo MeprProductsHelper::format_currency($product, true, null, false); // $product, $show_symbol, $coupon_code, $show_prorated + } elseif ('url' == $column) { + echo $product->url(); + } + } + } - if($product->custom_login_urls_enabled) - $product = self::set_custom_login_urls($product); + // Template selection + public static function template_include($template) + { + global $post, $wp_query; - //Setup the custom profile fields - if($product->customize_profile_fields && isset($_POST['product-profile-fields'])) { - $slugs = array(); + if (!is_singular()) { + return $template; + } - foreach($_POST['product-profile-fields'] as $key => $value) - $slugs[] = sanitize_title_with_dashes($key); + if (isset($post) && is_a($post, 'WP_Post') && $post->post_type == MeprProduct::$cpt) { + $product = new MeprProduct($post->ID); + $new_template = $product->get_page_template(); + } - $product->custom_profile_fields = $slugs; - } + if (isset($new_template) && !empty($new_template)) { + return $new_template; + } - $product = self::validate_product($product); - $product->store_meta(); // only storing metadata here + return $template; + } - //Some themes rely on this meta key to be set to use the custom template, and they don't use locate_template - if($product->use_custom_template && !empty($product->custom_template)) - update_post_meta($product->ID, '_wp_page_template', $product->custom_template); - else - update_post_meta($product->ID, '_wp_page_template', ''); + public static function add_meta_boxes() + { + global $post_id; - MeprHooks::do_action('mepr-product-save-meta', $product); // DEPRECATED - MeprHooks::do_action('mepr-membership-save-meta', $product); - } - } + $product = new MeprProduct($post_id); - public static function set_custom_login_urls($product) { - extract($_POST, EXTR_SKIP); + add_meta_box('memberpress-product-meta', __('Membership Terms', 'memberpress'), 'MeprProductsCtrl::product_meta_box', MeprProduct::$cpt, 'side', 'high', ['product' => $product]); - $custom_login_urls = array(); + add_meta_box('memberpress-custom-template', __('Custom Page Template', 'memberpress'), 'MeprProductsCtrl::custom_page_template', MeprProduct::$cpt, 'side', 'default', ['product' => $product]); - $product->custom_login_urls_default = (isset($_mepr_custom_login_urls_default) && !empty($_mepr_custom_login_urls_default))?stripslashes(trim($_mepr_custom_login_urls_default)):''; + add_meta_box('memberpress-product-options', __('Membership Options', 'memberpress'), 'MeprProductsCtrl::product_options_meta_box', MeprProduct::$cpt, 'normal', 'high', ['product' => $product]); - if(isset($_mepr_custom_login_urls) && !empty($_mepr_custom_login_urls)) { - foreach($_mepr_custom_login_urls as $i => $url) { - if(!empty($url)) { - $custom_login_urls[] = (object)array('url' => stripslashes(trim($url)), 'count' => (int)$_mepr_custom_login_urls_count[$i]); - } - } + MeprHooks::do_action('mepr-product-meta-boxes', $product); // DEPRECATED + MeprHooks::do_action('mepr-membership-meta-boxes', $product); } - $product->custom_login_urls = $custom_login_urls; + public static function save_postdata($post_id) + { + $post = get_post($post_id); + + if (!wp_verify_nonce((isset($_POST[MeprProduct::$nonce_str])) ? $_POST[MeprProduct::$nonce_str] : '', MeprProduct::$nonce_str . wp_salt())) { + return $post_id; // Nonce prevents meta data from being wiped on move to trash + } - return $product; - } + if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) { + return $post_id; + } - public static function get_who_can_purchase_array() { - $rows = array(); + if (defined('DOING_AJAX')) { + return; + } - if(empty($_POST[MeprProduct::$who_can_purchase_str.'-user_type'])) { - return $rows; + if (!empty($post) && $post->post_type == MeprProduct::$cpt) { + $product = new MeprProduct($post_id); + + extract($_POST, EXTR_SKIP); + + $product->price = (isset($_mepr_product_price)) ? MeprUtils::format_currency_us_float(sanitize_text_field($_mepr_product_price)) : $product->attrs['price']; + $product->period = (isset($_mepr_product_period)) ? sanitize_text_field($_mepr_product_period) : $product->attrs['period']; + $product->period_type = (isset($_mepr_product_period_type)) ? sanitize_text_field($_mepr_product_period_type) : $product->attrs['period_type']; + $product->signup_button_text = (isset($_mepr_product_signup_button_text)) ? wp_kses_post(trim($_mepr_product_signup_button_text)) : $product->attrs['signup_button_text']; + $product->limit_cycles = isset($_mepr_product_limit_cycles); + $product->limit_cycles_num = (isset($_mepr_product_limit_cycles_num)) ? sanitize_text_field($_mepr_product_limit_cycles_num) : $product->attrs['limit_cycles_num']; + $product->limit_cycles_action = (isset($_mepr_product_limit_cycles_action) ? sanitize_text_field($_mepr_product_limit_cycles_action) : $product->attrs['limit_cycles_action']); + $product->limit_cycles_expires_after = (isset($_mepr_product_limit_cycles_expires_after) ? sanitize_text_field($_mepr_product_limit_cycles_expires_after) : $product->attrs['limit_cycles_expires_after']); + $product->limit_cycles_expires_type = (isset($_mepr_product_limit_cycles_expires_type) ? sanitize_text_field($_mepr_product_limit_cycles_expires_type) : $product->attrs['limit_cycles_expires_type']); + $product->trial = isset($_mepr_product_trial); + $product->trial_days = (isset($_mepr_product_trial_days)) ? sanitize_text_field($_mepr_product_trial_days) : $product->attrs['trial_days']; + + // Make sure the number of trial days is always set to at least 1 day. + if ($product->trial_days <= 0) { + $product->trial_days = 1; + } + + $product->trial_amount = (isset($_mepr_product_trial_amount)) ? MeprUtils::format_currency_us_float(sanitize_text_field($_mepr_product_trial_amount)) : $product->attrs['trial_amount']; + $product->trial_once = isset($_mepr_product_trial_once); + $product->who_can_purchase = self::get_who_can_purchase_array(); + $product->is_highlighted = isset($_mepr_product_is_highlighted); + $product->pricing_title = (isset($_mepr_product_pricing_title)) ? wp_kses_post(trim($_mepr_product_pricing_title)) : $product->attrs['pricing_title']; + $product->pricing_show_price = isset($_mepr_product_pricing_show_price); + $product->plan_code = isset($_mepr_plan_code) ? sanitize_user($_mepr_plan_code, true) : $product->attrs['plan_code']; + + $product->pricing_display = isset($_mepr_product_pricing_display) ? sanitize_text_field($_mepr_product_pricing_display) : $product->attrs['pricing_display']; + + $product->custom_price = isset($_mepr_product_custom_price) ? sanitize_text_field($_mepr_product_custom_price) : $product->attrs['custom_price']; + + $product->pricing_heading_txt = (isset($_mepr_product_pricing_heading_text)) ? wp_kses_post($_mepr_product_pricing_heading_text) : $product->attrs['pricing_heading_text']; + $product->pricing_footer_txt = (isset($_mepr_product_pricing_footer_text)) ? wp_kses_post($_mepr_product_pricing_footer_text) : $product->attrs['pricing_footer_txt']; + $product->pricing_button_txt = (isset($_mepr_product_pricing_button_text)) ? wp_kses_post(trim($_mepr_product_pricing_button_text)) : $product->attrs['pricing_button_txt']; + $product->pricing_button_position = (isset($_mepr_product_pricing_button_position)) ? sanitize_text_field($_mepr_product_pricing_button_position) : $product->attrs['pricing_button_position']; + $product->pricing_benefits = (isset($_mepr_product_pricing_benefits)) ? array_map(function ($benefit) { + return trim(sanitize_text_field($benefit)); + }, $_mepr_product_pricing_benefits) : $product->attrs['pricing_benefits']; + $product->register_price_action = (isset($_mepr_register_price_action)) ? sanitize_text_field($_mepr_register_price_action) : $product->attrs['register_price_action']; + $product->register_price = (isset($_mepr_register_price)) ? sanitize_text_field($_mepr_register_price) : $product->attrs['register_price']; + $product->thank_you_page_enabled = isset($_mepr_thank_you_page_enabled); + $product->thank_you_message = (isset($meprproductthankyoumessage) && !empty($meprproductthankyoumessage)) ? wp_kses_post(wp_unslash($meprproductthankyoumessage)) : $product->attrs['thank_you_message']; + $product->thank_you_page_type = (isset($_mepr_thank_you_page_type) ? sanitize_text_field($_mepr_thank_you_page_type) : $product->attrs['thank_you_page_type']); + $product->thank_you_page_id = (isset($_mepr_product_thank_you_page_id) && is_numeric($_mepr_product_thank_you_page_id) && (int)$_mepr_product_thank_you_page_id > 0) ? (int)$_mepr_product_thank_you_page_id : $product->attrs['thank_you_page_id']; + + /** + * Sets thank_you_page_id to the id from the POST or Adds the new page. + */ + if ($product->thank_you_page_type == 'page' && isset($_mepr_product_thank_you_page_id)) { + if (is_numeric($_mepr_product_thank_you_page_id) && (int)$_mepr_product_thank_you_page_id > 0) { + $product->thank_you_page_id = (int)$_mepr_product_thank_you_page_id; + } elseif ($product->thank_you_page_enabled && preg_match('#^__auto_page:(.*?)$#', $_mepr_product_thank_you_page_id, $matches)) { + $product->thank_you_page_id = MeprAppHelper::auto_add_page($matches[1], esc_html__('Your subscription has been set up successfully.', 'memberpress')); + } else { + $product->thank_you_page_id = $product->attrs['thank_you_page_id']; + } + } + + $product->simultaneous_subscriptions = isset($_mepr_allow_simultaneous_subscriptions); + $product->use_custom_template = isset($_mepr_use_custom_template); + $product->custom_template = isset($_mepr_custom_template) ? sanitize_text_field($_mepr_custom_template) : $product->attrs['custom_template']; + $product->customize_payment_methods = isset($_mepr_customize_payment_methods); + $product->customize_profile_fields = isset($_mepr_customize_profile_fields); + $product->custom_profile_fields = []; // We'll populate it below if we need to + $product->custom_payment_methods = json_decode(sanitize_text_field(wp_unslash($_POST['mepr-product-payment-methods-json']))); + $product->custom_login_urls_enabled = isset($_mepr_custom_login_urls_enabled); + $product->expire_type = isset(${MeprProduct::$expire_type_str}) ? sanitize_text_field($_POST[MeprProduct::$expire_type_str]) : $product->attrs['expire_type']; + $product->expire_after = isset(${MeprProduct::$expire_after_str}) ? sanitize_text_field($_POST[MeprProduct::$expire_after_str]) : $product->attrs['expire_after']; + $product->expire_unit = isset(${MeprProduct::$expire_unit_str}) ? sanitize_text_field($_POST[MeprProduct::$expire_unit_str]) : $product->attrs['expire_unit']; + $product->expire_fixed = isset(${MeprProduct::$expire_fixed_str}) ? sanitize_text_field($_POST[MeprProduct::$expire_fixed_str]) : $product->attrs['expire_fixed']; + $product->tax_exempt = isset($_POST[MeprProduct::$tax_exempt_str]); + $product->tax_class = isset(${MeprProduct::$tax_class_str}) ? sanitize_text_field($_POST[MeprProduct::$tax_class_str]) : $product->attrs['tax_class']; + $product->allow_renewal = (($product->period_type == 'lifetime' || $product->price == 0.00) && (($product->expire_type == 'delay' && isset($_POST[MeprProduct::$allow_renewal_str])) || ($product->expire_type == 'fixed' && isset($_POST[MeprProduct::$allow_renewal_str . '-fixed'])))); + $product->access_url = isset($_mepr_access_url) ? sanitize_text_field(wp_unslash(trim($_mepr_access_url))) : $product->attrs['access_url']; + $product->disable_address_fields = (isset($_mepr_disable_address_fields) && $product->price <= 0.00); + $product->cannot_purchase_message = (!empty($meprcannotpurchasemessage)) ? wp_kses_post(wp_unslash($meprcannotpurchasemessage)) : $product->cannot_purchase_message; + + // Notification Settings + $emails = []; + foreach ($_POST[MeprProduct::$emails_str] as $email => $vals) { + $emails[$email] = [ + 'enabled' => isset($vals['enabled']), + 'use_template' => isset($vals['use_template']), + 'subject' => sanitize_text_field(wp_unslash($vals['subject'])), + 'body' => MeprUtils::maybe_wpautop(wp_kses_post(wp_unslash($vals['body']))), + ]; + } + $product->emails = $emails; + + if ($product->custom_login_urls_enabled) { + $product = self::set_custom_login_urls($product); + } + + // Setup the custom profile fields + if ($product->customize_profile_fields && isset($_POST['product-profile-fields'])) { + $slugs = []; + + foreach ($_POST['product-profile-fields'] as $key => $value) { + $slugs[] = sanitize_title_with_dashes($key); + } + + $product->custom_profile_fields = $slugs; + } + + $product = self::validate_product($product); + $product->store_meta(); // only storing metadata here + + // Some themes rely on this meta key to be set to use the custom template, and they don't use locate_template + if ($product->use_custom_template && !empty($product->custom_template)) { + update_post_meta($product->ID, '_wp_page_template', $product->custom_template); + } else { + update_post_meta($product->ID, '_wp_page_template', ''); + } + + MeprHooks::do_action('mepr-product-save-meta', $product); // DEPRECATED + MeprHooks::do_action('mepr-membership-save-meta', $product); + } } - $count = count($_POST[MeprProduct::$who_can_purchase_str.'-user_type']) - 1; + public static function set_custom_login_urls($product) + { + extract($_POST, EXTR_SKIP); - for($i = 0; $i < $count; $i++) { - $user_type = sanitize_text_field($_POST[MeprProduct::$who_can_purchase_str.'-user_type'][$i]); - $product_id = sanitize_text_field($_POST[MeprProduct::$who_can_purchase_str.'-product_id'][$i]); - $purchase_type = sanitize_text_field($_POST[MeprProduct::$have_or_had_str.'-type'][$i]); - $rows[] = (object)array( - 'user_type' => $user_type, - 'product_id' => $product_id, - 'purchase_type' => $purchase_type, - ); - } + $custom_login_urls = []; - return $rows; - } + $product->custom_login_urls_default = (isset($_mepr_custom_login_urls_default) && !empty($_mepr_custom_login_urls_default)) ? stripslashes(trim($_mepr_custom_login_urls_default)) : ''; - public static function validate_product($product) { - //Validate Periods - if($product->period_type == 'weeks' && $product->period > 52) { - $product->period = 52; - } + if (isset($_mepr_custom_login_urls) && !empty($_mepr_custom_login_urls)) { + foreach ($_mepr_custom_login_urls as $i => $url) { + if (!empty($url)) { + $custom_login_urls[] = (object)[ + 'url' => stripslashes(trim($url)), + 'count' => (int)$_mepr_custom_login_urls_count[$i], + ]; + } + } + } - if($product->period_type == 'months' && $product->period > 12) { - $product->period = 12; - } + $product->custom_login_urls = $custom_login_urls; - if(!is_numeric($product->period) || $product->period <= 0 || empty($product->period)) { - $product->period = 1; + return $product; } - if(!is_numeric($product->trial_days) || $product->trial_days <= 0 || empty($product->trial_days)) { - $product->trial_days = 0; - } + public static function get_who_can_purchase_array() + { + $rows = []; - if($product->trial_days > 365) { - $product->trial_days = 365; - } + if (empty($_POST[MeprProduct::$who_can_purchase_str . '-user_type'])) { + return $rows; + } - //Validate Prices - // preg_match replaces !is_numeric() to allow comma, period & space before applying (float) - if(preg_match("/[^0-9., ]/", $product->price) || $product->price < 0.00) { - $product->price = 0.00; - } + $count = count($_POST[MeprProduct::$who_can_purchase_str . '-user_type']) - 1; + + for ($i = 0; $i < $count; $i++) { + $user_type = sanitize_text_field($_POST[MeprProduct::$who_can_purchase_str . '-user_type'][$i]); + $product_id = sanitize_text_field($_POST[MeprProduct::$who_can_purchase_str . '-product_id'][$i]); + $purchase_type = sanitize_text_field($_POST[MeprProduct::$have_or_had_str . '-type'][$i]); + $rows[] = (object)[ + 'user_type' => $user_type, + 'product_id' => $product_id, + 'purchase_type' => $purchase_type, + ]; + } - if(!is_numeric($product->trial_amount) || $product->trial_amount < 0.00) { - $product->trial_amount = 0.00; + return $rows; } - //Disable trial && cycles limit if lifetime is set and set period to 1 - if($product->period_type == 'lifetime') { - $product->limit_cycles = false; - $product->trial = false; - $product->period = 1; - } + public static function validate_product($product) + { + // Validate Periods + if ($product->period_type == 'weeks' && $product->period > 52) { + $product->period = 52; + } - //Cycles limit must be positive - if(empty($product->limit_cycles_num) || !is_numeric($product->limit_cycles_num) || $product->limit_cycles_num <= 0) { - $product->limit_cycles_num = 2; - } + if ($product->period_type == 'months' && $product->period > 12) { + $product->period = 12; + } - //If price = 0.00 and period type is not lifetime, we need to disable cycles and trials - if($product->price == 0.00 && $product->period_type != 'lifetime') { - $product->limit_cycles = false; - $product->trial = false; - } + if (!is_numeric($product->period) || $product->period <= 0 || empty($product->period)) { + $product->period = 1; + } - //Handle delayed expirations on one-time payments - if($product->period_type == 'lifetime' && $product->expire_type == 'delay') { - if(!is_numeric($product->expire_after) || $product->expire_after < 0) { - $product->expire_after = 1; - } + if (!is_numeric($product->trial_days) || $product->trial_days <= 0 || empty($product->trial_days)) { + $product->trial_days = 0; + } - if(!in_array($product->expire_unit, array('days', 'weeks', 'months', 'years'))) { - $product->expire_unit = 'days'; - } - } + if ($product->trial_days > 365) { + $product->trial_days = 365; + } - //Handle fixed expirations on one-time payments - if($product->period_type == 'lifetime' && $product->expire_type == 'fixed') { - if(preg_match('/^(\d{4})-(\d{2})-(\d{2})$/', $product->expire_fixed, $datebit)) { - if(!checkdate($datebit[2] , $datebit[3] , $datebit[1])) { - $product->expire_type = 'none'; //an invalid date was set, so let's just make this a lifetime + // Validate Prices + // preg_match replaces !is_numeric() to allow comma, period & space before applying (float) + if (preg_match('/[^0-9., ]/', $product->price) || $product->price < 0.00) { + $product->price = 0.00; } - } else { - $product->expire_type = 'none'; //an invalid date was set, so let's just make this a lifetime - } - } - // Make sure there's at least one payment method selected when customizing payment methods. - if($product->customize_payment_methods && count($product->custom_payment_methods) <= 0) { - $product->customize_payment_methods = false; - } + if (!is_numeric($product->trial_amount) || $product->trial_amount < 0.00) { + $product->trial_amount = 0.00; + } - return $product; - } + // Disable trial && cycles limit if lifetime is set and set period to 1 + if ($product->period_type == 'lifetime') { + $product->limit_cycles = false; + $product->trial = false; + $product->period = 1; + } - /** - * Displays product terms for for meta boxe - * Returns terms from gateway or admin product terms form - * Don't use $post here, it is null on new membership - use args instead - */ - public static function product_meta_box($post, $args) { - $product = $args['args']['product']; - $mepr_options = MeprOptions::fetch(); - $gateway_ids = array_keys($mepr_options->payment_methods()); - foreach ($gateway_ids as $gateway_id) { - $gateway = $mepr_options->payment_method($gateway_id); - if($gateway instanceof MeprBaseExclusiveRecurringGateway) { - // Return terms from exclusive gateway - return $gateway->display_plans_terms($product); - } - } + // Cycles limit must be positive + if (empty($product->limit_cycles_num) || !is_numeric($product->limit_cycles_num) || $product->limit_cycles_num <= 0) { + $product->limit_cycles_num = 2; + } - // Render product terms form - MeprView::render('/admin/products/form', get_defined_vars()); - } + // If price = 0.00 and period type is not lifetime, we need to disable cycles and trials + if ($product->price == 0.00 && $product->period_type != 'lifetime') { + $product->limit_cycles = false; + $product->trial = false; + } - //Don't use $post here, it is null on new membership - use args instead - public static function product_options_meta_box($post, $args) { - $mepr_options = MeprOptions::fetch(); - $product = $args['args']['product']; + // Handle delayed expirations on one-time payments + if ($product->period_type == 'lifetime' && $product->expire_type == 'delay') { + if (!is_numeric($product->expire_after) || $product->expire_after < 0) { + $product->expire_after = 1; + } - MeprView::render('/admin/products/product_options_meta_box', get_defined_vars()); - } + if (!in_array($product->expire_unit, ['days', 'weeks', 'months', 'years'])) { + $product->expire_unit = 'days'; + } + } - //Don't use $post here, it is null on new membership - use args instead - public static function custom_page_template($post, $args) { - $product = $args['args']['product']; + // Handle fixed expirations on one-time payments + if ($product->period_type == 'lifetime' && $product->expire_type == 'fixed') { + if (preg_match('/^(\d{4})-(\d{2})-(\d{2})$/', $product->expire_fixed, $datebit)) { + if (!checkdate($datebit[2], $datebit[3], $datebit[1])) { + $product->expire_type = 'none'; // an invalid date was set, so let's just make this a lifetime + } + } else { + $product->expire_type = 'none'; // an invalid date was set, so let's just make this a lifetime + } + } - MeprView::render('/admin/products/custom_page_template_form', get_defined_vars()); - } + // Make sure there's at least one payment method selected when customizing payment methods. + if ($product->customize_payment_methods && count($product->custom_payment_methods) <= 0) { + $product->customize_payment_methods = false; + } - public static function display_registration_form($content, $manual = false) { - global $user_ID; - $mepr_options = MeprOptions::fetch(); - $current_post = MeprUtils::get_current_post(); + return $product; + } - //This isn't a post? Just return the content then - if($current_post === false) { return $content; } + /** + * Displays product terms for for meta boxe + * Returns terms from gateway or admin product terms form + * Don't use $post here, it is null on new membership - use args instead + */ + public static function product_meta_box($post, $args) + { + $product = $args['args']['product']; + $mepr_options = MeprOptions::fetch(); + $gateway_ids = array_keys($mepr_options->payment_methods()); + foreach ($gateway_ids as $gateway_id) { + $gateway = $mepr_options->payment_method($gateway_id); + if ($gateway instanceof MeprBaseExclusiveRecurringGateway) { + // Return terms from exclusive gateway + return $gateway->display_plans_terms($product); + } + } - //WARNING the_content CAN be run more than once per page load - //so this static var prevents stuff from happening twice - //like cancelling a subscr or resuming etc... - static $already_run = array(); - static $new_content = array(); - static $content_length = array(); + // Render product terms form + MeprView::render('/admin/products/form', get_defined_vars()); + } - //Init this posts static values - if(!isset($new_content[$current_post->ID]) || empty($new_content[$current_post->ID])) { - $already_run[$current_post->ID] = false; - $new_content[$current_post->ID] = ''; - $content_length[$current_post->ID] = -1; + // Don't use $post here, it is null on new membership - use args instead + public static function product_options_meta_box($post, $args) + { + $mepr_options = MeprOptions::fetch(); + $product = $args['args']['product']; + + MeprView::render('/admin/products/product_options_meta_box', get_defined_vars()); } - if($already_run[$current_post->ID] && strlen($content) == $content_length[$current_post->ID] && !$manual) { //shortcode may pass - return $new_content[$current_post->ID]; + // Don't use $post here, it is null on new membership - use args instead + public static function custom_page_template($post, $args) + { + $product = $args['args']['product']; + + MeprView::render('/admin/products/custom_page_template_form', get_defined_vars()); } - $content_length[$current_post->ID] = strlen($content); - $already_run[$current_post->ID] = true; + public static function display_registration_form($content, $manual = false) + { + global $user_ID; + $mepr_options = MeprOptions::fetch(); + $current_post = MeprUtils::get_current_post(); - if(isset($current_post) && is_a($current_post, 'WP_Post') && $current_post->post_type == MeprProduct::$cpt) { - if(post_password_required($current_post)) { - //See notes above - $new_content[$current_post->ID] = $content; - return $new_content[$current_post->ID]; - } + // This isn't a post? Just return the content then + if ($current_post === false) { + return $content; + } - $prd = new MeprProduct($current_post->ID); + // WARNING the_content CAN be run more than once per page load + // so this static var prevents stuff from happening twice + // like cancelling a subscr or resuming etc... + static $already_run = []; + static $new_content = []; + static $content_length = []; + + // Init this posts static values + if (!isset($new_content[$current_post->ID]) || empty($new_content[$current_post->ID])) { + $already_run[$current_post->ID] = false; + $new_content[$current_post->ID] = ''; + $content_length[$current_post->ID] = -1; + } - //Short circuiting for any of the following reasons - if( $prd->ID === null || //Bad membership for some reason - (!$manual && $prd->manual_append_signup()) || //the_content filter and show manually is enabled - ($manual && !$prd->manual_append_signup()) ) //do_shortcode and show manually is disabled - { - //See notes above - $new_content[$current_post->ID] = $content; - return $new_content[$current_post->ID]; - } - - // We want to render this form after processing the signup form unless - // there were errors and when trying to process the paymet form - if(isset($_REQUEST) and - ((isset($_POST['mepr_process_signup_form']) and !isset($_POST['errors'])) or - isset($_POST['mepr_process_payment_form']) or - (isset($_GET['action']) and $_GET['action']==='checkout' and isset($_GET['txn'])))) - { - ob_start(); - try { - $checkout_ctrl = MeprCtrlFactory::fetch('checkout'); - $checkout_ctrl->display_payment_form(); + if ($already_run[$current_post->ID] && strlen($content) == $content_length[$current_post->ID] && !$manual) { // shortcode may pass + return $new_content[$current_post->ID]; } - catch(Exception $e) { - ?> + + $content_length[$current_post->ID] = strlen($content); + $already_run[$current_post->ID] = true; + + if (isset($current_post) && is_a($current_post, 'WP_Post') && $current_post->post_type == MeprProduct::$cpt) { + if (post_password_required($current_post)) { + // See notes above + $new_content[$current_post->ID] = $content; + return $new_content[$current_post->ID]; + } + + $prd = new MeprProduct($current_post->ID); + + // Short circuiting for any of the following reasons + if ( + $prd->ID === null || // Bad membership for some reason + (!$manual && $prd->manual_append_signup()) || // the_content filter and show manually is enabled + ($manual && !$prd->manual_append_signup()) + ) { // do_shortcode and show manually is disabled + // See notes above + $new_content[$current_post->ID] = $content; + return $new_content[$current_post->ID]; + } + + // We want to render this form after processing the signup form unless + // there were errors and when trying to process the paymet form + if ( + isset($_REQUEST) and + ((isset($_POST['mepr_process_signup_form']) and !isset($_POST['errors'])) or + isset($_POST['mepr_process_payment_form']) or + (isset($_GET['action']) and $_GET['action'] === 'checkout' and isset($_GET['txn']))) + ) { + ob_start(); + try { + $checkout_ctrl = MeprCtrlFactory::fetch('checkout'); + $checkout_ctrl->display_payment_form(); + } catch (Exception $e) { + ?>
    - ID] = ob_get_clean(); + return $new_content[$current_post->ID]; + } + + $res = self::get_registration_form($prd); + if ($res->enabled) { + $content .= $res->content; + } else { + $content = $res->content; + } } - //See notes above - $new_content[$current_post->ID] = ob_get_clean(); + // See notes above + $new_content[$current_post->ID] = $content; return $new_content[$current_post->ID]; - } - - $res = self::get_registration_form($prd); - if($res->enabled) { - $content .= $res->content; - } - else { - $content = $res->content; - } - } - - //See notes above - $new_content[$current_post->ID] = $content; - return $new_content[$current_post->ID]; - } - - public static function get_registration_form($prd) { - global $user_ID; - $mepr_options = MeprOptions::fetch(); - - $product_access_str = ''; - if($user_ID && !$prd->simultaneous_subscriptions && !empty($prd->access_url) && - ($user = new MeprUser($user_ID)) && $user->is_already_subscribed_to($prd->ID)) { - $product_access_str = MeprHooks::apply_filters('mepr_product_access_string', sprintf( - __('%1$sYou have already subscribed to this item. %2$sClick here to access it%3$s', 'memberpress'), - '
    ', - '', - '
    ' - ), $prd); - } - - ob_start(); - //If the user can't purchase this let's show a message - if(!$prd->can_you_buy_me()) { - $enabled = false; - if(!empty($product_access_str)) { - $cant_purchase_str = $product_access_str; - } - else { - $cant_purchase_str = wpautop(do_shortcode($prd->cannot_purchase_message)); - } - - $cant_purchase_str = MeprHooks::apply_filters('mepr-product-cant-purchase-string', $cant_purchase_str, $prd); // DEPRECATED - echo MeprHooks::apply_filters('mepr-membership-cant-purchase-string', $cant_purchase_str, $prd); - } - else if(isset($_GET['pmt']) && + } + + public static function get_registration_form($prd) + { + global $user_ID; + $mepr_options = MeprOptions::fetch(); + + $product_access_str = ''; + if ( + $user_ID && !$prd->simultaneous_subscriptions && !empty($prd->access_url) && + ($user = new MeprUser($user_ID)) && $user->is_already_subscribed_to($prd->ID) + ) { + $product_access_str = MeprHooks::apply_filters('mepr_product_access_string', sprintf( + __('%1$sYou have already subscribed to this item. %2$sClick here to access it%3$s', 'memberpress'), + '
    ', + '', + '
    ' + ), $prd); + } + + ob_start(); + // If the user can't purchase this let's show a message + if (!$prd->can_you_buy_me()) { + $enabled = false; + if (!empty($product_access_str)) { + $cant_purchase_str = $product_access_str; + } else { + $cant_purchase_str = wpautop(do_shortcode($prd->cannot_purchase_message)); + } + + $cant_purchase_str = MeprHooks::apply_filters('mepr-product-cant-purchase-string', $cant_purchase_str, $prd); // DEPRECATED + echo MeprHooks::apply_filters('mepr-membership-cant-purchase-string', $cant_purchase_str, $prd); + } elseif ( + isset($_GET['pmt']) && isset($_GET['action']) && ($pm = $mepr_options->payment_method($_GET['pmt'])) && - ($msgp = $pm->message_page($_GET['action']))) { - $enabled = false; - call_user_func(array($pm, $msgp)); - } - else { - $enabled = true; - try { - $checkout_ctrl = MeprCtrlFactory::fetch('checkout'); - echo $product_access_str; - $checkout_ctrl->display_signup_form($prd); - } - catch(Exception $e) { - ?> + ($msgp = $pm->message_page($_GET['action'])) + ) { + $enabled = false; + call_user_func([$pm, $msgp]); + } else { + $enabled = true; + try { + $checkout_ctrl = MeprCtrlFactory::fetch('checkout'); + echo $product_access_str; + $checkout_ctrl->display_signup_form($prd); + } catch (Exception $e) { + ?>
    - post_type == MeprProduct::$cpt) { - $wp_scripts = new WP_Scripts(); - $ui = $wp_scripts->query('jquery-ui-core'); - $url = "//ajax.googleapis.com/ajax/libs/jqueryui/{$ui->ver}/themes/smoothness/jquery-ui.css"; + public static function enqueue_scripts($hook) + { + global $current_screen; + + if ($current_screen->post_type == MeprProduct::$cpt) { + $wp_scripts = new WP_Scripts(); + $ui = $wp_scripts->query('jquery-ui-core'); + $url = "//ajax.googleapis.com/ajax/libs/jqueryui/{$ui->ver}/themes/smoothness/jquery-ui.css"; + + wp_register_style('mepr-jquery-ui-smoothness', $url); + wp_register_style('jquery-ui-timepicker-addon', MEPR_CSS_URL . '/jquery-ui-timepicker-addon.css', ['mepr-jquery-ui-smoothness']); + wp_enqueue_style('mepr-transactions-css', MEPR_CSS_URL . '/admin-transactions.css', ['jquery-ui-timepicker-addon'], MEPR_VERSION); + wp_enqueue_style('mepr-emails-css', MEPR_CSS_URL . '/admin-emails.css', [], MEPR_VERSION); + wp_enqueue_style('mepr-products-css', MEPR_CSS_URL . '/admin-products.css', ['mepr-emails-css','mepr-settings-table-css','jquery-ui-timepicker-addon'], MEPR_VERSION); + + wp_dequeue_script('autosave'); // Disable auto-saving + + wp_register_script('mepr-timepicker-js', MEPR_JS_URL . '/jquery-ui-timepicker-addon.js', ['jquery-ui-datepicker']); + wp_register_script('mepr-date-picker-js', MEPR_JS_URL . '/date_picker.js', ['mepr-timepicker-js'], MEPR_VERSION); + wp_enqueue_script('mepr-products-js', MEPR_JS_URL . '/admin_products.js', ['jquery-ui-spinner','mepr-date-picker-js','jquery-ui-sortable','mepr-settings-table-js','mepr-admin-shared-js'], MEPR_VERSION); + $email_locals = [ + 'set_email_defaults_nonce' => wp_create_nonce('set_email_defaults'), + 'send_test_email_nonce' => wp_create_nonce('send_test_email'), + ]; + wp_enqueue_script('mepr-emails-js', MEPR_JS_URL . '/admin_emails.js', ['mepr-products-js'], MEPR_VERSION); + wp_localize_script('mepr-emails-js', 'MeprEmail', $email_locals); + + // We need to hide the timepicker stuff here + $date_picker_frontend = [ + 'timeFormat' => '', + 'showTime' => false, + ]; + wp_localize_script('mepr-date-picker-js', 'MeprDatePicker', $date_picker_frontend); + + $options = [ + 'removeBenefitStr' => __('Remove Benefit', 'memberpress'), + 'register_price_action_id' => '#' . MeprProduct::$register_price_action_str, + 'register_price_id' => '#' . MeprProduct::$register_price_str, + 'wpnonce' => wp_create_nonce(MEPR_PLUGIN_SLUG), + ]; + wp_localize_script('mepr-products-js', 'MeprProducts', $options); + + MeprHooks::do_action('mepr-product-admin-enqueue-script', $hook); // DEPRECATED + MeprHooks::do_action('mepr-membership-admin-enqueue-script', $hook); + } + } - wp_register_style('mepr-jquery-ui-smoothness', $url); - wp_register_style('jquery-ui-timepicker-addon', MEPR_CSS_URL.'/jquery-ui-timepicker-addon.css', array('mepr-jquery-ui-smoothness')); - wp_enqueue_style('mepr-transactions-css', MEPR_CSS_URL.'/admin-transactions.css', array('jquery-ui-timepicker-addon'), MEPR_VERSION); - wp_enqueue_style('mepr-emails-css', MEPR_CSS_URL.'/admin-emails.css', array(), MEPR_VERSION); - wp_enqueue_style('mepr-products-css', MEPR_CSS_URL.'/admin-products.css', array('mepr-emails-css','mepr-settings-table-css','jquery-ui-timepicker-addon'), MEPR_VERSION); + public static function nullify_records_on_delete($id) + { + MeprTransaction::nullify_product_id_on_delete($id); + MeprSubscription::nullify_product_id_on_delete($id); - wp_dequeue_script('autosave'); //Disable auto-saving + return $id; + } - wp_register_script('mepr-timepicker-js', MEPR_JS_URL.'/jquery-ui-timepicker-addon.js', array('jquery-ui-datepicker')); - wp_register_script('mepr-date-picker-js', MEPR_JS_URL.'/date_picker.js', array('mepr-timepicker-js'), MEPR_VERSION); - wp_enqueue_script('mepr-products-js', MEPR_JS_URL.'/admin_products.js', array('jquery-ui-spinner','mepr-date-picker-js','jquery-ui-sortable','mepr-settings-table-js','mepr-admin-shared-js'), MEPR_VERSION); - $email_locals = array( - 'set_email_defaults_nonce' => wp_create_nonce('set_email_defaults'), - 'send_test_email_nonce' => wp_create_nonce('send_test_email'), - ); - wp_enqueue_script('mepr-emails-js', MEPR_JS_URL.'/admin_emails.js', array('mepr-products-js'), MEPR_VERSION); - wp_localize_script('mepr-emails-js', 'MeprEmail', $email_locals); + public static function shortcode_product_link($atts, $content = '') + { + if (!isset($atts['id']) || !is_numeric($atts['id'])) { + return $content; + } - //We need to hide the timepicker stuff here - $date_picker_frontend = array('timeFormat' => '', 'showTime' => false); - wp_localize_script('mepr-date-picker-js', 'MeprDatePicker', $date_picker_frontend); + $product = new MeprProduct($atts['id']); - $options = array( 'removeBenefitStr' => __('Remove Benefit', 'memberpress'), - 'register_price_action_id' => '#'.MeprProduct::$register_price_action_str, - 'register_price_id' => '#'.MeprProduct::$register_price_str, - 'wpnonce' => wp_create_nonce( MEPR_PLUGIN_SLUG ) ); - wp_localize_script('mepr-products-js', 'MeprProducts', $options); + if ($product->ID === null) { + return $content; + } - MeprHooks::do_action('mepr-product-admin-enqueue-script', $hook); // DEPRECATED - MeprHooks::do_action('mepr-membership-admin-enqueue-script', $hook); + return MeprProductsHelper::generate_product_link_html($product, $content); } - } - - public static function nullify_records_on_delete($id) { - MeprTransaction::nullify_product_id_on_delete($id); - MeprSubscription::nullify_product_id_on_delete($id); - return $id; - } + public static function shortcode_registration_form($atts, $content = '') + { + $membership_id = (isset($atts['id'])) ? $atts['id'] : 0; + $membership_id = ($membership_id === 0 && isset($atts['product_id'])) ? $atts['product_id'] : $membership_id; // Back compat - public static function shortcode_product_link($atts, $content = '') { - if(!isset($atts['id']) || !is_numeric($atts['id'])) { return $content; } + $prd = ($membership_id > 0) ? new MeprProduct($membership_id) : false; - $product = new MeprProduct($atts['id']); + if ($prd !== false && isset($prd->ID) && $prd->ID > 0) { + $res = self::get_registration_form($prd); + return $res->content; + } - if($product->ID === null) { return $content; } + return self::display_registration_form('', true); + } - return MeprProductsHelper::generate_product_link_html($product, $content); - } + public static function shortcode_if_product_was_purchased($atts, $content = '') + { + // Let's keep the protected string hidden if we have garbage input + if ( + !isset($atts['id']) or + !is_numeric($atts['id']) or + !isset($_REQUEST['trans_num']) + ) { + return ''; + } - public static function shortcode_registration_form($atts, $content = '') { - $membership_id = (isset($atts['id'])) ? $atts['id'] : 0; - $membership_id = ($membership_id === 0 && isset($atts['product_id'])) ? $atts['product_id'] : $membership_id; //Back compat + $txn = new MeprTransaction(); + $data = MeprTransaction::get_one_by_trans_num($_REQUEST['trans_num']); + $txn->load_data($data); - $prd = ($membership_id > 0) ? new MeprProduct($membership_id) : false; + if (!$txn->id or $txn->product_id != $atts['id']) { + return ''; + } - if($prd !== false && isset($prd->ID) && $prd->ID > 0) { - $res = self::get_registration_form($prd); - return $res->content; + return $content; } - return self::display_registration_form('', true); - } + public static function maybe_get_thank_you_page_message() + { + if (isset($_REQUEST['membership_id'])) { + $product = new MeprProduct(intval($_REQUEST['membership_id'])); + } else { + if (! isset($_REQUEST['trans_num'])) { + return ''; + } - public static function shortcode_if_product_was_purchased($atts, $content = '') { - //Let's keep the protected string hidden if we have garbage input - if( !isset($atts['id']) or - !is_numeric($atts['id']) or - !isset($_REQUEST['trans_num']) ) - { return ''; } + $txn = new MeprTransaction(); + $data = MeprTransaction::get_one_by_trans_num($_REQUEST['trans_num']); + $txn->load_data($data); - $txn = new MeprTransaction(); - $data = MeprTransaction::get_one_by_trans_num($_REQUEST['trans_num']); - $txn->load_data($data); + if (! $txn->id || ! $txn->product_id) { + return ''; + } - if(!$txn->id or $txn->product_id != $atts['id']) { return ''; } + $product = $txn->product(); + } - return $content; - } + if ($product->ID === null || !$product->thank_you_page_enabled || empty($product->thank_you_message)) { + return ''; + } - public static function maybe_get_thank_you_page_message() { - if (isset($_REQUEST['membership_id'])) { - $product = new MeprProduct(intval($_REQUEST['membership_id'])); - } else { - if ( ! isset( $_REQUEST['trans_num'] ) ) { - return ''; - } + // Backwards compatibility check + if (!empty($product->thank_you_page_type) && $product->thank_you_page_type != 'message') { + return ''; + } - $txn = new MeprTransaction(); - $data = MeprTransaction::get_one_by_trans_num( $_REQUEST['trans_num'] ); - $txn->load_data( $data ); + $message = wpautop(stripslashes($product->thank_you_message)); + $message = do_shortcode($message); + $message = MeprHooks::apply_filters('mepr_custom_thankyou_message', $message); - if ( ! $txn->id || ! $txn->product_id ) { - return ''; - } + if (isset($txn)) { + MeprHooks::do_action('mepr-thank-you-page', $txn); + } - $product = $txn->product(); + return '
    ' . $message . '
    '; } - if($product->ID === null || !$product->thank_you_page_enabled || empty($product->thank_you_message)) { - return ''; + // Just a wrapper for track_and_override_login_redirect_mepr() + // this wrapper catches regular WP logins + public static function track_and_override_login_redirect_wp($url, $request, $user) + { + return self::track_and_override_login_redirect_mepr($url, $user, true); } - // Backwards compatibility check - if(!empty($product->thank_you_page_type) && $product->thank_you_page_type != 'message') { - return ''; - } + public static function track_and_override_login_redirect_mepr($url = '', $wp_user = false, $is_wp_login_page = false, $track = true) + { + static $exsubs = null; + static $num_logins = null; - $message = wpautop(stripslashes($product->thank_you_message)); - $message = do_shortcode($message); - $message = MeprHooks::apply_filters('mepr_custom_thankyou_message', $message); + $mepr_options = MeprOptions::fetch(); - if (isset($txn)) { - MeprHooks::do_action( 'mepr-thank-you-page', $txn ); - } + if (empty($wp_user) || is_wp_error($wp_user)) { + return $url; + } - return '
    '.$message.'
    '; - } + $is_login_page = ((isset($_POST['mepr_is_login_page']) && $_POST['mepr_is_login_page'] == 'true') || $is_wp_login_page); - //Just a wrapper for track_and_override_login_redirect_mepr() - //this wrapper catches regular WP logins - public static function track_and_override_login_redirect_wp($url, $request, $user) { - return self::track_and_override_login_redirect_mepr($url, $user, true); - } + // Track this login, then get the num total logins for this user + $user = new MeprUser($wp_user->ID); - public static function track_and_override_login_redirect_mepr($url = '', $wp_user = false, $is_wp_login_page = false, $track = true) { - static $exsubs = null; - static $num_logins = null; + if ($track) { + MeprEvent::record('login', $user); + } - $mepr_options = MeprOptions::fetch(); + // short circuit if user has expired subscriptions and is not an admin + if (is_null($exsubs)) { + $exsubs = $user->subscription_expirations('expired', true); + } + if (!empty($exsubs) && !$wp_user->has_cap('delete_users')) { + return $mepr_options->account_page_url(); + } - if(empty($wp_user) || is_wp_error($wp_user)) { return $url; } + if (is_null($num_logins)) { + $num_logins = $user->get_num_logins(); + } - $is_login_page = ((isset($_POST['mepr_is_login_page']) && $_POST['mepr_is_login_page'] == 'true') || $is_wp_login_page); + // Get user's active memberships + $membership_id = MeprProduct::get_highest_menu_order_active_membership_by_user($user->ID); - //Track this login, then get the num total logins for this user - $user = new MeprUser($wp_user->ID); + if ($membership_id === false) { + return $url; + } else { + $membership = new MeprProduct($membership_id); + } - if($track) { MeprEvent::record('login', $user); } + if ($membership->custom_login_urls_enabled && (!empty($membership->custom_login_urls_default) || !empty($membership->custom_login_urls))) { + if (!empty($membership->custom_login_urls)) { + foreach ($membership->custom_login_urls as $custom_url) { + if (!empty($custom_url) && $custom_url->count == $num_logins) { + return stripslashes($custom_url->url); + } + } + } - // short circuit if user has expired subscriptions and is not an admin - if(is_null($exsubs)) { - $exsubs = $user->subscription_expirations('expired', true); - } - if(!empty($exsubs) && !$wp_user->has_cap('delete_users')) { - return $mepr_options->account_page_url(); - } + return (!empty($membership->custom_login_urls_default) && $is_login_page) ? $membership->custom_login_urls_default : $url; + } - if(is_null($num_logins)) { - $num_logins = $user->get_num_logins(); + return $url; } - //Get user's active memberships - $membership_id = MeprProduct::get_highest_menu_order_active_membership_by_user( $user->ID ); + // Get's the price string via ajax for the price box in the dashboard + public static function get_price_str_ajax() + { + if (!isset($_POST['product_id']) || !is_numeric($_POST['product_id'])) { + die(__('An unknown error has occurred', 'memberpress')); + } - if( $membership_id === false ) { - return $url; - } - else { - $membership = new MeprProduct( $membership_id ); - } + $product = new MeprProduct($_POST['product_id']); - if($membership->custom_login_urls_enabled && (!empty($membership->custom_login_urls_default) || !empty($membership->custom_login_urls))) { - if(!empty($membership->custom_login_urls)) { - foreach($membership->custom_login_urls as $custom_url) { - if(!empty($custom_url) && $custom_url->count == $num_logins) { - return stripslashes($custom_url->url); - } + if (!isset($product->ID) || (int)$product->ID <= 0) { + die(__('Please save membership first to see the Price here.', 'memberpress')); } - } - return (!empty($membership->custom_login_urls_default) && $is_login_page)?$membership->custom_login_urls_default:$url; + die(MeprAppHelper::format_price_string($product, $product->price)); } - return $url; - } - - //Get's the price string via ajax for the price box in the dashboard - public static function get_price_str_ajax() { - if(!isset($_POST['product_id']) || !is_numeric($_POST['product_id'])) { - die(__('An unknown error has occurred', 'memberpress')); - } + public static function shortcode_access_url_link($atts = [], $content = '') + { + if (!isset($atts['id']) || !is_numeric($atts['id'])) { + return $content; + } - $product = new MeprProduct($_POST['product_id']); + $product = new MeprProduct($atts['id']); - if(!isset($product->ID) || (int)$product->ID <= 0) { - die(__('Please save membership first to see the Price here.', 'memberpress')); - } + if ($product->ID === null || empty($product->access_url)) { + return $content; + } - die(MeprAppHelper::format_price_string($product, $product->price)); - } + if (empty($content)) { + $link_text = $product->post_title; + } else { + $link_text = $content; + } - public static function shortcode_access_url_link($atts = array(), $content = '') { - if(!isset($atts['id']) || !is_numeric($atts['id'])) { - return $content; + return '' . $link_text . ''; } - $product = new MeprProduct($atts['id']); + public static function shortcode_price($atts = [], $content = '') + { + global $post; - if($product->ID === null || empty($product->access_url)) { - return $content; - } + if (!isset($atts['id']) && !empty($post) && $post->post_type == MeprProduct::$cpt) { + $membership = new MeprProduct($post->ID); + } elseif (isset($atts['id'])) { + $membership = new MeprProduct($atts['id']); + } else { + return ''; + } - if(empty($content)) { - $link_text = $product->post_title; - } - else { - $link_text = $content; - } + $coupon_code = null; + $diff = false; + if (isset($atts['coupon'])) { + if ($atts['coupon'] == 'param' && isset($_REQUEST['coupon'])) { + $coupon_code = $_REQUEST['coupon']; + } else { + $coupon_code = $atts['coupon']; + } - return ''.$link_text.''; - } + if (isset($atts['diff']) && $atts['diff']) { + $diff = true; + } - public static function shortcode_price($atts = array(), $content = '') { - global $post; + $coupon = MeprCoupon::get_one_from_code($coupon_code); - if(!isset($atts['id']) && !empty($post) && $post->post_type==MeprProduct::$cpt) { - $membership = new MeprProduct($post->ID); - } - else if(isset($atts['id'])) { - $membership = new MeprProduct($atts['id']); - } - else { - return ''; - } + if ($coupon) { + $coupon->maybe_apply_trial_override($membership); + } + } - $coupon_code = null; - $diff = false; - if(isset($atts['coupon'])) { - if($atts['coupon']=='param' && isset($_REQUEST['coupon'])) { - $coupon_code = $_REQUEST['coupon']; - } - else { - $coupon_code = $atts['coupon']; - } + if ($membership->trial) { + $adj_price = $membership->trial_amount; + } else { + $adj_price = $membership->adjusted_price($coupon_code); + } - if(isset($atts['diff']) && $atts['diff']) { - $diff = true; - } + if ($diff) { + $display_price = $membership->adjusted_price() - $adj_price; + } else { + $display_price = $adj_price; + } - $coupon = MeprCoupon::get_one_from_code($coupon_code); + if (isset($atts['format'])) { + preg_match('!^(\d*)(\.\d*)?$!', $display_price, $price_matches); + if ($atts['format'] == 'cents') { + return (isset($price_matches[2]) ? $price_matches[2] : '   '); + } elseif ($atts['format'] == 'dollars') { + return $price_matches[1]; + } + } - if($coupon) { - $coupon->maybe_apply_trial_override($membership); - } + return MeprUtils::format_float_drop_zero_decimals($display_price); } - if($membership->trial) { - $adj_price = $membership->trial_amount; - } - else { - $adj_price = $membership->adjusted_price($coupon_code); + /** + * Render filters for membership products. + */ + public static function render_memberships_filters($post_type) + { + if ($post_type === MeprProduct::$cpt) { + $taxonomy = MeprProduct::$taxonomy_product_category; + $selected = isset($_GET[$taxonomy]) ? $_GET[$taxonomy] : ''; + $info_taxonomy = get_taxonomy($taxonomy); + + if (false === $info_taxonomy) { + return; + } + + $taxonomy_args = [ + 'taxonomy' => $taxonomy, + 'hide_empty' => false, + 'fields' => 'ids', + 'number' => 1, + ]; + + $taxonomy_terms = get_terms($taxonomy_args); + + if (empty($taxonomy_terms)) { + return; + } + + wp_dropdown_categories([ + 'show_option_all' => sprintf(esc_html__('Show all %s', 'memberpress'), $info_taxonomy->label), + 'taxonomy' => $taxonomy, + 'name' => $taxonomy, + 'orderby' => 'name', + 'selected' => $selected, + 'show_count' => true, + 'hide_empty' => false, + ]); + + echo wp_kses( + sprintf('', esc_html__('Filter', 'memberpress')), + [ + 'input' => [ + 'type' => [], + 'name' => [], + 'id' => [], + 'class' => [], + 'value' => [], + ], + ] + ); + } } - if($diff) { - $display_price = $membership->adjusted_price() - $adj_price; - } - else { - $display_price = $adj_price; + public static function register_filter_queries() + { + add_action('parse_query', 'MeprProductsCtrl::filter_memberships'); } - if(isset($atts['format'])) { - preg_match('!^(\d*)(\.\d*)?$!', $display_price, $price_matches); - if($atts['format']=='cents') { - return (isset($price_matches[2]) ? $price_matches[2] : '   '); - } - else if($atts['format']=='dollars') { - return $price_matches[1]; - } - } - - return MeprUtils::format_float_drop_zero_decimals($display_price); - } - - /** - * Render filters for membership products. - */ - public static function render_memberships_filters($post_type) { - if ($post_type === MeprProduct::$cpt) { - $taxonomy = MeprProduct::$taxonomy_product_category; - $selected = isset($_GET[$taxonomy]) ? $_GET[$taxonomy] : ''; - $info_taxonomy = get_taxonomy($taxonomy); - - if( false === $info_taxonomy ) { - return; - } - - $taxonomy_args = array( - 'taxonomy' => $taxonomy, - 'hide_empty' => false, - 'fields' => 'ids', - 'number' => 1 - ); - - $taxonomy_terms = get_terms( $taxonomy_args ); - - if ( empty( $taxonomy_terms ) ) { - return; - } - - wp_dropdown_categories(array( - 'show_option_all' => sprintf( esc_html__( 'Show all %s', 'memberpress' ), $info_taxonomy->label ), - 'taxonomy' => $taxonomy, - 'name' => $taxonomy, - 'orderby' => 'name', - 'selected' => $selected, - 'show_count' => true, - 'hide_empty' => false - )); - - echo wp_kses( - sprintf( '', esc_html__('Filter', 'memberpress') ), - array( - 'input' => array( - 'type' => array(), - 'name' => array(), - 'id' => array(), - 'class'=> array(), - 'value'=> array(), - ) - ) - ); - } - } - - public static function register_filter_queries() { - add_action('parse_query', 'MeprProductsCtrl::filter_memberships'); - } - - /** - * Filter the memberships as per selected taxonomy. - * - * @param $query - */ - public static function filter_memberships($query) { - global $pagenow; - $taxonomy = MeprProduct::$taxonomy_product_category; - if ( - $pagenow == 'edit.php' && is_admin() - && isset($query->query_vars['post_type']) - && $query->query_vars['post_type'] === MeprProduct::$cpt - && isset($query->query_vars[$taxonomy]) - && is_numeric($query->query_vars[$taxonomy]) - && 0 < absint($query->query_vars[$taxonomy]) - ) { - $term = get_term_by('id', (int) $query->query_vars[$taxonomy], $taxonomy); - if ($term && ! is_wp_error($term)) { - $query->query_vars[$taxonomy] = $term->slug; - } - } - } - /** - * Render category button beside 'Add New' button. - * - * @return void - */ - public static function render_categories_button(){ - if ( empty( $_GET['post_type'] ) || MeprProduct::$cpt !== $_GET['post_type'] ) { - return; - } - $category_link = add_query_arg( array( - 'taxonomy' => MeprProduct::$taxonomy_product_category, - 'post_type' => MeprProduct::$cpt - ), - esc_url(admin_url('edit-tags.php')) - ); - $category_btn = wp_kses( - sprintf( - '%2$s', - $category_link, - esc_html__( 'Categories', 'memberpress' ) - ), - array( - 'a' => array( - 'href' => array(), - 'class' => array(), - 'target' => array() - ) - ) - ); - ?> + /** + * Filter the memberships as per selected taxonomy. + * + * @param $query + */ + public static function filter_memberships($query) + { + global $pagenow; + $taxonomy = MeprProduct::$taxonomy_product_category; + if ( + $pagenow == 'edit.php' && is_admin() + && isset($query->query_vars['post_type']) + && $query->query_vars['post_type'] === MeprProduct::$cpt + && isset($query->query_vars[$taxonomy]) + && is_numeric($query->query_vars[$taxonomy]) + && 0 < absint($query->query_vars[$taxonomy]) + ) { + $term = get_term_by('id', (int) $query->query_vars[$taxonomy], $taxonomy); + if ($term && ! is_wp_error($term)) { + $query->query_vars[$taxonomy] = $term->slug; + } + } + } + /** + * Render category button beside 'Add New' button. + * + * @return void + */ + public static function render_categories_button() + { + if (empty($_GET['post_type']) || MeprProduct::$cpt !== $_GET['post_type']) { + return; + } + $category_link = add_query_arg( + [ + 'taxonomy' => MeprProduct::$taxonomy_product_category, + 'post_type' => MeprProduct::$cpt, + ], + esc_url(admin_url('edit-tags.php')) + ); + $category_btn = wp_kses( + sprintf( + '%2$s', + $category_link, + esc_html__('Categories', 'memberpress') + ), + [ + 'a' => [ + 'href' => [], + 'class' => [], + 'target' => [], + ], + ] + ); + ?> - ID ) { - return esc_html__( 'No group found', 'memberpress' ); - } + $group = new MeprGroup($atts['group_id']); - add_filter( 'mepr_pro_templates_has_pricing_block', '__return_true' ); - $content = do_shortcode( '[mepr-group-price-boxes group_id="' . $group->ID . '" show_title="' . $atts['show_title'] . '" button_highlight_color="' . $atts['button_highlight_color'] . '"] ' ); - return $content; - } - - /** - * Render pricing table shortcode - * - * @param array $atts shortcode args. - * @return void - */ - public function account_shortcode( $atts = array() ) { - wp_enqueue_script( 'alpinejs', MEPR_JS_URL . '/vendor/alpine.min.js', array(), MEPR_VERSION, true ); - wp_enqueue_script( 'mepr-accountjs', MEPR_JS_URL . '/readylaunch/account.js', array( 'jquery' ), MEPR_VERSION, true ); - wp_localize_script( - 'mepr-accountjs', - 'MeprAccount', - array( - 'ajax_url' => admin_url( 'admin-ajax.php' ), - 'nonce' => wp_create_nonce( 'mepr_account_update' ), - 'current_url' => MeprUtils::get_current_url_without_params(), - ) - ); - - // Show welcome image - if ( isset( $atts['show_welcome_image'] ) ) { - $show_welcome_image = filter_var( $atts['show_welcome_image'], FILTER_VALIDATE_BOOLEAN ); - } + if (! $group->ID) { + return esc_html__('No group found', 'memberpress'); + } - // Get welcome image - $welcome_image = ''; - if ( isset( $atts['welcome_image'] ) && ! empty( $atts['welcome_image'] ) ) { - $welcome_image = $atts['welcome_image']; + add_filter('mepr_pro_templates_has_pricing_block', '__return_true'); + $content = do_shortcode('[mepr-group-price-boxes group_id="' . $group->ID . '" show_title="' . $atts['show_title'] . '" button_highlight_color="' . $atts['button_highlight_color'] . '"] '); + return $content; } - add_filter( 'mepr_pro_templates_has_account_block', '__return_true' ); - $content = do_shortcode( '[mepr-account-form welcome_image="' . $welcome_image . '" show_welcome_image="' . $show_welcome_image . '"]' ); + /** + * Render pricing table shortcode + * + * @param array $atts shortcode args. + * @return void + */ + public function account_shortcode($atts = []) + { + wp_enqueue_script('alpinejs', MEPR_JS_URL . '/vendor/alpine.min.js', [], MEPR_VERSION, true); + wp_enqueue_script('mepr-accountjs', MEPR_JS_URL . '/readylaunch/account.js', ['jquery'], MEPR_VERSION, true); + wp_localize_script( + 'mepr-accountjs', + 'MeprAccount', + [ + 'ajax_url' => admin_url('admin-ajax.php'), + 'nonce' => wp_create_nonce('mepr_account_update'), + 'current_url' => MeprUtils::get_current_url_without_params(), + ] + ); + + // Show welcome image + if (isset($atts['show_welcome_image'])) { + $show_welcome_image = filter_var($atts['show_welcome_image'], FILTER_VALIDATE_BOOLEAN); + } - if ( MeprUtils::is_user_logged_in() ) { - $content = "
    " . $content . '
    '; - } + // Get welcome image + $welcome_image = ''; + if (isset($atts['welcome_image']) && ! empty($atts['welcome_image'])) { + $welcome_image = $atts['welcome_image']; + } - return $content; - } - - /** - * Checkout Shortcode - * - * @param array $atts array of attributes. - * @return string - */ - public function checkout_shortcode( $atts = array() ) { - wp_enqueue_script( 'mepr-signupjs', MEPR_JS_URL . '/readylaunch/signup.js', array( 'jquery' ), MEPR_VERSION, true ); - - wp_localize_script( - 'mepr-signupjs', - 'MeprProTemplateSignup', - array( - 'spc_enabled' => true, - ) - ); - - if ( ! isset( $atts['membership_id'] ) || $atts['membership_id'] <= 0 ) { - return esc_html__( 'Please select membership', 'memberpress' ); - } + add_filter('mepr_pro_templates_has_account_block', '__return_true'); + $content = do_shortcode('[mepr-account-form welcome_image="' . $welcome_image . '" show_welcome_image="' . $show_welcome_image . '"]'); - $prd = new MeprProduct( $atts['membership_id'] ); + if (MeprUtils::is_user_logged_in()) { + $content = "
    " . $content . '
    '; + } - if ( ! $prd->ID ) { - return esc_html__( 'No membership found', 'memberpress' ); + return $content; } - add_filter( 'mepr_pro_templates_has_checkout_block', '__return_true' ); - $content = do_shortcode( '[mepr-membership-registration-form id="' . $prd->ID . '"]' ); - - return $content; - } - - /** - * Override default template with the courses page template - * - * @param string $template current template - * @return string $template modified template - */ - public function override_page_templates( $template ) { - global $post; - $mepr_options = MeprOptions::fetch(); - $logout_url = MeprUtils::logout_url(); - $account_url = $mepr_options->account_page_url(); - $delim = MeprAppCtrl::get_param_delimiter_char($account_url); - $change_password_url = MeprHooks::apply_filters( 'mepr-rl-change-password-url', $account_url . $delim . 'action=newpassword' ); - $logo = esc_url( wp_get_attachment_url( $mepr_options->design_logo_img ) ); - $user = MeprUtils::get_currentuserinfo(); - $wrapper_classes = ''; - - if ( self::template_enabled( 'pricing' ) ) { - $user = MeprUtils::get_currentuserinfo(); - $has_welcome_image = $mepr_options->design_login_welcome_img; - $group_ctrl = MeprCtrlFactory::fetch( 'groups' ); - - $template = \MeprView::file( '/readylaunch/layout/app' ); - include $template; - exit; - } + /** + * Checkout Shortcode + * + * @param array $atts array of attributes. + * @return string + */ + public function checkout_shortcode($atts = []) + { + wp_enqueue_script('mepr-signupjs', MEPR_JS_URL . '/readylaunch/signup.js', ['jquery'], MEPR_VERSION, true); + + wp_localize_script( + 'mepr-signupjs', + 'MeprProTemplateSignup', + [ + 'spc_enabled' => true, + ] + ); + + if (! isset($atts['membership_id']) || $atts['membership_id'] <= 0) { + return esc_html__('Please select membership', 'memberpress'); + } - if ( self::template_enabled( 'login' ) ) { - if ( $post->ID == $mepr_options->login_page_id ) { - $template = \MeprView::file( '/readylaunch/layout/guest' ); - include $template; - exit; - } - } + $prd = new MeprProduct($atts['membership_id']); - if ( self::template_enabled( 'account' ) ) { - $is_account_page = true; - $template = MeprView::file( '/readylaunch/layout/app' ); - include $template; - exit; - } + if (! $prd->ID) { + return esc_html__('No membership found', 'memberpress'); + } - // Checkout Page Template - if ( self::template_enabled( 'checkout' ) ) { - $template = MeprView::file( '/readylaunch/layout/app' ); - include $template; - exit; - } + add_filter('mepr_pro_templates_has_checkout_block', '__return_true'); + $content = do_shortcode('[mepr-membership-registration-form id="' . $prd->ID . '"]'); - if ( self::template_enabled( 'thankyou' ) ) { - $template = MeprView::file( '/readylaunch/layout/app' ); - include $template; - exit; + return $content; } - return $template; - } - - /** - * Gets the page content for thankyou page - * - * @param string $content - * @return string - */ - public function thankyou_page_content( $content ) { - if ( self::template_enabled( 'thankyou' ) ) { - $txn = isset( $_GET['trans_num'] ) ? MeprTransaction::get_one_by_trans_num( sanitize_text_field( wp_unslash( $_GET['trans_num'] ) ) ) : null; - $txn = null === $txn && isset( $_GET['transaction_id'] ) ? MeprTransaction::get_one( (int) $_GET['transaction_id'] ) : $txn; - $txn = !empty($txn->id) ? new MeprTransaction($txn->id) : null; - - if($txn instanceof MeprTransaction && !empty($txn->id)) { - $mepr_options = MeprOptions::fetch(); - $hide_invoice = $mepr_options->design_thankyou_hide_invoice; - $invoice_message = do_shortcode($mepr_options->design_thankyou_invoice_message); - $has_welcome_image = $mepr_options->design_show_thankyou_welcome_image; - $welcome_image = esc_url(wp_get_attachment_url($mepr_options->design_thankyou_welcome_img)); - - if(($order = $txn->order()) instanceof MeprOrder) { - $order_bump_transactions = MeprTransaction::get_all_by_order_id_and_gateway($order->id, $txn->gateway, $txn->id); - $transactions = array_merge([$txn], $order_bump_transactions); - $processed_sub_ids = []; - $order_bumps = []; - $amount = 0.00; - - foreach($transactions as $index => $transaction) { - if($transaction->is_one_time_payment()) { - $amount += (float) $transaction->total; - - if($index > 0) { - $order_bumps[] = [$transaction->product(), $transaction, null]; - } + /** + * Override default template with the courses page template + * + * @param string $template current template + * @return string $template modified template + */ + public function override_page_templates($template) + { + global $post; + $mepr_options = MeprOptions::fetch(); + $logout_url = MeprUtils::logout_url(); + $account_url = $mepr_options->account_page_url(); + $delim = MeprAppCtrl::get_param_delimiter_char($account_url); + $change_password_url = MeprHooks::apply_filters('mepr-rl-change-password-url', $account_url . $delim . 'action=newpassword'); + $logo = esc_url(wp_get_attachment_url($mepr_options->design_logo_img)); + $user = MeprUtils::get_currentuserinfo(); + $wrapper_classes = ''; + + if (self::template_enabled('pricing')) { + $user = MeprUtils::get_currentuserinfo(); + $has_welcome_image = $mepr_options->design_login_welcome_img; + $group_ctrl = MeprCtrlFactory::fetch('groups'); + + $template = \MeprView::file('/readylaunch/layout/app'); + include $template; + exit; + } + + if (self::template_enabled('login')) { + if ($post->ID == $mepr_options->login_page_id) { + $template = \MeprView::file('/readylaunch/layout/guest'); + include $template; + exit; } - else { - $subscription = $transaction->subscription(); + } - if($subscription instanceof MeprSubscription) { - // Subs can have both a payment txn and confirmation txn, make sure we don't process a sub twice - if(in_array((int) $subscription->id, $processed_sub_ids, true)) { - continue; - } + if (self::template_enabled('account')) { + $is_account_page = true; + $template = MeprView::file('/readylaunch/layout/app'); + include $template; + exit; + } - $processed_sub_ids[] = (int) $subscription->id; + // Checkout Page Template + if (self::template_enabled('checkout')) { + $template = MeprView::file('/readylaunch/layout/app'); + include $template; + exit; + } - if(($subscription->trial && $subscription->trial_days > 0 && $subscription->txn_count < 1) || $transaction->txn_type == MeprTransaction::$subscription_confirmation_str) { - MeprTransactionsHelper::set_invoice_txn_vars_from_sub($transaction, $subscription); - } + if (self::template_enabled('thankyou')) { + $template = MeprView::file('/readylaunch/layout/app'); + include $template; + exit; + } - $amount += (float) $transaction->total; + return $template; + } - if($index > 0) { - $order_bumps[] = [$transaction->product(), $transaction, $subscription]; + /** + * Gets the page content for thankyou page + * + * @param string $content + * @return string + */ + public function thankyou_page_content($content) + { + if (self::template_enabled('thankyou')) { + $txn = isset($_GET['trans_num']) ? MeprTransaction::get_one_by_trans_num(sanitize_text_field(wp_unslash($_GET['trans_num']))) : null; + $txn = null === $txn && isset($_GET['transaction_id']) ? MeprTransaction::get_one((int) $_GET['transaction_id']) : $txn; + $txn = !empty($txn->id) ? new MeprTransaction($txn->id) : null; + + if ($txn instanceof MeprTransaction && !empty($txn->id)) { + $mepr_options = MeprOptions::fetch(); + $hide_invoice = $mepr_options->design_thankyou_hide_invoice; + $invoice_message = do_shortcode($mepr_options->design_thankyou_invoice_message); + $has_welcome_image = $mepr_options->design_show_thankyou_welcome_image; + $welcome_image = esc_url(wp_get_attachment_url($mepr_options->design_thankyou_welcome_img)); + + if (($order = $txn->order()) instanceof MeprOrder) { + $order_bump_transactions = MeprTransaction::get_all_by_order_id_and_gateway($order->id, $txn->gateway, $txn->id); + $transactions = array_merge([$txn], $order_bump_transactions); + $processed_sub_ids = []; + $order_bumps = []; + $amount = 0.00; + + foreach ($transactions as $index => $transaction) { + if ($transaction->is_one_time_payment()) { + $amount += (float) $transaction->total; + + if ($index > 0) { + $order_bumps[] = [$transaction->product(), $transaction, null]; + } + } else { + $subscription = $transaction->subscription(); + + if ($subscription instanceof MeprSubscription) { + // Subs can have both a payment txn and confirmation txn, make sure we don't process a sub twice + if (in_array((int) $subscription->id, $processed_sub_ids, true)) { + continue; + } + + $processed_sub_ids[] = (int) $subscription->id; + + if (($subscription->trial && $subscription->trial_days > 0 && $subscription->txn_count < 1) || $transaction->txn_type == MeprTransaction::$subscription_confirmation_str) { + MeprTransactionsHelper::set_invoice_txn_vars_from_sub($transaction, $subscription); + } + + $amount += (float) $transaction->total; + + if ($index > 0) { + $order_bumps[] = [$transaction->product(), $transaction, $subscription]; + } + } + } + } + + $amount = MeprAppHelper::format_currency($amount); + $trans_num = $order->trans_num; + $invoice_html = MeprTransactionsHelper::get_invoice_order_bumps($txn, '', $order_bumps); + } else { + $sub = $txn->subscription(); + + if ($sub instanceof MeprSubscription && (($sub->trial && $sub->trial_days > 0 && $sub->txn_count < 1) || $txn->txn_type == MeprTransaction::$subscription_confirmation_str)) { + MeprTransactionsHelper::set_invoice_txn_vars_from_sub($txn, $sub); + } + + $amount = strtok(MeprAppHelper::format_price_string($txn, $txn->amount), ' '); + + $trans_num = $txn->trans_num; + $invoice_html = MeprTransactionsHelper::get_invoice($txn); } - } + + $content = MeprView::get_string('/readylaunch/thankyou', get_defined_vars()); + } else { + $content = '

    ' . esc_html__('Transaction not found', 'memberpress') . '

    '; } - } + } + + return $content; + } - $amount = MeprAppHelper::format_currency($amount); - $trans_num = $order->trans_num; - $invoice_html = MeprTransactionsHelper::get_invoice_order_bumps($txn, '', $order_bumps); + /** + * Enqueues scripts for admin view + * + * @param string $hook current page hook. + * @return void + */ + public static function enqueue_admin_scripts($hook) + { + if (strstr($hook, 'memberpress-options') !== false) { + wp_enqueue_style('mp-readylaunch', MEPR_CSS_URL . '/admin-readylaunch.css', [], MEPR_VERSION); + + // Let's localize data for our drag and drop settings. + $plupload_args = [ + 'file_data_name' => 'async-upload', + 'url' => admin_url('admin-ajax.php'), + 'filters' => [ + 'max_file_size' => wp_max_upload_size() . 'b', + 'mime_types' => [['extensions' => 'jpg,gif,png,jpeg']], + ], + 'multi_selection' => false, // Limit selection to just one. + + // additional post data to send to our ajax hook. + 'multipart_params' => [ + '_wpnonce' => wp_create_nonce('media-form'), + 'action' => 'upload-attachment', // the ajax action name. + ], + ]; + wp_enqueue_style('wp-color-picker'); + wp_enqueue_script('mp-readylaunch', MEPR_JS_URL . '/admin-readylaunch.js', ['mepr-uploader', 'plupload-all', 'wp-color-picker'], MEPR_VERSION); + wp_localize_script('mp-readylaunch', 'MeproTemplates', $plupload_args); } - else { - $sub = $txn->subscription(); + } + + /** + * Enqueues scripts for frontend view + * + * @return void + */ + public static function enqueue_scripts() + { + global $post; - if($sub instanceof MeprSubscription && (($sub->trial && $sub->trial_days > 0 && $sub->txn_count < 1) || $txn->txn_type == MeprTransaction::$subscription_confirmation_str)) { - MeprTransactionsHelper::set_invoice_txn_vars_from_sub($txn, $sub); - } + if (MeprUser::is_account_page($post)) { + wp_enqueue_script('mepr-popper', MEPR_JS_URL . '/vendor/popper.min.js', [], MEPR_VERSION, true); + } - $amount = strtok(MeprAppHelper::format_price_string($txn, $txn->amount), ' '); + $handles = ['dashicons', 'jquery-ui-timepicker-addon', 'jquery-magnific-popup']; - $trans_num = $txn->trans_num; - $invoice_html = MeprTransactionsHelper::get_invoice( $txn ); + // Login Scripts + if (self::template_enabled('login')) { + static::remove_styles($handles); + static::add_template_scripts('login'); } - $content = MeprView::get_string('/readylaunch/thankyou', get_defined_vars()); - } - else { - $content = '

    ' . esc_html__('Transaction not found', 'memberpress') . '

    '; - } - } + // Account Scripts + if (self::template_enabled('account')) { + static::remove_styles($handles); + static::add_template_scripts('account'); + } - return $content; - } - - /** - * Enqueues scripts for admin view - * - * @param string $hook current page hook. - * @return void - */ - public static function enqueue_admin_scripts( $hook ) { - if ( strstr( $hook, 'memberpress-options' ) !== false ) { - wp_enqueue_style( 'mp-readylaunch', MEPR_CSS_URL . '/admin-readylaunch.css', array(), MEPR_VERSION ); - - // Let's localize data for our drag and drop settings. - $plupload_args = array( - 'file_data_name' => 'async-upload', - 'url' => admin_url( 'admin-ajax.php' ), - 'filters' => array( - 'max_file_size' => wp_max_upload_size() . 'b', - 'mime_types' => array( array( 'extensions' => 'jpg,gif,png,jpeg' ) ), - ), - 'multi_selection' => false, // Limit selection to just one. - - // additional post data to send to our ajax hook. - 'multipart_params' => array( - '_wpnonce' => wp_create_nonce( 'media-form' ), - 'action' => 'upload-attachment', // the ajax action name. - ), - ); - wp_enqueue_style( 'wp-color-picker' ); - wp_enqueue_script( 'mp-readylaunch', MEPR_JS_URL . '/admin-readylaunch.js', array( 'mepr-uploader', 'plupload-all', 'wp-color-picker' ), MEPR_VERSION ); - wp_localize_script( 'mp-readylaunch', 'MeproTemplates', $plupload_args ); - } - } - - /** - * Enqueues scripts for frontend view - * - * @return void - */ - public static function enqueue_scripts() { - global $post; - - if ( MeprUser::is_account_page( $post ) ) { - wp_enqueue_script( 'mepr-popper', MEPR_JS_URL . '/vendor/popper.min.js', array(), MEPR_VERSION, true ); - } + // Pricing Scripts + if (self::template_enabled('pricing')) { + if ( + isset($post) && + is_a($post, 'WP_Post') && + $post->post_type == MeprGroup::$cpt + ) { + static::remove_styles($handles); + static::add_template_scripts('pricing'); + } + } - $handles = array( 'dashicons', 'jquery-ui-timepicker-addon', 'jquery-magnific-popup' ); + // Checkout Scripts + if (self::template_enabled('checkout') || self::template_enabled('thankyou')) { + static::remove_styles($handles); + static::add_template_scripts('checkout'); + } - // Login Scripts - if ( self::template_enabled( 'login' ) ) { - static::remove_styles( $handles ); - static::add_template_scripts( 'login' ); + if ( + function_exists('bp_is_current_action') && + bp_is_current_action('mp-subscriptions') + ) { + wp_enqueue_style('mp-rl-buddyboss', MEPR_CSS_URL . '/readylaunch/compatibility.css', [], MEPR_VERSION); + wp_enqueue_script('alpinejs', MEPR_JS_URL . '/vendor/alpine.min.js', [], MEPR_VERSION, true); + wp_enqueue_script('mepr-accountjs', MEPR_JS_URL . '/readylaunch/account.js', ['jquery'], MEPR_VERSION, true); + } } - // Account Scripts - if ( self::template_enabled( 'account' ) ) { - static::remove_styles( $handles ); - static::add_template_scripts( 'account' ); - } + /** + * Add Design tab toadmin memberpress page + * + * @return void + */ + public static function display_option_tab() + { + ?> + + products()); + if ($products_count > 5) { + $pricing_columns_limit = true; + break; + } + } - // Pricing Scripts - if ( self::template_enabled( 'pricing' ) ) { - if ( - isset( $post ) && - is_a( $post, 'WP_Post' ) && - $post->post_type == MeprGroup::$cpt - ) { - static::remove_styles( $handles ); - static::add_template_scripts( 'pricing' ); - } - } + $data = [ + 'global' => [ + 'logoId' => isset($mepr_options->design_logo_img) ? absint($mepr_options->design_logo_img) : '', + ], + 'pricing' => [ + 'enableTemplate' => isset($mepr_options->design_enable_pricing_template) ? filter_var($mepr_options->design_enable_pricing_template, FILTER_VALIDATE_BOOLEAN) : '', + ], + 'checkout' => [ + 'enableTemplate' => isset($mepr_options->design_enable_checkout_template) ? filter_var($mepr_options->design_enable_checkout_template, FILTER_VALIDATE_BOOLEAN) : '', + 'showPriceTerms' => isset($mepr_options->design_show_checkout_price_terms) ? filter_var($mepr_options->design_show_checkout_price_terms, FILTER_VALIDATE_BOOLEAN) : '', + ], + 'login' => [ + 'enableTemplate' => isset($mepr_options->design_enable_login_template) ? filter_var($mepr_options->design_enable_login_template, FILTER_VALIDATE_BOOLEAN) : '', + 'showWelcomeImage' => isset($mepr_options->design_show_login_welcome_image) ? filter_var($mepr_options->design_show_login_welcome_image, FILTER_VALIDATE_BOOLEAN) : '', + 'welcomeImageId' => isset($mepr_options->design_login_welcome_img) ? absint($mepr_options->design_login_welcome_img) : '', + ], + 'thankyou' => [ + 'enableTemplate' => isset($mepr_options->design_enable_thankyou_template) ? filter_var($mepr_options->design_enable_thankyou_template, FILTER_VALIDATE_BOOLEAN) : '', + 'showWelcomeImage' => isset($mepr_options->design_show_thankyou_welcome_image) ? filter_var($mepr_options->design_show_thankyou_welcome_image, FILTER_VALIDATE_BOOLEAN) : '', + 'hideInvoice' => isset($mepr_options->design_thankyou_hide_invoice) ? filter_var($mepr_options->design_thankyou_hide_invoice, FILTER_VALIDATE_BOOLEAN) : '', + 'welcomeImageId' => isset($mepr_options->design_thankyou_welcome_img) ? absint($mepr_options->design_thankyou_welcome_img) : '', + ], + 'account' => [ + 'enableTemplate' => isset($mepr_options->design_enable_account_template) ? filter_var($mepr_options->design_enable_account_template, FILTER_VALIDATE_BOOLEAN) : '', + 'showWelcomeImage' => isset($mepr_options->design_show_account_welcome_image) ? filter_var($mepr_options->design_show_account_welcome_image, FILTER_VALIDATE_BOOLEAN) : '', + 'welcomeImageId' => isset($mepr_options->design_account_welcome_img) ? absint($mepr_options->design_account_welcome_img) : '', + ], + 'courses' => [ + 'enableTemplate' => '', + 'showProtectedCourses' => '', + 'removeInstructorLink' => '', + 'logoId' => '', + ], + 'coaching' => [ + 'enableTemplate' => isset($mepr_options->rl_enable_coaching_template) ? filter_var($mepr_options->rl_enable_coaching_template, FILTER_VALIDATE_BOOLEAN) : '', + ], + ]; + + $data = MeprHooks::apply_filters('mepr-readylaunch-options-data', $data); + + MeprView::render('/admin/readylaunch/options', get_defined_vars()); + } + + /** + * Validates all Design tab admin settings + * + * @param array $errors The errors array. + * @return array + */ + public static function validate_settings_fields($errors) + { + $params = $_POST; + $mepr_options = MeprOptions::fetch(); + + // When LoginTemplate is enabled and ShowWelcomeImage is checked but dude forgot to upload the image. + if ( + isset($params[ $mepr_options->design_enable_login_template_str ]) && + isset($params[ $mepr_options->design_show_login_welcome_image_str ]) && + absint($params[ $mepr_options->design_login_welcome_img_str ]) == 0 + ) { + $errors[] = esc_html__('Welcome Image should be uploaded if Show Welcome Image button is checked', 'memberpress'); + } - // Checkout Scripts - if ( self::template_enabled( 'checkout' ) || self::template_enabled( 'thankyou' ) ) { - static::remove_styles( $handles ); - static::add_template_scripts( 'checkout' ); + return $errors; } - if( - function_exists('bp_is_current_action') && - bp_is_current_action('mp-subscriptions')) + /** + * Dequeues and deregisters styles unrelated to pro mode templates. + * + * @param array $allowed_handles CSS Handles that won't be deregistered and dequeued when using Pro Mode. + * @return void + */ + public static function remove_styles($allowed_handles = []) { - wp_enqueue_style( 'mp-rl-buddyboss', MEPR_CSS_URL . '/readylaunch/compatibility.css', array(), MEPR_VERSION ); - wp_enqueue_script( 'alpinejs', MEPR_JS_URL . '/vendor/alpine.min.js', array(), MEPR_VERSION, true ); - wp_enqueue_script( 'mepr-accountjs', MEPR_JS_URL . '/readylaunch/account.js', array( 'jquery' ), MEPR_VERSION, true ); - } + global $wp_styles; + $allowed_handles = apply_filters('mepr_design_style_handles', $allowed_handles); + $allowed_handle_prefixes = apply_filters('mepr_design_style_handle_prefixes', ['mepr-', 'mp-', 'mpca-', 'mpcs-', 'mpgft-', 'ca-course']); + + // Remove styles. + foreach ($wp_styles->queue as $style) { + $handle = $wp_styles->registered[ $style ]->handle; + if (! in_array($handle, $allowed_handles)) { + foreach ($allowed_handle_prefixes as $prefix) { + if (strpos($handle, $prefix) === 0) { + continue 2; + } + } - } - - /** - * Add Design tab toadmin memberpress page - * - * @return void - */ - public static function display_option_tab() { ?> - - products() ); - if ( $products_count > 5 ) { - $pricing_columns_limit = true; - break; - } + wp_deregister_style($handle); + wp_dequeue_style($handle); + } + } } - $data = array( - 'global' => array( - 'logoId' => isset( $mepr_options->design_logo_img ) ? absint( $mepr_options->design_logo_img ) : '', - ), - 'pricing' => array( - 'enableTemplate' => isset( $mepr_options->design_enable_pricing_template ) ? filter_var( $mepr_options->design_enable_pricing_template, FILTER_VALIDATE_BOOLEAN ) : '', - ), - 'checkout' => array( - 'enableTemplate' => isset( $mepr_options->design_enable_checkout_template ) ? filter_var( $mepr_options->design_enable_checkout_template, FILTER_VALIDATE_BOOLEAN ) : '', - 'showPriceTerms' => isset( $mepr_options->design_show_checkout_price_terms ) ? filter_var( $mepr_options->design_show_checkout_price_terms, FILTER_VALIDATE_BOOLEAN ) : '', - ), - 'login' => array( - 'enableTemplate' => isset( $mepr_options->design_enable_login_template ) ? filter_var( $mepr_options->design_enable_login_template, FILTER_VALIDATE_BOOLEAN ) : '', - 'showWelcomeImage' => isset( $mepr_options->design_show_login_welcome_image ) ? filter_var( $mepr_options->design_show_login_welcome_image, FILTER_VALIDATE_BOOLEAN ) : '', - 'welcomeImageId' => isset( $mepr_options->design_login_welcome_img ) ? absint( $mepr_options->design_login_welcome_img ) : '', - ), - 'thankyou' => array( - 'enableTemplate' => isset( $mepr_options->design_enable_thankyou_template ) ? filter_var( $mepr_options->design_enable_thankyou_template, FILTER_VALIDATE_BOOLEAN ) : '', - 'showWelcomeImage' => isset( $mepr_options->design_show_thankyou_welcome_image ) ? filter_var( $mepr_options->design_show_thankyou_welcome_image, FILTER_VALIDATE_BOOLEAN ) : '', - 'hideInvoice' => isset( $mepr_options->design_thankyou_hide_invoice ) ? filter_var( $mepr_options->design_thankyou_hide_invoice, FILTER_VALIDATE_BOOLEAN ) : '', - 'welcomeImageId' => isset( $mepr_options->design_thankyou_welcome_img ) ? absint( $mepr_options->design_thankyou_welcome_img ) : '', - ), - 'account' => array( - 'enableTemplate' => isset( $mepr_options->design_enable_account_template ) ? filter_var( $mepr_options->design_enable_account_template, FILTER_VALIDATE_BOOLEAN ) : '', - 'showWelcomeImage' => isset( $mepr_options->design_show_account_welcome_image ) ? filter_var( $mepr_options->design_show_account_welcome_image, FILTER_VALIDATE_BOOLEAN ) : '', - 'welcomeImageId' => isset( $mepr_options->design_account_welcome_img ) ? absint( $mepr_options->design_account_welcome_img ) : '', - ), - 'courses' => array( - 'enableTemplate' => '', - 'showProtectedCourses' => '', - 'removeInstructorLink' => '', - 'logoId' => '', - ), - 'coaching' => array( - 'enableTemplate' => isset( $mepr_options->rl_enable_coaching_template ) ? filter_var( $mepr_options->rl_enable_coaching_template, FILTER_VALIDATE_BOOLEAN ) : '', - ), - ); - - $data = MeprHooks::apply_filters( 'mepr-readylaunch-options-data', $data ); - - MeprView::render( '/admin/readylaunch/options', get_defined_vars() ); - } - - /** - * Validates all Design tab admin settings - * - * @param array $errors The errors array. - * @return array - */ - public static function validate_settings_fields( $errors ) { - $params = $_POST; - $mepr_options = MeprOptions::fetch(); - - // When LoginTemplate is enabled and ShowWelcomeImage is checked but dude forgot to upload the image. - if ( - isset( $params[ $mepr_options->design_enable_login_template_str ] ) && - isset( $params[ $mepr_options->design_show_login_welcome_image_str ] ) && - absint( $params[ $mepr_options->design_login_welcome_img_str ] ) == 0 - ) { - $errors[] = esc_html__( 'Welcome Image should be uploaded if Show Welcome Image button is checked', 'memberpress' ); - } + /** + * Add scripts to full page template + * + * @param string $page the template page. + * @return void + */ + public static function add_template_scripts($page = '') + { + global $post; - return $errors; - } - - /** - * Dequeues and deregisters styles unrelated to pro mode templates. - * - * @param array $allowed_handles CSS Handles that won't be deregistered and dequeued when using Pro Mode. - * @return void - */ - public static function remove_styles( $allowed_handles = array() ) { - global $wp_styles; - $allowed_handles = apply_filters( 'mepr_design_style_handles', $allowed_handles ); - $allowed_handle_prefixes = apply_filters( 'mepr_design_style_handle_prefixes', array( 'mepr-', 'mp-', 'mpca-', 'mpcs-', 'mpgft-', 'ca-course' ) ); - - // Remove styles. - foreach ( $wp_styles->queue as $style ) { - $handle = $wp_styles->registered[ $style ]->handle; - if ( ! in_array( $handle, $allowed_handles ) ) { - - foreach ( $allowed_handle_prefixes as $prefix ) { - if ( strpos( $handle, $prefix ) === 0 ) { - continue 2; - } - } - - wp_deregister_style( $handle ); - wp_dequeue_style( $handle ); - } - } - } + wp_enqueue_style('mp-pro-theme', MEPR_CSS_URL . '/readylaunch/theme.css', null, MEPR_VERSION); - /** - * Add scripts to full page template - * - * @param string $page the template page. - * @return void - */ - public static function add_template_scripts( $page = '' ) { - global $post; + if ('login' === $page) { + wp_enqueue_style('mp-pro-login', MEPR_CSS_URL . '/readylaunch/login.css', null, MEPR_VERSION); + } - wp_enqueue_style( 'mp-pro-theme', MEPR_CSS_URL . '/readylaunch/theme.css', null, MEPR_VERSION ); + if ('account' === $page) { + wp_enqueue_style('mp-pro-login', MEPR_CSS_URL . '/readylaunch/login.css', null, MEPR_VERSION); + wp_register_style('mp-pro-fonts', MEPR_CSS_URL . '/readylaunch/fonts.css', null, MEPR_VERSION); + wp_enqueue_style('mp-pro-account', MEPR_CSS_URL . '/readylaunch/account.css', ['mp-pro-fonts'], MEPR_VERSION); + wp_enqueue_script('alpinejs', MEPR_JS_URL . '/vendor/alpine.min.js', [], MEPR_VERSION, true); + wp_enqueue_script('mepr-accountjs', MEPR_JS_URL . '/readylaunch/account.js', ['jquery'], MEPR_VERSION, true); + + wp_localize_script( + 'mepr-accountjs', + 'MeprAccount', + [ + 'ajax_url' => admin_url('admin-ajax.php'), + 'nonce' => wp_create_nonce('mepr_account_update'), + 'account_url' => MeprUtils::get_current_url_without_params(), + ] + ); + } - if ( 'login' === $page ) { - wp_enqueue_style( 'mp-pro-login', MEPR_CSS_URL . '/readylaunch/login.css', null, MEPR_VERSION ); - } + if ('pricing' == $page) { + wp_enqueue_style('mp-pro-pricing', MEPR_CSS_URL . '/readylaunch/pricing.css', null, MEPR_VERSION); + wp_enqueue_script('mepr-pro-pricing', MEPR_JS_URL . '/readylaunch/pricing.js', ['jquery'], MEPR_VERSION, true); + wp_enqueue_script('alpinejs', MEPR_JS_URL . '/vendor/alpine.min.js', [], MEPR_VERSION, true); + } - if ( 'account' === $page ) { - wp_enqueue_style( 'mp-pro-login', MEPR_CSS_URL . '/readylaunch/login.css', null, MEPR_VERSION ); - wp_register_style( 'mp-pro-fonts', MEPR_CSS_URL . '/readylaunch/fonts.css', null, MEPR_VERSION ); - wp_enqueue_style( 'mp-pro-account', MEPR_CSS_URL . '/readylaunch/account.css', array( 'mp-pro-fonts' ), MEPR_VERSION ); - wp_enqueue_script( 'alpinejs', MEPR_JS_URL . '/vendor/alpine.min.js', array(), MEPR_VERSION, true ); - wp_enqueue_script( 'mepr-accountjs', MEPR_JS_URL . '/readylaunch/account.js', array( 'jquery' ), MEPR_VERSION, true ); - - wp_localize_script( - 'mepr-accountjs', - 'MeprAccount', - array( - 'ajax_url' => admin_url( 'admin-ajax.php' ), - 'nonce' => wp_create_nonce( 'mepr_account_update' ), - 'account_url' => MeprUtils::get_current_url_without_params(), - ) - ); - } + if ('checkout' == $page) { + wp_enqueue_style('mp-pro-checkout', MEPR_CSS_URL . '/readylaunch/checkout.css', null, MEPR_VERSION); + wp_enqueue_script('mepr-signupjs', MEPR_JS_URL . '/readylaunch/signup.js', ['jquery'], MEPR_VERSION, true); + wp_enqueue_script('alpinejs', MEPR_JS_URL . '/vendor/alpine.min.js', [], MEPR_VERSION, true); + + wp_localize_script( + 'mepr-signupjs', + 'MeprProTemplateSignup', + [ + 'spc_enabled' => true, + ] + ); + } - if ( 'pricing' == $page ) { - wp_enqueue_style( 'mp-pro-pricing', MEPR_CSS_URL . '/readylaunch/pricing.css', null, MEPR_VERSION ); - wp_enqueue_script( 'mepr-pro-pricing', MEPR_JS_URL . '/readylaunch/pricing.js', array( 'jquery' ), MEPR_VERSION, true ); - wp_enqueue_script( 'alpinejs', MEPR_JS_URL . '/vendor/alpine.min.js', array(), MEPR_VERSION, true ); + if ('thankyou' == $page) { + wp_enqueue_style('mp-pro-checkout', MEPR_CSS_URL . '/readylaunch/checkout.css', null, MEPR_VERSION); + } } - if ( 'checkout' == $page ) { - wp_enqueue_style( 'mp-pro-checkout', MEPR_CSS_URL . '/readylaunch/checkout.css', null, MEPR_VERSION ); - wp_enqueue_script( 'mepr-signupjs', MEPR_JS_URL . '/readylaunch/signup.js', array( 'jquery' ), MEPR_VERSION, true ); - wp_enqueue_script( 'alpinejs', MEPR_JS_URL . '/vendor/alpine.min.js', array(), MEPR_VERSION, true ); - - wp_localize_script( - 'mepr-signupjs', - 'MeprProTemplateSignup', - array( - 'spc_enabled' => true, - ) - ); + public function add_view_path_for_slug($paths, $slug, $allowed_slugs = []) + { + if (in_array($slug, $allowed_slugs) || empty($allowed_slugs)) { + array_splice($paths, 1, 0, MEPR_PATH . '/app/views/readylaunch'); + } + return $paths; } - if ( 'thankyou' == $page ) { - wp_enqueue_style( 'mp-pro-checkout', MEPR_CSS_URL . '/readylaunch/checkout.css', null, MEPR_VERSION ); - } - } + /** + * Only remove admin bar when on readylaunch pro templates + * + * @return boolean + */ + public function remove_admin_bar($show) + { + if ( + self::template_enabled('pricing') || + self::template_enabled('login') || + self::template_enabled('account') || + self::template_enabled('checkout') || + self::template_enabled('thankyou') + ) { // full page templates + $show = false; + } - public function add_view_path_for_slug( $paths, $slug, $allowed_slugs = array() ) { - if ( in_array( $slug, $allowed_slugs ) || empty( $allowed_slugs ) ) { - array_splice( $paths, 1, 0, MEPR_PATH . '/app/views/readylaunch' ); - } - return $paths; - } - - /** - * Only remove admin bar when on readylaunch pro templates - * - * @return bool - */ - public function remove_admin_bar( $show ){ - if ( - self::template_enabled( 'pricing' ) || - self::template_enabled( 'login' ) || - self::template_enabled( 'account' ) || - self::template_enabled( 'checkout' ) || - self::template_enabled( 'thankyou' ) - ) { // full page templates - $show = false; + return $show; } - return $show; - } - - /** - * Change the cant purchase message template - * - * @param string $str purchase message - * @return string - */ - public function cant_purchase_message( $str ){ - $errors[] = $str; - - // Is ReadyLaunch™️ checkout template is enabled? - if( self::template_enabled('checkout') ) { - $str = MeprView::get_string('/readylaunch/shared/errors', get_defined_vars()); - } + /** + * Change the cant purchase message template + * + * @param string $str purchase message + * @return string + */ + public function cant_purchase_message($str) + { + $errors[] = $str; - return '
    ' . $str . '
    '; - } - - /** - * Checks if we should override the page template - * - * @param string $template template name. - * @param MeprOptions $options MeprOptions object. - * @return boolean - */ - public static function template_enabled( $template ) { - global $post; - global $wp_query; - - $page_name = $template . '_page_id'; - $attribute_name = 'design_enable_' . $template . '_template'; - $options = MeprOptions::fetch(); - - if ( 'pricing' === $template ) { - return isset( $post ) && - is_a( $post, 'WP_Post' ) && - $post->post_type == MeprGroup::$cpt && - isset( $options->$attribute_name ) && - filter_var( $options->$attribute_name, FILTER_VALIDATE_BOOLEAN ); - } + // Is ReadyLaunch™️ checkout template is enabled? + if (self::template_enabled('checkout')) { + $str = MeprView::get_string('/readylaunch/shared/errors', get_defined_vars()); + } - if ( 'checkout' === $template ) { - return isset( $options->$attribute_name ) && - filter_var( $options->$attribute_name, FILTER_VALIDATE_BOOLEAN ) && - ( isset( $post ) && is_a( $post, 'WP_Post' ) && is_singular( MeprProduct::$cpt ) ); + return '
    ' . $str . '
    '; } - return isset( $wp_query ) && - isset( $options->$page_name ) && - is_page( $options->$page_name ) && - isset( $options->$attribute_name ) && - filter_var( $options->$attribute_name, FILTER_VALIDATE_BOOLEAN ); - } - - /** - * AJAX: Get the editable field HTML - * - * @return void. - */ - public function account_profile_editable_fields() { - $user = MeprUtils::get_currentuserinfo(); - $field_key = isset( $_POST['field'] ) ? wp_unslash( sanitize_text_field( $_POST['field'] ) ) : ''; - - $custom_fields = MeprUsersHelper::get_custom_fields(); - - $key = array_search( $field_key, array_column( $custom_fields, 'field_key' ) ); - $field = $custom_fields[ $key ]; - $value = $user ? get_user_meta( $user->ID, $field->field_key, true ) : ''; - - ob_start(); - echo '
    '; - echo MeprUsersHelper::render_custom_field( $field, $value ); - $content = ob_get_clean(); - - wp_send_json_success( $content ); - } - - public function validate_account_fields($errors, $user, $field_key ){ - $mepr_options = MeprOptions::fetch(); - - $errors = MeprUsersCtrl::validate_extra_profile_fields( null, true, $user, false, false, $field_key ); - - // validate first name and last name - if(isset($_POST['first_name']) || isset($_POST['last_name'])){ - if($mepr_options->require_fname_lname && (empty($_POST['first_name']) || empty($_POST['last_name']))) { - $errors[] = __('You must enter both your First and Last name', 'memberpress'); - } - } + /** + * Checks if we should override the page template + * + * @param string $template template name. + * @param MeprOptions $options MeprOptions object. + * @return boolean + */ + public static function template_enabled($template) + { + global $post; + global $wp_query; + + $page_name = $template . '_page_id'; + $attribute_name = 'design_enable_' . $template . '_template'; + $options = MeprOptions::fetch(); + + if ('pricing' === $template) { + return isset($post) && + is_a($post, 'WP_Post') && + $post->post_type == MeprGroup::$cpt && + isset($options->$attribute_name) && + filter_var($options->$attribute_name, FILTER_VALIDATE_BOOLEAN); + } - if(isset($_POST['user_email'])){ - if(empty($_POST['user_email']) || !is_email(stripslashes($_POST['user_email']))) { - $errors[] = __('You must enter a valid email address', 'memberpress'); - } + if ('checkout' === $template) { + return isset($options->$attribute_name) && + filter_var($options->$attribute_name, FILTER_VALIDATE_BOOLEAN) && + ( isset($post) && is_a($post, 'WP_Post') && is_singular(MeprProduct::$cpt) ); + } - //Old email is not the same as the new, so let's make sure no else has it - // $user = MeprUtils::get_currentuserinfo(); //Old user info is here since we haven't stored the new stuff yet - if($user !== false && $user->user_email != stripslashes($_POST['user_email']) && email_exists(stripslashes($_POST['user_email']))) { - $errors[] = __('This email is already in use by another member', 'memberpress'); - } + return isset($wp_query) && + isset($options->$page_name) && + is_page($options->$page_name) && + isset($options->$attribute_name) && + filter_var($options->$attribute_name, FILTER_VALIDATE_BOOLEAN); } - return $errors; - } + /** + * AJAX: Get the editable field HTML + * + * @return void. + */ + public function account_profile_editable_fields() + { + $user = MeprUtils::get_currentuserinfo(); + $field_key = isset($_POST['field']) ? wp_unslash(sanitize_text_field($_POST['field'])) : ''; + + $custom_fields = MeprUsersHelper::get_custom_fields(); + + $key = array_search($field_key, array_column($custom_fields, 'field_key')); + $field = $custom_fields[ $key ]; + $value = $user ? get_user_meta($user->ID, $field->field_key, true) : ''; - public function load_more_subscriptions() { - // Check for nonce security! - if ( isset( $_POST['nonce'] ) && ! wp_verify_nonce( $_POST['nonce'], 'mepr_account_update' ) ) { - die( 'Busted!' ); + ob_start(); + echo '
    '; + echo MeprUsersHelper::render_custom_field($field, $value); + $content = ob_get_clean(); + + wp_send_json_success($content); } - $count = isset( $_POST['count'] ) ? absint( wp_unslash( $_POST['count'] ) ) : 1; - $acct_ctrl = MeprCtrlFactory::fetch( 'account' ); + public function validate_account_fields($errors, $user, $field_key) + { + $mepr_options = MeprOptions::fetch(); + + $errors = MeprUsersCtrl::validate_extra_profile_fields(null, true, $user, false, false, $field_key); - ob_start(); + // validate first name and last name + if (isset($_POST['first_name']) || isset($_POST['last_name'])) { + if ($mepr_options->require_fname_lname && (empty($_POST['first_name']) || empty($_POST['last_name']))) { + $errors[] = __('You must enter both your First and Last name', 'memberpress'); + } + } - $acct_ctrl->subscriptions( - '', - array(), - array( - 'mode' => 'readylaunch', - 'count' => $count, - ) - ); + if (isset($_POST['user_email'])) { + if (empty($_POST['user_email']) || !is_email(stripslashes($_POST['user_email']))) { + $errors[] = __('You must enter a valid email address', 'memberpress'); + } - $content = ob_get_clean(); - wp_send_json_success( $content ); - } + // Old email is not the same as the new, so let's make sure no else has it + // $user = MeprUtils::get_currentuserinfo(); //Old user info is here since we haven't stored the new stuff yet + if ($user !== false && $user->user_email != stripslashes($_POST['user_email']) && email_exists(stripslashes($_POST['user_email']))) { + $errors[] = __('This email is already in use by another member', 'memberpress'); + } + } - public function load_more_payments() { - // Check for nonce security! - if ( isset( $_POST['nonce'] ) && ! wp_verify_nonce( $_POST['nonce'], 'mepr_account_update' ) ) { - die( 'Busted!' ); + return $errors; } - $count = isset( $_POST['count'] ) ? absint( wp_unslash( $_POST['count'] ) ) : 1; - $acct_ctrl = MeprCtrlFactory::fetch( 'account' ); + public function load_more_subscriptions() + { + // Check for nonce security! + if (isset($_POST['nonce']) && ! wp_verify_nonce($_POST['nonce'], 'mepr_account_update')) { + die('Busted!'); + } - ob_start(); - $acct_ctrl->payments( - array( - 'mode' => 'readylaunch', - 'count' => $count, - ) - ); + $count = isset($_POST['count']) ? absint(wp_unslash($_POST['count'])) : 1; + $acct_ctrl = MeprCtrlFactory::fetch('account'); - $content = ob_get_clean(); - wp_send_json_success( $content ); - } + ob_start(); - public function placeholders_to_address_fields( $fields ) { - foreach ( $fields as $key => $field ) { - $fields[ $key ]->placeholder = $field->field_name; - } + $acct_ctrl->subscriptions( + '', + [], + [ + 'mode' => 'readylaunch', + 'count' => $count, + ] + ); - return $fields; - } - - public static function theme_style() { - $mepr_options = MeprOptions::fetch(); - - $primary_color = ! empty( $mepr_options->design_primary_color ) ? $mepr_options->design_primary_color : '#06429e'; - $text_color = self::getContrastColor( $primary_color ); - $current_screen = is_admin() ? get_current_screen() : ''; - - $is_block_editor = ( - isset( $current_screen ) && ! empty( $current_screen ) && - method_exists( $current_screen, 'is_block_editor' ) && - $current_screen->is_block_editor() - ); - - if ( - self::template_enabled( 'pricing' ) || - self::template_enabled( 'login' ) || - self::template_enabled( 'account' ) || - self::template_enabled( 'checkout' ) || - self::template_enabled( 'thankyou' ) || - $is_block_editor - ) { // full page templates - $html = ''; - - echo $html; + $content = ob_get_clean(); + wp_send_json_success($content); } - if ( is_singular() && has_block( 'memberpress/pro-account-tabs' ) ) { - $html = ''; + public function load_more_payments() + { + // Check for nonce security! + if (isset($_POST['nonce']) && ! wp_verify_nonce($_POST['nonce'], 'mepr_account_update')) { + die('Busted!'); + } + + $count = isset($_POST['count']) ? absint(wp_unslash($_POST['count'])) : 1; + $acct_ctrl = MeprCtrlFactory::fetch('account'); - echo $html; + ob_start(); + $acct_ctrl->payments( + [ + 'mode' => 'readylaunch', + 'count' => $count, + ] + ); + + $content = ob_get_clean(); + wp_send_json_success($content); } - } - public static function getContrastColor( $hexColor ) { - $hexColor = trim( $hexColor ); - $tmp_hexColor = trim( $hexColor, '#' ); - if( ! ctype_xdigit( $tmp_hexColor ) ) { // Validate HEX code. - $hexColor = '#FFFFFF'; // Fallback to white color. + public function placeholders_to_address_fields($fields) + { + foreach ($fields as $key => $field) { + $fields[ $key ]->placeholder = $field->field_name; + } + + return $fields; } - // hexColor RGB - $R1 = hexdec( substr( $hexColor, 1, 2 ) ); - $G1 = hexdec( substr( $hexColor, 3, 2 ) ); - $B1 = hexdec( substr( $hexColor, 5, 2 ) ); - - // Black RGB - $blackColor = '#000000'; - $R2BlackColor = hexdec( substr( $blackColor, 1, 2 ) ); - $G2BlackColor = hexdec( substr( $blackColor, 3, 2 ) ); - $B2BlackColor = hexdec( substr( $blackColor, 5, 2 ) ); - - // Calc contrast ratio - $L1 = 0.2126 * pow( $R1 / 255, 2.2 ) + - 0.7152 * pow( $G1 / 255, 2.2 ) + - 0.0722 * pow( $B1 / 255, 2.2 ); - - $L2 = 0.2126 * pow( $R2BlackColor / 255, 2.2 ) + - 0.7152 * pow( $G2BlackColor / 255, 2.2 ) + - 0.0722 * pow( $B2BlackColor / 255, 2.2 ); - - $contrastRatio = 0; - if ( $L1 > $L2 ) { - $contrastRatio = (int) ( ( $L1 + 0.05 ) / ( $L2 + 0.05 ) ); - } else { - $contrastRatio = (int) ( ( $L2 + 0.05 ) / ( $L1 + 0.05 ) ); + public static function theme_style() + { + $mepr_options = MeprOptions::fetch(); + + $primary_color = ! empty($mepr_options->design_primary_color) ? $mepr_options->design_primary_color : '#06429e'; + $text_color = self::getContrastColor($primary_color); + $current_screen = is_admin() ? get_current_screen() : ''; + + $is_block_editor = ( + isset($current_screen) && ! empty($current_screen) && + method_exists($current_screen, 'is_block_editor') && + $current_screen->is_block_editor() + ); + + if ( + self::template_enabled('pricing') || + self::template_enabled('login') || + self::template_enabled('account') || + self::template_enabled('checkout') || + self::template_enabled('thankyou') || + $is_block_editor + ) { // full page templates + $html = ''; + + echo $html; + } + + if (is_singular() && has_block('memberpress/pro-account-tabs')) { + $html = ''; + + echo $html; + } } - // If contrast is more than 5, return black color - if ( $contrastRatio > 5 ) { - return '#000000'; - } else { - // if not, return white color. - return '#FFFFFF'; + public static function getContrastColor($hexColor) + { + $hexColor = trim($hexColor); + $tmp_hexColor = trim($hexColor, '#'); + if (! ctype_xdigit($tmp_hexColor)) { // Validate HEX code. + $hexColor = '#FFFFFF'; // Fallback to white color. + } + + // hexColor RGB + $R1 = hexdec(substr($hexColor, 1, 2)); + $G1 = hexdec(substr($hexColor, 3, 2)); + $B1 = hexdec(substr($hexColor, 5, 2)); + + // Black RGB + $blackColor = '#000000'; + $R2BlackColor = hexdec(substr($blackColor, 1, 2)); + $G2BlackColor = hexdec(substr($blackColor, 3, 2)); + $B2BlackColor = hexdec(substr($blackColor, 5, 2)); + + // Calc contrast ratio + $L1 = 0.2126 * pow($R1 / 255, 2.2) + + 0.7152 * pow($G1 / 255, 2.2) + + 0.0722 * pow($B1 / 255, 2.2); + + $L2 = 0.2126 * pow($R2BlackColor / 255, 2.2) + + 0.7152 * pow($G2BlackColor / 255, 2.2) + + 0.0722 * pow($B2BlackColor / 255, 2.2); + + $contrastRatio = 0; + if ($L1 > $L2) { + $contrastRatio = (int) ( ( $L1 + 0.05 ) / ( $L2 + 0.05 ) ); + } else { + $contrastRatio = (int) ( ( $L2 + 0.05 ) / ( $L1 + 0.05 ) ); + } + + // If contrast is more than 5, return black color + if ($contrastRatio > 5) { + return '#000000'; + } else { + // if not, return white color. + return '#FFFFFF'; + } } - } - - public static function hexToRgb( $hex, $alpha = false ) { - $hex = str_replace( '#', '', $hex ); - $length = strlen( $hex ); - $rgb['r'] = hexdec( $length == 6 ? substr( $hex, 0, 2 ) : ( $length == 3 ? str_repeat( substr( $hex, 0, 1 ), 2 ) : 0 ) ); - $rgb['g'] = hexdec( $length == 6 ? substr( $hex, 2, 2 ) : ( $length == 3 ? str_repeat( substr( $hex, 1, 1 ), 2 ) : 0 ) ); - $rgb['b'] = hexdec( $length == 6 ? substr( $hex, 4, 2 ) : ( $length == 3 ? str_repeat( substr( $hex, 2, 1 ), 2 ) : 0 ) ); - if ( $alpha ) { - $rgb['a'] = $alpha; + + public static function hexToRgb($hex, $alpha = false) + { + $hex = str_replace('#', '', $hex); + $length = strlen($hex); + $rgb['r'] = hexdec($length == 6 ? substr($hex, 0, 2) : ( $length == 3 ? str_repeat(substr($hex, 0, 1), 2) : 0 )); + $rgb['g'] = hexdec($length == 6 ? substr($hex, 2, 2) : ( $length == 3 ? str_repeat(substr($hex, 1, 1), 2) : 0 )); + $rgb['b'] = hexdec($length == 6 ? substr($hex, 4, 2) : ( $length == 3 ? str_repeat(substr($hex, 2, 1), 2) : 0 )); + if ($alpha) { + $rgb['a'] = $alpha; + } + return implode(',', $rgb); } - return implode( ',', $rgb ); - } } //End class diff --git a/app/controllers/MeprRemindersCtrl.php b/app/controllers/MeprRemindersCtrl.php index 5ffec23..ea393a2 100644 --- a/app/controllers/MeprRemindersCtrl.php +++ b/app/controllers/MeprRemindersCtrl.php @@ -1,539 +1,594 @@ event_actions as $e) { - add_action($e, array($this, 'send_reminders')); - } - - // Set up cron schedules - add_filter('cron_schedules', array($this, 'intervals')); - add_action('mepr_reminders_worker', array($this, 'worker')); - add_action('plugins_loaded', array($this, 'schedule_reminders')); - } - else { - $this->unschedule_reminders(); - } - - // Clean up crons and possibly other stuff when a reminder is deleted or trashed - add_action('before_delete_post', array($this, 'delete')); - add_action('wp_trash_post', array($this, 'delete')); - // Add some cols - add_action('manage_posts_custom_column', array( $this, 'custom_columns'), 10, 2); - add_filter('manage_edit-mp-reminder_columns', array( $this, 'columns')); - } +if (!defined('ABSPATH')) { + die('You are not allowed to call this page directly.'); +} - public function schedule_reminder($id) { - //Stop zombie cron jobs in their tracks here - $reminder = $this->get_valid_reminder($id); +class MeprRemindersCtrl extends MeprCptCtrl +{ + public function load_hooks() + { + add_action('admin_enqueue_scripts', [$this, 'enqueue_scripts']); + add_action('save_post', [$this, 'save_postdata']); + + $disable_reminder_crons = get_option('mepr_disable_reminder_crons'); + if (!$disable_reminder_crons) { + $r = new MeprReminder(); + foreach ($r->event_actions as $e) { + add_action($e, [$this, 'send_reminders']); + } - if($reminder===false) { - $this->unschedule_reminder($id); - return; - } + // Set up cron schedules + add_filter('cron_schedules', [$this, 'intervals']); + add_action('mepr_reminders_worker', [$this, 'worker']); + add_action('plugins_loaded', [$this, 'schedule_reminders']); + } else { + $this->unschedule_reminders(); + } - $args = array($id); + // Clean up crons and possibly other stuff when a reminder is deleted or trashed + add_action('before_delete_post', [$this, 'delete']); + add_action('wp_trash_post', [$this, 'delete']); - if(!wp_next_scheduled('mepr_reminders_worker', $args)) { - wp_schedule_event( - time(), - 'mepr_reminders_worker_interval', - 'mepr_reminders_worker', - $args - ); + // Add some cols + add_action('manage_posts_custom_column', [$this, 'custom_columns'], 10, 2); + add_filter('manage_edit-mp-reminder_columns', [$this, 'columns']); } - } - - public function schedule_reminders() { - $reminders = MeprCptModel::all('MeprReminder'); - if(!empty($reminders)) { - foreach($reminders as $r) { - $vr = $this->get_valid_reminder($r->ID); + public function schedule_reminder($id) + { + // Stop zombie cron jobs in their tracks here + $reminder = $this->get_valid_reminder($id); - if($vr!==false) { - $this->schedule_reminder($r->ID); + if ($reminder === false) { + $this->unschedule_reminder($id); + return; } - else { - $this->unschedule_reminder($r->ID); + + $args = [$id]; + + if (!wp_next_scheduled('mepr_reminders_worker', $args)) { + wp_schedule_event( + time(), + 'mepr_reminders_worker_interval', + 'mepr_reminders_worker', + $args + ); } - } } - } - public function unschedule_reminder($id) { - $args = array($id); - $timestamp = wp_next_scheduled('mepr_reminders_worker', $args); - wp_unschedule_event($timestamp, 'mepr_reminders_worker', $args); - } + public function schedule_reminders() + { + $reminders = MeprCptModel::all('MeprReminder'); - public function unschedule_reminders() { - $reminders = MeprCptModel::all('MeprReminder'); + if (!empty($reminders)) { + foreach ($reminders as $r) { + $vr = $this->get_valid_reminder($r->ID); - if(!empty($reminders)) { - foreach($reminders as $r) { - $this->unschedule_reminder($r->ID); - } - } - } - - public function columns($columns) { - $columns = array( - 'cb' => '', - 'title' => __('Reminder Title', 'memberpress'), - 'send_to_admin' => __('Send Notice to Admin', 'memberpress'), - 'send_to_member' => __('Send Reminder to Member', 'memberpress'), - 'reminder_products' => __('Memberships', 'memberpress') - ); - - return $columns; - } - - public function custom_columns($column, $post_id) { - $reminder = $this->get_valid_reminder($post_id); - - if($reminder!==false) { - switch( $reminder->trigger_event ) { - case 'sub-expires': - $uclass = 'MeprUserSubExpiresReminderEmail'; - $aclass = 'MeprAdminSubExpiresReminderEmail'; - break; - case 'sub-renews': - $uclass = 'MeprUserSubRenewsReminderEmail'; - $aclass = 'MeprAdminSubRenewsReminderEmail'; - break; - case 'signup-abandoned': - $uclass = 'MeprUserSignupAbandonedReminderEmail'; - $aclass = 'MeprAdminSignupAbandonedReminderEmail'; - break; - case 'member-signup': - $uclass = 'MeprUserMemberSignupReminderEmail'; - $aclass = 'MeprAdminMemberSignupReminderEmail'; - break; - case 'cc-expires': - $uclass = 'MeprUserCcExpiresReminderEmail'; - $aclass = 'MeprAdminCcExpiresReminderEmail'; - break; - case 'sub-trial-ends': - $uclass = 'MeprUserSubTrialEndsReminderEmail'; - $aclass = 'MeprAdminSubTrialEndsReminderEmail'; - break; - default: - echo ''; - return; - } - - // TODO: Yah, not pretty but works ... change at some point - $cval = '%s'; - - if("send_to_admin" == $column) { - (int)$reminder->emails[$aclass]['enabled'] > 0 ? printf( $cval, 'limegreen', '✔︎' ) : printf( $cval, 'red', '✖︎' ); - } - elseif("send_to_member" == $column) { - (int)$reminder->emails[$uclass]['enabled'] > 0 ? printf( $cval, 'limegreen', '✔︎' ) : printf( $cval, 'red', '✖︎' ); - } - elseif("reminder_products" == $column) { - echo implode(', ', $reminder->get_formatted_products()); - } + if ($vr !== false) { + $this->schedule_reminder($r->ID); + } else { + $this->unschedule_reminder($r->ID); + } + } + } } - } - - public function register_post_type() { - $this->cpt = (object)array( - 'slug' => MeprReminder::$cpt, - 'config' => array( - 'labels' => array( - 'name' => __('Reminders', 'memberpress'), - 'singular_name' => __('Reminder', 'memberpress'), - 'add_new' => __('Add New', 'memberpress'), - 'add_new_item' => __('Add New Reminder', 'memberpress'), - 'edit_item' => __('Edit Reminder', 'memberpress'), - 'new_item' => __('New Reminder', 'memberpress'), - 'view_item' => __('View Reminder', 'memberpress'), - 'search_items' => __('Search Reminders', 'memberpress'), - 'not_found' => __('No Reminders found', 'memberpress'), - 'not_found_in_trash' => __('No Reminders found in Trash', 'memberpress'), - 'parent_item_colon' => __('Parent Reminder:', 'memberpress') - ), - 'public' => false, - 'show_ui' => true, //MeprUpdateCtrl::is_activated(), - 'show_in_menu' => 'memberpress', - 'capability_type' => 'post', - 'hierarchical' => false, - 'register_meta_box_cb' => array( $this, 'add_meta_boxes' ), - 'rewrite' => false, - 'supports' => array('none', 'title') - ) - ); - register_post_type( $this->cpt->slug, $this->cpt->config ); - } - - public function add_meta_boxes() { - add_meta_box( "mp-reminder-trigger", __("Trigger", "memberpress"), - array( $this, 'trigger_meta_box' ), MeprReminder::$cpt, "normal" ); - add_meta_box( "mp-reminder-emails", __("Emails", "memberpress"), - array( $this, 'emails_meta_box' ), MeprReminder::$cpt, "normal" ); - } - - public function enqueue_scripts($hook) { - global $current_screen; - - $wp_scripts = new WP_Scripts(); - $ui = $wp_scripts->query('jquery-ui-core'); - $url = "//ajax.googleapis.com/ajax/libs/jqueryui/{$ui->ver}/themes/smoothness/jquery-ui.css"; - - if($current_screen->post_type == MeprReminder::$cpt) + + public function unschedule_reminder($id) { - wp_enqueue_style('mepr-jquery-ui-smoothness', $url); - wp_dequeue_script('autosave'); //Disable auto-saving - wp_enqueue_style('mepr-emails-css', MEPR_CSS_URL.'/admin-emails.css', array(), MEPR_VERSION); - $email_locals = array( - 'set_email_defaults_nonce' => wp_create_nonce('set_email_defaults'), - 'send_test_email_nonce' => wp_create_nonce('send_test_email'), - ); - wp_enqueue_script('mepr-emails-js', MEPR_JS_URL.'/admin_emails.js', array('jquery'), MEPR_VERSION); - wp_localize_script('mepr-emails-js', 'MeprEmail', $email_locals); - wp_enqueue_style('mepr-reminders-css', MEPR_CSS_URL.'/admin-reminders.css', array('mepr-emails-css'), MEPR_VERSION); - wp_enqueue_script('mepr-reminders-js', MEPR_JS_URL.'/admin_reminders.js', array('jquery','jquery-ui-spinner','mepr-emails-js'), MEPR_VERSION); + $args = [$id]; + $timestamp = wp_next_scheduled('mepr_reminders_worker', $args); + wp_unschedule_event($timestamp, 'mepr_reminders_worker', $args); } - } - public function trigger_meta_box() { - global $post_id; - - $reminder = new MeprReminder($post_id); - $nonce = wp_create_nonce( md5(MeprReminder::$nonce_str.wp_salt()) ); - - MeprView::render("/admin/reminders/trigger", get_defined_vars()); - } + public function unschedule_reminders() + { + $reminders = MeprCptModel::all('MeprReminder'); - public function emails_meta_box() { - global $post_id; + if (!empty($reminders)) { + foreach ($reminders as $r) { + $this->unschedule_reminder($r->ID); + } + } + } - $reminder = new MeprReminder($post_id); + public function columns($columns) + { + $columns = [ + 'cb' => '', + 'title' => __('Reminder Title', 'memberpress'), + 'send_to_admin' => __('Send Notice to Admin', 'memberpress'), + 'send_to_member' => __('Send Reminder to Member', 'memberpress'), + 'reminder_products' => __('Memberships', 'memberpress'), + ]; + + return $columns; + } - MeprView::render("/admin/reminders/emails", get_defined_vars()); - } + public function custom_columns($column, $post_id) + { + $reminder = $this->get_valid_reminder($post_id); + + if ($reminder !== false) { + switch ($reminder->trigger_event) { + case 'sub-expires': + $uclass = 'MeprUserSubExpiresReminderEmail'; + $aclass = 'MeprAdminSubExpiresReminderEmail'; + break; + case 'sub-renews': + $uclass = 'MeprUserSubRenewsReminderEmail'; + $aclass = 'MeprAdminSubRenewsReminderEmail'; + break; + case 'signup-abandoned': + $uclass = 'MeprUserSignupAbandonedReminderEmail'; + $aclass = 'MeprAdminSignupAbandonedReminderEmail'; + break; + case 'member-signup': + $uclass = 'MeprUserMemberSignupReminderEmail'; + $aclass = 'MeprAdminMemberSignupReminderEmail'; + break; + case 'cc-expires': + $uclass = 'MeprUserCcExpiresReminderEmail'; + $aclass = 'MeprAdminCcExpiresReminderEmail'; + break; + case 'sub-trial-ends': + $uclass = 'MeprUserSubTrialEndsReminderEmail'; + $aclass = 'MeprAdminSubTrialEndsReminderEmail'; + break; + default: + echo ''; + return; + } - public function save_postdata($post_id) { - $post = get_post($post_id); + // TODO: Yah, not pretty but works ... change at some point + $cval = '%s'; - if(!wp_verify_nonce( (isset($_POST[MeprReminder::$nonce_str]))?$_POST[MeprReminder::$nonce_str]:'', - md5(MeprReminder::$nonce_str.wp_salt()) )) { - return $post_id; //Nonce prevents meta data from being wiped on move to trash + if ('send_to_admin' == $column) { + (int)$reminder->emails[$aclass]['enabled'] > 0 ? printf($cval, 'limegreen', '✔︎') : printf($cval, 'red', '✖︎'); + } elseif ('send_to_member' == $column) { + (int)$reminder->emails[$uclass]['enabled'] > 0 ? printf($cval, 'limegreen', '✔︎') : printf($cval, 'red', '✖︎'); + } elseif ('reminder_products' == $column) { + echo implode(', ', $reminder->get_formatted_products()); + } + } } - if(defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) { return $post_id; } - if(defined('DOING_AJAX')) { return; } + public function register_post_type() + { + $this->cpt = (object)[ + 'slug' => MeprReminder::$cpt, + 'config' => [ + 'labels' => [ + 'name' => __('Reminders', 'memberpress'), + 'singular_name' => __('Reminder', 'memberpress'), + 'add_new' => __('Add New', 'memberpress'), + 'add_new_item' => __('Add New Reminder', 'memberpress'), + 'edit_item' => __('Edit Reminder', 'memberpress'), + 'new_item' => __('New Reminder', 'memberpress'), + 'view_item' => __('View Reminder', 'memberpress'), + 'search_items' => __('Search Reminders', 'memberpress'), + 'not_found' => __('No Reminders found', 'memberpress'), + 'not_found_in_trash' => __('No Reminders found in Trash', 'memberpress'), + 'parent_item_colon' => __('Parent Reminder:', 'memberpress'), + ], + 'public' => false, + 'show_ui' => true, // MeprUpdateCtrl::is_activated(), + 'show_in_menu' => 'memberpress', + 'capability_type' => 'post', + 'hierarchical' => false, + 'register_meta_box_cb' => [$this, 'add_meta_boxes'], + 'rewrite' => false, + 'supports' => ['none', 'title'], + ], + ]; + register_post_type($this->cpt->slug, $this->cpt->config); + } - if(!empty($post) && $post->post_type == MeprReminder::$cpt) { - $reminder = new MeprReminder($post_id); + public function add_meta_boxes() + { + add_meta_box( + 'mp-reminder-trigger', + __('Trigger', 'memberpress'), + [$this, 'trigger_meta_box'], + MeprReminder::$cpt, + 'normal' + ); + add_meta_box( + 'mp-reminder-emails', + __('Emails', 'memberpress'), + [$this, 'emails_meta_box'], + MeprReminder::$cpt, + 'normal' + ); + } - $reminder->trigger_length = sanitize_text_field($_POST[MeprReminder::$trigger_length_str]); - $reminder->trigger_interval = sanitize_text_field($_POST[MeprReminder::$trigger_interval_str]); - $reminder->trigger_timing = sanitize_text_field($_POST[MeprReminder::$trigger_timing_str]); - $reminder->trigger_event = sanitize_text_field($_POST[MeprReminder::$trigger_event_str]); - $reminder->filter_products = false; - $reminder->products = array(); + public function enqueue_scripts($hook) + { + global $current_screen; + + $wp_scripts = new WP_Scripts(); + $ui = $wp_scripts->query('jquery-ui-core'); + $url = "//ajax.googleapis.com/ajax/libs/jqueryui/{$ui->ver}/themes/smoothness/jquery-ui.css"; + + if ($current_screen->post_type == MeprReminder::$cpt) { + wp_enqueue_style('mepr-jquery-ui-smoothness', $url); + wp_dequeue_script('autosave'); // Disable auto-saving + wp_enqueue_style('mepr-emails-css', MEPR_CSS_URL . '/admin-emails.css', [], MEPR_VERSION); + $email_locals = [ + 'set_email_defaults_nonce' => wp_create_nonce('set_email_defaults'), + 'send_test_email_nonce' => wp_create_nonce('send_test_email'), + ]; + wp_enqueue_script('mepr-emails-js', MEPR_JS_URL . '/admin_emails.js', ['jquery'], MEPR_VERSION); + wp_localize_script('mepr-emails-js', 'MeprEmail', $email_locals); + wp_enqueue_style('mepr-reminders-css', MEPR_CSS_URL . '/admin-reminders.css', ['mepr-emails-css'], MEPR_VERSION); + wp_enqueue_script('mepr-reminders-js', MEPR_JS_URL . '/admin_reminders.js', ['jquery','jquery-ui-spinner','mepr-emails-js'], MEPR_VERSION); + } + } - //Override filter by products vars - if(isset($_POST[MeprReminder::$filter_products_str]) && !empty($_POST[MeprReminder::$products_str])) { - $reminder->filter_products = true; - $reminder->products = array_map('sanitize_text_field', $_POST[MeprReminder::$products_str]); - } + public function trigger_meta_box() + { + global $post_id; - // Notification Settings - $emails = array(); - foreach( $_POST[MeprReminder::$emails_str] as $email => $vals ) { - $emails[$email] = array( 'enabled' => isset($vals['enabled']), - 'use_template' => isset($vals['use_template']), - 'subject' => sanitize_text_field(wp_unslash($vals['subject'])), - 'body' => MeprUtils::maybe_wpautop(stripslashes($vals['body'])) - ); - } + $reminder = new MeprReminder($post_id); + $nonce = wp_create_nonce(md5(MeprReminder::$nonce_str . wp_salt())); - $reminder->emails = $emails; + MeprView::render('/admin/reminders/trigger', get_defined_vars()); + } - // Don't quite need this yet - //$reminder = $this->validate($reminder); + public function emails_meta_box() + { + global $post_id; - $reminder->store_meta(); // only storing metadata here + $reminder = new MeprReminder($post_id); - MeprHooks::do_action( 'mepr-reminder-save-meta', $reminder ); + MeprView::render('/admin/reminders/emails', get_defined_vars()); } - } - /** CRON SPECIFIC METHODS **/ - public function intervals( $schedules ) { - $schedules[ "mepr_reminders_worker_interval" ] = array( - 'interval' => MeprUtils::minutes(15), - 'display' => __( 'MemberPress Reminders Worker' , 'memberpress') - ); + public function save_postdata($post_id) + { + $post = get_post($post_id); + + if ( + !wp_verify_nonce( + (isset($_POST[MeprReminder::$nonce_str])) ? $_POST[MeprReminder::$nonce_str] : '', + md5(MeprReminder::$nonce_str . wp_salt()) + ) + ) { + return $post_id; // Nonce prevents meta data from being wiped on move to trash + } - return $schedules; - } + if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) { + return $post_id; + } + if (defined('DOING_AJAX')) { + return; + } - public function get_valid_reminder($id) { - // if the remider_id is empty then forget it - if(empty($id)) { return false; } + if (!empty($post) && $post->post_type == MeprReminder::$cpt) { + $reminder = new MeprReminder($post_id); - $post = get_post($id); + $reminder->trigger_length = sanitize_text_field($_POST[MeprReminder::$trigger_length_str]); + $reminder->trigger_interval = sanitize_text_field($_POST[MeprReminder::$trigger_interval_str]); + $reminder->trigger_timing = sanitize_text_field($_POST[MeprReminder::$trigger_timing_str]); + $reminder->trigger_event = sanitize_text_field($_POST[MeprReminder::$trigger_event_str]); + $reminder->filter_products = false; + $reminder->products = []; - // Post not found? fail - if(empty($post)) { return false; } + // Override filter by products vars + if (isset($_POST[MeprReminder::$filter_products_str]) && !empty($_POST[MeprReminder::$products_str])) { + $reminder->filter_products = true; + $reminder->products = array_map('sanitize_text_field', $_POST[MeprReminder::$products_str]); + } - // not the right post type? fail - if($post->post_type!==MeprReminder::$cpt) { return false; } + // Notification Settings + $emails = []; + foreach ($_POST[MeprReminder::$emails_str] as $email => $vals) { + $emails[$email] = [ + 'enabled' => isset($vals['enabled']), + 'use_template' => isset($vals['use_template']), + 'subject' => sanitize_text_field(wp_unslash($vals['subject'])), + 'body' => MeprUtils::maybe_wpautop(stripslashes($vals['body'])), + ]; + } - // not a published post? fail - if($post->post_status!=='publish') { return false; } + $reminder->emails = $emails; - $reminder = new MeprReminder($id); + // Don't quite need this yet + // $reminder = $this->validate($reminder); + $reminder->store_meta(); // only storing metadata here - // ID is empty? fail - if(empty($reminder->ID)) { return false; } + MeprHooks::do_action('mepr-reminder-save-meta', $reminder); + } + } - return $reminder; - } + /** + * CRON SPECIFIC METHODS + **/ + public function intervals($schedules) + { + $schedules[ 'mepr_reminders_worker_interval' ] = [ + 'interval' => MeprUtils::minutes(15), + 'display' => __('MemberPress Reminders Worker', 'memberpress'), + ]; - public function worker($reminder_id) { - $reminder = $this->get_valid_reminder($reminder_id); + return $schedules; + } - if($reminder !== false) { - @set_time_limit(0); // unlimited run time - $run_limit = MeprUtils::minutes(10); // limit to 10 minutes + public function get_valid_reminder($id) + { + // if the remider_id is empty then forget it + if (empty($id)) { + return false; + } - // Event name will be the same no matter what we're doing here - $event = "{$reminder->trigger_timing}-{$reminder->trigger_event}-reminder"; + $post = get_post($id); - while($this->run_time() < $run_limit) { - $args = $reminder_id; - $obj = null; + // Post not found? fail + if (empty($post)) { + return false; + } - switch($reminder->trigger_event) { - case 'sub-expires': - if(($txn = $reminder->get_next_expiring_txn())) { - $obj = new MeprTransaction($txn->id); // we need the actual model - } - break; - case 'sub-renews': - if ($reminder->trigger_timing == 'before') { - if (($txn = $reminder->get_next_renewing_txn())) { - $obj = new MeprTransaction($txn->id); // we need the actual model - } - } - else if ($reminder->trigger_timing == 'after') { - if (($txn = $reminder->get_renewed_txn())) { - $obj = new MeprTransaction($txn->id); // we need the actual model - } - } - break; - case 'member-signup': - if(($txn_id = $reminder->get_next_member_signup())) { - $obj = new MeprTransaction($txn_id); - } - break; - case 'signup-abandoned': - if(($txn_id = $reminder->get_next_abandoned_signup())) { - $obj = new MeprTransaction($txn_id); - } - break; - case 'cc-expires': - if(($sub_id = $reminder->get_next_expired_cc())) { - $obj = new MeprSubscription($sub_id); - $args = "{$reminder_id}|{$obj->cc_exp_month}|{$obj->cc_exp_year}"; - } - break; - case 'sub-trial-ends': - if ($reminder->trigger_timing == 'before') { - if (($sub_id = $reminder->get_next_trial_ends_subs())) { - $obj = new MeprSubscription($sub_id); - $args = "{$reminder_id}|{$obj->trial_days}"; - } - } - break; - default: - $this->unschedule_reminder($reminder_id); - break; + // not the right post type? fail + if ($post->post_type !== MeprReminder::$cpt) { + return false; } - if(isset($obj)) { - // We just catch the hooks from these events - MeprEvent::record($event, $obj, $args); + // not a published post? fail + if ($post->post_status !== 'publish') { + return false; } - else { - break; //break out of the while loop + + $reminder = new MeprReminder($id); + + // ID is empty? fail + if (empty($reminder->ID)) { + return false; } - } //End while + + return $reminder; } - } - private function run_time() { - static $start_time; + public function worker($reminder_id) + { + $reminder = $this->get_valid_reminder($reminder_id); + + if ($reminder !== false) { + @set_time_limit(0); // unlimited run time + $run_limit = MeprUtils::minutes(10); // limit to 10 minutes + + // Event name will be the same no matter what we're doing here + $event = "{$reminder->trigger_timing}-{$reminder->trigger_event}-reminder"; + + while ($this->run_time() < $run_limit) { + $args = $reminder_id; + $obj = null; + + switch ($reminder->trigger_event) { + case 'sub-expires': + if (($txn = $reminder->get_next_expiring_txn())) { + $obj = new MeprTransaction($txn->id); // we need the actual model + } + break; + case 'sub-renews': + if ($reminder->trigger_timing == 'before') { + if (($txn = $reminder->get_next_renewing_txn())) { + $obj = new MeprTransaction($txn->id); // we need the actual model + } + } elseif ($reminder->trigger_timing == 'after') { + if (($txn = $reminder->get_renewed_txn())) { + $obj = new MeprTransaction($txn->id); // we need the actual model + } + } + break; + case 'member-signup': + if (($txn_id = $reminder->get_next_member_signup())) { + $obj = new MeprTransaction($txn_id); + } + break; + case 'signup-abandoned': + if (($txn_id = $reminder->get_next_abandoned_signup())) { + $obj = new MeprTransaction($txn_id); + } + break; + case 'cc-expires': + if (($sub_id = $reminder->get_next_expired_cc())) { + $obj = new MeprSubscription($sub_id); + $args = "{$reminder_id}|{$obj->cc_exp_month}|{$obj->cc_exp_year}"; + } + break; + case 'sub-trial-ends': + if ($reminder->trigger_timing == 'before') { + if (($sub_id = $reminder->get_next_trial_ends_subs())) { + $obj = new MeprSubscription($sub_id); + $args = "{$reminder_id}|{$obj->trial_days}"; + } + } + break; + default: + $this->unschedule_reminder($reminder_id); + break; + } - if(!isset($start_time)) { - $start_time = time(); + if (isset($obj)) { + // We just catch the hooks from these events + MeprEvent::record($event, $obj, $args); + } else { + break; // break out of the while loop + } + } //End while + } } - return ( time() - $start_time ); - } + private function run_time() + { + static $start_time; - private function send_emails($usr, $uclass, $aclass, $params, $args) { - try { - $uemail = MeprEmailFactory::fetch( $uclass, 'MeprBaseReminderEmail', $args ); - $uemail->to = $usr->formatted_email(); - $uemail->send_if_enabled($params); + if (!isset($start_time)) { + $start_time = time(); + } - $aemail = MeprEmailFactory::fetch( $aclass, 'MeprBaseReminderEmail', $args ); - $aemail->send_if_enabled($params); + return ( time() - $start_time ); } - catch( Exception $e ) { - // Fail silently for now + + private function send_emails($usr, $uclass, $aclass, $params, $args) + { + try { + $uemail = MeprEmailFactory::fetch($uclass, 'MeprBaseReminderEmail', $args); + $uemail->to = $usr->formatted_email(); + $uemail->send_if_enabled($params); + + $aemail = MeprEmailFactory::fetch($aclass, 'MeprBaseReminderEmail', $args); + $aemail->send_if_enabled($params); + } catch (Exception $e) { + // Fail silently for now + } } - } - public function send_reminders($event) { - //Now that we support renewals on one-time purchases -- we need to make sure they don't get reminded of expirations - //if they have already renewed their one-time subscription again before the expiring sub reminder is sent out - $disable_email = false; //Do not send the emails if this gets set to true + public function send_reminders($event) + { + // Now that we support renewals on one-time purchases -- we need to make sure they don't get reminded of expirations + // if they have already renewed their one-time subscription again before the expiring sub reminder is sent out + $disable_email = false; // Do not send the emails if this gets set to true - if($event->evt_id_type == 'transactions' && ($txn = new MeprTransaction($event->evt_id))) { - //Do not send reminders to sub-accounts - if(isset($txn->parent_transaction_id) && $txn->parent_transaction_id > 0) { $disable_email = true; } + if ($event->evt_id_type == 'transactions' && ($txn = new MeprTransaction($event->evt_id))) { + // Do not send reminders to sub-accounts + if (isset($txn->parent_transaction_id) && $txn->parent_transaction_id > 0) { + $disable_email = true; + } - $usr = $txn->user(); - $prd = new MeprProduct($txn->product_id); - $reminder = $this->get_valid_reminder($event->args); + $usr = $txn->user(); + $prd = new MeprProduct($txn->product_id); + $reminder = $this->get_valid_reminder($event->args); + + if ($reminder === false) { + return; + } // fail silently if reminder is invalid + + $params = array_merge(MeprRemindersHelper::get_email_params($reminder), MeprTransactionsHelper::get_email_params($txn)); + + switch ($reminder->trigger_event) { + case 'sub-expires': + // Don't send a reminder if the user has already renewed either a one-time or an offline subscription + if ($reminder->trigger_timing == 'before') { // Handle when the reminder should go out before + $txn_count = count($usr->transactions_for_product($txn->product_id, false, true)); + + // txn_count > 1 works well for both renewals and offline subs actually because transactions_for_product + // should only ever return a count of currently active (payment type) transactions and no expired transactions + if ($txn_count > 1) { + $disable_email = true; + } + } else { // Handle when the reminder should go out after + // Don't send to folks if they have an active txn on this subscription (OR one in the + // products group) already yo + $active_subs = $usr->active_product_subscriptions('ids'); + $product = new MeprProduct($txn->product_id); + $grp = new MeprGroup($product->group_id); + + // If product is in a group with an upgrade path check for all memberships in that group + if ($product->group_id && $product->group_id > 0 && $grp->is_upgrade_path) { + foreach ($grp->products('ids') as $prd_id) { + if (in_array($prd_id, $active_subs, false)) { + $disable_email = true; + break; // Breack out of the loop once we find one. + } + } + } else { + // Just check this product + if (in_array($txn->product_id, $active_subs, false)) { + $disable_email = true; + } + } + } + + $uclass = 'MeprUserSubExpiresReminderEmail'; + $aclass = 'MeprAdminSubExpiresReminderEmail'; + break; + case 'sub-renews': + if ($reminder->trigger_timing == 'after') { + $sub = $txn->subscription(); + $txn_count = (isset($sub->txn_count) && $sub->txn_count) ? $sub->txn_count : 0; + if ($txn_count < 2 && ($sub->trial == false || ($sub->trial && $sub->trial_amount > 0.00))) { + $disable_email = true; + } + } + $uclass = 'MeprUserSubRenewsReminderEmail'; + $aclass = 'MeprAdminSubRenewsReminderEmail'; + break; + case 'signup-abandoned': + // Make sure the user is not active on another membership + $active_subs = $usr->active_product_subscriptions('ids'); + + if (!empty($active_subs)) { + $disable_email = true; + } + + $uclass = 'MeprUserSignupAbandonedReminderEmail'; + $aclass = 'MeprAdminSignupAbandonedReminderEmail'; + break; + case 'member-signup': + $uclass = 'MeprUserMemberSignupReminderEmail'; + $aclass = 'MeprAdminMemberSignupReminderEmail'; + break; + default: + $uclass = $aclass = ''; + } - if($reminder === false) { return; } // fail silently if reminder is invalid + $args = [['reminder_id' => $event->args]]; - $params = array_merge(MeprRemindersHelper::get_email_params($reminder), MeprTransactionsHelper::get_email_params($txn)); + $disable_email = MeprHooks::apply_filters("mepr-{$reminder->trigger_event}-reminder-disable", $disable_email, $reminder, $usr, $prd, $event); + if (!$disable_email) { + $this->send_emails($usr, $uclass, $aclass, $params, $args); + } + } elseif ($event->evt_id_type == 'subscriptions' && ($sub = new MeprSubscription($event->evt_id))) { + $usr = $sub->user(); + $prd = new MeprProduct($sub->product_id); + $reminder = $this->get_valid_reminder($event->args); - switch($reminder->trigger_event) { - case 'sub-expires': - //Don't send a reminder if the user has already renewed either a one-time or an offline subscription - if($reminder->trigger_timing == 'before') { //Handle when the reminder should go out before - $txn_count = count($usr->transactions_for_product($txn->product_id, false, true)); + if ($reminder === false) { + return; + } // fail silently if reminder is invalid - //txn_count > 1 works well for both renewals and offline subs actually because transactions_for_product - //should only ever return a count of currently active (payment type) transactions and no expired transactions - if($txn_count > 1) { - $disable_email = true; - } - } - else { //Handle when the reminder should go out after - //Don't send to folks if they have an active txn on this subscription (OR one in the - //products group) already yo - $active_subs = $usr->active_product_subscriptions('ids'); - $product = new MeprProduct($txn->product_id); - $grp = new MeprGroup($product->group_id); - - //If product is in a group with an upgrade path check for all memberships in that group - if($product->group_id && $product->group_id > 0 && $grp->is_upgrade_path) { - foreach ($grp->products('ids') as $prd_id) { - if(in_array($prd_id, $active_subs, false)) { - $disable_email = true; - break; //Breack out of the loop once we find one. - } - } - } else { - //Just check this product - if(in_array($txn->product_id, $active_subs, false)) { + $parents = get_user_meta($usr->ID, 'mpca_corporate_account_id'); + + if (count($parents) > 0) { $disable_email = true; - } + } //Do not email sub account users + + $params = array_merge( + MeprRemindersHelper::get_email_params($reminder), + MeprSubscriptionsHelper::get_email_params($sub) + ); + + switch ($reminder->trigger_event) { + case 'sub-trial-ends': + if ('before' == $reminder->trigger_timing) { + $uclass = 'MeprUserSubTrialEndsReminderEmail'; + $aclass = 'MeprAdminSubTrialEndsReminderEmail'; + } + break; + case 'cc-expires': + $uclass = 'MeprUserCcExpiresReminderEmail'; + $aclass = 'MeprAdminCcExpiresReminderEmail'; + break; + default: + $uclass = $aclass = ''; } - } - - $uclass = 'MeprUserSubExpiresReminderEmail'; - $aclass = 'MeprAdminSubExpiresReminderEmail'; - break; - case 'sub-renews': - if($reminder->trigger_timing == 'after') { - $sub = $txn->subscription(); - $txn_count = (isset($sub->txn_count) && $sub->txn_count) ? $sub->txn_count : 0; - if($txn_count < 2 && ($sub->trial == false || ($sub->trial && $sub->trial_amount > 0.00))) { - $disable_email = true; + + $args = [['reminder_id' => $reminder->ID]]; + + $disable_email = MeprHooks::apply_filters("mepr-{$reminder->trigger_event}-reminder-disable", $disable_email, $reminder, $usr, $prd, $event); + if (!$disable_email) { + $this->send_emails($usr, $uclass, $aclass, $params, $args); } - } - $uclass = 'MeprUserSubRenewsReminderEmail'; - $aclass = 'MeprAdminSubRenewsReminderEmail'; - break; - case 'signup-abandoned': - //Make sure the user is not active on another membership - $active_subs = $usr->active_product_subscriptions('ids'); - - if(!empty($active_subs)) { - $disable_email = true; - } - - $uclass = 'MeprUserSignupAbandonedReminderEmail'; - $aclass = 'MeprAdminSignupAbandonedReminderEmail'; - break; - case 'member-signup': - $uclass = 'MeprUserMemberSignupReminderEmail'; - $aclass = 'MeprAdminMemberSignupReminderEmail'; - break; - default: - $uclass=$aclass=''; - } - - $args = array(array('reminder_id'=>$event->args)); - - $disable_email = MeprHooks::apply_filters("mepr-{$reminder->trigger_event}-reminder-disable", $disable_email, $reminder, $usr, $prd, $event); - if(!$disable_email) { - $this->send_emails($usr, $uclass, $aclass, $params, $args); - } - } - else if( $event->evt_id_type == 'subscriptions' && ($sub = new MeprSubscription($event->evt_id)) ) { - $usr = $sub->user(); - $prd = new MeprProduct($sub->product_id); - $reminder = $this->get_valid_reminder($event->args); - - if($reminder===false) { return; } // fail silently if reminder is invalid - - $parents = get_user_meta($usr->ID, 'mpca_corporate_account_id'); - - if(count($parents) > 0) { $disable_email = true; } //Do not email sub account users - - $params = array_merge(MeprRemindersHelper::get_email_params($reminder), - MeprSubscriptionsHelper::get_email_params($sub)); - - switch( $reminder->trigger_event ) { - case 'sub-trial-ends': - if( 'before' == $reminder->trigger_timing ) { - $uclass = 'MeprUserSubTrialEndsReminderEmail'; - $aclass = 'MeprAdminSubTrialEndsReminderEmail'; - } - break; - case 'cc-expires': - $uclass = 'MeprUserCcExpiresReminderEmail'; - $aclass = 'MeprAdminCcExpiresReminderEmail'; - break; - default: - $uclass=$aclass=''; - } - - $args = array(array('reminder_id'=>$reminder->ID)); - - $disable_email = MeprHooks::apply_filters("mepr-{$reminder->trigger_event}-reminder-disable", $disable_email, $reminder, $usr, $prd, $event); - if(!$disable_email) { - $this->send_emails($usr, $uclass, $aclass, $params, $args); - } + } } - } - - public function delete( $id ) { - global $post_type; - if ( $post_type != MeprReminder::$cpt ) return; - $this->unschedule_reminder($id); - } + public function delete($id) + { + global $post_type; + if ($post_type != MeprReminder::$cpt) { + return; + } + $this->unschedule_reminder($id); + } } //End class diff --git a/app/controllers/MeprReportsCtrl.php b/app/controllers/MeprReportsCtrl.php index 1193e7d..64390e6 100644 --- a/app/controllers/MeprReportsCtrl.php +++ b/app/controllers/MeprReportsCtrl.php @@ -1,377 +1,604 @@ wp_create_nonce('mepr_reports'), + ]; + if ($hook == 'dashboard_page_memberpress-reports') { + wp_enqueue_script('mepr-google-jsapi', 'https://www.gstatic.com/charts/loader.js', [], MEPR_VERSION); + wp_enqueue_script('mepr-reports-js', MEPR_JS_URL . '/admin_reports.js', ['jquery', 'mepr-google-jsapi'], MEPR_VERSION, true); + wp_enqueue_style('mepr-reports-css', MEPR_CSS_URL . '/admin-reports.css', [], MEPR_VERSION); + + wp_localize_script('mepr-reports-js', 'MeprReportData', $local_data); + } + } - MeprView::render("/admin/reports/main", get_defined_vars()); - } + public static function csv() + { + check_ajax_referer('export_report', 'mepr_reports_nonce'); - public static function enqueue_scripts($hook) { - $local_data = array( - 'report_nonce' => wp_create_nonce('mepr_reports') - ); - if($hook == 'dashboard_page_memberpress-reports') { - wp_enqueue_script('mepr-google-jsapi', 'https://www.gstatic.com/charts/loader.js', array(), MEPR_VERSION); - wp_enqueue_script('mepr-reports-js', MEPR_JS_URL.'/admin_reports.js', array('jquery', 'mepr-google-jsapi'), MEPR_VERSION, true); - wp_enqueue_style('mepr-reports-css', MEPR_CSS_URL.'/admin-reports.css', array(), MEPR_VERSION); + if (!MeprUtils::is_mepr_admin()) { + MeprUtils::exit_with_status(403, __('Forbidden', 'memberpress')); + } - wp_localize_script('mepr-reports-js', 'MeprReportData', $local_data); + $export = $_REQUEST['export']; + $valid_exports = ['widget', 'yearly', 'monthly']; + if (in_array($export, $valid_exports)) { + call_user_func("MeprReportsCtrl::export_{$export}"); + } } - } - public static function csv() { - check_ajax_referer('export_report','mepr_reports_nonce'); + public static function export_widget() + { + $start_date = date_i18n('Y-m-d', time() - MeprUtils::days(6), true); + $end_date = date_i18n('Y-m-d', time(), true); + $filename = "memberpress-report-{$start_date}-to-{$end_date}"; + $txns = MeprReports::get_widget_data('transactions'); + $amts = MeprReports::get_widget_data('amounts'); + $results = self::format_for_csv($txns, $amts); + MeprUtils::render_csv($results, $filename); + } - if(!MeprUtils::is_mepr_admin()) { MeprUtils::exit_with_status(403,__('Forbidden', 'memberpress')); } + public static function load_widget() + { + check_ajax_referer('mepr_reports', 'report_nonce'); + + $mepr_options = MeprOptions::fetch(); + $currency_symbol = $mepr_options->currency_symbol; + $results = MeprReports::get_widget_data(); + $chart_data = + [ + 'cols' => + [ + [ + 'label' => __('Date', 'memberpress'), + 'type' => 'string', + ], + [ + 'label' => __('Completed', 'memberpress'), + 'type' => 'number', + ], + [ + 'role' => 'tooltip', + 'type' => 'string', + 'p' => ['role' => 'tooltip'], + ], + [ + 'label' => __('Pending', 'memberpress'), + 'type' => 'number', + ], + [ + 'role' => 'tooltip', + 'type' => 'string', + 'p' => ['role' => 'tooltip'], + ], + [ + 'label' => __('Failed', 'memberpress'), + 'type' => 'number', + ], + [ + 'role' => 'tooltip', + 'type' => 'string', + 'p' => ['role' => 'tooltip'], + ], + [ + 'label' => __('Refunded', 'memberpress'), + 'type' => 'number', + ], + [ + 'role' => 'tooltip', + 'type' => 'string', + 'p' => ['role' => 'tooltip'], + ], + ], + ]; + + foreach ($results as $r) { + $tooltip_date = date_i18n('M j, Y', mktime(0, 0, 0, gmdate('n', strtotime($r->date)), gmdate('j', strtotime($r->date)), gmdate('Y', strtotime($r->date))), true); + + $chart_data['rows'][] = + [ + 'c' => + [ + [ + 'v' => date_i18n('M j', mktime(0, 0, 0, gmdate('n', strtotime($r->date)), gmdate('j', strtotime($r->date)), gmdate('Y', strtotime($r->date))), true), + 'f' => null, + ], + [ + 'v' => (int)$r->c, + 'f' => null, + ], + [ + 'v' => $tooltip_date . "\n" . __('Completed:', 'memberpress') . ' ' . $currency_symbol . (float)$r->c, + 'f' => null, + ], + [ + 'v' => (int)$r->p, + 'f' => null, + ], + [ + 'v' => $tooltip_date . "\n" . __('Pending:', 'memberpress') . ' ' . $currency_symbol . (float)$r->p, + 'f' => null, + ], + [ + 'v' => (int)$r->f, + 'f' => null, + ], + [ + 'v' => $tooltip_date . "\n" . __('Failed:', 'memberpress') . ' ' . $currency_symbol . (float)$r->f, + 'f' => null, + ], + [ + 'v' => (int)$r->r, + 'f' => null, + ], + [ + 'v' => $tooltip_date . "\n" . __('Refunded:', 'memberpress') . ' ' . $currency_symbol . (float)$r->r, + 'f' => null, + ], + ], + ]; + } - $export = $_REQUEST['export']; - $valid_exports = array( 'widget', 'yearly', 'monthly' ); - if( in_array( $export, $valid_exports ) ) { - call_user_func( "MeprReportsCtrl::export_{$export}" ); + echo json_encode($chart_data); + die(); } - } - - public static function export_widget() { - $start_date = date_i18n('Y-m-d', time() - MeprUtils::days(6), true); - $end_date = date_i18n('Y-m-d', time(), true); - $filename = "memberpress-report-{$start_date}-to-{$end_date}"; - $txns = MeprReports::get_widget_data('transactions'); - $amts = MeprReports::get_widget_data('amounts'); - $results = self::format_for_csv( $txns, $amts ); - MeprUtils::render_csv( $results, $filename ); - } - - public static function load_widget() { - check_ajax_referer('mepr_reports', 'report_nonce'); - - $mepr_options = MeprOptions::fetch(); - $currency_symbol = $mepr_options->currency_symbol; - $results = MeprReports::get_widget_data(); - $chart_data = - array( 'cols' => - array( - array("label" => __('Date', 'memberpress'), 'type' => 'string'), - array("label" => __('Completed', 'memberpress'), 'type' => 'number'), - array("role" => 'tooltip', 'type' => 'string', 'p' => array('role' => 'tooltip')), - array("label" => __('Pending', 'memberpress'), 'type' => 'number'), - array("role" => 'tooltip', 'type' => 'string', 'p' => array('role' => 'tooltip')), - array("label" => __('Failed', 'memberpress'), 'type' => 'number'), - array("role" => 'tooltip', 'type' => 'string', 'p' => array('role' => 'tooltip')), - array("label" => __('Refunded', 'memberpress'), 'type' => 'number'), - array("role" => 'tooltip', 'type' => 'string', 'p' => array('role' => 'tooltip')) - ) - ); - - foreach($results as $r) { - $tooltip_date = date_i18n('M j, Y', mktime(0, 0, 0, gmdate('n', strtotime($r->date)), gmdate('j', strtotime($r->date)), gmdate('Y', strtotime($r->date))), true); - - $chart_data['rows'][] = - array( 'c' => - array( - array('v' => date_i18n('M j', mktime(0, 0, 0, gmdate('n', strtotime($r->date)), gmdate('j', strtotime($r->date)), gmdate('Y', strtotime($r->date))), true), 'f' => null), - array('v' => (int)$r->c, 'f' => null), - array('v' => $tooltip_date."\n".__('Completed:', 'memberpress').' '.$currency_symbol.(float)$r->c, 'f' => null), - array('v' => (int)$r->p, 'f' => null), - array('v' => $tooltip_date."\n".__('Pending:', 'memberpress').' '.$currency_symbol.(float)$r->p, 'f' => null), - array('v' => (int)$r->f, 'f' => null), - array('v' => $tooltip_date."\n".__('Failed:', 'memberpress').' '.$currency_symbol.(float)$r->f, 'f' => null), - array('v' => (int)$r->r, 'f' => null), - array('v' => $tooltip_date."\n".__('Refunded:', 'memberpress').' '.$currency_symbol.(float)$r->r, 'f' => null) - ) - ); + + public static function load_pie() + { + check_ajax_referer('mepr_reports', 'report_nonce'); + + $year = (isset($_REQUEST['year'])) ? $_REQUEST['year'] : false; + $month = (isset($_REQUEST['month'])) ? $_REQUEST['month'] : false; + $results = MeprReports::get_pie_data($year, $month); + + $chart_data = + [ + 'cols' => + [ + [ + 'label' => __('Membership', 'memberpress'), + 'type' => 'string', + ], + [ + 'label' => __('Transactions', 'memberpress'), + 'type' => 'number', + ], + ], + ]; + + foreach ($results as $result) { + $product = ($result->product) ? $result->product : __('Other', 'memberpress'); + $chart_data['rows'][] = + [ + 'c' => + [ + [ + 'v' => $product, + 'f' => null, + ], + [ + 'v' => (int)$result->transactions, + 'f' => null, + ], + ], + ]; + } + + echo json_encode($chart_data); + die(); } - echo json_encode($chart_data); - die(); - } - - public static function load_pie() { - check_ajax_referer('mepr_reports', 'report_nonce'); - - $year = (isset($_REQUEST['year']))?$_REQUEST['year']:false; - $month = (isset($_REQUEST['month']))?$_REQUEST['month']:false; - $results = MeprReports::get_pie_data($year, $month); - - $chart_data = - array( 'cols' => - array( - array("label" => __('Membership', 'memberpress'), 'type' => 'string'), - array("label" => __('Transactions', 'memberpress'), 'type' => 'number') - ) - ); - - foreach($results as $result) { - $product = ($result->product)?$result->product:__('Other', 'memberpress'); - $chart_data['rows'][] = - array( 'c' => - array( - array('v' => $product, 'f' => null), - array('v' => (int)$result->transactions, 'f' => null) - ) - ); + public static function export_monthly() + { + self::monthly_table(true); } - echo json_encode($chart_data); - die(); - } - - public static function export_monthly() { - self::monthly_table(true); - } - - public static function load_monthly() { - check_ajax_referer('mepr_reports', 'report_nonce'); - self::monthly_table(); - } - - public static function monthly_table( $export = false ) { - $mepr_options = MeprOptions::fetch(); - $type = (isset($_REQUEST['type']) && !empty($_REQUEST['type']))?$_REQUEST['type']:'amounts'; - $currency_symbol = ($type == 'amounts')?$mepr_options->currency_symbol:''; - $month = (isset($_REQUEST['month']) && !empty($_REQUEST['month']))?$_REQUEST['month']:gmdate('n'); - $year = (isset($_REQUEST['year']) && !empty($_REQUEST['year']))?$_REQUEST['year']:gmdate('Y'); - $product = (isset($_REQUEST['product']) && $_GET['product'] != 'all')?$_REQUEST['product']:'all'; - $q = (isset($_REQUEST['q']) && $_REQUEST['q'] != 'none')?$_REQUEST['q']:array(); - - if( $export ) { - $txns = MeprReports::get_monthly_dataset('transactions', $month, $year, $product, $q); - $amts = MeprReports::get_monthly_dataset('amounts', $month, $year, $product, $q); - $filename = "memberpress-monthly-{$month}-{$year}-{$type}-for-{$product}"; - $results = self::format_for_csv( $txns, $amts ); - MeprUtils::render_csv( array_values($results), $filename ); + public static function load_monthly() + { + check_ajax_referer('mepr_reports', 'report_nonce'); + self::monthly_table(); } - $results = MeprReports::get_monthly_dataset($type, $month, $year, $product); - - $chart_data = - array( 'cols' => - array( - array("label" => MeprUtils::period_type_name('days'), 'type' => 'string'), - array("label" => __('Completed', 'memberpress'), 'type' => 'number'), - array("role" => 'tooltip', 'type' => 'string', 'p' => array('role' => 'tooltip')), - array("label" => __('Pending', 'memberpress'), 'type' => 'number'), - array("role" => 'tooltip', 'type' => 'string', 'p' => array('role' => 'tooltip')), - array("label" => __('Failed', 'memberpress'), 'type' => 'number'), - array("role" => 'tooltip', 'type' => 'string', 'p' => array('role' => 'tooltip')), - array("label" => __('Refunded', 'memberpress'), 'type' => 'number'), - array("role" => 'tooltip', 'type' => 'string', 'p' => array('role' => 'tooltip')) - ) - ); - - foreach($results as $r) { - $tooltip_date = date_i18n('M j, Y', mktime(0, 0, 0, $month, $r->day, $year), true); - - $chart_data['rows'][] = - array( 'c' => - array( - array('v' => $r->day, 'f' => null), - array('v' => (int)$r->c, 'f' => null), - array('v' => $tooltip_date."\n".__('Completed:', 'memberpress').' '.$currency_symbol.(float)$r->c, 'f' => null), - array('v' => (int)$r->p, 'f' => null), - array('v' => $tooltip_date."\n".__('Pending:', 'memberpress').' '.$currency_symbol.(float)$r->p, 'f' => null), - array('v' => (int)$r->f, 'f' => null), - array('v' => $tooltip_date."\n".__('Failed:', 'memberpress').' '.$currency_symbol.(float)$r->f, 'f' => null), - array('v' => (int)$r->r, 'f' => null), - array('v' => $tooltip_date."\n".__('Refunded:', 'memberpress').' '.$currency_symbol.(float)$r->r, 'f' => null) - ) - ); + public static function monthly_table($export = false) + { + $mepr_options = MeprOptions::fetch(); + $type = (isset($_REQUEST['type']) && !empty($_REQUEST['type'])) ? $_REQUEST['type'] : 'amounts'; + $currency_symbol = ($type == 'amounts') ? $mepr_options->currency_symbol : ''; + $month = (isset($_REQUEST['month']) && !empty($_REQUEST['month'])) ? $_REQUEST['month'] : gmdate('n'); + $year = (isset($_REQUEST['year']) && !empty($_REQUEST['year'])) ? $_REQUEST['year'] : gmdate('Y'); + $product = (isset($_REQUEST['product']) && $_GET['product'] != 'all') ? $_REQUEST['product'] : 'all'; + $q = (isset($_REQUEST['q']) && $_REQUEST['q'] != 'none') ? $_REQUEST['q'] : []; + + if ($export) { + $txns = MeprReports::get_monthly_dataset('transactions', $month, $year, $product, $q); + $amts = MeprReports::get_monthly_dataset('amounts', $month, $year, $product, $q); + $filename = "memberpress-monthly-{$month}-{$year}-{$type}-for-{$product}"; + $results = self::format_for_csv($txns, $amts); + MeprUtils::render_csv(array_values($results), $filename); + } + + $results = MeprReports::get_monthly_dataset($type, $month, $year, $product); + + $chart_data = + [ + 'cols' => + [ + [ + 'label' => MeprUtils::period_type_name('days'), + 'type' => 'string', + ], + [ + 'label' => __('Completed', 'memberpress'), + 'type' => 'number', + ], + [ + 'role' => 'tooltip', + 'type' => 'string', + 'p' => ['role' => 'tooltip'], + ], + [ + 'label' => __('Pending', 'memberpress'), + 'type' => 'number', + ], + [ + 'role' => 'tooltip', + 'type' => 'string', + 'p' => ['role' => 'tooltip'], + ], + [ + 'label' => __('Failed', 'memberpress'), + 'type' => 'number', + ], + [ + 'role' => 'tooltip', + 'type' => 'string', + 'p' => ['role' => 'tooltip'], + ], + [ + 'label' => __('Refunded', 'memberpress'), + 'type' => 'number', + ], + [ + 'role' => 'tooltip', + 'type' => 'string', + 'p' => ['role' => 'tooltip'], + ], + ], + ]; + + foreach ($results as $r) { + $tooltip_date = date_i18n('M j, Y', mktime(0, 0, 0, $month, $r->day, $year), true); + + $chart_data['rows'][] = + [ + 'c' => + [ + [ + 'v' => $r->day, + 'f' => null, + ], + [ + 'v' => (int)$r->c, + 'f' => null, + ], + [ + 'v' => $tooltip_date . "\n" . __('Completed:', 'memberpress') . ' ' . $currency_symbol . (float)$r->c, + 'f' => null, + ], + [ + 'v' => (int)$r->p, + 'f' => null, + ], + [ + 'v' => $tooltip_date . "\n" . __('Pending:', 'memberpress') . ' ' . $currency_symbol . (float)$r->p, + 'f' => null, + ], + [ + 'v' => (int)$r->f, + 'f' => null, + ], + [ + 'v' => $tooltip_date . "\n" . __('Failed:', 'memberpress') . ' ' . $currency_symbol . (float)$r->f, + 'f' => null, + ], + [ + 'v' => (int)$r->r, + 'f' => null, + ], + [ + 'v' => $tooltip_date . "\n" . __('Refunded:', 'memberpress') . ' ' . $currency_symbol . (float)$r->r, + 'f' => null, + ], + ], + ]; + } + + echo json_encode($chart_data); + die(); } - echo json_encode($chart_data); - die(); - } - - public static function export_yearly() { - self::yearly_table(true); - } - - public static function load_yearly() { - check_ajax_referer('mepr_reports', 'report_nonce'); - self::yearly_table(); - } - - public static function yearly_table($export=false) { - $mepr_options = MeprOptions::fetch(); - $type = (isset($_REQUEST['type']) && !empty($_REQUEST['type']))?$_REQUEST['type']:'amounts'; - $currency_symbol = ($type == 'amounts')?$mepr_options->currency_symbol:''; - $year = (isset($_REQUEST['year']) && !empty($_REQUEST['year']))?$_REQUEST['year']:gmdate('Y'); - $product = (isset($_REQUEST['product']) && $_GET['product'] != 'all')?$_REQUEST['product']:'all'; - $q = (isset($_REQUEST['q']) && $_GET['q'] != 'none')?$_REQUEST['q']:''; - - if( $export ) { - $filename = "memberpress-yearly-{$year}-{$type}-for-{$product}"; - $txns = MeprReports::get_yearly_dataset('transactions', $year, $product, $q); - $amts = MeprReports::get_yearly_dataset('amounts', $year, $product, $q); - $results = self::format_for_csv( $txns, $amts ); - MeprUtils::render_csv( $results, $filename ); + public static function export_yearly() + { + self::yearly_table(true); } - $results = MeprReports::get_yearly_dataset($type, $year, $product); - - $chart_data = - array( 'cols' => - array( - array("label" => MeprUtils::period_type_name('months'), 'type' => 'string'), - array("label" => __('Completed', 'memberpress'), 'type' => 'number'), - array("role" => 'tooltip', 'type' => 'string', 'p' => array('role' => 'tooltip')), - array("label" => __('Pending', 'memberpress'), 'type' => 'number'), - array("role" => 'tooltip', 'type' => 'string', 'p' => array('role' => 'tooltip')), - array("label" => __('Failed', 'memberpress'), 'type' => 'number'), - array("role" => 'tooltip', 'type' => 'string', 'p' => array('role' => 'tooltip')), - array("label" => __('Refunded', 'memberpress'), 'type' => 'number'), - array("role" => 'tooltip', 'type' => 'string', 'p' => array('role' => 'tooltip')) - ) - ); - - foreach($results as $r) { - $tooltip_date = date_i18n('M, Y', mktime(0, 0, 0, $r->month, 15, $year), true); - $chart_data['rows'][] = - array( 'c' => - array( - array('v' => MeprUtils::month_names( true, $r->month, true), 'f' => null), - array('v' => (int)$r->c, 'f' => null), - array('v' => $tooltip_date."\n".__('Completed:', 'memberpress').' '.$currency_symbol.(float)$r->c, 'f' => null), - array('v' => (int)$r->p, 'f' => null), - array('v' => $tooltip_date."\n".__('Pending:', 'memberpress').' '.$currency_symbol.(float)$r->p, 'f' => null), - array('v' => (int)$r->f, 'f' => null), - array('v' => $tooltip_date."\n".__('Failed:', 'memberpress').' '.$currency_symbol.(float)$r->f, 'f' => null), - array('v' => (int)$r->r, 'f' => null), - array('v' => $tooltip_date."\n".__('Refunded:', 'memberpress').' '.$currency_symbol.(float)$r->r, 'f' => null) - ) - ); + public static function load_yearly() + { + check_ajax_referer('mepr_reports', 'report_nonce'); + self::yearly_table(); } - echo json_encode($chart_data); - die(); - } - - public static function format_for_csv( $txns, $amts ) { - $tmap = array( 'date' => 'date', - 'day' => 'day', - 'month' => 'month', - 'p' => 'pending.count', - 'f' => 'failed.count', - 'c' => 'complete.count', - 'r' => 'refunded.count' ); - - $amap = array( 'p' => 'pending.amount', - 'f' => 'failed.amount', - 't' => 'collected.amount', - 'r' => 'refunded.amount', - 'x' => 'tax.amount', - 'c' => 'complete.amount' ); - - $valid_cols = array_keys( $tmap ); - $ta_cols = array_keys( $amap ); - $a_cols = array_diff( $ta_cols, $valid_cols ); - - $txns = array_values( $txns ); - $amts = array_values( $amts ); - - $csv = array(); - for( $i = 0; $i < count($txns); $i++ ) { - $csv[$i] = array(); - - // Go through the columns that have txn and amt columns - foreach( $txns[$i] as $label => $value ) { - if( in_array( $label, $valid_cols ) ) { - $csv[$i][$tmap[$label]] = $value ? $value : 0; - - if( in_array( $label, $ta_cols ) ) { - $csv[$i][$amap[$label]] = $amts[$i]->{$label} ? $amts[$i]->{$label} : 0.00; - } + public static function yearly_table($export = false) + { + $mepr_options = MeprOptions::fetch(); + $type = (isset($_REQUEST['type']) && !empty($_REQUEST['type'])) ? $_REQUEST['type'] : 'amounts'; + $currency_symbol = ($type == 'amounts') ? $mepr_options->currency_symbol : ''; + $year = (isset($_REQUEST['year']) && !empty($_REQUEST['year'])) ? $_REQUEST['year'] : gmdate('Y'); + $product = (isset($_REQUEST['product']) && $_GET['product'] != 'all') ? $_REQUEST['product'] : 'all'; + $q = (isset($_REQUEST['q']) && $_GET['q'] != 'none') ? $_REQUEST['q'] : ''; + + if ($export) { + $filename = "memberpress-yearly-{$year}-{$type}-for-{$product}"; + $txns = MeprReports::get_yearly_dataset('transactions', $year, $product, $q); + $amts = MeprReports::get_yearly_dataset('amounts', $year, $product, $q); + $results = self::format_for_csv($txns, $amts); + MeprUtils::render_csv($results, $filename); } - } - // Pickup all the amount only variables - foreach( $a_cols as $index => $label ) { - if( in_array( $label, $ta_cols ) ) { - $csv[$i][$amap[$label]] = $amts[$i]->{$label} ? $amts[$i]->{$label} : 0.00; + $results = MeprReports::get_yearly_dataset($type, $year, $product); + + $chart_data = + [ + 'cols' => + [ + [ + 'label' => MeprUtils::period_type_name('months'), + 'type' => 'string', + ], + [ + 'label' => __('Completed', 'memberpress'), + 'type' => 'number', + ], + [ + 'role' => 'tooltip', + 'type' => 'string', + 'p' => ['role' => 'tooltip'], + ], + [ + 'label' => __('Pending', 'memberpress'), + 'type' => 'number', + ], + [ + 'role' => 'tooltip', + 'type' => 'string', + 'p' => ['role' => 'tooltip'], + ], + [ + 'label' => __('Failed', 'memberpress'), + 'type' => 'number', + ], + [ + 'role' => 'tooltip', + 'type' => 'string', + 'p' => ['role' => 'tooltip'], + ], + [ + 'label' => __('Refunded', 'memberpress'), + 'type' => 'number', + ], + [ + 'role' => 'tooltip', + 'type' => 'string', + 'p' => ['role' => 'tooltip'], + ], + ], + ]; + + foreach ($results as $r) { + $tooltip_date = date_i18n('M, Y', mktime(0, 0, 0, $r->month, 15, $year), true); + $chart_data['rows'][] = + [ + 'c' => + [ + [ + 'v' => MeprUtils::month_names(true, $r->month, true), + 'f' => null, + ], + [ + 'v' => (int)$r->c, + 'f' => null, + ], + [ + 'v' => $tooltip_date . "\n" . __('Completed:', 'memberpress') . ' ' . $currency_symbol . (float)$r->c, + 'f' => null, + ], + [ + 'v' => (int)$r->p, + 'f' => null, + ], + [ + 'v' => $tooltip_date . "\n" . __('Pending:', 'memberpress') . ' ' . $currency_symbol . (float)$r->p, + 'f' => null, + ], + [ + 'v' => (int)$r->f, + 'f' => null, + ], + [ + 'v' => $tooltip_date . "\n" . __('Failed:', 'memberpress') . ' ' . $currency_symbol . (float)$r->f, + 'f' => null, + ], + [ + 'v' => (int)$r->r, + 'f' => null, + ], + [ + 'v' => $tooltip_date . "\n" . __('Refunded:', 'memberpress') . ' ' . $currency_symbol . (float)$r->r, + 'f' => null, + ], + ], + ]; + } + + echo json_encode($chart_data); + die(); + } + + public static function format_for_csv($txns, $amts) + { + $tmap = [ + 'date' => 'date', + 'day' => 'day', + 'month' => 'month', + 'p' => 'pending.count', + 'f' => 'failed.count', + 'c' => 'complete.count', + 'r' => 'refunded.count', + ]; + + $amap = [ + 'p' => 'pending.amount', + 'f' => 'failed.amount', + 't' => 'collected.amount', + 'r' => 'refunded.amount', + 'x' => 'tax.amount', + 'c' => 'complete.amount', + ]; + + $valid_cols = array_keys($tmap); + $ta_cols = array_keys($amap); + $a_cols = array_diff($ta_cols, $valid_cols); + + $txns = array_values($txns); + $amts = array_values($amts); + + $csv = []; + for ($i = 0; $i < count($txns); $i++) { + $csv[$i] = []; + + // Go through the columns that have txn and amt columns + foreach ($txns[$i] as $label => $value) { + if (in_array($label, $valid_cols)) { + $csv[$i][$tmap[$label]] = $value ? $value : 0; + + if (in_array($label, $ta_cols)) { + $csv[$i][$amap[$label]] = $amts[$i]->{$label} ? $amts[$i]->{$label} : 0.00; + } + } + } + + // Pickup all the amount only variables + foreach ($a_cols as $index => $label) { + if (in_array($label, $ta_cols)) { + $csv[$i][$amap[$label]] = $amts[$i]->{$label} ? $amts[$i]->{$label} : 0.00; + } + } } - } + + return $csv; + } + + public static function load_overall_info() + { + check_ajax_referer('mepr_reports', 'report_nonce'); + wp_send_json_success([ + 'output' => MeprView::get_string('/admin/reports/overall_info_blocks'), + ]); + } + + protected static function do_common_vars() + { + $curr_month = (isset($_GET['month']) && !empty($_GET['month'])) ? $_GET['month'] : gmdate('n'); + $curr_year = (isset($_GET['year']) && !empty($_GET['year'])) ? $_GET['year'] : gmdate('Y'); + $curr_product = (isset($_GET['product']) && !empty($_GET['product'])) ? $_GET['product'] : 'all'; + + return [ + 'curr_month' => $curr_month, + 'curr_year' => $curr_year, + 'curr_product' => $curr_product, + ]; + } + + public static function load_month_info_blocks() + { + check_ajax_referer('mepr_reports', 'report_nonce'); + wp_send_json_success([ + 'output' => MeprView::get_string('/admin/reports/month_info_blocks', self::do_common_vars()), + ]); + } + + public static function load_month_info_table() + { + check_ajax_referer('mepr_reports', 'report_nonce'); + wp_send_json_success([ + 'output' => MeprView::get_string('/admin/reports/month_table', self::do_common_vars()), + ]); } - return $csv; - } - - public static function load_overall_info() { - check_ajax_referer('mepr_reports', 'report_nonce'); - wp_send_json_success(array( - 'output' => MeprView::get_string("/admin/reports/overall_info_blocks") - )); - } - - protected static function do_common_vars(){ - $curr_month = (isset($_GET['month']) && !empty($_GET['month']))?$_GET['month']:gmdate('n'); - $curr_year = (isset($_GET['year']) && !empty($_GET['year']))?$_GET['year']:gmdate('Y'); - $curr_product = (isset($_GET['product']) && !empty($_GET['product']))?$_GET['product']:'all'; - - return array( - 'curr_month' => $curr_month, - 'curr_year' => $curr_year, - 'curr_product' => $curr_product - ); - } - - public static function load_month_info_blocks() { - check_ajax_referer('mepr_reports', 'report_nonce'); - wp_send_json_success(array( - 'output' => MeprView::get_string("/admin/reports/month_info_blocks", self::do_common_vars()) - )); - } - - public static function load_month_info_table() { - check_ajax_referer('mepr_reports', 'report_nonce'); - wp_send_json_success(array( - 'output' => MeprView::get_string("/admin/reports/month_table", self::do_common_vars()) - )); - } - - public static function load_year_info_blocks() { - check_ajax_referer('mepr_reports', 'report_nonce'); - wp_send_json_success(array( - 'output' => MeprView::get_string("/admin/reports/year_info_blocks", self::do_common_vars()) - )); - } - - public static function load_year_info_table() { - check_ajax_referer('mepr_reports', 'report_nonce'); - wp_send_json_success(array( - 'output' => MeprView::get_string("/admin/reports/year_table", self::do_common_vars()) - )); - } - - public static function load_all_time_info_blocks() { - check_ajax_referer('mepr_reports', 'report_nonce'); - wp_send_json_success(array( - 'output' => MeprView::get_string("/admin/reports/all_time_info_blocks", self::do_common_vars()) - )); - } + public static function load_year_info_blocks() + { + check_ajax_referer('mepr_reports', 'report_nonce'); + wp_send_json_success([ + 'output' => MeprView::get_string('/admin/reports/year_info_blocks', self::do_common_vars()), + ]); + } + + public static function load_year_info_table() + { + check_ajax_referer('mepr_reports', 'report_nonce'); + wp_send_json_success([ + 'output' => MeprView::get_string('/admin/reports/year_table', self::do_common_vars()), + ]); + } + + public static function load_all_time_info_blocks() + { + check_ajax_referer('mepr_reports', 'report_nonce'); + wp_send_json_success([ + 'output' => MeprView::get_string('/admin/reports/all_time_info_blocks', self::do_common_vars()), + ]); + } } diff --git a/app/controllers/MeprRulesCtrl.php b/app/controllers/MeprRulesCtrl.php index 6bd7693..9be0a78 100644 --- a/app/controllers/MeprRulesCtrl.php +++ b/app/controllers/MeprRulesCtrl.php @@ -1,1003 +1,1069 @@ redirect_method}", 'MeprRulesCtrl::rule_redirection', 3); - - // add_filter('the_content_feed', 'MeprRulesCtrl::rule_content', 999999, 1); //I think the_content is called before the_content_feed, so this is redundant - add_filter('the_content', 'MeprRulesCtrl::rule_content', 999999, 1); - add_action('admin_init', 'MeprRulesCtrl::admin_rule_redirection', 3); - add_filter('comments_template', 'MeprRulesCtrl::rule_comments'); - add_action('mod_rewrite_rules', 'MeprRulesCtrl::mod_rewrite_rules'); - - //All other stuff - add_filter('bulk_actions-edit-memberpressrule', 'MeprRulesCtrl::disable_bulk'); - add_filter('post_row_actions', 'MeprRulesCtrl::disable_row', 10, 2); - add_action('admin_enqueue_scripts', 'MeprRulesCtrl::enqueue_scripts'); - add_action('admin_init', 'MeprRule::cleanup_db'); //Clear out all unused auto-save's - add_action('manage_posts_custom_column', 'MeprRulesCtrl::custom_columns', 10, 2); - add_filter('manage_edit-memberpressrule_columns', 'MeprRulesCtrl::columns'); - add_action('save_post', 'MeprRulesCtrl::save_postdata'); - add_action('delete_post', 'MeprRulesCtrl::delete_access_rules', 10); - add_action('wp_ajax_mepr_show_content_dropdown', 'MeprRulesCtrl::display_content_dropdown'); - add_action('wp_ajax_mepr_remove_access_condition', 'MeprRulesCtrl::remove_access_condition'); - add_action('wp_ajax_mepr_rule_content_search', 'MeprRulesCtrl::ajax_content_search'); - add_filter('default_title', 'MeprRulesCtrl::get_page_title_code'); - - // Add virtual capabilities - add_filter('user_has_cap', 'MeprRulesCtrl::authorized_cap', 10, 3); - add_filter('user_has_cap', 'MeprRulesCtrl::product_authorized_cap', 10, 3); // Deprecated - add_filter('user_has_cap', 'MeprRulesCtrl::rule_authorized_cap', 10, 3); // Deprecated - add_filter('user_has_cap', 'MeprRulesCtrl::active_cap', 10, 3); - - MeprHooks::add_shortcode('mepr-rule', 'MeprRulesCtrl::rule_shortcode'); // Deprecated - MeprHooks::add_shortcode('mepr-active', 'MeprRulesCtrl::active_shortcode'); - MeprHooks::add_shortcode('mepr-unauthorized-message', 'MeprRulesCtrl::unauthorized_message_shortcode'); - - MeprHooks::add_shortcode('mepr-show', 'MeprRulesCtrl::show_shortcode'); - MeprHooks::add_shortcode('mepr-hide', 'MeprRulesCtrl::hide_shortcode'); - - // Cleanup list view - add_filter('views_edit-'.MeprRule::$cpt, 'MeprAppCtrl::cleanup_list_view' ); - - // Protect WooCommerce Products (this used to be included in our old WC add-on which has since been deprecated) - include_once(ABSPATH . 'wp-admin/includes/plugin.php'); - if(!is_plugin_active('memberpress-woocommerce/main.php')) { - add_filter('woocommerce_is_purchasable', 'MeprRulesCtrl::override_wc_is_purchasable', 11, 2); - add_filter('woocommerce_product_is_visible', 'MeprRulesCtrl::override_wc_is_visible', 11, 2); - add_filter('woocommerce_variation_is_visible', 'MeprRulesCtrl::override_wc_is_visible', 11, 4); - add_filter('mepr-pre-run-rule-content', 'MeprRulesCtrl::dont_hide_wc_product_content', 11, 3); - } - add_action( 'wp_enqueue_scripts', 'MeprRulesCtrl::enqueue_scripts_paywall'); - } - - public function register_post_type() { - register_post_type( MeprRule::$cpt, array( - 'labels' => array( - 'name' => __('Rules', 'memberpress'), - 'singular_name' => __('Rule', 'memberpress'), - 'add_new' => __('Add New', 'memberpress'), - 'add_new_item' => __('Add New Rule', 'memberpress'), - 'edit_item' => __('Edit Rule', 'memberpress'), - 'new_item' => __('New Rule', 'memberpress'), - 'view_item' => __('View Rule', 'memberpress'), - 'search_items' => __('Search Rules', 'memberpress'), - 'not_found' => __('No Rules found', 'memberpress'), - 'not_found_in_trash' => __('No Rules found in Trash', 'memberpress'), - 'parent_item_colon' => __('Parent Rule:', 'memberpress') - ), - 'public' => false, - 'show_ui' => true, //MeprUpdateCtrl::is_activated(), - 'show_in_menu' => 'memberpress', - 'capability_type' => 'page', - 'hierarchical' => false, - 'register_meta_box_cb' => 'MeprRulesCtrl::add_meta_boxes', - 'rewrite' => false, - 'supports' => array('title') - ) - ); - } - - //Set an initial page title - public static function get_page_title_code($title) { - global $current_screen; - - if(empty($title) && isset($current_screen->post_type) && $current_screen->post_type == MeprRule::$cpt) { - return __('All Content: ', 'memberpress'); - } - else { - return $title; - } - } - - public static function columns($columns) { - $columns = array( - "cb" => "", - "ID" => __("ID", 'memberpress'), - "title" => __("Title", 'memberpress'), - "rule-type" => __("Type", 'memberpress'), - "rule-content" => __("Content", 'memberpress'), - "rule-products" => __("Access", 'memberpress'), - "rule-drip" => __("Drip time", 'memberpress'), - "rule-expiration" => __("Expiration time", 'memberpress') - ); - - return $columns; - } - - public static function custom_columns($column, $rule_id) { - $rule = new MeprRule($rule_id); - - if($rule->ID !== null) { - $rule_contents = MeprRule::get_contents_array($rule->mepr_type); - $types = MeprRule::get_types(); - - if("ID" == $column) { - echo $rule->ID; - } - elseif("rule-type" == $column and isset($types[$rule->mepr_type])) { - echo $types[$rule->mepr_type]; - } - elseif("rule-content" == $column and $rule->mepr_type != 'custom' and - isset($rule_contents[$rule->mepr_content])) { - echo $rule_contents[$rule->mepr_content]; - } - elseif("rule-content" == $column and $rule->mepr_type == 'custom' and - isset($rule->mepr_content)) { - echo $rule->mepr_content; - } - elseif("rule-content" == $column and - strstr($rule->mepr_type, 'all_') !== false and - isset($rule->mepr_content)) { - echo __('Except', 'memberpress') . ': ' . $rule->mepr_content; - } - elseif("rule-products" == $column) { - echo implode(', ', $rule->get_formatted_accesses()); - } - elseif("rule-drip" == $column) { - if($rule->drip_enabled) { - $time = array_keys(MeprRule::get_time_units(), $rule->drip_unit); - printf(__('%s %s after %s', 'memberpress'), - $rule->drip_amount, - $time[0], - MeprRule::get_expires_after($rule->drip_after, $rule->drip_after_fixed) - ); - } else { - echo __('--', 'memberpress'); - } - } - elseif("rule-expiration" == $column) { - if($rule->expires_enabled) { - $time = array_keys(MeprRule::get_time_units(), $rule->expires_unit); - printf(__('%s %s after %s', 'memberpress'), - $rule->expires_amount, - $time[0], - MeprRule::get_expires_after($rule->expires_after, $rule->expires_after_fixed) - ); + +if (!defined('ABSPATH')) { + die('You are not allowed to call this page directly.'); +} + +class MeprRulesCtrl extends MeprCptCtrl +{ + public function load_hooks() + { + $mepr_options = MeprOptions::fetch(); + + // Protection stuff + add_action("{$mepr_options->redirect_method}", 'MeprRulesCtrl::rule_redirection', 3); + + // add_filter('the_content_feed', 'MeprRulesCtrl::rule_content', 999999, 1); //I think the_content is called before the_content_feed, so this is redundant + add_filter('the_content', 'MeprRulesCtrl::rule_content', 999999, 1); + add_action('admin_init', 'MeprRulesCtrl::admin_rule_redirection', 3); + add_filter('comments_template', 'MeprRulesCtrl::rule_comments'); + add_action('mod_rewrite_rules', 'MeprRulesCtrl::mod_rewrite_rules'); + + // All other stuff + add_filter('bulk_actions-edit-memberpressrule', 'MeprRulesCtrl::disable_bulk'); + add_filter('post_row_actions', 'MeprRulesCtrl::disable_row', 10, 2); + add_action('admin_enqueue_scripts', 'MeprRulesCtrl::enqueue_scripts'); + add_action('admin_init', 'MeprRule::cleanup_db'); // Clear out all unused auto-save's + add_action('manage_posts_custom_column', 'MeprRulesCtrl::custom_columns', 10, 2); + add_filter('manage_edit-memberpressrule_columns', 'MeprRulesCtrl::columns'); + add_action('save_post', 'MeprRulesCtrl::save_postdata'); + add_action('delete_post', 'MeprRulesCtrl::delete_access_rules', 10); + add_action('wp_ajax_mepr_show_content_dropdown', 'MeprRulesCtrl::display_content_dropdown'); + add_action('wp_ajax_mepr_remove_access_condition', 'MeprRulesCtrl::remove_access_condition'); + add_action('wp_ajax_mepr_rule_content_search', 'MeprRulesCtrl::ajax_content_search'); + add_filter('default_title', 'MeprRulesCtrl::get_page_title_code'); + + // Add virtual capabilities + add_filter('user_has_cap', 'MeprRulesCtrl::authorized_cap', 10, 3); + add_filter('user_has_cap', 'MeprRulesCtrl::product_authorized_cap', 10, 3); // Deprecated + add_filter('user_has_cap', 'MeprRulesCtrl::rule_authorized_cap', 10, 3); // Deprecated + add_filter('user_has_cap', 'MeprRulesCtrl::active_cap', 10, 3); + + MeprHooks::add_shortcode('mepr-rule', 'MeprRulesCtrl::rule_shortcode'); // Deprecated + MeprHooks::add_shortcode('mepr-active', 'MeprRulesCtrl::active_shortcode'); + MeprHooks::add_shortcode('mepr-unauthorized-message', 'MeprRulesCtrl::unauthorized_message_shortcode'); + + MeprHooks::add_shortcode('mepr-show', 'MeprRulesCtrl::show_shortcode'); + MeprHooks::add_shortcode('mepr-hide', 'MeprRulesCtrl::hide_shortcode'); + + // Cleanup list view + add_filter('views_edit-' . MeprRule::$cpt, 'MeprAppCtrl::cleanup_list_view'); + + // Protect WooCommerce Products (this used to be included in our old WC add-on which has since been deprecated) + include_once(ABSPATH . 'wp-admin/includes/plugin.php'); + if (!is_plugin_active('memberpress-woocommerce/main.php')) { + add_filter('woocommerce_is_purchasable', 'MeprRulesCtrl::override_wc_is_purchasable', 11, 2); + add_filter('woocommerce_product_is_visible', 'MeprRulesCtrl::override_wc_is_visible', 11, 2); + add_filter('woocommerce_variation_is_visible', 'MeprRulesCtrl::override_wc_is_visible', 11, 4); + add_filter('mepr-pre-run-rule-content', 'MeprRulesCtrl::dont_hide_wc_product_content', 11, 3); + } + add_action('wp_enqueue_scripts', 'MeprRulesCtrl::enqueue_scripts_paywall'); + } + + public function register_post_type() + { + register_post_type(MeprRule::$cpt, [ + 'labels' => [ + 'name' => __('Rules', 'memberpress'), + 'singular_name' => __('Rule', 'memberpress'), + 'add_new' => __('Add New', 'memberpress'), + 'add_new_item' => __('Add New Rule', 'memberpress'), + 'edit_item' => __('Edit Rule', 'memberpress'), + 'new_item' => __('New Rule', 'memberpress'), + 'view_item' => __('View Rule', 'memberpress'), + 'search_items' => __('Search Rules', 'memberpress'), + 'not_found' => __('No Rules found', 'memberpress'), + 'not_found_in_trash' => __('No Rules found in Trash', 'memberpress'), + 'parent_item_colon' => __('Parent Rule:', 'memberpress'), + ], + 'public' => false, + 'show_ui' => true, // MeprUpdateCtrl::is_activated(), + 'show_in_menu' => 'memberpress', + 'capability_type' => 'page', + 'hierarchical' => false, + 'register_meta_box_cb' => 'MeprRulesCtrl::add_meta_boxes', + 'rewrite' => false, + 'supports' => ['title'], + ]); + } + + // Set an initial page title + public static function get_page_title_code($title) + { + global $current_screen; + + if (empty($title) && isset($current_screen->post_type) && $current_screen->post_type == MeprRule::$cpt) { + return __('All Content: ', 'memberpress'); } else { - echo __('--', 'memberpress'); + return $title; } - } } - } - public static function rule_comments($template = '') { - $current_post = MeprUtils::get_current_post(); - $mepr_options = MeprOptions::fetch(); + public static function columns($columns) + { + $columns = [ + 'cb' => '', + 'ID' => __('ID', 'memberpress'), + 'title' => __('Title', 'memberpress'), + 'rule-type' => __('Type', 'memberpress'), + 'rule-content' => __('Content', 'memberpress'), + 'rule-products' => __('Access', 'memberpress'), + 'rule-drip' => __('Drip time', 'memberpress'), + 'rule-expiration' => __('Expiration time', 'memberpress'), + ]; + + return $columns; + } - if(isset($current_post)) { - if(MeprRule::is_locked($current_post)) { - if(MeprHooks::apply_filters('mepr-rule-comments', true)) { - return MeprView::file('/shared/unauthorized_comments'); + public static function custom_columns($column, $rule_id) + { + $rule = new MeprRule($rule_id); + + if ($rule->ID !== null) { + $rule_contents = MeprRule::get_contents_array($rule->mepr_type); + $types = MeprRule::get_types(); + + if ('ID' == $column) { + echo $rule->ID; + } elseif ('rule-type' == $column and isset($types[$rule->mepr_type])) { + echo $types[$rule->mepr_type]; + } elseif ( + 'rule-content' == $column and $rule->mepr_type != 'custom' and + isset($rule_contents[$rule->mepr_content]) + ) { + echo $rule_contents[$rule->mepr_content]; + } elseif ( + 'rule-content' == $column and $rule->mepr_type == 'custom' and + isset($rule->mepr_content) + ) { + echo $rule->mepr_content; + } elseif ( + 'rule-content' == $column and + strstr($rule->mepr_type, 'all_') !== false and + isset($rule->mepr_content) + ) { + echo __('Except', 'memberpress') . ': ' . $rule->mepr_content; + } elseif ('rule-products' == $column) { + echo implode(', ', $rule->get_formatted_accesses()); + } elseif ('rule-drip' == $column) { + if ($rule->drip_enabled) { + $time = array_keys(MeprRule::get_time_units(), $rule->drip_unit); + printf( + __('%1$s %2$s after %3$s', 'memberpress'), + $rule->drip_amount, + $time[0], + MeprRule::get_expires_after($rule->drip_after, $rule->drip_after_fixed) + ); + } else { + echo __('--', 'memberpress'); + } + } elseif ('rule-expiration' == $column) { + if ($rule->expires_enabled) { + $time = array_keys(MeprRule::get_time_units(), $rule->expires_unit); + printf( + __('%1$s %2$s after %3$s', 'memberpress'), + $rule->expires_amount, + $time[0], + MeprRule::get_expires_after($rule->expires_after, $rule->expires_after_fixed) + ); + } else { + echo __('--', 'memberpress'); + } + } } - } } - return $template; - } + public static function rule_comments($template = '') + { + $current_post = MeprUtils::get_current_post(); + $mepr_options = MeprOptions::fetch(); - /** Used to redirect unauthorized visitors if redirect_on_unauthorized is selected in MeprOptions or - if we're protecting a WP controlled-URI. */ - public static function rule_redirection() { - global $post; + if (isset($current_post)) { + if (MeprRule::is_locked($current_post)) { + if (MeprHooks::apply_filters('mepr-rule-comments', true)) { + return MeprView::file('/shared/unauthorized_comments'); + } + } + } - //Prevents us double matching a URI and causing a redirect loop - if(isset($_GET['action']) && $_GET['action'] == 'mepr_unauthorized') { - return; + return $template; } - $uri = esc_url($_SERVER['REQUEST_URI']); - $mepr_options = MeprOptions::fetch(); - $delim = MeprAppCtrl::get_param_delimiter_char($mepr_options->unauthorized_redirect_url); - $is_ssl = MeprUtils::is_ssl(); + /** + * Used to redirect unauthorized visitors if redirect_on_unauthorized is selected in MeprOptions or + if we're protecting a WP controlled-URI. + */ + public static function rule_redirection() + { + global $post; + + // Prevents us double matching a URI and causing a redirect loop + if (isset($_GET['action']) && $_GET['action'] == 'mepr_unauthorized') { + return; + } + + $uri = esc_url($_SERVER['REQUEST_URI']); + $mepr_options = MeprOptions::fetch(); + $delim = MeprAppCtrl::get_param_delimiter_char($mepr_options->unauthorized_redirect_url); + $is_ssl = MeprUtils::is_ssl(); - //Add this filter to allow external resources - //to control whether to redirect away from this content - //if the resource sets the filter to FALSE then no redirect will occur - if(!MeprHooks::apply_filters('mepr-pre-run-rule-redirection', true, $uri, $delim)) { return; } + // Add this filter to allow external resources + // to control whether to redirect away from this content + // if the resource sets the filter to FALSE then no redirect will occur + if (!MeprHooks::apply_filters('mepr-pre-run-rule-redirection', true, $uri, $delim)) { + return; + } - // Let's check the URI's first ok? - // This is here to perform an unauthorized redirection based on the uri - if(MeprRule::is_uri_locked($uri)) { - if($mepr_options->redirect_on_unauthorized) { //Send to unauth page - $redirect_url = "{$mepr_options->unauthorized_redirect_url}{$delim}action=mepr_unauthorized&redirect_to=".urlencode($uri); - } - else { //Send to login page - $redirect_url = $mepr_options->login_page_url("action=mepr_unauthorized&redirect_to=".urlencode($uri)); - } + // Let's check the URI's first ok? + // This is here to perform an unauthorized redirection based on the uri + if (MeprRule::is_uri_locked($uri)) { + if ($mepr_options->redirect_on_unauthorized) { // Send to unauth page + $redirect_url = "{$mepr_options->unauthorized_redirect_url}{$delim}action=mepr_unauthorized&redirect_to=" . urlencode($uri); + } else { // Send to login page + $redirect_url = $mepr_options->login_page_url('action=mepr_unauthorized&redirect_to=' . urlencode($uri)); + } - //Handle SSL - $redirect_url = ($is_ssl)?str_replace('http:', 'https:', $redirect_url):$redirect_url; - MeprUtils::wp_redirect(MeprHooks::apply_filters('mepr-rule-redirect-unauthorized-url', $redirect_url, $delim, $uri)); - exit; + // Handle SSL + $redirect_url = ($is_ssl) ? str_replace('http:', 'https:', $redirect_url) : $redirect_url; + MeprUtils::wp_redirect(MeprHooks::apply_filters('mepr-rule-redirect-unauthorized-url', $redirect_url, $delim, $uri)); + exit; + } + + // If the URI isn't protected, let's check the other Rules + if ($mepr_options->redirect_on_unauthorized) { + $do_redirect = MeprHooks::apply_filters('mepr-rule-do-redirection', self::should_do_redirect()); + + if ( + (!is_singular() && $do_redirect) || + ($do_redirect && isset($post) && MeprRule::is_locked($post)) || + (!is_user_logged_in() && isset($post) && $post->ID == $mepr_options->account_page_id) + ) { + $redirect_url = "{$mepr_options->unauthorized_redirect_url}{$delim}mepr-unauth-page={$post->ID}&redirect_to=" . urlencode($uri); + + // Handle SSL + $redirect_url = ($is_ssl) ? str_replace('http:', 'https:', $redirect_url) : $redirect_url; + MeprUtils::wp_redirect(MeprHooks::apply_filters('mepr-rule-redirect-unauthorized-url', $redirect_url, $delim, $uri)); + exit; + } + } } - // If the URI isn't protected, let's check the other Rules - if($mepr_options->redirect_on_unauthorized) { - $do_redirect = MeprHooks::apply_filters('mepr-rule-do-redirection', self::should_do_redirect()); + // Allow control of the admin dashboard URL's too + public static function admin_rule_redirection() + { + $uri = esc_url($_SERVER['REQUEST_URI']); + $mepr_options = MeprOptions::fetch(); + $delim = MeprAppCtrl::get_param_delimiter_char($mepr_options->unauthorized_redirect_url); + + // This performs an unauthorized redirection based on the uri + if (MeprRule::is_uri_locked($uri)) { + if ($mepr_options->redirect_on_unauthorized) { // Send to unauth page + $redirect_url = "{$mepr_options->unauthorized_redirect_url}{$delim}action=mepr_unauthorized&redirect_to=" . urlencode($uri); + } else { // Send to login page + $redirect_url = $mepr_options->login_page_url('action=mepr_unauthorized&redirect_to=' . urlencode($uri)); + } - if( (!is_singular() && $do_redirect) || - ($do_redirect && isset($post) && MeprRule::is_locked($post)) || - (!is_user_logged_in() && isset($post) && $post->ID == $mepr_options->account_page_id) ) { - $redirect_url = "{$mepr_options->unauthorized_redirect_url}{$delim}mepr-unauth-page={$post->ID}&redirect_to=".urlencode($uri); + // Handle SSL + $redirect_url = (MeprUtils::is_ssl()) ? str_replace('http:', 'https:', $redirect_url) : $redirect_url; + MeprUtils::wp_redirect($redirect_url); + exit; + } + } - //Handle SSL - $redirect_url = ($is_ssl)?str_replace('http:', 'https:', $redirect_url):$redirect_url; - MeprUtils::wp_redirect(MeprHooks::apply_filters('mepr-rule-redirect-unauthorized-url', $redirect_url, $delim, $uri)); + /** + * Redirect to login page or unauth page + * Used by addons BBPress and MP Downloads + * + * @param WP_Post $post + * @return void + */ + public static function redirect_unauthorized($post) + { + $mepr_options = MeprOptions::fetch(); + $uri = urlencode(esc_url($_SERVER['REQUEST_URI'])); + + if ($mepr_options->redirect_on_unauthorized) { + $delim = MeprAppCtrl::get_param_delimiter_char($mepr_options->unauthorized_redirect_url); + $redirect_to = "{$mepr_options->unauthorized_redirect_url}{$delim}mepr-unauth-page={$post->ID}&redirect_to={$uri}"; + } else { + $redirect_to = $mepr_options->login_page_url("action=mepr_unauthorized&mepr-unauth-page={$post->ID}&redirect_to=" . $uri); + $redirect_to = (MeprUtils::is_ssl()) ? str_replace('http:', 'https:', $redirect_to) : $redirect_to; + } + MeprUtils::wp_redirect(MeprHooks::apply_filters('mepr-rule-redirect-unauthorized', $redirect_to, $uri)); exit; - } } - } - - //Allow control of the admin dashboard URL's too - public static function admin_rule_redirection() { - $uri = esc_url($_SERVER['REQUEST_URI']); - $mepr_options = MeprOptions::fetch(); - $delim = MeprAppCtrl::get_param_delimiter_char($mepr_options->unauthorized_redirect_url); - - // This performs an unauthorized redirection based on the uri - if(MeprRule::is_uri_locked($uri)) { - if($mepr_options->redirect_on_unauthorized) { //Send to unauth page - $redirect_url = "{$mepr_options->unauthorized_redirect_url}{$delim}action=mepr_unauthorized&redirect_to=".urlencode($uri); - } - else { //Send to login page - $redirect_url = $mepr_options->login_page_url("action=mepr_unauthorized&redirect_to=".urlencode($uri)); - } - //Handle SSL - $redirect_url = (MeprUtils::is_ssl())?str_replace('http:', 'https:', $redirect_url):$redirect_url; - MeprUtils::wp_redirect($redirect_url); - exit; - } - } - - /** - * Redirect to login page or unauth page - * Used by addons BBPress and MP Downloads - * @param WP_Post $post - * @return void - */ - public static function redirect_unauthorized($post) { - $mepr_options = MeprOptions::fetch(); - $uri = urlencode(esc_url($_SERVER['REQUEST_URI'])); + public static function should_do_redirect() + { + global $wp_query; + $mepr_options = MeprOptions::fetch(); + + if (!empty($wp_query->posts) && $mepr_options->redirect_non_singular) { + // If even one post on this non-singular page is protected, let's redirect brotha + foreach ($wp_query->posts as $post) { + if (MeprRule::is_locked($post)) { + return true; + } + } + } - if($mepr_options->redirect_on_unauthorized) { - $delim = MeprAppCtrl::get_param_delimiter_char($mepr_options->unauthorized_redirect_url); - $redirect_to = "{$mepr_options->unauthorized_redirect_url}{$delim}mepr-unauth-page={$post->ID}&redirect_to={$uri}"; + return is_singular(); } - else { - $redirect_to = $mepr_options->login_page_url("action=mepr_unauthorized&mepr-unauth-page={$post->ID}&redirect_to=".$uri); - $redirect_to = (MeprUtils::is_ssl()) ? str_replace('http:', 'https:', $redirect_to) : $redirect_to; - } - MeprUtils::wp_redirect(MeprHooks::apply_filters('mepr-rule-redirect-unauthorized', $redirect_to, $uri)); - exit; - } - public static function should_do_redirect() { - global $wp_query; - $mepr_options = MeprOptions::fetch(); + /** + * Used to replace content for unauthorized visitors if redirect_on_unauthorized is not selected in MeprOptions. + */ + public static function rule_content($content) + { + $current_post = MeprUtils::get_current_post(); - if(!empty($wp_query->posts) && $mepr_options->redirect_non_singular) { - //If even one post on this non-singular page is protected, let's redirect brotha - foreach($wp_query->posts as $post) - if(MeprRule::is_locked($post)) - return true; - } + // This isn't a post? Just return the content then + if ($current_post === false) { + return $content; + } + + // WARNING the_content CAN be run more than once per page load + // so this static var prevents stuff from happening twice + // like cancelling a subscr or resuming etc... + static $already_run = []; + static $new_content = []; + static $content_length = []; + + // Init this posts static values + if (!isset($new_content[$current_post->ID]) || empty($new_content[$current_post->ID])) { + $already_run[$current_post->ID] = false; + $new_content[$current_post->ID] = ''; + $content_length[$current_post->ID] = -1; + } + + if ($already_run[$current_post->ID] && strlen($content) == $content_length[$current_post->ID]) { + return $new_content[$current_post->ID]; + } - return is_singular(); - } + $content_length[$current_post->ID] = strlen($content); + $already_run[$current_post->ID] = true; - /** Used to replace content for unauthorized visitors if redirect_on_unauthorized is not selected in MeprOptions. */ - public static function rule_content($content) { - $current_post = MeprUtils::get_current_post(); + // Get the URI + $uri = $_SERVER['REQUEST_URI']; - //This isn't a post? Just return the content then - if($current_post === false) { return $content; } + // Add this filter to allow external resources + // to control whether to show or hide this content + // if the resource sets the filter to FALSE then it will not be protected + if (!MeprHooks::apply_filters('mepr-pre-run-rule-content', true, $current_post, $uri)) { + // See notes above + $new_content[$current_post->ID] = $content; + return $new_content[$current_post->ID]; + } - //WARNING the_content CAN be run more than once per page load - //so this static var prevents stuff from happening twice - //like cancelling a subscr or resuming etc... - static $already_run = array(); - static $new_content = array(); - static $content_length = array(); + if (MeprRule::is_locked($current_post) || (MeprRule::is_uri_locked($uri))) { + $content = do_shortcode(self::unauthorized_message($current_post)); + } else { + // The user is allowed to see this content, but let's give developers one last chance to + // block it if necessary - will be very helpful for magazine style membership sites + // return TRUE here to block the content from this user + if (MeprHooks::apply_filters('mepr-last-chance-to-block-content', false, $current_post, $uri)) { + $content = do_shortcode(self::unauthorized_message($current_post)); + } + } - //Init this posts static values - if(!isset($new_content[$current_post->ID]) || empty($new_content[$current_post->ID])) { - $already_run[$current_post->ID] = false; - $new_content[$current_post->ID] = ''; - $content_length[$current_post->ID] = -1; + // See notes above + $new_content[$current_post->ID] = $content; + return $new_content[$current_post->ID]; } - if($already_run[$current_post->ID] && strlen($content) == $content_length[$current_post->ID]) { - return $new_content[$current_post->ID]; + public static function unauthorized_message_shortcode($atts = '') + { + $mepr_options = MeprOptions::fetch(); + $message = ''; + + if ( + isset($_REQUEST['mepr-unauth-page']) && + is_numeric($_REQUEST['mepr-unauth-page']) && + $post = get_post(esc_html($_REQUEST['mepr-unauth-page'])) + ) { + $message = self::unauthorized_message($post); + } elseif (isset($GLOBALS['post'])) { + $message = self::unauthorized_message($GLOBALS['post']); + } else { + $message = wpautop($mepr_options->unauthorized_message); + } + + return do_shortcode($message); } - $content_length[$current_post->ID] = strlen($content); - $already_run[$current_post->ID] = true; + public static function unauthorized_message($post) + { + $mepr_options = MeprOptions::fetch(); + $unauth = MeprRule::get_unauth_settings_for($post); - //Get the URI - $uri = $_SERVER['REQUEST_URI']; + static $login_form_shown = false; + $show_login = ($unauth->show_login && !$login_form_shown); - //Add this filter to allow external resources - //to control whether to show or hide this content - //if the resource sets the filter to FALSE then it will not be protected - if(!MeprHooks::apply_filters('mepr-pre-run-rule-content', true, $current_post, $uri)) { - //See notes above - $new_content[$current_post->ID] = $content; - return $new_content[$current_post->ID]; - } + // if this is a singular page, then allow it to be shown more than once + // it won't literally be shown on the page more than once, but in case something + // calls the_content filter during an earlier hook, we'll want to make sure the form shows + // up on the page itself still. + if ($show_login && !is_singular()) { + $login_form_shown = true; + } + + try { + $login_ctrl = MeprCtrlFactory::fetch('login'); + $form = MeprHooks::apply_filters('mepr-unauthorized-login-form', $login_ctrl->render_login_form(null, null, true), $post); + } catch (Exception $e) { + $form = '' . __('Login', 'memberpress') . ''; + } - if(MeprRule::is_locked($current_post) || (MeprRule::is_uri_locked($uri))) { - $content = do_shortcode(self::unauthorized_message($current_post)); - } - else { - //The user is allowed to see this content, but let's give developers one last chance to - //block it if necessary - will be very helpful for magazine style membership sites - //return TRUE here to block the content from this user - if(MeprHooks::apply_filters('mepr-last-chance-to-block-content', false, $current_post, $uri)) { - $content = do_shortcode(self::unauthorized_message($current_post)); - } - } - - //See notes above - $new_content[$current_post->ID] = $content; - return $new_content[$current_post->ID]; - } + ob_start(); + if (MeprReadyLaunchCtrl::template_enabled('account') || MeprAppHelper::has_block('memberpress/pro-account-tabs')) { + MeprView::render('/readylaunch/shared/unauthorized_message', get_defined_vars()); + } else { + if (isset($unauth->modern_paywall) && true === $unauth->modern_paywall && ! MeprAppHelper::is_memberpress_page($post)) { + MeprView::render('/shared/unauthorized_message_modern_paywall', get_defined_vars()); + } else { + MeprView::render('/shared/unauthorized_message', get_defined_vars()); + } + } - public static function unauthorized_message_shortcode($atts = '') { - $mepr_options = MeprOptions::fetch(); - $message = ''; + $content = ob_get_clean(); - if( isset($_REQUEST['mepr-unauth-page']) && - is_numeric($_REQUEST['mepr-unauth-page']) && - $post = get_post(esc_html($_REQUEST['mepr-unauth-page'])) ) { - $message = self::unauthorized_message($post); - } - elseif(isset($GLOBALS['post'])) { - $message = self::unauthorized_message($GLOBALS['post']); + // TODO: oEmbed still not working for some strange reason + return MeprHooks::apply_filters('mepr-unauthorized-content', $content, $post); } - else { - $message = wpautop($mepr_options->unauthorized_message); + + public static function add_meta_boxes() + { + add_meta_box('memberpress-rule-meta', __('Content & Access', 'memberpress'), 'MeprRulesCtrl::rule_meta_box', MeprRule::$cpt, 'normal', 'high'); + add_meta_box('memberpress-rule-drip', __('Drip / Expiration', 'memberpress'), 'MeprRulesCtrl::rule_drip_meta_box', MeprRule::$cpt, 'normal', 'high'); + add_meta_box('memberpress-rule-unauth', __('Unauthorized Access', 'memberpress'), 'MeprRulesCtrl::rule_unauth_meta_box', MeprRule::$cpt, 'normal', 'high'); } - return do_shortcode($message); - } + public static function save_postdata($post_id) + { + $post = get_post($post_id); - public static function unauthorized_message($post) { - $mepr_options = MeprOptions::fetch(); - $unauth = MeprRule::get_unauth_settings_for($post); + if (!wp_verify_nonce((isset($_POST[MeprRule::$mepr_nonce_str])) ? $_POST[MeprRule::$mepr_nonce_str] : '', MeprRule::$mepr_nonce_str . wp_salt())) { + return $post_id; // Nonce prevents meta data from being wiped on move to trash + } - static $login_form_shown = false; - $show_login = ($unauth->show_login && !$login_form_shown); + if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) { + return $post_id; + } - //if this is a singular page, then allow it to be shown more than once - //it won't literally be shown on the page more than once, but in case something - //calls the_content filter during an earlier hook, we'll want to make sure the form shows - //up on the page itself still. - if($show_login && !is_singular()) { $login_form_shown = true; } + if (defined('DOING_AJAX')) { + return; + } - try { - $login_ctrl = MeprCtrlFactory::fetch('login'); - $form = MeprHooks::apply_filters('mepr-unauthorized-login-form', $login_ctrl->render_login_form(null, null, true), $post); - } - catch(Exception $e) { - $form = ''.__('Login', 'memberpress').''; + if (!empty($post) && $post->post_type == MeprRule::$cpt) { + $rule = new MeprRule($post_id); + $rule->mepr_type = sanitize_text_field($_POST[MeprRule::$mepr_type_str]); + $rule->mepr_content = (('partial' != $_POST[MeprRule::$mepr_type_str] && isset($_POST[MeprRule::$mepr_content_str])) ? sanitize_text_field($_POST[MeprRule::$mepr_content_str]) : ''); + $rule->drip_enabled = isset($_POST[MeprRule::$drip_enabled_str]); + $rule->drip_amount = sanitize_text_field($_POST[MeprRule::$drip_amount_str]); + $rule->drip_unit = sanitize_text_field($_POST[MeprRule::$drip_unit_str]); + $rule->drip_after = sanitize_text_field($_POST[MeprRule::$drip_after_str]); + $rule->drip_after_fixed = sanitize_text_field($_POST[MeprRule::$drip_after_fixed_str]); + $rule->expires_enabled = isset($_POST[MeprRule::$expires_enabled_str]); + $rule->expires_amount = sanitize_text_field($_POST[MeprRule::$expires_amount_str]); + $rule->expires_unit = sanitize_text_field($_POST[MeprRule::$expires_unit_str]); + $rule->expires_after = sanitize_text_field($_POST[MeprRule::$expires_after_str]); + $rule->expires_after_fixed = sanitize_text_field($_POST[MeprRule::$expires_after_fixed_str]); + $rule->unauth_excerpt_type = sanitize_text_field($_POST[MeprRule::$unauth_excerpt_type_str]); + $rule->unauth_excerpt_size = sanitize_text_field($_POST[MeprRule::$unauth_excerpt_size_str]); + $rule->unauth_message_type = sanitize_text_field($_POST[MeprRule::$unauth_message_type_str]); + $rule->unauth_message = wp_kses_post(wp_unslash($_POST[MeprRule::$unauth_message_str])); + $rule->unauth_login = sanitize_text_field($_POST[MeprRule::$unauth_login_str]); + $rule->auto_gen_title = ($_POST[MeprRule::$auto_gen_title_str] == 'true'); + $rule->unauth_modern_paywall = isset($_POST[MeprRule::$unauth_modern_paywall_str]); + + $rule->is_mepr_content_regexp = isset($_POST[MeprRule::$is_mepr_content_regexp_str]); + + $rule->store_meta(); + + self::validate_rule_content($rule, $post_id); + + // Delete rules first then add them back below + MeprRuleAccessCondition::delete_all_by_rule($post_id); + + // Let's store the access rules + if (isset($_POST['mepr_access_row']) && !empty($_POST['mepr_access_row'])) { + foreach ($_POST['mepr_access_row']['type'] as $index => $access_type) { + $rule_access_condition = new MeprRuleAccessCondition($_POST['mepr_access_row']['rule_access_condition_id'][$index]); + $rule_access_condition->rule_id = $post_id; + $rule_access_condition->access_type = sanitize_text_field($access_type); + $rule_access_condition->access_operator = isset($_POST['mepr_access_row']['operator'][$index]) ? sanitize_text_field($_POST['mepr_access_row']['operator'][$index]) : ''; + $rule_access_condition->access_condition = isset($_POST['mepr_access_row']['condition'][$index]) ? sanitize_text_field($_POST['mepr_access_row']['condition'][$index]) : ''; + $rule_access_condition->store(); + } + } + } } - ob_start(); - if(MeprReadyLaunchCtrl::template_enabled( 'account' ) || MeprAppHelper::has_block('memberpress/pro-account-tabs')){ - MeprView::render('/readylaunch/shared/unauthorized_message', get_defined_vars()); - } else { - if ( isset($unauth->modern_paywall) && true === $unauth->modern_paywall && ! MeprAppHelper::is_memberpress_page( $post ) ) { - MeprView::render('/shared/unauthorized_message_modern_paywall', get_defined_vars()); - } else { - MeprView::render('/shared/unauthorized_message', get_defined_vars()); - } + public static function delete_access_rules($post_id) + { + $rule = new MeprRule($post_id); + $rule->delete_access_conditions(); } - $content = ob_get_clean(); + public static function rule_meta_box() + { + global $post_id; + $mepr_options = MeprOptions::fetch(); + + $rule = new MeprRule($post_id); + $rule_access_conditions = $rule->access_conditions(); + $server = strtolower($_SERVER['SERVER_SOFTWARE']); + + if (preg_match('/(apache|litespeed)/', $server)) { // LiteSpeed is essentially the same as Apache, only it claims to be twice as fast + $server = 'apache'; + $htaccess = ABSPATH . '.htaccess'; + $htaccess_writable = (file_exists($htaccess) and is_writable($htaccess)); + } elseif (preg_match('/nginx/', $server)) { + $server = 'nginx'; + } else { + $server = 'unknown'; + } - // TODO: oEmbed still not working for some strange reason - return MeprHooks::apply_filters('mepr-unauthorized-content', $content, $post); - } + MeprView::render('/admin/rules/form', get_defined_vars()); + } - public static function add_meta_boxes() { - add_meta_box("memberpress-rule-meta", __("Content & Access", "memberpress"), "MeprRulesCtrl::rule_meta_box", MeprRule::$cpt, "normal", "high"); - add_meta_box("memberpress-rule-drip", __("Drip / Expiration", "memberpress"), "MeprRulesCtrl::rule_drip_meta_box", MeprRule::$cpt, "normal", "high"); - add_meta_box("memberpress-rule-unauth", __("Unauthorized Access", "memberpress"), "MeprRulesCtrl::rule_unauth_meta_box", MeprRule::$cpt, "normal", "high"); - } + public static function rule_drip_meta_box() + { + global $post_id; - public static function save_postdata($post_id) { - $post = get_post($post_id); + $rule = new MeprRule($post_id); - if(!wp_verify_nonce((isset($_POST[MeprRule::$mepr_nonce_str]))?$_POST[MeprRule::$mepr_nonce_str]:'', MeprRule::$mepr_nonce_str.wp_salt())) { - return $post_id; //Nonce prevents meta data from being wiped on move to trash + MeprView::render('/admin/rules/drip_form', get_defined_vars()); } - if(defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) { return $post_id; } - - if(defined('DOING_AJAX')) { return; } - - if(!empty($post) && $post->post_type == MeprRule::$cpt) { - $rule = new MeprRule($post_id); - $rule->mepr_type = sanitize_text_field($_POST[MeprRule::$mepr_type_str]); - $rule->mepr_content = (('partial' != $_POST[MeprRule::$mepr_type_str] && isset($_POST[MeprRule::$mepr_content_str])) ? sanitize_text_field($_POST[MeprRule::$mepr_content_str]) : ''); - $rule->drip_enabled = isset($_POST[MeprRule::$drip_enabled_str]); - $rule->drip_amount = sanitize_text_field($_POST[MeprRule::$drip_amount_str]); - $rule->drip_unit = sanitize_text_field($_POST[MeprRule::$drip_unit_str]); - $rule->drip_after = sanitize_text_field($_POST[MeprRule::$drip_after_str]); - $rule->drip_after_fixed = sanitize_text_field($_POST[MeprRule::$drip_after_fixed_str]); - $rule->expires_enabled = isset($_POST[MeprRule::$expires_enabled_str]); - $rule->expires_amount = sanitize_text_field($_POST[MeprRule::$expires_amount_str]); - $rule->expires_unit = sanitize_text_field($_POST[MeprRule::$expires_unit_str]); - $rule->expires_after = sanitize_text_field($_POST[MeprRule::$expires_after_str]); - $rule->expires_after_fixed = sanitize_text_field($_POST[MeprRule::$expires_after_fixed_str]); - $rule->unauth_excerpt_type = sanitize_text_field($_POST[MeprRule::$unauth_excerpt_type_str]); - $rule->unauth_excerpt_size = sanitize_text_field($_POST[MeprRule::$unauth_excerpt_size_str]); - $rule->unauth_message_type = sanitize_text_field($_POST[MeprRule::$unauth_message_type_str]); - $rule->unauth_message = wp_kses_post(wp_unslash($_POST[MeprRule::$unauth_message_str])); - $rule->unauth_login = sanitize_text_field($_POST[MeprRule::$unauth_login_str]); - $rule->auto_gen_title = ($_POST[MeprRule::$auto_gen_title_str] == 'true'); - $rule->unauth_modern_paywall = isset($_POST[MeprRule::$unauth_modern_paywall_str]); + public static function rule_unauth_meta_box() + { + global $post_id; - $rule->is_mepr_content_regexp = isset($_POST[MeprRule::$is_mepr_content_regexp_str]); + $rule = new MeprRule($post_id); - $rule->store_meta(); + MeprView::render('/admin/rules/unauth_meta_box', get_defined_vars()); + } - self::validate_rule_content($rule, $post_id); + public static function display_content_dropdown() + { + check_ajax_referer('content_dropdown', 'content_dropdown_nonce'); - // Delete rules first then add them back below - MeprRuleAccessCondition::delete_all_by_rule($post_id); + if (!isset($_POST['field_name']) || !isset($_POST['type'])) { + die(__('Error', 'memberpress')); + } - // Let's store the access rules - if(isset($_POST['mepr_access_row']) && !empty($_POST['mepr_access_row'])) { - foreach($_POST['mepr_access_row']['type'] as $index => $access_type) { - $rule_access_condition = new MeprRuleAccessCondition($_POST['mepr_access_row']['rule_access_condition_id'][$index]); - $rule_access_condition->rule_id = $post_id; - $rule_access_condition->access_type = sanitize_text_field($access_type); - $rule_access_condition->access_operator = isset($_POST['mepr_access_row']['operator'][$index]) ? sanitize_text_field($_POST['mepr_access_row']['operator'][$index]) : ''; - $rule_access_condition->access_condition = isset($_POST['mepr_access_row']['condition'][$index]) ? sanitize_text_field($_POST['mepr_access_row']['condition'][$index]) : ''; - $rule_access_condition->store(); + if (MeprUtils::is_logged_in_and_an_admin()) { + MeprRulesHelper::content_dropdown($_POST['field_name'], '', $_POST['type']); } - } + + die(); } - } - public static function delete_access_rules($post_id) { - $rule = new MeprRule($post_id); - $rule->delete_access_conditions(); - } + public static function remove_access_condition() + { + check_ajax_referer('remove_access_condition', 'remove_access_condition_nonce'); - public static function rule_meta_box() { - global $post_id; - $mepr_options = MeprOptions::fetch(); + if (!isset($_POST['rule_access_condition_id'])) { + wp_die(__('Error', 'memberpress')); + } - $rule = new MeprRule($post_id); - $rule_access_conditions = $rule->access_conditions(); - $server = strtolower($_SERVER['SERVER_SOFTWARE']); + if (MeprUtils::is_logged_in_and_an_admin()) { + $rule_access_condition = new MeprRuleAccessCondition($_POST['rule_access_condition_id']); + $rule_access_condition->destroy(); + } - if(preg_match('/(apache|litespeed)/', $server)) { //LiteSpeed is essentially the same as Apache, only it claims to be twice as fast - $server = 'apache'; - $htaccess = ABSPATH . ".htaccess"; - $htaccess_writable = (file_exists($htaccess) and is_writable($htaccess)); - } - else if(preg_match('/nginx/',$server)) { - $server = 'nginx'; - } - else { - $server = 'unknown'; + wp_die(); } - MeprView::render('/admin/rules/form', get_defined_vars()); - } + public static function disable_row($actions, $post) + { + global $current_screen; - public static function rule_drip_meta_box() { - global $post_id; + if (!isset($current_screen->post_type) || $current_screen->post_type != MeprRule::$cpt) { + return $actions; + } - $rule = new MeprRule($post_id); + unset($actions['inline hide-if-no-js']); // Hides quick-edit - MeprView::render('/admin/rules/drip_form', get_defined_vars()); - } + return $actions; + } - public static function rule_unauth_meta_box() { - global $post_id; + public static function disable_bulk($actions) + { + unset($actions['edit']); // disables bulk edit - $rule = new MeprRule($post_id); + return $actions; + } - MeprView::render('/admin/rules/unauth_meta_box', get_defined_vars()); - } + public static function enqueue_scripts($hook) + { + global $current_screen; + + $wp_scripts = new WP_Scripts(); + $ui = $wp_scripts->query('jquery-ui-core'); + $url = "//ajax.googleapis.com/ajax/libs/jqueryui/{$ui->ver}/themes/smoothness/jquery-ui.css"; + + if ($current_screen->post_type == MeprRule::$cpt) { + $rules_json = [ + 'mepr_no_products_message' => __('Please select at least one Membership before saving.', 'memberpress'), + 'types' => MeprRule::get_types(), + 'content_dropdown_nonce' => wp_create_nonce('content_dropdown'), + 'content_search_nonce' => wp_create_nonce('content_search'), + 'remove_access_condition_nonce' => wp_create_nonce('remove_access_condition'), + 'access_row' => [ + 'role' => [ + 'row_tpl' => MeprRulesHelper::access_row_string(new MeprRuleAccessCondition(['access_type' => 'role']), 1), + 'types_tpl' => MeprRulesHelper::access_types_dropdown_string('role'), + 'operator_tpl' => MeprRulesHelper::access_operators_dropdown_string('role'), + 'condition_tpl' => MeprRulesHelper::access_conditions_dropdown_string('role'), + ], + 'capability' => [ + 'row_tpl' => MeprRulesHelper::access_row_string(new MeprRuleAccessCondition(['access_type' => 'capability']), 1), + 'types_tpl' => MeprRulesHelper::access_types_dropdown_string('capability'), + 'operator_tpl' => MeprRulesHelper::access_operators_dropdown_string('capability'), + 'condition_tpl' => MeprRulesHelper::access_conditions_dropdown_string('capability'), + ], + 'membership' => [ + 'row_tpl' => MeprRulesHelper::access_row_string(new MeprRuleAccessCondition(['access_type' => 'membership']), 1), + 'types_tpl' => MeprRulesHelper::access_types_dropdown_string('membership'), + 'operator_tpl' => MeprRulesHelper::access_operators_dropdown_string('membership'), + 'condition_tpl' => MeprRulesHelper::access_conditions_dropdown_string('membership'), + ], + 'member' => [ + 'row_tpl' => MeprRulesHelper::access_row_string(new MeprRuleAccessCondition(['access_type' => 'member']), 1), + 'types_tpl' => MeprRulesHelper::access_types_dropdown_string('member'), + 'operator_tpl' => MeprRulesHelper::access_operators_dropdown_string('member'), + 'condition_tpl' => MeprRulesHelper::access_conditions_dropdown_string('member'), + ], + 'blank' => [ + 'row_tpl' => MeprRulesHelper::access_row_string(new MeprRuleAccessCondition(), 1), + 'types_tpl' => MeprRulesHelper::access_types_dropdown_string(), + 'operator_tpl' => MeprRulesHelper::access_operators_dropdown_string(), + 'condition_tpl' => MeprRulesHelper::access_conditions_dropdown_string(), + ], + ], + ]; + + wp_register_style('mepr-jquery-ui-smoothness', $url); + wp_enqueue_style('jquery-ui-timepicker-addon', MEPR_CSS_URL . '/jquery-ui-timepicker-addon.css', ['mepr-jquery-ui-smoothness']); + wp_register_script('mepr-timepicker-js', MEPR_JS_URL . '/jquery-ui-timepicker-addon.js', ['jquery-ui-datepicker']); + wp_register_script('mepr-date-picker-js', MEPR_JS_URL . '/date_picker.js', ['mepr-timepicker-js'], MEPR_VERSION); + wp_register_script('rule-form-validator', '//cdnjs.cloudflare.com/ajax/libs/jquery-form-validator/2.3.26/jquery.form-validator.min.js', ['jquery'], '2.3.26'); + wp_dequeue_script('autosave'); // Disable auto-saving + // Need mepr-rules-js to load in the footer since this script doesn't fully use document.ready() + wp_enqueue_script('mepr-rules-js', MEPR_JS_URL . '/admin_rules.js', ['jquery','jquery-ui-autocomplete','mepr-date-picker-js','rule-form-validator'], MEPR_VERSION, true); + wp_register_style('mepr-simplegrid', MEPR_CSS_URL . '/vendor/simplegrid.css', [], MEPR_VERSION); + wp_enqueue_style('mepr-rules-css', MEPR_CSS_URL . '/admin-rules.css', ['mepr-simplegrid'], MEPR_VERSION); + wp_localize_script('mepr-rules-js', 'MeprRule', $rules_json); + wp_enqueue_script('mepr-helpers', MEPR_JS_URL . '/mphelpers.js', ['suggest'], MEPR_VERSION); + } + } - public static function display_content_dropdown() { - check_ajax_referer('content_dropdown', 'content_dropdown_nonce'); - - if(!isset($_POST['field_name']) || !isset($_POST['type'])) { - die(__('Error', 'memberpress')); - } - - if(MeprUtils::is_logged_in_and_an_admin()) { - MeprRulesHelper::content_dropdown($_POST['field_name'], '', $_POST['type']); - } - - die(); - } - - public static function remove_access_condition() { - check_ajax_referer('remove_access_condition', 'remove_access_condition_nonce'); - - if(!isset($_POST['rule_access_condition_id'])) { - wp_die(__('Error', 'memberpress')); - } - - if(MeprUtils::is_logged_in_and_an_admin()) { - $rule_access_condition = new MeprRuleAccessCondition($_POST['rule_access_condition_id']); - $rule_access_condition->destroy(); - } + public static function mod_rewrite_rules($rules) + { + $mepr_options = MeprOptions::fetch(); - wp_die(); - } + // If disabled mod_rewrite is checked let's not go on + if ($mepr_options->disable_mod_rewrite) { + return $rules; + } - public static function disable_row($actions, $post) { - global $current_screen; + $rule_uri = MEPR_URL . '/lock.php'; + $rule_path = preg_replace('#^(https?:)?//[^/]+#', '', $rule_uri); // grab the root + $subdir = preg_replace('#^https?://[^/]+#', '', site_url()); + $mepr_rules = "\n"; + $mepr_rules .= "# BEGIN MemberPress Rules\n"; + $mepr_rules .= "\n\n"; + + // Make sure there's been a cookie set for us to access the file + $mepr_rules .= "RewriteCond %{HTTP_COOKIE} mplk=([a-zA-Z0-9]+)\n"; + + // See if there's also a rule file for the rule hash + $mepr_rules .= 'RewriteCond ' . MeprRule::rewrite_rule_file_dir(true) . "/%1 -f\n"; + // If rule hash exists in query string, there's a rule file and they match then short circuit to the actual url + $mepr_rules .= "RewriteRule ^(.*)$ - [L]\n\n"; + // If the url is the lock url then don't lock it or we'll end up in an infinite redirect + // Don't need this now that we're bypassing php files alltogether + // $mepr_rules .= "RewriteRule memberpress\/lock\.php$ - [L]\n"; + // Directories that we shouldn't allow to be protected + $no_protect_dirs = MeprHooks::apply_filters('mepr_rewrite_rules_no_protect_dirs', ['wp-admin','wp-includes','wp-content/plugins','wp-content/themes'], $rules); + $npstr = implode('|', $no_protect_dirs); + $mepr_rules .= 'RewriteCond %{REQUEST_URI} !^/(' . $npstr . ")\n"; + + // File types that we will allow to be protected + // Eventually we can maybe make this configurable by the user ... + $protect_types = MeprHooks::apply_filters('mepr_rewrite_rules_protect_types', ['zip','gz','tar','rar','doc','docx','xls','xlsx','xlsm','pdf','mp4','m4v','mp3','ts','key','m3u8'], $rules); + $ptstr = implode('|', $protect_types); + $mepr_rules .= 'RewriteCond %{REQUEST_URI} \.(' . strtolower($ptstr) . '|' . strtoupper($ptstr) . ")$\n"; + + // All else fails ... run it through lock.php to see if it's protected + $mepr_rules .= "RewriteRule . {$rule_path} [L]\n\n"; + $mepr_rules .= "\n"; + $mepr_rules .= "# END MemberPress Rules\n"; + + $mepr_rules = MeprHooks::apply_filters('mepr_rewrite_rules', $mepr_rules, $rules); + + // Mepr rules must appear *AFTER* wp's rules because we + // don't know how wp will handle the uri unless its a file + return $rules . $mepr_rules; + } - if(!isset($current_screen->post_type) || $current_screen->post_type != MeprRule::$cpt) { - return $actions; + // Deprecated + public static function rule_shortcode($atts, $content = '') + { + return self::protect_shortcode_content($atts, $content, 'mp-rule'); } - unset($actions['inline hide-if-no-js']); //Hides quick-edit - - return $actions; - } - - public static function disable_bulk($actions) { - unset($actions['edit']); //disables bulk edit - - return $actions; - } - - public static function enqueue_scripts($hook) { - global $current_screen; - - $wp_scripts = new WP_Scripts(); - $ui = $wp_scripts->query('jquery-ui-core'); - $url = "//ajax.googleapis.com/ajax/libs/jqueryui/{$ui->ver}/themes/smoothness/jquery-ui.css"; - - if($current_screen->post_type == MeprRule::$cpt) { - $rules_json = array( - 'mepr_no_products_message' => __('Please select at least one Membership before saving.', 'memberpress'), - 'types' => MeprRule::get_types(), - 'content_dropdown_nonce' => wp_create_nonce('content_dropdown'), - 'content_search_nonce' => wp_create_nonce('content_search'), - 'remove_access_condition_nonce' => wp_create_nonce('remove_access_condition'), - 'access_row' => array( - 'role' => array( - 'row_tpl' => MeprRulesHelper::access_row_string(new MeprRuleAccessCondition(array('access_type' => 'role')),1), - 'types_tpl' => MeprRulesHelper::access_types_dropdown_string('role'), - 'operator_tpl' => MeprRulesHelper::access_operators_dropdown_string('role'), - 'condition_tpl' => MeprRulesHelper::access_conditions_dropdown_string('role') - ), - 'capability' => array( - 'row_tpl' => MeprRulesHelper::access_row_string(new MeprRuleAccessCondition(array('access_type' => 'capability')),1), - 'types_tpl' => MeprRulesHelper::access_types_dropdown_string('capability'), - 'operator_tpl' => MeprRulesHelper::access_operators_dropdown_string('capability'), - 'condition_tpl' => MeprRulesHelper::access_conditions_dropdown_string('capability') - ), - 'membership' => array( - 'row_tpl' => MeprRulesHelper::access_row_string(new MeprRuleAccessCondition(array('access_type' => 'membership')),1), - 'types_tpl' => MeprRulesHelper::access_types_dropdown_string('membership'), - 'operator_tpl' => MeprRulesHelper::access_operators_dropdown_string('membership'), - 'condition_tpl' => MeprRulesHelper::access_conditions_dropdown_string('membership') - ), - 'member' => array( - 'row_tpl' => MeprRulesHelper::access_row_string(new MeprRuleAccessCondition(array('access_type' => 'member')),1), - 'types_tpl' => MeprRulesHelper::access_types_dropdown_string('member'), - 'operator_tpl' => MeprRulesHelper::access_operators_dropdown_string('member'), - 'condition_tpl' => MeprRulesHelper::access_conditions_dropdown_string('member') - ), - 'blank' => array( - 'row_tpl' => MeprRulesHelper::access_row_string(new MeprRuleAccessCondition(),1), - 'types_tpl' => MeprRulesHelper::access_types_dropdown_string(), - 'operator_tpl' => MeprRulesHelper::access_operators_dropdown_string(), - 'condition_tpl' => MeprRulesHelper::access_conditions_dropdown_string() - ) - ) - ); - - wp_register_style('mepr-jquery-ui-smoothness', $url); - wp_enqueue_style('jquery-ui-timepicker-addon', MEPR_CSS_URL.'/jquery-ui-timepicker-addon.css', array('mepr-jquery-ui-smoothness')); - wp_register_script('mepr-timepicker-js', MEPR_JS_URL.'/jquery-ui-timepicker-addon.js', array('jquery-ui-datepicker')); - wp_register_script('mepr-date-picker-js', MEPR_JS_URL.'/date_picker.js', array('mepr-timepicker-js'), MEPR_VERSION); - wp_register_script('rule-form-validator', '//cdnjs.cloudflare.com/ajax/libs/jquery-form-validator/2.3.26/jquery.form-validator.min.js', array('jquery'), '2.3.26'); - wp_dequeue_script('autosave'); //Disable auto-saving - //Need mepr-rules-js to load in the footer since this script doesn't fully use document.ready() - wp_enqueue_script('mepr-rules-js', MEPR_JS_URL.'/admin_rules.js', array('jquery','jquery-ui-autocomplete','mepr-date-picker-js','rule-form-validator'), MEPR_VERSION, true); - wp_register_style('mepr-simplegrid', MEPR_CSS_URL.'/vendor/simplegrid.css', array(), MEPR_VERSION); - wp_enqueue_style('mepr-rules-css', MEPR_CSS_URL.'/admin-rules.css', array('mepr-simplegrid'), MEPR_VERSION); - wp_localize_script('mepr-rules-js', 'MeprRule', $rules_json); - wp_enqueue_script('mepr-helpers', MEPR_JS_URL . '/mphelpers.js', array('suggest'), MEPR_VERSION); - } - } - - public static function mod_rewrite_rules($rules) { - $mepr_options = MeprOptions::fetch(); - - //If disabled mod_rewrite is checked let's not go on - if($mepr_options->disable_mod_rewrite) { return $rules; } - - $rule_uri = MEPR_URL . '/lock.php'; - $rule_path = preg_replace('#^(https?:)?//[^/]+#','',$rule_uri); // grab the root - $subdir = preg_replace("#^https?://[^/]+#", '', site_url()); - $mepr_rules = "\n"; - $mepr_rules .= "# BEGIN MemberPress Rules\n"; - $mepr_rules .= "\n\n"; - - // Make sure there's been a cookie set for us to access the file - $mepr_rules .= "RewriteCond %{HTTP_COOKIE} mplk=([a-zA-Z0-9]+)\n"; - - // See if there's also a rule file for the rule hash - $mepr_rules .= "RewriteCond " . MeprRule::rewrite_rule_file_dir(true) . "/%1 -f\n"; - // If rule hash exists in query string, there's a rule file and they match then short circuit to the actual url - $mepr_rules .= "RewriteRule ^(.*)$ - [L]\n\n"; - // If the url is the lock url then don't lock it or we'll end up in an infinite redirect - // Don't need this now that we're bypassing php files alltogether - //$mepr_rules .= "RewriteRule memberpress\/lock\.php$ - [L]\n"; - - // Directories that we shouldn't allow to be protected - $no_protect_dirs = MeprHooks::apply_filters('mepr_rewrite_rules_no_protect_dirs', array('wp-admin','wp-includes','wp-content/plugins','wp-content/themes'), $rules); - $npstr = implode('|', $no_protect_dirs); - $mepr_rules .= 'RewriteCond %{REQUEST_URI} !^/('.$npstr.")\n"; - - // File types that we will allow to be protected - // Eventually we can maybe make this configurable by the user ... - $protect_types = MeprHooks::apply_filters('mepr_rewrite_rules_protect_types', array('zip','gz','tar','rar','doc','docx','xls','xlsx','xlsm','pdf','mp4','m4v','mp3','ts','key','m3u8'), $rules); - $ptstr = implode('|', $protect_types); - $mepr_rules .= 'RewriteCond %{REQUEST_URI} \.('.strtolower($ptstr).'|'.strtoupper($ptstr).")$\n"; - - // All else fails ... run it through lock.php to see if it's protected - $mepr_rules .= "RewriteRule . {$rule_path} [L]\n\n"; - $mepr_rules .= "\n"; - $mepr_rules .= "# END MemberPress Rules\n"; - - $mepr_rules = MeprHooks::apply_filters('mepr_rewrite_rules', $mepr_rules, $rules); - - // Mepr rules must appear *AFTER* wp's rules because we - // don't know how wp will handle the uri unless its a file - return $rules.$mepr_rules; - } - - // Deprecated - public static function rule_shortcode($atts, $content='') { - return self::protect_shortcode_content($atts, $content, 'mp-rule'); - } - - public static function active_shortcode($atts, $content='') { - return self::protect_shortcode_content($atts, $content); - } - - public static function show_shortcode($atts, $content='') { - return self::protect_shortcode_content($atts, $content, 'mepr-show'); - } - - public static function hide_shortcode($atts, $content='') { - return self::protect_shortcode_content($atts, $content, 'mepr-hide'); - } - - public static function protect_shortcode_content($atts, $content='', $shortcode_type='mp-active') { - $mepr_options = MeprOptions::fetch(); - - //Allow single level shortcode nesting - //This only works if the inner shortcode does NOT have an ending tag - $content = do_shortcode($content); - - if($shortcode_type==='mepr-show') { - $hide_if_allowed = false; - } - elseif($shortcode_type==='mepr-hide') { - $hide_if_allowed = true; - } - else { - $hide_if_allowed = ( - ((isset($atts['hide']) && trim($atts['hide']) == 'true') || - (isset($atts['ifallowed']) && trim($atts['ifallowed']) == 'hide')) - ); - } - - $unauth = ''; - if(isset($atts['unauth'])) { - if(trim($atts['unauth'])=='message' || trim($atts['unauth'])=='both') { - if(isset($atts['unauth_message'])) { - $unauth = '
    '.trim($atts['unauth_message']).'
    '; - } - else { - $rule_ids = array(); - - if(isset($atts['rule'])) { - $rule_ids = array_map('trim', explode(',', $atts['rule'])); - } - if(isset($atts['rules'])) { - $rule_ids = array_map('trim', explode(',', $atts['rules'])); - } - - $unauth = MeprRule::get_custom_unauth_message_from_rule_ids($rule_ids); - } - } - - if(trim($atts['unauth'])=='login' || trim($atts['unauth'])=='both') { - try { - $login_ctrl = MeprCtrlFactory::fetch('login'); - $unauth .= '
    '.$login_ctrl->render_login_form().'
    '; - } - catch(Exception $e) { - $unauth = '
    '.__('Login', 'memberpress').'
    '; - } - } + public static function active_shortcode($atts, $content = '') + { + return self::protect_shortcode_content($atts, $content); } - $allowed = false; - if(isset($atts['if']) && preg_match('/^logged[ _-]?in$/', $atts['if'])) { - $allowed = MeprUtils::is_user_logged_in(); + public static function show_shortcode($atts, $content = '') + { + return self::protect_shortcode_content($atts, $content, 'mepr-show'); } - else if(isset($atts['if']) && preg_match('/^logged[ _-]?out$/', $atts['if'])) { - $allowed = !MeprUtils::is_user_logged_in(); + + public static function hide_shortcode($atts, $content = '') + { + return self::protect_shortcode_content($atts, $content, 'mepr-hide'); } - else { - //Check if we've been given sanitary input, if not this shortcode - //is no good so let's return the full content here - if(MeprUtils::is_mepr_admin()) { return ($hide_if_allowed?$unauth:$content); } - if(MeprUtils::is_user_logged_in()) { - // Deprecated - if($shortcode_type=='mp-rule') { - $allowed = ( - (isset($atts['id']) && current_user_can('mepr-active',"rule: {$atts['id']}")) || - (isset($atts['ids']) && current_user_can('mepr-active',"rules: {$atts['ids']}")) - ); + public static function protect_shortcode_content($atts, $content = '', $shortcode_type = 'mp-active') + { + $mepr_options = MeprOptions::fetch(); + + // Allow single level shortcode nesting + // This only works if the inner shortcode does NOT have an ending tag + $content = do_shortcode($content); + + if ($shortcode_type === 'mepr-show') { + $hide_if_allowed = false; + } elseif ($shortcode_type === 'mepr-hide') { + $hide_if_allowed = true; + } else { + $hide_if_allowed = ( + ((isset($atts['hide']) && trim($atts['hide']) == 'true') || + (isset($atts['ifallowed']) && trim($atts['ifallowed']) == 'hide')) + ); } - else { - $allowed = ( - (isset($atts['if']) && current_user_can('mepr-active',$atts['if'])) || - (isset($atts['id']) && current_user_can('mepr-active',$atts['id'])) || - (isset($atts['ids']) && current_user_can('mepr-active',$atts['ids'])) || - (isset($atts['rule']) && current_user_can('mepr-active',"rule: {$atts['rule']}")) || - (isset($atts['rules']) && current_user_can('mepr-active',"rules: {$atts['rules']}")) || - (isset($atts['product']) && current_user_can('mepr-active',"product: {$atts['product']}")) || - (isset($atts['products']) && current_user_can('mepr-active',"products: {$atts['products']}")) || - (isset($atts['membership']) && current_user_can('mepr-active',"membership: {$atts['membership']}")) || - (isset($atts['memberships']) && current_user_can('mepr-active',"membership: {$atts['memberships']}")) - ); + + $unauth = ''; + if (isset($atts['unauth'])) { + if (trim($atts['unauth']) == 'message' || trim($atts['unauth']) == 'both') { + if (isset($atts['unauth_message'])) { + $unauth = '
    ' . trim($atts['unauth_message']) . '
    '; + } else { + $rule_ids = []; + + if (isset($atts['rule'])) { + $rule_ids = array_map('trim', explode(',', $atts['rule'])); + } + if (isset($atts['rules'])) { + $rule_ids = array_map('trim', explode(',', $atts['rules'])); + } + + $unauth = MeprRule::get_custom_unauth_message_from_rule_ids($rule_ids); + } + } + + if (trim($atts['unauth']) == 'login' || trim($atts['unauth']) == 'both') { + try { + $login_ctrl = MeprCtrlFactory::fetch('login'); + $unauth .= '
    ' . $login_ctrl->render_login_form() . '
    '; + } catch (Exception $e) { + $unauth = '
    ' . __('Login', 'memberpress') . '
    '; + } + } } - } - } - return ((($allowed && !$hide_if_allowed) || (!$allowed && $hide_if_allowed))?$content:$unauth); - } + $allowed = false; + if (isset($atts['if']) && preg_match('/^logged[ _-]?in$/', $atts['if'])) { + $allowed = MeprUtils::is_user_logged_in(); + } elseif (isset($atts['if']) && preg_match('/^logged[ _-]?out$/', $atts['if'])) { + $allowed = !MeprUtils::is_user_logged_in(); + } else { + // Check if we've been given sanitary input, if not this shortcode + // is no good so let's return the full content here + if (MeprUtils::is_mepr_admin()) { + return ($hide_if_allowed ? $unauth : $content); + } - /* This will only work once $post is in place in the wp request flow */ - /* Will support dashes, underscores, full plugin name, short plugin name and authorized or auth */ - public static function authorized_cap($caps, $cap, $args) { - $regex = '(memberpress|mepr)[-_]auth(orized)?'; + if (MeprUtils::is_user_logged_in()) { + // Deprecated + if ($shortcode_type == 'mp-rule') { + $allowed = ( + (isset($atts['id']) && current_user_can('mepr-active', "rule: {$atts['id']}")) || + (isset($atts['ids']) && current_user_can('mepr-active', "rules: {$atts['ids']}")) + ); + } else { + $allowed = ( + (isset($atts['if']) && current_user_can('mepr-active', $atts['if'])) || + (isset($atts['id']) && current_user_can('mepr-active', $atts['id'])) || + (isset($atts['ids']) && current_user_can('mepr-active', $atts['ids'])) || + (isset($atts['rule']) && current_user_can('mepr-active', "rule: {$atts['rule']}")) || + (isset($atts['rules']) && current_user_can('mepr-active', "rules: {$atts['rules']}")) || + (isset($atts['product']) && current_user_can('mepr-active', "product: {$atts['product']}")) || + (isset($atts['products']) && current_user_can('mepr-active', "products: {$atts['products']}")) || + (isset($atts['membership']) && current_user_can('mepr-active', "membership: {$atts['membership']}")) || + (isset($atts['memberships']) && current_user_can('mepr-active', "membership: {$atts['memberships']}")) + ); + } + } + } - if(!isset($cap[0]) || !preg_match("/^{$regex}$/", $cap[0])) { - return $caps; + return ((($allowed && !$hide_if_allowed) || (!$allowed && $hide_if_allowed)) ? $content : $unauth); } - $caps[$cap[0]] = 1; - $current_post = MeprUtils::get_current_post(); + /* + This will only work once $post is in place in the wp request flow */ + /* Will support dashes, underscores, full plugin name, short plugin name and authorized or auth */ + public static function authorized_cap($caps, $cap, $args) + { + $regex = '(memberpress|mepr)[-_]auth(orized)?'; - // General MemberPress Authorized for this page - if(($current_post !== false && MeprRule::is_locked($current_post)) || - MeprRule::is_uri_locked($_SERVER['REQUEST_URI'])) { - unset($caps[$cap[0]]); - } + if (!isset($cap[0]) || !preg_match("/^{$regex}$/", $cap[0])) { + return $caps; + } - return $caps; - } + $caps[$cap[0]] = 1; + $current_post = MeprUtils::get_current_post(); - // Deprecated - /* membership based capabilities */ - public static function product_authorized_cap($caps, $cap, $args) { - $regex = '(memberpress|mepr)[-_](product|membership)[-_]auth(orized)?[-_](\d+)'; + // General MemberPress Authorized for this page + if ( + ($current_post !== false && MeprRule::is_locked($current_post)) || + MeprRule::is_uri_locked($_SERVER['REQUEST_URI']) + ) { + unset($caps[$cap[0]]); + } - if(!isset($cap[0]) || !preg_match("/^{$regex}$/i", $cap[0], $m)) { - return $caps; + return $caps; } - //User is most likely a guest, so they don't have access to whatever we're doing here - if(!isset($args[1]) || !$args[1]) { - return $caps; - } + // Deprecated + /* membership based capabilities */ + public static function product_authorized_cap($caps, $cap, $args) + { + $regex = '(memberpress|mepr)[-_](product|membership)[-_]auth(orized)?[-_](\d+)'; - $user = new MeprUser($args[1]); - $ids = $user->active_product_subscriptions(); + if (!isset($cap[0]) || !preg_match("/^{$regex}$/i", $cap[0], $m)) { + return $caps; + } - if(MeprUtils::is_mepr_admin() || in_array($m[4], $ids)) { $caps[$cap[0]] = 1; } + // User is most likely a guest, so they don't have access to whatever we're doing here + if (!isset($args[1]) || !$args[1]) { + return $caps; + } - return $caps; - } + $user = new MeprUser($args[1]); + $ids = $user->active_product_subscriptions(); - // Deprecated - /* rule based capabilities */ - public static function rule_authorized_cap($caps, $cap, $args) { - $regex = '(memberpress|mepr)[-_]rule[-_]auth(orized)?[-_](\d+)'; + if (MeprUtils::is_mepr_admin() || in_array($m[4], $ids)) { + $caps[$cap[0]] = 1; + } - if(!isset($cap[0]) || !preg_match("/^{$regex}$/i", $cap[0], $m)) { - return $caps; + return $caps; } - // User is most likely a guest, so they don't have access to whatever we're doing here - if(!isset($args[1]) || !$args[1]) { - return $caps; - } + // Deprecated + /* rule based capabilities */ + public static function rule_authorized_cap($caps, $cap, $args) + { + $regex = '(memberpress|mepr)[-_]rule[-_]auth(orized)?[-_](\d+)'; - $rule_id = $m[3]; + if (!isset($cap[0]) || !preg_match("/^{$regex}$/i", $cap[0], $m)) { + return $caps; + } - $user = new MeprUser($args[1]); - $rule = new MeprRule($rule_id); + // User is most likely a guest, so they don't have access to whatever we're doing here + if (!isset($args[1]) || !$args[1]) { + return $caps; + } - if($rule->ID <= 0 || !$rule->has_dripped() || $rule->has_expired()) { return $caps; } - if($user->has_access_from_rule($rule_id)) { $caps[$cap[0]] = 1; } + $rule_id = $m[3]; - return $caps; - } + $user = new MeprUser($args[1]); + $rule = new MeprRule($rule_id); - /* Is the user active on any membership, one specific rule or one specific membership? */ - public static function active_cap($caps, $cap, $args) { - $active_str = 'mepr-active'; + if ($rule->ID <= 0 || !$rule->has_dripped() || $rule->has_expired()) { + return $caps; + } + if ($user->has_access_from_rule($rule_id)) { + $caps[$cap[0]] = 1; + } - if(!isset($cap[0]) || !preg_match("/^{$active_str}$/", $cap[0])) { - return $caps; + return $caps; } - //User is most likely a guest, so they don't have access to whatever we're doing here - if(!isset($args[1]) || !$args[1]) { - return $caps; - } + /* Is the user active on any membership, one specific rule or one specific membership? */ + public static function active_cap($caps, $cap, $args) + { + $active_str = 'mepr-active'; - $user = new MeprUser($args[1]); - $ids = $user->active_product_subscriptions(); + if (!isset($cap[0]) || !preg_match("/^{$active_str}$/", $cap[0])) { + return $caps; + } - if(MeprUtils::is_mepr_admin($user->ID)) { - $caps[$active_str] = 1; - } - else { - // membership specific active - if(isset($args[2])) { - // If it's a membership then check that it's in the active membership subscriptions array - if(is_numeric($args[2]) && is_array($ids) && !empty($ids)) { - if(in_array($args[2],$ids)) { - $caps[$active_str] = 1; - } - } - // If it's spelled out as a product or membership do the same thing here - else if(preg_match('/^((product|membership)s?\s*[=:_-]?\s*)?((\d+\s*,\s*)*\d+)$/i',$args[2],$m) && is_array($ids) && !empty($ids)) { - $product_ids = array_map('trim', explode(',',$m[3])); - if(is_array($product_ids) && !empty($product_ids) && - ($intersect = array_intersect($product_ids, $ids)) && - !empty($intersect)) { + // User is most likely a guest, so they don't have access to whatever we're doing here + if (!isset($args[1]) || !$args[1]) { + return $caps; + } + + $user = new MeprUser($args[1]); + $ids = $user->active_product_subscriptions(); + + if (MeprUtils::is_mepr_admin($user->ID)) { $caps[$active_str] = 1; - } - } - // If it's an array then check that it's in the active membership subscriptions array - else if(preg_match('/^rules?\s*[=:_-]?\s*((\d+\s*,\s*)*\d+)$/i',$args[2],$m)) { - $product_ids = array(); - $rule_ids = array_map('trim', explode(',',$m[1])); - - if(is_array($rule_ids) && !empty($rule_ids)) { - foreach($rule_ids as $rule_id) { - $rule = new MeprRule($rule_id); - if($rule->ID <= 0 || !$rule->has_dripped() || $rule->has_expired()) { continue; } - if($user->has_access_from_rule($rule_id)) { + } else { + // membership specific active + if (isset($args[2])) { + // If it's a membership then check that it's in the active membership subscriptions array + if (is_numeric($args[2]) && is_array($ids) && !empty($ids)) { + if (in_array($args[2], $ids)) { + $caps[$active_str] = 1; + } + } elseif (preg_match('/^((product|membership)s?\s*[=:_-]?\s*)?((\d+\s*,\s*)*\d+)$/i', $args[2], $m) && is_array($ids) && !empty($ids)) { + // If it's spelled out as a product or membership do the same thing here + $product_ids = array_map('trim', explode(',', $m[3])); + if ( + is_array($product_ids) && !empty($product_ids) && + ($intersect = array_intersect($product_ids, $ids)) && + !empty($intersect) + ) { + $caps[$active_str] = 1; + } + } elseif (preg_match('/^rules?\s*[=:_-]?\s*((\d+\s*,\s*)*\d+)$/i', $args[2], $m)) { + // If it's an array then check that it's in the active membership subscriptions array + $product_ids = []; + $rule_ids = array_map('trim', explode(',', $m[1])); + + if (is_array($rule_ids) && !empty($rule_ids)) { + foreach ($rule_ids as $rule_id) { + $rule = new MeprRule($rule_id); + if ($rule->ID <= 0 || !$rule->has_dripped() || $rule->has_expired()) { + continue; + } + if ($user->has_access_from_rule($rule_id)) { + $caps[$active_str] = 1; + break; + } + } + } + } + } else { $caps[$active_str] = 1; - break; - } } - } } - } - else { - $caps[$active_str] = 1; - } + + return $caps; } - return $caps; - } + public static function ajax_content_search() + { + // Array( [action] => mepr_rule_content_search [type] => single_post [term] => you) + check_ajax_referer('content_search', 'content_search_nonce'); - public static function ajax_content_search() { - //Array( [action] => mepr_rule_content_search [type] => single_post [term] => you) - check_ajax_referer('content_search', 'content_search_nonce'); + $type = sanitize_text_field($_REQUEST['type']); + $term = sanitize_text_field($_REQUEST['term']); - $type = sanitize_text_field($_REQUEST['type']); - $term = sanitize_text_field($_REQUEST['term']); + $data = MeprRule::search_content($type, $term); + die(json_encode($data)); + } - $data = MeprRule::search_content($type, $term); - die(json_encode($data)); - } + public static function override_wc_is_purchasable($is, $prd) + { + // Bail if already locked, or is admin page, or is REST REQUEST + if (!$is || is_admin() || (defined('REST_REQUEST') && REST_REQUEST)) { + return $is; + } - public static function override_wc_is_purchasable($is, $prd) { - // Bail if already locked, or is admin page, or is REST REQUEST - if(!$is || is_admin() || (defined('REST_REQUEST') && REST_REQUEST)) { return $is; } + if (is_object($prd) && $prd->is_type('variation')) { + $post = get_post($prd->get_parent_id()); + } else { + $post = get_post($prd->get_id()); + } - if(is_object($prd) && $prd->is_type('variation')) { - $post = get_post($prd->get_parent_id()); - } else { - $post = get_post($prd->get_id()); + return !MeprRule::is_locked($post); } - return !MeprRule::is_locked($post); - } + public static function override_wc_is_visible($is, $prd_id, $prd_parent_id = false, $prd = false) + { + // Bail if already locked, or is admin page, or is REST REQUEST + if (!$is || is_admin() || (defined('REST_REQUEST') && REST_REQUEST)) { + return $is; + } - public static function override_wc_is_visible($is, $prd_id, $prd_parent_id = false, $prd = false) { - // Bail if already locked, or is admin page, or is REST REQUEST - if(!$is || is_admin() || (defined('REST_REQUEST') && REST_REQUEST)) { return $is; } + if ($prd && $prd->is_type('variation')) { + $parent_product = wc_get_product($prd->get_parent_id()); - if($prd && $prd->is_type('variation')) { - $parent_product = wc_get_product($prd->get_parent_id()); + // If the parent product (product variable) is not purchasable (i.e. it's hidden), hide the variation's attributes. + if (!$parent_product->is_purchasable()) { + add_filter('woocommerce_product_get_attributes', 'MeprRulesCtrl::hide_product_attributes'); + } + } - // If the parent product (product variable) is not purchasable (i.e. it's hidden), hide the variation's attributes. - if(!$parent_product->is_purchasable()) { - add_filter('woocommerce_product_get_attributes', 'MeprRulesCtrl::hide_product_attributes'); - } + $post = get_post($prd_id); + return !MeprRule::is_locked($post); } - $post = get_post($prd_id); - - return !MeprRule::is_locked($post); - } - - public static function hide_product_attributes($attributes) { - unset($attributes); - $attributes = array(); // Prevent warnings from WC. - return $attributes; - } - - //Never hide WooCommerce the_content - public static function dont_hide_wc_product_content($protect, $post, $uri) { - if(isset($post) && isset($post->post_type) && $post->post_type == 'product') { return false; } + public static function hide_product_attributes($attributes) + { + unset($attributes); + $attributes = []; // Prevent warnings from WC. + return $attributes; + } - return $protect; - } + // Never hide WooCommerce the_content + public static function dont_hide_wc_product_content($protect, $post, $uri) + { + if (isset($post) && isset($post->post_type) && $post->post_type == 'product') { + return false; + } - // Validates rule content and force the post status to draft if it's empty. - public static function validate_rule_content($rule, $post_id) { - // If the rule requires exclusion - Bailout. - if( 0 === (int) $post_id || $rule->mepr_type == 'all' || (strstr($rule->mepr_type, 'all_') !== false && !preg_match('#^all_tax_#',$rule->mepr_type)) || $rule->mepr_type == 'partial') { - return; + return $protect; } - $mepr_rules_content = isset($_POST[MeprRule::$mepr_content_str]) ? trim($_POST[MeprRule::$mepr_content_str]) : ''; - // If no content is added, force the status to draft. - if(empty($mepr_rules_content)) { - // Unhook so it doesn't loop infinitely. - remove_action('save_post', 'MeprRulesCtrl::save_postdata'); - - // Update the post status to draft. - $rule_post = array( - 'ID' => $post_id, - 'post_status' => 'draft' - ); - wp_update_post( $rule_post ); + // Validates rule content and force the post status to draft if it's empty. + public static function validate_rule_content($rule, $post_id) + { + // If the rule requires exclusion - Bailout. + if (0 === (int) $post_id || $rule->mepr_type == 'all' || (strstr($rule->mepr_type, 'all_') !== false && !preg_match('#^all_tax_#', $rule->mepr_type)) || $rule->mepr_type == 'partial') { + return; + } - // Hook it again. - add_action('save_post', 'MeprRulesCtrl::save_postdata'); - MeprUtils::debug_log("Rule (#{$post_id}) content can't be empty. Post status forced to 'draft'"); + $mepr_rules_content = isset($_POST[MeprRule::$mepr_content_str]) ? trim($_POST[MeprRule::$mepr_content_str]) : ''; + // If no content is added, force the status to draft. + if (empty($mepr_rules_content)) { + // Unhook so it doesn't loop infinitely. + remove_action('save_post', 'MeprRulesCtrl::save_postdata'); + + // Update the post status to draft. + $rule_post = [ + 'ID' => $post_id, + 'post_status' => 'draft', + ]; + wp_update_post($rule_post); + + // Hook it again. + add_action('save_post', 'MeprRulesCtrl::save_postdata'); + MeprUtils::debug_log("Rule (#{$post_id}) content can't be empty. Post status forced to 'draft'"); + } } - } - /** - * Enqueue Scripts for modern paywall. - */ - public static function enqueue_scripts_paywall() { - $current_post = MeprUtils::get_current_post(); - if ( false === $current_post ) { - return; - } - $unauth = MeprRule::get_unauth_settings_for($current_post); - if ( MeprRule::is_locked($current_post) && isset($unauth->modern_paywall) && true === $unauth->modern_paywall && ! MeprAppHelper::is_memberpress_page( $current_post ) ) { - wp_enqueue_script( 'modern-paywall', MEPR_JS_URL . '/modern_paywall.js', array('jquery'), MEPR_VERSION, true ); + /** + * Enqueue Scripts for modern paywall. + */ + public static function enqueue_scripts_paywall() + { + $current_post = MeprUtils::get_current_post(); + if (false === $current_post) { + return; + } + $unauth = MeprRule::get_unauth_settings_for($current_post); + if (MeprRule::is_locked($current_post) && isset($unauth->modern_paywall) && true === $unauth->modern_paywall && ! MeprAppHelper::is_memberpress_page($current_post)) { + wp_enqueue_script('modern-paywall', MEPR_JS_URL . '/modern_paywall.js', ['jquery'], MEPR_VERSION, true); + } } - } } //End class diff --git a/app/controllers/MeprStripeConnectCtrl.php b/app/controllers/MeprStripeConnectCtrl.php index f33f8e5..d08d5eb 100644 --- a/app/controllers/MeprStripeConnectCtrl.php +++ b/app/controllers/MeprStripeConnectCtrl.php @@ -1,592 +1,618 @@ maybe_update_domain(); + + /** + * Run the process for updating a webhook when a site's home or site URL changes + * + * @param string $old_url Old setting (URL) + * @param string $new_url New setting + * @param string $option Option name + * + * @return string + */ + public function url_changed($old_url, $new_url, $option) + { + if ($new_url !== $old_url) { + $this->maybe_update_domain(); + } } - } - /** - * This checks if the current site's domain has changed from what we have stored on the Authentication service. - * If the domain has changed, we need to update the site on the Auth service, and the connection on the Stripe Connect service. - * - * @return void - */ - public function maybe_update_domain() { + /** + * This checks if the current site's domain has changed from what we have stored on the Authentication service. + * If the domain has changed, we need to update the site on the Auth service, and the connection on the Stripe Connect service. + * + * @return void + */ + public function maybe_update_domain() + { - $old_site_url = get_option( 'mepr_old_site_url', get_site_url() ); + $old_site_url = get_option('mepr_old_site_url', get_site_url()); - // Exit if the home URL hasn't changed - if($old_site_url==get_site_url()) { - return; - } + // Exit if the home URL hasn't changed + if ($old_site_url == get_site_url()) { + return; + } - $mepr_options = MeprOptions::fetch(); - $site_uuid = get_option( 'mepr_authenticator_site_uuid' ); - - $payload = array( - 'site_uuid' => $site_uuid - ); - - $jwt = MeprAuthenticatorCtrl::generate_jwt( $payload ); - $domain = parse_url( get_site_url(), PHP_URL_HOST ); - - // Request to change the domain with the auth service (site.domain) - $response = wp_remote_post( MEPR_AUTH_SERVICE_URL . "/api/domains/update", array( - 'sslverify' => false, - 'headers' => MeprUtils::jwt_header($jwt, MEPR_AUTH_SERVICE_DOMAIN), - 'body' => array( - 'domain' => $domain - ) - ) ); - - $body = json_decode( wp_remote_retrieve_body( $response ), true ); - - // Request to change the notification/webhook URL on the Stripe Connect service (account.webhook_url) - $webhooks = array(); - foreach( $mepr_options->integrations as $id => $integration ) { - if ( 'connected' === $integration['connect_status'] ) { - $pm = $mepr_options->payment_method( $id ); - $webhooks[$id] = array( - 'webhook_url' => $pm->notify_url( 'whk' ), - 'service_webhook_url' => $pm->notify_url( 'stripe-service-whk' ) - ); - } - } + $mepr_options = MeprOptions::fetch(); + $site_uuid = get_option('mepr_authenticator_site_uuid'); + + $payload = [ + 'site_uuid' => $site_uuid, + ]; + + $jwt = MeprAuthenticatorCtrl::generate_jwt($payload); + $domain = parse_url(get_site_url(), PHP_URL_HOST); + + // Request to change the domain with the auth service (site.domain) + $response = wp_remote_post(MEPR_AUTH_SERVICE_URL . '/api/domains/update', [ + 'sslverify' => false, + 'headers' => MeprUtils::jwt_header($jwt, MEPR_AUTH_SERVICE_DOMAIN), + 'body' => [ + 'domain' => $domain, + ], + ]); + + $body = json_decode(wp_remote_retrieve_body($response), true); + + // Request to change the notification/webhook URL on the Stripe Connect service (account.webhook_url) + $webhooks = []; + foreach ($mepr_options->integrations as $id => $integration) { + if ('connected' === $integration['connect_status']) { + $pm = $mepr_options->payment_method($id); + $webhooks[$id] = [ + 'webhook_url' => $pm->notify_url('whk'), + 'service_webhook_url' => $pm->notify_url('stripe-service-whk'), + ]; + } + } - $response = wp_remote_post( MEPR_STRIPE_SERVICE_URL . "/api/webhooks/update", array( - 'sslverify' => false, - 'headers' => MeprUtils::jwt_header($jwt, MEPR_STRIPE_SERVICE_DOMAIN), - 'body' => compact( 'webhooks' ) - ) ); + $response = wp_remote_post(MEPR_STRIPE_SERVICE_URL . '/api/webhooks/update', [ + 'sslverify' => false, + 'headers' => MeprUtils::jwt_header($jwt, MEPR_STRIPE_SERVICE_DOMAIN), + 'body' => compact('webhooks'), + ]); - $body = wp_remote_retrieve_body( $response ); + $body = wp_remote_retrieve_body($response); - MeprUtils::debug_log("maybe_update_webhooks recived this from Stripe Service: " . print_r($body, true)); + MeprUtils::debug_log('maybe_update_webhooks recived this from Stripe Service: ' . print_r($body, true)); - // Store for next time - update_option( 'mepr_old_site_url', get_site_url() ); - } + // Store for next time + update_option('mepr_old_site_url', get_site_url()); + } - /** - * Display an admin notice for upgrading Stripe payment methods to Stripe Connect - * - * @return void - */ - public function upgrade_notice() { - if ( MeprStripeGateway::has_method_with_connect_status( 'not-connected' ) && ( ! isset( $_COOKIE['mepr_stripe_connect_upgrade_dismissed'] ) || false == $_COOKIE['mepr_stripe_connect_upgrade_dismissed'] ) ) { - ?> + /** + * Display an admin notice for upgrading Stripe payment methods to Stripe Connect + * + * @return void + */ + public function upgrade_notice() + { + if (MeprStripeGateway::has_method_with_connect_status('not-connected') && ( ! isset($_COOKIE['mepr_stripe_connect_upgrade_dismissed']) || false == $_COOKIE['mepr_stripe_connect_upgrade_dismissed'] )) { + ?>

    -

    -

    -

    +

    +

    +

    - +
    - payment_methods(); - $using_stripe = false; - - if ( is_array( $payment_methods ) ) { - foreach ( $payment_methods as $pm ) { - if ( isset( $pm->key ) && $pm->key == 'stripe') { - $using_stripe = true; - } - } + + /** + * Display a notice about the Stripe gateway when MemberPress.com account has been disconnected. + * + * @return void + */ + public function mp_disconnect_notice() + { + $mepr_options = MeprOptions::fetch(); + $account_email = get_option('mepr_authenticator_account_email'); + $secret = get_option('mepr_authenticator_secret_token'); + $site_uuid = get_option('mepr_authenticator_site_uuid'); + $payment_methods = $mepr_options->payment_methods(); + $using_stripe = false; + + if (is_array($payment_methods)) { + foreach ($payment_methods as $pm) { + if (isset($pm->key) && $pm->key == 'stripe') { + $using_stripe = true; + } + } + } + + if (! $account_email && ! $secret && ! $site_uuid && $using_stripe) { + ?>
    -

    -

    +

    +

    - + if (isset($_GET['mepr-action']) && 'error' === $_GET['mepr-action'] && isset($_GET['error']) && ! empty($_GET['error'])) : ?>
    -

    +

    - + ?>

    - __( 'MemberPress - Stripe Connect Security', 'memberpress' ), - 'test' => array( $this, 'run_site_health_test' ) - ); - - return $tests; - } - - /** - * Run a site health check and return the result - * - * @return array - */ - public function run_site_health_test() { - - $result = array( - 'label' => __( 'MemberPress is securely connected to Stripe', 'memberpress' ), - 'status' => 'good', - 'badge' => array( - 'label' => __( 'Security', 'memberpress' ), - 'color' => 'blue', - ), - 'description' => sprintf( - '

    %s

    ', - __( 'Your MemberPress Stripe connection is complete and secure.', 'memberpress' ) - ), - 'actions' => '', - 'test' => 'run_site_health_test', - ); - - if ( class_exists( 'MeprStripeGateway' ) && MeprStripeGateway::has_method_with_connect_status( 'not-connected' ) ) { - $result = array( - 'label' => __( 'MemberPress is not securely connected to Stripe', 'memberpress' ), - 'status' => 'critical', - 'badge' => array( - 'label' => __( 'Security', 'memberpress' ), - 'color' => 'red', - ), - 'description' => sprintf( - '

    %s

    ', - __( 'Your current Stripe payment connection is out of date and may become insecure or stop working. Please click the button below to re-connect your Stripe payment method now.', 'memberpress' ) - ), - 'actions' => '' . __( 'Re-connect Stripe Payments to Fix this Error Now', 'memberpress' ) . '', - 'test' => 'run_site_health_test', - ); + + /** + * Add a site health test callback + * + * @param array $tests Array of tests to be run + * + * @return array + */ + public function add_site_health_test($tests) + { + + $tests['direct']['mepr_stripe_connect_test'] = [ + 'label' => __('MemberPress - Stripe Connect Security', 'memberpress'), + 'test' => [$this, 'run_site_health_test'], + ]; + + return $tests; + } + + /** + * Run a site health check and return the result + * + * @return array + */ + public function run_site_health_test() + { + + $result = [ + 'label' => __('MemberPress is securely connected to Stripe', 'memberpress'), + 'status' => 'good', + 'badge' => [ + 'label' => __('Security', 'memberpress'), + 'color' => 'blue', + ], + 'description' => sprintf( + '

    %s

    ', + __('Your MemberPress Stripe connection is complete and secure.', 'memberpress') + ), + 'actions' => '', + 'test' => 'run_site_health_test', + ]; + + if (class_exists('MeprStripeGateway') && MeprStripeGateway::has_method_with_connect_status('not-connected')) { + $result = [ + 'label' => __('MemberPress is not securely connected to Stripe', 'memberpress'), + 'status' => 'critical', + 'badge' => [ + 'label' => __('Security', 'memberpress'), + 'color' => 'red', + ], + 'description' => sprintf( + '

    %s

    ', + __('Your current Stripe payment connection is out of date and may become insecure or stop working. Please click the button below to re-connect your Stripe payment method now.', 'memberpress') + ), + 'actions' => '' . __('Re-connect Stripe Payments to Fix this Error Now', 'memberpress') . '', + 'test' => 'run_site_health_test', + ]; + } + + return $result; } - return $result; - } - - /** - * Adds a notice to the top of the Weekly Summary email about Stripe Connect - * - * @return void - */ - public function maybe_add_notice_to_weekly_summary_email() { - if ( class_exists( 'MeprStripeGateway' ) && MeprStripeGateway::has_method_with_connect_status( 'not-connected' ) ) { - ?> + /** + * Adds a notice to the top of the Weekly Summary email about Stripe Connect + * + * @return void + */ + public function maybe_add_notice_to_weekly_summary_email() + { + if (class_exists('MeprStripeGateway') && MeprStripeGateway::has_method_with_connect_status('not-connected')) { + ?>
    -

    +

    - +

    -

    +

    - payment_method( $method_id ); + // Make sure the user is authorized + if (! MeprUtils::is_mepr_admin()) { + wp_die(__('Sorry, you don\'t have permission to do this.', 'memberpress')); + } - if(!($pm instanceof MeprStripeGateway)) { - wp_die(__('Sorry, this only works with Stripe.', 'memberpress')); - } + $mepr_options = MeprOptions::fetch(); - $pm->update_connect_credentials(); + $method_id = sanitize_text_field($_GET['pmt']); + $pm = $mepr_options->payment_method($method_id); - MeprUtils::debug_log("*** MeprStripeConnectCtrl->process_update_creds() stored payment methods [{$method_id}]: " . print_r($mepr_options->integrations[$method_id]['api_keys']['test']['secret'],true)); + if (!($pm instanceof MeprStripeGateway)) { + wp_die(__('Sorry, this only works with Stripe.', 'memberpress')); + } - $stripe_action = ( ! empty( $_GET['stripe-action'] ) ? sanitize_text_field( $_GET['stripe-action'] ) : 'updated' ); + $pm->update_connect_credentials(); - $onboarding = isset($_GET['onboarding']) ? sanitize_text_field(wp_unslash($_GET['onboarding'])) : ''; + MeprUtils::debug_log("*** MeprStripeConnectCtrl->process_update_creds() stored payment methods [{$method_id}]: " . print_r($mepr_options->integrations[$method_id]['api_keys']['test']['secret'], true)); - if($onboarding == 'true') { - $redirect_url = add_query_arg( array( - 'page' => 'memberpress-onboarding', - 'step' => '6', - 'stripe-action' => $stripe_action - ), admin_url('admin.php') ); - } - else { - $redirect_url = add_query_arg( array( - 'page' => 'memberpress-options', - 'stripe-action' => $stripe_action - ), admin_url('admin.php') ) . '#mepr-integration'; - } + $stripe_action = ( ! empty($_GET['stripe-action']) ? sanitize_text_field($_GET['stripe-action']) : 'updated' ); - wp_redirect($redirect_url); - exit; - } + $onboarding = isset($_GET['onboarding']) ? sanitize_text_field(wp_unslash($_GET['onboarding'])) : ''; - /** - * Process a request to refresh tokens - * - * @return void - */ - public function process_refresh_tokens() { + if ($onboarding == 'true') { + $redirect_url = add_query_arg([ + 'page' => 'memberpress-onboarding', + 'step' => '6', + 'stripe-action' => $stripe_action, + ], admin_url('admin.php')); + } else { + $redirect_url = add_query_arg([ + 'page' => 'memberpress-options', + 'stripe-action' => $stripe_action, + ], admin_url('admin.php')) . '#mepr-integration'; + } - // Security check - if ( ! isset( $_GET['_wpnonce'] ) || ! wp_verify_nonce( $_GET['_wpnonce'], 'stripe-refresh' ) ) { - wp_die(__('Sorry, the refresh failed.', 'memberpress')); + wp_redirect($redirect_url); + exit; } - // Make sure we have a method ID - if ( ! isset( $_GET['method-id'] ) ) { - wp_die(__('Sorry, the refresh failed.', 'memberpress')); - } + /** + * Process a request to refresh tokens + * + * @return void + */ + public function process_refresh_tokens() + { - // Make sure the user is authorized - if ( ! MeprUtils::is_mepr_admin() ) { - wp_die(__('Sorry, you don\'t have permission to do this.', 'memberpress')); - } + // Security check + if (! isset($_GET['_wpnonce']) || ! wp_verify_nonce($_GET['_wpnonce'], 'stripe-refresh')) { + wp_die(__('Sorry, the refresh failed.', 'memberpress')); + } - $method_id = sanitize_text_field( $_GET['method-id'] ); - $site_uuid = get_option( 'mepr_authenticator_site_uuid' ); + // Make sure we have a method ID + if (! isset($_GET['method-id'])) { + wp_die(__('Sorry, the refresh failed.', 'memberpress')); + } - $payload = array( - 'site_uuid' => $site_uuid - ); + // Make sure the user is authorized + if (! MeprUtils::is_mepr_admin()) { + wp_die(__('Sorry, you don\'t have permission to do this.', 'memberpress')); + } - $jwt = MeprAuthenticatorCtrl::generate_jwt( $payload ); + $method_id = sanitize_text_field($_GET['method-id']); + $site_uuid = get_option('mepr_authenticator_site_uuid'); - // Send request to Connect service - $response = wp_remote_post( MEPR_STRIPE_SERVICE_URL . "/api/refresh/{$method_id}", array( - 'headers' => MeprUtils::jwt_header($jwt, MEPR_STRIPE_SERVICE_DOMAIN), - ) ); + $payload = [ + 'site_uuid' => $site_uuid, + ]; - $body = json_decode( wp_remote_retrieve_body( $response ), true ); + $jwt = MeprAuthenticatorCtrl::generate_jwt($payload); - if ( ! isset( $body['connect_status'] ) || 'refreshed' !== $body['connect_status'] ) { - wp_die(__('Sorry, the refresh failed.', 'memberpress')); - } + // Send request to Connect service + $response = wp_remote_post(MEPR_STRIPE_SERVICE_URL . "/api/refresh/{$method_id}", [ + 'headers' => MeprUtils::jwt_header($jwt, MEPR_STRIPE_SERVICE_DOMAIN), + ]); - $mepr_options = MeprOptions::fetch(); - - $integration_updated_count = 0; - - foreach($mepr_options->integrations as $method_id => $integ) { - // Update ALL of the payment methods connected to this account - if( isset($mepr_options->integrations[$method_id]['service_account_id']) && - $mepr_options->integrations[$method_id]['service_account_id'] == sanitize_text_field($body['service_account_id']) ) - { - $mepr_options->integrations[$method_id]['service_account_name'] = sanitize_text_field($body['service_account_name']); - $mepr_options->integrations[$method_id]['api_keys']['test']['public'] = sanitize_text_field( $body['test_publishable_key'] ); - $mepr_options->integrations[$method_id]['api_keys']['test']['secret'] = sanitize_text_field( $body['test_secret_key'] ); - $mepr_options->integrations[$method_id]['api_keys']['live']['public'] = sanitize_text_field( $body['live_publishable_key'] ); - $mepr_options->integrations[$method_id]['api_keys']['live']['secret'] = sanitize_text_field( $body['live_secret_key'] ); - $integration_updated_count++; - } - } + $body = json_decode(wp_remote_retrieve_body($response), true); - if($integration_updated_count > 0) { - $mepr_options->store(false); - } + if (! isset($body['connect_status']) || 'refreshed' !== $body['connect_status']) { + wp_die(__('Sorry, the refresh failed.', 'memberpress')); + } - $redirect_url = add_query_arg( array( - 'page' => 'memberpress-options', - 'stripe-action' => 'refreshed' - ), admin_url('admin.php') ) . '#mepr-integration'; - - wp_redirect($redirect_url); - exit; - } - - /** - * Process a request to disconnect - * - * @return void - */ - public function process_disconnect() { - - // Security check - if ( ! isset( $_GET['_wpnonce'] ) || ! wp_verify_nonce( $_GET['_wpnonce'], 'stripe-disconnect' ) ) { - wp_die(__('Sorry, the disconnect failed.', 'memberpress')); - } + $mepr_options = MeprOptions::fetch(); + + $integration_updated_count = 0; + + foreach ($mepr_options->integrations as $method_id => $integ) { + // Update ALL of the payment methods connected to this account + if ( + isset($mepr_options->integrations[$method_id]['service_account_id']) && + $mepr_options->integrations[$method_id]['service_account_id'] == sanitize_text_field($body['service_account_id']) + ) { + $mepr_options->integrations[$method_id]['service_account_name'] = sanitize_text_field($body['service_account_name']); + $mepr_options->integrations[$method_id]['api_keys']['test']['public'] = sanitize_text_field($body['test_publishable_key']); + $mepr_options->integrations[$method_id]['api_keys']['test']['secret'] = sanitize_text_field($body['test_secret_key']); + $mepr_options->integrations[$method_id]['api_keys']['live']['public'] = sanitize_text_field($body['live_publishable_key']); + $mepr_options->integrations[$method_id]['api_keys']['live']['secret'] = sanitize_text_field($body['live_secret_key']); + $integration_updated_count++; + } + } - // Make sure we have a method ID - if ( ! isset( $_GET['method-id'] ) ) { - wp_die(__('Sorry, the disconnect failed.', 'memberpress')); - } + if ($integration_updated_count > 0) { + $mepr_options->store(false); + } - // Make sure the user is authorized - if ( ! MeprUtils::is_mepr_admin() ) { - wp_die(__('Sorry, you don\'t have permission to do this.', 'memberpress')); + $redirect_url = add_query_arg([ + 'page' => 'memberpress-options', + 'stripe-action' => 'refreshed', + ], admin_url('admin.php')) . '#mepr-integration'; + + wp_redirect($redirect_url); + exit; } - $method_id = sanitize_text_field( $_GET['method-id'] ); + /** + * Process a request to disconnect + * + * @return void + */ + public function process_disconnect() + { - $res = $this->disconnect( $method_id ); + // Security check + if (! isset($_GET['_wpnonce']) || ! wp_verify_nonce($_GET['_wpnonce'], 'stripe-disconnect')) { + wp_die(__('Sorry, the disconnect failed.', 'memberpress')); + } - if(!$res) { - wp_die(__('Sorry, the disconnect failed.', 'memberpress')); - } + // Make sure we have a method ID + if (! isset($_GET['method-id'])) { + wp_die(__('Sorry, the disconnect failed.', 'memberpress')); + } - $redirect_url = add_query_arg( array( - 'page' => 'memberpress-options', - 'stripe-action' => 'disconnected' - ), admin_url('admin.php') ) . '#mepr-integration'; - - wp_redirect($redirect_url); - exit; - } - - /** - * Disconnect ALL stripe connected payment methods - * - * @return void - */ - public function disconnect_all($site_uuid, $site_email) { - MeprUtils::debug_log("********** IN disconnect_all!"); - $mepr_options = MeprOptions::fetch(); - $pms = $mepr_options->payment_methods(false); - foreach( $pms as $method_id => $pm ) { - MeprUtils::debug_log("********** disconnect_all: $method_id"); - if( $pm instanceof MeprStripeGateway && MeprStripeGateway::is_stripe_connect( $method_id ) ) { - MeprUtils::debug_log("********** disconnect_all: Disconnecting: $method_id"); - $res = $this->disconnect( $method_id ); - MeprUtils::debug_log("********** disconnect_all: Disconnection " . ($res ? "SUCCESSFUL!" : "FAILED!")); - } - } - } + // Make sure the user is authorized + if (! MeprUtils::is_mepr_admin()) { + wp_die(__('Sorry, you don\'t have permission to do this.', 'memberpress')); + } + + $method_id = sanitize_text_field($_GET['method-id']); - public function disconnect($method_id, $disconnect_type='full') { + $res = $this->disconnect($method_id); - if($disconnect_type==='full') { - // Update connection data - $mepr_options = MeprOptions::fetch(); - $integ = $mepr_options->integrations[$method_id]; - $integ['connect_status'] = 'disconnected'; - unset( $integ['service_account_id'] ); - unset( $integ['service_account_name'] ); + if (!$res) { + wp_die(__('Sorry, the disconnect failed.', 'memberpress')); + } - $mepr_options->integrations[$method_id] = $integ; - $mepr_options->store(false); + $redirect_url = add_query_arg([ + 'page' => 'memberpress-options', + 'stripe-action' => 'disconnected', + ], admin_url('admin.php')) . '#mepr-integration'; + + wp_redirect($redirect_url); + exit; + } + + /** + * Disconnect ALL stripe connected payment methods + * + * @return void + */ + public function disconnect_all($site_uuid, $site_email) + { + MeprUtils::debug_log('********** IN disconnect_all!'); + $mepr_options = MeprOptions::fetch(); + $pms = $mepr_options->payment_methods(false); + foreach ($pms as $method_id => $pm) { + MeprUtils::debug_log("********** disconnect_all: $method_id"); + if ($pm instanceof MeprStripeGateway && MeprStripeGateway::is_stripe_connect($method_id)) { + MeprUtils::debug_log("********** disconnect_all: Disconnecting: $method_id"); + $res = $this->disconnect($method_id); + MeprUtils::debug_log('********** disconnect_all: Disconnection ' . ($res ? 'SUCCESSFUL!' : 'FAILED!')); + } + } } - $site_uuid = get_option( 'mepr_authenticator_site_uuid' ); + public function disconnect($method_id, $disconnect_type = 'full') + { - // Attempt to disconnect at the service - $payload = array( - 'method_id' => $method_id, - 'site_uuid' => $site_uuid - ); + if ($disconnect_type === 'full') { + // Update connection data + $mepr_options = MeprOptions::fetch(); + $integ = $mepr_options->integrations[$method_id]; + $integ['connect_status'] = 'disconnected'; + unset($integ['service_account_id']); + unset($integ['service_account_name']); - $jwt = MeprAuthenticatorCtrl::generate_jwt( $payload ); + $mepr_options->integrations[$method_id] = $integ; + $mepr_options->store(false); + } - // Send request to Connect service - $response = wp_remote_request( MEPR_STRIPE_SERVICE_URL . "/api/disconnect/{$method_id}", array( - 'method' => 'DELETE', - 'headers' => MeprUtils::jwt_header($jwt, MEPR_STRIPE_SERVICE_DOMAIN), - ) ); + $site_uuid = get_option('mepr_authenticator_site_uuid'); - $body = json_decode( wp_remote_retrieve_body( $response ), true ); + // Attempt to disconnect at the service + $payload = [ + 'method_id' => $method_id, + 'site_uuid' => $site_uuid, + ]; - if ( ! isset( $body['connect_status'] ) || 'disconnected' !== $body['connect_status'] ) { - return false; - } + $jwt = MeprAuthenticatorCtrl::generate_jwt($payload); - return true; - } + // Send request to Connect service + $response = wp_remote_request(MEPR_STRIPE_SERVICE_URL . "/api/disconnect/{$method_id}", [ + 'method' => 'DELETE', + 'headers' => MeprUtils::jwt_header($jwt, MEPR_STRIPE_SERVICE_DOMAIN), + ]); - /** Create a new payment method before redirection to Stripe Connect */ - public function create_new_payment_method() { - check_ajax_referer( 'new-stripe-connect', 'security' ); + $body = json_decode(wp_remote_retrieve_body($response), true); - $form_data = urldecode($_POST['form_data']); + if (! isset($body['connect_status']) || 'disconnected' !== $body['connect_status']) { + return false; + } + + return true; + } - $pm = array(); - parse_str($form_data, $pm); + /** + * Create a new payment method before redirection to Stripe Connect + */ + public function create_new_payment_method() + { + check_ajax_referer('new-stripe-connect', 'security'); - $mepr_options = MeprOptions::fetch(); - $mepr_options->integrations = array_merge($mepr_options->integrations, $pm['mepr-integrations']); - $mepr_options->store(false); + $form_data = urldecode($_POST['form_data']); - echo json_encode(array('status' => 'success', 'message' => __('You successfully stored a new payment method yo.', 'memberpress'))); - exit; - } + $pm = []; + parse_str($form_data, $pm); - /** - * When connected payment method is deleted, it should be disconnected. - * - * @return void - */ - public function disconnect_deleted_methods( $params ) { - $mepr_options = MeprOptions::fetch(); + $mepr_options = MeprOptions::fetch(); + $mepr_options->integrations = array_merge($mepr_options->integrations, $pm['mepr-integrations']); + $mepr_options->store(false); - // Bail early if no payment methods have been deleted - if ( empty( $params['mepr_deleted_payment_methods'] ) ) { - return; + echo json_encode([ + 'status' => 'success', + 'message' => __('You successfully stored a new payment method yo.', 'memberpress'), + ]); + exit; } - foreach ( $params['mepr_deleted_payment_methods'] as $method_id ) { - if(empty($mepr_options->integrations[$method_id])) { continue; } + /** + * When connected payment method is deleted, it should be disconnected. + * + * @return void + */ + public function disconnect_deleted_methods($params) + { + $mepr_options = MeprOptions::fetch(); - $integ = $mepr_options->integrations[$method_id]; + // Bail early if no payment methods have been deleted + if (empty($params['mepr_deleted_payment_methods'])) { + return; + } + + foreach ($params['mepr_deleted_payment_methods'] as $method_id) { + if (empty($mepr_options->integrations[$method_id])) { + continue; + } - if ( $integ['gateway'] === 'MeprStripeGateway' && MeprStripeGateway::is_stripe_connect( $method_id ) ) { - $this->disconnect( $method_id, 'remote-only' ); - } + $integ = $mepr_options->integrations[$method_id]; + + if ($integ['gateway'] === 'MeprStripeGateway' && MeprStripeGateway::is_stripe_connect($method_id)) { + $this->disconnect($method_id, 'remote-only'); + } + } } - } } diff --git a/app/controllers/MeprStripeCtrl.php b/app/controllers/MeprStripeCtrl.php index 715ca96..d7b9b3a 100644 --- a/app/controllers/MeprStripeCtrl.php +++ b/app/controllers/MeprStripeCtrl.php @@ -1,712 +1,706 @@ integrations as $integration ) { - if ( $integration['gateway'] == 'MeprStripeGateway' ) { - $payment_method = new MeprStripeGateway(); - $payment_method->load( $integration ); - $stripe_customer_id = $mepr_current_user->get_stripe_customer_id($payment_method->get_meta_gateway_id()); - - if ( empty( $stripe_customer_id ) ) { - // Continue on other instances of MeprStripeGateway - continue; - } - - $args = [ 'email' => $mepr_current_user->user_email ]; - - try { - $payment_method->send_stripe_request( 'customers/' . $stripe_customer_id, $args, 'post' ); - } catch (\Exception $exception) {} - } + public function load_hooks() + { + add_action('wp_ajax_mepr_stripe_confirm_payment', [$this, 'confirm_payment']); + add_action('wp_ajax_nopriv_mepr_stripe_confirm_payment', [$this, 'confirm_payment']); + add_action('wp_ajax_mepr_stripe_create_checkout_session', [$this, 'create_checkout_session']); + add_action('wp_ajax_nopriv_mepr_stripe_create_checkout_session', [$this, 'create_checkout_session']); + add_action('wp_ajax_mepr_stripe_create_account_setup_intent', [$this, 'create_account_setup_intent']); + add_action('wp_ajax_nopriv_mepr_stripe_create_account_setup_intent', [$this, 'create_account_setup_intent']); + add_action('wp_ajax_mepr_stripe_update_payment_method', [$this, 'update_payment_method']); + add_action('wp_ajax_nopriv_mepr_stripe_update_payment_method', [$this, 'update_payment_method']); + add_action('wp_ajax_mepr_stripe_debug_checkout_error', [$this, 'debug_checkout_error']); + add_action('wp_ajax_nopriv_mepr_stripe_debug_checkout_error', [$this, 'debug_checkout_error']); + add_action('mepr-update-new-user-email', [$this, 'update_user_email']); } - } - - public function create_checkout_session() { - MeprHooks::do_action('mepr_stripe_before_create_checkout_session'); - - $this->do_confirm_payment('stripe_checkout'); - } - public function confirm_payment() { - MeprHooks::do_action('mepr_stripe_before_confirm_payment'); - - try { - $this->do_confirm_payment(); - } catch (Throwable $t) { // Errors and exceptions in PHP 7 - $content = $t->__toString(); - } catch (Exception $e) { // Exceptions in PHP 5 - $content = $e->__toString(); + /** + * Update email of stripe user object when customer change email + * on Memberpress account page + * + * @param MeprUser $mepr_current_user + */ + public function update_user_email($mepr_current_user) + { + $mepr_options = MeprOptions::fetch(); + + foreach ($mepr_options->integrations as $integration) { + if ($integration['gateway'] == 'MeprStripeGateway') { + $payment_method = new MeprStripeGateway(); + $payment_method->load($integration); + $stripe_customer_id = $mepr_current_user->get_stripe_customer_id($payment_method->get_meta_gateway_id()); + + if (empty($stripe_customer_id)) { + // Continue on other instances of MeprStripeGateway + continue; + } + + $args = ['email' => $mepr_current_user->user_email]; + + try { + $payment_method->send_stripe_request('customers/' . $stripe_customer_id, $args, 'post'); + } catch (\Exception $exception) { + } + } + } } - $this->send_checkout_error_debug_email( - $content, - isset($_POST['mepr_transaction_id']) && is_numeric($_POST['mepr_transaction_id']) ? (int) $_POST['mepr_transaction_id'] : null, - isset($_POST['user_email']) && is_string($_POST['user_email']) ? sanitize_text_field(wp_unslash($_POST['user_email'])) : null - ); - - wp_send_json(array('error' => __('An error occurred, please DO NOT submit the form again as you may be double charged. Please contact us for further assistance instead.', 'memberpress'))); - } - - public function do_confirm_payment($mode = '') { - $mepr_options = MeprOptions::fetch(); - $transaction_id = isset($_POST['mepr_transaction_id']) && is_numeric($_POST['mepr_transaction_id']) ? (int) $_POST['mepr_transaction_id'] : 0; - - if($transaction_id > 0) { - $txn = new MeprTransaction($transaction_id); - - if(!$txn->id) { - wp_send_json(['error' => __('Transaction not found', 'memberpress')]); - } + public function create_checkout_session() + { + MeprHooks::do_action('mepr_stripe_before_create_checkout_session'); - $pm = $txn->payment_method(); - - if(!($pm instanceof MeprStripeGateway)) { - wp_send_json(['error' => __('Invalid payment gateway', 'memberpress')]); - } - - $prd = $txn->product(); - - if(!$prd->ID) { - wp_send_json(['error' => __('Product not found', 'memberpress')]); - } - - $usr = $txn->user(); - - if(!$usr->ID) { - wp_send_json(['error' => __('User not found', 'memberpress')]); - } + $this->do_confirm_payment('stripe_checkout'); + } - if(!$prd->is_one_time_payment()) { - $sub = $txn->subscription(); - } + public function confirm_payment() + { + MeprHooks::do_action('mepr_stripe_before_confirm_payment'); - $cpn = $txn->coupon(); - $coupon_code = $cpn instanceof MeprCoupon ? $cpn->post_title : ''; - } - else { - // We don't have a transaction ID (i.e. this is the Single Page Checkout), so let's create the user and transaction - // This code is essentially the same as MeprCheckoutCtrl::process_signup_form - $disable_checkout_password_fields = $mepr_options->disable_checkout_password_fields; - - // Validate the form post - $mepr_current_url = isset($_POST['mepr_current_url']) && is_string($_POST['mepr_current_url']) ? sanitize_text_field(wp_unslash($_POST['mepr_current_url'])) : ''; - $errors = MeprHooks::apply_filters('mepr-validate-signup', MeprUser::validate_signup($_POST, array(), $mepr_current_url)); - - if(!empty($errors)) { - wp_send_json(['errors' => $errors]); - } - - // Check if the user is logged in already - $is_existing_user = MeprUtils::is_user_logged_in(); - - if($is_existing_user) { - $usr = MeprUtils::get_currentuserinfo(); - } - else { // If new user we've got to create them and sign them in - $usr = new MeprUser(); - $usr->user_login = ($mepr_options->username_is_email)?sanitize_email($_POST['user_email']):sanitize_user($_POST['user_login']); - $usr->user_email = sanitize_email($_POST['user_email']); - $usr->first_name = isset($_POST['user_first_name']) && !empty($_POST['user_first_name']) ? sanitize_text_field(wp_unslash($_POST['user_first_name'])) : ''; - $usr->last_name = isset($_POST['user_last_name']) && !empty($_POST['user_last_name']) ? sanitize_text_field(wp_unslash($_POST['user_last_name'])) : ''; - - $password = ($disable_checkout_password_fields === true) ? wp_generate_password() : $_POST['mepr_user_password']; - //Have to use rec here because we unset user_pass on __construct - $usr->set_password($password); try { - $usr->store(); - - // We need to refresh the user object. In the case where emails are used as - // usernames, the email & username could differ after the user is saved. - $usr = new MeprUser( $usr->ID ); - - if ( $disable_checkout_password_fields === true ) { - $usr->send_password_notification( 'new' ); - } - // Log the new user in - if ( MeprHooks::apply_filters( 'mepr-auto-login', true, $_POST['mepr_product_id'], $usr ) ) { - wp_signon( - array( - 'user_login' => $usr->user_login, - 'user_password' => $password - ), - MeprUtils::is_ssl() //May help with the users getting logged out when going between http and https - ); - } - - MeprEvent::record( 'login', $usr ); //Record the first login here + $this->do_confirm_payment(); + } catch (Throwable $t) { // Errors and exceptions in PHP 7 + $content = $t->__toString(); + } catch (Exception $e) { // Exceptions in PHP 5 + $content = $e->__toString(); } - catch (MeprCreateException $e) { - wp_send_json(['error' => __('The user was unable to be saved.', 'memberpress')]); - } - } - - $product_id = isset($_POST['mepr_product_id']) ? (int) $_POST['mepr_product_id'] : 0; - $prd = new MeprProduct($product_id); - - if(empty($prd->ID)) { - wp_send_json(['error' => __('Sorry, we were unable to find the product.', 'memberpress')]); - } - - // If we're showing the fields on logged in purchases, let's save them here - if(!$is_existing_user || ($is_existing_user && $mepr_options->show_fields_logged_in_purchases)) { - MeprUsersCtrl::save_extra_profile_fields($usr->ID, true, $prd, true); - $usr = new MeprUser($usr->ID); //Re-load the user object with the metadata now (helps with first name last name missing from hooks below) - } - - // Needed for autoresponders (SPC + Stripe + Free Trial issue) - MeprHooks::do_action('mepr-signup-user-loaded', $usr); - - $payment_method_id = isset($_POST['mepr_payment_method']) ? sanitize_text_field(wp_unslash($_POST['mepr_payment_method'])) : ''; - $pm = $mepr_options->payment_method($payment_method_id); - - if(!($pm instanceof MeprStripeGateway)) { - wp_send_json(['error' => __('Invalid payment gateway', 'memberpress')]); - } - - $coupon_code = isset($_POST['mepr_coupon_code']) ? sanitize_text_field(wp_unslash($_POST['mepr_coupon_code'])) : ''; - $cpn = MeprCoupon::get_one_from_code($coupon_code); - $coupon_code = $cpn instanceof MeprCoupon ? $cpn->post_title : ''; - - try { - list($txn, $sub) = MeprCheckoutCtrl::prepare_transaction( - $prd, - 0, - $usr->ID, - $pm->id, - $cpn + + $this->send_checkout_error_debug_email( + $content, + isset($_POST['mepr_transaction_id']) && is_numeric($_POST['mepr_transaction_id']) ? (int) $_POST['mepr_transaction_id'] : null, + isset($_POST['user_email']) && is_string($_POST['user_email']) ? sanitize_text_field(wp_unslash($_POST['user_email'])) : null ); - } - catch(Exception $e) { - wp_send_json(['error' => $e->getMessage()]); - } - } - try { - // Prevent duplicate charges if the user is already subscribed - $this->check_if_already_subscribed($usr, $prd); - - $order_bump_product_ids = isset($_POST['mepr_order_bumps']) && is_array($_POST['mepr_order_bumps']) ? array_map('intval', $_POST['mepr_order_bumps']) : []; - $order_bump_products = MeprCheckoutCtrl::get_order_bump_products($prd->ID, $order_bump_product_ids); - $order_bump_total = 0.00; - $order_bump_transactions = []; - $has_subscription_order_bump = false; - - if(count($order_bump_products)) { - $order = new MeprOrder(); - $order->user_id = $usr->ID; - $order->primary_transaction_id = $txn->id; - $order->gateway = $pm->id; - $order->store(); - - $txn->order_id = $order->id; - $txn->store(); - - if(isset($sub) && $sub instanceof MeprSubscription) { - $sub->order_id = $order->id; - $sub->store(); - } + wp_send_json(['error' => __('An error occurred, please DO NOT submit the form again as you may be double charged. Please contact us for further assistance instead.', 'memberpress')]); + } - foreach($order_bump_products as $product) { - // Prevent duplicate charges if the user is already subscribed - $this->check_if_already_subscribed($usr, $product); + public function do_confirm_payment($mode = '') + { + $mepr_options = MeprOptions::fetch(); + $transaction_id = isset($_POST['mepr_transaction_id']) && is_numeric($_POST['mepr_transaction_id']) ? (int) $_POST['mepr_transaction_id'] : 0; - list($transaction, $subscription) = MeprCheckoutCtrl::prepare_transaction( - $product, - $order->id, - $usr->ID, - $pm->id - ); + if ($transaction_id > 0) { + $txn = new MeprTransaction($transaction_id); - if($product->is_one_time_payment()) { - if((float) $transaction->total > 0) { - $order_bump_total += (float) $transaction->total; - } - } - else { - if(!($subscription instanceof MeprSubscription)) { - wp_send_json_error(__('Subscription not found', 'memberpress')); + if (!$txn->id) { + wp_send_json(['error' => __('Transaction not found', 'memberpress')]); } - $has_subscription_order_bump = true; + $pm = $txn->payment_method(); - if($subscription->trial && $subscription->trial_days > 0) { - if((float) $subscription->trial_total > 0) { - $order_bump_total += (float) $subscription->trial_total; - } - } - else { - if((float) $subscription->total > 0) { - $order_bump_total += (float) $subscription->total; - } + if (!($pm instanceof MeprStripeGateway)) { + wp_send_json(['error' => __('Invalid payment gateway', 'memberpress')]); } - } - $order_bump_transactions[] = $transaction; - } - } + $prd = $txn->product(); - if($mode == 'stripe_checkout') { - if(count($order_bump_products)) { - $checkout_session = $pm->create_multi_item_checkout_session($txn, $prd, $usr, $coupon_code, $order_bump_transactions); - } - else { - if(!isset($sub)) { - $sub = $txn->subscription(); - } + if (!$prd->ID) { + wp_send_json(['error' => __('Product not found', 'memberpress')]); + } - $checkout_session = $pm->create_checkout_session($txn, $prd, $usr, $sub); - } + $usr = $txn->user(); - MeprHooks::do_action('mepr_stripe_checkout_pending', $txn, $usr); - MeprHooks::do_action('mepr-process-signup', $txn->amount, $usr, $prd->ID, $txn->id); - MeprHooks::do_action('mepr-signup', $txn); + if (!$usr->ID) { + wp_send_json(['error' => __('User not found', 'memberpress')]); + } - wp_send_json([ - 'id' => $checkout_session->id, - 'public_key' => $pm->get_public_key(), - ]); - } + if (!$prd->is_one_time_payment()) { + $sub = $txn->subscription(); + } - $customer_id = $usr->get_stripe_customer_id($pm->get_meta_gateway_id()); + $cpn = $txn->coupon(); + $coupon_code = $cpn instanceof MeprCoupon ? $cpn->post_title : ''; + } else { + // We don't have a transaction ID (i.e. this is the Single Page Checkout), so let's create the user and transaction + // This code is essentially the same as MeprCheckoutCtrl::process_signup_form + $disable_checkout_password_fields = $mepr_options->disable_checkout_password_fields; - if(!is_string($customer_id) || strpos($customer_id, 'cus_') !== 0) { - $customer_id = $pm->get_customer_id($usr); - } - else { - $pm->update_customer($customer_id, $usr); - } + // Validate the form post + $mepr_current_url = isset($_POST['mepr_current_url']) && is_string($_POST['mepr_current_url']) ? sanitize_text_field(wp_unslash($_POST['mepr_current_url'])) : ''; + $errors = MeprHooks::apply_filters('mepr-validate-signup', MeprUser::validate_signup($_POST, [], $mepr_current_url)); - $action = 'confirmPayment'; + if (!empty($errors)) { + wp_send_json(['errors' => $errors]); + } - $thank_you_page_args = [ - 'membership' => sanitize_title($prd->post_title), - 'membership_id' => $prd->ID, - 'transaction_id' => $txn->id, - ]; + // Check if the user is logged in already + $is_existing_user = MeprUtils::is_user_logged_in(); + + if ($is_existing_user) { + $usr = MeprUtils::get_currentuserinfo(); + } else { // If new user we've got to create them and sign them in + $usr = new MeprUser(); + $usr->user_login = ($mepr_options->username_is_email) ? sanitize_email($_POST['user_email']) : sanitize_user($_POST['user_login']); + $usr->user_email = sanitize_email($_POST['user_email']); + $usr->first_name = isset($_POST['user_first_name']) && !empty($_POST['user_first_name']) ? sanitize_text_field(wp_unslash($_POST['user_first_name'])) : ''; + $usr->last_name = isset($_POST['user_last_name']) && !empty($_POST['user_last_name']) ? sanitize_text_field(wp_unslash($_POST['user_last_name'])) : ''; + + $password = ($disable_checkout_password_fields === true) ? wp_generate_password() : $_POST['mepr_user_password']; + // Have to use rec here because we unset user_pass on __construct + $usr->set_password($password); + try { + $usr->store(); + + // We need to refresh the user object. In the case where emails are used as + // usernames, the email & username could differ after the user is saved. + $usr = new MeprUser($usr->ID); + + if ($disable_checkout_password_fields === true) { + $usr->send_password_notification('new'); + } + // Log the new user in + if (MeprHooks::apply_filters('mepr-auto-login', true, $_POST['mepr_product_id'], $usr)) { + wp_signon( + [ + 'user_login' => $usr->user_login, + 'user_password' => $password, + ], + MeprUtils::is_ssl() // May help with the users getting logged out when going between http and https + ); + } + + MeprEvent::record('login', $usr); // Record the first login here + } catch (MeprCreateException $e) { + wp_send_json(['error' => __('The user was unable to be saved.', 'memberpress')]); + } + } - if($prd->is_one_time_payment() || !$prd->is_payment_required($coupon_code)) { - $total = $prd->is_payment_required($coupon_code) ? (float) $txn->total : 0.00; - $total += $order_bump_total; + $product_id = isset($_POST['mepr_product_id']) ? (int) $_POST['mepr_product_id'] : 0; + $prd = new MeprProduct($product_id); - if($total > 0.00) { - $setup_future_usage = $has_subscription_order_bump ? 'off_session' : null; - $payment_intent = $pm->create_payment_intent($total, $customer_id, $txn, $prd, $setup_future_usage); + if (empty($prd->ID)) { + wp_send_json(['error' => __('Sorry, we were unable to find the product.', 'memberpress')]); + } - $client_secret = $payment_intent->client_secret; + // If we're showing the fields on logged in purchases, let's save them here + if (!$is_existing_user || ($is_existing_user && $mepr_options->show_fields_logged_in_purchases)) { + MeprUsersCtrl::save_extra_profile_fields($usr->ID, true, $prd, true); + $usr = new MeprUser($usr->ID); // Re-load the user object with the metadata now (helps with first name last name missing from hooks below) + } - $txn->trans_num = $payment_intent->id; - $txn->store(); - } - else { - $setup_intent = $pm->create_setup_intent($customer_id, $txn, $prd); + // Needed for autoresponders (SPC + Stripe + Free Trial issue) + MeprHooks::do_action('mepr-signup-user-loaded', $usr); - $client_secret = $setup_intent->client_secret; + $payment_method_id = isset($_POST['mepr_payment_method']) ? sanitize_text_field(wp_unslash($_POST['mepr_payment_method'])) : ''; + $pm = $mepr_options->payment_method($payment_method_id); - $txn->trans_num = $setup_intent->id; - $txn->store(); + if (!($pm instanceof MeprStripeGateway)) { + wp_send_json(['error' => __('Invalid payment gateway', 'memberpress')]); + } - $action = 'confirmSetup'; - } - } - else { - if(!isset($sub) || !$sub instanceof MeprSubscription) { - wp_send_json_error(__('Subscription not found', 'memberpress')); + $coupon_code = isset($_POST['mepr_coupon_code']) ? sanitize_text_field(wp_unslash($_POST['mepr_coupon_code'])) : ''; + $cpn = MeprCoupon::get_one_from_code($coupon_code); + $coupon_code = $cpn instanceof MeprCoupon ? $cpn->post_title : ''; + + try { + list($txn, $sub) = MeprCheckoutCtrl::prepare_transaction( + $prd, + 0, + $usr->ID, + $pm->id, + $cpn + ); + } catch (Exception $e) { + wp_send_json(['error' => $e->getMessage()]); + } } - $thank_you_page_args['subscription_id'] = $sub->id; - - $calculate_taxes = (bool) get_option('mepr_calculate_taxes'); - $tax_inclusive = $mepr_options->attr('tax_calc_type') == 'inclusive'; - $invoice_items = []; + try { + // Prevent duplicate charges if the user is already subscribed + $this->check_if_already_subscribed($usr, $prd); + + $order_bump_product_ids = isset($_POST['mepr_order_bumps']) && is_array($_POST['mepr_order_bumps']) ? array_map('intval', $_POST['mepr_order_bumps']) : []; + $order_bump_products = MeprCheckoutCtrl::get_order_bump_products($prd->ID, $order_bump_product_ids); + $order_bump_total = 0.00; + $order_bump_transactions = []; + $has_subscription_order_bump = false; + + if (count($order_bump_products)) { + $order = new MeprOrder(); + $order->user_id = $usr->ID; + $order->primary_transaction_id = $txn->id; + $order->gateway = $pm->id; + $order->store(); + + $txn->order_id = $order->id; + $txn->store(); + + if (isset($sub) && $sub instanceof MeprSubscription) { + $sub->order_id = $order->id; + $sub->store(); + } + + foreach ($order_bump_products as $product) { + // Prevent duplicate charges if the user is already subscribed + $this->check_if_already_subscribed($usr, $product); + + list($transaction, $subscription) = MeprCheckoutCtrl::prepare_transaction( + $product, + $order->id, + $usr->ID, + $pm->id + ); + + if ($product->is_one_time_payment()) { + if ((float) $transaction->total > 0) { + $order_bump_total += (float) $transaction->total; + } + } else { + if (!($subscription instanceof MeprSubscription)) { + wp_send_json_error(__('Subscription not found', 'memberpress')); + } + + $has_subscription_order_bump = true; + + if ($subscription->trial && $subscription->trial_days > 0) { + if ((float) $subscription->trial_total > 0) { + $order_bump_total += (float) $subscription->trial_total; + } + } else { + if ((float) $subscription->total > 0) { + $order_bump_total += (float) $subscription->total; + } + } + } + + $order_bump_transactions[] = $transaction; + } + } - foreach($order_bump_transactions as $transaction) { - $product = $transaction->product(); + if ($mode == 'stripe_checkout') { + if (count($order_bump_products)) { + $checkout_session = $pm->create_multi_item_checkout_session($txn, $prd, $usr, $coupon_code, $order_bump_transactions); + } else { + if (!isset($sub)) { + $sub = $txn->subscription(); + } + + $checkout_session = $pm->create_checkout_session($txn, $prd, $usr, $sub); + } + + MeprHooks::do_action('mepr_stripe_checkout_pending', $txn, $usr); + MeprHooks::do_action('mepr-process-signup', $txn->amount, $usr, $prd->ID, $txn->id); + MeprHooks::do_action('mepr-signup', $txn); + + wp_send_json([ + 'id' => $checkout_session->id, + 'public_key' => $pm->get_public_key(), + ]); + } - if(empty($product->ID)) { - wp_send_json(['error' => __('Product not found', 'memberpress')]); - } + $customer_id = $usr->get_stripe_customer_id($pm->get_meta_gateway_id()); - if(!$transaction->is_payment_required()) { - continue; - } - elseif($transaction->is_one_time_payment()) { - if((float) $transaction->total > 0) { - $amount = $calculate_taxes && !$tax_inclusive && $transaction->tax_rate > 0 ? (float) $transaction->amount : (float) $transaction->total; - $invoice_items[] = $pm->build_invoice_item($amount, $transaction, $product, $customer_id); + if (!is_string($customer_id) || strpos($customer_id, 'cus_') !== 0) { + $customer_id = $pm->get_customer_id($usr); + } else { + $pm->update_customer($customer_id, $usr); } - } - else { - $subscription = $transaction->subscription(); - if(!($subscription instanceof MeprSubscription)) { - wp_send_json_error(__('Subscription not found', 'memberpress')); - } + $action = 'confirmPayment'; + + $thank_you_page_args = [ + 'membership' => sanitize_title($prd->post_title), + 'membership_id' => $prd->ID, + 'transaction_id' => $txn->id, + ]; + + if ($prd->is_one_time_payment() || !$prd->is_payment_required($coupon_code)) { + $total = $prd->is_payment_required($coupon_code) ? (float) $txn->total : 0.00; + $total += $order_bump_total; + + if ($total > 0.00) { + $setup_future_usage = $has_subscription_order_bump ? 'off_session' : null; + $payment_intent = $pm->create_payment_intent($total, $customer_id, $txn, $prd, $setup_future_usage); + + $client_secret = $payment_intent->client_secret; + + $txn->trans_num = $payment_intent->id; + $txn->store(); + } else { + $setup_intent = $pm->create_setup_intent($customer_id, $txn, $prd); + + $client_secret = $setup_intent->client_secret; - if($subscription->trial && $subscription->trial_days > 0) { - if((float) $subscription->trial_total > 0) { - $amount = $calculate_taxes && !$tax_inclusive && $transaction->tax_rate > 0 ? (float) $subscription->trial_amount : (float) $subscription->trial_total; - $invoice_items[] = $pm->build_invoice_item($amount, $transaction, $product, $customer_id); - } - } - else { - $amount = $calculate_taxes && !$tax_inclusive && $transaction->tax_rate > 0 ? (float) $subscription->price : (float) $subscription->total; - $invoice_items[] = $pm->build_invoice_item($amount, $transaction, $product, $customer_id); + $txn->trans_num = $setup_intent->id; + $txn->store(); + + $action = 'confirmSetup'; + } + } else { + if (!isset($sub) || !$sub instanceof MeprSubscription) { + wp_send_json_error(__('Subscription not found', 'memberpress')); + } + + $thank_you_page_args['subscription_id'] = $sub->id; + + $calculate_taxes = (bool) get_option('mepr_calculate_taxes'); + $tax_inclusive = $mepr_options->attr('tax_calc_type') == 'inclusive'; + $invoice_items = []; + + foreach ($order_bump_transactions as $transaction) { + $product = $transaction->product(); + + if (empty($product->ID)) { + wp_send_json(['error' => __('Product not found', 'memberpress')]); + } + + if (!$transaction->is_payment_required()) { + continue; + } elseif ($transaction->is_one_time_payment()) { + if ((float) $transaction->total > 0) { + $amount = $calculate_taxes && !$tax_inclusive && $transaction->tax_rate > 0 ? (float) $transaction->amount : (float) $transaction->total; + $invoice_items[] = $pm->build_invoice_item($amount, $transaction, $product, $customer_id); + } + } else { + $subscription = $transaction->subscription(); + + if (!($subscription instanceof MeprSubscription)) { + wp_send_json_error(__('Subscription not found', 'memberpress')); + } + + if ($subscription->trial && $subscription->trial_days > 0) { + if ((float) $subscription->trial_total > 0) { + $amount = $calculate_taxes && !$tax_inclusive && $transaction->tax_rate > 0 ? (float) $subscription->trial_amount : (float) $subscription->trial_total; + $invoice_items[] = $pm->build_invoice_item($amount, $transaction, $product, $customer_id); + } + } else { + $amount = $calculate_taxes && !$tax_inclusive && $transaction->tax_rate > 0 ? (float) $subscription->price : (float) $subscription->total; + $invoice_items[] = $pm->build_invoice_item($amount, $transaction, $product, $customer_id); + } + } + } + + // Use a SetupIntent for free trials, and create the subscription via webhook later + if ($sub->trial && $sub->trial_days > 0 && (float) $sub->trial_total <= 0.00 && empty($invoice_items)) { + $setup_intent = $pm->create_setup_intent($customer_id, $txn, $prd); + + $client_secret = $setup_intent->client_secret; + + $txn->trans_num = $setup_intent->id; + $txn->store(); + + $action = 'confirmSetup'; + } else { + $trial_days = 0; + + if ($sub->trial && $sub->trial_days > 0) { + $trial_days = $sub->trial_days; + + if ((float) $sub->trial_total > 0.00) { + $amount = $calculate_taxes && !$tax_inclusive && $txn->tax_rate > 0 ? (float) $sub->trial_amount : (float) $sub->trial_total; + array_unshift($invoice_items, $pm->build_invoice_item($amount, $txn, $prd, $customer_id)); + } + } elseif (count($invoice_items)) { + // If there is no trial period and there is an order bump, set the trial days to cover one payment cycle and + // add the first subscription payment to the trial amount + $now = new DateTimeImmutable('now'); + $end = $now->modify(sprintf('+%d %s', $sub->period, $sub->period_type)); + $trial_days = $end->diff($now)->format('%a'); + + $amount = $calculate_taxes && !$tax_inclusive && $txn->tax_rate > 0 ? (float) $sub->price : (float) $sub->total; + + array_unshift($invoice_items, $pm->build_invoice_item($amount, $txn, $prd, $customer_id)); + } + + $invoice_item_ids = []; + + foreach ($invoice_items as $invoice_item) { + $invoice_item = (object) $pm->send_stripe_request('invoiceitems', $invoice_item, 'post'); + $invoice_item_ids[] = $invoice_item->id; + } + + try { + $subscription = $pm->create_subscription($txn, $sub, $prd, $customer_id, null, true, $trial_days); + + if (empty($subscription->latest_invoice['payment_intent']['client_secret'])) { + throw new MeprGatewayException(__('PaymentIntent not found', 'memberpress')); + } + + $client_secret = $subscription->latest_invoice['payment_intent']['client_secret']; + + $sub->subscr_id = $subscription->id; + $sub->store(); + + $txn->trans_num = $subscription->latest_invoice['id']; + $txn->store(); + } catch (Exception $e) { + // Delete any created invoice items if the subscription failed to be created + foreach ($invoice_item_ids as $invoice_item_id) { + try { + $pm->send_stripe_request("invoiceitems/$invoice_item_id", [], 'delete'); + } catch (Exception $e) { + // ignore any exception here, throw the original + } + } + + throw $e; + } + } } - } - } - // Use a SetupIntent for free trials, and create the subscription via webhook later - if($sub->trial && $sub->trial_days > 0 && (float) $sub->trial_total <= 0.00 && empty($invoice_items)) { - $setup_intent = $pm->create_setup_intent($customer_id, $txn, $prd); + MeprHooks::do_action('mepr_stripe_payment_pending', $txn, $usr); + MeprHooks::do_action('mepr-process-signup', $txn->amount, $usr, $prd->ID, $txn->id); + MeprHooks::do_action('mepr-signup', $txn); - $client_secret = $setup_intent->client_secret; + $return_url = add_query_arg( + [ + 'txn_id' => $txn->id, + 'redirect_to' => urlencode($mepr_options->thankyou_page_url($thank_you_page_args)), + ], + $pm->notify_url('return') + ); - $txn->trans_num = $setup_intent->id; - $txn->store(); + wp_send_json([ + 'action' => $action, + 'client_secret' => $client_secret, + 'return_url' => $return_url, + 'transaction_id' => $txn->id, + ]); + } catch (Exception $e) { + wp_send_json([ + 'error' => $e->getMessage(), + 'transaction_id' => $txn->id, + ]); + } + } - $action = 'confirmSetup'; + /** + * Ends execution with a JSON error response if the user is already subscribed to this product + * + * @param MeprUser $usr + * @param MeprProduct $product + */ + private function check_if_already_subscribed($usr, $product) + { + $mepr_options = MeprOptions::fetch(); + + if ($usr->is_already_subscribed_to($product->ID) && !$product->simultaneous_subscriptions && !$product->allow_renewal && !$product->allow_gifting) { + wp_send_json([ + 'error' => sprintf( + /* translators: %1$s: product name, %2$s: open link tag, %3$s: close link tag */ + esc_html__('You are already subscribed to %1$s, %2$sclick here%3$s to view your subscriptions.', 'memberpress'), + esc_html($product->post_title), + '', + '' + ), + ]); } - else { - $trial_days = 0; + } - if($sub->trial && $sub->trial_days > 0) { - $trial_days = $sub->trial_days; + /** + * Handle the Ajax request to create a SetupIntent for updating the payment method for a subscription + */ + public function create_account_setup_intent() + { + $subscription_id = isset($_POST['subscription_id']) && is_numeric($_POST['subscription_id']) ? (int) $_POST['subscription_id'] : 0; - if((float) $sub->trial_total > 0.00) { - $amount = $calculate_taxes && !$tax_inclusive && $txn->tax_rate > 0 ? (float) $sub->trial_amount : (float) $sub->trial_total; - array_unshift($invoice_items, $pm->build_invoice_item($amount, $txn, $prd, $customer_id)); - } - } - elseif(count($invoice_items)) { - // If there is no trial period and there is an order bump, set the trial days to cover one payment cycle and - // add the first subscription payment to the trial amount - $now = new DateTimeImmutable('now'); - $end = $now->modify(sprintf('+%d %s', $sub->period, $sub->period_type)); - $trial_days = $end->diff($now)->format('%a'); + if (empty($subscription_id)) { + wp_send_json_error(__('Bad request', 'memberpress')); + } - $amount = $calculate_taxes && !$tax_inclusive && $txn->tax_rate > 0 ? (float) $sub->price : (float) $sub->total; + if (!is_user_logged_in()) { + wp_send_json_error(__('Sorry, you must be logged in to do this.', 'memberpress')); + } - array_unshift($invoice_items, $pm->build_invoice_item($amount, $txn, $prd, $customer_id)); - } + if (!check_ajax_referer('mepr_process_update_account_form', '_mepr_nonce', false)) { + wp_send_json_error(__('Security check failed.', 'memberpress')); + } - $invoice_item_ids = []; + $sub = new MeprSubscription($subscription_id); - foreach($invoice_items as $invoice_item) { - $invoice_item = (object) $pm->send_stripe_request('invoiceitems', $invoice_item, 'post'); - $invoice_item_ids[] = $invoice_item->id; - } + if (!($sub instanceof MeprSubscription)) { + wp_send_json_error(__('Subscription not found', 'memberpress')); + } - try { - $subscription = $pm->create_subscription($txn, $sub, $prd, $customer_id, null, true, $trial_days); + $usr = $sub->user(); - if(empty($subscription->latest_invoice['payment_intent']['client_secret'])) { - throw new MeprGatewayException(__('PaymentIntent not found', 'memberpress')); - } + if ($usr->ID != get_current_user_id()) { + wp_send_json_error(__('This subscription is for another user.', 'memberpress')); + } - $client_secret = $subscription->latest_invoice['payment_intent']['client_secret']; - - $sub->subscr_id = $subscription->id; - $sub->store(); - - $txn->trans_num = $subscription->latest_invoice['id']; - $txn->store(); - } - catch(Exception $e) { - // Delete any created invoice items if the subscription failed to be created - foreach($invoice_item_ids as $invoice_item_id) { - try { - $pm->send_stripe_request("invoiceitems/$invoice_item_id", [], 'delete'); - } - catch(Exception $e) { - // ignore any exception here, throw the original - } - } + $pm = $sub->payment_method(); - throw $e; - } + if (!($pm instanceof MeprStripeGateway)) { + wp_send_json_error(__('Invalid payment gateway', 'memberpress')); } - } - - MeprHooks::do_action('mepr_stripe_payment_pending', $txn, $usr); - MeprHooks::do_action('mepr-process-signup', $txn->amount, $usr, $prd->ID, $txn->id); - MeprHooks::do_action('mepr-signup', $txn); - - $return_url = add_query_arg( - [ - 'txn_id' => $txn->id, - 'redirect_to' => urlencode($mepr_options->thankyou_page_url($thank_you_page_args)), - ], - $pm->notify_url('return') - ); - - wp_send_json([ - 'action' => $action, - 'client_secret' => $client_secret, - 'return_url' => $return_url, - 'transaction_id' => $txn->id, - ]); - } - catch(Exception $e) { - wp_send_json([ - 'error' => $e->getMessage(), - 'transaction_id' => $txn->id - ]); - } - } - - /** - * Ends execution with a JSON error response if the user is already subscribed to this product - * - * @param MeprUser $usr - * @param MeprProduct $product - */ - private function check_if_already_subscribed($usr, $product) { - $mepr_options = MeprOptions::fetch(); - - if($usr->is_already_subscribed_to($product->ID) && !$product->simultaneous_subscriptions && !$product->allow_renewal && !$product->allow_gifting) { - wp_send_json(array( - 'error' => sprintf( - /* translators: %1$s: product name, %2$s: open link tag, %3$s: close link tag */ - esc_html__('You are already subscribed to %1$s, %2$sclick here%3$s to view your subscriptions.', 'memberpress'), - esc_html($product->post_title), - '', - '' - ) - )); - } - } - /** - * Handle the Ajax request to create a SetupIntent for updating the payment method for a subscription - */ - public function create_account_setup_intent() { - $subscription_id = isset($_POST['subscription_id']) && is_numeric($_POST['subscription_id']) ? (int) $_POST['subscription_id'] : 0; - - if(empty($subscription_id)) { - wp_send_json_error(__('Bad request', 'memberpress')); - } + try { + if (strpos($sub->subscr_id, 'sub_') === 0) { + $subscription = $pm->retrieve_subscription($sub->subscr_id); + } else { + $subscription = $pm->get_customer_subscription($sub->subscr_id); + } - if (!is_user_logged_in()) { - wp_send_json_error(__('Sorry, you must be logged in to do this.', 'memberpress')); - } + $setup_intent = $pm->create_update_setup_intent($subscription->customer); - if (!check_ajax_referer('mepr_process_update_account_form', '_mepr_nonce', false)) { - wp_send_json_error(__('Security check failed.', 'memberpress')); + wp_send_json_success($setup_intent->client_secret); + } catch (Exception $e) { + wp_send_json_error($e->getMessage()); + } } - $sub = new MeprSubscription($subscription_id); + /** + * Handle the request to update the payment method for a subscription + */ + public function update_payment_method() + { + $mepr_options = MeprOptions::fetch(); - if (!($sub instanceof MeprSubscription)) { - wp_send_json_error(__('Subscription not found', 'memberpress')); - } + $subscription_id = isset($_GET['subscription_id']) && is_numeric($_GET['subscription_id']) ? (int) $_GET['subscription_id'] : 0; + $setup_intent_id = isset($_GET['setup_intent']) ? sanitize_text_field(wp_unslash($_GET['setup_intent'])) : ''; + $nonce = isset($_GET['nonce']) ? sanitize_text_field(wp_unslash($_GET['nonce'])) : ''; - $usr = $sub->user(); + $return_url = add_query_arg([ + 'action' => 'update', + 'sub' => $subscription_id, + ], $mepr_options->account_page_url()); - if ($usr->ID != get_current_user_id()) { - wp_send_json_error(__('This subscription is for another user.', 'memberpress')); - } + if (empty($subscription_id) || empty($setup_intent_id)) { + MeprUtils::wp_redirect(add_query_arg(['errors' => urlencode(__('Bad request', 'memberpress'))], $return_url)); + } - $pm = $sub->payment_method(); + if (!is_user_logged_in()) { + MeprUtils::wp_redirect(add_query_arg(['errors' => urlencode(__('Sorry, you must be logged in to do this.', 'memberpress'))], $return_url)); + } - if (!($pm instanceof MeprStripeGateway)) { - wp_send_json_error(__('Invalid payment gateway', 'memberpress')); - } + if (!wp_verify_nonce($nonce, 'mepr_process_update_account_form')) { + MeprUtils::wp_redirect(add_query_arg(['errors' => urlencode(__('Security check failed.', 'memberpress'))], $return_url)); + } - try { - if(strpos($sub->subscr_id, 'sub_') === 0) { - $subscription = $pm->retrieve_subscription($sub->subscr_id); - } - else { - $subscription = $pm->get_customer_subscription($sub->subscr_id); - } + $sub = new MeprSubscription($subscription_id); - $setup_intent = $pm->create_update_setup_intent($subscription->customer); + if (!($sub->id > 0)) { + MeprUtils::wp_redirect(add_query_arg(['errors' => urlencode(__('Subscription not found', 'memberpress'))], $return_url)); + } - wp_send_json_success($setup_intent->client_secret); - } - catch(Exception $e) { - wp_send_json_error($e->getMessage()); - } - } + $usr = $sub->user(); - /** - * Handle the request to update the payment method for a subscription - */ - public function update_payment_method() { - $mepr_options = MeprOptions::fetch(); + if ($usr->ID != get_current_user_id()) { + MeprUtils::wp_redirect(add_query_arg(['errors' => urlencode(__('This subscription is for another user.', 'memberpress'))], $return_url)); + } - $subscription_id = isset($_GET['subscription_id']) && is_numeric($_GET['subscription_id']) ? (int) $_GET['subscription_id'] : 0; - $setup_intent_id = isset($_GET['setup_intent']) ? sanitize_text_field(wp_unslash($_GET['setup_intent'])) : ''; - $nonce = isset($_GET['nonce']) ? sanitize_text_field(wp_unslash($_GET['nonce'])) : ''; + $pm = $sub->payment_method(); - $return_url = add_query_arg([ - 'action' => 'update', - 'sub' => $subscription_id, - ], $mepr_options->account_page_url()); + if (!($pm instanceof MeprStripeGateway)) { + MeprUtils::wp_redirect(add_query_arg(['errors' => urlencode(__('Invalid payment gateway', 'memberpress'))], $return_url)); + } - if(empty($subscription_id) || empty($setup_intent_id)) { - MeprUtils::wp_redirect(add_query_arg(['errors' => urlencode(__('Bad request', 'memberpress'))], $return_url)); - } + try { + $setup_intent = $pm->retrieve_setup_intent($setup_intent_id); - if(!is_user_logged_in()) { - MeprUtils::wp_redirect(add_query_arg(['errors' => urlencode(__('Sorry, you must be logged in to do this.', 'memberpress'))], $return_url)); - } + if ($setup_intent->status != 'succeeded') { + MeprUtils::wp_redirect(add_query_arg(['errors' => urlencode(__('The payment setup was unsuccessful, please try another payment method.', 'memberpress'))], $return_url)); + } - if(!wp_verify_nonce($nonce, 'mepr_process_update_account_form')) { - MeprUtils::wp_redirect(add_query_arg(['errors' => urlencode(__('Security check failed.', 'memberpress'))], $return_url)); - } + $subscription = $pm->update_subscription_payment_method($sub, $usr, (object) $setup_intent->payment_method); - $sub = new MeprSubscription($subscription_id); + if ($subscription->latest_invoice && $subscription->latest_invoice['status'] == 'open') { + try { + $pm->retry_invoice_payment($subscription->latest_invoice['id']); + } catch (Exception $e) { + // Ignore + } + } - if(!($sub->id > 0)) { - MeprUtils::wp_redirect(add_query_arg(['errors' => urlencode(__('Subscription not found', 'memberpress'))], $return_url)); + MeprUtils::wp_redirect(add_query_arg(['message' => urlencode(__('Your account information was successfully updated.', 'memberpress'))], $return_url)); + } catch (Exception $e) { + MeprUtils::wp_redirect(add_query_arg(['errors' => urlencode($e->getMessage())], $return_url)); + } } - $usr = $sub->user(); + /** + * Handle the Ajax request to debug a checkout error + */ + public function debug_checkout_error() + { + if (!MeprUtils::is_post_request() || !isset($_POST['data']) || !is_string($_POST['data'])) { + wp_send_json_error(); + } - if($usr->ID != get_current_user_id()) { - MeprUtils::wp_redirect(add_query_arg(['errors' => urlencode(__('This subscription is for another user.', 'memberpress'))], $return_url)); - } + $data = json_decode(wp_unslash($_POST['data']), true); - $pm = $sub->payment_method(); + if (!is_array($data)) { + wp_send_json_error(); + } - if(!($pm instanceof MeprStripeGateway)) { - MeprUtils::wp_redirect(add_query_arg(['errors' => urlencode(__('Invalid payment gateway', 'memberpress'))], $return_url)); - } + $allowed_keys = [ + 'text_status' => 'textStatus', + 'error_thrown' => 'errorThrown', + 'status' => 'jqXHR.status (200 expected)', + 'status_text' => 'jqXHR.statusText (OK expected)', + 'response_text' => 'jqXHR.responseText (JSON object expected)', + ]; - try { - $setup_intent = $pm->retrieve_setup_intent($setup_intent_id); + $content = 'INVALID SERVER RESPONSE' . "\n\n"; - if($setup_intent->status != 'succeeded') { - MeprUtils::wp_redirect(add_query_arg(['errors' => urlencode(__('The payment setup was unsuccessful, please try another payment method.', 'memberpress'))], $return_url)); - } + foreach ($allowed_keys as $key => $label) { + if (!array_key_exists($key, $data)) { + continue; + } - $subscription = $pm->update_subscription_payment_method($sub, $usr, (object) $setup_intent->payment_method); + ob_start(); + var_dump($data[$key]); + $value = ob_get_clean(); - if($subscription->latest_invoice && $subscription->latest_invoice['status'] == 'open') { - try { - $pm->retry_invoice_payment($subscription->latest_invoice['id']); - } catch (Exception $e) { - // Ignore + $content .= sprintf( + "%s:\n%s\n", + $label, + $value + ); } - } - - MeprUtils::wp_redirect(add_query_arg(['message' => urlencode(__('Your account information was successfully updated.', 'memberpress'))], $return_url)); - } catch (Exception $e) { - MeprUtils::wp_redirect(add_query_arg(['errors' => urlencode($e->getMessage())], $return_url)); - } - } - - /** - * Handle the Ajax request to debug a checkout error - */ - public function debug_checkout_error() { - if (!MeprUtils::is_post_request() || !isset($_POST['data']) || !is_string($_POST['data'])) { - wp_send_json_error(); - } - $data = json_decode(wp_unslash($_POST['data']), true); - - if (!is_array($data)) { - wp_send_json_error(); - } + $this->send_checkout_error_debug_email( + $content, + isset($data['transaction_id']) && is_numeric($data['transaction_id']) ? (int) $data['transaction_id'] : null, + isset($data['customer_email']) && is_string($data['customer_email']) ? sanitize_text_field($data['customer_email']) : null + ); - $allowed_keys = array( - 'text_status' => 'textStatus', - 'error_thrown' => 'errorThrown', - 'status' => 'jqXHR.status (200 expected)', - 'status_text' => 'jqXHR.statusText (OK expected)', - 'response_text' => 'jqXHR.responseText (JSON object expected)' - ); - - $content = 'INVALID SERVER RESPONSE' . "\n\n"; - - foreach ($allowed_keys as $key => $label) { - if (!array_key_exists($key, $data)) { - continue; - } - - ob_start(); - var_dump($data[$key]); - $value = ob_get_clean(); - - $content .= sprintf( - "%s:\n%s\n", - $label, - $value - ); + wp_send_json_success(); } - $this->send_checkout_error_debug_email( - $content, - isset($data['transaction_id']) && is_numeric($data['transaction_id']) ? (int) $data['transaction_id'] : null, - isset($data['customer_email']) && is_string($data['customer_email']) ? sanitize_text_field($data['customer_email']) : null - ); - - wp_send_json_success(); - } - - /** - * Sends an email to the admin email addresses alerting them of the given checkout error - * - * @param string $content - * @param int|null $transaction_id - * @param string|null $customer_email - */ - private function send_checkout_error_debug_email($content, $transaction_id = null, $customer_email = null) { - if (MeprHooks::apply_filters('mepr_disable_checkout_error_debug_email', false)) { - return; - } + /** + * Sends an email to the admin email addresses alerting them of the given checkout error + * + * @param string $content + * @param integer|null $transaction_id + * @param string|null $customer_email + */ + private function send_checkout_error_debug_email($content, $transaction_id = null, $customer_email = null) + { + if (MeprHooks::apply_filters('mepr_disable_checkout_error_debug_email', false)) { + return; + } - $message = 'An error occurred during the MemberPress checkout which resulted in an error message being displayed to the customer. The transaction may not have fully completed.' . "\n\n"; - $message .= 'The error may have happened due to a plugin conflict or custom code, please carefully check the details below to identify the cause, or you can send this email to support@memberpress.com for help.' . "\n\n"; + $message = 'An error occurred during the MemberPress checkout which resulted in an error message being displayed to the customer. The transaction may not have fully completed.' . "\n\n"; + $message .= 'The error may have happened due to a plugin conflict or custom code, please carefully check the details below to identify the cause, or you can send this email to support@memberpress.com for help.' . "\n\n"; - if ($transaction_id) { - $message .= sprintf("MemberPress transaction ID: %s\n", $transaction_id); + if ($transaction_id) { + $message .= sprintf("MemberPress transaction ID: %s\n", $transaction_id); - if (!$customer_email) { - $transaction = new MeprTransaction($transaction_id); - $user = $transaction->user(); + if (!$customer_email) { + $transaction = new MeprTransaction($transaction_id); + $user = $transaction->user(); - if ($user->ID > 0 && $user->user_email) { - $customer_email = $user->user_email; + if ($user->ID > 0 && $user->user_email) { + $customer_email = $user->user_email; + } + } } - } - } - if ($customer_email) { - $message .= sprintf("Customer email: %s\n", $customer_email); - } + if ($customer_email) { + $message .= sprintf("Customer email: %s\n", $customer_email); + } - $message .= sprintf("Customer IP: %s\n", $_SERVER['REMOTE_ADDR']); - $message .= sprintf("Customer User Agent: %s\n", !empty($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : '(empty)'); - $message .= sprintf("Date (UTC): %s\n\n", gmdate('Y-m-d H:i:s')); + $message .= sprintf("Customer IP: %s\n", $_SERVER['REMOTE_ADDR']); + $message .= sprintf("Customer User Agent: %s\n", !empty($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : '(empty)'); + $message .= sprintf("Date (UTC): %s\n\n", gmdate('Y-m-d H:i:s')); - MeprUtils::wp_mail_to_admin('[MemberPress] IMPORTANT: Checkout error', $message . $content); - } + MeprUtils::wp_mail_to_admin('[MemberPress] IMPORTANT: Checkout error', $message . $content); + } } diff --git a/app/controllers/MeprSubscriptionsCtrl.php b/app/controllers/MeprSubscriptionsCtrl.php index b9d9520..f0a9a05 100644 --- a/app/controllers/MeprSubscriptionsCtrl.php +++ b/app/controllers/MeprSubscriptionsCtrl.php @@ -1,593 +1,617 @@ get_hook(); - add_action( "load-{$hook}", array($this,'add_recurring_screen_options') ); - add_filter( "manage_{$hook}_columns", array($this, 'get_columns') ); - - $hook = $this->get_hook(true); - add_action( "load-{$hook}", array($this,'add_lifetime_screen_options') ); - add_filter( "manage_{$hook}_columns", array($this, 'get_lifetime_columns') ); - - add_filter( 'set_screen_option_mp_lifetime_subs_perpage', array($this,'setup_screen_options_lifetime'), 10, 3 ); - add_filter( 'set_screen_option_mp_subs_perpage', array($this,'setup_screen_options_subs'), 10, 3 ); - - add_action('mepr_table_controls_search', array($this, 'table_search_box')); - } - - private function get_hook($lifetime=false) { - if($lifetime) { return 'admin_page_memberpress-lifetimes'; } - return 'memberpress_page_memberpress-subscriptions'; - } - - private function is_lifetime() { - $screen = get_current_screen(); - - if( isset($screen) && is_object($screen) ) { - return ( $this->get_hook(true) === $screen->id ); - } - else if( isset($_GET['page']) ) { - return ( 'memberpress-lifetimes' === $_GET['page'] ); - } - else - return false; - } - - public function listing() { - $action = isset($_REQUEST['action']) ? $_REQUEST['action'] : 'list'; - - switch ($action) { - case 'new': - $this->new_sub(); - break; - case 'edit': - $this->edit(); - break; - case 'list': - $this->display_list(); - default: - break; - } - } - - private function display_list() { - $screen = get_current_screen(); - $lifetime = ( $screen->id === $this->get_hook(true) ); - $sub_table = new MeprSubscriptionsTable( $screen, $this->get_columns(), $lifetime ); - $sub_table->prepare_items(); - MeprView::render('/admin/subscriptions/list', get_defined_vars()); - } - - private function new_sub() { - $mepr_options = MeprOptions::fetch(); - $sub = new MeprSubscription(); - - if(MeprUtils::is_post_request()) { - $errors = $this->validate(); - if(empty($errors)) { - if($this->create_or_update($sub)) { - $sub = new MeprSubscription($sub->id); - $user = $sub->user(); - $sub->user_login = $user->user_login; - $message = __('A subscription was created successfully.', 'memberpress'); - } - else { - $errors[] = __('There was a problem creating the subscription', 'memberpress'); - } - $_REQUEST['id'] = $sub->id; - $_REQUEST['action'] = 'edit'; - MeprView::render('/admin/subscriptions/edit', get_defined_vars()); - } - else { - MeprView::render('/admin/subscriptions/new', get_defined_vars()); - } - } - else { - MeprView::render('/admin/subscriptions/new', get_defined_vars()); - } - } - - private function edit() { - $mepr_options = MeprOptions::fetch(); - if(isset($_REQUEST['id'])) { - $sub = new MeprSubscription($_REQUEST['id']); - if($sub->id > 0) { - $user = $sub->user(); - $sub->user_login = $user->user_login; - if(MeprUtils::is_post_request()) { - $errors = $this->validate(); - if(empty($errors) && $this->create_or_update($sub)){ - $message = __('The subscription was updated successfully.', 'memberpress'); - } - else { - $errors[] = __('There was a problem updating the subscription', 'memberpress'); - } - MeprView::render('/admin/subscriptions/edit', get_defined_vars()); - } - else { - MeprView::render('/admin/subscriptions/edit', get_defined_vars()); - } - } - else { - $this->new_sub(); - } - } - else { - $this->new_sub(); - } - } - - private function create_or_update($sub) { - check_admin_referer( 'mepr_create_or_update_subscription', 'mepr_subscriptions_nonce' ); - - extract($_POST, EXTR_SKIP); - $user = new MeprUser(); - $user->load_user_data_by_login($user_login); - $sub->user_id = $user->ID; - $sub->subscr_id = wp_unslash($subscr_id); - $sub->product_id = $product_id; - $product = new MeprProduct($product_id); - $sub->price = isset($price) ? MeprUtils::format_currency_us_float( $price ) : MeprUtils::format_currency_us_float( $product->price ); - $sub->period = isset($period) ? (int) $period : (int) $product->period; - $sub->period_type = isset($period_type) ? (string) $period_type : (string) $product->period_type; - $sub->limit_cycles = isset($limit_cycles) ? (boolean) $limit_cycles : $product->limit_cycles; - $sub->limit_cycles_num = isset($limit_cycles_num) ? (int) $limit_cycles_num : (int) $product->limit_cycles_num; - $sub->limit_cycles_action = isset($limit_cycles_action) ? $limit_cycles_action : $product->limit_cycles_action; - $sub->limit_cycles_expires_after = isset($limit_cycles_expires_after) ? (int) $limit_cycles_expires_after : (int) $product->limit_cycles_expires_after; - $sub->limit_cycles_expires_type = isset($limit_cycles_expires_type) ? (string) $limit_cycles_expires_type : (string) $product->limit_cycles_expires_type; - $sub->tax_amount = MeprUtils::format_currency_us_float( $tax_amount ); - $sub->tax_rate = MeprUtils::format_currency_us_float( $tax_rate ); - $sub->total = $sub->price + $sub->tax_amount; - $sub->status = $status; - $sub->gateway = $gateway; - $sub->trial = isset($trial) ? (boolean) $trial : false; - $sub->trial_days = (int) $trial_days; - $sub->trial_amount = MeprUtils::format_currency_us_float( $trial_amount ); - $sub->trial_tax_amount = (isset($trial_tax_amount) ? (float) $trial_tax_amount : 0.0); - $sub->trial_total = (isset($trial_total) ? (float) $trial_total : 0.0); - if(isset($created_at) && (empty($created_at) || is_null($created_at))) { - $sub->created_at = MeprUtils::ts_to_mysql_date(time()); - } - else { - $sub->created_at = MeprUtils::ts_to_mysql_date(strtotime($created_at)); - } - return $sub->store(); - } - - private function validate() { - $errors = array(); - extract($_POST, EXTR_SKIP); - $user = new MeprUser; - - if(isset($subscr_id) && !empty($subscr_id)) { - if(preg_match("#[^a-zA-z0-9_\-]#", $subscr_id)) { - $errors[] = __('The Subscription ID must contain only letters, numbers, underscores and hyphens', 'memberpress'); - } - } - else { - $errors[] = __('The Subscription ID is required', 'memberpress'); - } - if(!isset($user_login) || empty($user_login)) { - $errors[] = __("The username is required", 'memberpress'); - } - elseif(is_email($user_login) && !username_exists($user_login)) { - $user->load_user_data_by_email($user_login); - if(!$user->ID) { - $errors[] = __('You must enter a valid username or email address', 'memberpress'); - } - else { //For use downstream in create and update transaction methods - $_POST['user_login'] = $user->user_login; - } + public function load_hooks() + { + add_action('admin_enqueue_scripts', [$this, 'enqueue_scripts']); + add_action('wp_ajax_mepr_subscr_num_search', [$this, 'subscr_num_search']); + add_action('wp_ajax_mepr_subscr_edit_status', [$this, 'edit_subscr_status']); + add_action('wp_ajax_mepr_delete_subscription', [$this, 'delete_subscription']); + add_action('wp_ajax_mepr_suspend_subscription', [$this, 'suspend_subscription']); + add_action('wp_ajax_mepr_resume_subscription', [$this, 'resume_subscription']); + add_action('wp_ajax_mepr_resume_subscription_email_customer', [$this, 'resume_subscription_email_customer']); + add_action('wp_ajax_mepr_cancel_subscription', [$this, 'cancel_subscription']); + add_action('wp_ajax_mepr_subscriptions', [$this, 'csv']); + add_action('wp_ajax_mepr_lifetime_subscriptions', [$this, 'lifetime_csv']); + add_action('mepr_control_table_footer', [$this, 'export_footer_link'], 10, 3); + + // Screen Options + $hook = $this->get_hook(); + add_action("load-{$hook}", [$this,'add_recurring_screen_options']); + add_filter("manage_{$hook}_columns", [$this, 'get_columns']); + + $hook = $this->get_hook(true); + add_action("load-{$hook}", [$this,'add_lifetime_screen_options']); + add_filter("manage_{$hook}_columns", [$this, 'get_lifetime_columns']); + + add_filter('set_screen_option_mp_lifetime_subs_perpage', [$this,'setup_screen_options_lifetime'], 10, 3); + add_filter('set_screen_option_mp_subs_perpage', [$this,'setup_screen_options_subs'], 10, 3); + + add_action('mepr_table_controls_search', [$this, 'table_search_box']); + } + + private function get_hook($lifetime = false) + { + if ($lifetime) { + return 'admin_page_memberpress-lifetimes'; + } + return 'memberpress_page_memberpress-subscriptions'; } - else { - $user->load_user_data_by_login($user_login); - if(!$user->ID) { - $errors[] = __('You must enter a valid username or email address', 'memberpress'); - } + + private function is_lifetime() + { + $screen = get_current_screen(); + + if (isset($screen) && is_object($screen)) { + return ( $this->get_hook(true) === $screen->id ); + } elseif (isset($_GET['page'])) { + return ( 'memberpress-lifetimes' === $_GET['page'] ); + } else { + return false; + } } - if(!isset($product_id) || empty($product_id)) { - $errors[] = __('Membership is required', 'memberpress'); + + public function listing() + { + $action = isset($_REQUEST['action']) ? $_REQUEST['action'] : 'list'; + + switch ($action) { + case 'new': + $this->new_sub(); + break; + case 'edit': + $this->edit(); + break; + case 'list': + $this->display_list(); + default: + break; + } } - else { - $product = new MeprProduct($product_id); - if(!isset($product->ID)) { - $errors[] = __('A valid membership is required', 'memberpress'); - } + + private function display_list() + { + $screen = get_current_screen(); + $lifetime = ( $screen->id === $this->get_hook(true) ); + $sub_table = new MeprSubscriptionsTable($screen, $this->get_columns(), $lifetime); + $sub_table->prepare_items(); + MeprView::render('/admin/subscriptions/list', get_defined_vars()); } - if(!isset($price) || empty($price)) { - $errors[] = __("The sub-total is required", 'memberpress'); + + private function new_sub() + { + $mepr_options = MeprOptions::fetch(); + $sub = new MeprSubscription(); + + if (MeprUtils::is_post_request()) { + $errors = $this->validate(); + if (empty($errors)) { + if ($this->create_or_update($sub)) { + $sub = new MeprSubscription($sub->id); + $user = $sub->user(); + $sub->user_login = $user->user_login; + $message = __('A subscription was created successfully.', 'memberpress'); + } else { + $errors[] = __('There was a problem creating the subscription', 'memberpress'); + } + $_REQUEST['id'] = $sub->id; + $_REQUEST['action'] = 'edit'; + MeprView::render('/admin/subscriptions/edit', get_defined_vars()); + } else { + MeprView::render('/admin/subscriptions/new', get_defined_vars()); + } + } else { + MeprView::render('/admin/subscriptions/new', get_defined_vars()); + } } - if( preg_match("/[^0-9., ]/", $price) ) { - $errors[] = __("The sub-total must be a number", 'memberpress'); + + private function edit() + { + $mepr_options = MeprOptions::fetch(); + if (isset($_REQUEST['id'])) { + $sub = new MeprSubscription($_REQUEST['id']); + if ($sub->id > 0) { + $user = $sub->user(); + $sub->user_login = $user->user_login; + if (MeprUtils::is_post_request()) { + $errors = $this->validate(); + if (empty($errors) && $this->create_or_update($sub)) { + $message = __('The subscription was updated successfully.', 'memberpress'); + } else { + $errors[] = __('There was a problem updating the subscription', 'memberpress'); + } + MeprView::render('/admin/subscriptions/edit', get_defined_vars()); + } else { + MeprView::render('/admin/subscriptions/edit', get_defined_vars()); + } + } else { + $this->new_sub(); + } + } else { + $this->new_sub(); + } } - if(!is_numeric($trial_days)) { - $errors[] = __("The trial days must be a number", 'memberpress'); + + private function create_or_update($sub) + { + check_admin_referer('mepr_create_or_update_subscription', 'mepr_subscriptions_nonce'); + + extract($_POST, EXTR_SKIP); + $user = new MeprUser(); + $user->load_user_data_by_login($user_login); + $sub->user_id = $user->ID; + $sub->subscr_id = wp_unslash($subscr_id); + $sub->product_id = $product_id; + $product = new MeprProduct($product_id); + $sub->price = isset($price) ? MeprUtils::format_currency_us_float($price) : MeprUtils::format_currency_us_float($product->price); + $sub->period = isset($period) ? (int) $period : (int) $product->period; + $sub->period_type = isset($period_type) ? (string) $period_type : (string) $product->period_type; + $sub->limit_cycles = isset($limit_cycles) ? (bool) $limit_cycles : $product->limit_cycles; + $sub->limit_cycles_num = isset($limit_cycles_num) ? (int) $limit_cycles_num : (int) $product->limit_cycles_num; + $sub->limit_cycles_action = isset($limit_cycles_action) ? $limit_cycles_action : $product->limit_cycles_action; + $sub->limit_cycles_expires_after = isset($limit_cycles_expires_after) ? (int) $limit_cycles_expires_after : (int) $product->limit_cycles_expires_after; + $sub->limit_cycles_expires_type = isset($limit_cycles_expires_type) ? (string) $limit_cycles_expires_type : (string) $product->limit_cycles_expires_type; + $sub->tax_amount = MeprUtils::format_currency_us_float($tax_amount); + $sub->tax_rate = MeprUtils::format_currency_us_float($tax_rate); + $sub->total = $sub->price + $sub->tax_amount; + $sub->status = $status; + $sub->gateway = $gateway; + $sub->trial = isset($trial) ? (bool) $trial : false; + $sub->trial_days = (int) $trial_days; + $sub->trial_amount = MeprUtils::format_currency_us_float($trial_amount); + $sub->trial_tax_amount = (isset($trial_tax_amount) ? (float) $trial_tax_amount : 0.0); + $sub->trial_total = (isset($trial_total) ? (float) $trial_total : 0.0); + if (isset($created_at) && (empty($created_at) || is_null($created_at))) { + $sub->created_at = MeprUtils::ts_to_mysql_date(time()); + } else { + $sub->created_at = MeprUtils::ts_to_mysql_date(strtotime($created_at)); + } + return $sub->store(); } - return $errors; - } + private function validate() + { + $errors = []; + extract($_POST, EXTR_SKIP); + $user = new MeprUser(); + + if (isset($subscr_id) && !empty($subscr_id)) { + if (preg_match('#[^a-zA-z0-9_\-]#', $subscr_id)) { + $errors[] = __('The Subscription ID must contain only letters, numbers, underscores and hyphens', 'memberpress'); + } + } else { + $errors[] = __('The Subscription ID is required', 'memberpress'); + } + if (!isset($user_login) || empty($user_login)) { + $errors[] = __('The username is required', 'memberpress'); + } elseif (is_email($user_login) && !username_exists($user_login)) { + $user->load_user_data_by_email($user_login); + if (!$user->ID) { + $errors[] = __('You must enter a valid username or email address', 'memberpress'); + } else { // For use downstream in create and update transaction methods + $_POST['user_login'] = $user->user_login; + } + } else { + $user->load_user_data_by_login($user_login); + if (!$user->ID) { + $errors[] = __('You must enter a valid username or email address', 'memberpress'); + } + } + if (!isset($product_id) || empty($product_id)) { + $errors[] = __('Membership is required', 'memberpress'); + } else { + $product = new MeprProduct($product_id); + if (!isset($product->ID)) { + $errors[] = __('A valid membership is required', 'memberpress'); + } + } + if (!isset($price) || empty($price)) { + $errors[] = __('The sub-total is required', 'memberpress'); + } + if (preg_match('/[^0-9., ]/', $price)) { + $errors[] = __('The sub-total must be a number', 'memberpress'); + } + if (!is_numeric($trial_days)) { + $errors[] = __('The trial days must be a number', 'memberpress'); + } + + return $errors; + } - public function enqueue_scripts($hook) - { - if( $hook === $this->get_hook() || $hook === $this->get_hook(true) ) + public function enqueue_scripts($hook) { - $l10n = array( 'del_sub' => __('A Subscription should be cancelled (at the Gateway or here) by you, or by the Member on their Account page before being removed. Deleting an Active Subscription can cause future recurring payments not to be tracked properly. Are you sure you want to delete this Subscription?', 'memberpress'), - 'del_sub_error' => __('The Subscription could not be deleted. Please try again later.', 'memberpress'), - 'cancel_sub' => __('This will cancel all future payments for this subscription. Are you sure you want to cancel this Subscription?', 'memberpress'), - 'cancel_sub_error' => __('The Subscription could not be cancelled here. Please login to your gateway\'s virtual terminal to cancel it.', 'memberpress'), - 'cancel_sub_success' => __('The Subscription was successfully cancelled.', 'memberpress'), - 'cancelled_text' => __('Stopped', 'memberpress'), - 'suspend_sub' => __("This will stop all payments for this subscription until the user logs into their account and resumes.\n\nAre you sure you want to pause this Subscription?", 'memberpress'), - 'suspend_sub_error' => __('The Subscription could not be paused here. Please login to your gateway\'s virtual terminal to pause it.', 'memberpress'), - 'suspend_sub_success' => __('The Subscription was successfully paused.', 'memberpress'), - 'suspend_text' => __('Paused', 'memberpress'), - 'resume_sub' => __("This will resume payments for this subscription.\n\nAre you sure you want to resume this Subscription?", 'memberpress'), - 'resume_sub_error' => __('The Subscription could not be resumed here. Please login to your gateway\'s virtual terminal to resume it.', 'memberpress'), - 'resume_sub_success' => __('The Subscription was successfully resumed.', 'memberpress'), - 'resume_sub_requires_action' => __('The Subscription could not be resumed automatically because the customer needs to authorize the transaction. Do you want to send the customer an email with a link to pay the invoice?', 'memberpress'), - 'resume_sub_customer_email_sent' => __('The email has been sent to the customer. The Subscription will resume when the invoice has been paid.', 'memberpress'), - 'resume_sub_customer_email_error' => __("An error occurred sending the email to the customer:\n\n%s", 'memberpress'), - 'server_response_invalid' => __('The response from the server was invalid or malformed', 'memberpress'), - 'ajax_error' => __('Ajax error', 'memberpress'), - 'resume_text' => __('Enabled', 'memberpress'), - 'delete_subscription_nonce' => wp_create_nonce('delete_subscription'), - 'suspend_subscription_nonce' => wp_create_nonce('suspend_subscription'), - 'update_status_subscription_nonce' => wp_create_nonce('update_status_subscription'), - 'resume_subscription_nonce' => wp_create_nonce('resume_subscription'), - 'resume_subscription_email_customer_nonce' => wp_create_nonce('resume_subscription_email_customer'), - 'cancel_subscription_nonce' => wp_create_nonce('cancel_subscription') - ); - - wp_enqueue_style('mepr-subscriptions-css', MEPR_CSS_URL.'/admin-subscriptions.css', array(), MEPR_VERSION); - wp_register_script('mphelpers', MEPR_JS_URL.'/mphelpers.js', array('suggest'), MEPR_VERSION); - wp_enqueue_script('mepr-subscriptions-js', MEPR_JS_URL.'/admin_subscriptions.js', array('jquery', 'mphelpers'), MEPR_VERSION); - wp_enqueue_script('mepr-table-controls-js', MEPR_JS_URL.'/table_controls.js', array('jquery'), MEPR_VERSION); - wp_localize_script('mepr-subscriptions-js', 'MeprSub', $l10n); + if ($hook === $this->get_hook() || $hook === $this->get_hook(true)) { + $l10n = [ + 'del_sub' => __('A Subscription should be cancelled (at the Gateway or here) by you, or by the Member on their Account page before being removed. Deleting an Active Subscription can cause future recurring payments not to be tracked properly. Are you sure you want to delete this Subscription?', 'memberpress'), + 'del_sub_error' => __('The Subscription could not be deleted. Please try again later.', 'memberpress'), + 'cancel_sub' => __('This will cancel all future payments for this subscription. Are you sure you want to cancel this Subscription?', 'memberpress'), + 'cancel_sub_error' => __('The Subscription could not be cancelled here. Please login to your gateway\'s virtual terminal to cancel it.', 'memberpress'), + 'cancel_sub_success' => __('The Subscription was successfully cancelled.', 'memberpress'), + 'cancelled_text' => __('Stopped', 'memberpress'), + 'suspend_sub' => __("This will stop all payments for this subscription until the user logs into their account and resumes.\n\nAre you sure you want to pause this Subscription?", 'memberpress'), + 'suspend_sub_error' => __('The Subscription could not be paused here. Please login to your gateway\'s virtual terminal to pause it.', 'memberpress'), + 'suspend_sub_success' => __('The Subscription was successfully paused.', 'memberpress'), + 'suspend_text' => __('Paused', 'memberpress'), + 'resume_sub' => __("This will resume payments for this subscription.\n\nAre you sure you want to resume this Subscription?", 'memberpress'), + 'resume_sub_error' => __('The Subscription could not be resumed here. Please login to your gateway\'s virtual terminal to resume it.', 'memberpress'), + 'resume_sub_success' => __('The Subscription was successfully resumed.', 'memberpress'), + 'resume_sub_requires_action' => __('The Subscription could not be resumed automatically because the customer needs to authorize the transaction. Do you want to send the customer an email with a link to pay the invoice?', 'memberpress'), + 'resume_sub_customer_email_sent' => __('The email has been sent to the customer. The Subscription will resume when the invoice has been paid.', 'memberpress'), + 'resume_sub_customer_email_error' => __("An error occurred sending the email to the customer:\n\n%s", 'memberpress'), + 'server_response_invalid' => __('The response from the server was invalid or malformed', 'memberpress'), + 'ajax_error' => __('Ajax error', 'memberpress'), + 'resume_text' => __('Enabled', 'memberpress'), + 'delete_subscription_nonce' => wp_create_nonce('delete_subscription'), + 'suspend_subscription_nonce' => wp_create_nonce('suspend_subscription'), + 'update_status_subscription_nonce' => wp_create_nonce('update_status_subscription'), + 'resume_subscription_nonce' => wp_create_nonce('resume_subscription'), + 'resume_subscription_email_customer_nonce' => wp_create_nonce('resume_subscription_email_customer'), + 'cancel_subscription_nonce' => wp_create_nonce('cancel_subscription'), + ]; + + wp_enqueue_style('mepr-subscriptions-css', MEPR_CSS_URL . '/admin-subscriptions.css', [], MEPR_VERSION); + wp_register_script('mphelpers', MEPR_JS_URL . '/mphelpers.js', ['suggest'], MEPR_VERSION); + wp_enqueue_script('mepr-subscriptions-js', MEPR_JS_URL . '/admin_subscriptions.js', ['jquery', 'mphelpers'], MEPR_VERSION); + wp_enqueue_script('mepr-table-controls-js', MEPR_JS_URL . '/table_controls.js', ['jquery'], MEPR_VERSION); + wp_localize_script('mepr-subscriptions-js', 'MeprSub', $l10n); + } } - } - public function edit_subscr_status() { - check_ajax_referer('update_status_subscription','mepr_subscriptions_nonce'); + public function edit_subscr_status() + { + check_ajax_referer('update_status_subscription', 'mepr_subscriptions_nonce'); - if( !isset($_POST['id']) || empty($_POST['id']) || - !isset($_POST['value']) || empty($_POST['value']) ) - die(__('Save Failed', 'memberpress')); + if ( + !isset($_POST['id']) || empty($_POST['id']) || + !isset($_POST['value']) || empty($_POST['value']) + ) { + die(__('Save Failed', 'memberpress')); + } - $id = sanitize_key($_POST['id']); - $value = sanitize_text_field($_POST['value']); + $id = sanitize_key($_POST['id']); + $value = sanitize_text_field($_POST['value']); - $sub = new MeprSubscription($id); - if( empty($sub->id) ) - die(__('Save Failed', 'memberpress')); + $sub = new MeprSubscription($id); + if (empty($sub->id)) { + die(__('Save Failed', 'memberpress')); + } - $sub->status = $value; - $sub->store(); + $sub->status = $value; + $sub->store(); - echo MeprAppHelper::human_readable_status( $value, 'subscription' ); - die(); - } + echo MeprAppHelper::human_readable_status($value, 'subscription'); + die(); + } - public function delete_subscription() { - check_ajax_referer('delete_subscription','mepr_subscriptions_nonce'); + public function delete_subscription() + { + check_ajax_referer('delete_subscription', 'mepr_subscriptions_nonce'); - if(!MeprUtils::is_mepr_admin()) { - die(__('You do not have access.', 'memberpress')); - } + if (!MeprUtils::is_mepr_admin()) { + die(__('You do not have access.', 'memberpress')); + } - if(!isset($_POST['id']) || empty($_POST['id']) || !is_numeric($_POST['id'])) { - die(__('Could not delete subscription', 'memberpress')); + if (!isset($_POST['id']) || empty($_POST['id']) || !is_numeric($_POST['id'])) { + die(__('Could not delete subscription', 'memberpress')); + } + + $sub = new MeprSubscription($_POST['id']); + $sub->destroy(); + + die('true'); // don't localize this string } - $sub = new MeprSubscription($_POST['id']); - $sub->destroy(); + public function suspend_subscription() + { + check_ajax_referer('suspend_subscription', 'mepr_subscriptions_nonce'); - die('true'); //don't localize this string - } + if (!MeprUtils::is_mepr_admin()) { + die(__('You do not have access.', 'memberpress')); + } - public function suspend_subscription() { - check_ajax_referer('suspend_subscription','mepr_subscriptions_nonce'); + if (!isset($_POST['id']) || empty($_POST['id']) || !is_numeric($_POST['id'])) { + die(__('Could not pause subscription', 'memberpress')); + } - if(!MeprUtils::is_mepr_admin()) { - die(__('You do not have access.', 'memberpress')); - } + $sub = new MeprSubscription($_POST['id']); + $sub->suspend(); - if(!isset($_POST['id']) || empty($_POST['id']) || !is_numeric($_POST['id'])) { - die(__('Could not pause subscription', 'memberpress')); + die('true'); // don't localize this string } - $sub = new MeprSubscription($_POST['id']); - $sub->suspend(); + public function resume_subscription() + { + if (!check_ajax_referer('resume_subscription', 'mepr_subscriptions_nonce', false)) { + wp_send_json([ + 'status' => 'error', + 'message' => __('Security check failed.', 'memberpress'), + ]); + } - die('true'); //don't localize this string - } + if (!MeprUtils::is_mepr_admin()) { + wp_send_json([ + 'status' => 'error', + 'message' => __('You do not have access.', 'memberpress'), + ]); + } - public function resume_subscription() { - if(!check_ajax_referer('resume_subscription','mepr_subscriptions_nonce', false)) { - wp_send_json(array( - 'status' => 'error', - 'message' => __('Security check failed.', 'memberpress') - )); - } + if (!isset($_POST['id']) || empty($_POST['id']) || !is_numeric($_POST['id'])) { + wp_send_json([ + 'status' => 'error', + 'message' => __('Could not resume subscription', 'memberpress'), + ]); + } - if(!MeprUtils::is_mepr_admin()) { - wp_send_json(array( - 'status' => 'error', - 'message' => __('You do not have access.', 'memberpress') - )); - } + $sub = new MeprSubscription($_POST['id']); + + try { + $sub->resume(); + } catch (MeprGatewayRequiresActionException $e) { + wp_send_json([ + 'status' => 'requires_action', + ]); + } catch (Exception $e) { + wp_send_json([ + 'status' => 'error', + 'message' => $e->getMessage(), + ]); + } - if(!isset($_POST['id']) || empty($_POST['id']) || !is_numeric($_POST['id'])) { - wp_send_json(array( - 'status' => 'error', - 'message' => __('Could not resume subscription', 'memberpress') - )); + wp_send_json(['status' => 'success']); } - $sub = new MeprSubscription($_POST['id']); - - try { - $sub->resume(); - } catch (MeprGatewayRequiresActionException $e) { - wp_send_json(array( - 'status' => 'requires_action' - )); - } catch (Exception $e) { - wp_send_json(array( - 'status' => 'error', - 'message' => $e->getMessage() - )); - } + public function resume_subscription_email_customer() + { + if (!check_ajax_referer('resume_subscription_email_customer', 'mepr_subscriptions_nonce', false)) { + wp_send_json([ + 'status' => 'error', + 'message' => __('Security check failed.', 'memberpress'), + ]); + } - wp_send_json(array('status' => 'success')); - } + if (!MeprUtils::is_mepr_admin()) { + wp_send_json([ + 'status' => 'error', + 'message' => __('You do not have access.', 'memberpress'), + ]); + } - public function resume_subscription_email_customer() { - if(!check_ajax_referer('resume_subscription_email_customer','mepr_subscriptions_nonce', false)) { - wp_send_json(array( - 'status' => 'error', - 'message' => __('Security check failed.', 'memberpress') - )); - } + if (!isset($_POST['id']) || empty($_POST['id']) || !is_numeric($_POST['id'])) { + wp_send_json([ + 'status' => 'error', + 'message' => __('Could not resume subscription', 'memberpress'), + ]); + } - if(!MeprUtils::is_mepr_admin()) { - wp_send_json(array( - 'status' => 'error', - 'message' => __('You do not have access.', 'memberpress') - )); - } + $sub = new MeprSubscription($_POST['id']); - if(!isset($_POST['id']) || empty($_POST['id']) || !is_numeric($_POST['id'])) { - wp_send_json(array( - 'status' => 'error', - 'message' => __('Could not resume subscription', 'memberpress') - )); - } + if (!$sub->id) { + wp_send_json([ + 'status' => 'error', + 'message' => __('Subscription not found.', 'memberpress'), + ]); + } - $sub = new MeprSubscription($_POST['id']); + $pm = $sub->payment_method(); + + if ($pm instanceof MeprStripeGateway) { + try { + $pm->send_latest_invoice_payment_email($sub); + } catch (Exception $e) { + wp_send_json([ + 'status' => 'error', + 'message' => $e->getMessage(), + ]); + } + } else { + wp_send_json([ + 'status' => 'error', + 'message' => __('The subscription\'s payment method does not support this.', 'memberpress'), + ]); + } - if (!$sub->id) { - wp_send_json(array( - 'status' => 'error', - 'message' => __('Subscription not found.', 'memberpress') - )); + wp_send_json(['status' => 'success']); } - $pm = $sub->payment_method(); - - if ($pm instanceof MeprStripeGateway) { - try { - $pm->send_latest_invoice_payment_email($sub); - } catch (Exception $e) { - wp_send_json(array( - 'status' => 'error', - 'message' => $e->getMessage() - )); - } - } else { - wp_send_json(array( - 'status' => 'error', - 'message' => __('The subscription\'s payment method does not support this.', 'memberpress') - )); - } + public function cancel_subscription() + { + check_ajax_referer('cancel_subscription', 'mepr_subscriptions_nonce'); + + if (!MeprUtils::is_mepr_admin()) { + die(__('You do not have access.', 'memberpress')); + } + + if (!isset($_POST['id']) || empty($_POST['id']) || !is_numeric($_POST['id'])) { + die(__('Could not cancel subscription', 'memberpress')); + } - wp_send_json(array('status' => 'success')); - } + $sub = new MeprSubscription($_POST['id']); - public function cancel_subscription() { - check_ajax_referer('cancel_subscription','mepr_subscriptions_nonce'); + try { + $sub->cancel(); + } catch (Exception $e) { + die($e->getMessage()); + } - if(!MeprUtils::is_mepr_admin()) { - die(__('You do not have access.', 'memberpress')); + die('true'); // don't localize this string } - if(!isset($_POST['id']) || empty($_POST['id']) || !is_numeric($_POST['id'])) { - die(__('Could not cancel subscription', 'memberpress')); + public function subscr_num_search() + { + if (!MeprUtils::is_mepr_admin()) { + die('-1'); + } + + // jQuery suggest plugin has already trimmed and escaped user input (\ becomes \\) + // so we just need to sanitize the username + $s = sanitize_user($_GET['q']); + + if (strlen($s) < 5) { + die(); + } // require 5 chars for matching + + $subs = MeprSubscription::search_by_subscr_id($s); + + MeprView::render('/admin/subscriptions/search', get_defined_vars()); + die(); } - $sub = new MeprSubscription($_POST['id']); + public function csv($lifetime = false) + { + check_ajax_referer('export_subscriptions', 'mepr_subscriptions_nonce'); + + $filename = ( $lifetime ? 'non-recurring-' : '' ) . 'subscriptions-' . time(); + + // Since we're running WP_List_Table headless we need to do this + $GLOBALS['hook_suffix'] = false; + + $screen = get_current_screen(); + $tab = new MeprSubscriptionsTable($screen, $this->get_columns(), $lifetime); + + if (isset($_REQUEST['all']) && !empty($_REQUEST['all'])) { + $search = isset($_REQUEST['search']) && !empty($_REQUEST['search']) ? esc_sql($_REQUEST['search']) : ''; + $search_field = isset($_REQUEST['search']) && !empty($_REQUEST['search-field']) ? esc_sql($_REQUEST['search-field']) : 'any'; + $search_field = isset($tab->db_search_cols[$search_field]) ? $tab->db_search_cols[$search_field] : 'any'; + + if ($lifetime) { + $all = MeprSubscription::lifetime_subscr_table( + /* $order_by */ 'created_at', + /* $order */ 'ASC', + /* $paged */ '', + /* $search */ $search, + /* $search_field */ $search_field, + /* $perpage */ '', + /* $countonly */ false, + /* $params */ $_REQUEST + ); + } else { + $all = MeprSubscription::subscr_table( + /* $order_by */ 'created_at', + /* $order */ 'ASC', + /* $paged */ '', + /* $search */ $search, + /* $search_field */ $search_field, + /* $perpage */ '', + /* $countonly */ false, + /* $params */ $_REQUEST + ); + } + + MeprUtils::render_csv($all['results'], $filename); + } else { + $tab->prepare_items(); + + MeprUtils::render_csv($tab->get_items(), $filename); + } + } - try { - $sub->cancel(); + public function lifetime_csv() + { + $this->csv(true); } - catch( Exception $e ) { - die($e->getMessage()); + + public function export_footer_link($action, $totalitems, $itemcount) + { + if ($action == 'mepr_subscriptions' || $action == 'mepr_lifetime_subscriptions') { + MeprAppHelper::export_table_link($action, 'export_subscriptions', 'mepr_subscriptions_nonce', $itemcount); + ?> | __('Id', 'memberpress'), + $prefix . 'subscr_id' => __('Subscription', 'memberpress'), + $prefix . 'active' => __('Active', 'memberpress'), + $prefix . 'status' => __('Auto Rebill', 'memberpress'), + $prefix . 'product' => __('Membership', 'memberpress'), + $prefix . 'product_meta' => __('Terms', 'memberpress'), + $prefix . 'propername' => __('Name', 'memberpress'), + $prefix . 'member' => __('User', 'memberpress'), + $prefix . 'gateway' => __('Gateway', 'memberpress'), + $prefix . 'txn_count' => __('Transaction', 'memberpress'), + $prefix . 'created_at' => __('Created On', 'memberpress'), + $prefix . 'expires_at' => __('Expires On', 'memberpress'), + ]; + + if ($lifetime) { + unset($cols[$prefix . 'status']); + unset($cols[$prefix . 'delete_sub']); + unset($cols[$prefix . 'txn_count']); + $cols[$prefix . 'subscr_id'] = __('Transaction', 'memberpress'); + $cols[$prefix . 'product_meta'] = __('Price', 'memberpress'); + } - public function subscr_num_search() { - if(!MeprUtils::is_mepr_admin()) { - die('-1'); + return MeprHooks::apply_filters('mepr-admin-subscriptions-cols', $cols, $prefix, $lifetime); } - // jQuery suggest plugin has already trimmed and escaped user input (\ becomes \\) - // so we just need to sanitize the username - $s = sanitize_user($_GET['q']); - - if(strlen($s) < 5) { die(); } // require 5 chars for matching - - $subs = MeprSubscription::search_by_subscr_id($s); - - MeprView::render('/admin/subscriptions/search', get_defined_vars()); - die(); - } - - public function csv($lifetime = false) { - check_ajax_referer('export_subscriptions', 'mepr_subscriptions_nonce'); - - $filename = ( $lifetime ? 'non-recurring-' : '' ) . 'subscriptions-'.time(); - - // Since we're running WP_List_Table headless we need to do this - $GLOBALS['hook_suffix'] = false; - - $screen = get_current_screen(); - $tab = new MeprSubscriptionsTable( $screen, $this->get_columns(), $lifetime ); - - if(isset($_REQUEST['all']) && !empty($_REQUEST['all'])) { - $search = isset($_REQUEST["search"]) && !empty($_REQUEST["search"]) ? esc_sql($_REQUEST["search"]) : ''; - $search_field = isset($_REQUEST["search"]) && !empty($_REQUEST["search-field"]) ? esc_sql($_REQUEST["search-field"]) : 'any'; - $search_field = isset($tab->db_search_cols[$search_field]) ? $tab->db_search_cols[$search_field] : 'any'; - - if($lifetime) { - $all = MeprSubscription::lifetime_subscr_table( - /* $order_by */ 'created_at', - /* $order */ 'ASC', - /* $paged */ '', - /* $search */ $search, - /* $search_field */ $search_field, - /* $perpage */ '', - /* $countonly */ false, - /* $params */ $_REQUEST - ); - } - else { - $all = MeprSubscription::subscr_table( - /* $order_by */ 'created_at', - /* $order */ 'ASC', - /* $paged */ '', - /* $search */ $search, - /* $search_field */ $search_field, - /* $perpage */ '', - /* $countonly */ false, - /* $params */ $_REQUEST - ); - } - - MeprUtils::render_csv($all['results'], $filename); + public function get_lifetime_columns() + { + return $this->get_columns(true); } - else { - $tab->prepare_items(); - MeprUtils::render_csv( $tab->get_items(), $filename ); + public function add_screen_options($optname = 'mp_subs_perpage') + { + add_screen_option('layout_columns'); + add_screen_option('per_page', [ + 'label' => __('Subscriptions', 'memberpress'), + 'default' => 10, + 'option' => $optname, + ]); } - } + public function add_recurring_screen_options() + { + $this->add_screen_options('mp_subs_perpage'); + } - public function lifetime_csv() { - $this->csv(true); - } + public function add_lifetime_screen_options() + { + $this->add_screen_options('mp_lifetime_subs_perpage'); + } - public function export_footer_link($action, $totalitems, $itemcount) { - if($action=='mepr_subscriptions' || $action=='mepr_lifetime_subscriptions') { - MeprAppHelper::export_table_link($action, 'export_subscriptions', 'mepr_subscriptions_nonce', $itemcount); - ?> | __('Id', 'memberpress'), - $prefix.'subscr_id' => __('Subscription', 'memberpress'), - $prefix.'active' => __('Active', 'memberpress'), - $prefix.'status' => __('Auto Rebill', 'memberpress'), - $prefix.'product' => __('Membership', 'memberpress'), - $prefix.'product_meta' => __('Terms', 'memberpress'), - $prefix.'propername' => __('Name', 'memberpress'), - $prefix.'member' => __('User', 'memberpress'), - $prefix.'gateway' => __('Gateway', 'memberpress'), - $prefix.'txn_count' => __('Transaction', 'memberpress'), - $prefix.'created_at' => __('Created On', 'memberpress'), - $prefix.'expires_at' => __('Expires On', 'memberpress') - ); - - if($lifetime) { - unset($cols[$prefix.'status']); - unset($cols[$prefix.'delete_sub']); - unset($cols[$prefix.'txn_count']); - $cols[$prefix.'subscr_id'] = __('Transaction', 'memberpress'); - $cols[$prefix.'product_meta'] = __('Price', 'memberpress'); + + public function setup_screen_options_subs($status, $option, $value) + { + if ('mp_subs_perpage' === $option) { + return $value; + } + return $status; } - return MeprHooks::apply_filters('mepr-admin-subscriptions-cols', $cols, $prefix, $lifetime); - } - - public function get_lifetime_columns() { - return $this->get_columns(true); - } - - public function add_screen_options($optname='mp_subs_perpage') { - add_screen_option( 'layout_columns' ); - add_screen_option( 'per_page', array( - 'label' => __('Subscriptions', 'memberpress'), - 'default' => 10, - 'option' => $optname - ) - ); - } - - public function add_recurring_screen_options() { - $this->add_screen_options('mp_subs_perpage'); - } - - public function add_lifetime_screen_options() { - $this->add_screen_options('mp_lifetime_subs_perpage'); - } - - public function setup_screen_options_lifetime($status, $option, $value) { - if ( 'mp_lifetime_subs_perpage' === $option ) { return $value; } - return $status; - } - - public function setup_screen_options_subs($status, $option, $value) { - if ( 'mp_subs_perpage' === $option ) { return $value; } - return $status; - } - - public function table_search_box() { - if(isset($_REQUEST['page']) && ($_REQUEST['page']=='memberpress-subscriptions' || $_REQUEST['page']=='memberpress-lifetimes')) { - $mepr_options = MeprOptions::fetch(); - - $membership = (isset($_REQUEST['membership'])?$_REQUEST['membership']:false); - $status = (isset($_REQUEST['status'])?$_REQUEST['status']:'all'); - $gateway = (isset($_REQUEST['gateway'])?$_REQUEST['gateway']:'all'); - - $args = array('orderby' => 'title', - 'order' => 'ASC' ); - $prds = MeprCptModel::all('MeprProduct', false, $args); - $gateways = $mepr_options->payment_methods(); - - MeprView::render('/admin/subscriptions/search_box', compact('membership','status','prds','gateways','gateway')); + public function table_search_box() + { + if (isset($_REQUEST['page']) && ($_REQUEST['page'] == 'memberpress-subscriptions' || $_REQUEST['page'] == 'memberpress-lifetimes')) { + $mepr_options = MeprOptions::fetch(); + + $membership = (isset($_REQUEST['membership']) ? $_REQUEST['membership'] : false); + $status = (isset($_REQUEST['status']) ? $_REQUEST['status'] : 'all'); + $gateway = (isset($_REQUEST['gateway']) ? $_REQUEST['gateway'] : 'all'); + + $args = [ + 'orderby' => 'title', + 'order' => 'ASC', + ]; + $prds = MeprCptModel::all('MeprProduct', false, $args); + $gateways = $mepr_options->payment_methods(); + + MeprView::render('/admin/subscriptions/search_box', compact('membership', 'status', 'prds', 'gateways', 'gateway')); + } } - } } //End class diff --git a/app/controllers/MeprSummaryEmailCtrl.php b/app/controllers/MeprSummaryEmailCtrl.php index 47cc17d..f27e355 100644 --- a/app/controllers/MeprSummaryEmailCtrl.php +++ b/app/controllers/MeprSummaryEmailCtrl.php @@ -1,199 +1,207 @@ getTimestamp(), 'mepr_summary_email_interval', 'mepr_summary_email'); - } catch (Exception $e) { - // Fail silently for now - } - } - } - - /** - * Add the weekly schedule to WP Cron - * - * @param array $schedules - * @return array - */ - public function add_cron_schedule($schedules) { - $schedules['mepr_summary_email_interval'] = array( - 'interval' => 604800, // weekly - 'display' => __('MemberPress Summary Email', 'memberpress') - ); - - return $schedules; - } - - /** - * Send the summary report email - */ - public function send_summary_email() { - $mepr_options = MeprOptions::fetch(); - - if($mepr_options->disable_summary_email) { - return; - } - try { - $utc = new DateTimeZone('UTC'); - $tomorrow = new DateTimeImmutable('tomorrow', $utc); - $report_date = $tomorrow->modify('last monday 00:00:00'); - $last_week_end = $report_date->modify('-1 day'); - $last_week_start = $report_date->modify('-7 days'); - - $last_week = $previous_week = array( - 'failed' => 0, - 'pending' => 0, - 'refunded' => 0, - 'completed' => 0, - 'revenue' => 0, - 'refunds' => 0, - 'recurring' => 0, - 'new' => 0 - ); - - for ($i = 0; $i < 7; $i++) { - $ts = $last_week_start->getTimestamp() + MeprUtils::days($i); - $month = gmdate('n', $ts); - $day = gmdate('j', $ts); - $year = gmdate('Y', $ts); - $revenue = MeprReports::get_revenue($month, $day, $year); - $recurring = MeprReports::get_recurring_revenue($month, $day, $year); - - $last_week['pending'] += MeprReports::get_transactions_count(MeprTransaction::$pending_str, $day, $month, $year); - $last_week['failed'] += MeprReports::get_transactions_count(MeprTransaction::$failed_str, $day, $month, $year); - $last_week['refunded'] += MeprReports::get_transactions_count(MeprTransaction::$refunded_str, $day, $month, $year); - $last_week['completed'] += MeprReports::get_transactions_count(MeprTransaction::$complete_str, $day, $month, $year); - - $last_week['revenue'] += $revenue; - $last_week['refunds'] += MeprReports::get_refunds($month, $day, $year); - $last_week['recurring'] += $recurring; - $last_week['new'] += $revenue - $recurring; - - $ts = $last_week_start->getTimestamp() - MeprUtils::days($i); - $month = gmdate('n', $ts); - $day = gmdate('j', $ts); - $year = gmdate('Y', $ts); - $revenue = MeprReports::get_revenue($month, $day, $year); - $recurring = MeprReports::get_recurring_revenue($month, $day, $year); - - $previous_week['pending'] += MeprReports::get_transactions_count(MeprTransaction::$pending_str, $day, $month, $year); - $previous_week['failed'] += MeprReports::get_transactions_count(MeprTransaction::$failed_str, $day, $month, $year); - $previous_week['refunded'] += MeprReports::get_transactions_count(MeprTransaction::$refunded_str, $day, $month, $year); - $previous_week['completed'] += MeprReports::get_transactions_count(MeprTransaction::$complete_str, $day, $month, $year); - - $previous_week['revenue'] += $revenue; - $previous_week['refunds'] += MeprReports::get_refunds($month, $day, $year); - $previous_week['recurring'] += $recurring; - $previous_week['new'] += $revenue - $recurring; - } - - if($last_week['revenue'] + $previous_week['revenue'] <= 0.00) { - // Do not send the email if there has been no revenue for the last two weeks - return; - } - - $subject = sprintf( - __('[MemberPress] Your summary report for the week of %s', 'memberpress'), - MeprUtils::date('F j', $last_week_start, $utc) - ); - - $site = MeprUtils::blogname(); - $site = empty($site) ? get_bloginfo('url') : $site; - $ad = $this->get_ad(); - - $message = MeprView::get_string('/admin/reports/summary_email', get_defined_vars()); - - $headers = array( - sprintf('Content-type: text/html; charset=%s', apply_filters('wp_mail_charset', get_bloginfo('charset'))) - ); - - MeprUtils::wp_mail_to_admin($subject, $message, $headers); - } catch (Exception $e) { - // Fail silently for now - } - } - - /** - * Get a random ad or educational tip to display in the email - * - * @return string - */ - private function get_ad() { - $url = add_query_arg(array( - 'ad-group' => apply_filters('mepr_summary_email_ad_group', 3), - 'orderby' => 'rand', - ), 'https://sg-assets.caseproof.com/wp-json/wp/v2/ads'); - - $response = wp_remote_get($url); - $code = wp_remote_retrieve_response_code($response); - $body = wp_remote_retrieve_body($response); - - if($code == 200 && $body) { - $ads = json_decode($body, true); - - if(is_array($ads) && isset($ads[0]['rendered_ad'])) { - return $ads[0]['rendered_ad']; - } +if (!defined('ABSPATH')) { + die('You are not allowed to call this page directly.'); +} + +class MeprSummaryEmailCtrl extends MeprBaseCtrl +{ + /** + * Load the hooks for this controller + */ + public function load_hooks() + { + add_filter('cron_schedules', [$this, 'add_cron_schedule']); + add_action('mepr_summary_email', [$this, 'send_summary_email']); + + if (!wp_next_scheduled('mepr_summary_email')) { + try { + $date = new DateTime('next monday 00:05:00', new DateTimeZone('UTC')); + wp_schedule_event($date->getTimestamp(), 'mepr_summary_email_interval', 'mepr_summary_email'); + } catch (Exception $e) { + // Fail silently for now + } + } } - return ''; - } - - /** - * Get the HTML representing the percentage difference between two values - * - * @param int|float $new_value The new value - * @param int|float $previous_value The previous value - * @param bool $positive_is_favorable Whether a positive change is favorable - * @return string - */ - public static function get_change_percent($new_value, $previous_value, $positive_is_favorable = true) { - if($new_value == $previous_value) { - $change = 0; - } elseif($new_value == 0) { - $change = -100; - } elseif($previous_value == 0) { - $change = 100; - } else { - $change = (($new_value - $previous_value) / $previous_value) * 100; + /** + * Add the weekly schedule to WP Cron + * + * @param array $schedules + * @return array + */ + public function add_cron_schedule($schedules) + { + $schedules['mepr_summary_email_interval'] = [ + 'interval' => 604800, // weekly + 'display' => __('MemberPress Summary Email', 'memberpress'), + ]; + + return $schedules; } - if($change == 0) { - $color = '#757575'; - $image = ''; - } elseif ($change < 0 && $positive_is_favorable) { - $color = '#db4437'; - $image = '/down-arrow-red.png'; - } elseif ($change > 0 && !$positive_is_favorable) { - $color = '#db4437'; - $image = '/up-arrow-red.png'; - } elseif ($change < 0) { - $color = '#0f9d58'; - $image = '/down-arrow-green.png'; - } else { - $color = '#0f9d58'; - $image = '/up-arrow-green.png'; + /** + * Send the summary report email + */ + public function send_summary_email() + { + $mepr_options = MeprOptions::fetch(); + + if ($mepr_options->disable_summary_email) { + return; + } + + try { + $utc = new DateTimeZone('UTC'); + $tomorrow = new DateTimeImmutable('tomorrow', $utc); + $report_date = $tomorrow->modify('last monday 00:00:00'); + $last_week_end = $report_date->modify('-1 day'); + $last_week_start = $report_date->modify('-7 days'); + + $last_week = $previous_week = [ + 'failed' => 0, + 'pending' => 0, + 'refunded' => 0, + 'completed' => 0, + 'revenue' => 0, + 'refunds' => 0, + 'recurring' => 0, + 'new' => 0, + ]; + + for ($i = 0; $i < 7; $i++) { + $ts = $last_week_start->getTimestamp() + MeprUtils::days($i); + $month = gmdate('n', $ts); + $day = gmdate('j', $ts); + $year = gmdate('Y', $ts); + $revenue = MeprReports::get_revenue($month, $day, $year); + $recurring = MeprReports::get_recurring_revenue($month, $day, $year); + + $last_week['pending'] += MeprReports::get_transactions_count(MeprTransaction::$pending_str, $day, $month, $year); + $last_week['failed'] += MeprReports::get_transactions_count(MeprTransaction::$failed_str, $day, $month, $year); + $last_week['refunded'] += MeprReports::get_transactions_count(MeprTransaction::$refunded_str, $day, $month, $year); + $last_week['completed'] += MeprReports::get_transactions_count(MeprTransaction::$complete_str, $day, $month, $year); + + $last_week['revenue'] += $revenue; + $last_week['refunds'] += MeprReports::get_refunds($month, $day, $year); + $last_week['recurring'] += $recurring; + $last_week['new'] += $revenue - $recurring; + + $ts = $last_week_start->getTimestamp() - MeprUtils::days($i); + $month = gmdate('n', $ts); + $day = gmdate('j', $ts); + $year = gmdate('Y', $ts); + $revenue = MeprReports::get_revenue($month, $day, $year); + $recurring = MeprReports::get_recurring_revenue($month, $day, $year); + + $previous_week['pending'] += MeprReports::get_transactions_count(MeprTransaction::$pending_str, $day, $month, $year); + $previous_week['failed'] += MeprReports::get_transactions_count(MeprTransaction::$failed_str, $day, $month, $year); + $previous_week['refunded'] += MeprReports::get_transactions_count(MeprTransaction::$refunded_str, $day, $month, $year); + $previous_week['completed'] += MeprReports::get_transactions_count(MeprTransaction::$complete_str, $day, $month, $year); + + $previous_week['revenue'] += $revenue; + $previous_week['refunds'] += MeprReports::get_refunds($month, $day, $year); + $previous_week['recurring'] += $recurring; + $previous_week['new'] += $revenue - $recurring; + } + + if ($last_week['revenue'] + $previous_week['revenue'] <= 0.00) { + // Do not send the email if there has been no revenue for the last two weeks + return; + } + + $subject = sprintf( + __('[MemberPress] Your summary report for the week of %s', 'memberpress'), + MeprUtils::date('F j', $last_week_start, $utc) + ); + + $site = MeprUtils::blogname(); + $site = empty($site) ? get_bloginfo('url') : $site; + $ad = $this->get_ad(); + + $message = MeprView::get_string('/admin/reports/summary_email', get_defined_vars()); + + $headers = [ + sprintf('Content-type: text/html; charset=%s', apply_filters('wp_mail_charset', get_bloginfo('charset'))), + ]; + + MeprUtils::wp_mail_to_admin($subject, $message, $headers); + } catch (Exception $e) { + // Fail silently for now + } } - $output = sprintf( - '
    %s%s
    ', - $color, - $image ? '' : '', - $change == 0 ? ' ' : '' . number_format_i18n(abs($change), 0) . '%' - ); + /** + * Get a random ad or educational tip to display in the email + * + * @return string + */ + private function get_ad() + { + $url = add_query_arg([ + 'ad-group' => apply_filters('mepr_summary_email_ad_group', 3), + 'orderby' => 'rand', + ], 'https://sg-assets.caseproof.com/wp-json/wp/v2/ads'); + + $response = wp_remote_get($url); + $code = wp_remote_retrieve_response_code($response); + $body = wp_remote_retrieve_body($response); + + if ($code == 200 && $body) { + $ads = json_decode($body, true); + + if (is_array($ads) && isset($ads[0]['rendered_ad'])) { + return $ads[0]['rendered_ad']; + } + } + + return ''; + } - return $output; - } + /** + * Get the HTML representing the percentage difference between two values + * + * @param integer|float $new_value The new value + * @param integer|float $previous_value The previous value + * @param boolean $positive_is_favorable Whether a positive change is favorable + * @return string + */ + public static function get_change_percent($new_value, $previous_value, $positive_is_favorable = true) + { + if ($new_value == $previous_value) { + $change = 0; + } elseif ($new_value == 0) { + $change = -100; + } elseif ($previous_value == 0) { + $change = 100; + } else { + $change = (($new_value - $previous_value) / $previous_value) * 100; + } + + if ($change == 0) { + $color = '#757575'; + $image = ''; + } elseif ($change < 0 && $positive_is_favorable) { + $color = '#db4437'; + $image = '/down-arrow-red.png'; + } elseif ($change > 0 && !$positive_is_favorable) { + $color = '#db4437'; + $image = '/up-arrow-red.png'; + } elseif ($change < 0) { + $color = '#0f9d58'; + $image = '/down-arrow-green.png'; + } else { + $color = '#0f9d58'; + $image = '/up-arrow-green.png'; + } + + $output = sprintf( + '
    %s%s
    ', + $color, + $image ? '' : '', + $change == 0 ? ' ' : '' . number_format_i18n(abs($change), 0) . '%' + ); + + return $output; + } } diff --git a/app/controllers/MeprTaxesCtrl.php b/app/controllers/MeprTaxesCtrl.php index ca9df1a..7d1bb7c 100644 --- a/app/controllers/MeprTaxesCtrl.php +++ b/app/controllers/MeprTaxesCtrl.php @@ -1,130 +1,139 @@ - - 'tax_country', - 'Country Code' => 'tax_country', - 'state code' => 'tax_state', - 'State Code' => 'tax_state', - 'postcodes' => 'postcodes', - 'Postcodes' => 'postcodes', - 'postcode' => 'postcodes', - 'Postcode' => 'postcodes', - 'zip/postcodes' => 'postcodes', - 'ZIP/Postcodes' => 'postcodes', - 'zip/postcode' => 'postcodes', - 'ZIP/Postcode' => 'postcodes', - 'city' => 'cities', - 'City' => 'cities', - 'rate' => 'tax_rate', - 'Rate' => 'tax_rate', - 'Rate %' => 'tax_rate', - 'tax name' => 'tax_desc', - 'Tax Name' => 'tax_desc', - 'Tax Description' => 'tax_desc', - 'tax description' => 'tax_desc', - 'priority' => 'tax_priority', - 'Priority' => 'tax_priority', - 'compound' => 'tax_compound', - 'Compound' => 'tax_compound', - 'shipping' => 'tax_shipping', - 'Shipping' => 'tax_shipping', - 'tax class' => 'tax_class', - 'Tax Class' => 'tax_class' - ); - - $validations = array( - 'required' => array( - 'tax_country', - 'tax_state', - 'tax_rate', - 'tax_desc', - 'postcodes', - 'cities' - ), - 'not_empty' => array('tax_rate'), - 'number' => array('tax_rate'), - //'tax_priority' => array('required'), - //'tax_compound' => array('required'), - //'tax_shipping' => array('required'), - //'tax_class' => array('required') - ); - - $tax_rates = MeprUtils::parse_csv_file( $_FILES['mepr_tax_rates_csv']['tmp_name'], $validations, $mapping ); - MeprTaxRate::import($tax_rates); + public function display_option_tab() + { + ?> + + 'tax_country', + 'Country Code' => 'tax_country', + 'state code' => 'tax_state', + 'State Code' => 'tax_state', + 'postcodes' => 'postcodes', + 'Postcodes' => 'postcodes', + 'postcode' => 'postcodes', + 'Postcode' => 'postcodes', + 'zip/postcodes' => 'postcodes', + 'ZIP/Postcodes' => 'postcodes', + 'zip/postcode' => 'postcodes', + 'ZIP/Postcode' => 'postcodes', + 'city' => 'cities', + 'City' => 'cities', + 'rate' => 'tax_rate', + 'Rate' => 'tax_rate', + 'Rate %' => 'tax_rate', + 'tax name' => 'tax_desc', + 'Tax Name' => 'tax_desc', + 'Tax Description' => 'tax_desc', + 'tax description' => 'tax_desc', + 'priority' => 'tax_priority', + 'Priority' => 'tax_priority', + 'compound' => 'tax_compound', + 'Compound' => 'tax_compound', + 'shipping' => 'tax_shipping', + 'Shipping' => 'tax_shipping', + 'tax class' => 'tax_class', + 'Tax Class' => 'tax_class', + ]; + + $validations = [ + 'required' => [ + 'tax_country', + 'tax_state', + 'tax_rate', + 'tax_desc', + 'postcodes', + 'cities', + ], + 'not_empty' => ['tax_rate'], + 'number' => ['tax_rate'], + // 'tax_priority' => array('required'), + // 'tax_compound' => array('required'), + // 'tax_shipping' => array('required'), + // 'tax_class' => array('required') + ]; + + $tax_rates = MeprUtils::parse_csv_file($_FILES['mepr_tax_rates_csv']['tmp_name'], $validations, $mapping); + MeprTaxRate::import($tax_rates); + } } - exit; - } + public function export_tax_rates() + { + check_ajax_referer('export_tax_rates', 'mepr_taxes_nonce'); - public function remove_tax_rate() { - check_ajax_referer('mepr_taxes', 'tax_nonce'); + $tax_rates = MeprTaxRate::get_all(';', ARRAY_A); - if(!MeprUtils::is_mepr_admin()) { - header('HTTP/1.0 403 Forbidden'); - exit; - } + if (!empty($tax_rates) && is_array($tax_rates)) { + $header = array_keys($tax_rates[0]); + $filename = time() . '_tax_rates.csv'; + MeprAppHelper::render_csv($tax_rates, $header, $filename); + } else { + header('HTTP/1.0 403 Forbidden'); + } - if(!isset($_POST['id']) || !($tax_rate = new MeprTaxRate($_POST['id'])) || empty($tax_rate->id)) { - header('HTTP/1.0 404 Not Found'); - exit(json_encode(array('error'=>__('A valid tax rate id must be set', 'memberpress')))); + exit; } - $tax_rate->destroy(); + public function remove_tax_rate() + { + check_ajax_referer('mepr_taxes', 'tax_nonce'); + + if (!MeprUtils::is_mepr_admin()) { + header('HTTP/1.0 403 Forbidden'); + exit; + } - exit(json_encode(array('message'=>__('This tax rate was successfully deleted', 'memberpress')))); - } + if (!isset($_POST['id']) || !($tax_rate = new MeprTaxRate($_POST['id'])) || empty($tax_rate->id)) { + header('HTTP/1.0 404 Not Found'); + exit(json_encode(['error' => __('A valid tax rate id must be set', 'memberpress')])); + } + $tax_rate->destroy(); + + exit(json_encode(['message' => __('This tax rate was successfully deleted', 'memberpress')])); + } } //End class diff --git a/app/controllers/MeprTransactionsCtrl.php b/app/controllers/MeprTransactionsCtrl.php index db641bf..c294d93 100644 --- a/app/controllers/MeprTransactionsCtrl.php +++ b/app/controllers/MeprTransactionsCtrl.php @@ -1,680 +1,697 @@ MeprUtils::hours(1), // Every hour - 'display' => __('MemberPress Send Transaction Expire Events', 'memberpress') - ); + add_action('mepr_table_controls_search', [$this, 'table_search_box']); - return $schedules; - } + // Capture complete and refunded events for offline gateway + add_action('mepr-txn-store', 'MeprArtificialGateway::capture_txn_status_for_events'); + } - public static function unschedule_events() { - $timestamp = wp_next_scheduled( 'mepr_send_txn_expire_events' ); - wp_unschedule_event( $timestamp, 'mepr_send_txn_expire_events' ); - } + public function intervals($schedules) + { + $schedules['mepr_send_txn_expire_events_interval'] = [ + 'interval' => MeprUtils::hours(1), // Every hour + 'display' => __('MemberPress Send Transaction Expire Events', 'memberpress'), + ]; - public function listing() { - $action = (isset($_REQUEST['action']) && !empty($_REQUEST['action']))?$_REQUEST['action']:false; - if($action == 'new') { - $this->new_trans(); - } - else if($action == 'edit') { - $this->edit_trans(); + return $schedules; } - else { - $this->display_list(); - } - } - public function new_trans($errors = array()) { - $mepr_options = MeprOptions::fetch(); - $txn = new MeprTransaction(); - $user_login = $subscr_num = ''; + public static function unschedule_events() + { + $timestamp = wp_next_scheduled('mepr_send_txn_expire_events'); + wp_unschedule_event($timestamp, 'mepr_send_txn_expire_events'); + } - if(empty($errors) && MeprUtils::is_post_request()) { - $this->create_trans($txn); + public function listing() + { + $action = (isset($_REQUEST['action']) && !empty($_REQUEST['action'])) ? $_REQUEST['action'] : false; + if ($action == 'new') { + $this->new_trans(); + } elseif ($action == 'edit') { + $this->edit_trans(); + } else { + $this->display_list(); + } } - else { - if(isset($_REQUEST['user']) && !empty($_REQUEST['user'])) { - $user_login = stripslashes($_REQUEST['user']); - } - - if(isset($_REQUEST['subscription']) && is_numeric($_REQUEST['subscription'])) { - $sub = new MeprSubscription($_REQUEST['subscription']); - $usr = $sub->user(); - $prd = $sub->product(); - $user_login = $usr->user_login; - $subscr_num = $sub->subscr_id; - $txn->product_id = $sub->product_id; - } - MeprView::render('/admin/transactions/new_trans', get_defined_vars()); + public function new_trans($errors = []) + { + $mepr_options = MeprOptions::fetch(); + $txn = new MeprTransaction(); + $user_login = $subscr_num = ''; + + if (empty($errors) && MeprUtils::is_post_request()) { + $this->create_trans($txn); + } else { + if (isset($_REQUEST['user']) && !empty($_REQUEST['user'])) { + $user_login = stripslashes($_REQUEST['user']); + } + + if (isset($_REQUEST['subscription']) && is_numeric($_REQUEST['subscription'])) { + $sub = new MeprSubscription($_REQUEST['subscription']); + $usr = $sub->user(); + $prd = $sub->product(); + $user_login = $usr->user_login; + $subscr_num = $sub->subscr_id; + $txn->product_id = $sub->product_id; + } + + MeprView::render('/admin/transactions/new_trans', get_defined_vars()); + } } - } - public function edit_trans() { - $mepr_options = MeprOptions::fetch(); - $subscr_num = ''; + public function edit_trans() + { + $mepr_options = MeprOptions::fetch(); + $subscr_num = ''; - if(isset($_REQUEST['id'])) { - $txn = new MeprTransaction($_REQUEST['id']); - $usr = $txn->user(); - $user_login = $usr->user_login; + if (isset($_REQUEST['id'])) { + $txn = new MeprTransaction($_REQUEST['id']); + $usr = $txn->user(); + $user_login = $usr->user_login; - if($sub = $txn->subscription()) { - $subscr_num = $sub->subscr_id; - } + if ($sub = $txn->subscription()) { + $subscr_num = $sub->subscr_id; + } - if(MeprUtils::is_post_request()) { - $this->update_trans($txn); - } - else { - MeprView::render('/admin/transactions/edit_trans', get_defined_vars()); - } - } - else { - $this->new_trans(); - } - } - - /** - * @param MeprTransaction $txn - */ - public function create_trans($txn) { - check_admin_referer( 'mepr_create_or_update_transaction', 'mepr_transactions_nonce' ); - - $mepr_options = MeprOptions::fetch(); - - $errors = $this->validate_trans(); - - $usr = new MeprUser(); - $usr->load_user_data_by_login($_POST['user_login']); - $user_login = $usr->user_login; - $subscr_num = ''; - - $txn->trans_num = (isset($_POST['trans_num']) && !empty($_POST['trans_num']))?sanitize_file_name(wp_unslash($_POST['trans_num'])):uniqid(); - $txn->user_id = $usr->ID; - $txn->product_id = sanitize_key($_POST['product_id']); - // $txn->set_subtotal($_POST['amount']); //Don't do this, it doesn't work right on existing txns - $txn->amount = MeprUtils::format_currency_us_float( $_POST['amount'] ); - $txn->tax_amount = MeprUtils::format_currency_us_float($_POST['tax_amount']); - $txn->total = $txn->amount + $txn->tax_amount; - $txn->tax_rate = MeprUtils::format_currency_us_float($_POST['tax_rate']); - $txn->status = sanitize_text_field($_POST['status']); - $txn->gateway = sanitize_text_field($_POST['gateway']); - - if(isset($_POST['subscr_num']) && !empty($_POST['subscr_num'])) { - if($sub = MeprSubscription::get_one_by_subscr_id($_POST['subscr_num'])) { - $txn->subscription_id = $sub->id; - $subscr_num = $sub->subscr_id; - $sub->store(); - } + if (MeprUtils::is_post_request()) { + $this->update_trans($txn); + } else { + MeprView::render('/admin/transactions/edit_trans', get_defined_vars()); + } + } else { + $this->new_trans(); + } } - if(isset($_POST['created_at']) && ($_POST['created_at'] == '' || is_null($_POST['created_at']))) { - $txn->created_at = MeprUtils::ts_to_mysql_date(time()); // This crap is due to mysql craziness - } - else { - $txn->created_at = MeprUtils::ts_to_mysql_date(strtotime($_POST['created_at'])); - } + /** + * @param MeprTransaction $txn + */ + public function create_trans($txn) + { + check_admin_referer('mepr_create_or_update_transaction', 'mepr_transactions_nonce'); - if(isset($_POST['expires_at']) && ($_POST['expires_at'] == '' || is_null($_POST['expires_at']))) { - $txn->expires_at = MeprUtils::db_lifetime(); // This crap is due to mysql craziness - } - else { - $txn->expires_at = MeprUtils::ts_to_mysql_date(strtotime($_POST['expires_at']), 'Y-m-d 23:59:59'); - } + $mepr_options = MeprOptions::fetch(); - // Only save to the database if there aren't any errors - if(empty($errors)) { - $txn->store(); + $errors = $this->validate_trans(); - if($txn->status==MeprTransaction::$complete_str) { - MeprEvent::record('transaction-completed', $txn); + $usr = new MeprUser(); + $usr->load_user_data_by_login($_POST['user_login']); + $user_login = $usr->user_login; + $subscr_num = ''; + + $txn->trans_num = (isset($_POST['trans_num']) && !empty($_POST['trans_num'])) ? sanitize_file_name(wp_unslash($_POST['trans_num'])) : uniqid(); + $txn->user_id = $usr->ID; + $txn->product_id = sanitize_key($_POST['product_id']); + // $txn->set_subtotal($_POST['amount']); //Don't do this, it doesn't work right on existing txns + $txn->amount = MeprUtils::format_currency_us_float($_POST['amount']); + $txn->tax_amount = MeprUtils::format_currency_us_float($_POST['tax_amount']); + $txn->total = $txn->amount + $txn->tax_amount; + $txn->tax_rate = MeprUtils::format_currency_us_float($_POST['tax_rate']); + $txn->status = sanitize_text_field($_POST['status']); + $txn->gateway = sanitize_text_field($_POST['gateway']); + + if (isset($_POST['subscr_num']) && !empty($_POST['subscr_num'])) { + if ($sub = MeprSubscription::get_one_by_subscr_id($_POST['subscr_num'])) { + $txn->subscription_id = $sub->id; + $subscr_num = $sub->subscr_id; + $sub->store(); + } + } - // This is a recurring payment - if(($sub = $txn->subscription()) && $sub->txn_count > 1) { - MeprEvent::record('recurring-transaction-completed', $txn); + if (isset($_POST['created_at']) && ($_POST['created_at'] == '' || is_null($_POST['created_at']))) { + $txn->created_at = MeprUtils::ts_to_mysql_date(time()); // This crap is due to mysql craziness + } else { + $txn->created_at = MeprUtils::ts_to_mysql_date(strtotime($_POST['created_at'])); } - elseif(!$sub) { - MeprEvent::record('non-recurring-transaction-completed', $txn); + + if (isset($_POST['expires_at']) && ($_POST['expires_at'] == '' || is_null($_POST['expires_at']))) { + $txn->expires_at = MeprUtils::db_lifetime(); // This crap is due to mysql craziness + } else { + $txn->expires_at = MeprUtils::ts_to_mysql_date(strtotime($_POST['expires_at']), 'Y-m-d 23:59:59'); } - } - if ( empty( $txn->subscription_id ) ) { - // If the transaction is not part of a subscription (stand-alone transaction) - MeprHooks::do_action( 'mepr-signup', $txn ); - } else if ( - isset( $sub ) && - count( $sub->transactions() ) <= 1 && - $txn->status == MeprTransaction::$complete_str - ) { - // If the transaction is the only completed transaction of a recurring subscription - MeprHooks::do_action( 'mepr-signup', $txn ); - } + // Only save to the database if there aren't any errors + if (empty($errors)) { + $txn->store(); + + if ($txn->status == MeprTransaction::$complete_str) { + MeprEvent::record('transaction-completed', $txn); + + // This is a recurring payment + if (($sub = $txn->subscription()) && $sub->txn_count > 1) { + MeprEvent::record('recurring-transaction-completed', $txn); + } elseif (!$sub) { + MeprEvent::record('non-recurring-transaction-completed', $txn); + } + } + + if (empty($txn->subscription_id)) { + // If the transaction is not part of a subscription (stand-alone transaction) + MeprHooks::do_action('mepr-signup', $txn); + } elseif ( + isset($sub) && + count($sub->transactions()) <= 1 && + $txn->status == MeprTransaction::$complete_str + ) { + // If the transaction is the only completed transaction of a recurring subscription + MeprHooks::do_action('mepr-signup', $txn); + } + + $message = __('A transaction was created successfully.', 'memberpress'); + + $_REQUEST['action'] = 'edit'; + $txn = new MeprTransaction($txn->id); // refresh the txn obj to get all generated fields + MeprView::render('/admin/transactions/edit_trans', get_defined_vars()); + } else { + $this->new_trans($errors); + } + } - $message = __('A transaction was created successfully.', 'memberpress'); + public function update_trans($txn) + { + check_admin_referer('mepr_create_or_update_transaction', 'mepr_transactions_nonce'); - $_REQUEST['action'] = 'edit'; - $txn = new MeprTransaction($txn->id); // refresh the txn obj to get all generated fields - MeprView::render('/admin/transactions/edit_trans', get_defined_vars()); - } - else { - $this->new_trans($errors); - } - } - - public function update_trans($txn) { - check_admin_referer( 'mepr_create_or_update_transaction', 'mepr_transactions_nonce' ); - - $mepr_options = MeprOptions::fetch(); - - $errors = $this->validate_trans(); - - $usr = new MeprUser(); - $usr->load_user_data_by_login($_POST['user_login']); - $user_login = $usr->user_login; - $subscr_num = ''; - $txn->trans_num = sanitize_file_name(wp_unslash($_POST['trans_num'])); - $txn->user_id = $usr->ID; - $txn->product_id = sanitize_key($_POST['product_id']); - // $txn->set_subtotal($_POST['amount']); //Don't do this, it doesn't work right on existing txns - $txn->amount = MeprUtils::format_currency_us_float( $_POST['amount'] ); - $txn->tax_amount = MeprUtils::format_currency_us_float($_POST['tax_amount']); - $txn->total = $txn->amount + $txn->tax_amount; - $txn->tax_rate = MeprUtils::format_currency_us_float($_POST['tax_rate']); - $txn->status = sanitize_text_field($_POST['status']); - $txn->gateway = sanitize_text_field($_POST['gateway']); - - if(isset($_POST['subscr_num']) && !empty($_POST['subscr_num'])) { - if($sub = MeprSubscription::get_one_by_subscr_id($_POST['subscr_num'])) { - $txn->subscription_id = $sub->id; - $subscr_num = $sub->subscr_id; - $sub->store(); - } - } + $mepr_options = MeprOptions::fetch(); - if(isset($_POST['created_at']) && ($_POST['created_at'] == '' || is_null($_POST['created_at']))) { - $txn->created_at = MeprUtils::ts_to_mysql_date(time()); // This crap is due to mysql craziness - } - else { - $txn->created_at = MeprUtils::ts_to_mysql_date(strtotime($_POST['created_at'])); - } + $errors = $this->validate_trans(); - if(isset($_POST['expires_at']) && ($_POST['expires_at'] == '' || is_null($_POST['expires_at']))) { - $txn->expires_at = MeprUtils::db_lifetime(); // This crap is due to mysql craziness - } - else { - $txn->expires_at = MeprUtils::ts_to_mysql_date(strtotime($_POST['expires_at'])); - } + $usr = new MeprUser(); + $usr->load_user_data_by_login($_POST['user_login']); + $user_login = $usr->user_login; + $subscr_num = ''; + $txn->trans_num = sanitize_file_name(wp_unslash($_POST['trans_num'])); + $txn->user_id = $usr->ID; + $txn->product_id = sanitize_key($_POST['product_id']); + // $txn->set_subtotal($_POST['amount']); //Don't do this, it doesn't work right on existing txns + $txn->amount = MeprUtils::format_currency_us_float($_POST['amount']); + $txn->tax_amount = MeprUtils::format_currency_us_float($_POST['tax_amount']); + $txn->total = $txn->amount + $txn->tax_amount; + $txn->tax_rate = MeprUtils::format_currency_us_float($_POST['tax_rate']); + $txn->status = sanitize_text_field($_POST['status']); + $txn->gateway = sanitize_text_field($_POST['gateway']); + + if (isset($_POST['subscr_num']) && !empty($_POST['subscr_num'])) { + if ($sub = MeprSubscription::get_one_by_subscr_id($_POST['subscr_num'])) { + $txn->subscription_id = $sub->id; + $subscr_num = $sub->subscr_id; + $sub->store(); + } + } - // Only save to the database if there aren't any errors - if(empty($errors)) { - $txn->store(); - $message = __("The transaction was successfully updated.", 'memberpress'); - } + if (isset($_POST['created_at']) && ($_POST['created_at'] == '' || is_null($_POST['created_at']))) { + $txn->created_at = MeprUtils::ts_to_mysql_date(time()); // This crap is due to mysql craziness + } else { + $txn->created_at = MeprUtils::ts_to_mysql_date(strtotime($_POST['created_at'])); + } - MeprView::render('/admin/transactions/edit_trans', get_defined_vars()); - } + if (isset($_POST['expires_at']) && ($_POST['expires_at'] == '' || is_null($_POST['expires_at']))) { + $txn->expires_at = MeprUtils::db_lifetime(); // This crap is due to mysql craziness + } else { + $txn->expires_at = MeprUtils::ts_to_mysql_date(strtotime($_POST['expires_at'])); + } - public function validate_trans() { - $errors = array(); - $usr = new MeprUser(); + // Only save to the database if there aren't any errors + if (empty($errors)) { + $txn->store(); + $message = __('The transaction was successfully updated.', 'memberpress'); + } - if(!isset($_POST['user_login']) || empty($_POST['user_login'])) { - $errors[] = __("The username must be set.", 'memberpress'); - } - elseif(is_email($_POST['user_login']) && !username_exists($_POST['user_login'])) { - $usr->load_user_data_by_email($_POST['user_login']); - - if(!$usr->ID) { - $errors[] = __("You must enter a valid username or email address", 'memberpress'); - } - else { //For use downstream in create and update transaction methods - $_POST['user_login'] = $usr->user_login; - } + MeprView::render('/admin/transactions/edit_trans', get_defined_vars()); } - else { - $usr->load_user_data_by_login($_POST['user_login']); - if(!$usr->ID) { - $errors[] = __("You must enter a valid username or email address", 'memberpress'); - } - } + public function validate_trans() + { + $errors = []; + $usr = new MeprUser(); - // Simple validation here - if(!isset($_POST['amount']) || empty($_POST['amount'])) { - $errors[] = __("The amount must be set.", 'memberpress'); - } + if (!isset($_POST['user_login']) || empty($_POST['user_login'])) { + $errors[] = __('The username must be set.', 'memberpress'); + } elseif (is_email($_POST['user_login']) && !username_exists($_POST['user_login'])) { + $usr->load_user_data_by_email($_POST['user_login']); - if( preg_match("/[^0-9., ]/", $_POST['amount']) ) { - $errors[] = __("The amount must be a number.", 'memberpress'); - } + if (!$usr->ID) { + $errors[] = __('You must enter a valid username or email address', 'memberpress'); + } else { // For use downstream in create and update transaction methods + $_POST['user_login'] = $usr->user_login; + } + } else { + $usr->load_user_data_by_login($_POST['user_login']); - if(isset($_POST['subscr_num']) && !empty($_POST['subscr_num'])) { - if($sub = MeprSubscription::get_one_by_subscr_id($_POST['subscr_num'])) { - if($sub->product_id != $_POST['product_id']) { - $prd = new MeprProduct($_POST['product_id']); - $sub_prd = $sub->product(); - $errors[] = sprintf( __( "This is not a subscription for membership '%s' but for '%s'" , 'memberpress'), $prd->post_title, $sub_prd->post_title ); - } - - /* - ** $usr object is already set above - ** $usr = new MeprUser(); - ** $usr->load_user_data_by_login($_POST['user_login']); - */ - $sub_usr = $sub->user(); - - if($usr->ID != $sub_usr->ID) { - $errors[] = sprintf( __( "This is not a subscription for user '%s' but for '%s'" , 'memberpress'), $usr->user_login, $sub_usr->user_login ); - } - - /* don't enforce this for now - ** - if($sub->gateway != $_POST['gateway']) { - if( $sub->gateway == MeprTransaction::$free_gateway_str || - $sub->gateway == MeprTransaction::$manual_gateway_str ) { - $sub_gateway = $sub->gateway; - } - else { - $pm = $sub->payment_method(); - $sub_gateway = sprintf( __( '%s (%s)' ), $pm->label, $pm->name ); - } - - $errors[] = sprintf( __( "This subscription is using a different payment gateway: %s" ), $sub_gateway ); - } - */ - } - else { - $errors[] = __("This subscription was not found.", 'memberpress'); - } - } + if (!$usr->ID) { + $errors[] = __('You must enter a valid username or email address', 'memberpress'); + } + } - if(empty($_POST['trans_num']) || preg_match("#[^a-zA-z0-9_\-]#", $_POST['trans_num'])) { - $errors[] = __("The Transaction Number is required, and must contain only letters, numbers, underscores and hyphens.", 'memberpress'); - } + // Simple validation here + if (!isset($_POST['amount']) || empty($_POST['amount'])) { + $errors[] = __('The amount must be set.', 'memberpress'); + } - return MeprHooks::apply_filters('mepr-admin-transaction-validation-errors', $errors); - } - - public function enqueue_scripts($hook) { - $wp_scripts = new WP_Scripts(); - $ui = $wp_scripts->query('jquery-ui-core'); - $url = "//ajax.googleapis.com/ajax/libs/jqueryui/{$ui->ver}/themes/smoothness/jquery-ui.css"; - - if($hook == 'memberpress_page_memberpress-trans' || $hook == 'memberpress_page_memberpress-new-trans') { - $l10n = array( - 'del_txn' => __('Deleting Transactions could cause the associated member to lose access to protected content. Are you sure you want to delete this Transaction?', 'memberpress'), - 'del_txn_error' => __('The Transaction could not be deleted. Please try again later.', 'memberpress'), - 'refund_txn' => __('This will refund the transaction at the gateway level. This action is not reversable. Are you sure you want to refund this Transaction?', 'memberpress'), - 'refund_txn_and_cancel_sub' => __('This will refund the transaction and cancel the subscription associated with this transaction at the gateway level. This action is not reversable. Are you sure you want to refund this Transaction and cancel it\'s Subscription?', 'memberpress'), - 'refunded_text' => __('Refunded','memberpress'), - 'refund_txn_success' => __('Your transaction was successfully refunded.','memberpress'), - 'refund_txn_error' => __('The Transaction could not be refunded. Please issue the refund by logging into your gateway\'s virtual terminal','memberpress'), - 'refund_txn_and_cancel_sub_success' => __('Your transaction was refunded and subscription was cancelled successfully.','memberpress'), - 'refund_txn_and_cancel_sub_error' => __('The Transaction could not be refunded and/or Subscription could not be cancelled. Please issue the refund by logging into your gateway\'s virtual terminal','memberpress'), - 'delete_transaction_nonce' => wp_create_nonce('delete_transaction'), - 'edit_txn_status_nonce' => wp_create_nonce('edit_txn_status'), - 'refund_txn_nonce' => wp_create_nonce('refund_txn'), - 'refund_txn_cancel_sub_nonce' => wp_create_nonce('refund_txn_cancel_sub'), - 'click_to_copy' => __('Click to copy','memberpress'), - ); - - wp_register_style('mepr-jquery-ui-smoothness', $url); - wp_register_style('jquery-ui-timepicker-addon', MEPR_CSS_URL.'/jquery-ui-timepicker-addon.css', array('mepr-jquery-ui-smoothness')); - wp_register_style('mepr-clipboardtip', MEPR_CSS_URL . '/tooltipster.bundle.min.css', array(), MEPR_VERSION ); - wp_register_style('mepr-clipboardtip-borderless', MEPR_CSS_URL . '/tooltipster-sideTip-borderless.min.css', array('mepr-clipboardtip'), MEPR_VERSION ); - wp_enqueue_style('mepr-transactions-css', MEPR_CSS_URL.'/admin-transactions.css', array('jquery-ui-timepicker-addon', 'mepr-clipboardtip', 'mepr-clipboardtip-borderless'), MEPR_VERSION); - - wp_register_script('mepr-table-controls-js', MEPR_JS_URL.'/table_controls.js', array('jquery'), MEPR_VERSION); - wp_register_script('mepr-timepicker-js', MEPR_JS_URL.'/jquery-ui-timepicker-addon.js', array('jquery-ui-datepicker')); - wp_register_script('mepr-date-picker-js', MEPR_JS_URL.'/date_picker.js', array('mepr-timepicker-js'), MEPR_VERSION); - wp_register_script('mphelpers', MEPR_JS_URL.'/mphelpers.js', array('suggest'), MEPR_VERSION); - wp_register_script( 'mepr-clipboard-js', MEPR_JS_URL . '/clipboard.min.js', array(), MEPR_VERSION ); - wp_register_script( 'mepr-tooltipster', MEPR_JS_URL . '/tooltipster.bundle.min.js', array('jquery'), MEPR_VERSION ); - - wp_enqueue_script( - 'mepr-transactions-js', - MEPR_JS_URL.'/admin_transactions.js', - array( - 'jquery', - 'mphelpers', - 'mepr-table-controls-js', - 'mepr-date-picker-js', - 'mepr-clipboard-js', - 'mepr-tooltipster', - ), - MEPR_VERSION - ); - wp_localize_script('mepr-transactions-js', 'MeprTxn', $l10n); - } - } + if (preg_match('/[^0-9., ]/', $_POST['amount'])) { + $errors[] = __('The amount must be a number.', 'memberpress'); + } - public function edit_trans_status() { - global $wpdb; - check_ajax_referer('edit_txn_status', 'edit_txn_status_nonce'); + if (isset($_POST['subscr_num']) && !empty($_POST['subscr_num'])) { + if ($sub = MeprSubscription::get_one_by_subscr_id($_POST['subscr_num'])) { + if ($sub->product_id != $_POST['product_id']) { + $prd = new MeprProduct($_POST['product_id']); + $sub_prd = $sub->product(); + $errors[] = sprintf(__("This is not a subscription for membership '%1\$s' but for '%2\$s'", 'memberpress'), $prd->post_title, $sub_prd->post_title); + } + + /* + ** $usr object is already set above + ** $usr = new MeprUser(); + ** $usr->load_user_data_by_login($_POST['user_login']); + */ + $sub_usr = $sub->user(); + + if ($usr->ID != $sub_usr->ID) { + $errors[] = sprintf(__("This is not a subscription for user '%1\$s' but for '%2\$s'", 'memberpress'), $usr->user_login, $sub_usr->user_login); + } + + /* + don't enforce this for now + ** + if($sub->gateway != $_POST['gateway']) { + if( $sub->gateway == MeprTransaction::$free_gateway_str || + $sub->gateway == MeprTransaction::$manual_gateway_str ) { + $sub_gateway = $sub->gateway; + } + else { + $pm = $sub->payment_method(); + $sub_gateway = sprintf( __( '%s (%s)' ), $pm->label, $pm->name ); + } + + $errors[] = sprintf( __( "This subscription is using a different payment gateway: %s" ), $sub_gateway ); + } + */ + } else { + $errors[] = __('This subscription was not found.', 'memberpress'); + } + } - if(!MeprUtils::is_mepr_admin()) { - die(__('You do not have access.', 'memberpress')); - } + if (empty($_POST['trans_num']) || preg_match('#[^a-zA-z0-9_\-]#', $_POST['trans_num'])) { + $errors[] = __('The Transaction Number is required, and must contain only letters, numbers, underscores and hyphens.', 'memberpress'); + } - if(!isset($_POST['id']) || empty($_POST['id']) || !isset($_POST['value']) || empty($_POST['value'])) { - die(__('Save Failed', 'memberpress')); + return MeprHooks::apply_filters('mepr-admin-transaction-validation-errors', $errors); + } + + public function enqueue_scripts($hook) + { + $wp_scripts = new WP_Scripts(); + $ui = $wp_scripts->query('jquery-ui-core'); + $url = "//ajax.googleapis.com/ajax/libs/jqueryui/{$ui->ver}/themes/smoothness/jquery-ui.css"; + + if ($hook == 'memberpress_page_memberpress-trans' || $hook == 'memberpress_page_memberpress-new-trans') { + $l10n = [ + 'del_txn' => __('Deleting Transactions could cause the associated member to lose access to protected content. Are you sure you want to delete this Transaction?', 'memberpress'), + 'del_txn_error' => __('The Transaction could not be deleted. Please try again later.', 'memberpress'), + 'refund_txn' => __('This will refund the transaction at the gateway level. This action is not reversable. Are you sure you want to refund this Transaction?', 'memberpress'), + 'refund_txn_and_cancel_sub' => __('This will refund the transaction and cancel the subscription associated with this transaction at the gateway level. This action is not reversable. Are you sure you want to refund this Transaction and cancel it\'s Subscription?', 'memberpress'), + 'refunded_text' => __('Refunded', 'memberpress'), + 'refund_txn_success' => __('Your transaction was successfully refunded.', 'memberpress'), + 'refund_txn_error' => __('The Transaction could not be refunded. Please issue the refund by logging into your gateway\'s virtual terminal', 'memberpress'), + 'refund_txn_and_cancel_sub_success' => __('Your transaction was refunded and subscription was cancelled successfully.', 'memberpress'), + 'refund_txn_and_cancel_sub_error' => __('The Transaction could not be refunded and/or Subscription could not be cancelled. Please issue the refund by logging into your gateway\'s virtual terminal', 'memberpress'), + 'delete_transaction_nonce' => wp_create_nonce('delete_transaction'), + 'edit_txn_status_nonce' => wp_create_nonce('edit_txn_status'), + 'refund_txn_nonce' => wp_create_nonce('refund_txn'), + 'refund_txn_cancel_sub_nonce' => wp_create_nonce('refund_txn_cancel_sub'), + 'click_to_copy' => __('Click to copy', 'memberpress'), + ]; + + wp_register_style('mepr-jquery-ui-smoothness', $url); + wp_register_style('jquery-ui-timepicker-addon', MEPR_CSS_URL . '/jquery-ui-timepicker-addon.css', ['mepr-jquery-ui-smoothness']); + wp_register_style('mepr-clipboardtip', MEPR_CSS_URL . '/tooltipster.bundle.min.css', [], MEPR_VERSION); + wp_register_style('mepr-clipboardtip-borderless', MEPR_CSS_URL . '/tooltipster-sideTip-borderless.min.css', ['mepr-clipboardtip'], MEPR_VERSION); + wp_enqueue_style('mepr-transactions-css', MEPR_CSS_URL . '/admin-transactions.css', ['jquery-ui-timepicker-addon', 'mepr-clipboardtip', 'mepr-clipboardtip-borderless'], MEPR_VERSION); + + wp_register_script('mepr-table-controls-js', MEPR_JS_URL . '/table_controls.js', ['jquery'], MEPR_VERSION); + wp_register_script('mepr-timepicker-js', MEPR_JS_URL . '/jquery-ui-timepicker-addon.js', ['jquery-ui-datepicker']); + wp_register_script('mepr-date-picker-js', MEPR_JS_URL . '/date_picker.js', ['mepr-timepicker-js'], MEPR_VERSION); + wp_register_script('mphelpers', MEPR_JS_URL . '/mphelpers.js', ['suggest'], MEPR_VERSION); + wp_register_script('mepr-clipboard-js', MEPR_JS_URL . '/clipboard.min.js', [], MEPR_VERSION); + wp_register_script('mepr-tooltipster', MEPR_JS_URL . '/tooltipster.bundle.min.js', ['jquery'], MEPR_VERSION); + + wp_enqueue_script( + 'mepr-transactions-js', + MEPR_JS_URL . '/admin_transactions.js', + [ + 'jquery', + 'mphelpers', + 'mepr-table-controls-js', + 'mepr-date-picker-js', + 'mepr-clipboard-js', + 'mepr-tooltipster', + ], + MEPR_VERSION + ); + wp_localize_script('mepr-transactions-js', 'MeprTxn', $l10n); + } } - $id = sanitize_key($_POST['id']); - $value = sanitize_key($_POST['value']); - $tdata = MeprTransaction::get_one($id, ARRAY_A); + public function edit_trans_status() + { + global $wpdb; + check_ajax_referer('edit_txn_status', 'edit_txn_status_nonce'); - if(!empty($tdata)) { - $txn = new MeprTransaction(); - $txn->load_data($tdata); - $txn->status = esc_sql($value); //escape the input this way since $wpdb->escape() is depracated - $txn->store(); - die($txn->status); - } - else { - die(__('Save Failed', 'memberpress')); - } - } + if (!MeprUtils::is_mepr_admin()) { + die(__('You do not have access.', 'memberpress')); + } - public function refund_transaction() { - check_ajax_referer('refund_txn', 'refund_txn_nonce'); + if (!isset($_POST['id']) || empty($_POST['id']) || !isset($_POST['value']) || empty($_POST['value'])) { + die(__('Save Failed', 'memberpress')); + } - if(!MeprUtils::is_mepr_admin()) { - die(__('You do not have access.', 'memberpress')); + $id = sanitize_key($_POST['id']); + $value = sanitize_key($_POST['value']); + $tdata = MeprTransaction::get_one($id, ARRAY_A); + + if (!empty($tdata)) { + $txn = new MeprTransaction(); + $txn->load_data($tdata); + $txn->status = esc_sql($value); // escape the input this way since $wpdb->escape() is depracated + $txn->store(); + die($txn->status); + } else { + die(__('Save Failed', 'memberpress')); + } } - if(!isset($_POST['id']) || empty($_POST['id']) || !is_numeric($_POST['id'])) { - die(__('Could not refund transaction', 'memberpress')); - } + public function refund_transaction() + { + check_ajax_referer('refund_txn', 'refund_txn_nonce'); - $txn = new MeprTransaction($_POST['id']); + if (!MeprUtils::is_mepr_admin()) { + die(__('You do not have access.', 'memberpress')); + } - try { - $txn->refund(); - } - catch( Exception $e ) { - die($e->getMessage()); - } + if (!isset($_POST['id']) || empty($_POST['id']) || !is_numeric($_POST['id'])) { + die(__('Could not refund transaction', 'memberpress')); + } - die('true'); //don't localize this string - } + $txn = new MeprTransaction($_POST['id']); - public function refund_txn_and_cancel_sub() { - check_ajax_referer('refund_txn_cancel_sub', 'refund_txn_cancel_sub_nonce'); + try { + $txn->refund(); + } catch (Exception $e) { + die($e->getMessage()); + } - if(!MeprUtils::is_mepr_admin()) { - die(__('You do not have access.', 'memberpress')); + die('true'); // don't localize this string } - if(!isset($_POST['id']) || empty($_POST['id']) || !is_numeric($_POST['id'])) { - die(__('Could not refund transaction', 'memberpress')); - } + public function refund_txn_and_cancel_sub() + { + check_ajax_referer('refund_txn_cancel_sub', 'refund_txn_cancel_sub_nonce'); - $txn = new MeprTransaction($_POST['id']); + if (!MeprUtils::is_mepr_admin()) { + die(__('You do not have access.', 'memberpress')); + } - try { - $txn->refund(); + if (!isset($_POST['id']) || empty($_POST['id']) || !is_numeric($_POST['id'])) { + die(__('Could not refund transaction', 'memberpress')); + } - if(($sub = $txn->subscription())) { - $sub->cancel(); - } - } - catch( Exception $e ) { - die($e->getMessage()); - } + $txn = new MeprTransaction($_POST['id']); - die('true'); //don't localize this string - } + try { + $txn->refund(); - public function delete_transaction() { - check_ajax_referer('delete_transaction','mepr_transactions_nonce'); + if (($sub = $txn->subscription())) { + $sub->cancel(); + } + } catch (Exception $e) { + die($e->getMessage()); + } - if(!MeprUtils::is_mepr_admin()) { - die(__('You do not have access.', 'memberpress')); + die('true'); // don't localize this string } - if(!isset($_POST['id']) || empty($_POST['id']) || !is_numeric($_POST['id'])) { - die(__('Could not delete transaction', 'memberpress')); + public function delete_transaction() + { + check_ajax_referer('delete_transaction', 'mepr_transactions_nonce'); + + if (!MeprUtils::is_mepr_admin()) { + die(__('You do not have access.', 'memberpress')); + } + + if (!isset($_POST['id']) || empty($_POST['id']) || !is_numeric($_POST['id'])) { + die(__('Could not delete transaction', 'memberpress')); + } + + $txn = new MeprTransaction($_POST['id']); + $txn->destroy(); + + die('true'); // don't localize this string } - $txn = new MeprTransaction($_POST['id']); - $txn->destroy(); - - die('true'); //don't localize this string - } - - /* This is here to use wherever we want. */ - public function get_columns() { - $cols = array( - 'col_id' => __('Id', 'memberpress'), - 'col_trans_num' => __('Transaction', 'memberpress'), - 'col_subscr_id' => __('Subscription', 'memberpress'), - 'col_status' => __('Status', 'memberpress'), - 'col_product' => __('Membership', 'memberpress'), - 'col_net' => __('Net', 'memberpress'), - 'col_tax' => __('Tax', 'memberpress'), - 'col_total' => __('Total', 'memberpress'), - 'col_propername' => __('Name', 'memberpress'), - 'col_user_login' => __('User', 'memberpress'), - 'col_payment_system' => __('Gateway', 'memberpress'), - 'col_created_at' => __('Created On', 'memberpress'), - 'col_expires_at' => __('Expires On', 'memberpress') - ); - - return MeprHooks::apply_filters('mepr-admin-transactions-cols', $cols); - } - - public function display_list() { - $screen = get_current_screen(); - $list_table = new MeprTransactionsTable( $screen, $this->get_columns() ); - - $list_table->prepare_items(); - - MeprView::render('/admin/transactions/list', get_defined_vars()); - } - - public function resend_txn_email() { - $mepr_options = MeprOptions::fetch(); - - if(!MeprUtils::is_mepr_admin()) { - die(__('You do not have access.', 'memberpress')); + /* This is here to use wherever we want. */ + public function get_columns() + { + $cols = [ + 'col_id' => __('Id', 'memberpress'), + 'col_trans_num' => __('Transaction', 'memberpress'), + 'col_subscr_id' => __('Subscription', 'memberpress'), + 'col_status' => __('Status', 'memberpress'), + 'col_product' => __('Membership', 'memberpress'), + 'col_net' => __('Net', 'memberpress'), + 'col_tax' => __('Tax', 'memberpress'), + 'col_total' => __('Total', 'memberpress'), + 'col_propername' => __('Name', 'memberpress'), + 'col_user_login' => __('User', 'memberpress'), + 'col_payment_system' => __('Gateway', 'memberpress'), + 'col_created_at' => __('Created On', 'memberpress'), + 'col_expires_at' => __('Expires On', 'memberpress'), + ]; + + return MeprHooks::apply_filters('mepr-admin-transactions-cols', $cols); } - if(!isset($_POST['id']) || empty($_POST['id']) || !is_numeric($_POST['id'])) { - die(__('Could not send email. Please try again later.', 'memberpress')); + public function display_list() + { + $screen = get_current_screen(); + $list_table = new MeprTransactionsTable($screen, $this->get_columns()); + + $list_table->prepare_items(); + + MeprView::render('/admin/transactions/list', get_defined_vars()); } - $txn = new MeprTransaction($_POST['id']); + public function resend_txn_email() + { + $mepr_options = MeprOptions::fetch(); - MeprUtils::send_notices( - $txn, - 'MeprUserReceiptEmail', - 'MeprAdminReceiptEmail' - ); + if (!MeprUtils::is_mepr_admin()) { + die(__('You do not have access.', 'memberpress')); + } - die(__('Email sent', 'memberpress')); - } + if (!isset($_POST['id']) || empty($_POST['id']) || !is_numeric($_POST['id'])) { + die(__('Could not send email. Please try again later.', 'memberpress')); + } - public function send_welcome_email(){ - $mepr_options = MeprOptions::fetch(); + $txn = new MeprTransaction($_POST['id']); - if(!MeprUtils::is_mepr_admin()) { - die(__('You do not have access.', 'memberpress')); - } + MeprUtils::send_notices( + $txn, + 'MeprUserReceiptEmail', + 'MeprAdminReceiptEmail' + ); - if(!isset($_POST['id']) || empty($_POST['id']) || !is_numeric($_POST['id'])) { - die(__('Could not send email. Please try again later.', 'memberpress')); + die(__('Email sent', 'memberpress')); } - $txn = new MeprTransaction($_POST['id']); - $usr = $txn->user(); + public function send_welcome_email() + { + $mepr_options = MeprOptions::fetch(); - MeprUtils::maybe_send_product_welcome_notices($txn, $usr); + if (!MeprUtils::is_mepr_admin()) { + die(__('You do not have access.', 'memberpress')); + } + + if (!isset($_POST['id']) || empty($_POST['id']) || !is_numeric($_POST['id'])) { + die(__('Could not send email. Please try again later.', 'memberpress')); + } - die(__('Welcome Email sent', 'memberpress')); - } + $txn = new MeprTransaction($_POST['id']); + $usr = $txn->user(); - public function default_expiration() { - if( isset($_REQUEST['product_id']) && isset($_REQUEST['created_at']) && - ($prd = MeprProduct::get_one($_REQUEST['product_id'])) && - // !$prd->is_one_time_payment() && //Breaking one-offs which have an expiration date - ( preg_match('/\d\d\d\d-\d\d-\d\d/', $_REQUEST['created_at']) || - preg_match('/\d\d\d\d-\d\d-\d\d \d\d-\d\d-\d\d/', $_REQUEST['created_at']) || - empty($_REQUEST['created_at']) ) ) { - $expires_at_ts = $prd->get_expires_at(strtotime($_REQUEST['created_at']), false); + MeprUtils::maybe_send_product_welcome_notices($txn, $usr); - if(!is_null($expires_at_ts)) { - echo date('Y-m-d H:i:s', (int)$expires_at_ts); - } + die(__('Welcome Email sent', 'memberpress')); } - die; - } + public function default_expiration() + { + if ( + isset($_REQUEST['product_id']) && isset($_REQUEST['created_at']) && + ($prd = MeprProduct::get_one($_REQUEST['product_id'])) && + // !$prd->is_one_time_payment() && //Breaking one-offs which have an expiration date + ( preg_match('/\d\d\d\d-\d\d-\d\d/', $_REQUEST['created_at']) || + preg_match('/\d\d\d\d-\d\d-\d\d \d\d-\d\d-\d\d/', $_REQUEST['created_at']) || + empty($_REQUEST['created_at']) ) + ) { + $expires_at_ts = $prd->get_expires_at(strtotime($_REQUEST['created_at']), false); - public function csv() { - check_ajax_referer('export_transactions', 'mepr_transactions_nonce'); + if (!is_null($expires_at_ts)) { + echo date('Y-m-d H:i:s', (int)$expires_at_ts); + } + } - $filename = 'transactions-'.time(); + die; + } - // Since we're running WP_List_Table headless we need to do this - $GLOBALS['hook_suffix'] = false; + public function csv() + { + check_ajax_referer('export_transactions', 'mepr_transactions_nonce'); - $screen = get_current_screen(); - $tab = new MeprTransactionsTable($screen,$this->get_columns()); + $filename = 'transactions-' . time(); - if(isset($_REQUEST['all']) && !empty($_REQUEST['all'])) { - $search = isset($_REQUEST["search"]) && !empty($_REQUEST["search"]) ? esc_sql($_REQUEST["search"]) : ''; - $search_field = isset($_REQUEST["search"]) && !empty($_REQUEST["search-field"]) ? esc_sql($_REQUEST["search-field"]) : 'any'; - $search_field = isset($tab->db_search_cols[$search_field]) ? $tab->db_search_cols[$search_field] : 'any'; + // Since we're running WP_List_Table headless we need to do this + $GLOBALS['hook_suffix'] = false; - $all = MeprTransaction::list_table( - /* $order_by */ 'created_at', - /* $order */ 'ASC', - /* $paged */ '', - /* $search */ $search, - /* $search_field */ $search_field, - /* $perpage */ '', - /* $params */ $_REQUEST - ); + $screen = get_current_screen(); + $tab = new MeprTransactionsTable($screen, $this->get_columns()); - MeprUtils::render_csv($all['results'], $filename); - } - else { - $tab->prepare_items(); - MeprUtils::render_csv( $tab->get_items(), $filename ); + if (isset($_REQUEST['all']) && !empty($_REQUEST['all'])) { + $search = isset($_REQUEST['search']) && !empty($_REQUEST['search']) ? esc_sql($_REQUEST['search']) : ''; + $search_field = isset($_REQUEST['search']) && !empty($_REQUEST['search-field']) ? esc_sql($_REQUEST['search-field']) : 'any'; + $search_field = isset($tab->db_search_cols[$search_field]) ? $tab->db_search_cols[$search_field] : 'any'; + + $all = MeprTransaction::list_table( + /* $order_by */ 'created_at', + /* $order */ 'ASC', + /* $paged */ '', + /* $search */ $search, + /* $search_field */ $search_field, + /* $perpage */ '', + /* $params */ $_REQUEST + ); + + MeprUtils::render_csv($all['results'], $filename); + } else { + $tab->prepare_items(); + MeprUtils::render_csv($tab->get_items(), $filename); + } } - } - public function export_footer_link($action, $totalitems, $itemcount) { - if($action=='mepr_transactions') { - MeprAppHelper::export_table_link($action, 'export_transactions', 'mepr_transactions_nonce', $itemcount); - ?> | | __('Transactions', 'memberpress'), - 'default' => 10, - 'option' => 'mp_transactions_perpage' - ); + $args = [ + 'label' => __('Transactions', 'memberpress'), + 'default' => 10, + 'option' => 'mp_transactions_perpage', + ]; - add_screen_option( $option, $args ); - } + add_screen_option($option, $args); + } - public function setup_screen_options($status, $option, $value) { - if ( 'mp_transactions_perpage' === $option ) { return $value; } - return $status; - } + public function setup_screen_options($status, $option, $value) + { + if ('mp_transactions_perpage' === $option) { + return $value; + } + return $status; + } - public function send_expired_txn_events() { - $start_time = time(); - $max_time = MeprUtils::minutes(10); + public function send_expired_txn_events() + { + $start_time = time(); + $max_time = MeprUtils::minutes(10); - $res = MeprTransaction::get_expired_txns(); + $res = MeprTransaction::get_expired_txns(); - foreach( $res as $row ) { - $run_time = time() - $start_time; - if( $run_time >= $max_time ) { return; } - $txn = new MeprTransaction($row->id); - MeprEvent::record('transaction-expired', $txn); + foreach ($res as $row) { + $run_time = time() - $start_time; + if ($run_time >= $max_time) { + return; + } + $txn = new MeprTransaction($row->id); + MeprEvent::record('transaction-expired', $txn); - if($txn->subscription()) { - MeprEvent::record('recurring-transaction-expired', $txn); - } - else { - MeprEvent::record('non-recurring-transaction-expired', $txn); - } + if ($txn->subscription()) { + MeprEvent::record('recurring-transaction-expired', $txn); + } else { + MeprEvent::record('non-recurring-transaction-expired', $txn); + } - MeprHooks::do_action('mepr-txn-expired', $txn, $row->sub_status); // DEPRECATED - MeprHooks::do_action('mepr-transaction-expired', $txn, $row->sub_status); + MeprHooks::do_action('mepr-txn-expired', $txn, $row->sub_status); // DEPRECATED + MeprHooks::do_action('mepr-transaction-expired', $txn, $row->sub_status); + } } - } - public function table_search_box() { - if(isset($_REQUEST['page']) && $_REQUEST['page']=='memberpress-trans') { - $mepr_options = MeprOptions::fetch(); + public function table_search_box() + { + if (isset($_REQUEST['page']) && $_REQUEST['page'] == 'memberpress-trans') { + $mepr_options = MeprOptions::fetch(); - $membership = (isset($_REQUEST['membership'])?$_REQUEST['membership']:false); - $status = (isset($_REQUEST['status'])?$_REQUEST['status']:'all'); - $gateway = (isset($_REQUEST['gateway'])?$_REQUEST['gateway']:'all'); + $membership = (isset($_REQUEST['membership']) ? $_REQUEST['membership'] : false); + $status = (isset($_REQUEST['status']) ? $_REQUEST['status'] : 'all'); + $gateway = (isset($_REQUEST['gateway']) ? $_REQUEST['gateway'] : 'all'); - $args = array('orderby' => 'title', - 'order' => 'ASC' ); - $prds = MeprCptModel::all('MeprProduct', false, $args); - $gateways = $mepr_options->payment_methods(); + $args = [ + 'orderby' => 'title', + 'order' => 'ASC', + ]; + $prds = MeprCptModel::all('MeprProduct', false, $args); + $gateways = $mepr_options->payment_methods(); - MeprView::render('/admin/transactions/search_box', compact('membership','status','prds','gateways','gateway')); + MeprView::render('/admin/transactions/search_box', compact('membership', 'status', 'prds', 'gateways', 'gateway')); + } } - } } //End class diff --git a/app/controllers/MeprUpdateCtrl.php b/app/controllers/MeprUpdateCtrl.php index d5e5a85..1831088 100644 --- a/app/controllers/MeprUpdateCtrl.php +++ b/app/controllers/MeprUpdateCtrl.php @@ -1,608 +1,618 @@ mothership_license)) { + $li = get_site_transient('mepr_license_info'); - $mepr_options = MeprOptions::fetch(); + if (false === $li) { + MeprUpdateCtrl::manually_queue_update(); + $li = get_site_transient('mepr_license_info'); + } + } - if(!empty($mepr_options->mothership_license)) { - $li = get_site_transient('mepr_license_info'); + // Default + $link = 'https://memberpress.com/dc2022/dc-alert'; + $heading = 'Monetize & Save!'; + $message = 'Ready, Set, CONNECT💥Add a Forum to MemberPress 👉 Monetize Your Discord 👉 GROW Your Business w/ This New FREE Add-On'; + $button_text = '👉 LEARN MORE 👈'; + + /* + if ( ! empty( $li['license_key']['expires_at'] ) && strtotime( $li['license_key']['expires_at'] ) < time() ) { + // Expired + $heading = 'It’s Time To Renew!'; + $message = "Renew Your License NOW & You'll 👉 Save Up To $250 👈 It’s Summer Savings Time 🌼 Use Code SUMMER22 Thru 7/18"; + $button_text = '👉 RENEW NOW 👈'; + $link = 'https://memberpress.com/su2022/su-alert/lic-exp'; + } elseif ( ! empty( $li['product_slug'] ) ) { + // Active + switch ( $li['product_slug'] ) { + case 'memberpress-basic': + $heading = 'Upgrade Now & Save!'; + $message = "UPGRADE to Plus or Pro NOW & You'll 👉 Save Up To $250 👈 It’s Summer Savings Time 🌼 Use Code SUMMER22 Thru 7/18"; + $button_text = '👉 UPGRADE NOW 👈'; + $link = 'https://memberpress.com/su2022/su-alert/lic-basic'; + break; + + case 'memberpress-plus-2': + $heading = 'Upgrade Now & Save!'; + $message = "UPGRADE to Pro NOW & You'll 👉 Save Up To $250 👈 It’s Summer Savings Time 🌼 Use Code SUMMER22 Thru 7/18"; + $button_text = '👉 UPGRADE NOW 👈'; + $link = 'https://memberpress.com/su2022/su-alert/lic-plus'; + break; + + case 'memberpress-pro': + case 'memberpress-pro-5': + case 'business': + case 'memberpress-plus': + case 'developer': + $link = ''; + break; + + default: + break; + } + } + */ - if(false === $li) { - MeprUpdateCtrl::manually_queue_update(); - $li = get_site_transient('mepr_license_info'); - } - } + if (empty($link)) { + return; + } - // Default - $link = 'https://memberpress.com/dc2022/dc-alert'; - $heading = 'Monetize & Save!'; - $message = "Ready, Set, CONNECT💥Add a Forum to MemberPress 👉 Monetize Your Discord 👉 GROW Your Business w/ This New FREE Add-On"; - $button_text = '👉 LEARN MORE 👈'; - - /* - if ( ! empty( $li['license_key']['expires_at'] ) && strtotime( $li['license_key']['expires_at'] ) < time() ) { - // Expired - $heading = 'It’s Time To Renew!'; - $message = "Renew Your License NOW & You'll 👉 Save Up To $250 👈 It’s Summer Savings Time 🌼 Use Code SUMMER22 Thru 7/18"; - $button_text = '👉 RENEW NOW 👈'; - $link = 'https://memberpress.com/su2022/su-alert/lic-exp'; - } elseif ( ! empty( $li['product_slug'] ) ) { - // Active - switch ( $li['product_slug'] ) { - case 'memberpress-basic': - $heading = 'Upgrade Now & Save!'; - $message = "UPGRADE to Plus or Pro NOW & You'll 👉 Save Up To $250 👈 It’s Summer Savings Time 🌼 Use Code SUMMER22 Thru 7/18"; - $button_text = '👉 UPGRADE NOW 👈'; - $link = 'https://memberpress.com/su2022/su-alert/lic-basic'; - break; - - case 'memberpress-plus-2': - $heading = 'Upgrade Now & Save!'; - $message = "UPGRADE to Pro NOW & You'll 👉 Save Up To $250 👈 It’s Summer Savings Time 🌼 Use Code SUMMER22 Thru 7/18"; - $button_text = '👉 UPGRADE NOW 👈'; - $link = 'https://memberpress.com/su2022/su-alert/lic-plus'; - break; - - case 'memberpress-pro': - case 'memberpress-pro-5': - case 'business': - case 'memberpress-plus': - case 'developer': - $link = ''; - break; - - default: - break; - } + MeprView::render('/admin/admin-notification', get_defined_vars()); } - */ - if ( empty( $link ) ) { - return; + public static function admin_menu() + { + // Create an official rollback page in the fashion of WordPress' built in upgrader + if (isset($_GET['page']) && $_GET['page'] == 'mepr-rollback') { + add_dashboard_page(__('Rollback MemberPress', 'memberpress'), __('Rollback MemberPress', 'memberpress'), 'update_plugins', 'mepr-rollback', 'MeprUpdateCtrl::rollback'); + } } - MeprView::render('/admin/admin-notification', get_defined_vars()); - } + public function display_options() + { + $mepr_options = MeprOptions::fetch(); + MeprView::render('admin/auto-updates/option', get_defined_vars()); + } - public static function admin_menu() { - // Create an official rollback page in the fashion of WordPress' built in upgrader - if(isset($_GET['page']) && $_GET['page'] == 'mepr-rollback') { - add_dashboard_page(__('Rollback MemberPress', 'memberpress'), __('Rollback MemberPress', 'memberpress'), 'update_plugins', 'mepr-rollback', 'MeprUpdateCtrl::rollback'); + public function store_options() + { + $mepr_options = MeprOptions::fetch(); + $name = $mepr_options->auto_updates_str; + $mepr_options->auto_updates = isset($_POST[$name]) ? sanitize_text_field($_POST[$name]) : false; + $mepr_options->store(false); } - } - - public function display_options() { - $mepr_options = MeprOptions::fetch(); - MeprView::render('admin/auto-updates/option', get_defined_vars()); - } - - public function store_options() { - $mepr_options = MeprOptions::fetch(); - $name = $mepr_options->auto_updates_str; - $mepr_options->auto_updates = isset( $_POST[$name] ) ? sanitize_text_field( $_POST[$name] ) : false; - $mepr_options->store(false); - } - - /** - * Gets major version - * - * @param string $version Version - * - * @return string - */ - public static function get_major_version( $version ) { - $exploded_version = explode( '.', $version ); - return $exploded_version[0]; - } - - /** - * Filters the auto update plugin routine to allow MemberPress to be - * automatically updated. - * - * @param bool $update Flag to update the plugin or not. - * @param array $item Update data about a specific plugin. - * @return bool $update The new update state. - */ - /** - * Notes about autoupdater: - * This runs on the normal WordPress auto-update sequence: - * 1. In wp-includes/update.php, wp_version_check() is called by the WordPress update cron (every 8 or 12 hours; can be overriden to be faster/long or turned off by plugins) - * 2. In wp-includes/update.php, wp_version_check() ends with a action call to do_action( 'wp_maybe_auto_update' ) if cron is running - * 3. In wp-includes/update.php, wp_maybe_auto_update() hooks into wp_maybe_auto_update action, creates a new WP_Automatic_Updater instance and calls WP_Automatic_Updater->run - * 4. In wp-admin/includes/class-wp-automatic-updater.php $this->run() checks to make sure we're on the main site if on a network, and also if the autoupdates are disabled (by plugin, by being on a version controlled site, etc ) - * 5. In wp-admin/includes/class-wp-automatic-updater.php $this->run() then checks to see which plugins have new versions (version/update check) - * 6. In wp-admin/includes/class-wp-automatic-updater.php $this->run() then calls $this->update() for each plugin installed who has an upgrade. - * 7 In wp-admin/includes/class-wp-automatic-updater.php $this->update() double checks filesystem access and then installs the plugin if able - * - * Notes: - * - This autoupdater only works if WordPress core detects no version control. If you want to test this, do it on a new WP site without any .git folders anywhere. - * - This autoupdater only works if the file access is able to be written to - * - This autoupdater only works if a new version has been detected, and will run not the second the update is released, but whenever the cron for wp_version_check is next released. This is generally run every 8-12 hours. - * - However, that cron can be disabled, the autoupdater can be turned off via constant or filter, version control or file lock can be detected, and other plugins can be installed (incl in functions of theme) that turn off all - * all automatic plugin updates. - * - If you want to test this is working, you have to manually run the wp_version_check cron. Install the WP Crontrol plugin or Core Control plugin, and run the cron manually using it. - * - Again, because you skimmed over it the first time, if you want to test this manually you need to test this on a new WP install without version control for core, plugins, etc, without file lock, with license key entered (for pro only) - * and use the WP Crontrol or Core Control plugin to run wp_version_check - * - You may have to manually remove an option called "auto_update.lock" from the WP options table - * - You may need to run wp_version_check multiple times (note though that they must be spaced at least 60 seconds apart) - * - Because WP's updater asks the OS if the file is writable, make sure you do not have any files/folders for the plugin you are trying to autoupdate open when testing. - * - You may need to delete the plugin info transient to get it to hard refresh the plugin info. - */ - public static function automatic_updates( $update, $item ) { - - // If this is multisite and is not on the main site, return early. - if ( is_multisite() && ! is_main_site() ) { - return $update; - } - - // If we don't have everything we need, return early. - $item = (array) $item; - if ( ! isset( $item['new_version'] ) || ! isset( $item['slug'] ) ) { - return $update; - } - - // If the plugin isn't ours, return early. - $is_memberpress = 'memberpress' === $item['slug']; - $is_addon = isset( $item['slug'] ) && 0 === strpos( $item['slug'], 'memberpress-' ); // see updater class - if ( ! $is_memberpress && ! $is_addon ) { - return $update; - } - - // If the plugin folder is a git repo, return early. - if(@is_dir(WP_PLUGIN_DIR . '/' . $item['slug'] . '/.git')) { - return $update; - } - $mepr_options = MeprOptions::fetch(); + /** + * Gets major version + * + * @param string $version Version + * + * @return string + */ + public static function get_major_version($version) + { + $exploded_version = explode('.', $version); + return $exploded_version[0]; + } + + /** + * Filters the auto update plugin routine to allow MemberPress to be + * automatically updated. + * + * @param bool $update Flag to update the plugin or not. + * @param array $item Update data about a specific plugin. + * @return bool $update The new update state. + */ + /** + * Notes about autoupdater: + * This runs on the normal WordPress auto-update sequence: + * 1. In wp-includes/update.php, wp_version_check() is called by the WordPress update cron (every 8 or 12 hours; can be overriden to be faster/long or turned off by plugins) + * 2. In wp-includes/update.php, wp_version_check() ends with a action call to do_action( 'wp_maybe_auto_update' ) if cron is running + * 3. In wp-includes/update.php, wp_maybe_auto_update() hooks into wp_maybe_auto_update action, creates a new WP_Automatic_Updater instance and calls WP_Automatic_Updater->run + * 4. In wp-admin/includes/class-wp-automatic-updater.php $this->run() checks to make sure we're on the main site if on a network, and also if the autoupdates are disabled (by plugin, by being on a version controlled site, etc ) + * 5. In wp-admin/includes/class-wp-automatic-updater.php $this->run() then checks to see which plugins have new versions (version/update check) + * 6. In wp-admin/includes/class-wp-automatic-updater.php $this->run() then calls $this->update() for each plugin installed who has an upgrade. + * 7 In wp-admin/includes/class-wp-automatic-updater.php $this->update() double checks filesystem access and then installs the plugin if able + * + * Notes: + * - This autoupdater only works if WordPress core detects no version control. If you want to test this, do it on a new WP site without any .git folders anywhere. + * - This autoupdater only works if the file access is able to be written to + * - This autoupdater only works if a new version has been detected, and will run not the second the update is released, but whenever the cron for wp_version_check is next released. This is generally run every 8-12 hours. + * - However, that cron can be disabled, the autoupdater can be turned off via constant or filter, version control or file lock can be detected, and other plugins can be installed (incl in functions of theme) that turn off all + * all automatic plugin updates. + * - If you want to test this is working, you have to manually run the wp_version_check cron. Install the WP Crontrol plugin or Core Control plugin, and run the cron manually using it. + * - Again, because you skimmed over it the first time, if you want to test this manually you need to test this on a new WP install without version control for core, plugins, etc, without file lock, with license key entered (for pro only) + * and use the WP Crontrol or Core Control plugin to run wp_version_check + * - You may have to manually remove an option called "auto_update.lock" from the WP options table + * - You may need to run wp_version_check multiple times (note though that they must be spaced at least 60 seconds apart) + * - Because WP's updater asks the OS if the file is writable, make sure you do not have any files/folders for the plugin you are trying to autoupdate open when testing. + * - You may need to delete the plugin info transient to get it to hard refresh the plugin info. + */ + public static function automatic_updates($update, $item) + { + + // If this is multisite and is not on the main site, return early. + if (is_multisite() && ! is_main_site()) { + return $update; + } - $automatic_updates = ! empty( $mepr_options->auto_updates ) ? $mepr_options->auto_updates : 'all'; - $current_major = self::get_major_version( mepr_plugin_info( 'Version' ) ); - $new_major = self::get_major_version( $item['new_version'] ); + // If we don't have everything we need, return early. + $item = (array) $item; + if (! isset($item['new_version']) || ! isset($item['slug'])) { + return $update; + } - // Major update available - // If major update are enabled, run the update, else bail - if ( $current_major < $new_major ) { - return 'all' === $automatic_updates ? true : $update; - } + // If the plugin isn't ours, return early. + $is_memberpress = 'memberpress' === $item['slug']; + $is_addon = isset($item['slug']) && 0 === strpos($item['slug'], 'memberpress-'); // see updater class + if (! $is_memberpress && ! $is_addon) { + return $update; + } - // Minor update available - // If minor (or major) updates are enabled, run the update, else bail - if ( $current_major === $new_major && version_compare( mepr_plugin_info( 'Version' ), $item['new_version'], '<' ) ) { - return 'all' === $automatic_updates || 'minor' === $automatic_updates ? true : $update; - } + // If the plugin folder is a git repo, return early. + if (@is_dir(WP_PLUGIN_DIR . '/' . $item['slug'] . '/.git')) { + return $update; + } - return $update; - } + $mepr_options = MeprOptions::fetch(); - public static function rollback() { - // Ensure the rollback is valid - check_admin_referer('mepr_rollback_nonce'); + $automatic_updates = ! empty($mepr_options->auto_updates) ? $mepr_options->auto_updates : 'all'; + $current_major = self::get_major_version(mepr_plugin_info('Version')); + $new_major = self::get_major_version($item['new_version']); - // Permissions check - if(!current_user_can('update_plugins')) { - wp_die(__('You don\'t have sufficient permissions to rollback MemberPress.', 'memberpress')); - } + // Major update available + // If major update are enabled, run the update, else bail + if ($current_major < $new_major) { + return 'all' === $automatic_updates ? true : $update; + } - $transient = get_site_transient('update_plugins'); - $transient = self::queue_update($transient, true, true); + // Minor update available + // If minor (or major) updates are enabled, run the update, else bail + if ($current_major === $new_major && version_compare(mepr_plugin_info('Version'), $item['new_version'], '<')) { + return 'all' === $automatic_updates || 'minor' === $automatic_updates ? true : $update; + } - $info = get_site_transient('mepr_update_info'); + return $update; + } - //Get the necessary class - include_once(ABSPATH . 'wp-admin/includes/class-wp-upgrader.php'); - include_once(MEPR_LIB_PATH . '/class-rollback-memberpress-upgrader.php'); + public static function rollback() + { + // Ensure the rollback is valid + check_admin_referer('mepr_rollback_nonce'); - $args = wp_parse_args($_GET, array('page' => 'mepr-rollback')); + // Permissions check + if (!current_user_can('update_plugins')) { + wp_die(__('You don\'t have sufficient permissions to rollback MemberPress.', 'memberpress')); + } - $title = ''; - $nonce = 'upgrade-plugin_' . MEPR_PLUGIN_NAME; - $url = 'index.php?page=mepr-rollback'; - $plugin = MEPR_PLUGIN_NAME; - $version = $info['curr_version']; + $transient = get_site_transient('update_plugins'); + $transient = self::queue_update($transient, true, true); - $upgrader = new WP_Rollback_MemberPress_Upgrader( - new Plugin_Upgrader_Skin(compact('title','nonce','url','plugin','version')) - ); + $info = get_site_transient('mepr_update_info'); - $upgrader->rollback($info); - } + // Get the necessary class + include_once(ABSPATH . 'wp-admin/includes/class-wp-upgrader.php'); + include_once(MEPR_LIB_PATH . '/class-rollback-memberpress-upgrader.php'); - public static function rollback_url() { - $nonce = wp_create_nonce('mepr_rollback_nonce'); - return admin_url("index.php?page=mepr-rollback&_wpnonce={$nonce}"); - } + $args = wp_parse_args($_GET, ['page' => 'mepr-rollback']); - public static function is_activated() { - $mepr_options = MeprOptions::fetch(); - $activated = get_option('mepr_activated'); - return (!empty($mepr_options->mothership_license) && !empty($activated)); - } + $title = ''; + $nonce = 'upgrade-plugin_' . MEPR_PLUGIN_NAME; + $url = 'index.php?page=mepr-rollback'; + $plugin = MEPR_PLUGIN_NAME; + $version = $info['curr_version']; - public static function check_license_activation() { - $aov = get_option('mepr_activation_override'); + $upgrader = new WP_Rollback_MemberPress_Upgrader( + new Plugin_Upgrader_Skin(compact('title', 'nonce', 'url', 'plugin', 'version')) + ); - if(!empty($aov)) { - update_option('mepr_activated', true); - do_action('mepr_license_activated', array('aov' => 1)); - return; + $upgrader->rollback($info); } - $mepr_options = MeprOptions::fetch(); - - if(empty($mepr_options->mothership_license)) { - return; + public static function rollback_url() + { + $nonce = wp_create_nonce('mepr_rollback_nonce'); + return admin_url("index.php?page=mepr-rollback&_wpnonce={$nonce}"); } - // Only check the key once per day - $option_key = "mepr_license_check_{$mepr_options->mothership_license}"; - - if(get_site_transient($option_key)) { - return; + public static function is_activated() + { + $mepr_options = MeprOptions::fetch(); + $activated = get_option('mepr_activated'); + return (!empty($mepr_options->mothership_license) && !empty($activated)); } - $check_count = get_option($option_key, 0) + 1; - update_option($option_key, $check_count); - - set_site_transient($option_key, true, MeprUtils::hours($check_count > 3 ? 72 : 24)); + public static function check_license_activation() + { + $aov = get_option('mepr_activation_override'); - $domain = urlencode(MeprUtils::site_domain()); - $args = compact('domain'); + if (!empty($aov)) { + update_option('mepr_activated', true); + do_action('mepr_license_activated', ['aov' => 1]); + return; + } - try { - $act = self::send_mothership_request("/license_keys/check/{$mepr_options->mothership_license}", $args); + $mepr_options = MeprOptions::fetch(); - if(!empty($act) && is_array($act)) { - $license_expired = false; + if (empty($mepr_options->mothership_license)) { + return; + } - if(isset($act['expires_at'])) { - $expires_at = strtotime($act['expires_at']); + // Only check the key once per day + $option_key = "mepr_license_check_{$mepr_options->mothership_license}"; - if($expires_at && $expires_at < time()) { - $license_expired = true; - update_option('mepr_activated', false); - do_action('mepr_license_expired', $act); - } + if (get_site_transient($option_key)) { + return; } - if(isset($act['status']) && !$license_expired) { - if($act['status'] == 'enabled') { - update_option($option_key, 0); - update_option('mepr_activated', true); - do_action('mepr_license_activated', $act); - } - elseif($act['status'] == 'disabled') { - update_option('mepr_activated', false); - do_action('mepr_license_invalidated', $act); - } - } - } - } - catch(Exception $e) { - if($e->getMessage() == 'Not Found') { - update_option('mepr_activated', false); - do_action('mepr_license_invalidated'); - } - } - } - - public static function maybe_activate() { - $activated = get_option('mepr_activated'); + $check_count = get_option($option_key, 0) + 1; + update_option($option_key, $check_count); - if(!$activated) { - self::check_license_activation(); - } - } + set_site_transient($option_key, true, MeprUtils::hours($check_count > 3 ? 72 : 24)); - public static function activate_from_define() { - $mepr_options = MeprOptions::fetch(); + $domain = urlencode(MeprUtils::site_domain()); + $args = compact('domain'); - if(defined('MEMBERPRESS_LICENSE_KEY') && $mepr_options->mothership_license != MEMBERPRESS_LICENSE_KEY) { - try { - if(!empty($mepr_options->mothership_license)) { - // Deactivate the old license key - self::deactivate_license(); + try { + $act = self::send_mothership_request("/license_keys/check/{$mepr_options->mothership_license}", $args); + + if (!empty($act) && is_array($act)) { + $license_expired = false; + + if (isset($act['expires_at'])) { + $expires_at = strtotime($act['expires_at']); + + if ($expires_at && $expires_at < time()) { + $license_expired = true; + update_option('mepr_activated', false); + do_action('mepr_license_expired', $act); + } + } + + if (isset($act['status']) && !$license_expired) { + if ($act['status'] == 'enabled') { + update_option($option_key, 0); + update_option('mepr_activated', true); + do_action('mepr_license_activated', $act); + } elseif ($act['status'] == 'disabled') { + update_option('mepr_activated', false); + do_action('mepr_license_invalidated', $act); + } + } + } + } catch (Exception $e) { + if ($e->getMessage() == 'Not Found') { + update_option('mepr_activated', false); + do_action('mepr_license_invalidated'); + } } + } - // If we're using defines then we have to do this with defines too - $mepr_options = MeprOptions::fetch(); - $mepr_options->edge_updates = false; - $mepr_options->store(false); + public static function maybe_activate() + { + $activated = get_option('mepr_activated'); - $act = self::activate_license(MEMBERPRESS_LICENSE_KEY); - - $message = $act['message']; - $view = '/admin/errors'; - $callback = function() use($view, $message) { - return MeprView::render($view, compact('message')); - }; - } - catch(Exception $e) { - $view = '/admin/update/activation_warning'; - $error = $e->getMessage(); - $callback = function() use($view, $error) { - return MeprView::render($view, compact('error')); - }; - } - - add_action( 'admin_notices', $callback ); + if (!$activated) { + self::check_license_activation(); + } } - } - /** - * Activate the license with the given key - * - * @param string $license_key The license key - * @return array The license data - * @throws Exception If there was an error activating the license - */ - public static function activate_license($license_key) { - $mepr_options = MeprOptions::fetch(); + public static function activate_from_define() + { + $mepr_options = MeprOptions::fetch(); - $args = array( - 'domain' => urlencode(MeprUtils::site_domain()), - 'product' => MEPR_EDITION, - ); + if (defined('MEMBERPRESS_LICENSE_KEY') && $mepr_options->mothership_license != MEMBERPRESS_LICENSE_KEY) { + try { + if (!empty($mepr_options->mothership_license)) { + // Deactivate the old license key + self::deactivate_license(); + } + + // If we're using defines then we have to do this with defines too + $mepr_options = MeprOptions::fetch(); + $mepr_options->edge_updates = false; + $mepr_options->store(false); + + $act = self::activate_license(MEMBERPRESS_LICENSE_KEY); + + $message = $act['message']; + $view = '/admin/errors'; + $callback = function () use ($view, $message) { + return MeprView::render($view, compact('message')); + }; + } catch (Exception $e) { + $view = '/admin/update/activation_warning'; + $error = $e->getMessage(); + $callback = function () use ($view, $error) { + return MeprView::render($view, compact('error')); + }; + } - $act = self::send_mothership_request("/license_keys/activate/{$license_key}", $args, 'post'); + add_action('admin_notices', $callback); + } + } - $mepr_options->mothership_license = $license_key; - $mepr_options->store(false); + /** + * Activate the license with the given key + * + * @param string $license_key The license key + * @return array The license data + * @throws Exception If there was an error activating the license + */ + public static function activate_license($license_key) + { + $mepr_options = MeprOptions::fetch(); - $option_key = "mepr_license_check_{$license_key}"; - delete_site_transient($option_key); - delete_option($option_key); + $args = [ + 'domain' => urlencode(MeprUtils::site_domain()), + 'product' => MEPR_EDITION, + ]; - delete_site_transient('mepr_update_info'); + $act = self::send_mothership_request("/license_keys/activate/{$license_key}", $args, 'post'); - do_action('mepr_license_activated_before_queue_update'); + $mepr_options->mothership_license = $license_key; + $mepr_options->store(false); - self::manually_queue_update(); + $option_key = "mepr_license_check_{$license_key}"; + delete_site_transient($option_key); + delete_option($option_key); - // Clear the cache of add-ons - delete_site_transient('mepr_addons'); - delete_site_transient('mepr_all_addons'); + delete_site_transient('mepr_update_info'); - do_action('mepr_license_activated', $act); + do_action('mepr_license_activated_before_queue_update'); - return $act; - } + self::manually_queue_update(); - /** - * Deactivate the license - * - * @return array - */ - public static function deactivate_license() { - $mepr_options = MeprOptions::fetch(); - $license_key = $mepr_options->mothership_license; - $act = array('message' => __('License key deactivated', 'memberpress')); + // Clear the cache of add-ons + delete_site_transient('mepr_addons'); + delete_site_transient('mepr_all_addons'); - if(!empty($mepr_options->mothership_license)) { - try { - $args = array( - 'domain' => urlencode(MeprUtils::site_domain()) - ); + do_action('mepr_license_activated', $act); - $act = self::send_mothership_request("/license_keys/deactivate/{$mepr_options->mothership_license}", $args, 'post'); - } - catch(Exception $e) { - // Catching here to allow invalid license keys to be deactivated - } + return $act; } - $mepr_options->mothership_license = ''; - $mepr_options->store(false); + /** + * Deactivate the license + * + * @return array + */ + public static function deactivate_license() + { + $mepr_options = MeprOptions::fetch(); + $license_key = $mepr_options->mothership_license; + $act = ['message' => __('License key deactivated', 'memberpress')]; + + if (!empty($mepr_options->mothership_license)) { + try { + $args = [ + 'domain' => urlencode(MeprUtils::site_domain()), + ]; + + $act = self::send_mothership_request("/license_keys/deactivate/{$mepr_options->mothership_license}", $args, 'post'); + } catch (Exception $e) { + // Catching here to allow invalid license keys to be deactivated + } + } - $option_key = "mepr_license_check_{$license_key}"; - delete_site_transient($option_key); - delete_option($option_key); + $mepr_options->mothership_license = ''; + $mepr_options->store(false); - delete_site_transient('mepr_update_info'); + $option_key = "mepr_license_check_{$license_key}"; + delete_site_transient($option_key); + delete_option($option_key); - do_action('mepr_license_deactivated_before_queue_update'); + delete_site_transient('mepr_update_info'); - self::manually_queue_update(); + do_action('mepr_license_deactivated_before_queue_update'); - // Don't need to check the mothership for this one ... we just deactivated - update_option('mepr_activated', false); + self::manually_queue_update(); - // Clear the cache of the license and add-ons - delete_site_transient('mepr_license_info'); - delete_site_transient('mepr_addons'); - delete_site_transient('mepr_all_addons'); + // Don't need to check the mothership for this one ... we just deactivated + update_option('mepr_activated', false); - do_action('mepr_license_deactivated', $act); + // Clear the cache of the license and add-ons + delete_site_transient('mepr_license_info'); + delete_site_transient('mepr_addons'); + delete_site_transient('mepr_all_addons'); - return $act; - } + do_action('mepr_license_deactivated', $act); - public static function queue_update($transient, $force=false, $rollback=false) { - if(empty($transient) || !is_object($transient)) { - return $transient; + return $act; } - $mepr_options = MeprOptions::fetch(); - - $update_info = get_site_transient('mepr_update_info'); - - if($force || (false === $update_info)) { - if(empty($mepr_options->mothership_license)) { - // Just here to query for the current version - $args = array(); - if( $mepr_options->edge_updates || ( defined( "MEMBERPRESS_EDGE" ) && MEMBERPRESS_EDGE ) ) { - $args['edge'] = 'true'; + public static function queue_update($transient, $force = false, $rollback = false) + { + if (empty($transient) || !is_object($transient)) { + return $transient; } - try { - $version_info = self::send_mothership_request( "/versions/latest/developer", $args ); - $curr_version = $version_info['version']; - $download_url = ''; - } - catch(Exception $e) { - return $transient; - } - } - else { - try { - $domain = urlencode(MeprUtils::site_domain()); - $args = compact('domain'); - - if( $mepr_options->edge_updates || ( defined( "MEMBERPRESS_EDGE" ) && MEMBERPRESS_EDGE ) ) { - $args['edge'] = 'true'; - } - - if($rollback) { - $args['curr_version'] = MEPR_VERSION; - $args['rollback'] = 'true'; - } - - $license_info = self::send_mothership_request("/versions/info/{$mepr_options->mothership_license}", $args, 'post'); - $curr_version = $license_info['version']; - $download_url = $license_info['url']; - - set_site_transient('mepr_license_info', $license_info, MeprUtils::hours(24)); - - if(MeprUtils::is_incorrect_edition_installed()) { - $download_url = ''; - } - } - catch(Exception $e) { - try { - // Just here to query for the current version - $args = array(); - if( $mepr_options->edge_updates || ( defined( "MEMBERPRESS_EDGE" ) && MEMBERPRESS_EDGE ) ) { - $args['edge'] = 'true'; - } + $mepr_options = MeprOptions::fetch(); - $version_info = self::send_mothership_request("/versions/latest/developer", $args); - $curr_version = $version_info['version']; - $download_url = ''; - } - catch(Exception $e) { - if(isset($transient->response[MEPR_PLUGIN_SLUG])) { - unset($transient->response[MEPR_PLUGIN_SLUG]); + $update_info = get_site_transient('mepr_update_info'); + + if ($force || (false === $update_info)) { + if (empty($mepr_options->mothership_license)) { + // Just here to query for the current version + $args = []; + if ($mepr_options->edge_updates || ( defined('MEMBERPRESS_EDGE') && MEMBERPRESS_EDGE )) { + $args['edge'] = 'true'; + } + + try { + $version_info = self::send_mothership_request('/versions/latest/developer', $args); + $curr_version = $version_info['version']; + $download_url = ''; + } catch (Exception $e) { + return $transient; + } + } else { + try { + $domain = urlencode(MeprUtils::site_domain()); + $args = compact('domain'); + + if ($mepr_options->edge_updates || ( defined('MEMBERPRESS_EDGE') && MEMBERPRESS_EDGE )) { + $args['edge'] = 'true'; + } + + if ($rollback) { + $args['curr_version'] = MEPR_VERSION; + $args['rollback'] = 'true'; + } + + $license_info = self::send_mothership_request("/versions/info/{$mepr_options->mothership_license}", $args, 'post'); + $curr_version = $license_info['version']; + $download_url = $license_info['url']; + + set_site_transient('mepr_license_info', $license_info, MeprUtils::hours(24)); + + if (MeprUtils::is_incorrect_edition_installed()) { + $download_url = ''; + } + } catch (Exception $e) { + try { + // Just here to query for the current version + $args = []; + if ($mepr_options->edge_updates || ( defined('MEMBERPRESS_EDGE') && MEMBERPRESS_EDGE )) { + $args['edge'] = 'true'; + } + + $version_info = self::send_mothership_request('/versions/latest/developer', $args); + $curr_version = $version_info['version']; + $download_url = ''; + } catch (Exception $e) { + if (isset($transient->response[MEPR_PLUGIN_SLUG])) { + unset($transient->response[MEPR_PLUGIN_SLUG]); + } + + self::check_license_activation(); + return $transient; + } + } } - self::check_license_activation(); - return $transient; - } + set_site_transient( + 'mepr_update_info', + compact('curr_version', 'download_url'), + MeprUtils::hours(12) + ); + + self::addons(false, true); + } else { + extract($update_info); } - } - set_site_transient( - 'mepr_update_info', - compact('curr_version', 'download_url'), - MeprUtils::hours(12) - ); + if (isset($curr_version) && ($rollback || version_compare($curr_version, MEPR_VERSION, '>'))) { + $transient->response[MEPR_PLUGIN_SLUG] = (object)[ + 'id' => $curr_version, + 'plugin' => MEPR_PLUGIN_SLUG, + 'slug' => 'memberpress', + 'new_version' => $curr_version, + 'url' => 'http://memberpress.com', + 'package' => $download_url, + ]; + } else { + unset($transient->response[MEPR_PLUGIN_SLUG]); + } - self::addons(false, true); - } - else { - extract( $update_info ); + self::check_license_activation(); + return $transient; } - if(isset($curr_version) && ($rollback || version_compare($curr_version, MEPR_VERSION, '>'))) { - $transient->response[MEPR_PLUGIN_SLUG] = (object)array( - 'id' => $curr_version, - 'plugin' => MEPR_PLUGIN_SLUG, - 'slug' => 'memberpress', - 'new_version' => $curr_version, - 'url' => 'http://memberpress.com', - 'package' => $download_url - ); + public static function manually_queue_update() + { + $transient = get_site_transient('update_plugins'); + set_site_transient('update_plugins', self::queue_update($transient, true)); } - else { - unset( $transient->response[MEPR_PLUGIN_SLUG] ); - } - - self::check_license_activation(); - return $transient; - } - - public static function manually_queue_update() { - $transient = get_site_transient('update_plugins'); - set_site_transient('update_plugins', self::queue_update($transient, true)); - } - public static function queue_button() { - ?> + public static function queue_button() + { + ?> - slug) && preg_match("#^(affiliate-royale)#", $args->slug)) { - // If AR is installed we allow it to take care of updates - if(is_plugin_active('affiliate-royale/affiliate-royale.php')) { - return $api; - } - } - elseif(isset($args->slug) && !preg_match("#^(memberpress|affiliate-royale)#", $args->slug)) { - return $api; - } + if (!isset($action) || $action != 'plugin_information') { + return $api; + } elseif (isset($args->slug) && preg_match('#^(affiliate-royale)#', $args->slug)) { + // If AR is installed we allow it to take care of updates + if (is_plugin_active('affiliate-royale/affiliate-royale.php')) { + return $api; + } + } elseif (isset($args->slug) && !preg_match('#^(memberpress|affiliate-royale)#', $args->slug)) { + return $api; + } - if($args->slug === 'memberpress') { - $mothership_slug = 'developer'; - $display_name = MEPR_DISPLAY_NAME; - $description = ' + if ($args->slug === 'memberpress') { + $mothership_slug = MEPR_EDITION; + $display_name = MEPR_DISPLAY_NAME; + $description = '

    The "All-In-One" Membership Plugin for WordPress

    MemberPress will help you build astounding WordPress membership sites, accept credit cards securely, control who sees your content, and sell digital downloads ... all without the difficult setup. @@ -614,267 +624,280 @@ public static function plugin_info($api, $action, $args) { With MemberPress you’ll be able to create powerful and compelling WordPress membership sites that leverage all of the great features of WordPress, WordPress plugins and other 3rd party services including content management, forums, and social communities.

    '; - $faq = 'You can read more about how to use MemberPress by visiting the user manual.'; - $changelog = 'You can read more about the latest changes to MemberPress by visiting the change log'; - } - else { - $mothership_slug = $args->slug; - $faq = 'You can read more about MemberPress Add-Ons by visiting the user manual.'; - $addon_info = self::mepr_addon_info($args->slug); - if(!empty($addon_info)) { - $display_name = $addon_info['Name']; - $description = $addon_info['Description']; - } - else { - $display_name = 'MemberPress Add-On'; - $description = 'MemberPress Add-On'; - } - if ( in_array( $display_name, array( - 'MemberPress Courses', - 'MemberPress Downloads', - 'MemberPress Developer Tools', - 'MemberPress Corporate Accounts', - 'MemberPress PDF Invoice', - 'MemberPress + BuddyPress Integration' - ) ) ) { - $plugin_slug = ($args->slug === 'memberpress-courses') ? 'memberpress-courses' : str_replace('memberpress', '', $addon_info['TextDomain']); - $changelog = "You can read more about the latest changes to $display_name by visiting the change log"; - } - } - - $mepr_options = MeprOptions::fetch(); - - $mothership_args = array(); - if( $mepr_options->edge_updates || (defined('MEMBERPRESS_EDGE') && MEMBERPRESS_EDGE)) { - $mothership_args['edge'] = 'true'; - } - $download_url = ''; - try { - if(empty($mepr_options->mothership_license)) { - $version_info = self::send_mothership_request("/versions/latest/{$mothership_slug}", $mothership_args); - } - else { - $mothership_args['domain'] = urlencode(MeprUtils::site_domain()); - $version_info = self::send_mothership_request("/versions/info/{$mothership_slug}/{$mepr_options->mothership_license}", $mothership_args); - $download_url = $version_info['url']; - } - } - catch(Exception $e) { - MeprUtils::error_log($e->getMessage()); - $version_info = array('version' => '', 'version_date' => ''); - } - $plugin_info = array( - 'slug' => $args->slug, - 'name' => $display_name, - 'author' => 'Caseproof, LLC', - 'author_profile' => 'http://blairwilliams.com', - 'contributors' => array( - array('display_name' => 'Caseproof', 'profile' => '', 'avatar' => '') - ), - 'homepage' => 'https://memberpress.com', - 'version' => $version_info['version'], - 'requires' => '3.8', - 'requires_php' => '5.3', - 'tested' => $wp_version, - 'compatibility' => array($wp_version => array($wp_version => array(100, 0, 0))), - 'last_updated' => $version_info['version_date'], - 'download_link' => $download_url, - 'sections' => array( - 'description' => $description, - 'faq' => $faq, - ), - 'banners' => array( - 'low' => MEPR_IMAGES_URL . '/banner-772x250.png', - 'high' => MEPR_IMAGES_URL . '/banner-1544x500.png' - ) - ); - - if(isset($changelog)) { - $plugin_info['sections']['changelog'] = $changelog; - } - - return (object)$plugin_info; - } + $faq = 'You can read more about how to use MemberPress by visiting the user manual.'; + $changelog = 'You can read more about the latest changes to MemberPress by visiting the change log'; + } else { + $mothership_slug = $args->slug; + $faq = 'You can read more about MemberPress Add-Ons by visiting the user manual.'; + $addon_info = self::mepr_addon_info($args->slug); + if (!empty($addon_info)) { + $display_name = $addon_info['Name']; + $description = $addon_info['Description']; + } else { + $display_name = 'MemberPress Add-On'; + $description = 'MemberPress Add-On'; + } + if ( + in_array($display_name, [ + 'MemberPress Courses', + 'MemberPress Downloads', + 'MemberPress Developer Tools', + 'MemberPress Corporate Accounts', + 'MemberPress PDF Invoice', + 'MemberPress + BuddyPress Integration', + ]) + ) { + $plugin_slug = ($args->slug === 'memberpress-courses') ? 'memberpress-courses' : str_replace('memberpress', '', $addon_info['TextDomain']); + $changelog = "You can read more about the latest changes to $display_name by visiting the change log"; + } + } - private static function mepr_addon_info($slug) { - static $curr_plugins; + $mepr_options = MeprOptions::fetch(); - if( !isset($curr_plugins) ) { - if( !function_exists( 'get_plugins' ) ) { - require_once( ABSPATH . '/wp-admin/includes/plugin.php' ); - } - $curr_plugins = get_plugins(); - wp_cache_delete('plugins', 'plugins'); - } + $mothership_args = []; + if ($mepr_options->edge_updates || (defined('MEMBERPRESS_EDGE') && MEMBERPRESS_EDGE)) { + $mothership_args['edge'] = 'true'; + } + $download_url = ''; + try { + if (empty($mepr_options->mothership_license)) { + $version_info = self::send_mothership_request("/versions/latest/{$mothership_slug}", $mothership_args); + } else { + $mothership_args['domain'] = urlencode(MeprUtils::site_domain()); + $version_info = self::send_mothership_request("/versions/info/{$mothership_slug}/{$mepr_options->mothership_license}", $mothership_args); + $download_url = $version_info['url']; + } + } catch (Exception $e) { + MeprUtils::debug_log($e->getMessage()); + $version_info = [ + 'version' => '', + 'version_date' => '', + ]; + } + $plugin_info = [ + 'slug' => $args->slug, + 'name' => $display_name, + 'author' => 'Caseproof, LLC', + 'author_profile' => 'http://blairwilliams.com', + 'contributors' => [ + [ + 'display_name' => 'Caseproof', + 'profile' => '', + 'avatar' => '', + ], + ], + 'homepage' => 'https://memberpress.com', + 'version' => $version_info['version'], + 'requires' => '3.8', + 'requires_php' => '5.3', + 'tested' => $wp_version, + 'compatibility' => [$wp_version => [$wp_version => [100, 0, 0]]], + 'last_updated' => $version_info['version_date'], + 'download_link' => $download_url, + 'sections' => [ + 'description' => $description, + 'faq' => $faq, + ], + 'banners' => [ + 'low' => MEPR_IMAGES_URL . '/banner-772x250.png', + 'high' => MEPR_IMAGES_URL . '/banner-1544x500.png', + ], + ]; + + if (isset($changelog)) { + $plugin_info['sections']['changelog'] = $changelog; + } - if(isset($curr_plugins[$slug . '/main.php'])) { - return $curr_plugins[$slug . '/main.php']; - } - elseif(isset($curr_plugins[$slug . "/{$slug}.php"])) { - return $curr_plugins[$slug . "/{$slug}.php"]; + return (object)$plugin_info; } - return ''; - } - - public static function send_mothership_request( $endpoint, $args=array(), $method='get', $blocking=true ) { - $domain = defined('MEPR_MOTHERSHIP_DOMAIN') ? MEPR_MOTHERSHIP_DOMAIN : 'https://mothership.caseproof.com'; - $mepr_options = MeprOptions::fetch(); - $uri = "{$domain}{$endpoint}"; + private static function mepr_addon_info($slug) + { + static $curr_plugins; - $arg_array = array( - 'method' => strtoupper($method), - 'body' => $args, - 'timeout' => 15, - 'blocking' => $blocking, - 'sslverify' => $mepr_options->sslverify, - ); + if (!isset($curr_plugins)) { + if (!function_exists('get_plugins')) { + require_once(ABSPATH . '/wp-admin/includes/plugin.php'); + } + $curr_plugins = get_plugins(); + wp_cache_delete('plugins', 'plugins'); + } - $resp = wp_remote_request($uri, $arg_array); + if (isset($curr_plugins[$slug . '/main.php'])) { + return $curr_plugins[$slug . '/main.php']; + } elseif (isset($curr_plugins[$slug . "/{$slug}.php"])) { + return $curr_plugins[$slug . "/{$slug}.php"]; + } - // If we're not blocking then the response is irrelevant - // So we'll just return true. - if($blocking == false) { - return true; + return ''; } - if(is_wp_error($resp)) { - throw new Exception(__('You had an HTTP error connecting to Caseproof\'s Mothership API', 'memberpress')); - } - else { - if(null !== ($json_res = json_decode($resp['body'], true))) { - if(isset($json_res['error'])) { - throw new Exception($json_res['error']); - } - else { - return $json_res; - } - } - else { - throw new Exception(__('Your License Key was invalid', 'memberpress')); - } - } + public static function send_mothership_request($endpoint, $args = [], $method = 'get', $blocking = true) + { + $domain = defined('MEPR_MOTHERSHIP_DOMAIN') ? MEPR_MOTHERSHIP_DOMAIN : 'https://mothership.caseproof.com'; + $mepr_options = MeprOptions::fetch(); + $uri = "{$domain}{$endpoint}"; + + $arg_array = [ + 'method' => strtoupper($method), + 'body' => $args, + 'timeout' => 15, + 'blocking' => $blocking, + 'sslverify' => $mepr_options->sslverify, + ]; + + $resp = wp_remote_request($uri, $arg_array); + + // If we're not blocking then the response is irrelevant + // So we'll just return true. + if ($blocking == false) { + return true; + } - return false; - } + if (is_wp_error($resp)) { + throw new Exception(__('You had an HTTP error connecting to Caseproof\'s Mothership API', 'memberpress')); + } else { + if (null !== ($json_res = json_decode($resp['body'], true))) { + if (isset($json_res['error'])) { + throw new Exception($json_res['error']); + } else { + return $json_res; + } + } else { + throw new Exception(__('Your License Key was invalid', 'memberpress')); + } + } - public static function enqueue_scripts($hook) { - // toplevel_page_memberpress will only be accessible if the plugin is not enabled - if($hook == 'memberpress_page_memberpress-options' || - (!MeprUpdateCtrl::is_activated() && $hook == 'toplevel_page_memberpress')) { - wp_enqueue_style('mepr-activate-css', MEPR_CSS_URL.'/admin-activate.css', array('mepr-settings-table-css'), MEPR_VERSION); + return false; } - } - public static function activation_warning() { - $mepr_options = MeprOptions::fetch(); - - if(empty($mepr_options->mothership_license) && - (!isset($_REQUEST['page']) || - !($_REQUEST['page']=='memberpress-options' || - (!self::is_activated() && $_REQUEST['page']=='memberpress')))) { - MeprView::render('/admin/update/activation_warning', get_defined_vars()); + public static function enqueue_scripts($hook) + { + // toplevel_page_memberpress will only be accessible if the plugin is not enabled + if ( + $hook == 'memberpress_page_memberpress-options' || + (!MeprUpdateCtrl::is_activated() && $hook == 'toplevel_page_memberpress') + ) { + wp_enqueue_style('mepr-activate-css', MEPR_CSS_URL . '/admin-activate.css', ['mepr-settings-table-css'], MEPR_VERSION); + } } - } - public static function mepr_edge_updates() { - if(!MeprUtils::is_mepr_admin() || !wp_verify_nonce($_POST['wpnonce'],'wp-edge-updates')) { - die(json_encode(array('error' => __('You do not have access.', 'memberpress')))); - } + public static function activation_warning() + { + $mepr_options = MeprOptions::fetch(); - if(!isset($_POST['edge'])) { - die(json_encode(array('error' => __('Edge updates couldn\'t be updated.', 'memberpress')))); + if ( + empty($mepr_options->mothership_license) && + (!isset($_REQUEST['page']) || + !($_REQUEST['page'] == 'memberpress-options' || + (!self::is_activated() && $_REQUEST['page'] == 'memberpress'))) + ) { + MeprView::render('/admin/update/activation_warning', get_defined_vars()); + } } - $mepr_options = MeprOptions::fetch(); - $mepr_options->edge_updates = ($_POST['edge']=='true'); - $mepr_options->store(false); + public static function mepr_edge_updates() + { + if (!MeprUtils::is_mepr_admin() || !wp_verify_nonce($_POST['wpnonce'], 'wp-edge-updates')) { + die(json_encode(['error' => __('You do not have access.', 'memberpress')])); + } - // Re-queue updates when this is checked - self::manually_queue_update(); + if (!isset($_POST['edge'])) { + die(json_encode(['error' => __('Edge updates couldn\'t be updated.', 'memberpress')])); + } - die(json_encode(array('state' => ($mepr_options->edge_updates ? 'true' : 'false')))); - } + $mepr_options = MeprOptions::fetch(); + $mepr_options->edge_updates = ($_POST['edge'] == 'true'); + $mepr_options->store(false); - public static function addons($return_object=false, $force=false, $all=false) { - $mepr_options = MeprOptions::fetch(); - $license = $mepr_options->mothership_license; - $transient = $all ? 'mepr_all_addons' : 'mepr_addons'; + // Re-queue updates when this is checked + self::manually_queue_update(); - if($force) { - delete_site_transient($transient); + die(json_encode(['state' => ($mepr_options->edge_updates ? 'true' : 'false')])); } - if(($addons = get_site_transient($transient))) { - $addons = json_decode($addons); - } - else { - $addons = array(); + public static function addons($return_object = false, $force = false, $all = false) + { + $mepr_options = MeprOptions::fetch(); + $license = $mepr_options->mothership_license; + $transient = $all ? 'mepr_all_addons' : 'mepr_addons'; - if(!empty($license)) { - try { - $domain = urlencode(MeprUtils::site_domain()); - $args = compact('domain'); + if ($force) { + delete_site_transient($transient); + } - if ($all) { - $args['all'] = 'true'; - } + if (($addons = get_site_transient($transient))) { + $addons = json_decode($addons); + } else { + $addons = []; + + if (!empty($license)) { + try { + $domain = urlencode(MeprUtils::site_domain()); + $args = compact('domain'); + + if ($all) { + $args['all'] = 'true'; + } + + if (defined('MEMBERPRESS_EDGE') && MEMBERPRESS_EDGE) { + $args['edge'] = 'true'; + } + $addons = self::send_mothership_request('/versions/addons/' . MEPR_EDITION . "/{$license}", $args); + } catch (Exception $e) { + // fail silently + MeprUtils::debug_log(MeprUtils::object_to_string($e)); + } + } - if(defined('MEMBERPRESS_EDGE') && MEMBERPRESS_EDGE) { $args['edge'] = 'true'; } - $addons = self::send_mothership_request('/versions/addons/'.MEPR_EDITION."/{$license}", $args); + $json = json_encode($addons); + set_site_transient($transient, $json, MeprUtils::hours(12)); + if ($return_object) { + $addons = json_decode($json); + } } - catch(Exception $e) { - // fail silently - MeprUtils::debug_log(MeprUtils::object_to_string($e)); - } - } - $json = json_encode($addons); - set_site_transient($transient, $json, MeprUtils::hours(12)); + return $addons; + } - if($return_object) { - $addons = json_decode($json); - } + public static function check_incorrect_edition() + { + if (MeprUtils::is_incorrect_edition_installed()) { + printf( + /* translators: %1$s: open link tag, %2$s: close link tag */ + ' ' . esc_html__('To restore automatic updates, %1$sinstall the correct edition%2$s of MemberPress.', 'memberpress') . '', + sprintf('', esc_url(admin_url('admin.php?page=memberpress-options#mepr-license'))), + '' + ); + } } - return $addons; - } - - public static function check_incorrect_edition() { - if(MeprUtils::is_incorrect_edition_installed()) { - printf( - /* translators: %1$s: open link tag, %2$s: close link tag */ - ' ' . esc_html__('To restore automatic updates, %1$sinstall the correct edition%2$s of MemberPress.', 'memberpress') . '', - sprintf('', esc_url(admin_url('admin.php?page=memberpress-options#mepr-license'))), - '' - ); + public static function clear_update_transients() + { + delete_site_transient('update_plugins'); + delete_site_transient('mepr_update_info'); + delete_site_transient('mepr_addons'); + delete_site_transient('mepr_all_addons'); } - } - public static function clear_update_transients() { - delete_site_transient('update_plugins'); - delete_site_transient('mepr_update_info'); - delete_site_transient('mepr_addons'); - delete_site_transient('mepr_all_addons'); - } + public static function get_license_info() + { + $mepr_options = MeprOptions::fetch(); + $license_info = get_site_transient('mepr_license_info'); - public static function get_license_info() { - $mepr_options = MeprOptions::fetch(); - $license_info = get_site_transient('mepr_license_info'); + if (!$license_info && !empty($mepr_options->mothership_license)) { + try { + $domain = urlencode(MeprUtils::site_domain()); + $args = compact('domain'); + $license_info = self::send_mothership_request("/versions/info/{$mepr_options->mothership_license}", $args, 'post'); - if(!$license_info && !empty($mepr_options->mothership_license)) { - try { - $domain = urlencode(MeprUtils::site_domain()); - $args = compact('domain'); - $license_info = self::send_mothership_request("/versions/info/{$mepr_options->mothership_license}", $args, 'post'); + set_site_transient('mepr_license_info', $license_info, MeprUtils::hours(24)); + } catch (Exception $e) { + // Fail silently, license info will return false. + } + } - set_site_transient('mepr_license_info', $license_info, MeprUtils::hours(24)); - } catch (Exception $e) { - //Fail silently, license info will return false. - } + return $license_info; } - - return $license_info; - } } //End class diff --git a/app/controllers/MeprUsageCtrl.php b/app/controllers/MeprUsageCtrl.php index 7ed1823..e3a390c 100644 --- a/app/controllers/MeprUsageCtrl.php +++ b/app/controllers/MeprUsageCtrl.php @@ -1,110 +1,86 @@ MeprUtils::weeks(1), + 'display' => __('MemberPress Snapshot Interval', 'memberpress'), + ]; - if(!($snapshot_timestamp = wp_next_scheduled('mepr_snapshot_worker'))) { - wp_schedule_event( time() + MeprUtils::weeks(1), 'mepr_snapshot_interval', 'mepr_snapshot_worker' ); - } + return $schedules; } - add_action('mepr_display_general_options', array($this,'display_options'), 99); - add_action('mepr-process-options', array($this,'save_options')); - - // Set disable senddata via AJAX for our popup - add_action('wp_ajax_mepr_disable_senddata', array($this, 'ajax_enable_or_disable_senddata')); - add_action('wp_ajax_mepr_enable_senddata', array($this, 'ajax_enable_or_disable_senddata')); - } - - public static function enqueue_scripts($hook) { - if(MeprUtils::is_mepr_admin()) { - $mepr_locals = array( - 'toggle_senddata_nonce' => wp_create_nonce('toggle_senddata'), - ); - wp_enqueue_script('mepr-usage-js', MEPR_JS_URL . '/admin_usage.js', array('jquery'), MEPR_VERSION, true); - wp_localize_script('mepr-usage-js', 'MeprUsage', $mepr_locals); + public function snapshot() + { + if (get_option('mepr_disable_senddata')) { + return; + } + + // This is here because we've learned through sad experience that we can't fully + // rely on WP-CRON to wait for an entire week so we check here to ensure we're ready. + $already_sent = MeprExpiringOption::get('sent_snapshot'); + if (!empty($already_sent)) { + MeprUtils::debug_log( + __('Your site is attempting to send too many snapshots, we\'ll put an end to that.', 'memberpress') + ); + return; + } + + $ep = + 'aHR0cHM6Ly9tZW1iZXJwcmVz' . + 'cy1hbmFseXRpY3MuaGVyb2t1' . + 'YXBwLmNvbS9zbmFwc2hvdA=='; + + $usage = new MeprUsage(); + $body = wp_json_encode($usage->snapshot()); + + $headers = [ + 'Accept' => 'application/json', + 'Content-Type' => 'application/json', + 'Content-Length' => strlen($body), + ]; + + // Setup variable for wp_remote_request + $post = [ + 'method' => 'POST', + 'headers' => $headers, + 'body' => $body, + ]; + + wp_remote_request(base64_decode($ep), $post); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode, Generic.Files.LineLength.TooLong + + // 6 days so we don't accidentally miss the weekly cron + MeprExpiringOption::set('sent_snapshot', 1, MeprUtils::days(6)); } - } - - public function intervals( $schedules ) { - $schedules['mepr_snapshot_interval'] = array( - 'interval' => MeprUtils::weeks(1), - 'display' => __('MemberPress Snapshot Interval', 'memberpress'), - ); - return $schedules; - } - - public function snapshot() { - $disable_senddata = get_option('mepr_disable_senddata'); - if($disable_senddata) { - return; - } + public function display_options() + { + $disable_senddata = get_option('mepr_disable_senddata'); + $hide_announcements = get_option('mepr_hide_announcements'); - // This is here because we've learned through sad experience that we can't fully - // rely on WP-CRON to wait for an entire week so we check here to ensure we're ready. - $already_sent = MeprExpiringOption::get('sent_snapshot'); - if(!empty($already_sent)) { - MeprUtils::debug_log(__('Your site is attempting to send too many snapshots, we\'ll put an end to that.', 'memberpress')); - return; + MeprView::render('admin/usage/option', compact('disable_senddata', 'hide_announcements')); } - $ep = - "aHR0cHM6Ly9tZW1iZXJwcmVz". - "cy1hbmFseXRpY3MuaGVyb2t1". - "YXBwLmNvbS9zbmFwc2hvdA=="; - - $usage = new MeprUsage(); - $body = json_encode($usage->snapshot()); - - $headers = array( - 'Accept' => 'application/json', - 'Content-Type' => 'application/json', - 'Content-Length' => strlen($body) - ); - - // Setup variable for wp_remote_request - $post = array( - 'method' => 'POST', - 'headers' => $headers, - 'body' => $body - ); - - $resp = wp_remote_request(base64_decode($ep), $post); - - // 6 days so we don't accidentally miss the weekly cron - MeprExpiringOption::set('sent_snapshot',1,MeprUtils::days(6)); - } - - public function display_options() { - $disable_senddata = get_option('mepr_disable_senddata'); - $hide_announcements = get_option('mepr_hide_announcements'); - MeprView::render('admin/usage/option', compact('disable_senddata', 'hide_announcements')); - } - - public function save_options($params) { - update_option('mepr_disable_senddata',isset($params['mepr_disable_senddata'])); - update_option('mepr_hide_announcements', isset($params['mepr_hide_announcements'])); - } - - public function ajax_enable_or_disable_senddata() { - check_ajax_referer('toggle_usage', 'toggle_usage_nonce'); - - if(!MeprUtils::is_mepr_admin() || !MeprUtils::is_post_request()) { - MeprUtils::exit_with_status(403,__('Forbidden', 'memberpress')); + public function save_options($params) + { + update_option('mepr_disable_senddata', !isset($params['mepr_enable_senddata'])); + update_option('mepr_hide_announcements', isset($params['mepr_hide_announcements'])); } - - update_option('mepr_disable_senddata',($_POST['action']=='mepr_disable_senddata')); - - $message = __('Disable Send Data option was updated successfully', 'memberpress'); - MeprUtils::exit_with_status(200,json_encode(compact('message'))); - } - -} // End class +} diff --git a/app/controllers/MeprUsersCtrl.php b/app/controllers/MeprUsersCtrl.php index 25de2fe..110b503 100644 --- a/app/controllers/MeprUsersCtrl.php +++ b/app/controllers/MeprUsersCtrl.php @@ -1,759 +1,815 @@ custom_fields ) ) { - foreach ( $mepr_options->custom_fields as $field ) { - if ( 'tel' === $field->field_type && $field->show_on_signup ) { - $has_phone = true; - break; + if (! empty($mepr_options->custom_fields)) { + foreach ($mepr_options->custom_fields as $field) { + if ('tel' === $field->field_type && $field->show_on_signup) { + $has_phone = true; + break; + } + } } - } - } - // Check if there's a phone field - if ( $has_phone ) { - wp_enqueue_style( 'mepr-phone-css', MEPR_CSS_URL . '/intlTelInput.min.css', '', '16.0.0' ); - wp_enqueue_style( 'mepr-tel-config-css', MEPR_CSS_URL . '/tel_input.css', '', MEPR_VERSION ); - wp_enqueue_script( 'mepr-phone-js', MEPR_JS_URL . '/intlTelInput.js', '', '16.0.0', true ); - wp_enqueue_script( 'mepr-tel-config-js', MEPR_JS_URL . '/tel_input.js', array( 'mepr-phone-js' ), MEPR_VERSION, true ); - wp_localize_script( 'mepr-tel-config-js', 'meprTel', MeprHooks::apply_filters( 'mepr-phone-input-config', array( - 'defaultCountry' => strtolower( get_option( 'mepr_biz_country' ) ), - 'utilsUrl' => MEPR_JS_URL . '/intlTelInputUtils.js', - 'onlyCountries' => '' - ) ) ); + // Check if there's a phone field + if ($has_phone) { + wp_enqueue_style('mepr-phone-css', MEPR_CSS_URL . '/intlTelInput.min.css', '', '16.0.0'); + wp_enqueue_style('mepr-tel-config-css', MEPR_CSS_URL . '/tel_input.css', '', MEPR_VERSION); + wp_enqueue_script('mepr-phone-js', MEPR_JS_URL . '/intlTelInput.js', '', '16.0.0', true); + wp_enqueue_script('mepr-tel-config-js', MEPR_JS_URL . '/tel_input.js', ['mepr-phone-js'], MEPR_VERSION, true); + wp_localize_script('mepr-tel-config-js', 'meprTel', MeprHooks::apply_filters('mepr-phone-input-config', [ + 'defaultCountry' => strtolower(get_option('mepr_biz_country')), + 'utilsUrl' => MEPR_JS_URL . '/intlTelInputUtils.js', + 'onlyCountries' => '', + ])); + } } - } - - public static function register_mepr_data_eraser($erasers) { - $erasers[MEPR_PLUGIN_NAME] = array( - 'eraser_friendly_name' => MEPR_PLUGIN_NAME, - 'callback' => array('MeprUsersCtrl', 'erase_pii'), - ); - - return $erasers; - } - - public static function erase_pii($email, $page = 1) { - $user = get_user_by('email', $email); - if(!$user) { return; } - - delete_user_meta($user->ID, 'mepr_vat_number'); - delete_user_meta($user->ID, 'mepr-geo-country'); - delete_user_meta($user->ID, 'mepr-address-one'); - delete_user_meta($user->ID, 'mepr-address-two'); - delete_user_meta($user->ID, 'mepr-address-city'); - delete_user_meta($user->ID, 'mepr-address-state'); - delete_user_meta($user->ID, 'mepr-address-zip'); - delete_user_meta($user->ID, 'mepr-address-country'); - - return array( - 'items_removed' => true, - 'items_retained' => false, - 'messages' => array(), - 'done' => true, - ); - } - - public static function display_unauthorized_page() { - if(MeprUtils::is_user_logged_in()) - MeprView::render('/shared/member_unauthorized', get_defined_vars()); - else - MeprView::render('/shared/unauthorized', get_defined_vars()); - } - - public static function resend_welcome_email_callback() { - $ajax_nonce = $_REQUEST['nonce']; - $mepr_options = MeprOptions::fetch(); - - if(wp_verify_nonce($ajax_nonce, 'mepr_resend_welcome_email')) { - if(MeprUtils::is_logged_in_and_an_admin()) { - $usr = new MeprUser($_REQUEST['uid']); - - // Get the most recent transaction - $txns = MeprTransaction::get_all_complete_by_user_id( $usr->ID, - 'created_at DESC', /* $order_by='' */ - '1', /* $limit='' */ - false, /* $count=false */ - false, /* $exclude_expired=false */ - true /* $include_confirmations=false */ ); - - if(count($txns) <= 0) - die(__('This user hasn\'t purchased any memberships - so no email will be sent.', 'memberpress')); - - $txn = new MeprTransaction($txns[0]->id); - $params = MeprTransactionsHelper::get_email_params($txn); - $usr = $txn->user(); - - try { - $uemail = MeprEmailFactory::fetch('MeprUserWelcomeEmail'); - $uemail->to = $usr->formatted_email(); - $uemail->send($params); - die(__('Message Sent', 'memberpress')); - } - catch( Exception $e ) { - die(__('There was an issue sending the email', 'memberpress')); - } - } - die(__('Why you creepin\'?', 'memberpress')); + + public static function register_mepr_data_eraser($erasers) + { + $erasers[MEPR_PLUGIN_NAME] = [ + 'eraser_friendly_name' => MEPR_PLUGIN_NAME, + 'callback' => ['MeprUsersCtrl', 'erase_pii'], + ]; + + return $erasers; } - die(__('Cannot resend message', 'memberpress')); - } - - public static function nullify_records_on_delete($id) { - MeprTransaction::nullify_user_id_on_delete($id); - MeprSubscription::nullify_user_id_on_delete($id); - - return $id; - } - - public static function email_users_with_expiring_transactions() { - return MeprUser::email_users_with_expiring_transactions(); - } - - //public static function unschedule_email_users_with_expiring_transactions() - //{ - // if($t = wp_next_scheduled('mepr_schedule_renew_emails')) - // wp_unschedule_event($t, 'mepr_schedule_renew_emails'); - //} - - public static function enqueue_scripts($hook) { - $wp_scripts = new WP_Scripts(); - $ui = $wp_scripts->query('jquery-ui-core'); - $url = "//ajax.googleapis.com/ajax/libs/jqueryui/{$ui->ver}/themes/smoothness/jquery-ui.css"; - - if($hook == 'user-edit.php' || $hook == 'profile.php') { - wp_enqueue_style('mepr-jquery-ui-smoothness', $url); - wp_enqueue_style('jquery-ui-timepicker-addon', MEPR_CSS_URL.'/jquery-ui-timepicker-addon.css', array('mepr-jquery-ui-smoothness')); - - wp_register_script('mepr-timepicker-js', MEPR_JS_URL.'/jquery-ui-timepicker-addon.js', array('jquery-ui-datepicker')); - wp_register_script('mepr-date-picker-js', MEPR_JS_URL.'/date_picker.js', array('mepr-timepicker-js'), MEPR_VERSION); - wp_enqueue_script('mp-i18n', MEPR_JS_URL.'/i18n.js', array('jquery')); - wp_localize_script('mp-i18n', 'MeprI18n', array('states' => MeprUtils::states())); - wp_enqueue_script('mp-edit-user', MEPR_JS_URL.'/admin_profile.js', array('jquery', 'suggest', 'mp-i18n'), MEPR_VERSION); + + public static function erase_pii($email, $page = 1) + { + $user = get_user_by('email', $email); + if (!$user) { + return; + } + + delete_user_meta($user->ID, 'mepr_vat_number'); + delete_user_meta($user->ID, 'mepr-geo-country'); + delete_user_meta($user->ID, 'mepr-address-one'); + delete_user_meta($user->ID, 'mepr-address-two'); + delete_user_meta($user->ID, 'mepr-address-city'); + delete_user_meta($user->ID, 'mepr-address-state'); + delete_user_meta($user->ID, 'mepr-address-zip'); + delete_user_meta($user->ID, 'mepr-address-country'); + + return [ + 'items_removed' => true, + 'items_retained' => false, + 'messages' => [], + 'done' => true, + ]; + } + + public static function display_unauthorized_page() + { + if (MeprUtils::is_user_logged_in()) { + MeprView::render('/shared/member_unauthorized', get_defined_vars()); + } else { + MeprView::render('/shared/unauthorized', get_defined_vars()); + } } - } - public static function extra_profile_fields($wpuser) { - $mepr_options = MeprOptions::fetch(); - $user = new MeprUser($wpuser->ID); + public static function resend_welcome_email_callback() + { + $ajax_nonce = $_REQUEST['nonce']; + $mepr_options = MeprOptions::fetch(); - MeprView::render("/admin/users/extra_profile_fields", get_defined_vars()); - } + if (wp_verify_nonce($ajax_nonce, 'mepr_resend_welcome_email')) { + if (MeprUtils::is_logged_in_and_an_admin()) { + $usr = new MeprUser($_REQUEST['uid']); - public static function save_extra_profile_fields($user_id, $validated=false, $product=false, $is_signup=false, $selected=[]) { - $mepr_options = MeprOptions::fetch(); - $errors = array(); - $user = new MeprUser($user_id); + // Get the most recent transaction + $txns = MeprTransaction::get_all_complete_by_user_id( + $usr->ID, + 'created_at DESC', /* $order_by='' */ + '1', /* $limit='' */ + false, /* $count=false */ + false, /* $exclude_expired=false */ + true /* $include_confirmations=false */ + ); - if(isset($_POST[MeprUser::$user_message_str])) { - update_user_meta($user_id, MeprUser::$user_message_str, (string)wp_kses_post($_POST[MeprUser::$user_message_str])); - } + if (count($txns) <= 0) { + die(__('This user hasn\'t purchased any memberships - so no email will be sent.', 'memberpress')); + } - //Get the right custom fields - if(is_admin() && MeprUtils::is_mepr_admin()) { //An admin is editing the user's profile, so let's save all fields - $custom_fields = $mepr_options->custom_fields; - } - elseif($product !== false) { - if($product->customize_profile_fields) { - $custom_fields = $product->custom_profile_fields(); - } - else { - $custom_fields = $mepr_options->custom_fields; - } - } - else { - $custom_fields = $user->custom_profile_fields(); + $txn = new MeprTransaction($txns[0]->id); + $params = MeprTransactionsHelper::get_email_params($txn); + $usr = $txn->user(); + + try { + $uemail = MeprEmailFactory::fetch('MeprUserWelcomeEmail'); + $uemail->to = $usr->formatted_email(); + $uemail->send($params); + die(__('Message Sent', 'memberpress')); + } catch (Exception $e) { + die(__('There was an issue sending the email', 'memberpress')); + } + } + die(__('Why you creepin\'?', 'memberpress')); + } + die(__('Cannot resend message', 'memberpress')); } - //Since we use user_* for these, we need to artifically set the $_POST keys correctly for this to work - if(!isset($_POST['first_name']) || empty($_POST['first_name'])) { - $_POST['first_name'] = (isset($_POST['user_first_name']))?sanitize_text_field(wp_unslash($_POST['user_first_name'])):''; + public static function nullify_records_on_delete($id) + { + MeprTransaction::nullify_user_id_on_delete($id); + MeprSubscription::nullify_user_id_on_delete($id); + + return $id; } - if(!isset($_POST['last_name']) || empty($_POST['last_name'])) { - $_POST['last_name'] = (isset($_POST['user_last_name']))?sanitize_text_field(wp_unslash($_POST['user_last_name'])):''; + public static function email_users_with_expiring_transactions() + { + return MeprUser::email_users_with_expiring_transactions(); } - $custom_fields[] = (object)array('field_key' => 'first_name', 'field_type' => 'text'); - $custom_fields[] = (object)array('field_key' => 'last_name', 'field_type' => 'text'); + // public static function unschedule_email_users_with_expiring_transactions() + // { + // if($t = wp_next_scheduled('mepr_schedule_renew_emails')) + // wp_unschedule_event($t, 'mepr_schedule_renew_emails'); + // } + public static function enqueue_scripts($hook) + { + $wp_scripts = new WP_Scripts(); + $ui = $wp_scripts->query('jquery-ui-core'); + $url = "//ajax.googleapis.com/ajax/libs/jqueryui/{$ui->ver}/themes/smoothness/jquery-ui.css"; - if($mepr_options->show_address_fields) { - if(!$product || !$product->disable_address_fields) { - $custom_fields = array_merge($mepr_options->address_fields, $custom_fields); - } - } + if ($hook == 'user-edit.php' || $hook == 'profile.php') { + wp_enqueue_style('mepr-jquery-ui-smoothness', $url); + wp_enqueue_style('jquery-ui-timepicker-addon', MEPR_CSS_URL . '/jquery-ui-timepicker-addon.css', ['mepr-jquery-ui-smoothness']); - if($mepr_options->require_privacy_policy && isset($_POST['mepr_agree_to_privacy_policy'])) { - update_user_meta($user_id, 'mepr_agree_to_privacy_policy', true); + wp_register_script('mepr-timepicker-js', MEPR_JS_URL . '/jquery-ui-timepicker-addon.js', ['jquery-ui-datepicker']); + wp_register_script('mepr-date-picker-js', MEPR_JS_URL . '/date_picker.js', ['mepr-timepicker-js'], MEPR_VERSION); + wp_enqueue_script('mp-i18n', MEPR_JS_URL . '/i18n.js', ['jquery']); + wp_localize_script('mp-i18n', 'MeprI18n', ['states' => MeprUtils::states()]); + wp_enqueue_script('mp-edit-user', MEPR_JS_URL . '/admin_profile.js', ['jquery', 'suggest', 'mp-i18n'], MEPR_VERSION); + } } - if($mepr_options->require_tos && isset($_POST['mepr_agree_to_tos'])) { - update_user_meta($user_id, 'mepr_agree_to_tos', true); + public static function extra_profile_fields($wpuser) + { + $mepr_options = MeprOptions::fetch(); + $user = new MeprUser($wpuser->ID); + + MeprView::render('/admin/users/extra_profile_fields', get_defined_vars()); } - // Even though the validate_extra_profile_fields function will show an error on the - // dashboard profile. It doesn't prevent the profile from saved because - // user_profile_update_errors is called after the account has been saved which is really lame - // So let's take care of that here. $validated should ALWAYS be true, except in this one case - if(!$validated) { $errors = self::validate_extra_profile_fields(); } - - if(empty($errors)) { - // TODO: move this somewhere it makes more sense - if(isset($_POST['mepr-geo-country'])) { - update_user_meta($user_id, 'mepr-geo-country', sanitize_text_field($_POST['mepr-geo-country'])); - } - - foreach($custom_fields as $line) { - // Allows fields to be selectively saved. - if( ! empty( $selected ) && ! in_array( $line->field_key, $selected ) ) { - continue; - } - - //Don't do anything if this field isn't shown during signup, and this is a signup - if($is_signup && isset($line->show_on_signup) && !$line->show_on_signup) { continue; } - //Only allow admin to update if it is not shown in account - if(!is_admin() && !$is_signup && isset($line->show_in_account) && !$line->show_in_account) { continue; } - - if(isset($_POST[$line->field_key]) && !empty($_POST[$line->field_key])) { - if(in_array($line->field_type, array('checkboxes', 'multiselect'))) { - update_user_meta($user_id, $line->field_key, array_map('sanitize_text_field', array_filter( $_POST[$line->field_key] ) )); - } - elseif($line->field_type == 'textarea') { - update_user_meta($user_id, $line->field_key, sanitize_textarea_field($_POST[$line->field_key])); - } - else { - update_user_meta($user_id, $line->field_key, sanitize_text_field($_POST[$line->field_key])); - } - } - else { - if($line->field_type == 'file') { - if(isset($_FILES[$line->field_key]['error']) && $_FILES[$line->field_key]['error'] != UPLOAD_ERR_NO_FILE) { - $file = $_FILES[$line->field_key]; - - if(!empty($file['name']) && !empty($file['size']) && !empty($file['tmp_name']) && is_uploaded_file($file['tmp_name'])) { - add_filter('upload_dir', 'MeprUsersHelper::get_upload_dir'); - add_filter('upload_mimes', 'MeprUsersHelper::get_allowed_mime_types'); - - $pathinfo = pathinfo($file['name']); - $filename = sanitize_file_name($pathinfo['filename'] . '_' . uniqid() . '.' . $pathinfo['extension']); - $contents = @file_get_contents($file['tmp_name']); - - if($contents !== false) { - $file = wp_upload_bits($filename, null, $contents); - - if(isset($file['url'])) { - update_user_meta($user_id, $line->field_key, esc_url_raw($file['url'])); - } - } + public static function save_extra_profile_fields($user_id, $validated = false, $product = false, $is_signup = false, $selected = []) + { + $mepr_options = MeprOptions::fetch(); + $errors = []; + $user = new MeprUser($user_id); + + if (isset($_POST[MeprUser::$user_message_str])) { + update_user_meta($user_id, MeprUser::$user_message_str, (string)wp_kses_post($_POST[MeprUser::$user_message_str])); + } - remove_filter('upload_mimes', 'MeprUsersHelper::get_allowed_mime_types'); - remove_filter('upload_dir', 'MeprUsersHelper::get_upload_dir'); - } + // Get the right custom fields + if (is_admin() && MeprUtils::is_mepr_admin()) { // An admin is editing the user's profile, so let's save all fields + $custom_fields = $mepr_options->custom_fields; + } elseif ($product !== false) { + if ($product->customize_profile_fields) { + $custom_fields = $product->custom_profile_fields(); + } else { + $custom_fields = $mepr_options->custom_fields; } - } - elseif($line->field_type == 'checkbox') { - update_user_meta($user_id, $line->field_key, false); - } - elseif(in_array($line->field_type, array('checkboxes', 'multiselect'))) { - update_user_meta($user_id, $line->field_key, array()); - } - else { - update_user_meta($user_id, $line->field_key, ''); - } - } - } - - if(!$is_signup) { - MeprEvent::record('member-account-updated', $user); - } - - MeprHooks::do_action('mepr_user_account_saved', $user); - - return true; - } + } else { + $custom_fields = $user->custom_profile_fields(); + } - return false; - } - - //Should be moved to the Model eventually - //This should be run before MeprUsersCtrl::save_extra_profile_fields is run - public static function validate_extra_profile_fields( $errors = null, - $update = null, - $user = null, - $is_signup = false, - $product = false, - $selected = [] ) { - $mepr_options = MeprOptions::fetch(); - $errs = array(); - - // Prevent checking when adding a new user via WP's New User system - // or if an admin is editing the profile in the dashboard - if($update === false || ($update !== false && MeprUtils::is_mepr_admin())) { return $errs; } - - //Get the right custom fields - if($is_signup && $product !== false) { - if($product->customize_profile_fields) { - $custom_fields = $product->custom_profile_fields(); - } - else { - $custom_fields = $mepr_options->custom_fields; - } - } - elseif(!is_null($user)) { - $mepr_user = new MeprUser($user->ID); - $custom_fields = $mepr_user->custom_profile_fields(); - } - else { - $custom_fields = $mepr_options->custom_fields; - } + // Since we use user_* for these, we need to artifically set the $_POST keys correctly for this to work + if (!isset($_POST['first_name']) || empty($_POST['first_name'])) { + $_POST['first_name'] = (isset($_POST['user_first_name'])) ? sanitize_text_field(wp_unslash($_POST['user_first_name'])) : ''; + } - //If the address line is set in POST then we should validate it - if(isset($_POST['mepr-address-one'])) { - $custom_fields = array_merge($mepr_options->address_fields, $custom_fields); - } + if (!isset($_POST['last_name']) || empty($_POST['last_name'])) { + $_POST['last_name'] = (isset($_POST['user_last_name'])) ? sanitize_text_field(wp_unslash($_POST['user_last_name'])) : ''; + } - foreach($custom_fields as $line) { - // Allows fields to be selectively validated. - if( ! empty( $selected ) && ! in_array( $line->field_key, $selected ) ) { - continue; - } - - // If we're processing a signup and the custom field is not set - // to show on signup we need to make sure it isn't required - if($is_signup && $line->required && !$line->show_on_signup) { - $line->required = false; - } - elseif(!$is_signup && !is_admin() && isset($line->show_in_account) && !$line->show_in_account) { - //Account page shouldn't show errors if the fields have been hidden from the account page - $line->required = false; - } - - if((!isset($_POST[$line->field_key]) || (empty($_POST[$line->field_key]) && $_POST[$line->field_key] != '0')) && $line->required && 'file' != $line->field_type) { - $errs[$line->field_key] = sprintf(__('%s is required.', 'memberpress'), stripslashes($line->field_name)); - - //This allows us to run this on dashboard profile fields as well as front end - if(is_object($errors)) { - $errors->add($line->field_key, sprintf(__('%s is required.', 'memberpress'), stripslashes($line->field_name))); - } - } - - if('file' == $line->field_type) { - $file_provided = isset($_FILES[$line->field_key]['error']) && $_FILES[$line->field_key]['error'] != UPLOAD_ERR_NO_FILE; - - if($file_provided) { - // Validate new file upload - $file = $_FILES[$line->field_key]; - - if(empty($file['tmp_name']) || empty($file['name']) || empty($file['size'])) { - if($line->required) { - $errs[$line->field_key] = sprintf(__('%s is required.', 'memberpress'), stripslashes($line->field_name)); - } - } - elseif($file['error'] == UPLOAD_ERR_OK) { - add_filter('upload_mimes', 'MeprUsersHelper::get_allowed_mime_types'); - $wp_filetype = wp_check_filetype($file['name']); - remove_filter('upload_mimes', 'MeprUsersHelper::get_allowed_mime_types'); - - if(!$wp_filetype['ext'] && !current_user_can('unfiltered_upload')) { - $errs[$line->field_key] = sprintf(__('%s file type not allowed.', 'memberpress'), stripslashes($line->field_name)); - } - } - else { - $errs[$line->field_key] = sprintf(__('%s could not be uploaded.', 'memberpress'), stripslashes($line->field_name)); - } - } - else { - // Validate existing file - if($line->required) { - $file = get_user_meta(get_current_user_id(), $line->field_key, true); - - if(empty($file)) { - $errs[$line->field_key] = sprintf(__('%s is required.', 'memberpress'), stripslashes($line->field_name)); + $custom_fields[] = (object)[ + 'field_key' => 'first_name', + 'field_type' => 'text', + ]; + $custom_fields[] = (object)[ + 'field_key' => 'last_name', + 'field_type' => 'text', + ]; + + if ($mepr_options->show_address_fields) { + if (!$product || !$product->disable_address_fields) { + $custom_fields = array_merge($mepr_options->address_fields, $custom_fields); } - } } - } - if( $line->required && 'email' == $line->field_type && !empty($_POST[$line->field_key]) ){ - if( !is_email(stripcslashes($_POST[$line->field_key])) ){ - $errs[$line->field_key] = sprintf(__('%s is not a valid email address.', 'memberpress'), stripslashes($line->field_name)); + if ($mepr_options->require_privacy_policy && isset($_POST['mepr_agree_to_privacy_policy'])) { + update_user_meta($user_id, 'mepr_agree_to_privacy_policy', true); } - } - if( $line->required && 'url' == $line->field_type && !empty($_POST[$line->field_key]) ){ - if( !preg_match('/(https?:\/\/)?(www\.)?[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_\+.~#()?&\/\/=]*)/', $_POST[$line->field_key]) ){ - $errs[$line->field_key] = sprintf(__('%s is not a valid URL.', 'memberpress'), stripslashes($line->field_name)); + if ($mepr_options->require_tos && isset($_POST['mepr_agree_to_tos'])) { + update_user_meta($user_id, 'mepr_agree_to_tos', true); } - } - } - return $errs; - } + // Even though the validate_extra_profile_fields function will show an error on the + // dashboard profile. It doesn't prevent the profile from saved because + // user_profile_update_errors is called after the account has been saved which is really lame + // So let's take care of that here. $validated should ALWAYS be true, except in this one case + if (!$validated) { + $errors = self::validate_extra_profile_fields(); + } - public static function user_search() { - if(!MeprUtils::is_mepr_admin()) { - die('-1'); - } + if (empty($errors)) { + // TODO: move this somewhere it makes more sense + if (isset($_POST['mepr-geo-country'])) { + update_user_meta($user_id, 'mepr-geo-country', sanitize_text_field($_POST['mepr-geo-country'])); + } - // jQuery suggest plugin has already trimmed and escaped user input (\ becomes \\) - // so we just need to sanitize the username - $s = sanitize_user($_GET['q']); + foreach ($custom_fields as $line) { + // Allows fields to be selectively saved. + if (! empty($selected) && ! in_array($line->field_key, $selected)) { + continue; + } - if(strlen($s) < 2) { - die; // require 2 chars for matching - } + // Don't do anything if this field isn't shown during signup, and this is a signup + if ($is_signup && isset($line->show_on_signup) && !$line->show_on_signup) { + continue; + } + // Only allow admin to update if it is not shown in account + if (!is_admin() && !$is_signup && isset($line->show_in_account) && !$line->show_in_account) { + continue; + } - $users = get_users(array('search' => "*$s*")); + if (isset($_POST[$line->field_key]) && !empty($_POST[$line->field_key])) { + if (in_array($line->field_type, ['checkboxes', 'multiselect'])) { + update_user_meta($user_id, $line->field_key, array_map('sanitize_text_field', array_filter($_POST[$line->field_key]))); + } elseif ($line->field_type == 'textarea') { + update_user_meta($user_id, $line->field_key, sanitize_textarea_field($_POST[$line->field_key])); + } else { + update_user_meta($user_id, $line->field_key, sanitize_text_field($_POST[$line->field_key])); + } + } else { + if ($line->field_type == 'file') { + if (isset($_FILES[$line->field_key]['error']) && $_FILES[$line->field_key]['error'] != UPLOAD_ERR_NO_FILE) { + $file = $_FILES[$line->field_key]; + + if (!empty($file['name']) && !empty($file['size']) && !empty($file['tmp_name']) && is_uploaded_file($file['tmp_name'])) { + add_filter('upload_dir', 'MeprUsersHelper::get_upload_dir'); + add_filter('upload_mimes', 'MeprUsersHelper::get_allowed_mime_types'); + + $pathinfo = pathinfo($file['name']); + $filename = sanitize_file_name($pathinfo['filename'] . '_' . uniqid() . '.' . $pathinfo['extension']); + $contents = @file_get_contents($file['tmp_name']); + + if ($contents !== false) { + $file = wp_upload_bits($filename, null, $contents); + + if (isset($file['url'])) { + update_user_meta($user_id, $line->field_key, esc_url_raw($file['url'])); + } + } + + remove_filter('upload_mimes', 'MeprUsersHelper::get_allowed_mime_types'); + remove_filter('upload_dir', 'MeprUsersHelper::get_upload_dir'); + } + } + } elseif ($line->field_type == 'checkbox') { + update_user_meta($user_id, $line->field_key, false); + } elseif (in_array($line->field_type, ['checkboxes', 'multiselect'])) { + update_user_meta($user_id, $line->field_key, []); + } else { + update_user_meta($user_id, $line->field_key, ''); + } + } + } - MeprView::render('/admin/users/search', get_defined_vars()); - die(); - } + if (!$is_signup) { + MeprEvent::record('member-account-updated', $user); + } - //Add extra columns to the Users list table - public static function add_extra_user_columns($columns) { - $columns['mepr_products'] = __('Active Memberships', 'memberpress'); - $columns['mepr_registered'] = __('Registered', 'memberpress'); - $columns['mepr_last_login'] = __('Last Login', 'memberpress'); - $columns['mepr_num_logins'] = __('# Logins', 'memberpress'); + MeprHooks::do_action('mepr_user_account_saved', $user); - return $columns; - } + return true; + } - //Tells WP which columns should be sortable - public static function sortable_extra_user_columns($cols) { - $cols['mepr_registered'] = 'user_registered'; - $cols['mepr_last_login'] = 'last_login'; - $cols['mepr_num_logins'] = 'num_logins'; + return false; + } + + // Should be moved to the Model eventually + // This should be run before MeprUsersCtrl::save_extra_profile_fields is run + public static function validate_extra_profile_fields( + $errors = null, + $update = null, + $user = null, + $is_signup = false, + $product = false, + $selected = [] + ) { + $mepr_options = MeprOptions::fetch(); + $errs = []; + + // Prevent checking when adding a new user via WP's New User system + // or if an admin is editing the profile in the dashboard + if ($update === false || ($update !== false && MeprUtils::is_mepr_admin())) { + return $errs; + } - return $cols; - } + // Get the right custom fields + if ($is_signup && $product !== false) { + if ($product->customize_profile_fields) { + $custom_fields = $product->custom_profile_fields(); + } else { + $custom_fields = $mepr_options->custom_fields; + } + } elseif (!is_null($user)) { + $mepr_user = new MeprUser($user->ID); + $custom_fields = $mepr_user->custom_profile_fields(); + } else { + $custom_fields = $mepr_options->custom_fields; + } - //This allows us to sort the column properly behind the scenes - public static function extra_user_columns_query_override($query) { - global $wpdb; - $vars = $query->query_vars; - $mepr_db = new MeprDb(); + // If the address line is set in POST then we should validate it + if (isset($_POST['mepr-address-one'])) { + $custom_fields = array_merge($mepr_options->address_fields, $custom_fields); + } - if(isset($vars['orderby']) && $vars['orderby'] == 'last_login') { - $query->query_fields .= ", (SELECT e.created_at FROM {$mepr_db->events} AS e WHERE {$wpdb->users}.ID = e.evt_id AND e.evt_id_type='" . MeprEvent::$users_str . "' AND e.event = '" . MeprEvent::$login_event_str . "' ORDER BY e.created_at DESC LIMIT 1) AS last_login"; - $query->query_orderby = "ORDER BY last_login {$vars['order']}"; - } + foreach ($custom_fields as $line) { + // Allows fields to be selectively validated. + if (! empty($selected) && ! in_array($line->field_key, $selected)) { + continue; + } - if(isset($vars['orderby']) && $vars['orderby'] == 'num_logins') { - $query->query_fields .= ", (SELECT count(*) FROM {$mepr_db->events} AS e WHERE {$wpdb->users}.ID = e.evt_id AND e.evt_id_type='" . MeprEvent::$users_str . "' AND e.event = '" . MeprEvent::$login_event_str . "') AS num_logins"; - $query->query_orderby = "ORDER BY num_logins {$vars['order']}"; - } - } + // If we're processing a signup and the custom field is not set + // to show on signup we need to make sure it isn't required + if ($is_signup && $line->required && !$line->show_on_signup) { + $line->required = false; + } elseif (!$is_signup && !is_admin() && isset($line->show_in_account) && !$line->show_in_account) { + // Account page shouldn't show errors if the fields have been hidden from the account page + $line->required = false; + } - //This actually shows the content in the table HTML output - public static function manage_extra_user_columns($value, $column_name, $user_id) { - $user = new MeprUser($user_id); + if ((!isset($_POST[$line->field_key]) || (empty($_POST[$line->field_key]) && $_POST[$line->field_key] != '0')) && $line->required && 'file' != $line->field_type) { + $errs[$line->field_key] = sprintf(__('%s is required.', 'memberpress'), stripslashes($line->field_name)); - if($column_name == 'mepr_registered') { - $registered = $user->user_registered; - return MeprAppHelper::format_date($registered, __('Unknown', 'memberpress'), 'M j, Y') . '
    ' . MeprAppHelper::format_date($registered, __('Unknown', 'memberpress'), 'g:i A'); - } + // This allows us to run this on dashboard profile fields as well as front end + if (is_object($errors)) { + $errors->add($line->field_key, sprintf(__('%s is required.', 'memberpress'), stripslashes($line->field_name))); + } + } - if($column_name == 'mepr_products') { - $titles = $user->get_active_subscription_titles("
    "); + if ('file' == $line->field_type) { + $file_provided = isset($_FILES[$line->field_key]['error']) && $_FILES[$line->field_key]['error'] != UPLOAD_ERR_NO_FILE; + + if ($file_provided) { + // Validate new file upload + $file = $_FILES[$line->field_key]; + + if (empty($file['tmp_name']) || empty($file['name']) || empty($file['size'])) { + if ($line->required) { + $errs[$line->field_key] = sprintf(__('%s is required.', 'memberpress'), stripslashes($line->field_name)); + } + } elseif ($file['error'] == UPLOAD_ERR_OK) { + add_filter('upload_mimes', 'MeprUsersHelper::get_allowed_mime_types'); + $wp_filetype = wp_check_filetype($file['name']); + remove_filter('upload_mimes', 'MeprUsersHelper::get_allowed_mime_types'); + + if (!$wp_filetype['ext'] && !current_user_can('unfiltered_upload')) { + $errs[$line->field_key] = sprintf(__('%s file type not allowed.', 'memberpress'), stripslashes($line->field_name)); + } + } else { + $errs[$line->field_key] = sprintf(__('%s could not be uploaded.', 'memberpress'), stripslashes($line->field_name)); + } + } else { + // Validate existing file + if ($line->required) { + $file = get_user_meta(get_current_user_id(), $line->field_key, true); + + if (empty($file)) { + $errs[$line->field_key] = sprintf(__('%s is required.', 'memberpress'), stripslashes($line->field_name)); + } + } + } + } - if(!empty($titles)) { - return $titles; - } - else { - return __('None', 'memberpress'); - } - } + if ($line->required && 'email' == $line->field_type && !empty($_POST[$line->field_key])) { + if (!is_email(stripcslashes($_POST[$line->field_key]))) { + $errs[$line->field_key] = sprintf(__('%s is not a valid email address.', 'memberpress'), stripslashes($line->field_name)); + } + } - if($column_name == 'mepr_last_login') { - $login = $user->get_last_login_data(); + if ($line->required && 'url' == $line->field_type && !empty($_POST[$line->field_key])) { + if (!preg_match('/(https?:\/\/)?(www\.)?[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_\+.~#()?&\/\/=]*)/', $_POST[$line->field_key])) { + $errs[$line->field_key] = sprintf(__('%s is not a valid URL.', 'memberpress'), stripslashes($line->field_name)); + } + } - if(!empty($login)) { - return MeprAppHelper::format_date($login->created_at, __('Never', 'memberpress'), 'M j, Y') . '
    ' . MeprAppHelper::format_date($login->created_at, __('Never', 'memberpress'), 'g:i A'); - } - else { - return __('Never', 'memberpress'); - } - } + if ('date' == $line->field_type && !empty($_POST[$line->field_key])) { + if (!MeprUtils::is_date($_POST[$line->field_key])) { + $errs[$line->field_key] = sprintf(__('%s is not a valid date.', 'memberpress'), stripslashes($line->field_name)); + } + } + } - if($column_name == 'mepr_num_logins') { return (int)$user->get_num_logins(); } + return $errs; + } - return $value; - } + public static function user_search() + { + if (!MeprUtils::is_mepr_admin()) { + die('-1'); + } - public static function maybe_redirect_member_from_admin() { - $mepr_options = MeprOptions::fetch(); + // jQuery suggest plugin has already trimmed and escaped user input (\ becomes \\) + // so we just need to sanitize the username + $s = sanitize_user($_GET['q']); - // Don't mess up AJAX requests - if(defined('DOING_AJAX')) { return; } + if (strlen($s) < 2) { + die; // require 2 chars for matching + } - //Don't mess up admin_post.php requests - if(strpos($_SERVER['REQUEST_URI'], 'admin-post.php') !== false && isset($_REQUEST['action'])) { return; } + $users = get_users(['search' => "*$s*"]); - if($mepr_options->lock_wp_admin && !current_user_can('delete_posts')) { - if(isset($mepr_options->login_redirect_url) && !empty($mepr_options->login_redirect_url)) { - MeprUtils::wp_redirect($mepr_options->login_redirect_url); - } - else { - MeprUtils::wp_redirect(home_url()); - } + MeprView::render('/admin/users/search', get_defined_vars()); + die(); } - } - public static function maybe_disable_wp_registration_form($login, $email, $errors) { - $mepr_options = MeprOptions::fetch(); + // Add extra columns to the Users list table + public static function add_extra_user_columns($columns) + { + $columns['mepr_products'] = __('Active Memberships', 'memberpress'); + $columns['mepr_registered'] = __('Registered', 'memberpress'); + $columns['mepr_last_login'] = __('Last Login', 'memberpress'); + $columns['mepr_num_logins'] = __('# Logins', 'memberpress'); - if($mepr_options->disable_wp_registration_form) { - $message = __('You cannot register with this form. Please use the registration page found on the website instead.', 'memberpress'); - $errors->add('mepr_disabled_error', $message); + return $columns; } - } - public static function maybe_disable_admin_bar() { - $mepr_options = MeprOptions::fetch(); + // Tells WP which columns should be sortable + public static function sortable_extra_user_columns($cols) + { + $cols['mepr_registered'] = 'user_registered'; + $cols['mepr_last_login'] = 'last_login'; + $cols['mepr_num_logins'] = 'num_logins'; - if(!current_user_can('delete_posts') && $mepr_options->disable_wp_admin_bar) - show_admin_bar(false); - } + return $cols; + } - public static function login_page_meta_box() { - global $post; + // This allows us to sort the column properly behind the scenes + public static function extra_user_columns_query_override($query) + { + global $wpdb; + $vars = $query->query_vars; + $mepr_db = new MeprDb(); - $mepr_options = MeprOptions::fetch(); + if (isset($vars['orderby']) && $vars['orderby'] == 'last_login') { + $query->query_fields .= ", (SELECT e.created_at FROM {$mepr_db->events} AS e WHERE {$wpdb->users}.ID = e.evt_id AND e.evt_id_type='" . MeprEvent::$users_str . "' AND e.event = '" . MeprEvent::$login_event_str . "' ORDER BY e.created_at DESC LIMIT 1) AS last_login"; + $query->query_orderby = "ORDER BY last_login {$vars['order']}"; + } - if(isset($post) && $post instanceof WP_Post && $post->ID == $mepr_options->login_page_id) { - add_meta_box('mepr_login_page_meta_box', __('MemberPress Settings', 'memberpress'), 'MeprUsersCtrl::show_login_page_meta_box', 'page', 'normal', 'high'); + if (isset($vars['orderby']) && $vars['orderby'] == 'num_logins') { + $query->query_fields .= ", (SELECT count(*) FROM {$mepr_db->events} AS e WHERE {$wpdb->users}.ID = e.evt_id AND e.evt_id_type='" . MeprEvent::$users_str . "' AND e.event = '" . MeprEvent::$login_event_str . "') AS num_logins"; + $query->query_orderby = "ORDER BY num_logins {$vars['order']}"; + } } - } - public static function show_login_page_meta_box() { - global $post; + // This actually shows the content in the table HTML output + public static function manage_extra_user_columns($value, $column_name, $user_id) + { + $user = new MeprUser($user_id); - $mepr_options = MeprOptions::fetch(); + if ($column_name == 'mepr_registered') { + $registered = $user->user_registered; + return MeprAppHelper::format_date($registered, __('Unknown', 'memberpress'), 'M j, Y') . '
    ' . MeprAppHelper::format_date($registered, __('Unknown', 'memberpress'), 'g:i A'); + } - if(isset($post) && $post->ID) { - $manual_login_form = get_post_meta($post->ID, '_mepr_manual_login_form', true); + if ($column_name == 'mepr_products') { + $titles = $user->get_active_subscription_titles('
    '); - MeprView::render('/admin/users/login_page_meta_box', get_defined_vars()); + if (!empty($titles)) { + return $titles; + } else { + return __('None', 'memberpress'); + } + } + + if ($column_name == 'mepr_last_login') { + $login = $user->get_last_login_data(); + + if (!empty($login)) { + return MeprAppHelper::format_date($login->created_at, __('Never', 'memberpress'), 'M j, Y') . '
    ' . MeprAppHelper::format_date($login->created_at, __('Never', 'memberpress'), 'g:i A'); + } else { + return __('Never', 'memberpress'); + } + } + + if ($column_name == 'mepr_num_logins') { + return (int)$user->get_num_logins(); + } + + return $value; } - } - public static function save_postdata($post_id) { - $post = get_post($post_id); - $mepr_options = MeprOptions::fetch(); + public static function maybe_redirect_member_from_admin() + { + $mepr_options = MeprOptions::fetch(); + + // Don't mess up AJAX requests + if (defined('DOING_AJAX')) { + return; + } - if(!wp_verify_nonce((isset($_POST[MeprUser::$nonce_str]))?$_POST[MeprUser::$nonce_str]:'', MeprUser::$nonce_str.wp_salt())) { - return $post_id; //Nonce prevents meta data from being wiped on move to trash + // Don't mess up admin_post.php requests + if (strpos($_SERVER['REQUEST_URI'], 'admin-post.php') !== false && isset($_REQUEST['action'])) { + return; + } + + if ($mepr_options->lock_wp_admin && !current_user_can('delete_posts')) { + if (isset($mepr_options->login_redirect_url) && !empty($mepr_options->login_redirect_url)) { + MeprUtils::wp_redirect($mepr_options->login_redirect_url); + } else { + MeprUtils::wp_redirect(home_url()); + } + } } - if(defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) { - return $post_id; + public static function maybe_disable_wp_registration_form($login, $email, $errors) + { + $mepr_options = MeprOptions::fetch(); + + if ($mepr_options->disable_wp_registration_form) { + $message = __('You cannot register with this form. Please use the registration page found on the website instead.', 'memberpress'); + $errors->add('mepr_disabled_error', $message); + } } - if(defined('DOING_AJAX')) { - return; + public static function maybe_disable_admin_bar() + { + $mepr_options = MeprOptions::fetch(); + + if (!current_user_can('delete_posts') && $mepr_options->disable_wp_admin_bar) { + show_admin_bar(false); + } } - if(!empty($post) && $post->ID == $mepr_options->login_page_id) { - $manual_login_form = (isset($_POST['_mepr_manual_login_form'])); - update_post_meta($post->ID, '_mepr_manual_login_form', $manual_login_form); + public static function login_page_meta_box() + { + global $post; + + $mepr_options = MeprOptions::fetch(); + + if (isset($post) && $post instanceof WP_Post && $post->ID == $mepr_options->login_page_id) { + add_meta_box('mepr_login_page_meta_box', __('MemberPress Settings', 'memberpress'), 'MeprUsersCtrl::show_login_page_meta_box', 'page', 'normal', 'high'); + } } - } - public static function list_users_subscriptions($atts, $content = '') { - $user = MeprUtils::get_currentuserinfo(); - $active_rows = array(); - $inactive_rows = array(); - $alt_row = 'mp_users_subscriptions_list_alt'; + public static function show_login_page_meta_box() + { + global $post; - if(!$user) { return ''; } + $mepr_options = MeprOptions::fetch(); - $status = (isset($atts['status'])) ? $atts['status'] : 'all'; + if (isset($post) && $post->ID) { + $manual_login_form = get_post_meta($post->ID, '_mepr_manual_login_form', true); - $all_ids = $user->current_and_prior_subscriptions(); //returns an array of Product ID's the user has ever been subscribed to - $active_ids = $user->active_product_subscriptions('ids'); + MeprView::render('/admin/users/login_page_meta_box', get_defined_vars()); + } + } - foreach($all_ids as $id) { - $prd = new MeprProduct($id); - $created_at = MeprUser::get_user_product_signup_date($user->ID, $id); + public static function save_postdata($post_id) + { + $post = get_post($post_id); + $mepr_options = MeprOptions::fetch(); - if(in_array($id, $active_ids) && $status !== 'expired') { - $expiring_txn = MeprUser::get_user_product_expires_at_date($user->ID, $id, true); - $renewal_link = ''; - $expires_at = _x('Unknown', 'ui', 'memberpress'); + if (!wp_verify_nonce((isset($_POST[MeprUser::$nonce_str])) ? $_POST[MeprUser::$nonce_str] : '', MeprUser::$nonce_str . wp_salt())) { + return $post_id; // Nonce prevents meta data from being wiped on move to trash + } - if($expiring_txn instanceof MeprTransaction) { - $renewal_link = MeprHooks::apply_filters( 'mepr_list_subscriptions_renewal_link', $user->renewal_link($expiring_txn->id), $expiring_txn ); - $expires_at = MeprAppHelper::format_date($expiring_txn->expires_at, _x('Never', 'ui', 'memberpress')); + if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) { + return $post_id; } - $active_rows[] = (object)array('membership' => $prd->post_title, 'expires' => $expires_at, 'renewal_link' => $renewal_link, 'access_url' => $prd->access_url, 'created_at' => $created_at); - } - elseif(!in_array($id, $active_ids) && in_array($status, array('expired', 'all'))) { - $inactive_rows[] = (object)array('membership' => $prd->post_title, 'purchase_link' => $prd->url(), 'created_at' => $created_at); - } - } + if (defined('DOING_AJAX')) { + return; + } - // Sorting active subs - if(!empty($active_rows) && isset($atts['orderby']) && in_array($atts['orderby'], array('date', 'title'))) { - if($atts['orderby'] == 'date') { - if($atts['order'] == 'asc') { - usort($active_rows, function ($a, $b) { - return $a->created_at <=> $b->created_at; - }); - } else { - usort($active_rows, function ($a, $b) { - return $b->created_at <=> $a->created_at; - }); - } - } - if($atts['orderby'] == 'title') { - if($atts['order'] == 'desc') { - usort($active_rows, function ($a, $b) { - return $b->membership <=> $a->membership; - }); - } else { - usort($active_rows, function ($a, $b) { - return $a->membership <=> $b->membership; - }); + if (!empty($post) && $post->ID == $mepr_options->login_page_id) { + $manual_login_form = (isset($_POST['_mepr_manual_login_form'])); + update_post_meta($post->ID, '_mepr_manual_login_form', $manual_login_form); } - } } - // Sorting inactive subs - if (!empty($inactive_rows) && isset($atts['orderby']) && in_array($atts['orderby'], array('date', 'title'))) { - if($atts['orderby'] == 'date') { - if($atts['order'] == 'asc') { - usort($inactive_rows, function ($a, $b) { - return $a->created_at <=> $b->created_at; - }); - } else { - usort($inactive_rows, function ($a, $b) { - return $b->created_at <=> $a->created_at; - }); - } - } - if($atts['orderby'] == 'title') { - if($atts['order'] == 'desc') { - usort($inactive_rows, function ($a, $b) { - return $b->membership <=> $a->membership; - }); - } else { - usort($inactive_rows, function ($a, $b) { - return $a->membership <=> $b->membership; - }); + public static function list_users_subscriptions($atts, $content = '') + { + $user = MeprUtils::get_currentuserinfo(); + $active_rows = []; + $inactive_rows = []; + $alt_row = 'mp_users_subscriptions_list_alt'; + + if (!$user) { + return ''; } - } - } - ob_start(); - MeprView::render('/shortcodes/list_users_subscriptions', get_defined_vars()); - return ob_get_clean(); - } + $status = (isset($atts['status'])) ? $atts['status'] : 'all'; + $all_ids = $user->current_and_prior_subscriptions(); // returns an array of Product ID's the user has ever been subscribed to + $active_ids = $user->active_product_subscriptions('ids'); - /** - * Adds shortcode for displaying user files - * - * @param mixed $atts - * @param mixed $content - * @return mixed - */ - public static function show_user_file($atts, $content = ''){ - $key = (isset($atts['slug'])) ? sanitize_text_field($atts['slug']) : ''; - $userid = (isset($atts['userid'])) ? intval($atts['userid']) : get_current_user_id(); + foreach ($all_ids as $id) { + $prd = new MeprProduct($id); + $created_at = MeprUser::get_user_product_signup_date($user->ID, $id); - $mepr_options = MeprOptions::fetch(); - $custom_fields = (array) $mepr_options->custom_fields; + if (in_array($id, $active_ids) && $status !== 'expired') { + $expiring_txn = MeprUser::get_user_product_expires_at_date($user->ID, $id, true); + $renewal_link = ''; + $expires_at = _x('Unknown', 'ui', 'memberpress'); - $field = array_filter($custom_fields, function($field) use ($key) { - return $field->field_key === $key && $field->field_type === 'file'; - }); + if ($expiring_txn instanceof MeprTransaction) { + $renewal_link = MeprHooks::apply_filters('mepr_list_subscriptions_renewal_link', $user->renewal_link($expiring_txn->id), $expiring_txn); + $expires_at = MeprAppHelper::format_date($expiring_txn->expires_at, _x('Never', 'ui', 'memberpress')); + } - if(empty($field)) { return; } + $active_rows[] = (object)[ + 'membership' => $prd->post_title, + 'expires' => $expires_at, + 'renewal_link' => $renewal_link, + 'access_url' => $prd->access_url, + 'created_at' => $created_at, + ]; + } elseif (!in_array($id, $active_ids) && in_array($status, ['expired', 'all'])) { + $inactive_rows[] = (object)[ + 'membership' => $prd->post_title, + 'purchase_link' => $prd->url(), + 'created_at' => $created_at, + ]; + } + } - $download = get_user_meta($userid, $key, true); + // Sorting active subs + if (!empty($active_rows) && isset($atts['orderby']) && in_array($atts['orderby'], ['date', 'title'])) { + if ($atts['orderby'] == 'date') { + if ($atts['order'] == 'asc') { + usort($active_rows, function ($a, $b) { + return $a->created_at <=> $b->created_at; + }); + } else { + usort($active_rows, function ($a, $b) { + return $b->created_at <=> $a->created_at; + }); + } + } + if ($atts['orderby'] == 'title') { + if ($atts['order'] == 'desc') { + usort($active_rows, function ($a, $b) { + return $b->membership <=> $a->membership; + }); + } else { + usort($active_rows, function ($a, $b) { + return $a->membership <=> $b->membership; + }); + } + } + } - if( false === MeprUsersHelper::uploaded_file_exists($download) ){ - return; + // Sorting inactive subs + if (!empty($inactive_rows) && isset($atts['orderby']) && in_array($atts['orderby'], ['date', 'title'])) { + if ($atts['orderby'] == 'date') { + if ($atts['order'] == 'asc') { + usort($inactive_rows, function ($a, $b) { + return $a->created_at <=> $b->created_at; + }); + } else { + usort($inactive_rows, function ($a, $b) { + return $b->created_at <=> $a->created_at; + }); + } + } + if ($atts['orderby'] == 'title') { + if ($atts['order'] == 'desc') { + usort($inactive_rows, function ($a, $b) { + return $b->membership <=> $a->membership; + }); + } else { + usort($inactive_rows, function ($a, $b) { + return $a->membership <=> $b->membership; + }); + } + } + } + + ob_start(); + MeprView::render('/shortcodes/list_users_subscriptions', get_defined_vars()); + return ob_get_clean(); } - ob_start(); - MeprView::render('/shortcodes/user_files', get_defined_vars()); - return ob_get_clean(); - } - public static function get_user_active_membership_titles($atts, $content = '') { - $userid = (isset($atts['userid']) && !empty($atts['userid'])) ? (int)trim($atts['userid']) : get_current_user_id(); + /** + * Adds shortcode for displaying user files + * + * @param mixed $atts + * @param mixed $content + * @return mixed + */ + public static function show_user_file($atts, $content = '') + { + $key = (isset($atts['slug'])) ? sanitize_text_field($atts['slug']) : ''; + $userid = (isset($atts['userid'])) ? intval($atts['userid']) : get_current_user_id(); + + $mepr_options = MeprOptions::fetch(); + $custom_fields = (array) $mepr_options->custom_fields; - if(!$userid) { - return; + $field = array_filter($custom_fields, function ($field) use ($key) { + return $field->field_key === $key && $field->field_type === 'file'; + }); + + if (empty($field)) { + return; + } + + $download = get_user_meta($userid, $key, true); + + if (false === MeprUsersHelper::uploaded_file_exists($download)) { + return; + } + + ob_start(); + MeprView::render('/shortcodes/user_files', get_defined_vars()); + return ob_get_clean(); } - $user = new MeprUser($userid); - $message = (isset($atts['message']) && !empty($atts['message'])) ? wp_kses_post($atts['message']) : ''; - $titles = esc_attr(trim($user->get_active_subscription_titles())); + public static function get_user_active_membership_titles($atts, $content = '') + { + $userid = (isset($atts['userid']) && !empty($atts['userid'])) ? (int)trim($atts['userid']) : get_current_user_id(); - return ('' != $titles) ? $titles : $message; - } + if (!$userid) { + return; + } + + $user = new MeprUser($userid); + $message = (isset($atts['message']) && !empty($atts['message'])) ? wp_kses_post($atts['message']) : ''; + $titles = esc_attr(trim($user->get_active_subscription_titles())); + + return ('' != $titles) ? $titles : $message; + } } //End class diff --git a/app/controllers/MeprVatTaxCtrl.php b/app/controllers/MeprVatTaxCtrl.php index 3aeeeda..15d0786 100644 --- a/app/controllers/MeprVatTaxCtrl.php +++ b/app/controllers/MeprVatTaxCtrl.php @@ -1,578 +1,613 @@ -vat_calc_possible()) { + $vat_enabled = get_option('mepr_vat_enabled'); + + if (($mepr_options->global_styles || $is_product_page) && $vat_enabled) { + $countries = $this->get_vat_countries(); + $vat_country = strtoupper(get_option('mepr_vat_country')); + wp_enqueue_script('mpvat', MEPR_JS_URL . '/mpvat.js', ['jquery', 'mp-i18n']); + wp_localize_script('mpvat', 'MpVat', [ + 'rates' => $countries, + 'countries' => array_keys($countries), + 'vat_country' => $vat_country, + ]); + $prereqs[] = 'mpvat'; + } + } - // Filter for signup / payment page - add_action('mepr-checkout-before-coupon-field', array($this,'signup')); + return $prereqs; + } - // Validate the VAT number - add_filter('mepr-validate-signup', array($this,'validate_signup')); + public function options() + { + $vat_enabled = get_option('mepr_vat_enabled'); + $vat_country = get_option('mepr_vat_country'); + $vat_tax_businesses = get_option('mepr_vat_tax_businesses'); + $vat_disable_vies_service = get_option('mepr_vat_disable_vies_service'); + $charge_business_customer_net_price = get_option('mepr_charge_business_customer_net_price'); + $show_negative_tax_on_invoice = get_option('mepr_show_negative_tax_on_invoice'); - // STORE THE VAT FIELDS WITH THE USER RECORD - add_action('mepr-process-signup', array($this,'process_signup'), 10, 4); + $countries = $this->get_vat_countries(); - if(!$tax_stripe_enabled) { - // Filter for tax calculation - add_filter('mepr_find_tax_rate', array($this, 'find_rate'), 20, 8); - } + MeprView::render('/admin/taxes/vat_options', get_defined_vars()); + } - // Follow use merchant address from here on out? - //add_filter('mepr-tax-rate-use-customer-address', array($this,'use_customer_address'), 10, 2); + public function validate_signup($errors) + { + $prd = new MeprProduct($_POST['mepr_product_id']); - add_action('mepr_extra_profile_fields', array($this,'extra_profile_fields')); - add_action('user_profile_update_errors', array($this, 'validate_extra_profile_fields'), 10, 3); - add_action('edit_user_profile_update', array($this, 'save_extra_profile_fields')); - add_action('personal_options_update', array($this, 'save_extra_profile_fields')); + if ($this->vat_calc_possible() && ($prd->price > 0.00 || ($prd->price <= 0.00 && !$prd->disable_address_fields))) { + $country = $_POST['mepr-address-country']; + $customer_type = self::get_customer_type(); + $vat_number = self::get_vat_number(); + $vat_tax_businesses = get_option('mepr_vat_tax_businesses', false); - // TODO: VAT collected by month available as CSV download - add_action('mepr-report-footer', array($this,'vat_csv_buttons')); - add_action('wp_ajax_mepr_vat_country_report', array($this,'country_vat_csv')); - add_filter('mepr_stripe_product_base_price', array($this, 'maybe_apply_vat_reversal'), 10, 3); - } - } - - public function product_scripts($prereqs, $is_product_page, $is_account_page) { - $mepr_options = MeprOptions::fetch(); - - if($this->vat_calc_possible()) { - $vat_enabled = get_option('mepr_vat_enabled'); - - if(($mepr_options->global_styles || $is_product_page) && $vat_enabled) { - $countries = $this->get_vat_countries(); - $vat_country = strtoupper( get_option( 'mepr_vat_country' ) ); - wp_enqueue_script('mpvat', MEPR_JS_URL.'/mpvat.js', array('jquery', 'mp-i18n')); - wp_localize_script('mpvat', 'MpVat', array( - 'rates' => $countries, - 'countries' => array_keys($countries), - 'vat_country' => $vat_country, - )); - $prereqs[] = 'mpvat'; - } - } + // If customer is a business, then a value must be entered for the vat number + // Unless tax all eu business is enabled + if ($customer_type == 'business' && empty($vat_number) && !$vat_tax_businesses) { + $errors['mepr_vat_number'] = __('VAT number is required', 'memberpress'); + } elseif ( + $customer_type == 'business' && + !empty($vat_number) && + !$this->vat_number_is_valid($vat_number, $country) + ) { + $errors['mepr_vat_number'] = __('Your VAT number is invalid', 'memberpress'); + } + } - return $prereqs; - } - - public function options() { - $vat_enabled = get_option('mepr_vat_enabled'); - $vat_country = get_option('mepr_vat_country'); - $vat_tax_businesses = get_option('mepr_vat_tax_businesses'); - $vat_disable_vies_service = get_option('mepr_vat_disable_vies_service'); - $charge_business_customer_net_price = get_option('mepr_charge_business_customer_net_price'); - $show_negative_tax_on_invoice = get_option('mepr_show_negative_tax_on_invoice'); - - $countries = $this->get_vat_countries(); - - MeprView::render('/admin/taxes/vat_options', get_defined_vars()); - } - - public function validate_signup($errors) { - $prd = new MeprProduct($_POST['mepr_product_id']); - - if($this->vat_calc_possible() && ($prd->price > 0.00 || ($prd->price <= 0.00 && !$prd->disable_address_fields))) { - $country = $_POST['mepr-address-country']; - $customer_type = self::get_customer_type(); - $vat_number = self::get_vat_number(); - $vat_tax_businesses = get_option('mepr_vat_tax_businesses', false); - - //If customer is a business, then a value must be entered for the vat number - //Unless tax all eu business is enabled - if ($customer_type=='business' && empty($vat_number) && !$vat_tax_businesses) { - $errors['mepr_vat_number'] = __('VAT number is required', 'memberpress'); - } else if($customer_type=='business' && - !empty($vat_number) && - !$this->vat_number_is_valid($vat_number, $country)) { - $errors['mepr_vat_number'] = __('Your VAT number is invalid', 'memberpress'); - } + return $errors; } - return $errors; - } - - public function store_options() { - $vat_enabled = isset($_POST['mepr_vat_enabled']); - $vat_country = isset($_POST['mepr_vat_country']) ? sanitize_text_field($_POST['mepr_vat_country']) : ''; - $vat_tax_businesses = isset($_POST['mepr_vat_tax_businesses']); - $vat_disable_vies_service = isset($_POST['mepr_vat_disable_vies_service']); - $charge_business_customer_net_price = isset($_POST['mepr_charge_business_customer_net_price']); - $show_negative_tax_on_invoice = isset($_POST['mepr_show_negative_tax_on_invoice']); + public function store_options() + { + $vat_enabled = isset($_POST['mepr_vat_enabled']); + $vat_country = isset($_POST['mepr_vat_country']) ? sanitize_text_field($_POST['mepr_vat_country']) : ''; + $vat_tax_businesses = isset($_POST['mepr_vat_tax_businesses']); + $vat_disable_vies_service = isset($_POST['mepr_vat_disable_vies_service']); + $charge_business_customer_net_price = isset($_POST['mepr_charge_business_customer_net_price']); + $show_negative_tax_on_invoice = isset($_POST['mepr_show_negative_tax_on_invoice']); - update_option('mepr_vat_enabled', $vat_enabled); - update_option('mepr_vat_country', $vat_country); - update_option('mepr_vat_tax_businesses', $vat_tax_businesses); - update_option('mepr_vat_disable_vies_service', $vat_disable_vies_service); - update_option('mepr_charge_business_customer_net_price', $charge_business_customer_net_price); - update_option('mepr_show_negative_tax_on_invoice', $show_negative_tax_on_invoice); - } + update_option('mepr_vat_enabled', $vat_enabled); + update_option('mepr_vat_country', $vat_country); + update_option('mepr_vat_tax_businesses', $vat_tax_businesses); + update_option('mepr_vat_disable_vies_service', $vat_disable_vies_service); + update_option('mepr_charge_business_customer_net_price', $charge_business_customer_net_price); + update_option('mepr_show_negative_tax_on_invoice', $show_negative_tax_on_invoice); + } - public function signup($prd_id) { - $prd = new MeprProduct($prd_id); + public function signup($prd_id) + { + $prd = new MeprProduct($prd_id); - if($this->vat_calc_possible() && $prd->price > 0.00 && !isset($_GET['ca'])) { - $vat_customer_type = self::get_customer_type(); - $vat_number = self::get_vat_number(); + if ($this->vat_calc_possible() && $prd->price > 0.00 && !isset($_GET['ca'])) { + $vat_customer_type = self::get_customer_type(); + $vat_number = self::get_vat_number(); - static $unique_suffix = 0; - $unique_suffix++; + static $unique_suffix = 0; + $unique_suffix++; - MeprView::render('/taxes/vat_signup', get_defined_vars()); + MeprView::render('/taxes/vat_signup', get_defined_vars()); + } } - } - public function process_signup($amt, $usr, $pid, $tid) { - if($this->vat_calc_possible()) { - if(isset($_POST['mepr_vat_customer_type'])) { - update_user_meta($usr->ID, 'mepr_vat_customer_type', self::get_customer_type()); - } + public function process_signup($amt, $usr, $pid, $tid) + { + if ($this->vat_calc_possible()) { + if (isset($_POST['mepr_vat_customer_type'])) { + update_user_meta($usr->ID, 'mepr_vat_customer_type', self::get_customer_type()); + } - if(isset($_POST['mepr_vat_number'])) { - update_user_meta($usr->ID, 'mepr_vat_number', self::get_vat_number()); - } - } - } - - /** VAT overrides anything that could possibly be set by the standard tax rate db tables */ - public function find_rate($tax_rate, $country, $state, $postcode, $city, $street, $usr=null, $prd_id = null) { - $mepr_options = MeprOptions::fetch(); - $countries = $this->get_vat_countries(); - $customer_type = self::get_customer_type($usr); - $vat_number = self::get_vat_number($usr); - $vat_tax_businesses = get_option('mepr_vat_tax_businesses', false); - $usr_country = null; - $vat_country = get_option('mepr_vat_country'); - $vies_country = $country; - - if ( $customer_type === 'business' ) { - $tax_rate->customer_type = 'business'; + if (isset($_POST['mepr_vat_number'])) { + update_user_meta($usr->ID, 'mepr_vat_number', self::get_vat_number()); + } + } } - if(!empty($usr) && $usr instanceof MeprUser && $usr->address_is_set()) { - $usr_country = $usr->address('country'); - $use_address_from_request = $usr->use_address_from_request(); + /** + * VAT overrides anything that could possibly be set by the standard tax rate db tables + */ + public function find_rate($tax_rate, $country, $state, $postcode, $city, $street, $usr = null, $prd_id = null) + { + $mepr_options = MeprOptions::fetch(); + $countries = $this->get_vat_countries(); + $customer_type = self::get_customer_type($usr); + $vat_number = self::get_vat_number($usr); + $vat_tax_businesses = get_option('mepr_vat_tax_businesses', false); + $usr_country = null; + $vat_country = get_option('mepr_vat_country'); + $vies_country = $country; + + if ($customer_type === 'business') { + $tax_rate->customer_type = 'business'; + } - // When updating pricing terms string with AJAX,user country should be the POST country - if($use_address_from_request) { - $usr_country = isset($_POST['mepr-address-country']) ? sanitize_text_field(wp_unslash($_POST['mepr-address-country'])) : ''; - } + if (!empty($usr) && $usr instanceof MeprUser && $usr->address_is_set()) { + $usr_country = $usr->address('country'); + $use_address_from_request = $usr->use_address_from_request(); + + // When updating pricing terms string with AJAX,user country should be the POST country + if ($use_address_from_request) { + $usr_country = isset($_POST['mepr-address-country']) ? sanitize_text_field(wp_unslash($_POST['mepr-address-country'])) : ''; + } + + if ($customer_type == 'business') { + $vies_country = $usr_country; + } + + // If the user's address is set and their country is outside the UK or EU then bail + if ($vat_country != $usr_country && !array_key_exists($usr_country, $countries)) { + return $tax_rate; + } + + // Canary Islands (Spain) has different VAT Rules + if ($usr_country == 'ES') { + $canary_island_zips = ['35','38','51','52']; + $usr_zip = (string)trim($usr->address('zip')); + + if ($use_address_from_request) { + $usr_zip = isset($_POST['mepr-address-zip']) ? sanitize_text_field(wp_unslash($_POST['mepr-address-zip'])) : ''; + } + + foreach ($canary_island_zips as $zip_prefix) { + if (strpos($usr_zip, $zip_prefix) === 0) { + return $tax_rate; + } + } + } + + if ( + $mepr_options->attr('tax_calc_location') == 'customer' + && $vat_country === 'GB' + && $usr_country !== 'GB' + && strtotime('2020-12-31 23:59:59') < time() + ) { + return new MeprTaxRate(); + } + } - if($customer_type == 'business'){ - $vies_country = $usr_country; - } + // Make sure this is an EU country + if (array_key_exists($country, $countries)) { + $is_valid_vat = $this->vat_number_is_valid($vat_number, $vies_country); + $is_vat_removal = $this->is_valid_vat_removal_rule($is_valid_vat, $customer_type, $vat_country, $usr_country); + + // Conditions for calculating VAT or not + // If we're taxing all businesses then vat tax validation doesn't matter + if ( + $customer_type == 'consumer' || + ( $customer_type == 'business' && + ( $vat_country == $usr_country || + $vat_tax_businesses || + !$is_valid_vat ) + ) || $is_vat_removal + ) { + $tax_rate = $this->get_rate($tax_rate, $country, $prd_id); + + if ($is_vat_removal) { + $tax_rate->reversal = true; + } + } + } - // If the user's address is set and their country is outside the UK or EU then bail - if($vat_country != $usr_country && !array_key_exists($usr_country, $countries)) { return $tax_rate; - } + } - // Canary Islands (Spain) has different VAT Rules - if($usr_country == 'ES') { - $canary_island_zips = array('35','38','51','52'); - $usr_zip = (string)trim($usr->address('zip')); + private function get_rate(MeprTaxRate $tax_rate, $country, $prd_id = null) + { + $countries = $this->get_vat_countries(); - if($use_address_from_request) { - $usr_zip = isset($_POST['mepr-address-zip']) ? sanitize_text_field(wp_unslash($_POST['mepr-address-zip'])) : ''; + if (empty($prd_id)) { + $prd_id = !empty($_POST['mepr_product_id']) ? (int) $_POST['mepr_product_id'] : 0; + $prd_id = !empty($_POST['prd_id']) ? (int) $_POST['prd_id'] : $prd_id; } - foreach($canary_island_zips as $zip_prefix) { - if(strpos($usr_zip, $zip_prefix) === 0) { - return $tax_rate; - } + $prd = new MeprProduct($prd_id); + + $tax_rate->tax_rate = $countries[$country]['rate']; + + if ($prd->ID && $prd->tax_class == 'reduced' && isset($countries[$country]['reduced_rate'])) { + $tax_rate->tax_rate = $countries[$country]['reduced_rate']; } - } + $tax_rate->tax_desc = sprintf(__('VAT (%s)', 'memberpress'), $countries[$country]['name']); + $tax_rate->tax_class = 'vat'; - if($mepr_options->attr('tax_calc_location')=='customer' - && $vat_country === 'GB' - && $usr_country !== 'GB' - && strtotime('2020-12-31 23:59:59') < time() - ) { - return new MeprTaxRate(); - } + return MeprHooks::apply_filters('mepr_vat_tax_rate', $tax_rate, $country, $prd_id); } - // Make sure this is an EU country - if(array_key_exists($country,$countries)) { - - $is_valid_vat = $this->vat_number_is_valid($vat_number, $vies_country); - $is_vat_removal = $this->is_valid_vat_removal_rule($is_valid_vat, $customer_type, $vat_country, $usr_country); + public function vat_number_is_valid($vat_number, $country) + { + $countries = $this->get_vat_countries(); + $vat_number = trim(preg_replace('/[-.●]/', '', $vat_number)); - // Conditions for calculating VAT or not - // If we're taxing all businesses then vat tax validation doesn't matter - if( $customer_type=='consumer' || - ( $customer_type=='business' && - ( $vat_country==$usr_country || - $vat_tax_businesses || - !$is_valid_vat ) - ) || $is_vat_removal - ) { - $tax_rate = $this->get_rate($tax_rate, $country, $prd_id); + return ( + isset($countries[$country]) && + preg_match("/^{$countries[$country]['fmt']}$/i", $vat_number) && + $this->vies_says_vat_is_valid($vat_number, $country) + ); + } - if($is_vat_removal) { - $tax_rate->reversal = true; + // https://ec.europa.eu/taxation_customs/vies/#/technical-information + private function vies_says_vat_is_valid($vat_number, $country) + { + if (get_option('mepr_vat_disable_vies_service')) { + return true; } - } - } - return $tax_rate; - } + // If the vat number is prefixed by the country code, cut it out + $vat_number = preg_replace('/^' . preg_quote($country) . '/i', '', $vat_number); - private function get_rate(MeprTaxRate $tax_rate, $country, $prd_id=null) { - $countries = $this->get_vat_countries(); + static $result = []; - if(empty($prd_id)) { - $prd_id = !empty($_POST['mepr_product_id']) ? (int) $_POST['mepr_product_id'] : 0; - $prd_id = !empty($_POST['prd_id']) ? (int) $_POST['prd_id'] : $prd_id; - } + if (isset($result[$vat_number])) { + return $result[$vat_number]; + } - $prd = new MeprProduct( $prd_id ); + $response = wp_remote_post('https://ec.europa.eu/taxation_customs/vies/rest-api/check-vat-number', [ + 'headers' => [ + 'Content-Type' => 'application/json', + ], + 'body' => wp_json_encode( + [ + 'countryCode' => $country, + 'vatNumber' => $vat_number, + ] + ), + ]); + + // Default to true (so we can proceed if the VIES service is down etc.) + $result[$vat_number] = MeprHooks::apply_filters('mepr_vat_vies_default_result', true, $vat_number, $country); + + if (wp_remote_retrieve_response_code($response) == 200) { + $body = json_decode(wp_remote_retrieve_body($response), true); + + if (isset($body['valid']) && is_bool($body['valid'])) { + $result[$vat_number] = $body['valid']; + } + } - $tax_rate->tax_rate = $countries[$country]['rate']; + $result[$vat_number] = MeprHooks::apply_filters( + 'mepr_vat_vies_result', + $result[$vat_number], + $vat_number, + $country + ); - if($prd->ID && $prd->tax_class == 'reduced' && isset($countries[$country]['reduced_rate'])){ - $tax_rate->tax_rate = $countries[$country]['reduced_rate']; - } - $tax_rate->tax_desc = sprintf(__('VAT (%s)', 'memberpress'), $countries[$country]['name']); - $tax_rate->tax_class = 'vat'; - - return MeprHooks::apply_filters( 'mepr_vat_tax_rate', $tax_rate, $country, $prd_id ); - } - - public function vat_number_is_valid($vat_number, $country) { - $countries = $this->get_vat_countries(); - $vat_number = trim(preg_replace('/[-.●]/','',$vat_number)); - - return ( - isset($countries[$country]) && - preg_match("/^{$countries[$country]['fmt']}$/i",$vat_number) && - $this->vies_says_vat_is_valid($vat_number,$country) - ); - } - - // https://ec.europa.eu/taxation_customs/vies/#/technical-information - private function vies_says_vat_is_valid($vat_number, $country) { - if(get_option('mepr_vat_disable_vies_service')) { - return true; + return $result[$vat_number]; } - // If the vat number is prefixed by the country code, cut it out - $vat_number = preg_replace('/^'.preg_quote($country).'/i', '', $vat_number); + public static function get_customer_type($usr = null) + { + if (array_key_exists('mepr_vat_customer_type', $_POST)) { + return sanitize_text_field($_POST['mepr_vat_customer_type']); + } - static $result = []; + // If the vat number is empty then grab the current user info + if (MeprUtils::is_user_logged_in() && empty($usr)) { + $usr = MeprUtils::get_currentuserinfo(); + } - if(isset($result[$vat_number])) { - return $result[$vat_number]; - } + if ( + !empty($usr) && + ($ctype = get_user_meta($usr->ID, 'mepr_vat_customer_type', true)) && + !empty($ctype) + ) { + return $ctype; + } - $response = wp_remote_post('https://ec.europa.eu/taxation_customs/vies/rest-api/check-vat-number', [ - 'headers' => [ - 'Content-Type' => 'application/json', - ], - 'body' => wp_json_encode( - [ - 'countryCode' => $country, - 'vatNumber' => $vat_number - ] - ) - ]); - - // Default to true (so we can proceed if the VIES service is down etc.) - $result[$vat_number] = MeprHooks::apply_filters('mepr_vat_vies_default_result', true, $vat_number, $country); - - if(wp_remote_retrieve_response_code($response) == 200) { - $body = json_decode(wp_remote_retrieve_body($response), true); - - if(isset($body['valid']) && is_bool($body['valid'])) { - $result[$vat_number] = $body['valid']; - } + // Default customer type right here people + return 'consumer'; } - $result[$vat_number] = MeprHooks::apply_filters( - 'mepr_vat_vies_result', - $result[$vat_number], - $vat_number, - $country - ); - - return $result[$vat_number]; - } + public static function get_vat_number($usr = null) + { + if (array_key_exists('mepr_vat_number', $_POST)) { + return sanitize_text_field($_POST['mepr_vat_number']); + } - public static function get_customer_type($usr=null) { - if(array_key_exists('mepr_vat_customer_type',$_POST)) { - return sanitize_text_field($_POST['mepr_vat_customer_type']); - } + // If the vat number is empty then grab the current user info + if (MeprUtils::is_user_logged_in() && empty($usr)) { + $usr = MeprUtils::get_currentuserinfo(); + } - // If the vat number is empty then grab the current user info - if(MeprUtils::is_user_logged_in() && empty($usr)) { - $usr = MeprUtils::get_currentuserinfo(); - } + if ( + !empty($usr) && + ($vnum = get_user_meta($usr->ID, 'mepr_vat_number', true)) && + !empty($vnum) + ) { + return $vnum; + } - if(!empty($usr) && - ($ctype = get_user_meta($usr->ID,'mepr_vat_customer_type',true)) && - !empty($ctype)) { - return $ctype; + return ''; + } + + public function vat_csv_buttons($type = 'monthly') + { + // Download transactions with VAT country, business/consumer, and VAT number + $totals_url = MeprUtils::admin_url( + 'admin-ajax.php', + ['export_report','mepr_reports_nonce'], + [ + 'action' => 'mepr_export_report', + 'export' => $type, + 'q' => [ + 'var' => 'tax_class', + 'val' => 'vat', + ], + ], + true + ); + + $countries_url = MeprUtils::admin_url( + 'admin-ajax.php', + ['export_vat_countries', 'mepr_vattaxes_nonce'], + [ + 'action' => 'mepr_vat_country_report', + 'export' => $type, + ], + true + ); + + ?> + + + ID,'mepr_vat_number',true)) && - !empty($vnum)) { - return $vnum; - } + $year = (isset($_REQUEST['year']) && !empty($_REQUEST['year'])) ? $_REQUEST['year'] : date('Y'); + $product = (isset($_REQUEST['product']) && $_REQUEST['product'] != 'all') ? $_REQUEST['product'] : 'all'; - return ''; - } - - public function vat_csv_buttons($type='monthly') { - // Download transactions with VAT country, business/consumer, and VAT number - - $totals_url = MeprUtils::admin_url( - 'admin-ajax.php', - array('export_report','mepr_reports_nonce'), - array( - 'action' => 'mepr_export_report', - 'export' => $type, - 'q' => array( - 'var' => 'tax_class', - 'val' => 'vat', - ) - ), - true - ); - - $countries_url = MeprUtils::admin_url( - 'admin-ajax.php', - array('export_vat_countries', 'mepr_vattaxes_nonce'), - array( - 'action' => 'mepr_vat_country_report', - 'export' => $type - ), - true - ); - - ?> - - - get_country_vat_data($year, $month, $product); - public function country_vat_csv() { - check_ajax_referer('export_vat_countries', 'mepr_vattaxes_nonce'); + $countries = $this->get_vat_countries(); + for ($i = 0; $i < count($data); $i++) { + if (isset($countries[$data[$i][__('Country Code', 'memberpress')]]['name'])) { + $data[$i][__('Country', 'memberpress')] = $countries[$data[$i][__('Country Code', 'memberpress')]]['name']; + } else { + $data[$i][__('Country', 'memberpress')] = $data[$i][__('Country Code', 'memberpress')]; + } + } - $type = (isset($_REQUEST['export']) && !empty($_REQUEST['export']))?$_REQUEST['export']:'monthly'; + $filename = sprintf( + 'memberpress%s-vat-by-country%s%s-for%s', + "-{$type}", + ($month ? "-{$month}" : ''), + "-{$year}", + "-{$product}" + ); - if($type=='monthly') { - $month = (isset($_REQUEST['month']) && !empty($_REQUEST['month']))?$_REQUEST['month']:date('n'); - } - else { - $month = false; + MeprUtils::render_csv($data, $filename); + exit(); } - $year = (isset($_REQUEST['year']) && !empty($_REQUEST['year']))?$_REQUEST['year']:date('Y'); - $product = (isset($_REQUEST['product']) && $_REQUEST['product'] != 'all')?$_REQUEST['product']:'all'; + // VAT tax collected by country per month + public function get_country_vat_data($year = false, $month = false, $product = 'all') + { + global $wpdb; + $mepr_db = new MeprDb(); - $data = $this->get_country_vat_data($year, $month, $product); + $andmonth = $month ? $wpdb->prepare(' AND MONTH(created_at) = %d', $month) : ''; + $andyear = $year ? $wpdb->prepare(' AND YEAR(created_at) = %d', $year) : ''; + $andproduct = (!isset($product) || empty($product) || $product == 'all') ? '' : " AND product_id = {$product}"; - $countries = $this->get_vat_countries(); - for($i=0; $iprepare(' AND MONTH(created_at) = %d', $month) : ''; - $andyear = $year ? $wpdb->prepare(' AND YEAR(created_at) = %d', $year) : ''; - $andproduct = (!isset($product) || empty($product) || $product == 'all') ? '' : " AND product_id = {$product}"; - - $q = " - SELECT um.meta_value AS \"" . __('Country Code', 'memberpress') . "\", - SUM(tr.tax_amount) AS " . __('Total', 'memberpress') . " + $q = ' + SELECT um.meta_value AS "' . __('Country Code', 'memberpress') . '", + SUM(tr.tax_amount) AS ' . __('Total', 'memberpress') . " FROM {$mepr_db->transactions} AS tr INNER JOIN {$wpdb->usermeta} AS um ON um.user_id=tr.user_id AND um.meta_key='mepr-address-country' WHERE tr.tax_class='vat' - AND tr.txn_type = '".MeprTransaction::$payment_str."' - AND tr.status = '".MeprTransaction::$complete_str."' + AND tr.txn_type = '" . MeprTransaction::$payment_str . "' + AND tr.status = '" . MeprTransaction::$complete_str . "' {$andmonth} {$andyear} {$andproduct} GROUP BY um.meta_value "; - $res = $wpdb->get_results($q, ARRAY_A); + $res = $wpdb->get_results($q, ARRAY_A); - return $res; - } + return $res; + } - // VAT tax ALWAYS uses the customer address now ... so we override that here - public function use_customer_address($use, $usr) { - $countries = $this->get_vat_countries(); - $customer_country = $usr->address('country'); + // VAT tax ALWAYS uses the customer address now ... so we override that here + public function use_customer_address($use, $usr) + { + $countries = $this->get_vat_countries(); + $customer_country = $usr->address('country'); - if(isset($countries[$customer_country])) { - return true; - } - else { - return $use; + if (isset($countries[$customer_country])) { + return true; + } else { + return $use; + } } - } - public function extra_profile_fields($usr) { - $tax_rate = $usr->tax_rate(); + public function extra_profile_fields($usr) + { + $tax_rate = $usr->tax_rate(); - // We're showing these regardless - $ctype = self::get_customer_type($usr); - $vnum = self::get_vat_number($usr); - MeprView::render('/admin/taxes/vat_profile_fields', get_defined_vars()); - } + // We're showing these regardless + $ctype = self::get_customer_type($usr); + $vnum = self::get_vat_number($usr); + MeprView::render('/admin/taxes/vat_profile_fields', get_defined_vars()); + } - public function validate_extra_profile_fields($errors, $update, $user) { - if (MeprUtils::is_logged_in_and_an_admin()) { - if($update === false) { return $errors; } + public function validate_extra_profile_fields($errors, $update, $user) + { + if (MeprUtils::is_logged_in_and_an_admin()) { + if ($update === false) { + return $errors; + } - $ctype = self::get_customer_type($user); - $vnum = self::get_vat_number($user); - $country = get_user_meta($user->ID, 'mepr-address-country', true); + $ctype = self::get_customer_type($user); + $vnum = self::get_vat_number($user); + $country = get_user_meta($user->ID, 'mepr-address-country', true); - if($ctype=='business' && !empty($vnum) && !empty($country) && !$this->vat_number_is_valid($vnum, $country)) { - $errors->add('mepr_vat_number', __('VAT number is invalid', 'memberpress')); - } + if ($ctype == 'business' && !empty($vnum) && !empty($country) && !$this->vat_number_is_valid($vnum, $country)) { + $errors->add('mepr_vat_number', __('VAT number is invalid', 'memberpress')); + } + } } - } - public function save_extra_profile_fields($user_id) { - if (MeprUtils::is_logged_in_and_an_admin()) { - if(isset($_POST['mepr_vat_number'])) { - update_user_meta($user_id, 'mepr_vat_number', sanitize_text_field($_POST['mepr_vat_number'])); - } + public function save_extra_profile_fields($user_id) + { + if (MeprUtils::is_logged_in_and_an_admin()) { + if (isset($_POST['mepr_vat_number'])) { + update_user_meta($user_id, 'mepr_vat_number', sanitize_text_field($_POST['mepr_vat_number'])); + } - if(isset($_POST['mepr_vat_customer_type'])) { - update_user_meta($user_id, 'mepr_vat_customer_type', sanitize_text_field($_POST['mepr_vat_customer_type'])); - } + if (isset($_POST['mepr_vat_customer_type'])) { + update_user_meta($user_id, 'mepr_vat_customer_type', sanitize_text_field($_POST['mepr_vat_customer_type'])); + } + } } - } - private function get_vat_countries() { - $mepr_options = MeprOptions::fetch(); + private function get_vat_countries() + { + $mepr_options = MeprOptions::fetch(); - $moss_country = get_option('mepr_vat_country'); - $countries = require(MEPR_DATA_PATH.'/taxes/vat_countries.php'); + $moss_country = get_option('mepr_vat_country'); + $countries = require(MEPR_DATA_PATH . '/taxes/vat_countries.php'); - return $countries; - } + return $countries; + } - private function vat_calc_possible() { - $mepr_options = MeprOptions::fetch(); - global $post; + private function vat_calc_possible() + { + $mepr_options = MeprOptions::fetch(); + global $post; - // Remove VAT Inputs on Tax-Exempt Memberships - if ( false !== ( $prd = MeprProduct::is_product_page($post) ) ){ - if ( $prd->ID && $prd->is_tax_exempt() ){ - return false; - } - } + // Remove VAT Inputs on Tax-Exempt Memberships + if (false !== ( $prd = MeprProduct::is_product_page($post) )) { + if ($prd->ID && $prd->is_tax_exempt()) { + return false; + } + } - return (!MeprUtils::is_user_logged_in() || + return (!MeprUtils::is_user_logged_in() || ($mepr_options->show_fields_logged_in_purchases && $mepr_options->show_address_fields && $mepr_options->require_address_fields)); - } - - private function is_valid_vat_removal_rule($is_valid_vat, $customer_type, $vat_country, $usr_country) { - if(!$is_valid_vat) { - return false; } - if($customer_type !== 'business') { - return false; - } + private function is_valid_vat_removal_rule($is_valid_vat, $customer_type, $vat_country, $usr_country) + { + if (!$is_valid_vat) { + return false; + } - if(!MeprTransactionsHelper::is_charging_business_net_price()) { - return false; - } + if ($customer_type !== 'business') { + return false; + } - $vat_countries = $this->get_vat_countries(); + if (!MeprTransactionsHelper::is_charging_business_net_price()) { + return false; + } - if(!array_key_exists($vat_country, $vat_countries)) { - return false; - } + $vat_countries = $this->get_vat_countries(); - if(empty($usr_country) || !array_key_exists($usr_country, $vat_countries)) { - return false; - } + if (!array_key_exists($vat_country, $vat_countries)) { + return false; + } - // VAT is charged if Customer is a business with a valid VAT number, but the merchant and customer are both from the same country. - if($usr_country == $vat_country) { - return false; - } + if (empty($usr_country) || !array_key_exists($usr_country, $vat_countries)) { + return false; + } - return true; - } - - /** - * Remove the VAT portion from the given price, if charging net price with a valid VAT number. - * - * @param string $price - * @param MeprProduct $prd - * @param MeprUser $usr - * @return string - */ - public function maybe_apply_vat_reversal($price, $prd, $usr) { - if(get_option('mepr_calculate_taxes') && get_option('mepr_vat_enabled') && MeprTransactionsHelper::is_charging_business_net_price()) { - $tax_rate = $usr->tax_rate($prd->ID); - - if($tax_rate->customer_type === 'business' && $tax_rate->reversal) { - $subtotal = $usr->calculate_subtotal($price, null, 2, $prd); - $tax_amount = MeprUtils::format_float(($subtotal*($tax_rate->tax_rate/100.00))); - $price = MeprUtils::format_float((float) $price - (float) $tax_amount); - } - } + // VAT is charged if Customer is a business with a valid VAT number, but the merchant and customer are both from the same country. + if ($usr_country == $vat_country) { + return false; + } + + return true; + } + + /** + * Remove the VAT portion from the given price, if charging net price with a valid VAT number. + * + * @param string $price + * @param MeprProduct $prd + * @param MeprUser $usr + * @return string + */ + public function maybe_apply_vat_reversal($price, $prd, $usr) + { + if (get_option('mepr_calculate_taxes') && get_option('mepr_vat_enabled') && MeprTransactionsHelper::is_charging_business_net_price()) { + $tax_rate = $usr->tax_rate($prd->ID); + + if ($tax_rate->customer_type === 'business' && $tax_rate->reversal) { + $subtotal = $usr->calculate_subtotal($price, null, 2, $prd); + $tax_amount = MeprUtils::format_float(($subtotal * ($tax_rate->tax_rate / 100.00))); + $price = MeprUtils::format_float((float) $price - (float) $tax_amount); + } + } - return $price; - } + return $price; + } } diff --git a/app/controllers/MeprZxcvbnCtrl.php b/app/controllers/MeprZxcvbnCtrl.php index 1a4fbd5..19d3901 100644 --- a/app/controllers/MeprZxcvbnCtrl.php +++ b/app/controllers/MeprZxcvbnCtrl.php @@ -1,120 +1,132 @@ enforce_strong_password) { - add_filter('mepr-signup-scripts', 'MeprZxcvbnCtrl::load_scripts', 10, 3); - add_action('wp_enqueue_scripts', 'MeprZxcvbnCtrl::load_reset_password_scripts'); - add_action('mepr-after-password-fields', 'MeprZxcvbnCtrl::display_meter'); - add_action('mepr-account-after-password-fields', 'MeprZxcvbnCtrl::display_meter'); - add_action('mepr-reset-password-after-password-fields', 'MeprZxcvbnCtrl::display_meter'); - add_filter('mepr-validate-signup', 'MeprZxcvbnCtrl::validate_signup'); + +if (!defined('ABSPATH')) { + die('You are not allowed to call this page directly.'); +} + +class MeprZxcvbnCtrl extends MeprBaseCtrl +{ + public function load_hooks() + { + $mepr_options = MeprOptions::fetch(); + + if ($mepr_options->enforce_strong_password) { + add_filter('mepr-signup-scripts', 'MeprZxcvbnCtrl::load_scripts', 10, 3); + add_action('wp_enqueue_scripts', 'MeprZxcvbnCtrl::load_reset_password_scripts'); + add_action('mepr-after-password-fields', 'MeprZxcvbnCtrl::display_meter'); + add_action('mepr-account-after-password-fields', 'MeprZxcvbnCtrl::display_meter'); + add_action('mepr-reset-password-after-password-fields', 'MeprZxcvbnCtrl::display_meter'); + add_filter('mepr-validate-signup', 'MeprZxcvbnCtrl::validate_signup'); + } } - } - public static function validate_signup($errors) { - if(isset($_POST['mp-pass-strength']) && (int)$_POST['mp-pass-strength'] < self::get_required_int()) { - $errors['mepr_user_password'] = sprintf(__('This password doesn\'t meet the minimum strength requirement. %s.', 'memberpress'), self::get_required_str()); + public static function validate_signup($errors) + { + if (isset($_POST['mp-pass-strength']) && (int)$_POST['mp-pass-strength'] < self::get_required_int()) { + $errors['mepr_user_password'] = sprintf(__('This password doesn\'t meet the minimum strength requirement. %s.', 'memberpress'), self::get_required_str()); + } + + return $errors; } - return $errors; - } - - public static function get_i18n_array() { - //Weak is actually still relatively strong, so we're going to alter the mapping a bit - return array( - 'script_url' => MEPR_JS_URL.'/zxcvbn.js', - 'very_weak' => __('Weak', 'memberpress'), - 'weak' => __('Medium', 'memberpress'), - 'medium' => __('Strong', 'memberpress'), - 'strong' => __('Very Strong', 'memberpress'), - 'very_strong' => __('Unbreakable', 'memberpress'), - 'required' => '', - 'indicator' => __('Password Strength', 'memberpress') - ); - } - - public static function load_scripts($reqs, $is_prod_page, $is_acct_page) { - $mepr_options = MeprOptions::fetch(); - - if($mepr_options->global_styles || $is_prod_page || $is_acct_page) { - $i18n = self::get_i18n_array(); - - wp_register_script('mepr-zxcvbn', MEPR_JS_URL.'/zxcvbn-async.js'); - wp_localize_script('mepr-zxcvbn', 'MeprZXCVBN', $i18n); - wp_enqueue_style('mepr-zxcvbn-css', MEPR_CSS_URL.'/zxcvbn.css'); - - $reqs[] = 'mepr-zxcvbn'; + public static function get_i18n_array() + { + // Weak is actually still relatively strong, so we're going to alter the mapping a bit + return [ + 'script_url' => MEPR_JS_URL . '/zxcvbn.js', + 'very_weak' => __('Weak', 'memberpress'), + 'weak' => __('Medium', 'memberpress'), + 'medium' => __('Strong', 'memberpress'), + 'strong' => __('Very Strong', 'memberpress'), + 'very_strong' => __('Unbreakable', 'memberpress'), + 'required' => '', + 'indicator' => __('Password Strength', 'memberpress'), + ]; } - return $reqs; - } + public static function load_scripts($reqs, $is_prod_page, $is_acct_page) + { + $mepr_options = MeprOptions::fetch(); + + if ($mepr_options->global_styles || $is_prod_page || $is_acct_page) { + $i18n = self::get_i18n_array(); - public static function load_reset_password_scripts() { - $mepr_options = MeprOptions::fetch(); + wp_register_script('mepr-zxcvbn', MEPR_JS_URL . '/zxcvbn-async.js'); + wp_localize_script('mepr-zxcvbn', 'MeprZXCVBN', $i18n); + wp_enqueue_style('mepr-zxcvbn-css', MEPR_CSS_URL . '/zxcvbn.css'); - if(!$mepr_options->global_styles && isset($_GET['action']) && $_GET['action'] == 'reset_password') { - $i18n = self::get_i18n_array(); + $reqs[] = 'mepr-zxcvbn'; + } - wp_register_script('mepr-zxcvbn', MEPR_JS_URL.'/zxcvbn-async.js', array('jquery')); - wp_localize_script('mepr-zxcvbn', 'MeprZXCVBN', $i18n); - wp_enqueue_script('mepr-zxcvbn'); - wp_enqueue_style('mepr-zxcvbn-css', MEPR_CSS_URL.'/zxcvbn.css'); + return $reqs; } - } - public static function display_meter($user = null) { - $required_str = self::get_required_str(); + public static function load_reset_password_scripts() + { + $mepr_options = MeprOptions::fetch(); - ?> + if (!$mepr_options->global_styles && isset($_GET['action']) && $_GET['action'] == 'reset_password') { + $i18n = self::get_i18n_array(); + + wp_register_script('mepr-zxcvbn', MEPR_JS_URL . '/zxcvbn-async.js', ['jquery']); + wp_localize_script('mepr-zxcvbn', 'MeprZXCVBN', $i18n); + wp_enqueue_script('mepr-zxcvbn'); + wp_enqueue_style('mepr-zxcvbn-css', MEPR_CSS_URL . '/zxcvbn.css'); + } + } + + public static function display_meter($user = null) + { + $required_str = self::get_required_str(); + + ?>
    - +   - +
    - enforce_strong_password) { - case 'weak': // Mapped as "Medium" - $txt = __('Password must be "Medium" or stronger', 'memberpress'); - break; - case 'medium': // Mapped as "Strong" - $txt = __('Password must be "Strong" or stronger', 'memberpress'); - break; - case 'strong': // Mapped as "Very Strong" - $txt = __('Password must be "Very Strong" or stronger', 'memberpress'); - break; + enforce_strong_password) { + case 'weak': // Mapped as "Medium" + $txt = __('Password must be "Medium" or stronger', 'memberpress'); + break; + case 'medium': // Mapped as "Strong" + $txt = __('Password must be "Strong" or stronger', 'memberpress'); + break; + case 'strong': // Mapped as "Very Strong" + $txt = __('Password must be "Very Strong" or stronger', 'memberpress'); + break; + } + + return MeprHooks::apply_filters('mepr-password-meter-text', $txt, $mepr_options->enforce_strong_password); } - return MeprHooks::apply_filters('mepr-password-meter-text', $txt, $mepr_options->enforce_strong_password); - } - - public static function get_required_int() { - $mepr_options = MeprOptions::fetch(); - - switch($mepr_options->enforce_strong_password) { - case 'weak': // Mapped as "Medium" - return 1; - case 'medium': // Mapped as "Strong" - return 2; - break; - case 'strong': // Mapped as "Very Strong" - return 3; - default: - return 0; // Not required + public static function get_required_int() + { + $mepr_options = MeprOptions::fetch(); + + switch ($mepr_options->enforce_strong_password) { + case 'weak': // Mapped as "Medium" + return 1; + case 'medium': // Mapped as "Strong" + return 2; + break; + case 'strong': // Mapped as "Very Strong" + return 3; + default: + return 0; // Not required + } } - } } //End class diff --git a/app/controllers/index.php b/app/controllers/index.php index 1641e43..82ca979 100644 --- a/app/controllers/index.php +++ b/app/controllers/index.php @@ -1 +1,3 @@ - + (object)array( - 'unique' => true - ), - 'member-signup-completed' => (object)array( - 'unique' => true - ), - 'member-account-updated' => (object)array( - 'unique' => false - ), - 'member-deleted' => (object)array( - 'unique' => true - ), - 'login' => (object)array( - 'unique' => false - ), - /***** Events for Subscriptions *****/ - 'subscription-created' => (object)array( - 'unique' => true - ), - 'subscription-paused' => (object)array( - 'unique' => false - ), - 'subscription-resumed' => (object)array( - 'unique' => false - ), - 'subscription-stopped' => (object)array( - 'unique' => true - ), - 'subscription-upgraded' => (object)array( - 'unique' => true - ), - 'subscription-downgraded' => (object)array( - 'unique' => true - ), - 'subscription-upgraded-to-one-time' => (object)array( - 'unique' => true - ), - 'subscription-upgraded-to-recurring' => (object)array( - 'unique' => true - ), - 'subscription-downgraded-to-one-time' => (object)array( - 'unique' => true - ), - 'subscription-downgraded-to-recurring' => (object)array( - 'unique' => true - ), - 'subscription-expired' => (object)array( - 'unique' => false - ), +return [ + /** + * Events for Members + */ + 'member-added' => (object) [ + 'unique' => true, + ], + 'member-signup-completed' => (object) [ + 'unique' => true, + ], + 'member-account-updated' => (object) [ + 'unique' => false, + ], + 'member-deleted' => (object) [ + 'unique' => true, + ], + 'login' => (object) [ + 'unique' => false, + ], - /***** Events for Transactions *****/ - 'transaction-completed' => (object)array( - 'unique' => true - ), - 'transaction-refunded' => (object)array( - 'unique' => true - ), - 'transaction-failed' => (object)array( - 'unique' => true - ), - 'transaction-expired' => (object)array( - 'unique' => true - ), - 'offline-payment-pending' => (object)array( - 'unique' => true - ), - 'offline-payment-complete' => (object)array( - 'unique' => true - ), - 'offline-payment-refunded' => (object)array( - 'unique' => true - ), - // Recurring Transactions - 'recurring-transaction-completed' => (object)array( - 'unique' => true - ), - 'renewal-transaction-completed' => (object)array( - 'unique' => true - ), - 'recurring-transaction-failed' => (object)array( - 'unique' => true - ), - 'recurring-transaction-expired' => (object)array( - 'unique' => true - ), - // Non-Recurring Transactions - 'non-recurring-transaction-completed' => (object)array( - 'unique' => true - ), - 'non-recurring-transaction-expired' => (object)array( - 'unique' => true - ), + /** + * Events for Subscriptions + */ + 'subscription-created' => (object) [ + 'unique' => true, + ], + 'subscription-paused' => (object) [ + 'unique' => false, + ], + 'subscription-resumed' => (object) [ + 'unique' => false, + ], + 'subscription-stopped' => (object) [ + 'unique' => true, + ], + 'subscription-upgraded' => (object) [ + 'unique' => true, + ], + 'subscription-downgraded' => (object) [ + 'unique' => true, + ], + 'subscription-upgraded-to-one-time' => (object) [ + 'unique' => true, + ], + 'subscription-upgraded-to-recurring' => (object) [ + 'unique' => true, + ], + 'subscription-downgraded-to-one-time' => (object) [ + 'unique' => true, + ], + 'subscription-downgraded-to-recurring' => (object) [ + 'unique' => true, + ], + 'subscription-expired' => (object) [ + 'unique' => false, + ], - /***** Events from Reminders *****/ - // Note, uniqueness of Reminders is handled by the reminders routines - // So all reminders should be classified as non-unique here. - 'after-member-signup-reminder' => (object)array( - 'unique' => false - ), - 'after-signup-abandoned-reminder' => (object)array( - 'unique' => false - ), - 'before-sub-expires-reminder' => (object)array( - 'unique' => false - ), - 'after-sub-expires-reminder' => (object)array( - 'unique' => false - ), - 'before-sub-renews-reminder' => (object)array( - 'unique' => false - ), - 'after-cc-expires-reminder' => (object)array( - 'unique' => false - ), - 'before-cc-expires-reminder' => (object)array( - 'unique' => false - ), - 'before-sub-trial-ends' => (object)array( - 'unique' => false - ), + /** + * Events for Transactions + */ + 'transaction-completed' => (object) [ + 'unique' => true, + ], + 'transaction-refunded' => (object) [ + 'unique' => true, + ], + 'transaction-failed' => (object) [ + 'unique' => true, + ], + 'transaction-expired' => (object) [ + 'unique' => true, + ], + 'offline-payment-pending' => (object) [ + 'unique' => true, + ], + 'offline-payment-complete' => (object) [ + 'unique' => true, + ], + 'offline-payment-refunded' => (object) [ + 'unique' => true, + ], + // Recurring Transactions + 'recurring-transaction-completed' => (object) [ + 'unique' => true, + ], + 'renewal-transaction-completed' => (object) [ + 'unique' => true, + ], + 'recurring-transaction-failed' => (object) [ + 'unique' => true, + ], + 'recurring-transaction-expired' => (object) [ + 'unique' => true, + ], + // Non-Recurring Transactions + 'non-recurring-transaction-completed' => (object) [ + 'unique' => true, + ], + 'non-recurring-transaction-expired' => (object) [ + 'unique' => true, + ], - /** Events for Corporate Accounts */ - 'sub-account-added' => (object)array( - 'unique' => false - ), - 'sub-account-removed' => (object)array( - 'unique' => false - ), + /** + * Events from Reminders + */ + // Note, uniqueness of Reminders is handled by the reminders routines + // So all reminders should be classified as non-unique here. + 'after-member-signup-reminder' => (object) [ + 'unique' => false, + ], + 'after-signup-abandoned-reminder' => (object) [ + 'unique' => false, + ], + 'before-sub-expires-reminder' => (object) [ + 'unique' => false, + ], + 'after-sub-expires-reminder' => (object) [ + 'unique' => false, + ], + 'before-sub-renews-reminder' => (object) [ + 'unique' => false, + ], + 'after-cc-expires-reminder' => (object) [ + 'unique' => false, + ], + 'before-cc-expires-reminder' => (object) [ + 'unique' => false, + ], + 'before-sub-trial-ends' => (object) [ + 'unique' => false, + ], - /***** Events for Courses *****/ - 'mpca-course-started' => (object)array( - 'unique' => false - ), - 'mpca-course-completed' => (object)array( - 'unique' => false - ), - 'mpca-lesson-started' => (object)array( - 'unique' => false - ), - 'mpca-lesson-completed' => (object)array( - 'unique' => false - ), - 'mpca-quiz-attempt-completed' => (object)array( - 'unique' => false - ) -); + /** + * Events for Corporate Accounts + */ + 'sub-account-added' => (object) [ + 'unique' => false, + ], + 'sub-account-removed' => (object) [ + 'unique' => false, + ], + /** + * Events for Courses + */ + 'mpca-course-started' => (object) [ + 'unique' => false, + ], + 'mpca-course-completed' => (object) [ + 'unique' => false, + ], + 'mpca-lesson-started' => (object) [ + 'unique' => false, + ], + 'mpca-lesson-completed' => (object) [ + 'unique' => false, + ], + 'mpca-quiz-attempt-completed' => (object) [ + 'unique' => false, + ], +]; diff --git a/app/data/features/editions.php b/app/data/features/editions.php index e1911b9..9869067 100644 --- a/app/data/features/editions.php +++ b/app/data/features/editions.php @@ -1,91 +1,166 @@ array( - 'payments' => array( - 'MeprStripeGateway', 'MeprPayPalCommerceGateway', 'MeprPayPalStandardGateway' - ), - 'addons' => array( - 'memberpress-courses', 'memberpress-downloads' - ) - ), - 'developer' => array( - 'payments' => array( - 'MeprStripeGateway', 'MeprPayPalCommerceGateway', 'MeprAuthorizeGateway', 'MeprPayPalStandardGateway' - ), - 'addons' => array( - 'memberpress-courses', 'memberpress-downloads', 'memberpress-buddypress', 'memberpress-developer-tools', 'memberpress-gifting', 'memberpress-corporate' - ) - ), - 'memberpress-plus' => array( - 'payments' => array( - 'MeprStripeGateway', 'MeprPayPalCommerceGateway', 'MeprPayPalStandardGateway' - ), - 'addons' => array( - 'memberpress-courses', 'memberpress-downloads', 'memberpress-buddypress', 'memberpress-developer-tools', 'memberpress-gifting' - ) - ), - 'memberpress-pro' => array( - 'payments' => array( - 'MeprStripeGateway', 'MeprPayPalCommerceGateway', 'MeprAuthorizeGateway', 'MeprPayPalStandardGateway' - ), - 'addons' => array( - 'memberpress-courses', 'memberpress-downloads', 'memberpress-buddypress', 'memberpress-developer-tools', 'memberpress-gifting', 'memberpress-corporate' - ) - ), - 'memberpress-basic' => array( - 'payments' => array( - 'MeprStripeGateway', 'MeprPayPalCommerceGateway', 'MeprPayPalStandardGateway' - ), - 'addons' => array( - 'memberpress-courses', 'memberpress-downloads' - ) - ), - 'memberpress-plus-2' => array( - 'payments' => array( - 'MeprStripeGateway', 'MeprPayPalCommerceGateway', 'MeprPayPalStandardGateway' - ), - 'addons' => array( - 'memberpress-courses', 'memberpress-downloads', 'memberpress-buddypress', 'memberpress-developer-tools' - ) - ), - 'memberpress-pro-5' => array( - 'payments' => array( - 'MeprStripeGateway', 'MeprPayPalCommerceGateway', 'MeprAuthorizeGateway', 'MeprPayPalStandardGateway' - ), - 'addons' => array( - 'memberpress-courses', 'memberpress-downloads', 'memberpress-buddypress', 'memberpress-developer-tools', 'memberpress-gifting', 'memberpress-corporate' - ) - ), - 'memberpress-reseller-nhw' => array( - 'payments' => array( - 'MeprStripeGateway', 'MeprPayPalCommerceGateway', 'MeprAuthorizeGateway', 'MeprPayPalStandardGateway' - ), - 'addons' => array( - 'memberpress-courses', 'memberpress-downloads', 'memberpress-buddypress', 'memberpress-developer-tools', 'memberpress-gifting', 'memberpress-corporate' - ) - ), - 'memberpress-reseller-sng' => array( - 'payments' => array( - 'MeprStripeGateway', 'MeprPayPalCommerceGateway', 'MeprAuthorizeGateway', 'MeprPayPalStandardGateway' - ), - 'addons' => array( - 'memberpress-courses', 'memberpress-downloads', 'memberpress-buddypress', 'memberpress-developer-tools', 'memberpress-gifting', 'memberpress-corporate' - ) - ), - 'memberpress-oem' => array( - 'payments' => array( - 'MeprStripeGateway', 'MeprPayPalCommerceGateway', 'MeprAuthorizeGateway', 'MeprPayPalStandardGateway' - ), - 'addons' => array( - 'memberpress-courses', 'memberpress-downloads', 'memberpress-buddypress', 'memberpress-developer-tools', 'memberpress-gifting', 'memberpress-corporate' - ) - ), - 'memberpress-elite' => array( - 'payments' => array( - 'MeprStripeGateway', 'MeprPayPalCommerceGateway', 'MeprAuthorizeGateway', 'MeprPayPalStandardGateway' - ), - 'addons' => array( - 'memberpress-courses', 'memberpress-downloads', 'memberpress-buddypress', 'memberpress-developer-tools', 'memberpress-gifting', 'memberpress-corporate', 'memberpress-coachkit' - ) - ), -); + +return [ + 'business' => [ + 'payments' => [ + 'MeprStripeGateway', + 'MeprPayPalCommerceGateway', + 'MeprPayPalStandardGateway', + ], + 'addons' => [ + 'memberpress-courses', + 'memberpress-downloads', + ], + ], + 'developer' => [ + 'payments' => [ + 'MeprStripeGateway', + 'MeprPayPalCommerceGateway', + 'MeprAuthorizeGateway', + 'MeprPayPalStandardGateway', + ], + 'addons' => [ + 'memberpress-courses', + 'memberpress-downloads', + 'memberpress-buddypress', + 'memberpress-developer-tools', + 'memberpress-gifting', + 'memberpress-corporate', + ], + ], + 'memberpress-plus' => [ + 'payments' => [ + 'MeprStripeGateway', + 'MeprPayPalCommerceGateway', + 'MeprPayPalStandardGateway', + ], + 'addons' => [ + 'memberpress-courses', + 'memberpress-downloads', + 'memberpress-buddypress', + 'memberpress-developer-tools', + 'memberpress-gifting', + ], + ], + 'memberpress-pro' => [ + 'payments' => [ + 'MeprStripeGateway', + 'MeprPayPalCommerceGateway', + 'MeprAuthorizeGateway', + 'MeprPayPalStandardGateway', + ], + 'addons' => [ + 'memberpress-courses', + 'memberpress-downloads', + 'memberpress-buddypress', + 'memberpress-developer-tools', + 'memberpress-gifting', + 'memberpress-corporate', + ], + ], + 'memberpress-basic' => [ + 'payments' => [ + 'MeprStripeGateway', + 'MeprPayPalCommerceGateway', + 'MeprPayPalStandardGateway', + ], + 'addons' => [ + 'memberpress-courses', + 'memberpress-downloads', + ], + ], + 'memberpress-plus-2' => [ + 'payments' => [ + 'MeprStripeGateway', + 'MeprPayPalCommerceGateway', + 'MeprPayPalStandardGateway', + ], + 'addons' => [ + 'memberpress-courses', + 'memberpress-downloads', + 'memberpress-buddypress', + 'memberpress-developer-tools', + ], + ], + 'memberpress-pro-5' => [ + 'payments' => [ + 'MeprStripeGateway', + 'MeprPayPalCommerceGateway', + 'MeprAuthorizeGateway', + 'MeprPayPalStandardGateway', + ], + 'addons' => [ + 'memberpress-courses', + 'memberpress-downloads', + 'memberpress-buddypress', + 'memberpress-developer-tools', + 'memberpress-gifting', + 'memberpress-corporate', + ], + ], + 'memberpress-reseller-nhw' => [ + 'payments' => [ + 'MeprStripeGateway', + 'MeprPayPalCommerceGateway', + 'MeprAuthorizeGateway', + 'MeprPayPalStandardGateway', + ], + 'addons' => [ + 'memberpress-courses', + 'memberpress-downloads', + 'memberpress-buddypress', + 'memberpress-developer-tools', + 'memberpress-gifting', + 'memberpress-corporate', + ], + ], + 'memberpress-reseller-sng' => [ + 'payments' => [ + 'MeprStripeGateway', + 'MeprPayPalCommerceGateway', + 'MeprAuthorizeGateway', + 'MeprPayPalStandardGateway', + ], + 'addons' => [ + 'memberpress-courses', + 'memberpress-downloads', + 'memberpress-buddypress', + 'memberpress-developer-tools', + 'memberpress-gifting', + 'memberpress-corporate', + ], + ], + 'memberpress-oem' => [ + 'payments' => [ + 'MeprStripeGateway', + 'MeprPayPalCommerceGateway', + 'MeprAuthorizeGateway', + 'MeprPayPalStandardGateway', + ], + 'addons' => [ + 'memberpress-courses', + 'memberpress-downloads', + 'memberpress-buddypress', + 'memberpress-developer-tools', + 'memberpress-gifting', + 'memberpress-corporate', + ], + ], + 'memberpress-elite' => [ + 'payments' => [ + 'MeprStripeGateway', + 'MeprPayPalCommerceGateway', + 'MeprAuthorizeGateway', + 'MeprPayPalStandardGateway', + ], + 'addons' => [ + 'memberpress-courses', + 'memberpress-downloads', + 'memberpress-buddypress', + 'memberpress-developer-tools', + 'memberpress-gifting', + 'memberpress-corporate', + 'memberpress-coachkit', + ], + ], +]; diff --git a/app/data/minimum_charge_amounts.php b/app/data/minimum_charge_amounts.php index aa8f17d..3c882a3 100644 --- a/app/data/minimum_charge_amounts.php +++ b/app/data/minimum_charge_amounts.php @@ -1,30 +1,31 @@ 0.50, - 'AED' => 2.00, - 'AUD' => 0.50, - 'BGN' => 1.00, - 'BRL' => 0.50, - 'CAD' => 0.50, - 'CHF' => 0.50, - 'CZK' => 15.00, - 'DKK' => 2.50, - 'EUR' => 0.50, - 'GBP' => 0.30, - 'HKD' => 4.00, - 'HRK' => 0.50, - 'HUF' => 175.00, - 'INR' => 0.50, - 'JPY' => 50, - 'MXN' => 10, - 'MYR' => 2, - 'NOK' => 3.00, - 'NZD' => 0.50, - 'PLN' => 2.00, - 'RON' => 2.00, - 'SEK' => 3.00, - 'SGD' => 0.50, - 'THB' => 10, -)); +return MeprHooks::apply_filters('mepr_minimum_charge_amounts', [ + 'USD' => 0.50, + 'AED' => 2.00, + 'AUD' => 0.50, + 'BGN' => 1.00, + 'BRL' => 0.50, + 'CAD' => 0.50, + 'CHF' => 0.50, + 'CZK' => 15.00, + 'DKK' => 2.50, + 'EUR' => 0.50, + 'GBP' => 0.30, + 'HKD' => 4.00, + 'HRK' => 0.50, + 'HUF' => 175.00, + 'INR' => 0.50, + 'JPY' => 50, + 'MXN' => 10, + 'MYR' => 2, + 'NOK' => 3.00, + 'NZD' => 0.50, + 'PLN' => 2.00, + 'RON' => 2.00, + 'SEK' => 3.00, + 'SGD' => 0.50, + 'THB' => 10, +]); diff --git a/app/data/stripe_payment_methods.php b/app/data/stripe_payment_methods.php index 2f31b4e..2e2219d 100644 --- a/app/data/stripe_payment_methods.php +++ b/app/data/stripe_payment_methods.php @@ -1,185 +1,187 @@ 'link', - 'name' => 'Link (recommended)', - 'currencies' => 'all', - 'capabilities' => ['payment_intents', 'setup_future_usage', 'setup_intents', 'subscriptions'], - ], - // Bank debits - [ - 'key' => 'us_bank_account', - 'name' => 'ACH Direct Debit', - 'currencies' => ['USD'], - 'capabilities' => ['payment_intents', 'setup_future_usage', 'setup_intents', 'subscriptions'], - 'async' => true, - ], - [ - 'key' => 'bacs_debit', - 'name' => 'Bacs Direct Debit', - 'currencies' => ['GBP'], - 'capabilities' => ['payment_intents', 'setup_future_usage', 'subscriptions'], - 'async' => true, - ], - [ - 'key' => 'au_becs_debit', - 'name' => 'BECS Direct Debit', - 'currencies' => ['AUD'], - 'capabilities' => ['payment_intents', 'setup_future_usage', 'setup_intents', 'subscriptions'], - 'async' => true, - ], - [ - 'key' => 'sepa_debit', - 'name' => 'SEPA Direct Debit', - 'currencies' => ['EUR'], - 'capabilities' => ['payment_intents', 'setup_future_usage', 'setup_intents', 'subscriptions'], - 'async' => true, - ], - // Bank redirects - [ - 'key' => 'bancontact', - 'name' => 'Bancontact', - 'currencies' => ['EUR'], - 'capabilities' => ['payment_intents'], - ], - [ - 'key' => 'blik', - 'name' => 'BLIK', - 'currencies' => ['PLN'], - 'capabilities' => ['payment_intents'], - ], - [ - 'key' => 'eps', - 'name' => 'EPS', - 'currencies' => ['EUR'], - 'capabilities' => ['payment_intents'], - ], - [ - 'key' => 'fpx', - 'name' => 'FPX', - 'currencies' => ['MYR'], - 'capabilities' => ['payment_intents'], - ], - [ - 'key' => 'giropay', - 'name' => 'Giropay', - 'currencies' => ['EUR'], - 'capabilities' => ['payment_intents'], - ], - [ - 'key' => 'ideal', - 'name' => 'iDEAL', - 'currencies' => ['EUR'], - 'capabilities' => ['payment_intents'], - ], - [ - 'key' => 'p24', - 'name' => 'P24', - 'currencies' => ['EUR', 'PLN'], - 'capabilities' => ['payment_intents'], - ], - [ - 'key' => 'sofort', - 'name' => 'Sofort', - 'currencies' => ['EUR'], - 'capabilities' => ['payment_intents'], - 'async' => true, - ], - [ - 'key' => 'twint', - 'name' => 'TWINT', - 'currencies' => ['CHF'], - 'capabilities' => ['payment_intents'], - ], - // Buy now, pay later - [ - 'key' => 'affirm', - 'name' => 'Affirm', - 'currencies' => ['USD'], - 'capabilities' => ['payment_intents'], - ], - [ - 'key' => 'afterpay_clearpay', - 'name' => 'Afterpay and Clearpay', - 'currencies' => ['AUD', 'CAD', 'NZD', 'GBP', 'USD'], - 'capabilities' => ['payment_intents'], - ], - [ - 'key' => 'klarna', - 'name' => 'Klarna', - 'currencies' => ['AUD', 'CAD', 'CHF', 'CZK', 'DKK', 'EUR', 'GBP', 'NOK', 'NZD', 'PLN', 'SEK', 'USD'], - 'capabilities' => ['payment_intents'], - ], - // Real-time payments - [ - 'key' => 'paynow', - 'name' => 'PayNow', - 'currencies' => ['SGD'], - 'capabilities' => ['payment_intents'], - ], - [ - 'key' => 'promptpay', - 'name' => 'PromptPay', - 'currencies' => ['THB'], - 'capabilities' => ['payment_intents'], - ], - // Vouchers - [ - 'key' => 'boleto', - 'name' => 'Boleto', - 'currencies' => ['BRL'], - 'capabilities' => ['payment_intents', 'setup_future_usage', 'setup_intents', 'subscriptions'], - 'async' => true, - ], - [ - 'key' => 'konbini', - 'name' => 'Konbini', - 'currencies' => ['JPY'], - 'capabilities' => ['payment_intents'], - 'async' => true, - ], - [ - 'key' => 'oxxo', - 'name' => 'OXXO', - 'currencies' => ['MXN'], - 'capabilities' => ['payment_intents'], - 'async' => true, - ], - // Wallets - [ - 'key' => 'alipay', - 'name' => 'Alipay', - 'currencies' => ['AUD', 'CAD', 'CNY', 'EUR', 'GBP', 'HKD', 'JPY', 'MYR', 'NZD', 'SGD', 'USD'], - 'capabilities' => ['payment_intents'], - ], - [ - 'key' => 'cashapp', - 'name' => 'Cash App Pay', - 'currencies' => ['USD'], - 'capabilities' => ['payment_intents', 'setup_future_usage', 'setup_intents', 'subscriptions'], - ], - [ - 'key' => 'grabpay', - 'name' => 'GrabPay', - 'currencies' => ['MYR', 'SGD'], - 'capabilities' => ['payment_intents'], - ], - [ - 'key' => 'mobilepay', - 'name' => 'MobilePay', - 'currencies' => ['DKK', 'EUR', 'NOK', 'SEK'], - 'capabilities' => ['payment_intents'], - ], - [ - 'key' => 'wechat_pay', - 'name' => 'WeChat Pay', - 'currencies' => ['CNY', 'AUD', 'CAD', 'EUR', 'GBP', 'HKD', 'JPY', 'SGD', 'USD', 'DKK', 'NOK', 'SEK', 'CHF'], - 'capabilities' => ['payment_intents'], - ], + [ + 'key' => 'link', + 'name' => 'Link (recommended)', + 'currencies' => 'all', + 'capabilities' => ['payment_intents', 'setup_future_usage', 'setup_intents', 'subscriptions'], + ], + // Bank debits + [ + 'key' => 'us_bank_account', + 'name' => 'ACH Direct Debit', + 'currencies' => ['USD'], + 'capabilities' => ['payment_intents', 'setup_future_usage', 'setup_intents', 'subscriptions'], + 'async' => true, + ], + [ + 'key' => 'bacs_debit', + 'name' => 'Bacs Direct Debit', + 'currencies' => ['GBP'], + 'capabilities' => ['payment_intents', 'setup_future_usage', 'subscriptions'], + 'async' => true, + ], + [ + 'key' => 'au_becs_debit', + 'name' => 'BECS Direct Debit', + 'currencies' => ['AUD'], + 'capabilities' => ['payment_intents', 'setup_future_usage', 'setup_intents', 'subscriptions'], + 'async' => true, + ], + [ + 'key' => 'sepa_debit', + 'name' => 'SEPA Direct Debit', + 'currencies' => ['EUR'], + 'capabilities' => ['payment_intents', 'setup_future_usage', 'setup_intents', 'subscriptions'], + 'async' => true, + ], + // Bank redirects + [ + 'key' => 'bancontact', + 'name' => 'Bancontact', + 'currencies' => ['EUR'], + 'capabilities' => ['payment_intents'], + ], + [ + 'key' => 'blik', + 'name' => 'BLIK', + 'currencies' => ['PLN'], + 'capabilities' => ['payment_intents'], + ], + [ + 'key' => 'eps', + 'name' => 'EPS', + 'currencies' => ['EUR'], + 'capabilities' => ['payment_intents'], + ], + [ + 'key' => 'fpx', + 'name' => 'FPX', + 'currencies' => ['MYR'], + 'capabilities' => ['payment_intents'], + ], + [ + 'key' => 'giropay', + 'name' => 'Giropay', + 'currencies' => ['EUR'], + 'capabilities' => ['payment_intents'], + ], + [ + 'key' => 'ideal', + 'name' => 'iDEAL', + 'currencies' => ['EUR'], + 'capabilities' => ['payment_intents'], + ], + [ + 'key' => 'p24', + 'name' => 'P24', + 'currencies' => ['EUR', 'PLN'], + 'capabilities' => ['payment_intents'], + ], + [ + 'key' => 'sofort', + 'name' => 'Sofort', + 'currencies' => ['EUR'], + 'capabilities' => ['payment_intents'], + 'async' => true, + ], + [ + 'key' => 'twint', + 'name' => 'TWINT', + 'currencies' => ['CHF'], + 'capabilities' => ['payment_intents'], + ], + // Buy now, pay later + [ + 'key' => 'affirm', + 'name' => 'Affirm', + 'currencies' => ['USD'], + 'capabilities' => ['payment_intents'], + ], + [ + 'key' => 'afterpay_clearpay', + 'name' => 'Afterpay and Clearpay', + 'currencies' => ['AUD', 'CAD', 'NZD', 'GBP', 'USD'], + 'capabilities' => ['payment_intents'], + ], + [ + 'key' => 'klarna', + 'name' => 'Klarna', + 'currencies' => ['AUD', 'CAD', 'CHF', 'CZK', 'DKK', 'EUR', 'GBP', 'NOK', 'NZD', 'PLN', 'SEK', 'USD'], + 'capabilities' => ['payment_intents'], + ], + // Real-time payments + [ + 'key' => 'paynow', + 'name' => 'PayNow', + 'currencies' => ['SGD'], + 'capabilities' => ['payment_intents'], + ], + [ + 'key' => 'promptpay', + 'name' => 'PromptPay', + 'currencies' => ['THB'], + 'capabilities' => ['payment_intents'], + ], + // Vouchers + [ + 'key' => 'boleto', + 'name' => 'Boleto', + 'currencies' => ['BRL'], + 'capabilities' => ['payment_intents', 'setup_future_usage', 'setup_intents', 'subscriptions'], + 'async' => true, + ], + [ + 'key' => 'konbini', + 'name' => 'Konbini', + 'currencies' => ['JPY'], + 'capabilities' => ['payment_intents'], + 'async' => true, + ], + [ + 'key' => 'oxxo', + 'name' => 'OXXO', + 'currencies' => ['MXN'], + 'capabilities' => ['payment_intents'], + 'async' => true, + ], + // Wallets + [ + 'key' => 'alipay', + 'name' => 'Alipay', + 'currencies' => ['AUD', 'CAD', 'CNY', 'EUR', 'GBP', 'HKD', 'JPY', 'MYR', 'NZD', 'SGD', 'USD'], + 'capabilities' => ['payment_intents'], + ], + [ + 'key' => 'cashapp', + 'name' => 'Cash App Pay', + 'currencies' => ['USD'], + 'capabilities' => ['payment_intents', 'setup_future_usage', 'setup_intents', 'subscriptions'], + ], + [ + 'key' => 'grabpay', + 'name' => 'GrabPay', + 'currencies' => ['MYR', 'SGD'], + 'capabilities' => ['payment_intents'], + ], + [ + 'key' => 'mobilepay', + 'name' => 'MobilePay', + 'currencies' => ['DKK', 'EUR', 'NOK', 'SEK'], + 'capabilities' => ['payment_intents'], + ], + [ + 'key' => 'wechat_pay', + 'name' => 'WeChat Pay', + 'currencies' => ['CNY', 'AUD', 'CAD', 'EUR', 'GBP', 'HKD', 'JPY', 'SGD', 'USD', 'DKK', 'NOK', 'SEK', 'CHF'], + 'capabilities' => ['payment_intents'], + ], ]); diff --git a/app/data/taxes/vat_countries.php b/app/data/taxes/vat_countries.php index fa6312e..687112a 100644 --- a/app/data/taxes/vat_countries.php +++ b/app/data/taxes/vat_countries.php @@ -1,41 +1,220 @@ array( 'name' => __('Austria', 'memberpress'), 'rate' => 20, 'reduced_rate' => 10, 'fmt' => '(AT)?U[0-9]{8}' ), - 'BE' => array( 'name' => __('Belgium', 'memberpress'), 'rate' => 21, 'reduced_rate' => 6, 'fmt' => '(BE)?0[0-9]{9}' ), - 'BG' => array( 'name' => __('Bulgaria', 'memberpress'), 'rate' => 20, 'fmt' => '(BG)?[0-9]{9,10}' ), - 'CY' => array( 'name' => __('Cyprus', 'memberpress'), 'rate' => 19, 'reduced_rate' => 5, 'fmt' => '(CY)?[0-9]{8}[a-zA-Z]{1}' ), - 'CZ' => array( 'name' => __('Czech Republic', 'memberpress'), 'rate' => 21, 'reduced_rate' => 10, 'fmt' => '(CZ)?[0-9]{8,10}' ), - 'DE' => array( 'name' => __('Germany', 'memberpress'), 'rate' => 19, 'reduced_rate' => 7, 'fmt' => '(DE)?[0-9]{9}' ), - 'DK' => array( 'name' => __('Denmark', 'memberpress'), 'rate' => 25, 'reduced_rate' => 0, 'fmt' => '(DK)?[0-9]{8}' ), - 'EE' => array( 'name' => __('Estonia', 'memberpress'), 'rate' => 22, 'reduced_rate' => 9, 'fmt' => '(EE)?[0-9]{9}' ), - 'GR' => array( 'name' => __('Greece', 'memberpress'), 'rate' => 24, 'reduced_rate' => 6, 'fmt' => '(EL|GR)?[0-9]{9}' ), - 'ES' => array( 'name' => __('Spain', 'memberpress'), 'rate' => 21, 'reduced_rate' => 4, 'fmt' => '(ES)?[0-9A-Z][0-9]{7}[0-9A-Z]' ), - 'FI' => array( 'name' => __('Finland', 'memberpress'), 'rate' => 24, 'reduced_rate' => 10, 'fmt' => '(FI)?[0-9]{8}' ), - 'FR' => array( 'name' => __('France', 'memberpress'), 'rate' => 20, 'reduced_rate' => 5.5, 'fmt' => '(FR)?[0-9A-Z]{2}[0-9]{9}' ), - 'HR' => array( 'name' => __('Croatia', 'memberpress'), 'rate' => 25, 'reduced_rate' => 5, 'fmt' => '(HR)?[0-9]{11}' ), - 'GB' => array( 'name' => __('United Kingdom', 'memberpress'), 'rate' => 20, 'reduced_rate' => 0, 'fmt' => '(GB)?([0-9]{9}([0-9]{3})?|[A-Z]{2}[0-9]{3})' ), - 'HU' => array( 'name' => __('Hungary', 'memberpress'), 'rate' => 27, 'reduced_rate' => 5, 'fmt' => '(HU)?[0-9]{8}' ), - 'IE' => array( 'name' => __('Ireland', 'memberpress'), 'rate' => 23, 'reduced_rate' => 9, 'fmt' => '(IE)?[0-9][0-9|A-Z][0-9]{5}[0-9|A-Z]{1,2}' ), - 'IT' => array( 'name' => __('Italy', 'memberpress'), 'rate' => 22, 'reduced_rate' => 4, 'fmt' => '(IT)?[0-9]{11}' ), - 'LT' => array( 'name' => __('Lithuania', 'memberpress'), 'rate' => 21, 'reduced_rate' => 5, 'fmt' => '(LT)?([0-9]{9}|[0-9]{12})' ), - 'LU' => array( 'name' => __('Luxembourg', 'memberpress'), 'rate' => 17, 'reduced_rate' => 3, 'fmt' => '(LU)?[0-9]{8}' ), - 'LV' => array( 'name' => __('Latvia', 'memberpress'), 'rate' => 21, 'reduced_rate' => 12, 'fmt' => '(LV)?[0-9]{11}' ), - 'MT' => array( 'name' => __('Malta', 'memberpress'), 'rate' => 18, 'reduced_rate' => 5, 'fmt' => '(MT)?[0-9]{8}' ), - 'NL' => array( 'name' => __('Netherlands', 'memberpress'), 'rate' => 21, 'reduced_rate' => 9, 'fmt' => '(NL)?[0-9]{9}B[0-9]{2}' ), - 'PL' => array( 'name' => __('Poland', 'memberpress'), 'rate' => 23, 'reduced_rate' => 5, 'fmt' => '(PL)?[0-9]{10}' ), - 'PT' => array( 'name' => __('Portugal', 'memberpress'), 'rate' => 23, 'reduced_rate' => 6, 'fmt' => '(PT)?[0-9]{9}' ), - 'RO' => array( 'name' => __('Romania', 'memberpress'), 'rate' => 19, 'reduced_rate' => 5, 'fmt' => '(RO)?[0-9]{2,10}' ), - 'SE' => array( 'name' => __('Sweden', 'memberpress'), 'rate' => 25, 'reduced_rate' => 6, 'fmt' => '(SE)?[0-9]{12}' ), - 'SI' => array( 'name' => __('Slovenia', 'memberpress'), 'rate' => 22, 'reduced_rate' => 5, 'fmt' => '(SI)?[0-9]{8}' ), - 'SK' => array( 'name' => __('Slovakia', 'memberpress'), 'rate' => 20, 'fmt' => '(SK)?[0-9]{10}' ), - 'GF' => array( 'name' => __('French Guiana', 'memberpress'), 'rate' => 0, 'reduced_rate' => 0, 'fmt' => '[0-9A-Z]{2}[0-9]{9}' ), - 'MF' => array( 'name' => __('Saint Martin (French part)', 'memberpress'), 'rate' => 0, 'reduced_rate' => 0, 'fmt' => '?[0-9A-Z]{2}[0-9]{9}' ), - 'MQ' => array( 'name' => __('Martinique', 'memberpress'), 'rate' => 0, 'reduced_rate' => 0, 'fmt' => '[0-9A-Z]{2}[0-9]{9}' ), - 'RE' => array( 'name' => __('Reunion', 'memberpress'), 'rate' => 0, 'reduced_rate' => 0, 'fmt' => '[0-9A-Z]{2}[0-9]{9}' ), - 'YT' => array( 'name' => __('Mayotte', 'memberpress'), 'rate' => 0, 'reduced_rate' => 0, 'fmt' => '[0-9A-Z]{2}[0-9]{9}' ), - 'PM' => array( 'name' => __('Saint Pierre and Miquelon', 'memberpress'), 'rate' => 0, 'reduced_rate' => 0, 'fmt' => '[0-9A-Z]{2}[0-9]{9}' ), - 'GP' => array( 'name' => __('Guadeloupe', 'memberpress'), 'rate' => 0, 'reduced_rate' => 0, 'fmt' => '[0-9A-Z]{2}[0-9]{9}' ), - 'MC' => array( 'name' => __('Monaco', 'memberpress'), 'rate' => 20, 'reduced_rate' => 5.5, 'fmt' => '(FR)?[0-9A-Z]{2}[0-9]{9}' ), -) ); +return MeprHooks::apply_filters('mepr-vat-countries', [ + 'AT' => [ + 'name' => __('Austria', 'memberpress'), + 'rate' => 20, + 'reduced_rate' => 10, + 'fmt' => '(AT)?U[0-9]{8}', + ], + 'BE' => [ + 'name' => __('Belgium', 'memberpress'), + 'rate' => 21, + 'reduced_rate' => 6, + 'fmt' => '(BE)?0[0-9]{9}', + ], + 'BG' => [ + 'name' => __('Bulgaria', 'memberpress'), + 'rate' => 20, + 'fmt' => '(BG)?[0-9]{9,10}', + ], + 'CY' => [ + 'name' => __('Cyprus', 'memberpress'), + 'rate' => 19, + 'reduced_rate' => 5, + 'fmt' => '(CY)?[0-9]{8}[a-zA-Z]{1}', + ], + 'CZ' => [ + 'name' => __('Czech Republic', 'memberpress'), + 'rate' => 21, + 'reduced_rate' => 10, + 'fmt' => '(CZ)?[0-9]{8,10}', + ], + 'DE' => [ + 'name' => __('Germany', 'memberpress'), + 'rate' => 19, + 'reduced_rate' => 7, + 'fmt' => '(DE)?[0-9]{9}', + ], + 'DK' => [ + 'name' => __('Denmark', 'memberpress'), + 'rate' => 25, + 'reduced_rate' => 0, + 'fmt' => '(DK)?[0-9]{8}', + ], + 'EE' => [ + 'name' => __('Estonia', 'memberpress'), + 'rate' => 22, + 'reduced_rate' => 9, + 'fmt' => '(EE)?[0-9]{9}', + ], + 'GR' => [ + 'name' => __('Greece', 'memberpress'), + 'rate' => 24, + 'reduced_rate' => 6, + 'fmt' => '(EL|GR)?[0-9]{9}', + ], + 'ES' => [ + 'name' => __('Spain', 'memberpress'), + 'rate' => 21, + 'reduced_rate' => 4, + 'fmt' => '(ES)?[0-9A-Z][0-9]{7}[0-9A-Z]', + ], + 'FI' => [ + 'name' => __('Finland', 'memberpress'), + 'rate' => 25.5, + 'reduced_rate' => 10, + 'fmt' => '(FI)?[0-9]{8}', + ], + 'FR' => [ + 'name' => __('France', 'memberpress'), + 'rate' => 20, + 'reduced_rate' => 5.5, + 'fmt' => '(FR)?[0-9A-Z]{2}[0-9]{9}', + ], + 'HR' => [ + 'name' => __('Croatia', 'memberpress'), + 'rate' => 25, + 'reduced_rate' => 5, + 'fmt' => '(HR)?[0-9]{11}', + ], + 'GB' => [ + 'name' => __('United Kingdom', 'memberpress'), + 'rate' => 20, + 'reduced_rate' => 0, + 'fmt' => '(GB)?([0-9]{9}([0-9]{3})?|[A-Z]{2}[0-9]{3})', + ], + 'HU' => [ + 'name' => __('Hungary', 'memberpress'), + 'rate' => 27, + 'reduced_rate' => 5, + 'fmt' => '(HU)?[0-9]{8}', + ], + 'IE' => [ + 'name' => __('Ireland', 'memberpress'), + 'rate' => 23, + 'reduced_rate' => 9, + 'fmt' => '(IE)?[0-9][0-9|A-Z][0-9]{5}[0-9|A-Z]{1,2}', + ], + 'IT' => [ + 'name' => __('Italy', 'memberpress'), + 'rate' => 22, + 'reduced_rate' => 4, + 'fmt' => '(IT)?[0-9]{11}', + ], + 'LT' => [ + 'name' => __('Lithuania', 'memberpress'), + 'rate' => 21, + 'reduced_rate' => 5, + 'fmt' => '(LT)?([0-9]{9}|[0-9]{12})', + ], + 'LU' => [ + 'name' => __('Luxembourg', 'memberpress'), + 'rate' => 17, + 'reduced_rate' => 3, + 'fmt' => '(LU)?[0-9]{8}', + ], + 'LV' => [ + 'name' => __('Latvia', 'memberpress'), + 'rate' => 21, + 'reduced_rate' => 12, + 'fmt' => '(LV)?[0-9]{11}', + ], + 'MT' => [ + 'name' => __('Malta', 'memberpress'), + 'rate' => 18, + 'reduced_rate' => 5, + 'fmt' => '(MT)?[0-9]{8}', + ], + 'NL' => [ + 'name' => __('Netherlands', 'memberpress'), + 'rate' => 21, + 'reduced_rate' => 9, + 'fmt' => '(NL)?[0-9]{9}B[0-9]{2}', + ], + 'PL' => [ + 'name' => __('Poland', 'memberpress'), + 'rate' => 23, + 'reduced_rate' => 5, + 'fmt' => '(PL)?[0-9]{10}', + ], + 'PT' => [ + 'name' => __('Portugal', 'memberpress'), + 'rate' => 23, + 'reduced_rate' => 6, + 'fmt' => '(PT)?[0-9]{9}', + ], + 'RO' => [ + 'name' => __('Romania', 'memberpress'), + 'rate' => 19, + 'reduced_rate' => 5, + 'fmt' => '(RO)?[0-9]{2,10}', + ], + 'SE' => [ + 'name' => __('Sweden', 'memberpress'), + 'rate' => 25, + 'reduced_rate' => 6, + 'fmt' => '(SE)?[0-9]{12}', + ], + 'SI' => [ + 'name' => __('Slovenia', 'memberpress'), + 'rate' => 22, + 'reduced_rate' => 5, + 'fmt' => '(SI)?[0-9]{8}', + ], + 'SK' => [ + 'name' => __('Slovakia', 'memberpress'), + 'rate' => 20, + 'fmt' => '(SK)?[0-9]{10}', + ], + 'GF' => [ + 'name' => __('French Guiana', 'memberpress'), + 'rate' => 0, + 'reduced_rate' => 0, + 'fmt' => '[0-9A-Z]{2}[0-9]{9}', + ], + 'MF' => [ + 'name' => __('Saint Martin (French part)', 'memberpress'), + 'rate' => 0, + 'reduced_rate' => 0, + 'fmt' => '?[0-9A-Z]{2}[0-9]{9}', + ], + 'MQ' => [ + 'name' => __('Martinique', 'memberpress'), + 'rate' => 0, + 'reduced_rate' => 0, + 'fmt' => '[0-9A-Z]{2}[0-9]{9}', + ], + 'RE' => [ + 'name' => __('Reunion', 'memberpress'), + 'rate' => 0, + 'reduced_rate' => 0, + 'fmt' => '[0-9A-Z]{2}[0-9]{9}', + ], + 'YT' => [ + 'name' => __('Mayotte', 'memberpress'), + 'rate' => 0, + 'reduced_rate' => 0, + 'fmt' => '[0-9A-Z]{2}[0-9]{9}', + ], + 'PM' => [ + 'name' => __('Saint Pierre and Miquelon', 'memberpress'), + 'rate' => 0, + 'reduced_rate' => 0, + 'fmt' => '[0-9A-Z]{2}[0-9]{9}', + ], + 'GP' => [ + 'name' => __('Guadeloupe', 'memberpress'), + 'rate' => 0, + 'reduced_rate' => 0, + 'fmt' => '[0-9A-Z]{2}[0-9]{9}', + ], + 'MC' => [ + 'name' => __('Monaco', 'memberpress'), + 'rate' => 20, + 'reduced_rate' => 5.5, + 'fmt' => '(FR)?[0-9A-Z]{2}[0-9]{9}', + ], +]); diff --git a/app/emails/MeprAdminCancelledSubEmail.php b/app/emails/MeprAdminCancelledSubEmail.php index 2d73b2a..b4166d6 100644 --- a/app/emails/MeprAdminCancelledSubEmail.php +++ b/app/emails/MeprAdminCancelledSubEmail.php @@ -1,22 +1,28 @@ to = $mepr_options->admin_email_addresses; +if (!defined('ABSPATH')) { + die('You are not allowed to call this page directly.'); +} - $this->title = __('Cancelled Subscription Notice','memberpress'); - $this->description = __('This email is sent to you when a subscription is cancelled.', 'memberpress'); - $this->ui_order = 3; +class MeprAdminCancelledSubEmail extends MeprBaseOptionsAdminEmail +{ + /** + * Set the default enabled, title, subject & body + */ + public function set_defaults($args = []) + { + $mepr_options = MeprOptions::fetch(); + $this->to = $mepr_options->admin_email_addresses; - $enabled = $use_template = $this->show_form = true; - $subject = __('** Subscription {$subscr_num} Was Cancelled', 'memberpress'); - $body = $this->body_partial(); + $this->title = __('Cancelled Subscription Notice', 'memberpress'); + $this->description = __('This email is sent to you when a subscription is cancelled.', 'memberpress'); + $this->ui_order = 3; - $this->defaults = compact( 'enabled', 'subject', 'body', 'use_template' ); - $this->variables = MeprSubscriptionsHelper::get_email_vars(); - } -} + $enabled = $use_template = $this->show_form = true; + $subject = __('** Subscription {$subscr_num} Was Cancelled', 'memberpress'); + $body = $this->body_partial(); + $this->defaults = compact('enabled', 'subject', 'body', 'use_template'); + $this->variables = MeprSubscriptionsHelper::get_email_vars(); + } +} diff --git a/app/emails/MeprAdminCcExpiresReminderEmail.php b/app/emails/MeprAdminCcExpiresReminderEmail.php index 00eb5b7..dc7306e 100644 --- a/app/emails/MeprAdminCcExpiresReminderEmail.php +++ b/app/emails/MeprAdminCcExpiresReminderEmail.php @@ -1,36 +1,44 @@ to = $mepr_options->admin_email_addresses; +if (!defined('ABSPATH')) { + die('You are not allowed to call this page directly.'); +} - $this->title = __('Credit Card Expires Reminder Email to Admin','memberpress'); - $this->description = __('This email is sent to the admin when triggered for a user.', 'memberpress'); - $this->ui_order = 1; +class MeprAdminCcExpiresReminderEmail extends MeprBaseReminderEmail +{ + /** + * Set the default enabled, title, subject & body + */ + public function set_defaults($args = []) + { + $mepr_options = MeprOptions::fetch(); + $this->to = $mepr_options->admin_email_addresses; - $enabled = $use_template = $this->show_form = true; - $subject = sprintf( __('** %1$s Reminder Sent to %2$s', 'memberpress'), '{$reminder_name}', '{$username}' ); - $body = $this->body_partial(); + $this->title = __('Credit Card Expires Reminder Email to Admin', 'memberpress'); + $this->description = __('This email is sent to the admin when triggered for a user.', 'memberpress'); + $this->ui_order = 1; - $this->defaults = compact( 'enabled', 'subject', 'body', 'use_template' ); - $this->variables = array_unique( - array_merge( MeprRemindersHelper::get_email_vars(), - MeprSubscriptionsHelper::get_email_vars(), - MeprTransactionsHelper::get_email_vars() ) - ); + $enabled = $use_template = $this->show_form = true; + $subject = sprintf(__('** %1$s Reminder Sent to %2$s', 'memberpress'), '{$reminder_name}', '{$username}'); + $body = $this->body_partial(); - $this->test_vars = array( - 'reminder_id' => 28, - 'reminder_trigger_length' => 2, - 'reminder_trigger_interval' => 'days', - 'reminder_trigger_timing' => 'before', - 'reminder_trigger_event' => 'cc-expires', - 'reminder_name' => __('Credit Card Expiring', 'memberpress'), - 'reminder_description' => __('Credit Card Expiring in 2 Days', 'memberpress'), - ); - } -} + $this->defaults = compact('enabled', 'subject', 'body', 'use_template'); + $this->variables = array_unique( + array_merge( + MeprRemindersHelper::get_email_vars(), + MeprSubscriptionsHelper::get_email_vars(), + MeprTransactionsHelper::get_email_vars() + ) + ); + $this->test_vars = [ + 'reminder_id' => 28, + 'reminder_trigger_length' => 2, + 'reminder_trigger_interval' => 'days', + 'reminder_trigger_timing' => 'before', + 'reminder_trigger_event' => 'cc-expires', + 'reminder_name' => __('Credit Card Expiring', 'memberpress'), + 'reminder_description' => __('Credit Card Expiring in 2 Days', 'memberpress'), + ]; + } +} diff --git a/app/emails/MeprAdminCcExpiringEmail.php b/app/emails/MeprAdminCcExpiringEmail.php index 25294b2..23f2a96 100644 --- a/app/emails/MeprAdminCcExpiringEmail.php +++ b/app/emails/MeprAdminCcExpiringEmail.php @@ -1,22 +1,28 @@ to = $mepr_options->admin_email_addresses; +if (!defined('ABSPATH')) { + die('You are not allowed to call this page directly.'); +} - $this->title = __('Credit Card Expiring Notice','memberpress'); - $this->description = __('This email is sent to you when a member\'s credit card is expiring', 'memberpress'); - $this->ui_order = 10; +class MeprAdminCcExpiringEmail extends MeprBaseOptionsAdminEmail +{ + /** + * Set the default enabled, title, subject & body + */ + public function set_defaults($args = []) + { + $mepr_options = MeprOptions::fetch(); + $this->to = $mepr_options->admin_email_addresses; - $enabled = $use_template = $this->show_form = true; - $subject = __('** Credit Card Expiring For {$subscr_num}', 'memberpress'); - $body = $this->body_partial(); + $this->title = __('Credit Card Expiring Notice', 'memberpress'); + $this->description = __('This email is sent to you when a member\'s credit card is expiring', 'memberpress'); + $this->ui_order = 10; - $this->defaults = compact( 'enabled', 'subject', 'body', 'use_template' ); - $this->variables = MeprSubscriptionsHelper::get_email_vars(); - } -} + $enabled = $use_template = $this->show_form = true; + $subject = __('** Credit Card Expiring For {$subscr_num}', 'memberpress'); + $body = $this->body_partial(); + $this->defaults = compact('enabled', 'subject', 'body', 'use_template'); + $this->variables = MeprSubscriptionsHelper::get_email_vars(); + } +} diff --git a/app/emails/MeprAdminDowngradedSubEmail.php b/app/emails/MeprAdminDowngradedSubEmail.php index f8b0791..46ebeb9 100644 --- a/app/emails/MeprAdminDowngradedSubEmail.php +++ b/app/emails/MeprAdminDowngradedSubEmail.php @@ -1,22 +1,28 @@ to = $mepr_options->admin_email_addresses; +if (!defined('ABSPATH')) { + die('You are not allowed to call this page directly.'); +} - $this->title = __('Downgraded Subscription Notice','memberpress'); - $this->description = __('This email is sent to you when a subscription is downgraded.', 'memberpress'); - $this->ui_order = 5; +class MeprAdminDowngradedSubEmail extends MeprBaseOptionsAdminEmail +{ + /** + * Set the default enabled, title, subject & body + */ + public function set_defaults($args = []) + { + $mepr_options = MeprOptions::fetch(); + $this->to = $mepr_options->admin_email_addresses; - $enabled = $use_template = $this->show_form = true; - $subject = __('** Subscription {$subscr_num} Downgraded', 'memberpress'); - $body = $this->body_partial(); + $this->title = __('Downgraded Subscription Notice', 'memberpress'); + $this->description = __('This email is sent to you when a subscription is downgraded.', 'memberpress'); + $this->ui_order = 5; - $this->defaults = compact( 'enabled', 'subject', 'body', 'use_template' ); - $this->variables = MeprSubscriptionsHelper::get_email_vars(); - } -} + $enabled = $use_template = $this->show_form = true; + $subject = __('** Subscription {$subscr_num} Downgraded', 'memberpress'); + $body = $this->body_partial(); + $this->defaults = compact('enabled', 'subject', 'body', 'use_template'); + $this->variables = MeprSubscriptionsHelper::get_email_vars(); + } +} diff --git a/app/emails/MeprAdminFailedTxnEmail.php b/app/emails/MeprAdminFailedTxnEmail.php index 7d4a53d..7df927b 100644 --- a/app/emails/MeprAdminFailedTxnEmail.php +++ b/app/emails/MeprAdminFailedTxnEmail.php @@ -1,22 +1,28 @@ to = $mepr_options->admin_email_addresses; +if (!defined('ABSPATH')) { + die('You are not allowed to call this page directly.'); +} - $this->title = __('Failed Transaction Notice','memberpress'); - $this->description = __('This email is sent to you when a transaction fails.', 'memberpress'); - $this->ui_order = 9; +class MeprAdminFailedTxnEmail extends MeprBaseOptionsAdminEmail +{ + /** + * Set the default enabled, title, subject & body + */ + public function set_defaults($args = []) + { + $mepr_options = MeprOptions::fetch(); + $this->to = $mepr_options->admin_email_addresses; - $enabled = $use_template = $this->show_form = true; - $subject = __('** Transaction Failed', 'memberpress'); - $body = $this->body_partial(); + $this->title = __('Failed Transaction Notice', 'memberpress'); + $this->description = __('This email is sent to you when a transaction fails.', 'memberpress'); + $this->ui_order = 9; - $this->defaults = compact( 'enabled', 'subject', 'body', 'use_template' ); - $this->variables = MeprTransactionsHelper::get_email_vars(); - } -} + $enabled = $use_template = $this->show_form = true; + $subject = __('** Transaction Failed', 'memberpress'); + $body = $this->body_partial(); + $this->defaults = compact('enabled', 'subject', 'body', 'use_template'); + $this->variables = MeprTransactionsHelper::get_email_vars(); + } +} diff --git a/app/emails/MeprAdminMemberSignupReminderEmail.php b/app/emails/MeprAdminMemberSignupReminderEmail.php index dfd9a8a..61be36c 100644 --- a/app/emails/MeprAdminMemberSignupReminderEmail.php +++ b/app/emails/MeprAdminMemberSignupReminderEmail.php @@ -1,37 +1,43 @@ to = $mepr_options->admin_email_addresses; +if (!defined('ABSPATH')) { + die('You are not allowed to call this page directly.'); +} - $this->title = __('Member Signup Reminder Email to Admin','memberpress'); - $this->description = __('This email is sent to the admin when triggered for a user.', 'memberpress'); - $this->ui_order = 1; +class MeprAdminMemberSignupReminderEmail extends MeprBaseReminderEmail +{ + /** + * Set the default enabled, title, subject & body + */ + public function set_defaults($args = []) + { + $mepr_options = MeprOptions::fetch(); + $this->to = $mepr_options->admin_email_addresses; - $enabled = $use_template = $this->show_form = true; - $subject = sprintf( __('** %1$s Reminder Sent to %2$s', 'memberpress'), '{$reminder_name}', '{$username}' ); - $body = $this->body_partial(); + $this->title = __('Member Signup Reminder Email to Admin', 'memberpress'); + $this->description = __('This email is sent to the admin when triggered for a user.', 'memberpress'); + $this->ui_order = 1; - $this->defaults = compact( 'enabled', 'subject', 'body', 'use_template' ); - $this->variables = array_unique( - array_merge( - MeprRemindersHelper::get_email_vars(), - MeprTransactionsHelper::get_email_vars() - ) - ); + $enabled = $use_template = $this->show_form = true; + $subject = sprintf(__('** %1$s Reminder Sent to %2$s', 'memberpress'), '{$reminder_name}', '{$username}'); + $body = $this->body_partial(); - $this->test_vars = array( - 'reminder_id' => 28, - 'reminder_trigger_length' => 2, - 'reminder_trigger_interval' => 'days', - 'reminder_trigger_timing' => 'after', - 'reminder_trigger_event' => 'member-signup', - 'reminder_name' => __('Member Signed Up', 'memberpress'), - 'reminder_description' => __('Member Signed Up 2 days ago', 'memberpress'), - ); - } -} + $this->defaults = compact('enabled', 'subject', 'body', 'use_template'); + $this->variables = array_unique( + array_merge( + MeprRemindersHelper::get_email_vars(), + MeprTransactionsHelper::get_email_vars() + ) + ); + $this->test_vars = [ + 'reminder_id' => 28, + 'reminder_trigger_length' => 2, + 'reminder_trigger_interval' => 'days', + 'reminder_trigger_timing' => 'after', + 'reminder_trigger_event' => 'member-signup', + 'reminder_name' => __('Member Signed Up', 'memberpress'), + 'reminder_description' => __('Member Signed Up 2 days ago', 'memberpress'), + ]; + } +} diff --git a/app/emails/MeprAdminNewOneOffEmail.php b/app/emails/MeprAdminNewOneOffEmail.php index 000f9be..56ae884 100644 --- a/app/emails/MeprAdminNewOneOffEmail.php +++ b/app/emails/MeprAdminNewOneOffEmail.php @@ -1,22 +1,28 @@ to = $mepr_options->admin_email_addresses; +if (!defined('ABSPATH')) { + die('You are not allowed to call this page directly.'); +} - $this->title = __('New One-Time Subscription Notice','memberpress'); - $this->description = __('This email is sent to you when a new non-recurring subscription is created.', 'memberpress'); - $this->ui_order = 1; +class MeprAdminNewOneOffEmail extends MeprBaseOptionsAdminEmail +{ + /** + * Set the default enabled, title, subject & body + */ + public function set_defaults($args = []) + { + $mepr_options = MeprOptions::fetch(); + $this->to = $mepr_options->admin_email_addresses; - $enabled = $use_template = $this->show_form = true; - $subject = __('** New One-Time Subscription: {$trans_num}', 'memberpress'); - $body = $this->body_partial(); + $this->title = __('New One-Time Subscription Notice', 'memberpress'); + $this->description = __('This email is sent to you when a new non-recurring subscription is created.', 'memberpress'); + $this->ui_order = 1; - $this->defaults = compact( 'enabled', 'subject', 'body', 'use_template' ); - $this->variables = MeprTransactionsHelper::get_email_vars(); - } -} + $enabled = $use_template = $this->show_form = true; + $subject = __('** New One-Time Subscription: {$trans_num}', 'memberpress'); + $body = $this->body_partial(); + $this->defaults = compact('enabled', 'subject', 'body', 'use_template'); + $this->variables = MeprTransactionsHelper::get_email_vars(); + } +} diff --git a/app/emails/MeprAdminNewSubEmail.php b/app/emails/MeprAdminNewSubEmail.php index 5073278..a6d786e 100644 --- a/app/emails/MeprAdminNewSubEmail.php +++ b/app/emails/MeprAdminNewSubEmail.php @@ -1,22 +1,28 @@ to = $mepr_options->admin_email_addresses; +if (!defined('ABSPATH')) { + die('You are not allowed to call this page directly.'); +} - $this->title = __('New Recurring Subscription Notice','memberpress'); - $this->description = __('This email is sent to you when a subscription is created.', 'memberpress'); - $this->ui_order = 1; +class MeprAdminNewSubEmail extends MeprBaseOptionsAdminEmail +{ + /** + * Set the default enabled, title, subject & body + */ + public function set_defaults($args = []) + { + $mepr_options = MeprOptions::fetch(); + $this->to = $mepr_options->admin_email_addresses; - $enabled = $use_template = $this->show_form = true; - $subject = __('** New Recurring Subscription: {$subscr_num}', 'memberpress'); - $body = $this->body_partial(); + $this->title = __('New Recurring Subscription Notice', 'memberpress'); + $this->description = __('This email is sent to you when a subscription is created.', 'memberpress'); + $this->ui_order = 1; - $this->defaults = compact( 'enabled', 'subject', 'body', 'use_template' ); - $this->variables = MeprSubscriptionsHelper::get_email_vars(); - } -} + $enabled = $use_template = $this->show_form = true; + $subject = __('** New Recurring Subscription: {$subscr_num}', 'memberpress'); + $body = $this->body_partial(); + $this->defaults = compact('enabled', 'subject', 'body', 'use_template'); + $this->variables = MeprSubscriptionsHelper::get_email_vars(); + } +} diff --git a/app/emails/MeprAdminReceiptEmail.php b/app/emails/MeprAdminReceiptEmail.php index 825bf05..80938c8 100644 --- a/app/emails/MeprAdminReceiptEmail.php +++ b/app/emails/MeprAdminReceiptEmail.php @@ -1,22 +1,28 @@ to = $mepr_options->admin_email_addresses; +if (!defined('ABSPATH')) { + die('You are not allowed to call this page directly.'); +} - $this->title = __('Payment Receipt Notice','memberpress'); - $this->description = __('This email is sent to you when a payment comes through on your membership site', 'memberpress'); - $this->ui_order = 2; +class MeprAdminReceiptEmail extends MeprBaseOptionsAdminEmail +{ + /** + * Set the default enabled, title, subject & body + */ + public function set_defaults($args = []) + { + $mepr_options = MeprOptions::fetch(); + $this->to = $mepr_options->admin_email_addresses; - $enabled = $use_template = $this->show_form = true; - $subject = __('** Payment of {$payment_amount} from {$user_full_name}', 'memberpress'); - $body = $this->body_partial(); + $this->title = __('Payment Receipt Notice', 'memberpress'); + $this->description = __('This email is sent to you when a payment comes through on your membership site', 'memberpress'); + $this->ui_order = 2; - $this->defaults = compact( 'enabled', 'subject', 'body', 'use_template' ); - $this->variables = MeprTransactionsHelper::get_email_vars(); - } -} + $enabled = $use_template = $this->show_form = true; + $subject = __('** Payment of {$payment_amount} from {$user_full_name}', 'memberpress'); + $body = $this->body_partial(); + $this->defaults = compact('enabled', 'subject', 'body', 'use_template'); + $this->variables = MeprTransactionsHelper::get_email_vars(); + } +} diff --git a/app/emails/MeprAdminRefundedTxnEmail.php b/app/emails/MeprAdminRefundedTxnEmail.php index 19f0d92..7037154 100644 --- a/app/emails/MeprAdminRefundedTxnEmail.php +++ b/app/emails/MeprAdminRefundedTxnEmail.php @@ -1,22 +1,28 @@ to = $mepr_options->admin_email_addresses; +if (!defined('ABSPATH')) { + die('You are not allowed to call this page directly.'); +} - $this->title = __('Refunded Transaction Notice','memberpress'); - $this->description = __('This email is sent to you when a transaction is refunded.', 'memberpress'); - $this->ui_order = 8; +class MeprAdminRefundedTxnEmail extends MeprBaseOptionsAdminEmail +{ + /** + * Set the default enabled, title, subject & body + */ + public function set_defaults($args = []) + { + $mepr_options = MeprOptions::fetch(); + $this->to = $mepr_options->admin_email_addresses; - $enabled = $use_template = $this->show_form = true; - $subject = __('** Transaction {$trans_num} Was Refunded', 'memberpress'); - $body = $this->body_partial(); + $this->title = __('Refunded Transaction Notice', 'memberpress'); + $this->description = __('This email is sent to you when a transaction is refunded.', 'memberpress'); + $this->ui_order = 8; - $this->defaults = compact( 'enabled', 'subject', 'body', 'use_template' ); - $this->variables = MeprTransactionsHelper::get_email_vars(); - } -} + $enabled = $use_template = $this->show_form = true; + $subject = __('** Transaction {$trans_num} Was Refunded', 'memberpress'); + $body = $this->body_partial(); + $this->defaults = compact('enabled', 'subject', 'body', 'use_template'); + $this->variables = MeprTransactionsHelper::get_email_vars(); + } +} diff --git a/app/emails/MeprAdminResumedSubEmail.php b/app/emails/MeprAdminResumedSubEmail.php index fcbe8cb..96c2488 100644 --- a/app/emails/MeprAdminResumedSubEmail.php +++ b/app/emails/MeprAdminResumedSubEmail.php @@ -1,22 +1,28 @@ to = $mepr_options->admin_email_addresses; +if (!defined('ABSPATH')) { + die('You are not allowed to call this page directly.'); +} - $this->title = __('Resumed Subscription Notice','memberpress'); - $this->description = __('This email is sent to you when a subscription is resumed.', 'memberpress'); - $this->ui_order = 7; +class MeprAdminResumedSubEmail extends MeprBaseOptionsAdminEmail +{ + /** + * Set the default enabled, title, subject & body + */ + public function set_defaults($args = []) + { + $mepr_options = MeprOptions::fetch(); + $this->to = $mepr_options->admin_email_addresses; - $enabled = $use_template = $this->show_form = true; - $subject = __('** Subscription {$subscr_num} Resumed', 'memberpress'); - $body = $this->body_partial(); + $this->title = __('Resumed Subscription Notice', 'memberpress'); + $this->description = __('This email is sent to you when a subscription is resumed.', 'memberpress'); + $this->ui_order = 7; - $this->defaults = compact( 'enabled', 'subject', 'body', 'use_template' ); - $this->variables = MeprSubscriptionsHelper::get_email_vars(); - } -} + $enabled = $use_template = $this->show_form = true; + $subject = __('** Subscription {$subscr_num} Resumed', 'memberpress'); + $body = $this->body_partial(); + $this->defaults = compact('enabled', 'subject', 'body', 'use_template'); + $this->variables = MeprSubscriptionsHelper::get_email_vars(); + } +} diff --git a/app/emails/MeprAdminSignupAbandonedReminderEmail.php b/app/emails/MeprAdminSignupAbandonedReminderEmail.php index a5cd8b5..c10ac0d 100644 --- a/app/emails/MeprAdminSignupAbandonedReminderEmail.php +++ b/app/emails/MeprAdminSignupAbandonedReminderEmail.php @@ -1,37 +1,43 @@ to = $mepr_options->admin_email_addresses; +if (!defined('ABSPATH')) { + die('You are not allowed to call this page directly.'); +} - $this->title = __('Signup Abandoned Reminder Email to Admin','memberpress'); - $this->description = __('This email is sent to the admin when triggered for a user.', 'memberpress'); - $this->ui_order = 1; +class MeprAdminSignupAbandonedReminderEmail extends MeprBaseReminderEmail +{ + /** + * Set the default enabled, title, subject & body + */ + public function set_defaults($args = []) + { + $mepr_options = MeprOptions::fetch(); + $this->to = $mepr_options->admin_email_addresses; - $enabled = $use_template = $this->show_form = true; - $subject = sprintf( __('** %1$s Reminder Sent to %2$s', 'memberpress'), '{$reminder_name}', '{$username}' ); - $body = $this->body_partial(); + $this->title = __('Signup Abandoned Reminder Email to Admin', 'memberpress'); + $this->description = __('This email is sent to the admin when triggered for a user.', 'memberpress'); + $this->ui_order = 1; - $this->defaults = compact( 'enabled', 'subject', 'body', 'use_template' ); - $this->variables = array_unique( - array_merge( - MeprRemindersHelper::get_email_vars(), - MeprTransactionsHelper::get_email_vars() - ) - ); + $enabled = $use_template = $this->show_form = true; + $subject = sprintf(__('** %1$s Reminder Sent to %2$s', 'memberpress'), '{$reminder_name}', '{$username}'); + $body = $this->body_partial(); - $this->test_vars = array( - 'reminder_id' => 28, - 'reminder_trigger_length' => 2, - 'reminder_trigger_interval' => 'days', - 'reminder_trigger_timing' => 'after', - 'reminder_trigger_event' => 'signup-abandoned', - 'reminder_name' => __('Sign Up Abandoned', 'memberpress'), - 'reminder_description' => __('Sign Up Abandoned 2 days ago', 'memberpress'), - ); - } -} + $this->defaults = compact('enabled', 'subject', 'body', 'use_template'); + $this->variables = array_unique( + array_merge( + MeprRemindersHelper::get_email_vars(), + MeprTransactionsHelper::get_email_vars() + ) + ); + $this->test_vars = [ + 'reminder_id' => 28, + 'reminder_trigger_length' => 2, + 'reminder_trigger_interval' => 'days', + 'reminder_trigger_timing' => 'after', + 'reminder_trigger_event' => 'signup-abandoned', + 'reminder_name' => __('Sign Up Abandoned', 'memberpress'), + 'reminder_description' => __('Sign Up Abandoned 2 days ago', 'memberpress'), + ]; + } +} diff --git a/app/emails/MeprAdminSignupEmail.php b/app/emails/MeprAdminSignupEmail.php index 3b59486..f339c08 100644 --- a/app/emails/MeprAdminSignupEmail.php +++ b/app/emails/MeprAdminSignupEmail.php @@ -1,22 +1,28 @@ to = $mepr_options->admin_email_addresses; +if (!defined('ABSPATH')) { + die('You are not allowed to call this page directly.'); +} - $this->title = __('New Signup Notice','memberpress'); - $this->description = __('This email is sent to you when a user registers for your membership site and their first transaction completes.', 'memberpress'); - $this->ui_order = 0; +class MeprAdminSignupEmail extends MeprBaseOptionsAdminEmail +{ + /** + * Set the default enabled, title, subject & body + */ + public function set_defaults($args = []) + { + $mepr_options = MeprOptions::fetch(); + $this->to = $mepr_options->admin_email_addresses; - $enabled = $use_template = $this->show_form = true; - $subject = __('** New Signup: {$username}', 'memberpress'); - $body = $this->body_partial(); + $this->title = __('New Signup Notice', 'memberpress'); + $this->description = __('This email is sent to you when a user registers for your membership site and their first transaction completes.', 'memberpress'); + $this->ui_order = 0; - $this->defaults = compact( 'enabled', 'subject', 'body', 'use_template' ); - $this->variables = MeprTransactionsHelper::get_email_vars(); - } -} + $enabled = $use_template = $this->show_form = true; + $subject = __('** New Signup: {$username}', 'memberpress'); + $body = $this->body_partial(); + $this->defaults = compact('enabled', 'subject', 'body', 'use_template'); + $this->variables = MeprTransactionsHelper::get_email_vars(); + } +} diff --git a/app/emails/MeprAdminSubExpiresReminderEmail.php b/app/emails/MeprAdminSubExpiresReminderEmail.php index d97e7b4..e239c59 100644 --- a/app/emails/MeprAdminSubExpiresReminderEmail.php +++ b/app/emails/MeprAdminSubExpiresReminderEmail.php @@ -1,36 +1,44 @@ to = $mepr_options->admin_email_addresses; +if (!defined('ABSPATH')) { + die('You are not allowed to call this page directly.'); +} - $this->title = __('Subscription Expires Reminder Email to Admin','memberpress'); - $this->description = __('This email is sent to the admin when triggered for a user.', 'memberpress'); - $this->ui_order = 1; +class MeprAdminSubExpiresReminderEmail extends MeprBaseReminderEmail +{ + /** + * Set the default enabled, title, subject & body + */ + public function set_defaults($args = []) + { + $mepr_options = MeprOptions::fetch(); + $this->to = $mepr_options->admin_email_addresses; - $enabled = $use_template = $this->show_form = true; - $subject = sprintf( __('** %1$s Reminder Sent to %2$s', 'memberpress'), '{$reminder_name}', '{$username}' ); - $body = $this->body_partial(); + $this->title = __('Subscription Expires Reminder Email to Admin', 'memberpress'); + $this->description = __('This email is sent to the admin when triggered for a user.', 'memberpress'); + $this->ui_order = 1; - $this->defaults = compact( 'enabled', 'subject', 'body', 'use_template' ); - $this->variables = array_unique( - array_merge( MeprRemindersHelper::get_email_vars(), - MeprSubscriptionsHelper::get_email_vars(), - MeprTransactionsHelper::get_email_vars() ) - ); + $enabled = $use_template = $this->show_form = true; + $subject = sprintf(__('** %1$s Reminder Sent to %2$s', 'memberpress'), '{$reminder_name}', '{$username}'); + $body = $this->body_partial(); - $this->test_vars = array( - 'reminder_id' => 28, - 'reminder_trigger_length' => 2, - 'reminder_trigger_interval' => 'days', - 'reminder_trigger_timing' => 'before', - 'reminder_trigger_event' => 'sub-expires', - 'reminder_name' => __('Subscription Expiring', 'memberpress'), - 'reminder_description' => __('Subscription Expiring in 2 days', 'memberpress') - ); - } -} + $this->defaults = compact('enabled', 'subject', 'body', 'use_template'); + $this->variables = array_unique( + array_merge( + MeprRemindersHelper::get_email_vars(), + MeprSubscriptionsHelper::get_email_vars(), + MeprTransactionsHelper::get_email_vars() + ) + ); + $this->test_vars = [ + 'reminder_id' => 28, + 'reminder_trigger_length' => 2, + 'reminder_trigger_interval' => 'days', + 'reminder_trigger_timing' => 'before', + 'reminder_trigger_event' => 'sub-expires', + 'reminder_name' => __('Subscription Expiring', 'memberpress'), + 'reminder_description' => __('Subscription Expiring in 2 days', 'memberpress'), + ]; + } +} diff --git a/app/emails/MeprAdminSubRenewsReminderEmail.php b/app/emails/MeprAdminSubRenewsReminderEmail.php index 7d80801..2f08772 100644 --- a/app/emails/MeprAdminSubRenewsReminderEmail.php +++ b/app/emails/MeprAdminSubRenewsReminderEmail.php @@ -1,36 +1,44 @@ to = $mepr_options->admin_email_addresses; +if (!defined('ABSPATH')) { + die('You are not allowed to call this page directly.'); +} - $this->title = __('Subscription Renews Reminder Email to Admin','memberpress'); - $this->description = __('This email is sent to the admin when triggered for a user.', 'memberpress'); - $this->ui_order = 3; +class MeprAdminSubRenewsReminderEmail extends MeprBaseReminderEmail +{ + /** + * Set the default enabled, title, subject & body + */ + public function set_defaults($args = []) + { + $mepr_options = MeprOptions::fetch(); + $this->to = $mepr_options->admin_email_addresses; - $enabled = $use_template = $this->show_form = true; - $subject = sprintf( __('** %1$s Reminder Sent to %2$s', 'memberpress'), '{$reminder_name}', '{$username}' ); - $body = $this->body_partial(); + $this->title = __('Subscription Renews Reminder Email to Admin', 'memberpress'); + $this->description = __('This email is sent to the admin when triggered for a user.', 'memberpress'); + $this->ui_order = 3; - $this->defaults = compact( 'enabled', 'subject', 'body', 'use_template' ); - $this->variables = array_unique( - array_merge( MeprRemindersHelper::get_email_vars(), - MeprSubscriptionsHelper::get_email_vars(), - MeprTransactionsHelper::get_email_vars() ) - ); + $enabled = $use_template = $this->show_form = true; + $subject = sprintf(__('** %1$s Reminder Sent to %2$s', 'memberpress'), '{$reminder_name}', '{$username}'); + $body = $this->body_partial(); - $this->test_vars = array( - 'reminder_id' => 28, - 'reminder_trigger_length' => 2, - 'reminder_trigger_interval' => 'days', - 'reminder_trigger_timing' => 'before', - 'reminder_trigger_event' => 'sub-renews', - 'reminder_name' => __('Subscription Renewing', 'memberpress'), - 'reminder_description' => __('Subscription Renewing in 2 Days', 'memberpress'), - ); - } -} + $this->defaults = compact('enabled', 'subject', 'body', 'use_template'); + $this->variables = array_unique( + array_merge( + MeprRemindersHelper::get_email_vars(), + MeprSubscriptionsHelper::get_email_vars(), + MeprTransactionsHelper::get_email_vars() + ) + ); + $this->test_vars = [ + 'reminder_id' => 28, + 'reminder_trigger_length' => 2, + 'reminder_trigger_interval' => 'days', + 'reminder_trigger_timing' => 'before', + 'reminder_trigger_event' => 'sub-renews', + 'reminder_name' => __('Subscription Renewing', 'memberpress'), + 'reminder_description' => __('Subscription Renewing in 2 Days', 'memberpress'), + ]; + } +} diff --git a/app/emails/MeprAdminSubTrialEndsReminderEmail.php b/app/emails/MeprAdminSubTrialEndsReminderEmail.php index fd81aab..58bc9e7 100644 --- a/app/emails/MeprAdminSubTrialEndsReminderEmail.php +++ b/app/emails/MeprAdminSubTrialEndsReminderEmail.php @@ -1,36 +1,44 @@ to = $mepr_options->admin_email_addresses; +if (!defined('ABSPATH')) { + die('You are not allowed to call this page directly.'); +} - $this->title = __('Subscription Trial Ending Reminder Email to Admin','memberpress'); - $this->description = __('This email is sent to the admin when triggered for a user.', 'memberpress'); - $this->ui_order = 3; +class MeprAdminSubTrialEndsReminderEmail extends MeprBaseReminderEmail +{ + /** + * Set the default enabled, title, subject & body + */ + public function set_defaults($args = []) + { + $mepr_options = MeprOptions::fetch(); + $this->to = $mepr_options->admin_email_addresses; - $enabled = $use_template = $this->show_form = true; - $subject = __("A Member's Subscription Trial Period is Ending Soon", 'memberpress'); - $body = $this->body_partial(); + $this->title = __('Subscription Trial Ending Reminder Email to Admin', 'memberpress'); + $this->description = __('This email is sent to the admin when triggered for a user.', 'memberpress'); + $this->ui_order = 3; - $this->defaults = compact( 'enabled', 'subject', 'body', 'use_template' ); - $this->variables = array_unique( - array_merge( MeprRemindersHelper::get_email_vars(), - MeprSubscriptionsHelper::get_email_vars(), - MeprTransactionsHelper::get_email_vars() ) - ); + $enabled = $use_template = $this->show_form = true; + $subject = __("A Member's Subscription Trial Period is Ending Soon", 'memberpress'); + $body = $this->body_partial(); - $this->test_vars = array( - 'reminder_id' => 28, - 'reminder_trigger_length' => 2, - 'reminder_trigger_interval' => 'days', - 'reminder_trigger_timing' => 'before', - 'reminder_trigger_event' => 'sub-trial-ends', - 'reminder_name' => __('Subscription Trial Ending', 'memberpress'), - 'reminder_description' => __('Subscription Trial Ending in 2 Days', 'memberpress'), - ); - } -} + $this->defaults = compact('enabled', 'subject', 'body', 'use_template'); + $this->variables = array_unique( + array_merge( + MeprRemindersHelper::get_email_vars(), + MeprSubscriptionsHelper::get_email_vars(), + MeprTransactionsHelper::get_email_vars() + ) + ); + $this->test_vars = [ + 'reminder_id' => 28, + 'reminder_trigger_length' => 2, + 'reminder_trigger_interval' => 'days', + 'reminder_trigger_timing' => 'before', + 'reminder_trigger_event' => 'sub-trial-ends', + 'reminder_name' => __('Subscription Trial Ending', 'memberpress'), + 'reminder_description' => __('Subscription Trial Ending in 2 Days', 'memberpress'), + ]; + } +} diff --git a/app/emails/MeprAdminSuspendedSubEmail.php b/app/emails/MeprAdminSuspendedSubEmail.php index e952fcc..2503e1c 100644 --- a/app/emails/MeprAdminSuspendedSubEmail.php +++ b/app/emails/MeprAdminSuspendedSubEmail.php @@ -1,22 +1,28 @@ to = $mepr_options->admin_email_addresses; +if (!defined('ABSPATH')) { + die('You are not allowed to call this page directly.'); +} - $this->title = __('Paused Subscription Notice','memberpress'); - $this->description = __('This email is sent to you when a subscription is paused.', 'memberpress'); - $this->ui_order = 6; +class MeprAdminSuspendedSubEmail extends MeprBaseOptionsAdminEmail +{ + /** + * Set the default enabled, title, subject & body + */ + public function set_defaults($args = []) + { + $mepr_options = MeprOptions::fetch(); + $this->to = $mepr_options->admin_email_addresses; - $enabled = $use_template = $this->show_form = true; - $subject = __('** Subscription {$subscr_num} Paused', 'memberpress'); - $body = $this->body_partial(); + $this->title = __('Paused Subscription Notice', 'memberpress'); + $this->description = __('This email is sent to you when a subscription is paused.', 'memberpress'); + $this->ui_order = 6; - $this->defaults = compact( 'enabled', 'subject', 'body', 'use_template' ); - $this->variables = MeprSubscriptionsHelper::get_email_vars(); - } -} + $enabled = $use_template = $this->show_form = true; + $subject = __('** Subscription {$subscr_num} Paused', 'memberpress'); + $body = $this->body_partial(); + $this->defaults = compact('enabled', 'subject', 'body', 'use_template'); + $this->variables = MeprSubscriptionsHelper::get_email_vars(); + } +} diff --git a/app/emails/MeprAdminUpgradedSubEmail.php b/app/emails/MeprAdminUpgradedSubEmail.php index 767fb87..5ef2776 100644 --- a/app/emails/MeprAdminUpgradedSubEmail.php +++ b/app/emails/MeprAdminUpgradedSubEmail.php @@ -1,22 +1,28 @@ to = $mepr_options->admin_email_addresses; +if (!defined('ABSPATH')) { + die('You are not allowed to call this page directly.'); +} - $this->title = __('Upgraded Subscription Notice','memberpress'); - $this->description = __('This email is sent to you when a subscription is upgraded.', 'memberpress'); - $this->ui_order = 4; +class MeprAdminUpgradedSubEmail extends MeprBaseOptionsAdminEmail +{ + /** + * Set the default enabled, title, subject & body + */ + public function set_defaults($args = []) + { + $mepr_options = MeprOptions::fetch(); + $this->to = $mepr_options->admin_email_addresses; - $enabled = $use_template = $this->show_form = true; - $subject = __('** Subscription {$subscr_num} Upgraded', 'memberpress'); - $body = $this->body_partial(); + $this->title = __('Upgraded Subscription Notice', 'memberpress'); + $this->description = __('This email is sent to you when a subscription is upgraded.', 'memberpress'); + $this->ui_order = 4; - $this->defaults = compact( 'enabled', 'subject', 'body', 'use_template' ); - $this->variables = MeprSubscriptionsHelper::get_email_vars(); - } -} + $enabled = $use_template = $this->show_form = true; + $subject = __('** Subscription {$subscr_num} Upgraded', 'memberpress'); + $body = $this->body_partial(); + $this->defaults = compact('enabled', 'subject', 'body', 'use_template'); + $this->variables = MeprSubscriptionsHelper::get_email_vars(); + } +} diff --git a/app/emails/MeprUserCancelledSubEmail.php b/app/emails/MeprUserCancelledSubEmail.php index a32ec03..f98ea1c 100644 --- a/app/emails/MeprUserCancelledSubEmail.php +++ b/app/emails/MeprUserCancelledSubEmail.php @@ -1,19 +1,25 @@ title = __('Cancelled Subscription Notice','memberpress'); - $this->description = __('This email is sent to the user when a subscription is cancelled.', 'memberpress'); - $this->ui_order = 2; +if (!defined('ABSPATH')) { + die('You are not allowed to call this page directly.'); +} - $enabled = $use_template = $this->show_form = true; - $subject = __('** Your Automatic Payments Have Been Stopped', 'memberpress'); - $body = $this->body_partial(); +class MeprUserCancelledSubEmail extends MeprBaseOptionsUserEmail +{ + /** + * Set the default enabled, title, subject & body + */ + public function set_defaults($args = []) + { + $this->title = __('Cancelled Subscription Notice', 'memberpress'); + $this->description = __('This email is sent to the user when a subscription is cancelled.', 'memberpress'); + $this->ui_order = 2; - $this->defaults = compact( 'enabled', 'subject', 'body', 'use_template' ); - $this->variables = MeprSubscriptionsHelper::get_email_vars(); - } -} + $enabled = $use_template = $this->show_form = true; + $subject = __('** Your Automatic Payments Have Been Stopped', 'memberpress'); + $body = $this->body_partial(); + $this->defaults = compact('enabled', 'subject', 'body', 'use_template'); + $this->variables = MeprSubscriptionsHelper::get_email_vars(); + } +} diff --git a/app/emails/MeprUserCcExpiresReminderEmail.php b/app/emails/MeprUserCcExpiresReminderEmail.php index 5feb1fe..27e771e 100644 --- a/app/emails/MeprUserCcExpiresReminderEmail.php +++ b/app/emails/MeprUserCcExpiresReminderEmail.php @@ -1,33 +1,41 @@ title = __('Credit Card Expires Reminder Email to User','memberpress'); - $this->description = __('This email is sent to the user when triggered.', 'memberpress'); - $this->ui_order = 0; +if (!defined('ABSPATH')) { + die('You are not allowed to call this page directly.'); +} - $enabled = $use_template = $this->show_form = true; - $subject = sprintf( __('** Your %1$s', 'memberpress'), '{$reminder_description}' ); - $body = $this->body_partial(); +class MeprUserCcExpiresReminderEmail extends MeprBaseReminderEmail +{ + /** + * Set the default enabled, title, subject & body + */ + public function set_defaults($args = []) + { + $this->title = __('Credit Card Expires Reminder Email to User', 'memberpress'); + $this->description = __('This email is sent to the user when triggered.', 'memberpress'); + $this->ui_order = 0; - $this->defaults = compact( 'enabled', 'subject', 'body', 'use_template' ); - $this->variables = array_unique( - array_merge( MeprRemindersHelper::get_email_vars(), - MeprSubscriptionsHelper::get_email_vars(), - MeprTransactionsHelper::get_email_vars() ) - ); + $enabled = $use_template = $this->show_form = true; + $subject = sprintf(__('** Your %1$s', 'memberpress'), '{$reminder_description}'); + $body = $this->body_partial(); - $this->test_vars = array( - 'reminder_id' => 28, - 'reminder_trigger_length' => 2, - 'reminder_trigger_interval' => 'days', - 'reminder_trigger_timing' => 'before', - 'reminder_trigger_event' => 'cc-expires', - 'reminder_name' => __('Credit Card Expiring', 'memberpress'), - 'reminder_description' => __('Credit Card Expiring in 2 Days', 'memberpress'), - ); - } -} + $this->defaults = compact('enabled', 'subject', 'body', 'use_template'); + $this->variables = array_unique( + array_merge( + MeprRemindersHelper::get_email_vars(), + MeprSubscriptionsHelper::get_email_vars(), + MeprTransactionsHelper::get_email_vars() + ) + ); + $this->test_vars = [ + 'reminder_id' => 28, + 'reminder_trigger_length' => 2, + 'reminder_trigger_interval' => 'days', + 'reminder_trigger_timing' => 'before', + 'reminder_trigger_event' => 'cc-expires', + 'reminder_name' => __('Credit Card Expiring', 'memberpress'), + 'reminder_description' => __('Credit Card Expiring in 2 Days', 'memberpress'), + ]; + } +} diff --git a/app/emails/MeprUserCcExpiringEmail.php b/app/emails/MeprUserCcExpiringEmail.php index 24e5171..83ff7b8 100644 --- a/app/emails/MeprUserCcExpiringEmail.php +++ b/app/emails/MeprUserCcExpiringEmail.php @@ -1,19 +1,25 @@ title = __('Credit Card Expiring Notice','memberpress'); - $this->description = __('This email is sent to a member when their credit card is expiring', 'memberpress'); - $this->ui_order = 9; +if (!defined('ABSPATH')) { + die('You are not allowed to call this page directly.'); +} - $enabled = $use_template = $this->show_form = true; - $subject = __('** Your Credit Card is Expiring', 'memberpress'); - $body = $this->body_partial(); +class MeprUserCcExpiringEmail extends MeprBaseOptionsUserEmail +{ + /** + * Set the default enabled, title, subject & body + */ + public function set_defaults($args = []) + { + $this->title = __('Credit Card Expiring Notice', 'memberpress'); + $this->description = __('This email is sent to a member when their credit card is expiring', 'memberpress'); + $this->ui_order = 9; - $this->defaults = compact( 'enabled', 'subject', 'body', 'use_template' ); - $this->variables = MeprSubscriptionsHelper::get_email_vars(); - } -} + $enabled = $use_template = $this->show_form = true; + $subject = __('** Your Credit Card is Expiring', 'memberpress'); + $body = $this->body_partial(); + $this->defaults = compact('enabled', 'subject', 'body', 'use_template'); + $this->variables = MeprSubscriptionsHelper::get_email_vars(); + } +} diff --git a/app/emails/MeprUserDowngradedSubEmail.php b/app/emails/MeprUserDowngradedSubEmail.php index 8fc5299..a112ad7 100644 --- a/app/emails/MeprUserDowngradedSubEmail.php +++ b/app/emails/MeprUserDowngradedSubEmail.php @@ -1,19 +1,25 @@ title = __('Downgraded Subscription Notice','memberpress'); - $this->description = __('This email is sent to the user when they downgrade a subscription.', 'memberpress'); - $this->ui_order = 4; +if (!defined('ABSPATH')) { + die('You are not allowed to call this page directly.'); +} - $enabled = $use_template = $this->show_form = true; - $subject = __('** You\'ve downgraded your subscription', 'memberpress'); - $body = $this->body_partial(); +class MeprUserDowngradedSubEmail extends MeprBaseOptionsUserEmail +{ + /** + * Set the default enabled, title, subject & body + */ + public function set_defaults($args = []) + { + $this->title = __('Downgraded Subscription Notice', 'memberpress'); + $this->description = __('This email is sent to the user when they downgrade a subscription.', 'memberpress'); + $this->ui_order = 4; - $this->defaults = compact( 'enabled', 'subject', 'body', 'use_template' ); - $this->variables = MeprSubscriptionsHelper::get_email_vars(); - } -} + $enabled = $use_template = $this->show_form = true; + $subject = __('** You\'ve downgraded your subscription', 'memberpress'); + $body = $this->body_partial(); + $this->defaults = compact('enabled', 'subject', 'body', 'use_template'); + $this->variables = MeprSubscriptionsHelper::get_email_vars(); + } +} diff --git a/app/emails/MeprUserFailedTxnEmail.php b/app/emails/MeprUserFailedTxnEmail.php index 138e8a5..bb0278c 100644 --- a/app/emails/MeprUserFailedTxnEmail.php +++ b/app/emails/MeprUserFailedTxnEmail.php @@ -1,19 +1,25 @@ title = __('Failed Transaction Notice','memberpress'); - $this->description = __('This email is sent to the user when a transaction of theirs fails.', 'memberpress'); - $this->ui_order = 8; +if (!defined('ABSPATH')) { + die('You are not allowed to call this page directly.'); +} - $enabled = $use_template = $this->show_form = true; - $subject = __('** Your Transaction Failed', 'memberpress'); - $body = $this->body_partial(); +class MeprUserFailedTxnEmail extends MeprBaseOptionsUserEmail +{ + /** + * Set the default enabled, title, subject & body + */ + public function set_defaults($args = []) + { + $this->title = __('Failed Transaction Notice', 'memberpress'); + $this->description = __('This email is sent to the user when a transaction of theirs fails.', 'memberpress'); + $this->ui_order = 8; - $this->defaults = compact( 'enabled', 'subject', 'body', 'use_template' ); - $this->variables = MeprTransactionsHelper::get_email_vars(); - } -} + $enabled = $use_template = $this->show_form = true; + $subject = __('** Your Transaction Failed', 'memberpress'); + $body = $this->body_partial(); + $this->defaults = compact('enabled', 'subject', 'body', 'use_template'); + $this->variables = MeprTransactionsHelper::get_email_vars(); + } +} diff --git a/app/emails/MeprUserMemberSignupReminderEmail.php b/app/emails/MeprUserMemberSignupReminderEmail.php index 49f2d69..946058b 100644 --- a/app/emails/MeprUserMemberSignupReminderEmail.php +++ b/app/emails/MeprUserMemberSignupReminderEmail.php @@ -1,32 +1,40 @@ title = __('Member Signup Reminder Email to User','memberpress'); - $this->description = __('This email is sent to the user when triggered.', 'memberpress'); - $this->ui_order = 0; +if (!defined('ABSPATH')) { + die('You are not allowed to call this page directly.'); +} - $enabled = $use_template = $this->show_form = true; - $subject = __('** Thanks for signing up', 'memberpress'); - $body = $this->body_partial(); +class MeprUserMemberSignupReminderEmail extends MeprBaseReminderEmail +{ + /** + * Set the default enabled, title, subject & body + */ + public function set_defaults($args = []) + { + $this->title = __('Member Signup Reminder Email to User', 'memberpress'); + $this->description = __('This email is sent to the user when triggered.', 'memberpress'); + $this->ui_order = 0; - $this->defaults = compact( 'enabled', 'subject', 'body', 'use_template' ); - $this->variables = array_unique( - array_merge( MeprRemindersHelper::get_email_vars(), - MeprUsersHelper::get_email_vars() ) - ); + $enabled = $use_template = $this->show_form = true; + $subject = __('** Thanks for signing up', 'memberpress'); + $body = $this->body_partial(); - $this->test_vars = array( - 'reminder_id' => 28, - 'reminder_trigger_length' => 2, - 'reminder_trigger_interval' => 'days', - 'reminder_trigger_timing' => 'after', - 'reminder_trigger_event' => 'member-signup', - 'reminder_name' => __('Member Signed Up', 'memberpress'), - 'reminder_description' => __('Member Signed Up 2 days ago', 'memberpress') - ); - } -} + $this->defaults = compact('enabled', 'subject', 'body', 'use_template'); + $this->variables = array_unique( + array_merge( + MeprRemindersHelper::get_email_vars(), + MeprUsersHelper::get_email_vars() + ) + ); + $this->test_vars = [ + 'reminder_id' => 28, + 'reminder_trigger_length' => 2, + 'reminder_trigger_interval' => 'days', + 'reminder_trigger_timing' => 'after', + 'reminder_trigger_event' => 'member-signup', + 'reminder_name' => __('Member Signed Up', 'memberpress'), + 'reminder_description' => __('Member Signed Up 2 days ago', 'memberpress'), + ]; + } +} diff --git a/app/emails/MeprUserProductWelcomeEmail.php b/app/emails/MeprUserProductWelcomeEmail.php index e68da54..5505ec6 100644 --- a/app/emails/MeprUserProductWelcomeEmail.php +++ b/app/emails/MeprUserProductWelcomeEmail.php @@ -1,23 +1,29 @@ title = __('Membership-Specific Welcome Email to User','memberpress'); - $this->description = __('This email is sent when this membership is purchased.', 'memberpress'); - $this->ui_order = 1; + $this->title = __('Membership-Specific Welcome Email to User', 'memberpress'); + $this->description = __('This email is sent when this membership is purchased.', 'memberpress'); + $this->ui_order = 1; - $enabled = false; - $use_template = $this->show_form = true; - $subject = __('** Thanks for Purchasing {$product_name}', 'memberpress'); - $body = $this->body_partial(); + $enabled = false; + $use_template = $this->show_form = true; + $subject = __('** Thanks for Purchasing {$product_name}', 'memberpress'); + $body = $this->body_partial(); - $this->defaults = compact( 'enabled', 'subject', 'body', 'use_template' ); - $this->variables = MeprTransactionsHelper::get_email_vars(); - } + $this->defaults = compact('enabled', 'subject', 'body', 'use_template'); + $this->variables = MeprTransactionsHelper::get_email_vars(); + } } - diff --git a/app/emails/MeprUserReceiptEmail.php b/app/emails/MeprUserReceiptEmail.php index d8fef89..9eedcf4 100644 --- a/app/emails/MeprUserReceiptEmail.php +++ b/app/emails/MeprUserReceiptEmail.php @@ -1,19 +1,25 @@ title = __('Payment Receipt Notice','memberpress'); - $this->description = __('This email is sent to a user when payment completes for one of your memberships in her behalf.', 'memberpress'); - $this->ui_order = 1; +if (!defined('ABSPATH')) { + die('You are not allowed to call this page directly.'); +} - $enabled = $use_template = $this->show_form = true; - $subject = __('** Payment Receipt', 'memberpress'); - $body = $this->body_partial(); +class MeprUserReceiptEmail extends MeprBaseOptionsUserEmail +{ + /** + * Set the default enabled, title, subject & body + */ + public function set_defaults($args = []) + { + $this->title = __('Payment Receipt Notice', 'memberpress'); + $this->description = __('This email is sent to a user when payment completes for one of your memberships in her behalf.', 'memberpress'); + $this->ui_order = 1; - $this->defaults = compact( 'enabled', 'subject', 'body', 'use_template' ); - $this->variables = MeprTransactionsHelper::get_email_vars(); - } -} + $enabled = $use_template = $this->show_form = true; + $subject = __('** Payment Receipt', 'memberpress'); + $body = $this->body_partial(); + $this->defaults = compact('enabled', 'subject', 'body', 'use_template'); + $this->variables = MeprTransactionsHelper::get_email_vars(); + } +} diff --git a/app/emails/MeprUserRefundedTxnEmail.php b/app/emails/MeprUserRefundedTxnEmail.php index a1db268..15d5ee7 100644 --- a/app/emails/MeprUserRefundedTxnEmail.php +++ b/app/emails/MeprUserRefundedTxnEmail.php @@ -1,19 +1,25 @@ title = __('Refunded Transaction Notice','memberpress'); - $this->description = __('This email is sent to the user when a transaction is refunded.', 'memberpress'); - $this->ui_order = 7; +if (!defined('ABSPATH')) { + die('You are not allowed to call this page directly.'); +} - $enabled = $use_template = $this->show_form = true; - $subject = __('** Your Transaction Was Refunded', 'memberpress'); - $body = $this->body_partial(); +class MeprUserRefundedTxnEmail extends MeprBaseOptionsUserEmail +{ + /** + * Set the default enabled, title, subject & body + */ + public function set_defaults($args = []) + { + $this->title = __('Refunded Transaction Notice', 'memberpress'); + $this->description = __('This email is sent to the user when a transaction is refunded.', 'memberpress'); + $this->ui_order = 7; - $this->defaults = compact( 'enabled', 'subject', 'body', 'use_template' ); - $this->variables = MeprTransactionsHelper::get_email_vars(); - } -} + $enabled = $use_template = $this->show_form = true; + $subject = __('** Your Transaction Was Refunded', 'memberpress'); + $body = $this->body_partial(); + $this->defaults = compact('enabled', 'subject', 'body', 'use_template'); + $this->variables = MeprTransactionsHelper::get_email_vars(); + } +} diff --git a/app/emails/MeprUserResumedSubEmail.php b/app/emails/MeprUserResumedSubEmail.php index 7a1cfd7..6007a96 100644 --- a/app/emails/MeprUserResumedSubEmail.php +++ b/app/emails/MeprUserResumedSubEmail.php @@ -1,19 +1,25 @@ title = __('Resumed Subscription Notice','memberpress'); - $this->description = __('This email is sent to the user when they resume a subscription.', 'memberpress'); - $this->ui_order = 6; +if (!defined('ABSPATH')) { + die('You are not allowed to call this page directly.'); +} - $enabled = $use_template = $this->show_form = true; - $subject = __('** Your subscription has resumed', 'memberpress'); - $body = $this->body_partial(); +class MeprUserResumedSubEmail extends MeprBaseOptionsUserEmail +{ + /** + * Set the default enabled, title, subject & body + */ + public function set_defaults($args = []) + { + $this->title = __('Resumed Subscription Notice', 'memberpress'); + $this->description = __('This email is sent to the user when they resume a subscription.', 'memberpress'); + $this->ui_order = 6; - $this->defaults = compact( 'enabled', 'subject', 'body', 'use_template' ); - $this->variables = MeprSubscriptionsHelper::get_email_vars(); - } -} + $enabled = $use_template = $this->show_form = true; + $subject = __('** Your subscription has resumed', 'memberpress'); + $body = $this->body_partial(); + $this->defaults = compact('enabled', 'subject', 'body', 'use_template'); + $this->variables = MeprSubscriptionsHelper::get_email_vars(); + } +} diff --git a/app/emails/MeprUserSignupAbandonedReminderEmail.php b/app/emails/MeprUserSignupAbandonedReminderEmail.php index 5b83c38..7f2d3de 100644 --- a/app/emails/MeprUserSignupAbandonedReminderEmail.php +++ b/app/emails/MeprUserSignupAbandonedReminderEmail.php @@ -1,34 +1,40 @@ title = __('Signup Abandoned Reminder Email to User','memberpress'); - $this->description = __('This email is sent to the user when triggered.', 'memberpress'); - $this->ui_order = 0; +if (!defined('ABSPATH')) { + die('You are not allowed to call this page directly.'); +} - $enabled = $use_template = $this->show_form = true; - $subject = __('** Please complete your signup', 'memberpress'); - $body = $this->body_partial(); +class MeprUserSignupAbandonedReminderEmail extends MeprBaseReminderEmail +{ + /** + * Set the default enabled, title, subject & body + */ + public function set_defaults($args = []) + { + $this->title = __('Signup Abandoned Reminder Email to User', 'memberpress'); + $this->description = __('This email is sent to the user when triggered.', 'memberpress'); + $this->ui_order = 0; - $this->defaults = compact( 'enabled', 'subject', 'body', 'use_template' ); - $this->variables = array_unique( - array_merge( - MeprRemindersHelper::get_email_vars(), - MeprTransactionsHelper::get_email_vars() - ) - ); + $enabled = $use_template = $this->show_form = true; + $subject = __('** Please complete your signup', 'memberpress'); + $body = $this->body_partial(); - $this->test_vars = array( - 'reminder_id' => 28, - 'reminder_trigger_length' => 2, - 'reminder_trigger_interval' => 'days', - 'reminder_trigger_timing' => 'after', - 'reminder_trigger_event' => 'signup-abandoned', - 'reminder_name' => __('Sign Up Abandoned', 'memberpress'), - 'reminder_description' => __('Sign Up Abandoned 2 days ago', 'memberpress'), - ); - } -} + $this->defaults = compact('enabled', 'subject', 'body', 'use_template'); + $this->variables = array_unique( + array_merge( + MeprRemindersHelper::get_email_vars(), + MeprTransactionsHelper::get_email_vars() + ) + ); + $this->test_vars = [ + 'reminder_id' => 28, + 'reminder_trigger_length' => 2, + 'reminder_trigger_interval' => 'days', + 'reminder_trigger_timing' => 'after', + 'reminder_trigger_event' => 'signup-abandoned', + 'reminder_name' => __('Sign Up Abandoned', 'memberpress'), + 'reminder_description' => __('Sign Up Abandoned 2 days ago', 'memberpress'), + ]; + } +} diff --git a/app/emails/MeprUserStripeInvoiceEmail.php b/app/emails/MeprUserStripeInvoiceEmail.php index b6afd48..c3b577f 100644 --- a/app/emails/MeprUserStripeInvoiceEmail.php +++ b/app/emails/MeprUserStripeInvoiceEmail.php @@ -1,19 +1,25 @@ title = __('Stripe Failed Payment Notice','memberpress'); - $this->description = __('This email is sent to the user when a Stripe subscription payment of theirs fails, with a link to pay the outstanding invoice.', 'memberpress'); - $this->ui_order = 10; +if (!defined('ABSPATH')) { + die('You are not allowed to call this page directly.'); +} - $enabled = $use_template = $this->show_form = true; - $subject = __('** Your Subscription Payment Failed', 'memberpress'); - $body = $this->body_partial(); +class MeprUserStripeInvoiceEmail extends MeprBaseOptionsUserEmail +{ + /** + * Set the default enabled, title, subject & body + */ + public function set_defaults($args = []) + { + $this->title = __('Stripe Failed Payment Notice', 'memberpress'); + $this->description = __('This email is sent to the user when a Stripe subscription payment of theirs fails, with a link to pay the outstanding invoice.', 'memberpress'); + $this->ui_order = 10; - $this->defaults = compact( 'enabled', 'subject', 'body', 'use_template' ); - $this->variables = array_merge(MeprSubscriptionsHelper::get_email_vars(), array('stripe_invoice_url')); - } -} + $enabled = $use_template = $this->show_form = true; + $subject = __('** Your Subscription Payment Failed', 'memberpress'); + $body = $this->body_partial(); + $this->defaults = compact('enabled', 'subject', 'body', 'use_template'); + $this->variables = array_merge(MeprSubscriptionsHelper::get_email_vars(), ['stripe_invoice_url']); + } +} diff --git a/app/emails/MeprUserSubExpiresReminderEmail.php b/app/emails/MeprUserSubExpiresReminderEmail.php index 5a0f9d2..b4fae7b 100644 --- a/app/emails/MeprUserSubExpiresReminderEmail.php +++ b/app/emails/MeprUserSubExpiresReminderEmail.php @@ -1,33 +1,41 @@ title = __('Subscription Expires Reminder Email to User','memberpress'); - $this->description = __('This email is sent to the user when triggered.', 'memberpress'); - $this->ui_order = 0; +if (!defined('ABSPATH')) { + die('You are not allowed to call this page directly.'); +} - $enabled = $use_template = $this->show_form = true; - $subject = sprintf( __('** Your %1$s', 'memberpress'), '{$reminder_description}' ); - $body = $this->body_partial(); +class MeprUserSubExpiresReminderEmail extends MeprBaseReminderEmail +{ + /** + * Set the default enabled, title, subject & body + */ + public function set_defaults($args = []) + { + $this->title = __('Subscription Expires Reminder Email to User', 'memberpress'); + $this->description = __('This email is sent to the user when triggered.', 'memberpress'); + $this->ui_order = 0; - $this->defaults = compact( 'enabled', 'subject', 'body', 'use_template' ); - $this->variables = array_unique( - array_merge( MeprRemindersHelper::get_email_vars(), - MeprSubscriptionsHelper::get_email_vars(), - MeprTransactionsHelper::get_email_vars() ) - ); + $enabled = $use_template = $this->show_form = true; + $subject = sprintf(__('** Your %1$s', 'memberpress'), '{$reminder_description}'); + $body = $this->body_partial(); - $this->test_vars = array( - 'reminder_id' => 28, - 'reminder_trigger_length' => 2, - 'reminder_trigger_interval' => 'days', - 'reminder_trigger_timing' => 'before', - 'reminder_trigger_event' => 'sub-expires', - 'reminder_name' => __('Subscription Expiring', 'memberpress'), - 'reminder_description' => __('Subscription Expiring in 2 days', 'memberpress') - ); - } -} + $this->defaults = compact('enabled', 'subject', 'body', 'use_template'); + $this->variables = array_unique( + array_merge( + MeprRemindersHelper::get_email_vars(), + MeprSubscriptionsHelper::get_email_vars(), + MeprTransactionsHelper::get_email_vars() + ) + ); + $this->test_vars = [ + 'reminder_id' => 28, + 'reminder_trigger_length' => 2, + 'reminder_trigger_interval' => 'days', + 'reminder_trigger_timing' => 'before', + 'reminder_trigger_event' => 'sub-expires', + 'reminder_name' => __('Subscription Expiring', 'memberpress'), + 'reminder_description' => __('Subscription Expiring in 2 days', 'memberpress'), + ]; + } +} diff --git a/app/emails/MeprUserSubRenewsReminderEmail.php b/app/emails/MeprUserSubRenewsReminderEmail.php index 98912bc..e41ab7a 100644 --- a/app/emails/MeprUserSubRenewsReminderEmail.php +++ b/app/emails/MeprUserSubRenewsReminderEmail.php @@ -1,33 +1,41 @@ title = __('Subscription Renews Reminder Email to User','memberpress'); - $this->description = __('This email is sent to the user when triggered.', 'memberpress'); - $this->ui_order = 0; +if (!defined('ABSPATH')) { + die('You are not allowed to call this page directly.'); +} - $enabled = $use_template = $this->show_form = true; - $subject = sprintf( __('** Your %1$s', 'memberpress'), '{$reminder_description}' ); - $body = $this->body_partial(); +class MeprUserSubRenewsReminderEmail extends MeprBaseReminderEmail +{ + /** + * Set the default enabled, title, subject & body + */ + public function set_defaults($args = []) + { + $this->title = __('Subscription Renews Reminder Email to User', 'memberpress'); + $this->description = __('This email is sent to the user when triggered.', 'memberpress'); + $this->ui_order = 0; - $this->defaults = compact( 'enabled', 'subject', 'body', 'use_template' ); - $this->variables = array_unique( - array_merge( MeprRemindersHelper::get_email_vars(), - MeprSubscriptionsHelper::get_email_vars(), - MeprTransactionsHelper::get_email_vars() ) - ); + $enabled = $use_template = $this->show_form = true; + $subject = sprintf(__('** Your %1$s', 'memberpress'), '{$reminder_description}'); + $body = $this->body_partial(); - $this->test_vars = array( - 'reminder_id' => 28, - 'reminder_trigger_length' => 2, - 'reminder_trigger_interval' => 'days', - 'reminder_trigger_timing' => 'before', - 'reminder_trigger_event' => 'sub-renews', - 'reminder_name' => __('Subscription Renewing', 'memberpress'), - 'reminder_description' => __('Subscription Renewing in 2 days', 'memberpress') - ); - } -} + $this->defaults = compact('enabled', 'subject', 'body', 'use_template'); + $this->variables = array_unique( + array_merge( + MeprRemindersHelper::get_email_vars(), + MeprSubscriptionsHelper::get_email_vars(), + MeprTransactionsHelper::get_email_vars() + ) + ); + $this->test_vars = [ + 'reminder_id' => 28, + 'reminder_trigger_length' => 2, + 'reminder_trigger_interval' => 'days', + 'reminder_trigger_timing' => 'before', + 'reminder_trigger_event' => 'sub-renews', + 'reminder_name' => __('Subscription Renewing', 'memberpress'), + 'reminder_description' => __('Subscription Renewing in 2 days', 'memberpress'), + ]; + } +} diff --git a/app/emails/MeprUserSubTrialEndsReminderEmail.php b/app/emails/MeprUserSubTrialEndsReminderEmail.php index bf12453..c53b44d 100644 --- a/app/emails/MeprUserSubTrialEndsReminderEmail.php +++ b/app/emails/MeprUserSubTrialEndsReminderEmail.php @@ -1,34 +1,40 @@ title = __('Subscription Trial Ending Reminder Email to User','memberpress'); - $this->description = __('This email is sent to the user when triggered.', 'memberpress'); - $this->ui_order = 0; +if (!defined('ABSPATH')) { + die('You are not allowed to call this page directly.'); +} - $enabled = $use_template = $this->show_form = true; - $subject = __('Your Subscription Trial Period is Ending Soon', 'memberpress'); - $body = $this->body_partial(); +class MeprUserSubTrialEndsReminderEmail extends MeprBaseReminderEmail +{ + /** + * Set the default enabled, title, subject & body + */ + public function set_defaults($args = []) + { + $this->title = __('Subscription Trial Ending Reminder Email to User', 'memberpress'); + $this->description = __('This email is sent to the user when triggered.', 'memberpress'); + $this->ui_order = 0; - $this->defaults = compact( 'enabled', 'subject', 'body', 'use_template' ); - $this->variables = array_unique( - array_merge( - MeprRemindersHelper::get_email_vars(), - MeprTransactionsHelper::get_email_vars() - ) - ); + $enabled = $use_template = $this->show_form = true; + $subject = __('Your Subscription Trial Period is Ending Soon', 'memberpress'); + $body = $this->body_partial(); - $this->test_vars = array( - 'reminder_id' => 28, - 'reminder_trigger_length' => 2, - 'reminder_trigger_interval' => 'days', - 'reminder_trigger_timing' => 'before', - 'reminder_trigger_event' => 'sub-trial-ends', - 'reminder_name' => __('Subscription Trial Ending', 'memberpress'), - 'reminder_description' => __('Subscription Trial Ending in 2 days', 'memberpress'), - ); - } -} + $this->defaults = compact('enabled', 'subject', 'body', 'use_template'); + $this->variables = array_unique( + array_merge( + MeprRemindersHelper::get_email_vars(), + MeprTransactionsHelper::get_email_vars() + ) + ); + $this->test_vars = [ + 'reminder_id' => 28, + 'reminder_trigger_length' => 2, + 'reminder_trigger_interval' => 'days', + 'reminder_trigger_timing' => 'before', + 'reminder_trigger_event' => 'sub-trial-ends', + 'reminder_name' => __('Subscription Trial Ending', 'memberpress'), + 'reminder_description' => __('Subscription Trial Ending in 2 days', 'memberpress'), + ]; + } +} diff --git a/app/emails/MeprUserSuspendedSubEmail.php b/app/emails/MeprUserSuspendedSubEmail.php index a36c6ed..f6bd5be 100644 --- a/app/emails/MeprUserSuspendedSubEmail.php +++ b/app/emails/MeprUserSuspendedSubEmail.php @@ -1,19 +1,25 @@ title = __('Paused Subscription Notice','memberpress'); - $this->description = __('This email is sent to the user when one of their subscriptions is paused.', 'memberpress'); - $this->ui_order = 5; +if (!defined('ABSPATH')) { + die('You are not allowed to call this page directly.'); +} - $enabled = $use_template = $this->show_form = true; - $subject = __('** You\'ve paused your subscription', 'memberpress'); - $body = $this->body_partial(); +class MeprUserSuspendedSubEmail extends MeprBaseOptionsUserEmail +{ + /** + * Set the default enabled, title, subject & body + */ + public function set_defaults($args = []) + { + $this->title = __('Paused Subscription Notice', 'memberpress'); + $this->description = __('This email is sent to the user when one of their subscriptions is paused.', 'memberpress'); + $this->ui_order = 5; - $this->defaults = compact( 'enabled', 'subject', 'body', 'use_template' ); - $this->variables = MeprSubscriptionsHelper::get_email_vars(); - } -} + $enabled = $use_template = $this->show_form = true; + $subject = __('** You\'ve paused your subscription', 'memberpress'); + $body = $this->body_partial(); + $this->defaults = compact('enabled', 'subject', 'body', 'use_template'); + $this->variables = MeprSubscriptionsHelper::get_email_vars(); + } +} diff --git a/app/emails/MeprUserUpgradedSubEmail.php b/app/emails/MeprUserUpgradedSubEmail.php index d008e6a..0b4668b 100644 --- a/app/emails/MeprUserUpgradedSubEmail.php +++ b/app/emails/MeprUserUpgradedSubEmail.php @@ -1,19 +1,25 @@ title = __('Upgraded Subscription Notice','memberpress'); - $this->description = __('This email is sent to the user when they upgrade a subscription.', 'memberpress'); - $this->ui_order = 3; +if (!defined('ABSPATH')) { + die('You are not allowed to call this page directly.'); +} - $enabled = $use_template = $this->show_form = true; - $subject = __('** You\'ve upgraded your subscription', 'memberpress'); - $body = $this->body_partial(); +class MeprUserUpgradedSubEmail extends MeprBaseOptionsUserEmail +{ + /** + * Set the default enabled, title, subject & body + */ + public function set_defaults($args = []) + { + $this->title = __('Upgraded Subscription Notice', 'memberpress'); + $this->description = __('This email is sent to the user when they upgrade a subscription.', 'memberpress'); + $this->ui_order = 3; - $this->defaults = compact( 'enabled', 'subject', 'body', 'use_template' ); - $this->variables = MeprSubscriptionsHelper::get_email_vars(); - } -} + $enabled = $use_template = $this->show_form = true; + $subject = __('** You\'ve upgraded your subscription', 'memberpress'); + $body = $this->body_partial(); + $this->defaults = compact('enabled', 'subject', 'body', 'use_template'); + $this->variables = MeprSubscriptionsHelper::get_email_vars(); + } +} diff --git a/app/emails/MeprUserWelcomeEmail.php b/app/emails/MeprUserWelcomeEmail.php index 0f9c4f4..9cd5cb4 100644 --- a/app/emails/MeprUserWelcomeEmail.php +++ b/app/emails/MeprUserWelcomeEmail.php @@ -1,19 +1,25 @@ title = __('Welcome Email','memberpress'); - $this->description = __('This email is sent welcome a new user when she initially signs up for your membership site with a completed purchase.', 'memberpress'); - $this->ui_order = 0; +if (!defined('ABSPATH')) { + die('You are not allowed to call this page directly.'); +} - $enabled = $use_template = $this->show_form = true; - $subject = __('** Welcome to {$blog_name}', 'memberpress'); - $body = $this->body_partial(); +class MeprUserWelcomeEmail extends MeprBaseOptionsUserEmail +{ + /** + * Set the default enabled, title, subject & body + */ + public function set_defaults($args = []) + { + $this->title = __('Welcome Email', 'memberpress'); + $this->description = __('This email is sent welcome a new user when she initially signs up for your membership site with a completed purchase.', 'memberpress'); + $this->ui_order = 0; - $this->defaults = compact( 'enabled', 'subject', 'body', 'use_template' ); - $this->variables = MeprTransactionsHelper::get_email_vars(); - } -} + $enabled = $use_template = $this->show_form = true; + $subject = __('** Welcome to {$blog_name}', 'memberpress'); + $body = $this->body_partial(); + $this->defaults = compact('enabled', 'subject', 'body', 'use_template'); + $this->variables = MeprTransactionsHelper::get_email_vars(); + } +} diff --git a/app/emails/index.php b/app/emails/index.php index 89c513c..d3a3233 100644 --- a/app/emails/index.php +++ b/app/emails/index.php @@ -1 +1,3 @@ - +name = __('Offline Payment', 'memberpress'); - $this->key = __('offline', 'memberpress'); - $this->has_spc_form = true; - $this->set_defaults(); - - $this->capabilities = array( - //'process-payments', - //'create-subscriptions', - //'process-refunds', - 'cancel-subscriptions', //Yup we can cancel them here - needed for upgrade/downgrades - //'update-subscriptions', - //'suspend-subscriptions', - //'send-cc-expirations' - ); - - // Setup the notification actions for this gateway - $this->notifiers = array(); - } - - public function load($settings) { - $this->settings = (object)$settings; - $this->set_defaults(); - } - - protected function set_defaults() { - if(!isset($this->settings)) { - $this->settings = array(); - } - - $this->settings = (object)array_merge( - array( - 'gateway' => 'MeprArtificialGateway', - 'id' => $this->generate_id(), - 'label' => '', - 'use_label' => true, - 'icon' => '', - 'use_icon' => true, - 'desc' => '', - 'use_desc' => true, - 'manually_complete' => false, - 'always_send_welcome' => false, - 'no_cancel_up_down_grade' => false, - 'email' => '', - 'sandbox' => false, - 'debug' => false - ), - (array)$this->settings - ); - - $this->id = $this->settings->id; - $this->label = $this->settings->label; - $this->use_label = $this->settings->use_label; - $this->icon = $this->settings->icon; - $this->use_icon = $this->settings->use_icon; - $this->desc = $this->settings->desc; - $this->use_desc = $this->settings->use_desc; - //$this->recurrence_type = $this->settings->recurrence_type; - } - - // Hide update link when using offline gateway. - protected function hide_update_link($subscription) { - return true; - } - - public function spc_payment_fields() { - return $this->settings->desc; - } - - //Used for capturing offline gateway events - public static function capture_txn_status_for_events($txn) { - $mepr_options = MeprOptions::fetch(); - $gateway = $mepr_options->payment_method($txn->gateway); - - if(self::event_exists_already($txn)) { return; } - - if($gateway !== false && isset($gateway->settings->gateway) && $gateway->settings->gateway == 'MeprArtificialGateway') { - MeprEvent::record('offline-payment-'.$txn->status, $txn); - - if($txn->status == MeprTransaction::$complete_str) { +class MeprArtificialGateway extends MeprBaseRealGateway +{ + /** + * Used in the view to identify the gateway + */ + public function __construct() + { + $this->name = __('Offline Payment', 'memberpress'); + $this->key = __('offline', 'memberpress'); + $this->has_spc_form = true; + $this->set_defaults(); + + $this->capabilities = [ + // 'process-payments', + // 'create-subscriptions', + // 'process-refunds', + 'cancel-subscriptions', // Yup we can cancel them here - needed for upgrade/downgrades + // 'update-subscriptions', + // 'suspend-subscriptions', + // 'send-cc-expirations' + ]; + + // Setup the notification actions for this gateway + $this->notifiers = []; + } + + public function load($settings) + { + $this->settings = (object)$settings; + $this->set_defaults(); + } + + protected function set_defaults() + { + if (!isset($this->settings)) { + $this->settings = []; + } + + $this->settings = (object)array_merge( + [ + 'gateway' => 'MeprArtificialGateway', + 'id' => $this->generate_id(), + 'label' => '', + 'use_label' => true, + 'icon' => '', + 'use_icon' => true, + 'desc' => '', + 'use_desc' => true, + 'manually_complete' => false, + 'always_send_welcome' => false, + 'no_cancel_up_down_grade' => false, + 'email' => '', + 'sandbox' => false, + 'debug' => false, + ], + (array)$this->settings + ); + + $this->id = $this->settings->id; + $this->label = $this->settings->label; + $this->use_label = $this->settings->use_label; + $this->icon = $this->settings->icon; + $this->use_icon = $this->settings->use_icon; + $this->desc = $this->settings->desc; + $this->use_desc = $this->settings->use_desc; + // $this->recurrence_type = $this->settings->recurrence_type; + } + + // Hide update link when using offline gateway. + protected function hide_update_link($subscription) + { + return true; + } + + public function spc_payment_fields() + { + return $this->settings->desc; + } + + // Used for capturing offline gateway events + public static function capture_txn_status_for_events($txn) + { + $mepr_options = MeprOptions::fetch(); + $gateway = $mepr_options->payment_method($txn->gateway); + + if (self::event_exists_already($txn)) { + return; + } + + if ($gateway !== false && isset($gateway->settings->gateway) && $gateway->settings->gateway == 'MeprArtificialGateway') { + MeprEvent::record('offline-payment-' . $txn->status, $txn); + + if ($txn->status == MeprTransaction::$complete_str) { + $sub = $txn->subscription(); + if ($sub && $sub instanceof MeprSubscription) { + $sub->limit_payment_cycles(); + } + + self::maybe_cancel_old_sub($txn, $gateway); + MeprUtils::send_transaction_receipt_notices($txn); + } + } + } + + public static function event_exists_already($txn) + { + global $wpdb; + $mepr_db = new MeprDb(); + + return $wpdb->get_results("SELECT * FROM {$mepr_db->events} WHERE event = 'offline-payment-{$txn->status}' AND evt_id = {$txn->id} AND evt_id_type = 'transactions'"); + } + + /** + * Used to send data to a given payment gateway. In gateways which redirect + * before this step is necessary this method should just be left blank. + */ + public function process_payment($txn) + { + if (isset($txn) && $txn instanceof MeprTransaction) { + $usr = new MeprUser($txn->user_id); + $prd = new MeprProduct($txn->product_id); + } else { + return; + } + + $upgrade = $txn->is_upgrade(); + $downgrade = $txn->is_downgrade(); + + $event_txn = $txn->maybe_cancel_old_sub(); + + if ($upgrade) { + $this->upgraded_sub($txn, $event_txn); + } elseif ($downgrade) { + $this->downgraded_sub($txn, $event_txn); + } else { + $this->new_sub($txn); + } + + $txn->gateway = $this->id; + $txn->trans_num = 't_' . uniqid(); + $txn->store(); + + if (!$this->settings->manually_complete) { + $txn->status = MeprTransaction::$complete_str; + $txn->store(); // Need to store here so the event will show as "complete" when firing the hooks + // The receipt is set when the transaction is automatically set to complete (see: capture_txn_status_for_events) + // MeprUtils::send_transaction_receipt_notices($txn); + MeprUtils::send_signup_notices($txn); + } else { + if ($this->settings->always_send_welcome) { + MeprUtils::send_signup_notices($txn, false, true); + } elseif (!$usr->signup_notice_sent) { + MeprUtils::send_notices($txn, null, 'MeprAdminSignupEmail'); + MeprUtils::send_notices($txn, null, 'MeprAdminNewOneOffEmail'); + $usr->signup_notice_sent = true; + $usr->store(); + } + + MeprEvent::record('member-signup-completed', $usr, (object)$txn->rec); + } + + return $txn; + } + + /** + * Used to record a successful recurring payment by the given gateway. It + * should have the ability to record a successful payment or a failure. It is + * this method that should be used when receiving an IPN from PayPal or a + * Silent Post from Authorize.net. + */ + public function record_subscription_payment() + { + // Doesn't happen in test mode ... no need + } + + /** + * Used to record a declined payment. + */ + public function record_payment_failure() + { + // No need for this here + } + + /** + * Used to record a successful payment by the given gateway. It should have + * the ability to record a successful payment or a failure. It is this method + * that should be used when receiving an IPN from PayPal or a Silent Post + * from Authorize.net. + */ + public function record_payment() + { + // This happens manually in test mode + } + + /** + * This method should be used by the class to record a successful refund from + * the gateway. This method should also be used by any IPN requests or Silent Posts. + */ + public function process_refund(MeprTransaction $txn) + { + // This happens manually in test mode + } + + /** + * This method should be used by the class to record a successful refund from + * the gateway. This method should also be used by any IPN requests or Silent Posts. + */ + public function record_refund() + { + // This happens manually in test mode + } + + // Not needed in the Artificial gateway + public function process_trial_payment($transaction) + { + } + public function record_trial_payment($transaction) + { + } + + /** + * Used to send subscription data to a given payment gateway. In gateways + * which redirect before this step is necessary this method should just be + * left blank. + */ + public function process_create_subscription($txn) + { + if (isset($txn) && $txn instanceof MeprTransaction) { + $usr = new MeprUser($txn->user_id); + $prd = new MeprProduct($txn->product_id); + } else { + return; + } $sub = $txn->subscription(); - if( $sub && $sub instanceof MeprSubscription ) { - $sub->limit_payment_cycles(); + + // Not super thrilled about this but there are literally + // no automated recurring profiles when paying offline + $sub->subscr_id = 'ts_' . uniqid(); + $sub->status = MeprSubscription::$active_str; + $sub->created_at = gmdate('c'); + $sub->gateway = $this->id; + + // If this subscription has a paid trail, we need to change the price of this transaction to the trial price duh + if ($sub->trial) { + $txn->set_subtotal(MeprUtils::format_float($sub->trial_amount)); + $expires_ts = time() + MeprUtils::days($sub->trial_days); + $txn->expires_at = gmdate('c', $expires_ts); } - self::maybe_cancel_old_sub($txn, $gateway); - MeprUtils::send_transaction_receipt_notices($txn); - } - } - } - - public static function event_exists_already($txn) { - global $wpdb; - $mepr_db = new MeprDb(); - - return $wpdb->get_results("SELECT * FROM {$mepr_db->events} WHERE event = 'offline-payment-{$txn->status}' AND evt_id = {$txn->id} AND evt_id_type = 'transactions'"); - } - - /** Used to send data to a given payment gateway. In gateways which redirect - * before this step is necessary this method should just be left blank. - */ - public function process_payment($txn) { - if(isset($txn) && $txn instanceof MeprTransaction) { - $usr = new MeprUser($txn->user_id); - $prd = new MeprProduct($txn->product_id); - } - else { - return; - } - - $upgrade = $txn->is_upgrade(); - $downgrade = $txn->is_downgrade(); - - $event_txn = $txn->maybe_cancel_old_sub(); - - if($upgrade) { - $this->upgraded_sub($txn, $event_txn); - } - elseif($downgrade) { - $this->downgraded_sub($txn, $event_txn); - } - else { - $this->new_sub($txn); - } - - $txn->gateway = $this->id; - $txn->trans_num = 't_' . uniqid(); - $txn->store(); - - if(!$this->settings->manually_complete) { - $txn->status = MeprTransaction::$complete_str; - $txn->store(); //Need to store here so the event will show as "complete" when firing the hooks - //The receipt is set when the transaction is automatically set to complete (see: capture_txn_status_for_events) - //MeprUtils::send_transaction_receipt_notices($txn); - MeprUtils::send_signup_notices($txn); - } - else { - if($this->settings->always_send_welcome) { - MeprUtils::send_signup_notices($txn, false, true); - } - else if (!$usr->signup_notice_sent) { - MeprUtils::send_notices($txn, null, 'MeprAdminSignupEmail'); - MeprUtils::send_notices($txn, null, 'MeprAdminNewOneOffEmail'); - $usr->signup_notice_sent = true; - $usr->store(); - } + // This will only work before maybe_cancel_old_sub is run + $upgrade = $sub->is_upgrade(); + $downgrade = $sub->is_downgrade(); - MeprEvent::record('member-signup-completed', $usr, (object)$txn->rec); + $event_txn = $sub->maybe_cancel_old_sub(); + + if ($upgrade) { + $this->upgraded_sub($sub, $event_txn); + } elseif ($downgrade) { + $this->downgraded_sub($sub, $event_txn); + } else { + $this->new_sub($sub, true); + } + + $sub->store(); + + $txn->gateway = $this->id; + $txn->trans_num = 't_' . uniqid(); + $txn->store(); + + if (!$this->settings->manually_complete) { + $txn->status = MeprTransaction::$complete_str; + $txn->store(); // Need to store here so the event will show as "complete" when firing the hooks + MeprUtils::send_signup_notices($txn); + } else { + if ($this->settings->always_send_welcome) { + MeprUtils::send_signup_notices($txn, false, true); + } elseif (!$usr->signup_notice_sent) { + MeprUtils::send_notices($txn, null, 'MeprAdminSignupEmail'); + $usr->signup_notice_sent = true; + $usr->store(); + } + + // Apparently this gets sent already somewhere else + // MeprUtils::send_notices($sub, null, 'MeprAdminNewSubEmail'); + MeprEvent::record('member-signup-completed', $usr, (object)$txn->rec); + } + + return [ + 'subscription' => $sub, + 'transaction' => $txn, + ]; } - return $txn; - } + /** + * Used to record a successful subscription by the given gateway. It should have + * the ability to record a successful subscription or a failure. It is this method + * that should be used when receiving an IPN from PayPal or a Silent Post + * from Authorize.net. + */ + public function record_create_subscription() + { + // No reason to separate this out without webhooks/postbacks/ipns + } - /** Used to record a successful recurring payment by the given gateway. It - * should have the ability to record a successful payment or a failure. It is - * this method that should be used when receiving an IPN from PayPal or a - * Silent Post from Authorize.net. - */ - public function record_subscription_payment() { - // Doesn't happen in test mode ... no need - } - - /** Used to record a declined payment. */ - public function record_payment_failure() { - // No need for this here - } + public function process_update_subscription($sub_id) + { + // This happens manually in test mode + } - /** Used to record a successful payment by the given gateway. It should have - * the ability to record a successful payment or a failure. It is this method - * that should be used when receiving an IPN from PayPal or a Silent Post - * from Authorize.net. - */ - public function record_payment() { - // This happens manually in test mode - } - - /** This method should be used by the class to record a successful refund from - * the gateway. This method should also be used by any IPN requests or Silent Posts. - */ - public function process_refund(MeprTransaction $txn) { - // This happens manually in test mode - } + /** + * This method should be used by the class to record a successful cancellation + * from the gateway. This method should also be used by any IPN requests or + * Silent Posts. + */ + public function record_update_subscription() + { + // No need for this one with the artificial gateway + } + + /** + * Used to suspend a subscription by the given gateway. + */ + public function process_suspend_subscription($sub_id) + { + } + + /** + * This method should be used by the class to record a successful suspension + * from the gateway. + */ + public function record_suspend_subscription() + { + } + + /** + * Used to suspend a subscription by the given gateway. + */ + public function process_resume_subscription($sub_id) + { + } + + /** + * This method should be used by the class to record a successful resuming of + * as subscription from the gateway. + */ + public function record_resume_subscription() + { + } + + /** + * Used to cancel a subscription by the given gateway. This method should be used + * by the class to record a successful cancellation from the gateway. This method + * should also be used by any IPN requests or Silent Posts. + */ + public function process_cancel_subscription($sub_id) + { + $sub = new MeprSubscription($sub_id); + $_REQUEST['sub_id'] = $sub_id; + $this->record_cancel_subscription(); + } + + /** + * This method should be used by the class to record a successful cancellation + * from the gateway. This method should also be used by any IPN requests or + * Silent Posts. + */ + public function record_cancel_subscription() + { + $sub = new MeprSubscription($_REQUEST['sub_id']); + + if (!$sub) { + return false; + } + + // Seriously ... if sub was already cancelled what are we doing here? + if ($sub->status == MeprSubscription::$cancelled_str) { + return $sub; + } + + $sub->status = MeprSubscription::$cancelled_str; + $sub->store(); + + if (isset($_REQUEST['expire'])) { + $sub->limit_reached_actions(); + } + + if (!isset($_REQUEST['silent']) || ($_REQUEST['silent'] == false)) { + MeprUtils::send_cancelled_sub_notices($sub); + } + + return $sub; + } + + /** + * This gets called on the 'init' hook when the signup form is processed ... + * this is in place so that payment solutions like paypal can redirect + * before any content is rendered. + */ + public function process_signup_form($txn) + { + // if($txn->amount <= 0.00) { + // MeprTransaction::create_free_transaction($txn); + // return; + // } + // Redirect to thank you page + // $mepr_options = MeprOptions::fetch(); + // $product = new MeprProduct($txn->product_id); + // $sanitized_title = sanitize_title($product->post_title); + // MeprUtils::wp_redirect($mepr_options->thankyou_page_url("membership={$sanitized_title}&trans_num={$txn->trans_num}")); + } + + public function display_payment_page($txn) + { + // Nothing here yet + } + + /** + * This gets called on wp_enqueue_script and enqueues a set of + * scripts for use on the page containing the payment form + */ + public function enqueue_payment_form_scripts() + { + // This happens manually in test mode + } - /** This method should be used by the class to record a successful refund from - * the gateway. This method should also be used by any IPN requests or Silent Posts. - */ - public function record_refund() { - // This happens manually in test mode - } - - //Not needed in the Artificial gateway - public function process_trial_payment($transaction) { } - public function record_trial_payment($transaction) { } - - /** Used to send subscription data to a given payment gateway. In gateways - * which redirect before this step is necessary this method should just be - * left blank. - */ - public function process_create_subscription($txn) { - if(isset($txn) && $txn instanceof MeprTransaction) { - $usr = new MeprUser($txn->user_id); - $prd = new MeprProduct($txn->product_id); - } - else { - return; - } - - $sub = $txn->subscription(); - - // Not super thrilled about this but there are literally - // no automated recurring profiles when paying offline - $sub->subscr_id = 'ts_' . uniqid(); - $sub->status = MeprSubscription::$active_str; - $sub->created_at = gmdate('c'); - $sub->gateway = $this->id; - - //If this subscription has a paid trail, we need to change the price of this transaction to the trial price duh - if($sub->trial) { - $txn->set_subtotal(MeprUtils::format_float($sub->trial_amount)); - $expires_ts = time() + MeprUtils::days($sub->trial_days); - $txn->expires_at = gmdate('c', $expires_ts); - } - - // This will only work before maybe_cancel_old_sub is run - $upgrade = $sub->is_upgrade(); - $downgrade = $sub->is_downgrade(); - - $event_txn = $sub->maybe_cancel_old_sub(); - - if($upgrade) { - $this->upgraded_sub($sub, $event_txn); - } - else if($downgrade) { - $this->downgraded_sub($sub, $event_txn); - } - else { - $this->new_sub($sub, true); - } - - $sub->store(); - - $txn->gateway = $this->id; - $txn->trans_num = 't_' . uniqid(); - $txn->store(); - - if(!$this->settings->manually_complete) { - $txn->status = MeprTransaction::$complete_str; - $txn->store(); //Need to store here so the event will show as "complete" when firing the hooks - MeprUtils::send_signup_notices($txn); - } - else { - if($this->settings->always_send_welcome) { - MeprUtils::send_signup_notices($txn, false, true); - } - else if (!$usr->signup_notice_sent) { - MeprUtils::send_notices($txn, null, 'MeprAdminSignupEmail'); - $usr->signup_notice_sent = true; - $usr->store(); - } - - // Apparently this gets sent already somewhere else - // MeprUtils::send_notices($sub, null, 'MeprAdminNewSubEmail'); - - MeprEvent::record('member-signup-completed', $usr, (object)$txn->rec); - } - - return array('subscription' => $sub, 'transaction' => $txn); - } - - /** Used to record a successful subscription by the given gateway. It should have - * the ability to record a successful subscription or a failure. It is this method - * that should be used when receiving an IPN from PayPal or a Silent Post - * from Authorize.net. - */ - public function record_create_subscription() { - // No reason to separate this out without webhooks/postbacks/ipns - } - - public function process_update_subscription($sub_id) { - // This happens manually in test mode - } - - /** This method should be used by the class to record a successful cancellation - * from the gateway. This method should also be used by any IPN requests or - * Silent Posts. - */ - public function record_update_subscription() { - // No need for this one with the artificial gateway - } - - /** Used to suspend a subscription by the given gateway. - */ - public function process_suspend_subscription($sub_id) {} - - /** This method should be used by the class to record a successful suspension - * from the gateway. - */ - public function record_suspend_subscription() {} - - /** Used to suspend a subscription by the given gateway. - */ - public function process_resume_subscription($sub_id) {} - - /** This method should be used by the class to record a successful resuming of - * as subscription from the gateway. - */ - public function record_resume_subscription() {} - - /** Used to cancel a subscription by the given gateway. This method should be used - * by the class to record a successful cancellation from the gateway. This method - * should also be used by any IPN requests or Silent Posts. - */ - public function process_cancel_subscription($sub_id) { - $sub = new MeprSubscription($sub_id); - $_REQUEST['sub_id'] = $sub_id; - $this->record_cancel_subscription(); - } - - /** This method should be used by the class to record a successful cancellation - * from the gateway. This method should also be used by any IPN requests or - * Silent Posts. - */ - public function record_cancel_subscription() { - $sub = new MeprSubscription($_REQUEST['sub_id']); - - if(!$sub) { return false; } - - // Seriously ... if sub was already cancelled what are we doing here? - if($sub->status == MeprSubscription::$cancelled_str) { return $sub; } - - $sub->status = MeprSubscription::$cancelled_str; - $sub->store(); - - if(isset($_REQUEST['expire'])) - $sub->limit_reached_actions(); - - if(!isset($_REQUEST['silent']) || ($_REQUEST['silent'] == false)) - MeprUtils::send_cancelled_sub_notices($sub); - - return $sub; - } - - /** This gets called on the 'init' hook when the signup form is processed ... - * this is in place so that payment solutions like paypal can redirect - * before any content is rendered. - */ - public function process_signup_form($txn) { - //if($txn->amount <= 0.00) { - // MeprTransaction::create_free_transaction($txn); - // return; - //} - - // Redirect to thank you page - //$mepr_options = MeprOptions::fetch(); - // $product = new MeprProduct($txn->product_id); - // $sanitized_title = sanitize_title($product->post_title); - //MeprUtils::wp_redirect($mepr_options->thankyou_page_url("membership={$sanitized_title}&trans_num={$txn->trans_num}")); - } - - public function display_payment_page($txn) { - // Nothing here yet - } - - /** This gets called on wp_enqueue_script and enqueues a set of - * scripts for use on the page containing the payment form - */ - public function enqueue_payment_form_scripts() { - // This happens manually in test mode - } - - /** This gets called on the_content and just renders the payment form - */ - public function display_payment_form($amount, $user, $product_id, $txn_id) { - $mepr_options = MeprOptions::fetch(); - $prd = new MeprProduct($product_id); - $coupon = false; - - $txn = new MeprTransaction($txn_id); - - //Artifically set the price of the $prd in case a coupon was used - if($prd->price != $amount) { - $coupon = true; - $prd->price = $amount; - } - - ob_start(); - - $invoice = MeprTransactionsHelper::get_invoice($txn); - echo $invoice; - echo MeprOptionsHelper::payment_method_description( $this ); - - ?> + /** + * This gets called on the_content and just renders the payment form + */ + public function display_payment_form($amount, $user, $product_id, $txn_id) + { + $mepr_options = MeprOptions::fetch(); + $prd = new MeprProduct($product_id); + $coupon = false; + + $txn = new MeprTransaction($txn_id); + + // Artifically set the price of the $prd in case a coupon was used + if ($prd->price != $amount) { + $coupon = true; + $prd->price = $amount; + } + + ob_start(); + + $invoice = MeprTransactionsHelper::get_invoice($txn); + echo $invoice; + echo MeprOptionsHelper::payment_method_description($this); + + ?>
    @@ -411,24 +469,30 @@ public function display_payment_form($amount, $user, $product_id, $txn_id) {
    - settings->manually_complete == "on" || $this->settings->manually_complete == true); - $no_cancel_up_down_grade = ($this->settings->no_cancel_up_down_grade == "on" || $this->settings->no_cancel_up_down_grade == true); - $always_send_welcome = ($this->settings->always_send_welcome == "on" || $this->settings->always_send_welcome == true); - ?> + settings->manually_complete == 'on' || $this->settings->manually_complete == true); + $no_cancel_up_down_grade = ($this->settings->no_cancel_up_down_grade == 'on' || $this->settings->no_cancel_up_down_grade == true); + $always_send_welcome = ($this->settings->always_send_welcome == 'on' || $this->settings->always_send_welcome == true); + ?>
    @@ -452,60 +516,78 @@ public function display_options_form() {
    - -

    - status == MeprTransaction::$complete_str && $gateway->settings->manually_complete && $gateway->settings->no_cancel_up_down_grade ) { - if ($sub = $txn->subscription()) { - $sub->maybe_cancel_old_sub(true); //pass true here to by pass the artificial gateway check - } else { - $txn->maybe_cancel_old_sub(true); //pass true here to by pass the artificial gateway check - } - } - } + +

    + status == MeprTransaction::$complete_str && $gateway->settings->manually_complete && $gateway->settings->no_cancel_up_down_grade) { + if ($sub = $txn->subscription()) { + $sub->maybe_cancel_old_sub(true); // pass true here to by pass the artificial gateway check + } else { + $txn->maybe_cancel_old_sub(true); // pass true here to by pass the artificial gateway check + } + } + } } //End class diff --git a/app/gateways/MeprAuthorizeAPI.php b/app/gateways/MeprAuthorizeAPI.php index d2091d3..a1163f8 100644 --- a/app/gateways/MeprAuthorizeAPI.php +++ b/app/gateways/MeprAuthorizeAPI.php @@ -1,68 +1,78 @@ login_name = isset($settings->login_name) ? $settings->login_name : ''; - $this->transaction_key = isset($settings->transaction_key) ? $settings->transaction_key : ''; - $this->test_mode = isset($settings->test_mode) && $settings->test_mode; - if($this->test_mode) { - $this->api_endpoint = self::$sandbox_api_endpoint; - } - else { - $this->api_endpoint = self::$live_api_endpoint; +class MeprAuthorizeAPI +{ + public static $sandbox_api_endpoint = 'https://apitest.authorize.net/xml/v1/request.api'; + public static $live_api_endpoint = 'https://api.authorize.net/xml/v1/request.api'; + private $api_endpoint; + private $login_name; + private $transaction_key; + private $test_mode; + + public function __construct($settings = []) + { + $this->login_name = isset($settings->login_name) ? $settings->login_name : ''; + $this->transaction_key = isset($settings->transaction_key) ? $settings->transaction_key : ''; + $this->test_mode = isset($settings->test_mode) && $settings->test_mode; + if ($this->test_mode) { + $this->api_endpoint = self::$sandbox_api_endpoint; + } else { + $this->api_endpoint = self::$live_api_endpoint; + } } - } - /** - * Fetch transaction details from the Auth.net API - * @param int Transaction ID - * @return object|null JSON decoded transaction object. NULL on API error. - */ - public function get_transaction_details($id) { - return $this->send_request('getTransactionDetailsRequest', array('transId' => $id)); - } + /** + * Fetch transaction details from the Auth.net API + * + * @param int Transaction ID + * @return object|null JSON decoded transaction object. NULL on API error. + */ + public function get_transaction_details($id) + { + return $this->send_request('getTransactionDetailsRequest', ['transId' => $id]); + } - /** - * Send request to the Auth.net api - * @param string $type API request type - * @param array $args API request arguments - * @return object|null JSON decoded transaction object. NULL on API error. - */ - public function send_request($type, $args = array()) { - $post_body = json_encode( - array( - $type => array( - 'merchantAuthentication' => array( - 'name' => $this->login_name, - 'transactionKey' => $this->transaction_key - ), - 'transId' => $args['transId'] - ) - ) - ); + /** + * Send request to the Auth.net api + * + * @param string $type API request type + * @param array $args API request arguments + * @return object|null JSON decoded transaction object. NULL on API error. + */ + public function send_request($type, $args = []) + { + $post_body = json_encode( + [ + $type => [ + 'merchantAuthentication' => [ + 'name' => $this->login_name, + 'transactionKey' => $this->transaction_key, + ], + 'transId' => $args['transId'], + ], + ] + ); - $api_response_body = wp_remote_retrieve_body(wp_remote_post($this->api_endpoint, array('body' => $post_body, 'headers' => array('content-type' => 'application/json')))); - // Authorize.net is sending some garbage at the beginning of the response body that is not valid JSON - // Reference: https://community.developer.authorize.net/t5/Integration-and-Testing/JSON-issues/td-p/48851 - $api_response_body = preg_replace('/^[^\{]*/', '', $api_response_body); - $response_json = json_decode($api_response_body); + $api_response_body = wp_remote_retrieve_body(wp_remote_post($this->api_endpoint, [ + 'body' => $post_body, + 'headers' => ['content-type' => 'application/json'], + ])); + // Authorize.net is sending some garbage at the beginning of the response body that is not valid JSON + // Reference: https://community.developer.authorize.net/t5/Integration-and-Testing/JSON-issues/td-p/48851 + $api_response_body = preg_replace('/^[^\{]*/', '', $api_response_body); + $response_json = json_decode($api_response_body); - if($response_json->messages->resultCode === 'Error') { - foreach ($response_json->messages->message as $error) { - MeprUtils::error_log('Authorize API Error ' . $error->code . '-' . $error->text); - } - return null; - } - else { - return $response_json; + if ($response_json->messages->resultCode === 'Error') { + foreach ($response_json->messages->message as $error) { + MeprUtils::error_log('Authorize API Error ' . $error->code . '-' . $error->text); + } + return null; + } else { + return $response_json; + } } - } } diff --git a/app/gateways/MeprAuthorizeGateway.php b/app/gateways/MeprAuthorizeGateway.php index 11560d4..bb1a5ed 100644 --- a/app/gateways/MeprAuthorizeGateway.php +++ b/app/gateways/MeprAuthorizeGateway.php @@ -1,949 +1,1048 @@ name = __("Authorize.net", 'memberpress'); - $this->key = __('authorize', 'memberpress'); - $this->has_spc_form = true; - $this->set_defaults(); - - $this->capabilities = array( - 'process-credit-cards', - 'process-payments', - 'create-subscriptions', - 'cancel-subscriptions', - 'update-subscriptions', - 'send-cc-expirations', - 'order-bumps', - 'multiple-subscriptions', - ); - - // Setup the notification actions for this gateway - $this->notifiers = array( - 'sp' => 'listener', - 'whk' => 'webhook_listener', - ); - $this->message_pages = array(); - } - - public function load($settings) { - $this->settings = (object)$settings; - $this->set_defaults(); - } - - public function set_defaults() { - if(!isset($this->settings)) - $this->settings = array(); - - $this->settings = (object)array_merge( - array( - 'gateway' => get_class($this), - 'id' => $this->generate_id(), - 'label' => '', - 'use_label' => true, - 'icon' => MEPR_IMAGES_URL . '/checkout/cards.png', - 'use_icon' => true, - 'desc' => __('Pay with your credit card via Authorize.net', 'memberpress'), - 'use_desc' => true, - //'recurrence_type' => '', - 'login_name' => '', - 'transaction_key' => '', - 'signature_key' => '', - 'force_ssl' => false, - 'debug' => false, - //'use_cron' => false, - 'test_mode' => false, - 'aimUrl' => '', - 'arbUrl' => '' - ), - (array)$this->settings - ); - - $this->id = $this->settings->id; - $this->label = $this->settings->label; - $this->use_label = $this->settings->use_label; - $this->icon = $this->settings->icon; - $this->use_icon = $this->settings->use_icon; - $this->desc = $this->settings->desc; - $this->use_desc = $this->settings->use_desc; - //$this->recurrence_type = $this->settings->recurrence_type; - $this->hash = strtoupper(substr(md5($this->id),0,20)); // MD5 hashes used for Silent posts can only be 20 chars long - - if($this->is_test_mode()) { - $this->settings->aimUrl = 'https://test.authorize.net/gateway/transact.dll'; - $this->settings->arbUrl = 'https://apitest.authorize.net/xml/v1/request.api'; - } else { - $this->settings->aimUrl = 'https://secure2.authorize.net/gateway/transact.dll'; - $this->settings->arbUrl = 'https://api2.authorize.net/xml/v1/request.api'; +class MeprAuthorizeGateway extends MeprBaseRealGateway +{ + public static $order_invoice_str = '_mepr_authnet_order_invoice'; + + /** + * Used in the view to identify the gateway + */ + public function __construct() + { + $this->name = __('Authorize.net', 'memberpress'); + $this->key = __('authorize', 'memberpress'); + $this->has_spc_form = true; + $this->set_defaults(); + + $this->capabilities = [ + 'process-credit-cards', + 'process-payments', + 'create-subscriptions', + 'cancel-subscriptions', + 'update-subscriptions', + 'send-cc-expirations', + 'order-bumps', + 'multiple-subscriptions', + ]; + + // Setup the notification actions for this gateway + $this->notifiers = [ + 'sp' => 'listener', + 'whk' => 'webhook_listener', + ]; + $this->message_pages = []; } - // An attempt to correct people who paste in spaces along with their credentials - $this->settings->login_name = trim($this->settings->login_name); - $this->settings->transaction_key = trim($this->settings->transaction_key); - $this->settings->signature_key = trim($this->settings->signature_key); - } - - public function listener() { - $this->email_status("Silent Post Just Came In (" . $_SERVER['REQUEST_METHOD'] . "):\n" . MeprUtils::object_to_string($_REQUEST, true) . "\n", $this->settings->debug); - - if($this->validate_sp_md5()) { - if(isset($_REQUEST['x_response_code']) && $_REQUEST['x_response_code'] > 1) { - return $this->record_payment_failure(); - } - // AUTHORIZE.NET HAS DEPRECATED MD5, BUT SILENT POST IS STILL AROUND - // WE'RE GOING TO USE SP TO CAPTURE FAILED PAYMENTS STILL - // else if(isset($_REQUEST['x_subscription_id']) and !empty($_REQUEST['x_subscription_id'])) { - // $sub = MeprSubscription::get_one_by_subscr_id($_REQUEST['x_subscription_id']); - // if(!$sub) { return false; } - // return $this->record_subscription_payment(); - // } - // else if(strtoupper($_REQUEST['x_type']) == 'VOID' || strtoupper($_REQUEST['x_type']) == 'CREDIT') - // return $this->record_refund(); - - // Nothing applied so let's bail - return false; - } - } - - /** - * Webhook listener. Responds to select Auth.net webhook notifications. - * - */ - public function webhook_listener() { - $this->email_status("Webhook Just Came In (" . $_SERVER['REQUEST_METHOD'] . "):\n" . MeprUtils::object_to_string($_REQUEST, true) . "\n", $this->settings->debug); - require_once(__DIR__ . '/MeprAuthorizeWebhooks.php'); - $webhook_handler = new MeprAuthorizeWebhooks($this->settings); - try { - $webhook_handler->process_webhook(); - } - catch(Exception $e) { - MeprUtils::error_log('MeprAuthorizeGateway Webhook Error: ' . $e->getMessage()); + public function load($settings) + { + $this->settings = (object)$settings; + $this->set_defaults(); } - } - - /** - * Validate the request using the MD5 hash - * @deprecated 1.3.49 will be removed in future release - * @see https://developer.authorize.net/support/hash_upgrade/ - */ - public function validate_sp_md5() { - // $md5_input = $this->hash . $this->settings->login_name . $_REQUEST['x_trans_id'] . $_REQUEST['x_amount']; - // $md5 = md5($md5_input); - - // return strtoupper($md5) === strtoupper($_REQUEST['x_MD5_Hash']); - - // AUTHORIZE.NET HAS DEPRECATED MD5, BUT SILENT POST IS STILL AROUND - // WE'RE GOING TO USE SP TO CAPTURE FAILED PAYMENTS STILL - return true; - } - - public function process_order_bumps($txn, $order_bumps) { - $i = 1; - foreach ($order_bumps as $order) { - if ($i == 1) { - $result = $this->process_single_order_bump( $order ); - } else { - try { - $result = $this->process_single_order_bump( $order, true ); - } catch (\Exception $e) { - $order->update_meta('_authorizenet_txn_error_', $e->getMessage()); + + public function set_defaults() + { + if (!isset($this->settings)) { + $this->settings = []; } - } - if ($i == count($order_bumps)) { - $mepr_order = $txn->order(); + $this->settings = (object)array_merge( + [ + 'gateway' => get_class($this), + 'id' => $this->generate_id(), + 'label' => '', + 'use_label' => true, + 'icon' => MEPR_IMAGES_URL . '/checkout/cards.png', + 'use_icon' => true, + 'desc' => __('Pay with your credit card via Authorize.net', 'memberpress'), + 'use_desc' => true, + // 'recurrence_type' => '', + 'login_name' => '', + 'transaction_key' => '', + 'signature_key' => '', + 'force_ssl' => false, + 'debug' => false, + // 'use_cron' => false, + 'test_mode' => false, + 'aimUrl' => '', + 'arbUrl' => '', + ], + (array)$this->settings + ); - if ( $mepr_order instanceof MeprOrder ) { - $mepr_order->status = \MeprOrder::$complete_str; - $mepr_order->store(); + $this->id = $this->settings->id; + $this->label = $this->settings->label; + $this->use_label = $this->settings->use_label; + $this->icon = $this->settings->icon; + $this->use_icon = $this->settings->use_icon; + $this->desc = $this->settings->desc; + $this->use_desc = $this->settings->use_desc; + // $this->recurrence_type = $this->settings->recurrence_type; + $this->hash = strtoupper(substr(md5($this->id), 0, 20)); // MD5 hashes used for Silent posts can only be 20 chars long + + if ($this->is_test_mode()) { + $this->settings->aimUrl = 'https://test.authorize.net/gateway/transact.dll'; + $this->settings->arbUrl = 'https://apitest.authorize.net/xml/v1/request.api'; + } else { + $this->settings->aimUrl = 'https://secure2.authorize.net/gateway/transact.dll'; + $this->settings->arbUrl = 'https://api2.authorize.net/xml/v1/request.api'; } - return $result; - } - $i++; - } - } - - /** Used to send data to a given payment gateway. In gateways which redirect - * before this step is necessary -- this method should just be left blank. - */ - public function process_payment($txn) { - $order_bump_product_ids = isset($_POST['mepr_order_bumps']) && is_array($_POST['mepr_order_bumps']) ? array_map('intval', $_POST['mepr_order_bumps']) : []; - $order_bump_products = MeprCheckoutCtrl::get_order_bump_products($txn->product_id, $order_bump_product_ids); - $order_bumps = $this->process_order($txn, $order_bump_products); - - if (count($order_bumps) < 1) { - return $this->process_single_payment($txn); + // An attempt to correct people who paste in spaces along with their credentials + $this->settings->login_name = trim($this->settings->login_name); + $this->settings->transaction_key = trim($this->settings->transaction_key); + $this->settings->signature_key = trim($this->settings->signature_key); } - array_unshift($order_bumps, $txn); - unset($_POST['mepr_order_bumps']); + public function listener() + { + $this->email_status('Silent Post Just Came In (' . $_SERVER['REQUEST_METHOD'] . "):\n" . MeprUtils::object_to_string($_REQUEST, true) . "\n", $this->settings->debug); + + if ($this->validate_sp_md5()) { + if (isset($_REQUEST['x_response_code']) && $_REQUEST['x_response_code'] > 1) { + return $this->record_payment_failure(); + } + // AUTHORIZE.NET HAS DEPRECATED MD5, BUT SILENT POST IS STILL AROUND + // WE'RE GOING TO USE SP TO CAPTURE FAILED PAYMENTS STILL + // else if(isset($_REQUEST['x_subscription_id']) and !empty($_REQUEST['x_subscription_id'])) { + // $sub = MeprSubscription::get_one_by_subscr_id($_REQUEST['x_subscription_id']); + // if(!$sub) { return false; } + // return $this->record_subscription_payment(); + // } + // else if(strtoupper($_REQUEST['x_type']) == 'VOID' || strtoupper($_REQUEST['x_type']) == 'CREDIT') + // return $this->record_refund(); + // Nothing applied so let's bail + return false; + } + } - return $this->process_order_bumps($txn, $order_bumps); - } + /** + * Webhook listener. Responds to select Auth.net webhook notifications. + */ + public function webhook_listener() + { + $this->email_status('Webhook Just Came In (' . $_SERVER['REQUEST_METHOD'] . "):\n" . MeprUtils::object_to_string($_REQUEST, true) . "\n", $this->settings->debug); + require_once(__DIR__ . '/MeprAuthorizeWebhooks.php'); + $webhook_handler = new MeprAuthorizeWebhooks($this->settings); + try { + $webhook_handler->process_webhook(); + } catch (Exception $e) { + MeprUtils::error_log('MeprAuthorizeGateway Webhook Error: ' . $e->getMessage()); + } + } - /** Used to send data to a given payment gateway. In gateways which redirect - * before this step is necessary -- this method should just be left blank. - */ - public function process_single_payment($txn) { - $mepr_options = MeprOptions::fetch(); + /** + * Validate the request using the MD5 hash + * + * @deprecated 1.3.49 will be removed in future release + * @see https://developer.authorize.net/support/hash_upgrade/ + */ + public function validate_sp_md5() + { + // $md5_input = $this->hash . $this->settings->login_name . $_REQUEST['x_trans_id'] . $_REQUEST['x_amount']; + // $md5 = md5($md5_input); + // return strtoupper($md5) === strtoupper($_REQUEST['x_MD5_Hash']); + // AUTHORIZE.NET HAS DEPRECATED MD5, BUT SILENT POST IS STILL AROUND + // WE'RE GOING TO USE SP TO CAPTURE FAILED PAYMENTS STILL + return true; + } - if(isset($txn) and $txn instanceof MeprTransaction) { - $usr = $txn->user(); - $prd = $txn->product(); + public function process_order_bumps($txn, $order_bumps) + { + $i = 1; + foreach ($order_bumps as $order) { + if ($i == 1) { + $result = $this->process_single_order_bump($order); + } else { + try { + $result = $this->process_single_order_bump($order, true); + } catch (\Exception $e) { + $order->update_meta('_authorizenet_txn_error_', $e->getMessage()); + } + } + + if ($i == count($order_bumps)) { + $mepr_order = $txn->order(); + + if ($mepr_order instanceof MeprOrder) { + $mepr_order->status = \MeprOrder::$complete_str; + $mepr_order->store(); + } + + return $result; + } + $i++; + } } - else - throw new MeprGatewayException( __('Payment was unsuccessful, please check your payment details and try again.', 'memberpress') ); + /** + * Used to send data to a given payment gateway. In gateways which redirect + * before this step is necessary -- this method should just be left blank. + */ + public function process_payment($txn) + { + $order_bump_product_ids = isset($_POST['mepr_order_bumps']) && is_array($_POST['mepr_order_bumps']) ? array_map('intval', $_POST['mepr_order_bumps']) : []; + $order_bump_products = MeprCheckoutCtrl::get_order_bump_products($txn->product_id, $order_bump_product_ids); + $order_bumps = $this->process_order($txn, $order_bump_products); + + if (count($order_bumps) < 1) { + return $this->process_single_payment($txn); + } - if( empty($usr->first_name) or empty($usr->last_name) ) { - $usr->first_name = sanitize_text_field(wp_unslash($_POST['mepr_first_name'])); - $usr->last_name = sanitize_text_field(wp_unslash($_POST['mepr_last_name'])); - $usr->store(); - } + array_unshift($order_bumps, $txn); + unset($_POST['mepr_order_bumps']); - $invoice = $txn->id.'-'.time(); - $args = array( 'x_card_num' => sanitize_text_field($_POST['mepr_cc_num']), - 'x_card_code' => sanitize_text_field($_POST['mepr_cvv_code']), - 'x_exp_date' => sprintf('%02d', sanitize_text_field($_POST['mepr_cc_exp_month'])) . '-' . sanitize_text_field($_POST['mepr_cc_exp_year']), - 'x_amount' => MeprUtils::format_float($txn->total), - 'x_description' => $prd->post_title, - 'x_invoice_num' => $invoice, - 'x_first_name' => $usr->first_name, - 'x_last_name' => $usr->last_name ); - - if($txn->tax_amount > 0.00) { - $args['x_tax'] = $txn->tax_desc.'<|>'.MeprUtils::format_float($txn->tax_rate, 3).'%<|>'.(string)MeprUtils::format_float($txn->tax_amount); + return $this->process_order_bumps($txn, $order_bumps); } - if($mepr_options->show_address_fields && $mepr_options->require_address_fields) { - $args = array_merge( array( 'x_address' => str_replace('&', '&', get_user_meta($usr->ID, 'mepr-address-one', true)), - 'x_city' => get_user_meta($usr->ID, 'mepr-address-city', true), - 'x_state' => get_user_meta($usr->ID, 'mepr-address-state', true), - 'x_zip' => get_user_meta($usr->ID, 'mepr-address-zip', true), - 'x_country' => get_user_meta($usr->ID, 'mepr-address-country', true) ), $args ); - } + /** + * Used to send data to a given payment gateway. In gateways which redirect + * before this step is necessary -- this method should just be left blank. + */ + public function process_single_payment($txn) + { + $mepr_options = MeprOptions::fetch(); + + if (isset($txn) and $txn instanceof MeprTransaction) { + $usr = $txn->user(); + $prd = $txn->product(); + } else { + throw new MeprGatewayException(__('Payment was unsuccessful, please check your payment details and try again.', 'memberpress')); + } - //If customer provided a new ZIP code let's add it here - if(isset($_POST['mepr_zip_post_code']) /* && !empty($_POST['mepr_zip_post_code']) */) { - $args['x_zip'] = sanitize_text_field(wp_unslash($_POST['mepr_zip_post_code'])); - } - $args = MeprHooks::apply_filters('mepr_authorize_payment_args', $args, $txn); - $res = $this->send_aim_request('AUTH_CAPTURE', $args); - $this->email_status("translated AIM response from Authorize.net: \n" . MeprUtils::object_to_string($res, true) . "\n", $this->settings->debug); - - $txn->trans_num = $res['transaction_id']; - $txn->store(); - - $_POST['x_trans_id'] = $res['transaction_id']; - $_POST['response'] = $res; - - return $this->record_payment(); - } - - /** Used to record a successful recurring payment by the given gateway. It - * should have the ability to record a successful payment or a failure. It is - * this method that should be used when receiving an IPN from PayPal or a - * Silent Post from Authorize.net. - */ - public function record_subscription_payment() { - // Make sure there's a valid subscription for this request and this payment hasn't already been recorded - if( !($sub = MeprSubscription::get_one_by_subscr_id(sanitize_text_field($_POST['x_subscription_id']))) or - MeprTransaction::get_one_by_trans_num(sanitize_text_field($_POST['x_trans_id'])) ) { - return false; - } + if (empty($usr->first_name) or empty($usr->last_name)) { + $usr->first_name = sanitize_text_field(wp_unslash($_POST['mepr_first_name'])); + $usr->last_name = sanitize_text_field(wp_unslash($_POST['mepr_last_name'])); + $usr->store(); + } - $first_txn = $sub->first_txn(); - if($first_txn == false || !($first_txn instanceof MeprTransaction)) { - $coupon_id = $sub->coupon_id; - } - else { - $coupon_id = $first_txn->coupon_id; - } + $invoice = $txn->id . '-' . time(); + $args = [ + 'x_card_num' => sanitize_text_field($_POST['mepr_cc_num']), + 'x_card_code' => sanitize_text_field($_POST['mepr_cvv_code']), + 'x_exp_date' => sprintf('%02d', sanitize_text_field($_POST['mepr_cc_exp_month'])) . '-' . sanitize_text_field($_POST['mepr_cc_exp_year']), + 'x_amount' => MeprUtils::format_float($txn->total), + 'x_description' => $prd->post_title, + 'x_invoice_num' => $invoice, + 'x_first_name' => $usr->first_name, + 'x_last_name' => $usr->last_name, + ]; + + if ($txn->tax_amount > 0.00) { + $args['x_tax'] = $txn->tax_desc . '<|>' . MeprUtils::format_float($txn->tax_rate, 3) . '%<|>' . (string)MeprUtils::format_float($txn->tax_amount); + } - $txn = new MeprTransaction(); - $txn->user_id = $sub->user_id; - $txn->product_id = $sub->product_id; - $txn->txn_type = MeprTransaction::$payment_str; - $txn->status = MeprTransaction::$complete_str; - $txn->coupon_id = $coupon_id; - $txn->trans_num = sanitize_text_field($_POST['x_trans_id']); - $txn->subscription_id = $sub->id; - $txn->gateway = $this->id; - - $txn->set_gross( sanitize_text_field($_POST['x_amount']) ); - - $txn->store(); - - $sub->status = MeprSubscription::$active_str; - $sub->cc_last4 = substr(sanitize_text_field($_POST['x_account_number']), -4); // Don't get the XXXX part of the string - //$sub->txn_count = sanitize_text_field($_POST['x_subscription_paynum']); - $sub->gateway = $this->id; - $sub->store(); - - // Not waiting for a silent post here bro ... just making it happen even - // though totalOccurrences is Already capped in record_create_subscription() - $sub->limit_payment_cycles(); - - MeprUtils::send_transaction_receipt_notices( $txn ); - if(!isset($_REQUEST['silence_expired_cc'])) { - MeprUtils::send_cc_expiration_notices( $txn ); //Silence this when a user is updating their CC, or they'll get the old card notice - } + if ($mepr_options->show_address_fields && $mepr_options->require_address_fields) { + $args = array_merge([ + 'x_address' => str_replace('&', '&', get_user_meta($usr->ID, 'mepr-address-one', true)), + 'x_city' => get_user_meta($usr->ID, 'mepr-address-city', true), + 'x_state' => get_user_meta($usr->ID, 'mepr-address-state', true), + 'x_zip' => get_user_meta($usr->ID, 'mepr-address-zip', true), + 'x_country' => get_user_meta($usr->ID, 'mepr-address-country', true), + ], $args); + } - return $txn; - } + // If customer provided a new ZIP code let's add it here + if (isset($_POST['mepr_zip_post_code']) /* && !empty($_POST['mepr_zip_post_code']) */) { + $args['x_zip'] = sanitize_text_field(wp_unslash($_POST['mepr_zip_post_code'])); + } - /** Used to record a declined payment. */ - public function record_payment_failure() { - if(isset($_POST['x_trans_id']) and !empty($_POST['x_trans_id'])) { - $txn_res = MeprTransaction::get_one_by_trans_num(sanitize_text_field($_POST['x_trans_id'])); + $args = MeprHooks::apply_filters('mepr_authorize_payment_args', $args, $txn); + $res = $this->send_aim_request('AUTH_CAPTURE', $args); + $this->email_status("translated AIM response from Authorize.net: \n" . MeprUtils::object_to_string($res, true) . "\n", $this->settings->debug); - if(is_object($txn_res) and isset($txn_res->id)) { - $txn = new MeprTransaction($txn_res->id); - $txn->status = MeprTransaction::$failed_str; + $txn->trans_num = $res['transaction_id']; $txn->store(); - } - else if( isset($_POST['x_subscription_id']) and - $sub = MeprSubscription::get_one_by_subscr_id(sanitize_text_field($_POST['x_subscription_id'])) ) { - $first_txn = $sub->first_txn(); - if($first_txn == false || !($first_txn instanceof MeprTransaction)) { - $coupon_id = $sub->coupon_id; + + $_POST['x_trans_id'] = $res['transaction_id']; + $_POST['response'] = $res; + + return $this->record_payment(); + } + + /** + * Used to record a successful recurring payment by the given gateway. It + * should have the ability to record a successful payment or a failure. It is + * this method that should be used when receiving an IPN from PayPal or a + * Silent Post from Authorize.net. + */ + public function record_subscription_payment() + { + // Make sure there's a valid subscription for this request and this payment hasn't already been recorded + if ( + !($sub = MeprSubscription::get_one_by_subscr_id(sanitize_text_field($_POST['x_subscription_id']))) or + MeprTransaction::get_one_by_trans_num(sanitize_text_field($_POST['x_trans_id'])) + ) { + return false; } - else { - $coupon_id = $first_txn->coupon_id; + + $first_txn = $sub->first_txn(); + if ($first_txn == false || !($first_txn instanceof MeprTransaction)) { + $coupon_id = $sub->coupon_id; + } else { + $coupon_id = $first_txn->coupon_id; } $txn = new MeprTransaction(); $txn->user_id = $sub->user_id; $txn->product_id = $sub->product_id; - $txn->coupon_id = $coupon_id; $txn->txn_type = MeprTransaction::$payment_str; - $txn->status = MeprTransaction::$failed_str; - $txn->subscription_id = $sub->id; + $txn->status = MeprTransaction::$complete_str; + $txn->coupon_id = $coupon_id; $txn->trans_num = sanitize_text_field($_POST['x_trans_id']); + $txn->subscription_id = $sub->id; $txn->gateway = $this->id; - $txn->set_gross( sanitize_text_field($_POST['x_amount']) ); + $txn->set_gross(sanitize_text_field($_POST['x_amount'])); $txn->store(); $sub->status = MeprSubscription::$active_str; + $sub->cc_last4 = substr(sanitize_text_field($_POST['x_account_number']), -4); // Don't get the XXXX part of the string + // $sub->txn_count = sanitize_text_field($_POST['x_subscription_paynum']); $sub->gateway = $this->id; - $sub->expire_txns(); //Expire associated transactions for the old subscription $sub->store(); - } - else - return false; // Nothing we can do here ... so we outta here - MeprUtils::send_failed_txn_notices($txn); + // Not waiting for a silent post here bro ... just making it happen even + // though totalOccurrences is Already capped in record_create_subscription() + $sub->limit_payment_cycles(); - return $txn; + MeprUtils::send_transaction_receipt_notices($txn); + if (!isset($_REQUEST['silence_expired_cc'])) { + MeprUtils::send_cc_expiration_notices($txn); // Silence this when a user is updating their CC, or they'll get the old card notice + } + + return $txn; } - return false; - } + /** + * Used to record a declined payment. + */ + public function record_payment_failure() + { + if (isset($_POST['x_trans_id']) and !empty($_POST['x_trans_id'])) { + $txn_res = MeprTransaction::get_one_by_trans_num(sanitize_text_field($_POST['x_trans_id'])); + + if (is_object($txn_res) and isset($txn_res->id)) { + $txn = new MeprTransaction($txn_res->id); + $txn->status = MeprTransaction::$failed_str; + $txn->store(); + } elseif ( + isset($_POST['x_subscription_id']) and + $sub = MeprSubscription::get_one_by_subscr_id(sanitize_text_field($_POST['x_subscription_id'])) + ) { + $first_txn = $sub->first_txn(); + if ($first_txn == false || !($first_txn instanceof MeprTransaction)) { + $coupon_id = $sub->coupon_id; + } else { + $coupon_id = $first_txn->coupon_id; + } + + $txn = new MeprTransaction(); + $txn->user_id = $sub->user_id; + $txn->product_id = $sub->product_id; + $txn->coupon_id = $coupon_id; + $txn->txn_type = MeprTransaction::$payment_str; + $txn->status = MeprTransaction::$failed_str; + $txn->subscription_id = $sub->id; + $txn->trans_num = sanitize_text_field($_POST['x_trans_id']); + $txn->gateway = $this->id; + + $txn->set_gross(sanitize_text_field($_POST['x_amount'])); + + $txn->store(); + + $sub->status = MeprSubscription::$active_str; + $sub->gateway = $this->id; + $sub->expire_txns(); // Expire associated transactions for the old subscription + $sub->store(); + } else { + return false; // Nothing we can do here ... so we outta here + } + + MeprUtils::send_failed_txn_notices($txn); + + return $txn; + } - /** Used to record a successful payment by the given gateway. It should have - * the ability to record a successful payment or a failure. It is this method - * that should be used when receiving an IPN from PayPal or a Silent Post - * from Authorize.net. - */ - public function record_payment() - { - if(isset($_POST['x_trans_id']) and !empty($_POST['x_trans_id'])) { - $obj = MeprTransaction::get_one_by_trans_num(sanitize_text_field($_POST['x_trans_id'])); + return false; + } - if(is_object($obj) and isset($obj->id)) { - $txn = new MeprTransaction(); - $txn->load_data($obj); - $usr = $txn->user(); + /** + * Used to record a successful payment by the given gateway. It should have + * the ability to record a successful payment or a failure. It is this method + * that should be used when receiving an IPN from PayPal or a Silent Post + * from Authorize.net. + */ + public function record_payment() + { + if (isset($_POST['x_trans_id']) and !empty($_POST['x_trans_id'])) { + $obj = MeprTransaction::get_one_by_trans_num(sanitize_text_field($_POST['x_trans_id'])); + + if (is_object($obj) and isset($obj->id)) { + $txn = new MeprTransaction(); + $txn->load_data($obj); + $usr = $txn->user(); + + // Just short circuit if the transaction has already completed + if ($txn->status == MeprTransaction::$complete_str) { + return; + } + + $txn->status = MeprTransaction::$complete_str; + + // This will only work before maybe_cancel_old_sub is run + $upgrade = $txn->is_upgrade(); + $downgrade = $txn->is_downgrade(); + + $event_txn = $txn->maybe_cancel_old_sub(); + $txn->store(); + + $this->email_status("record_payment: Transaction\n" . MeprUtils::object_to_string($txn->rec, true) . "\n", $this->settings->debug); + + $prd = $txn->product(); + + if ($prd->period_type == 'lifetime') { + if ($upgrade) { + $this->upgraded_sub($txn, $event_txn); + } elseif ($downgrade) { + $this->downgraded_sub($txn, $event_txn); + } else { + $this->new_sub($txn); + } + + MeprUtils::send_signup_notices($txn); + } + + MeprUtils::send_transaction_receipt_notices($txn); + if (!isset($_REQUEST['silence_expired_cc'])) { + MeprUtils::send_cc_expiration_notices($txn); // Silence this when a user is updating their CC, or they'll get the old card notice + } + + return $txn; + } + } - // Just short circuit if the transaction has already completed - if($txn->status == MeprTransaction::$complete_str) { return; } + return false; + } - $txn->status = MeprTransaction::$complete_str; + public function record_refund() + { + if (strtoupper($_REQUEST['x_type']) == 'CREDIT') { + // This is all we've got to reference the old sale in a credit + if (!isset($_POST['x_invoice_num'])) { + return false; + } + + preg_match('#^(\d+)-#', sanitize_text_field($_POST['x_invoice_num']), $m); + $txn_id = $m[1]; + $txn_res = MeprTransaction::get_one($txn_id); + } elseif (strtoupper($_REQUEST['x_type']) == 'VOID') { + $txn_res = MeprTransaction::get_one_by_trans_num(sanitize_text_field($_POST['x_trans_id'])); + } + + if (!isset($txn_res) or empty($txn_res)) { + return false; + } + + $txn = new MeprTransaction($txn_res->id); + + // Seriously ... if txn was already refunded what are we doing here? + if ($txn->status == MeprTransaction::$refunded_str) { + return $txn->id; + } + + $returned_amount = MeprUtils::format_float(sanitize_text_field($_POST['x_amount'])); + $current_amount = MeprUtils::format_float($txn->total); - // This will only work before maybe_cancel_old_sub is run - $upgrade = $txn->is_upgrade(); - $downgrade = $txn->is_downgrade(); + if (strtoupper(sanitize_text_field($_POST['x_type'])) == 'CREDIT' and $returned_amount < $current_amount) { + $txn->set_gross($amount); + $txn->status = MeprTransaction::$complete_str; + } else { + $txn->status = MeprTransaction::$refunded_str; + } - $event_txn = $txn->maybe_cancel_old_sub(); $txn->store(); - $this->email_status("record_payment: Transaction\n" . MeprUtils::object_to_string($txn->rec, true) . "\n", $this->settings->debug); + MeprUtils::send_refunded_txn_notices($txn); - $prd = $txn->product(); + return $txn->id; + } - if( $prd->period_type=='lifetime' ) { - if( $upgrade ) { - $this->upgraded_sub($txn, $event_txn); - } - else if( $downgrade ) { - $this->downgraded_sub($txn, $event_txn); - } - else { - $this->new_sub($txn); - } + public function process_refund(MeprTransaction $txn) + { + } - MeprUtils::send_signup_notices( $txn ); - } + public function process_trial_payment($txn) + { + $mepr_options = MeprOptions::fetch(); + $sub = $txn->subscription(); - MeprUtils::send_transaction_receipt_notices( $txn ); - if(!isset($_REQUEST['silence_expired_cc'])) { - MeprUtils::send_cc_expiration_notices( $txn ); //Silence this when a user is updating their CC, or they'll get the old card notice - } + // Prepare the $txn for the process_payment method + $txn->set_subtotal($sub->trial_amount + $sub->trial_tax_reversal_amount); + $txn->status = MeprTransaction::$pending_str; - return $txn; - } + // Attempt processing the payment here - the send_aim_request will throw the exceptions for us + $this->process_single_payment($txn); + + return $this->record_trial_payment($txn); } - return false; - } + public function record_trial_payment($txn) + { + $sub = $txn->subscription(); - public function record_refund() { - if(strtoupper($_REQUEST['x_type']) == 'CREDIT') { - // This is all we've got to reference the old sale in a credit - if(!isset($_POST['x_invoice_num'])) { return false; } + // Update the txn member vars and store + $txn->txn_type = MeprTransaction::$payment_str; + $txn->status = MeprTransaction::$complete_str; + $txn->expires_at = MeprUtils::ts_to_mysql_date(time() + MeprUtils::days($sub->trial_days), 'Y-m-d 23:59:59'); + $txn->store(); - preg_match('#^(\d+)-#',sanitize_text_field($_POST['x_invoice_num']),$m); - $txn_id = $m[1]; - $txn_res = MeprTransaction::get_one($txn_id); + return true; } - else if(strtoupper($_REQUEST['x_type']) == 'VOID') - $txn_res = MeprTransaction::get_one_by_trans_num(sanitize_text_field($_POST['x_trans_id'])); - if(!isset($txn_res) or empty($txn_res)) { return false; } + public function authorize_card_before_subscription($txn) + { + if (MeprHooks::apply_filters('mepr_authorize_skip_auth_charge', false, $txn)) { + return; + } + + $mepr_options = MeprOptions::fetch(); - $txn = new MeprTransaction($txn_res->id); + if (isset($txn) and $txn instanceof MeprTransaction) { + $usr = $txn->user(); + $prd = $txn->product(); + $sub = $txn->subscription(); + } else { + throw new MeprGatewayException(__('Payment was unsuccessful, please check your payment details and try again.', 'memberpress')); + } + + $invoice = $this->create_new_order_invoice($sub); + + $args = [ + 'x_card_num' => sanitize_text_field($_POST['mepr_cc_num']), + 'x_card_code' => sanitize_text_field($_POST['mepr_cvv_code']), + 'x_exp_date' => sprintf('%02d', sanitize_text_field($_POST['mepr_cc_exp_month'])) . '-' . sanitize_text_field($_POST['mepr_cc_exp_year']), + 'x_amount' => MeprUtils::format_float(MeprHooks::apply_filters('mepr_authorize_auth_only_amount', 1.00, $txn, $sub)), + 'x_description' => $prd->post_title, + 'x_invoice_num' => $invoice, + 'x_first_name' => $usr->first_name, + 'x_last_name' => $usr->last_name, + ]; + + if ($mepr_options->show_address_fields && $mepr_options->require_address_fields) { + $args = array_merge([ + 'x_address' => str_replace('&', '&', get_user_meta($usr->ID, 'mepr-address-one', true)), + 'x_city' => get_user_meta($usr->ID, 'mepr-address-city', true), + 'x_state' => get_user_meta($usr->ID, 'mepr-address-state', true), + 'x_zip' => get_user_meta($usr->ID, 'mepr-address-zip', true), + 'x_country' => get_user_meta($usr->ID, 'mepr-address-country', true), + ], $args); + } - // Seriously ... if txn was already refunded what are we doing here? - if($txn->status == MeprTransaction::$refunded_str) { return $txn->id; } + // If customer provided a new ZIP code let's add it here + if (isset($_POST['mepr_zip_post_code']) /* && !empty($_POST['mepr_zip_post_code']) */) { + $args['x_zip'] = sanitize_text_field(wp_unslash($_POST['mepr_zip_post_code'])); + } - $returned_amount = MeprUtils::format_float(sanitize_text_field($_POST['x_amount'])); - $current_amount = MeprUtils::format_float($txn->total); + $args = MeprHooks::apply_filters('mepr_authorize_auth_card_args', $args, $txn); - if(strtoupper(sanitize_text_field($_POST['x_type'])) == 'CREDIT' and $returned_amount < $current_amount ) { - $txn->set_gross( $amount ); - $txn->status = MeprTransaction::$complete_str; - } - else - $txn->status = MeprTransaction::$refunded_str; + $res = $this->send_aim_request('AUTH_ONLY', $args); - $txn->store(); + // If we made it here than the above response was successful -- otherwise an Exception would have been thrown + // Now that we know the authorization succeeded, we should void this authorization + $res2 = $this->send_aim_request('VOID', ['x_trans_id' => $res['transaction_id']]); + } - MeprUtils::send_refunded_txn_notices($txn); + public function process_create_single_subscription($txn, $check_for_trial = false) + { + $mepr_options = MeprOptions::fetch(); - return $txn->id; - } + if (isset($txn) and $txn instanceof MeprTransaction) { + $usr = $txn->user(); + $prd = $txn->product(); + $sub = $txn->subscription(); + } else { + throw new MeprGatewayException(__('Payment was unsuccessful, please check your payment details and try again.', 'memberpress')); + } - public function process_refund(MeprTransaction $txn) {} + if ($check_for_trial && $sub->trial && $sub->trial_amount > 0.00) { + $txn->set_subtotal($sub->trial_amount + $sub->trial_tax_reversal_amount); + $this->email_status("Calling process_trial_payment ...\n\n" . MeprUtils::object_to_string($txn) . "\n\n" . MeprUtils::object_to_string($sub), $this->settings->debug); + $this->process_trial_payment($txn); + } - public function process_trial_payment($txn) { - $mepr_options = MeprOptions::fetch(); - $sub = $txn->subscription(); + // Validate card first unless we have a paid trial period as that will go through AIM and validate the card immediately + if (!$sub->trial || ($sub->trial && $sub->trial_amount <= 0.00)) { + $this->authorize_card_before_subscription($txn); + } - //Prepare the $txn for the process_payment method - $txn->set_subtotal($sub->trial_amount + $sub->trial_tax_reversal_amount); - $txn->status = MeprTransaction::$pending_str; + // $invoice = $txn->id.'-'.time(); + if (empty($usr->first_name) or empty($usr->last_name)) { + $usr->first_name = sanitize_text_field(wp_unslash($_POST['mepr_first_name'])); + $usr->last_name = sanitize_text_field(wp_unslash($_POST['mepr_last_name'])); + $usr->store(); + } - //Attempt processing the payment here - the send_aim_request will throw the exceptions for us - $this->process_single_payment($txn); + // Default to 9999 for infinite occurrences + $invoice = $this->create_new_order_invoice($sub); + $total_occurrences = $sub->limit_cycles ? $sub->limit_cycles_num : 9999; + $args = [ + 'refId' => $invoice, + 'subscription' => [ + 'name' => $prd->post_title, + 'paymentSchedule' => [ + 'interval' => $this->arb_subscription_interval($sub), + // Since Authorize doesn't allow trials that have a different period_type + // from the subscription itself we have to do our trials here manually + 'startDate' => MeprUtils::get_date_from_ts((time() + (($sub->trial) ? MeprUtils::days($sub->trial_days) : 0)), 'Y-m-d'), + 'totalOccurrences' => $total_occurrences, + ], + 'amount' => MeprUtils::format_float($sub->total), // Use $sub->total here because $txn->amount may be a trial price + 'payment' => [ + 'creditCard' => [ + 'cardNumber' => sanitize_text_field($_POST['mepr_cc_num']), + 'expirationDate' => sanitize_text_field($_POST['mepr_cc_exp_month']) . '-' . sanitize_text_field($_POST['mepr_cc_exp_year']), + 'cardCode' => sanitize_text_field($_POST['mepr_cvv_code']), + ], + ], + 'order' => [ + 'invoiceNumber' => $invoice, + 'description' => $prd->post_title, + ], + 'billTo' => [ + 'firstName' => $usr->first_name, + 'lastName' => $usr->last_name, + ], + ], + ]; + + if ($mepr_options->show_address_fields && $mepr_options->require_address_fields) { + $args['subscription']['billTo'] = + array_merge( + $args['subscription']['billTo'], + [ + 'address' => str_replace('&', '&', get_user_meta($usr->ID, 'mepr-address-one', true)), + 'city' => get_user_meta($usr->ID, 'mepr-address-city', true), + 'state' => get_user_meta($usr->ID, 'mepr-address-state', true), + 'zip' => get_user_meta($usr->ID, 'mepr-address-zip', true), + 'country' => get_user_meta( + $usr->ID, + 'mepr-address-country', + true + ), + ] + ); + } - return $this->record_trial_payment($txn); - } + // If customer provided a new ZIP code let's add it here + if (isset($_POST['mepr_zip_post_code'])) { + $args['subscription']['billTo']['zip'] = sanitize_text_field(wp_unslash($_POST['mepr_zip_post_code'])); + } - public function record_trial_payment($txn) { - $sub = $txn->subscription(); + $args = MeprHooks::apply_filters('mepr_authorize_create_subscription_args', $args, $txn, $sub); - //Update the txn member vars and store - $txn->txn_type = MeprTransaction::$payment_str; - $txn->status = MeprTransaction::$complete_str; - $txn->expires_at = MeprUtils::ts_to_mysql_date(time() + MeprUtils::days($sub->trial_days), 'Y-m-d 23:59:59'); - $txn->store(); + $res = $this->send_arb_request('ARBCreateSubscriptionRequest', $args); - return true; - } + $_POST['txn_id'] = $txn->id; + $_POST['subscr_id'] = $res->subscriptionId; - public function authorize_card_before_subscription($txn) { - if(MeprHooks::apply_filters('mepr_authorize_skip_auth_charge', false, $txn)) { - return; + return $this->record_create_subscription(); } - $mepr_options = MeprOptions::fetch(); + /** + * @param MeprTransaction $order + * @param boolean $check_for_trial + */ + public function process_single_order_bump($order, $check_for_trial = false) + { + $product = $order->product(); + + if (!$order->is_payment_required()) { + MeprTransaction::create_free_transaction($order, false, sprintf('mi_%d_%s', $order->id, uniqid())); + return; + } - if(isset($txn) and $txn instanceof MeprTransaction) { - $usr = $txn->user(); - $prd = $txn->product(); - $sub = $txn->subscription(); + if ($product->is_one_time_payment()) { + return $this->process_single_payment($order); + } else { + return $this->process_create_single_subscription($order, $check_for_trial); + } } - else - throw new MeprGatewayException( __('Payment was unsuccessful, please check your payment details and try again.', 'memberpress') ); - - $invoice = $this->create_new_order_invoice($sub); - - $args = array('x_card_num' => sanitize_text_field($_POST['mepr_cc_num']), - 'x_card_code' => sanitize_text_field($_POST['mepr_cvv_code']), - 'x_exp_date' => sprintf('%02d', sanitize_text_field($_POST['mepr_cc_exp_month'])) . '-' . sanitize_text_field($_POST['mepr_cc_exp_year']), - 'x_amount' => MeprUtils::format_float(MeprHooks::apply_filters('mepr_authorize_auth_only_amount', 1.00, $txn, $sub)), - 'x_description' => $prd->post_title, - 'x_invoice_num' => $invoice, - 'x_first_name' => $usr->first_name, - 'x_last_name' => $usr->last_name); - - if($mepr_options->show_address_fields && $mepr_options->require_address_fields) { - $args = array_merge( array( 'x_address' => str_replace('&', '&', get_user_meta($usr->ID, 'mepr-address-one', true)), - 'x_city' => get_user_meta($usr->ID, 'mepr-address-city', true), - 'x_state' => get_user_meta($usr->ID, 'mepr-address-state', true), - 'x_zip' => get_user_meta($usr->ID, 'mepr-address-zip', true), - 'x_country' => get_user_meta($usr->ID, 'mepr-address-country', true) ), $args ); + + /** + * Used to send subscription data to a given payment gateway. In gateways + * which redirect before this step is necessary this method should just be + * left blank. + */ + public function process_create_subscription($txn) + { + $order_bump_product_ids = isset($_POST['mepr_order_bumps']) && is_array($_POST['mepr_order_bumps']) ? array_map('intval', $_POST['mepr_order_bumps']) : []; + $order_bump_products = MeprCheckoutCtrl::get_order_bump_products($txn->product_id, $order_bump_product_ids); + $order_bumps = $this->process_order($txn, $order_bump_products); + + if (count($order_bumps) < 1) { + return $this->process_create_single_subscription($txn); + } + + array_unshift($order_bumps, $txn); + unset($_POST['mepr_order_bumps']); + + return $this->process_order_bumps($txn, $order_bumps); } - //If customer provided a new ZIP code let's add it here - if(isset($_POST['mepr_zip_post_code']) /* && !empty($_POST['mepr_zip_post_code']) */) { - $args['x_zip'] = sanitize_text_field(wp_unslash($_POST['mepr_zip_post_code'])); + /** + * Used to record a successful subscription by the given gateway. It should have + * the ability to record a successful subscription or a failure. It is this method + * that should be used when receiving an IPN from PayPal or a Silent Post + * from Authorize.net. + */ + public function record_create_subscription() + { + $mepr_options = MeprOptions::fetch(); + + if (isset($_POST['txn_id']) and is_numeric($_POST['txn_id'])) { + $txn = new MeprTransaction((int)$_POST['txn_id']); + $sub = $txn->subscription(); + $sub->subscr_id = sanitize_text_field($_POST['subscr_id']); + $sub->status = MeprSubscription::$active_str; + $sub->created_at = gmdate('c'); + $sub->cc_last4 = substr(sanitize_text_field($_POST['mepr_cc_num']), -4); // Seriously ... only grab the last 4 digits! + $sub->cc_exp_month = sanitize_text_field($_POST['mepr_cc_exp_month']); + $sub->cc_exp_year = sanitize_text_field($_POST['mepr_cc_exp_year']); + $sub->store(); + + // This will only work before maybe_cancel_old_sub is run + $upgrade = $sub->is_upgrade(); + $downgrade = $sub->is_downgrade(); + + $event_txn = $sub->maybe_cancel_old_sub(); + + $old_total = $txn->total; // Save for later + + // If no trial or trial amount is zero then we've got to make + // sure the confirmation txn lasts through the trial + if (!$sub->trial || ($sub->trial and $sub->trial_amount <= 0.00)) { + if ($sub->trial) { + $expires_at = MeprUtils::ts_to_mysql_date(time() + MeprUtils::days($sub->trial_days), 'Y-m-d 23:59:59'); + } elseif (!$mepr_options->disable_grace_init_days && $mepr_options->grace_init_days > 0) { + $expires_at = MeprUtils::ts_to_mysql_date(time() + MeprUtils::days($mepr_options->grace_init_days), 'Y-m-d 23:59:59'); + } else { + $expires_at = $txn->created_at; // Expire immediately + } + + $txn->expires_at = $expires_at; + $txn->txn_type = MeprTransaction::$subscription_confirmation_str; + $txn->status = MeprTransaction::$confirmed_str; + $txn->trans_num = $sub->subscr_id; + $txn->set_subtotal(0.00); // This txn is just a confirmation txn ... it shouldn't have a cost + $txn->store(true); + } + + if ($upgrade) { + $this->upgraded_sub($sub, $event_txn); + } elseif ($downgrade) { + $this->downgraded_sub($sub, $event_txn); + } else { + $this->new_sub($sub, true); + } + + // Artificially set the txn amount for the notifications + // $txn->set_gross($old_total); + // This will only send if there's a new signup + MeprUtils::send_signup_notices($txn); + } } - $args = MeprHooks::apply_filters('mepr_authorize_auth_card_args', $args, $txn); + /** + * Used to cancel a subscription by the given gateway. This method should be used + * by the class to record a successful cancellation from the gateway. This method + * should also be used by any IPN requests or Silent Posts. + */ + public function process_update_subscription($sub_id) + { + $mepr_options = MeprOptions::fetch(); + + $sub = new MeprSubscription($sub_id); + if (!isset($sub->id) || (int)$sub->id <= 0) { + throw new MeprGatewayException(__('Your payment details are invalid, please check them and try again.', 'memberpress')); + } - $res = $this->send_aim_request('AUTH_ONLY', $args); + $usr = $sub->user(); + if (!isset($usr->ID) || (int)$usr->ID <= 0) { + throw new MeprGatewayException(__('Your payment details are invalid, please check them and try again.', 'memberpress')); + } - //If we made it here than the above response was successful -- otherwise an Exception would have been thrown - //Now that we know the authorization succeeded, we should void this authorization - $res2 = $this->send_aim_request('VOID', array('x_trans_id' => $res['transaction_id'])); - } + $args = [ + 'refId' => $sub->id, + 'subscriptionId' => $sub->subscr_id, + 'subscription' => [ + 'payment' => [ + 'creditCard' => [ + 'cardNumber' => sanitize_text_field($_POST['update_cc_num']), + 'expirationDate' => sanitize_text_field($_POST['update_cc_exp_month']) . '-' . sanitize_text_field($_POST['update_cc_exp_year']), + 'cardCode' => sanitize_text_field($_POST['update_cvv_code']), + ], + ], + 'billTo' => [ + 'firstName' => $usr->first_name, + 'lastName' => $usr->last_name, + ], + ], + ]; + + if ($mepr_options->show_address_fields && $mepr_options->require_address_fields) { + $args['subscription']['billTo'] = + array_merge( + $args['subscription']['billTo'], + [ + 'address' => str_replace('&', '&', get_user_meta($usr->ID, 'mepr-address-one', true)), + 'city' => get_user_meta($usr->ID, 'mepr-address-city', true), + 'state' => get_user_meta($usr->ID, 'mepr-address-state', true), + 'zip' => get_user_meta($usr->ID, 'mepr-address-zip', true), + 'country' => get_user_meta( + $usr->ID, + 'mepr-address-country', + true + ), + ] + ); + } - public function process_create_single_subscription($txn, $check_for_trial = false) { - $mepr_options = MeprOptions::fetch(); + if (isset($_POST['update_zip_post_code'])) { + $args['subscription']['billTo']['zip'] = sanitize_text_field(wp_unslash($_POST['update_zip_post_code'])); + } - if(isset($txn) and $txn instanceof MeprTransaction) { - $usr = $txn->user(); - $prd = $txn->product(); - $sub = $txn->subscription(); - } - else { - throw new MeprGatewayException( __('Payment was unsuccessful, please check your payment details and try again.', 'memberpress') ); + $args = MeprHooks::apply_filters('mepr_authorize_update_subscription_args', $args, $sub); + + $res = $this->send_arb_request('ARBUpdateSubscriptionRequest', $args); + + return $res; } - if ( $check_for_trial && $sub->trial && $sub->trial_amount > 0.00 ) { - $txn->set_subtotal( $sub->trial_amount + $sub->trial_tax_reversal_amount ); - $this->email_status( "Calling process_trial_payment ...\n\n" . MeprUtils::object_to_string( $txn ) . "\n\n" . MeprUtils::object_to_string( $sub ), $this->settings->debug ); - $this->process_trial_payment( $txn ); + /** + * This method should be used by the class to record a successful cancellation + * from the gateway. This method should also be used by any IPN requests or + * Silent Posts. + */ + public function record_update_subscription() + { + // I don't think we need to do anything here } - //Validate card first unless we have a paid trial period as that will go through AIM and validate the card immediately - if(!$sub->trial || ($sub->trial && $sub->trial_amount <= 0.00)) { - $this->authorize_card_before_subscription($txn); + /** + * Used to suspend a subscription by the given gateway. + */ + public function process_suspend_subscription($sub_id) + { } - //$invoice = $txn->id.'-'.time(); + /** + * This method should be used by the class to record a successful suspension + * from the gateway. + */ + public function record_suspend_subscription() + { + } - if( empty($usr->first_name) or empty($usr->last_name) ) { - $usr->first_name = sanitize_text_field(wp_unslash($_POST['mepr_first_name'])); - $usr->last_name = sanitize_text_field(wp_unslash($_POST['mepr_last_name'])); - $usr->store(); + /** + * Used to suspend a subscription by the given gateway. + */ + public function process_resume_subscription($sub_id) + { } - // Default to 9999 for infinite occurrences - $invoice = $this->create_new_order_invoice($sub); - $total_occurrences = $sub->limit_cycles ? $sub->limit_cycles_num : 9999; - $args = array( "refId" => $invoice, - "subscription" => array( - "name" => $prd->post_title, - "paymentSchedule" => array( - "interval" => $this->arb_subscription_interval($sub), - // Since Authorize doesn't allow trials that have a different period_type - // from the subscription itself we have to do our trials here manually - "startDate" => MeprUtils::get_date_from_ts((time() + (($sub->trial)?MeprUtils::days($sub->trial_days):0)), 'Y-m-d'), - "totalOccurrences" => $total_occurrences, - ), - "amount" => MeprUtils::format_float($sub->total), //Use $sub->total here because $txn->amount may be a trial price - "payment" => array( - "creditCard" => array( - "cardNumber" => sanitize_text_field($_POST['mepr_cc_num']), - "expirationDate" => sanitize_text_field($_POST['mepr_cc_exp_month']) . '-' . sanitize_text_field($_POST['mepr_cc_exp_year']), - "cardCode" => sanitize_text_field($_POST['mepr_cvv_code']) - ) - ), - "order" => array( - "invoiceNumber" => $invoice, - "description" => $prd->post_title - ), - "billTo" => array( - "firstName" => $usr->first_name, - "lastName" => $usr->last_name - ) - ) - ); - - if($mepr_options->show_address_fields && $mepr_options->require_address_fields) { - $args['subscription']['billTo'] = - array_merge($args['subscription']['billTo'], - array("address" => str_replace('&', '&', get_user_meta($usr->ID, 'mepr-address-one', true)), - "city" => get_user_meta($usr->ID, 'mepr-address-city', true), - "state" => get_user_meta($usr->ID, 'mepr-address-state', true), - "zip" => get_user_meta($usr->ID, 'mepr-address-zip', true), - "country" => get_user_meta($usr->ID, 'mepr-address-country', true))); + /** + * This method should be used by the class to record a successful resuming of + * as subscription from the gateway. + */ + public function record_resume_subscription() + { } - //If customer provided a new ZIP code let's add it here - if(isset($_POST['mepr_zip_post_code'])) { - $args['subscription']['billTo']['zip'] = sanitize_text_field(wp_unslash($_POST['mepr_zip_post_code'])); + /** + * Used to cancel a subscription by the given gateway. This method should be used + * by the class to record a successful cancellation from the gateway. This method + * should also be used by any IPN requests or Silent Posts. + */ + public function process_cancel_subscription($sub_id) + { + $sub = new MeprSubscription($sub_id); + + if (!isset($sub->id) || (int)$sub->id <= 0) { + throw new MeprGatewayException(__('This subscription is invalid.', 'memberpress')); + } + + // Should already expire naturally at authorize.net so we have no need + // to do this when we're "cancelling" because of a natural expiration + if (!isset($_REQUEST['expire'])) { + $args = [ + 'refId' => $sub->id, + 'subscriptionId' => $sub->subscr_id, + ]; + $args = MeprHooks::apply_filters('mepr_authorize_cancel_subscription_args', $args, $sub); + $res = $this->send_arb_request('ARBCancelSubscriptionRequest', $args); + } + + $_POST['subscr_ID'] = $sub->id; + return $this->record_cancel_subscription(); } - $args = MeprHooks::apply_filters('mepr_authorize_create_subscription_args', $args, $txn, $sub); + /** + * This method should be used by the class to record a successful cancellation + * from the gateway. This method should also be used by any IPN requests or + * Silent Posts. + */ + public function record_cancel_subscription() + { + $subscr_ID = (isset($_POST['subscr_ID'])) ? (int)$_POST['subscr_ID'] : null; + $sub = new MeprSubscription($subscr_ID); + + if (!isset($sub->id) || $sub->id <= 0) { + return false; + } - $res = $this->send_arb_request('ARBCreateSubscriptionRequest', $args); + // Seriously ... if sub was already cancelled what are we doing here? + if ($sub->status == MeprSubscription::$cancelled_str) { + return true; + } - $_POST['txn_id'] = $txn->id; - $_POST['subscr_id'] = $res->subscriptionId; + $sub->status = MeprSubscription::$cancelled_str; + $sub->store(); - return $this->record_create_subscription(); - } + if (isset($_REQUEST['expire'])) { + $sub->limit_reached_actions(); + } - /** - * @param MeprTransaction $order - * @param boolean $check_for_trial - */ - public function process_single_order_bump($order, $check_for_trial = false) - { - $product = $order->product(); + if (!isset($_REQUEST['silent']) || ($_REQUEST['silent'] == false)) { + MeprUtils::send_cancelled_sub_notices($sub); + } - if(!$order->is_payment_required()) { - MeprTransaction::create_free_transaction($order, false, sprintf('mi_%d_%s', $order->id, uniqid())); - return; + return true; } - if($product->is_one_time_payment()) { - return $this->process_single_payment($order); - } else { - return $this->process_create_single_subscription($order, $check_for_trial); - } - } - - /** Used to send subscription data to a given payment gateway. In gateways - * which redirect before this step is necessary this method should just be - * left blank. - */ - public function process_create_subscription($txn) { - $order_bump_product_ids = isset($_POST['mepr_order_bumps']) && is_array($_POST['mepr_order_bumps']) ? array_map('intval', $_POST['mepr_order_bumps']) : []; - $order_bump_products = MeprCheckoutCtrl::get_order_bump_products($txn->product_id, $order_bump_product_ids); - $order_bumps = $this->process_order($txn, $order_bump_products); - - if (count($order_bumps) < 1) { - return $this->process_create_single_subscription($txn); + /** + * This gets called on the 'init' hook when the signup form is processed ... + * this is in place so that payment solutions like paypal can redirect + * before any content is rendered. + */ + public function process_signup_form($txn) + { + // if($txn->amount <= 0.00) { + // MeprTransaction::create_free_transaction($txn); + // return; + // } } - array_unshift($order_bumps, $txn); - unset($_POST['mepr_order_bumps']); - - return $this->process_order_bumps($txn, $order_bumps); - } - - /** Used to record a successful subscription by the given gateway. It should have - * the ability to record a successful subscription or a failure. It is this method - * that should be used when receiving an IPN from PayPal or a Silent Post - * from Authorize.net. - */ - public function record_create_subscription() { - $mepr_options = MeprOptions::fetch(); - - if(isset($_POST['txn_id']) and is_numeric($_POST['txn_id'])) { - $txn = new MeprTransaction((int)$_POST['txn_id']); - $sub = $txn->subscription(); - $sub->subscr_id = sanitize_text_field($_POST['subscr_id']); - $sub->status = MeprSubscription::$active_str; - $sub->created_at = gmdate('c'); - $sub->cc_last4 = substr(sanitize_text_field($_POST['mepr_cc_num']),-4); // Seriously ... only grab the last 4 digits! - $sub->cc_exp_month = sanitize_text_field($_POST['mepr_cc_exp_month']); - $sub->cc_exp_year = sanitize_text_field($_POST['mepr_cc_exp_year']); - $sub->store(); - - // This will only work before maybe_cancel_old_sub is run - $upgrade = $sub->is_upgrade(); - $downgrade = $sub->is_downgrade(); - - $event_txn = $sub->maybe_cancel_old_sub(); - - $old_total = $txn->total; // Save for later - - // If no trial or trial amount is zero then we've got to make - // sure the confirmation txn lasts through the trial - if(!$sub->trial || ($sub->trial and $sub->trial_amount <= 0.00)) { - if($sub->trial) { - $expires_at = MeprUtils::ts_to_mysql_date(time() + MeprUtils::days($sub->trial_days), 'Y-m-d 23:59:59'); - } - elseif(!$mepr_options->disable_grace_init_days && $mepr_options->grace_init_days > 0) { - $expires_at = MeprUtils::ts_to_mysql_date(time() + MeprUtils::days($mepr_options->grace_init_days), 'Y-m-d 23:59:59'); - } - else { - $expires_at = $txn->created_at; // Expire immediately - } - - $txn->expires_at = $expires_at; - $txn->txn_type = MeprTransaction::$subscription_confirmation_str; - $txn->status = MeprTransaction::$confirmed_str; - $txn->trans_num = $sub->subscr_id; - $txn->set_subtotal(0.00); // This txn is just a confirmation txn ... it shouldn't have a cost - $txn->store(true); - } - - if($upgrade) { - $this->upgraded_sub($sub, $event_txn); - } - elseif($downgrade) { - $this->downgraded_sub($sub, $event_txn); - } - else { - $this->new_sub($sub, true); - } - - // Artificially set the txn amount for the notifications - // $txn->set_gross($old_total); - - /// This will only send if there's a new signup - MeprUtils::send_signup_notices($txn); - } - } - - /** Used to cancel a subscription by the given gateway. This method should be used - * by the class to record a successful cancellation from the gateway. This method - * should also be used by any IPN requests or Silent Posts. - */ - public function process_update_subscription($sub_id) { - $mepr_options = MeprOptions::fetch(); - - $sub = new MeprSubscription($sub_id); - if(!isset($sub->id) || (int)$sub->id <= 0) - throw new MeprGatewayException( __('Your payment details are invalid, please check them and try again.', 'memberpress') ); - - $usr = $sub->user(); - if(!isset($usr->ID) || (int)$usr->ID <= 0) - throw new MeprGatewayException( __('Your payment details are invalid, please check them and try again.', 'memberpress') ); - - $args = array( "refId" => $sub->id, - "subscriptionId" => $sub->subscr_id, - "subscription" => array( - "payment" => array( - "creditCard" => array( - "cardNumber" => sanitize_text_field($_POST['update_cc_num']), - "expirationDate" => sanitize_text_field($_POST['update_cc_exp_month']) . '-' . sanitize_text_field($_POST['update_cc_exp_year']), - "cardCode" => sanitize_text_field($_POST['update_cvv_code']) - ) - ), - "billTo" => array( - "firstName" => $usr->first_name, - "lastName" => $usr->last_name - ) - ) - ); - - if($mepr_options->show_address_fields && $mepr_options->require_address_fields) { - $args['subscription']['billTo'] = - array_merge($args['subscription']['billTo'], - array("address" => str_replace('&', '&', get_user_meta($usr->ID, 'mepr-address-one', true)), - "city" => get_user_meta($usr->ID, 'mepr-address-city', true), - "state" => get_user_meta($usr->ID, 'mepr-address-state', true), - "zip" => get_user_meta($usr->ID, 'mepr-address-zip', true), - "country" => get_user_meta($usr->ID, 'mepr-address-country', true))); + public function display_payment_page($txn) + { + // Nothing here yet } - if(isset($_POST['update_zip_post_code'])) { - $args['subscription']['billTo']['zip'] = sanitize_text_field(wp_unslash($_POST['update_zip_post_code'])); + /** + * This gets called on wp_enqueue_script and enqueues a set of + * scripts for use on the page containing the payment form + */ + public function enqueue_payment_form_scripts() + { + wp_enqueue_script('mepr-gateway-checkout', MEPR_JS_URL . '/gateway/checkout.js', ['mepr-checkout-js'], MEPR_VERSION); } - $args = MeprHooks::apply_filters('mepr_authorize_update_subscription_args', $args, $sub); - - $res = $this->send_arb_request('ARBUpdateSubscriptionRequest', $args); - - return $res; - } - - /** This method should be used by the class to record a successful cancellation - * from the gateway. This method should also be used by any IPN requests or - * Silent Posts. - */ - public function record_update_subscription() { - // I don't think we need to do anything here - } - - /** Used to suspend a subscription by the given gateway. - */ - public function process_suspend_subscription($sub_id) {} - - /** This method should be used by the class to record a successful suspension - * from the gateway. - */ - public function record_suspend_subscription() {} - - /** Used to suspend a subscription by the given gateway. - */ - public function process_resume_subscription($sub_id) {} - - /** This method should be used by the class to record a successful resuming of - * as subscription from the gateway. - */ - public function record_resume_subscription() {} - - /** Used to cancel a subscription by the given gateway. This method should be used - * by the class to record a successful cancellation from the gateway. This method - * should also be used by any IPN requests or Silent Posts. - */ - public function process_cancel_subscription($sub_id) { - $sub = new MeprSubscription($sub_id); - - if(!isset($sub->id) || (int)$sub->id <= 0) - throw new MeprGatewayException( __('This subscription is invalid.', 'memberpress') ); - - // Should already expire naturally at authorize.net so we have no need - // to do this when we're "cancelling" because of a natural expiration - if(!isset($_REQUEST['expire'])) { - $args = array( "refId" => $sub->id, "subscriptionId" => $sub->subscr_id ); - $args = MeprHooks::apply_filters('mepr_authorize_cancel_subscription_args', $args, $sub); - $res = $this->send_arb_request('ARBCancelSubscriptionRequest', $args); + /** + * This gets called on wp_enqueue_script and enqueues a set of + * scripts for use on the front end user account page. + * Can be overridden if custom scripts are necessary. + */ + public function enqueue_user_account_scripts() + { + if ( + MeprUtils::valid_url_param('action', 'update', 'GET') && // (routing) Are we on the update credit card page? + MeprUtils::valid_url_param('sub', null, 'GET') && // (routing) Do we have a sub url parameter? + MeprSubscription::exists((int)$_GET['sub']) + ) { // Does the subscription exist? + $sub = new MeprSubscription((int)$_GET['sub']); + + // Ensure that the gateway associated with the subscription we're updating is for Authorize.net + if ($sub->gateway == $this->id) { + wp_enqueue_script('mepr-default-gateway-checkout-js'); + } + } } - $_POST['subscr_ID'] = $sub->id; - return $this->record_cancel_subscription(); - } - - /** This method should be used by the class to record a successful cancellation - * from the gateway. This method should also be used by any IPN requests or - * Silent Posts. - */ - public function record_cancel_subscription() { - $subscr_ID = (isset($_POST['subscr_ID'])) ? (int)$_POST['subscr_ID'] : null; - $sub = new MeprSubscription($subscr_ID); - - if(!isset($sub->id) || $sub->id <= 0) { return false; } - - // Seriously ... if sub was already cancelled what are we doing here? - if($sub->status == MeprSubscription::$cancelled_str) { return true; } - - $sub->status = MeprSubscription::$cancelled_str; - $sub->store(); - - if(isset($_REQUEST['expire'])) - $sub->limit_reached_actions(); - - if(!isset($_REQUEST['silent']) || ($_REQUEST['silent']==false)) - MeprUtils::send_cancelled_sub_notices($sub); - - return true; - } - - /** This gets called on the 'init' hook when the signup form is processed ... - * this is in place so that payment solutions like paypal can redirect - * before any content is rendered. - */ - public function process_signup_form($txn) { - //if($txn->amount <= 0.00) { - // MeprTransaction::create_free_transaction($txn); - // return; - //} - } - - public function display_payment_page($txn) { - // Nothing here yet - } - - /** This gets called on wp_enqueue_script and enqueues a set of - * scripts for use on the page containing the payment form - */ - public function enqueue_payment_form_scripts() { - wp_enqueue_script('mepr-gateway-checkout', MEPR_JS_URL . '/gateway/checkout.js', array('mepr-checkout-js'), MEPR_VERSION); - } - - /** This gets called on wp_enqueue_script and enqueues a set of - * scripts for use on the front end user account page. - * Can be overridden if custom scripts are necessary. - */ - public function enqueue_user_account_scripts() { - if( MeprUtils::valid_url_param('action','update','GET') && // (routing) Are we on the update credit card page? - MeprUtils::valid_url_param('sub', null, 'GET') && // (routing) Do we have a sub url parameter? - MeprSubscription::exists((int)$_GET['sub']) ) { // Does the subscription exist? - $sub = new MeprSubscription((int)$_GET['sub']); - - // Ensure that the gateway associated with the subscription we're updating is for Authorize.net - if($sub->gateway == $this->id) { - wp_enqueue_script('mepr-default-gateway-checkout-js'); - } - } - } - - /** - * Returs the payment for and required fields for the gateway - */ - public function spc_payment_fields() { - $payment_method = $this; - $payment_form_action = 'mepr-authorize-net-payment-form'; - $txn = new MeprTransaction; //FIXME: This is simply for the action mepr-authorize-net-payment-form - return MeprView::get_string("/checkout/payment_form", get_defined_vars()); - } - - /** This spits out html for the payment form on the registration / payment - * page for the user to fill out for payment. If we're using an offsite - * payment solution like PayPal then this method will just redirect to it. - */ - public function display_payment_form($amount, $usr, $product_id, $txn_id) { - $prd = new MeprProduct($product_id); - $order_bump_product_ids = isset($_REQUEST['obs']) && is_array($_REQUEST['obs']) ? array_map('intval', $_REQUEST['obs']) : []; - $coupon = false; - $mepr_options = MeprOptions::fetch(); - - $txn = new MeprTransaction($txn_id); - $usr = $txn->user(); - $errors = isset( $_POST['errors'] ) ? $_POST['errors'] : array(); - - //Artifically set the price of the $prd in case a coupon was used - if($prd->price != $amount) { - $coupon = true; - $prd->price = $amount; + /** + * Returs the payment for and required fields for the gateway + */ + public function spc_payment_fields() + { + $payment_method = $this; + $payment_form_action = 'mepr-authorize-net-payment-form'; + $txn = new MeprTransaction(); // FIXME: This is simply for the action mepr-authorize-net-payment-form + return MeprView::get_string('/checkout/payment_form', get_defined_vars()); } - $order_bumps = []; + /** + * This spits out html for the payment form on the registration / payment + * page for the user to fill out for payment. If we're using an offsite + * payment solution like PayPal then this method will just redirect to it. + */ + public function display_payment_form($amount, $usr, $product_id, $txn_id) + { + $prd = new MeprProduct($product_id); + $order_bump_product_ids = isset($_REQUEST['obs']) && is_array($_REQUEST['obs']) ? array_map('intval', $_REQUEST['obs']) : []; + $coupon = false; + $mepr_options = MeprOptions::fetch(); + + $txn = new MeprTransaction($txn_id); + $usr = $txn->user(); + $errors = isset($_POST['errors']) ? $_POST['errors'] : []; - try { - $order_bump_product_ids = isset($_GET['obs']) && is_array($_GET['obs']) ? array_map('intval', $_GET['obs']) : []; - $order_bump_products = MeprCheckoutCtrl::get_order_bump_products($txn->product_id, $order_bump_product_ids); + // Artifically set the price of the $prd in case a coupon was used + if ($prd->price != $amount) { + $coupon = true; + $prd->price = $amount; + } - foreach($order_bump_products as $product) { - list($transaction, $subscription) = MeprCheckoutCtrl::prepare_transaction( - $product, - 0, - get_current_user_id(), - 'manual', - false, - false - ); + $order_bumps = []; - $order_bumps[] = [$product, $transaction, $subscription]; - } - } - catch(Exception $e) { - // ignore exception - } + try { + $order_bump_product_ids = isset($_GET['obs']) && is_array($_GET['obs']) ? array_map('intval', $_GET['obs']) : []; + $order_bump_products = MeprCheckoutCtrl::get_order_bump_products($txn->product_id, $order_bump_product_ids); + + foreach ($order_bump_products as $product) { + list($transaction, $subscription) = MeprCheckoutCtrl::prepare_transaction( + $product, + 0, + get_current_user_id(), + 'manual', + false, + false + ); + + $order_bumps[] = [$product, $transaction, $subscription]; + } + } catch (Exception $e) { + // ignore exception + } - if(count($order_bumps)) { - echo MeprTransactionsHelper::get_invoice_order_bumps($txn, '', $order_bumps); - } - else { - echo MeprTransactionsHelper::get_invoice($txn); - } - ?> + if (count($order_bumps)) { + echo MeprTransactionsHelper::get_invoice_order_bumps($txn, '', $order_bumps); + } else { + echo MeprTransactionsHelper::get_invoice($txn); + } + ?>
    - +
    - first_name) or empty($usr->last_name) ): ?> + first_name) or empty($usr->last_name)) : ?>
    @@ -953,12 +1052,12 @@ public function display_payment_form($amount, $usr, $product_id, $txn_id) {
    - +
    - +
    @@ -987,13 +1086,13 @@ public function display_payment_form($amount, $usr, $product_id, $txn_id) { - - + foreach ($order_bump_product_ids as $orderId) { + ?> + + -
    - - - -
    -
    - - -
    - - - - -
    - -
    -
    - - + + + + + + +
    + +
    + +
    - -
    + + -
    - -
    - -
    + public function validate_payment_form($errors) + { + // TODO: Implement validate_payment_form() method. + } + + public function validate_options_form($errors) + { + // TODO: Implement validate_options_form() method. + } + + + /** + * Used to cancel a subscription by the given gateway. This method should be used + * by the class to record a successful cancellation from the gateway. This method + * should also be used by any IPN requests or Silent Posts. + */ + public function process_update_subscription($sub_id) + { + $mepr_options = MeprOptions::fetch(); + + $sub = new MeprSubscription($sub_id); + if (!isset($sub->id) || (int) $sub->id <= 0) { + throw new MeprGatewayException(__('Your payment details are invalid, please check them and try again.', 'memberpress')); + } + + $usr = $sub->user(); + if (!isset($usr->ID) || (int) $usr->ID <= 0) { + throw new MeprGatewayException(__('Your payment details are invalid, please check them and try again.', 'memberpress')); + } + + $args = [ + 'refId' => $sub->id, + 'subscriptionId' => $sub->subscr_id, + 'subscription' => [ + 'payment' => [ + 'creditCard' => [ + 'cardNumber' => sanitize_text_field($_POST['update_cc_num']), + 'expirationDate' => sanitize_text_field($_POST['update_cc_exp_month']) . '-' . sanitize_text_field($_POST['update_cc_exp_year']), + 'cardCode' => sanitize_text_field($_POST['update_cvv_code']), + ], + ], + ], + ]; + + if (!empty($usr->first_name) && !empty($usr->last_name)) { + $args['subscription']['billTo'] = [ + 'firstName' => $usr->first_name, + 'lastName' => $usr->last_name, + ]; + } + + if ($mepr_options->show_address_fields && $mepr_options->require_address_fields && isset($args['subscription']['billTo'])) { + $args['subscription']['billTo'] = + array_merge( + $args['subscription']['billTo'], + [ + 'address' => str_replace('&', '&', get_user_meta($usr->ID, 'mepr-address-one', true)), + 'city' => get_user_meta($usr->ID, 'mepr-address-city', true), + 'state' => get_user_meta($usr->ID, 'mepr-address-state', true), + 'zip' => get_user_meta($usr->ID, 'mepr-address-zip', true), + 'country' => get_user_meta( + $usr->ID, + 'mepr-address-country', + true + ), + ] + ); + } + + if (isset($_POST['update_zip_post_code'])) { + $args['subscription']['billTo']['zip'] = sanitize_text_field(wp_unslash($_POST['update_zip_post_code'])); + } + + $args = MeprHooks::apply_filters('mepr_authorize_update_subscription_args', $args, $sub); + + $res = $this->getHttpClient()->updateSubscription($args); + + return $res; + } + + /** + * Displays the update account form on the subscription account page + **/ + public function display_update_account_form($sub_id, $errors = [], $message = '') + { + $sub = new MeprSubscription($sub_id); + + $last4 = isset($_POST['update_cc_num']) ? substr(sanitize_text_field($_POST['update_cc_num']), -4) : $sub->cc_last4; + $exp_month = isset($_POST['update_cc_exp_month']) ? sanitize_text_field($_POST['update_cc_exp_month']) : $sub->cc_exp_month; + $exp_year = isset($_POST['update_cc_exp_year']) ? sanitize_text_field($_POST['update_cc_exp_year']) : $sub->cc_exp_year; + + // Only include the full cc number if there are errors + if (strtolower($_SERVER['REQUEST_METHOD']) == 'post' and empty($errors)) { + $sub->cc_last4 = $last4; + $sub->cc_exp_month = $exp_month; + $sub->cc_exp_year = $exp_year; + $sub->store(); + + unset($_POST['update_cvv_code']); // Unset this for security + } else { // If there are errors then show the full cc num ... if it's there + $last4 = isset($_POST['update_cc_num']) ? sanitize_text_field($_POST['update_cc_num']) : $sub->cc_last4; + } + + $ccv_code = (isset($_POST['update_cvv_code'])) ? sanitize_text_field($_POST['update_cvv_code']) : ''; + $exp = sprintf('%02d', $exp_month) . " / {$exp_year}"; + + ?> +
    +
    + + + +
     
    + + + + +
    + is_credit_card_valid($_POST['update_cc_num'])) { + $errors[] = __('Your credit card number is invalid.', 'memberpress'); + } + + if (!isset($_POST['update_cvv_code']) || empty($_POST['update_cvv_code'])) { + $errors[] = __('You must enter your CVV code.', 'memberpress'); + } + + return $errors; + } + + /** + * Actually pushes the account update to the payment processor + */ + public function process_update_account_form($sub_id) + { + return $this->process_update_subscription($sub_id); + } + + public function is_test_mode() + { + return $this->settings->test_mode; + } + + public function force_ssl() + { + return false; + } + + /** + * Returs the payment for and required fields for the gateway + */ + public function spc_payment_fields() + { + $payment_method = $this; + $public_key = $this->get_public_key(); + $is_test = $this->is_test_mode(); + $login_id = $this->settings->login_name; + $payment_form_action = 'mepr-authorize-net-payment-form'; + $txn = new MeprTransaction(); // FIXME: This is simply for the action mepr-authorize-net-payment-form + + return MeprView::get_string('/checkout/MeprAuthorizeProfileGateway/payment_gateway_fields', get_defined_vars()); + } -
     
    - - - - - -
    - is_credit_card_valid($_POST['update_cc_num'])) - $errors[] = __('Your credit card number is invalid.', 'memberpress'); - - if(!isset($_POST['update_cvv_code']) || empty($_POST['update_cvv_code'])) - $errors[] = __('You must enter your CVV code.', 'memberpress'); - - return $errors; - } - - /** Actually pushes the account update to the payment processor */ - public function process_update_account_form($sub_id) { - return $this->process_update_subscription($sub_id); - } - - public function is_test_mode() { - return $this->settings->test_mode; - } - - public function force_ssl() { - return false; - } - - /** - * Returs the payment for and required fields for the gateway - */ - public function spc_payment_fields() { - $payment_method = $this; - $public_key = $this->get_public_key(); - $is_test = $this->is_test_mode(); - $login_id = $this->settings->login_name; - $payment_form_action = 'mepr-authorize-net-payment-form'; - $txn = new MeprTransaction; //FIXME: This is simply for the action mepr-authorize-net-payment-form - - return MeprView::get_string( "/checkout/MeprAuthorizeProfileGateway/payment_gateway_fields", get_defined_vars() ); - } - - public function process_suspend_subscription( $subscription_id ) { - // TODO: Implement process_suspend_subscription() method. - } - - public function record_suspend_subscription() { - // TODO: Implement record_suspend_subscription() method. - } - - public function process_resume_subscription( $subscription_id ) { - // TODO: Implement process_resume_subscription() method. - } - - public function record_resume_subscription() { - // TODO: Implement record_resume_subscription() method. - } - - public function process_payment( $transaction ) { - $this->process_signup_form( $transaction ); - } + public function process_suspend_subscription($subscription_id) + { + // TODO: Implement process_suspend_subscription() method. + } + + public function record_suspend_subscription() + { + // TODO: Implement record_suspend_subscription() method. + } + + public function process_resume_subscription($subscription_id) + { + // TODO: Implement process_resume_subscription() method. + } + + public function record_resume_subscription() + { + // TODO: Implement record_resume_subscription() method. + } + + public function process_payment($transaction) + { + $this->process_signup_form($transaction); + } } diff --git a/app/gateways/MeprAuthorizeWebhooks.php b/app/gateways/MeprAuthorizeWebhooks.php index b941b5f..d76705c 100644 --- a/app/gateways/MeprAuthorizeWebhooks.php +++ b/app/gateways/MeprAuthorizeWebhooks.php @@ -1,247 +1,263 @@ gateway_settings = $gateway_settings; - // This allows me to pass in a mock API for tests. - $this->authorize_api = isset($authorize_api) ? $authorize_api : new MeprAuthorizeAPI($gateway_settings); - } - - /** - * Validate and process select Authorize.net webhooks - * @throws MeprGatewayException - * @return object|false MeprTransaction or false - */ - public function process_webhook() { - $request_body = $this->get_input_stream(); - MeprUtils::debug_log('Authorize.net Webhook Received ' . $request_body); - if($this->validate_webhook($request_body)) { - MeprUtils::debug_log('Authorize.net Validate Webhook Passed'); - $request_json = json_decode($request_body); - if($request_json && preg_match('/^net.authorize.payment/', $request_json->eventType)) { - MeprUtils::debug_log('Authorize.net Valid eventType'); - $auth_transaction = $this->authorize_api->get_transaction_details($request_json->payload->id); - if($auth_transaction && $auth_transaction !== '') { - MeprUtils::debug_log('Authorize.net auth_transaction: ' . MeprUtils::object_to_string($auth_transaction)); - switch($request_json->eventType) { - case 'net.authorize.payment.authcapture.created': - case 'net.authorize.payment.capture.created': - case 'net.authorize.payment.fraud.approved': - if($request_json->payload->responseCode > 1) { - return $this->record_payment_failure($auth_transaction->transaction); - } - else { - return $this->record_subscription_payment($auth_transaction->transaction); - } - case 'net.authorize.payment.refund.created': - return $this->record_refund($auth_transaction->transaction); - case 'net.authorize.payment.fraud.declined': - return $this->record_payment_failure($auth_transaction->transaction); - default: - MeprUtils::debug_log('Authorize.net Webhook not processed: ' . $request_json->eventType); - } - } - else { - // Transaction details are null - throw new MeprGatewayException(__('MeprAuthorizeAPI Error: Unable to retrieve transaction details. Check your logs for errors.', 'memberpress')); - } - } - elseif($request_json && $request_json->eventType == 'net.authorize.customer.subscription.failed') { - MeprUtils::debug_log('Received net.authorize.customer.subscription.failed eventType'); - $auth_transaction = $this->authorize_api->get_transaction_details($request_json->payload->transactionDetails->transId); - - if(is_object($auth_transaction)) { - MeprUtils::debug_log('Authorize.net auth_transaction: ' . MeprUtils::object_to_string($auth_transaction)); - return $this->record_payment_failure($auth_transaction->transaction); - } - } +class MeprAuthorizeWebhooks +{ + private $gateway_settings; + /** + * @var MeprAuthorizeAPI|MeprArtificialAuthorizeNetProfileHttpClient $authorize_api + */ + private $authorize_api; + + public function __construct($gateway_settings, $authorize_api = null) + { + $this->gateway_settings = $gateway_settings; + // This allows me to pass in a mock API for tests. + $this->authorize_api = isset($authorize_api) ? $authorize_api : new MeprAuthorizeAPI($gateway_settings); } - else { - throw new MeprGatewayException(__('This is not a valid Webhook! Check your settings.', 'memberpress')); + + /** + * Validate and process select Authorize.net webhooks + * + * @throws MeprGatewayException + * @return object|false MeprTransaction or false + */ + public function process_webhook() + { + $request_body = $this->get_input_stream(); + MeprUtils::debug_log('Authorize.net Webhook Received ' . $request_body); + if ($this->validate_webhook($request_body)) { + MeprUtils::debug_log('Authorize.net Validate Webhook Passed'); + $request_json = json_decode($request_body); + if ($request_json && preg_match('/^net.authorize.payment/', $request_json->eventType)) { + MeprUtils::debug_log('Authorize.net Valid eventType'); + $auth_transaction = $this->authorize_api->get_transaction_details($request_json->payload->id); + if ($auth_transaction && $auth_transaction !== '') { + MeprUtils::debug_log('Authorize.net auth_transaction: ' . MeprUtils::object_to_string($auth_transaction)); + switch ($request_json->eventType) { + case 'net.authorize.payment.authcapture.created': + case 'net.authorize.payment.capture.created': + case 'net.authorize.payment.fraud.approved': + if ($request_json->payload->responseCode > 1) { + return $this->record_payment_failure($auth_transaction->transaction); + } else { + return $this->record_subscription_payment($auth_transaction->transaction); + } + case 'net.authorize.payment.refund.created': + return $this->record_refund($auth_transaction->transaction); + case 'net.authorize.payment.fraud.declined': + return $this->record_payment_failure($auth_transaction->transaction); + default: + MeprUtils::debug_log('Authorize.net Webhook not processed: ' . $request_json->eventType); + } + } else { + // Transaction details are null + throw new MeprGatewayException(__('MeprAuthorizeAPI Error: Unable to retrieve transaction details. Check your logs for errors.', 'memberpress')); + } + } elseif ($request_json && $request_json->eventType == 'net.authorize.customer.subscription.failed') { + MeprUtils::debug_log('Received net.authorize.customer.subscription.failed eventType'); + $auth_transaction = $this->authorize_api->get_transaction_details($request_json->payload->transactionDetails->transId); + + if (is_object($auth_transaction)) { + MeprUtils::debug_log('Authorize.net auth_transaction: ' . MeprUtils::object_to_string($auth_transaction)); + return $this->record_payment_failure($auth_transaction->transaction); + } + } + } else { + throw new MeprGatewayException(__('This is not a valid Webhook! Check your settings.', 'memberpress')); + } + + return false; } - return false; - } - - public function get_input_stream() { - return file_get_contents("php://input"); - } - - /** - * Validate the webhook signature from Authorize.net - * @param string $request_body Raw HTTP request body - * @return boolean - */ - private function validate_webhook($request_body) { - if(isset($_SERVER['HTTP_X_ANET_SIGNATURE'])) { - $webhook_signature = strtoupper(explode('=', $_SERVER['HTTP_X_ANET_SIGNATURE'])[1]); - $hashed_body = strtoupper(hash_hmac('sha512', $request_body, $this->gateway_settings->signature_key)); - return $webhook_signature === $hashed_body; + public function get_input_stream() + { + return file_get_contents('php://input'); } - return false; - } - - /** - * Handle payment failure webhook notifications (responseCode > 1) - * Only used for recurring payments through ARB - * net.authorize.payment.authcapture.created - * net.authorize.payment.capture.created - * net.authorize.payment.fraud.approved - * - * @param object $auth_transaction JSON transaction object - * @param boolean $setup_job Set to true to enqueue a job to retry if subscription data is not yet available. - * @return object|false MeprTransaction or false - */ - public function record_payment_failure($auth_transaction, $setup_job = true) { - if(isset($auth_transaction->transId) and !empty($auth_transaction->transId)) { - $txn_res = MeprTransaction::get_one_by_trans_num($auth_transaction->transId); - - if(is_object($txn_res) and isset($txn_res->id)) { - $txn = new MeprTransaction($txn_res->id); - $txn->status = MeprTransaction::$failed_str; - $txn->store(); - } - else if(!isset($auth_transaction->subscription->id) && $setup_job) { - $job = new MeprAuthorizeRetryJob(); - $job->gateway_settings = $this->gateway_settings; - $job->transaction_data = json_encode($auth_transaction); - $job->payment_failed = true; - $job->enqueue_in('10m'); // Try again in 10 minutes, then it will retry every 30 minutes after. - return false; - } - else if(isset($auth_transaction->subscription->id) && $sub = MeprSubscription::get_one_by_subscr_id($auth_transaction->subscription->id) ) { - $txn = $this->insert_transaction($sub, $auth_transaction, MeprTransaction::$failed_str); - $sub->status = MeprSubscription::$active_str; - $sub->gateway = $this->gateway_settings->id; - $sub->expire_txns(); - $sub->store(); - } - else { + /** + * Validate the webhook signature from Authorize.net + * + * @param string $request_body Raw HTTP request body + * @return boolean + */ + private function validate_webhook($request_body) + { + if (isset($_SERVER['HTTP_X_ANET_SIGNATURE'])) { + $webhook_signature = strtoupper(explode('=', $_SERVER['HTTP_X_ANET_SIGNATURE'])[1]); + $hashed_body = strtoupper(hash_hmac('sha512', $request_body, $this->gateway_settings->signature_key)); + return $webhook_signature === $hashed_body; + } return false; - } + } - if(!defined('TESTS_RUNNING')) { - MeprUtils::send_failed_txn_notices($txn); - } + /** + * Handle payment failure webhook notifications (responseCode > 1) + * Only used for recurring payments through ARB + * net.authorize.payment.authcapture.created + * net.authorize.payment.capture.created + * net.authorize.payment.fraud.approved + * + * @param object $auth_transaction JSON transaction object + * @param boolean $setup_job Set to true to enqueue a job to retry if subscription data is not yet available. + * @return object|false MeprTransaction or false + */ + public function record_payment_failure($auth_transaction, $setup_job = true) + { + if (isset($auth_transaction->transId) and !empty($auth_transaction->transId)) { + $txn_res = MeprTransaction::get_one_by_trans_num($auth_transaction->transId); + + if (is_object($txn_res) and isset($txn_res->id)) { + $txn = new MeprTransaction($txn_res->id); + $txn->status = MeprTransaction::$failed_str; + $txn->store(); + } elseif (!isset($auth_transaction->subscription->id) && $setup_job) { + $job = new MeprAuthorizeRetryJob(); + $job->gateway_settings = $this->gateway_settings; + $job->transaction_data = json_encode($auth_transaction); + $job->payment_failed = true; + $job->enqueue_in('10m'); // Try again in 10 minutes, then it will retry every 30 minutes after. + return false; + } elseif (isset($auth_transaction->subscription->id) && $sub = MeprSubscription::get_one_by_subscr_id($auth_transaction->subscription->id)) { + $txn = $this->insert_transaction($sub, $auth_transaction, MeprTransaction::$failed_str); + + $sub->status = MeprSubscription::$active_str; + $sub->gateway = $this->gateway_settings->id; + $sub->expire_txns(); + $sub->store(); + } else { + return false; + } + + if (!defined('TESTS_RUNNING')) { + MeprUtils::send_failed_txn_notices($txn); + } + + return $txn; + } - return $txn; + return false; } - return false; - } + public function log($data) + { + if (! defined('WP_MEPR_DEBUG')) { + return; + } - public function log( $data ) { - if ( ! defined( 'WP_MEPR_DEBUG' ) ) { - return; + file_put_contents(WP_CONTENT_DIR . '/authorize-net.log', print_r($data, true) . PHP_EOL, FILE_APPEND); } - file_put_contents( WP_CONTENT_DIR . '/authorize-net.log', print_r( $data, true ) . PHP_EOL, FILE_APPEND ); - } - - /** - * Handle successful payment webhook notifications (responseCode 1) - * Only used for recurring payments through ARB - * net.authorize.payment.authcapture.created - * net.authorize.payment.capture.created - * net.authorize.payment.fraud.approved - * @param object $auth_transaction JSON transaction object - * @return object|false MeprTransaction or false - */ - public function record_subscription_payment($auth_transaction, $setup_job = true) { - if($setup_job && !isset($auth_transaction->subscription)) { - // Enqueue a job to try again in 30 minutes - $job = new MeprAuthorizeRetryJob(); - $job->gateway_settings = $this->gateway_settings; - $job->transaction_data = json_encode($auth_transaction); - $job->payment_failed = false; - $job->enqueue_in('10m'); //Try again in 10 minutes. Then it will retry every 30 minutes after - return false; - } + /** + * Handle successful payment webhook notifications (responseCode 1) + * Only used for recurring payments through ARB + * net.authorize.payment.authcapture.created + * net.authorize.payment.capture.created + * net.authorize.payment.fraud.approved + * + * @param object $auth_transaction JSON transaction object + * @return object|false MeprTransaction or false + */ + public function record_subscription_payment($auth_transaction, $setup_job = true) + { + if ($setup_job && !isset($auth_transaction->subscription)) { + // Enqueue a job to try again in 30 minutes + $job = new MeprAuthorizeRetryJob(); + $job->gateway_settings = $this->gateway_settings; + $job->transaction_data = json_encode($auth_transaction); + $job->payment_failed = false; + $job->enqueue_in('10m'); // Try again in 10 minutes. Then it will retry every 30 minutes after + return false; + } - $this->log('New recurring payment came'); - $this->log($auth_transaction); - if(!$sub = MeprSubscription::get_one_by_subscr_id($auth_transaction->subscription->id)) { - return false; - } + $this->log('New recurring payment came'); + $this->log($auth_transaction); + if (!$sub = MeprSubscription::get_one_by_subscr_id($auth_transaction->subscription->id)) { + return false; + } - $txn = $this->insert_transaction($sub, $auth_transaction, MeprTransaction::$complete_str); + $txn = $this->insert_transaction($sub, $auth_transaction, MeprTransaction::$complete_str); - $sub->status = MeprSubscription::$active_str; - $sub->cc_last4 = substr($auth_transaction->payment->creditCard->cardNumber, -4); // Don't get the XXXX part of the string - $sub->gateway = $this->gateway_settings->id; - $sub->store(); - $sub->limit_payment_cycles(); + $sub->status = MeprSubscription::$active_str; + $sub->cc_last4 = substr($auth_transaction->payment->creditCard->cardNumber, -4); // Don't get the XXXX part of the string + $sub->gateway = $this->gateway_settings->id; + $sub->store(); + $sub->limit_payment_cycles(); - if(!defined('TESTS_RUNNING')) { - MeprUtils::send_transaction_receipt_notices($txn); - } + if (!defined('TESTS_RUNNING')) { + MeprUtils::send_transaction_receipt_notices($txn); + } - return $txn; - } + return $txn; + } - /** - * Handle payment refund webhook notifications - * Only used for recurring payments through ARB - * net.authorize.payment.refund.created - * @param object $auth_transaction JSON transaction object - * @return object|false MeprTransaction or false - */ - private function record_refund($auth_transaction) { - $txn_res = MeprTransaction::get_one_by_trans_num($auth_transaction->transId); + /** + * Handle payment refund webhook notifications + * Only used for recurring payments through ARB + * net.authorize.payment.refund.created + * + * @param object $auth_transaction JSON transaction object + * @return object|false MeprTransaction or false + */ + private function record_refund($auth_transaction) + { + $txn_res = MeprTransaction::get_one_by_trans_num($auth_transaction->transId); + + if (!isset($txn_res) or empty($txn_res)) { + return false; + } - if(!isset($txn_res) or empty($txn_res)) { return false; } + $txn = new MeprTransaction($txn_res->id); - $txn = new MeprTransaction($txn_res->id); + if ($txn->status == MeprTransaction::$refunded_str) { + return $txn; + } - if($txn->status == MeprTransaction::$refunded_str) { return $txn; } + $txn->status = MeprTransaction::$refunded_str; + $txn->store(); - $txn->status = MeprTransaction::$refunded_str; - $txn->store(); + if (!defined('TESTS_RUNNING')) { + MeprUtils::send_refunded_txn_notices($txn); + } - if(!defined('TESTS_RUNNING')) { - MeprUtils::send_refunded_txn_notices($txn); + return $txn; } - return $txn; - } - - /** - * Create a MeprTransaction from the Authorize.net transaction - * @param object $sub MeprSubscription - * @param object $auth_transaction AuthorizeNet transaction object - * @param string $status - * @return object MeprTransaction - */ - private function insert_transaction($sub, $auth_transaction, $status) { - $first_txn = $sub->first_txn(); - if($first_txn == false || !($first_txn instanceof MeprTransaction)) { - $coupon_id = $sub->coupon_id; - } - else { - $coupon_id = $first_txn->coupon_id; - } + /** + * Create a MeprTransaction from the Authorize.net transaction + * + * @param object $sub MeprSubscription + * @param object $auth_transaction AuthorizeNet transaction object + * @param string $status + * @return object MeprTransaction + */ + private function insert_transaction($sub, $auth_transaction, $status) + { + $first_txn = $sub->first_txn(); + if ($first_txn == false || !($first_txn instanceof MeprTransaction)) { + $coupon_id = $sub->coupon_id; + } else { + $coupon_id = $first_txn->coupon_id; + } + + $txn = new MeprTransaction(); + $txn->user_id = $sub->user_id; + $txn->product_id = $sub->product_id; + $txn->coupon_id = $coupon_id; + $txn->txn_type = MeprTransaction::$payment_str; + $txn->status = $status; + $txn->subscription_id = $sub->id; + $txn->trans_num = $auth_transaction->transId; + $txn->gateway = $this->gateway_settings->id; + $txn->set_gross(isset($auth_transaction->settleAmount) ? $auth_transaction->settleAmount : $auth_transaction->authAmount); + $txn->store(); - $txn = new MeprTransaction(); - $txn->user_id = $sub->user_id; - $txn->product_id = $sub->product_id; - $txn->coupon_id = $coupon_id; - $txn->txn_type = MeprTransaction::$payment_str; - $txn->status = $status; - $txn->subscription_id = $sub->id; - $txn->trans_num = $auth_transaction->transId; - $txn->gateway = $this->gateway_settings->id; - $txn->set_gross(isset($auth_transaction->settleAmount) ? $auth_transaction->settleAmount : $auth_transaction->authAmount); - $txn->store(); - - return $txn; - } + return $txn; + } } diff --git a/app/gateways/MeprPayPalCommerceGateway.php b/app/gateways/MeprPayPalCommerceGateway.php index 4cb265c..1d54593 100644 --- a/app/gateways/MeprPayPalCommerceGateway.php +++ b/app/gateways/MeprPayPalCommerceGateway.php @@ -1,1909 +1,1984 @@ name = __( "PayPal", 'memberpress' ); - $this->key = __( 'paypalcommerce', 'memberpress' ); - $this->has_spc_form = true; - $this->set_defaults(); - - // Setup the notification actions for this gateway - $this->notifiers = array( - 'ipn' => 'ipn_listener', - 'cancel' => 'cancel_handler', - 'webhook' => 'webhook_handler', - 'return' => 'return_handler' - ); - - $this->message_pages = array( 'cancel' => 'cancel_message' ); - } - - public function record_create_subscription() { - // not needed, subscription will be created with PENDING satatus, before payment done - } - - public function record_payment_failure() { - if ( isset( $_POST['txn_id'] ) && ( $txn_res = MeprTransaction::get_one_by_trans_num( $_POST['txn_id'] ) ) && isset( $txn_res->id ) ) { - $txn = new MeprTransaction( $txn_res->id ); - $txn->status = MeprTransaction::$failed_str; - $txn->store(); - } elseif ( ( isset( $_POST['recurring_payment_id'] ) and - ( $sub = MeprSubscription::get_one_by_subscr_id( $_POST['recurring_payment_id'] ) ) ) or - ( isset( $_POST['subscr_id'] ) and - ( $sub = MeprSubscription::get_one_by_subscr_id( $_POST['subscr_id'] ) ) ) ) { - $first_txn = $sub->first_txn(); - - if ( $first_txn == false || ! ( $first_txn instanceof MeprTransaction ) ) { - $first_txn = new MeprTransaction(); - $first_txn->user_id = $sub->user_id; - $first_txn->product_id = $sub->product_id; - $first_txn->coupon_id = $sub->coupon_id; - } - - $txn = new MeprTransaction(); - $txn->user_id = $sub->user_id; - $txn->product_id = $sub->product_id; - $txn->coupon_id = $first_txn->coupon_id; - $txn->txn_type = MeprTransaction::$payment_str; - $txn->status = MeprTransaction::$failed_str; - $txn->subscription_id = $sub->id; - $txn->trans_num = ( isset( $_POST['recurring_payment_id'] ) ? $_POST['recurring_payment_id'] : uniqid() ); - $txn->gateway = $this->id; - - $txn->set_gross( isset( $_POST['mc_gross'] ) ? $_POST['mc_gross'] : ( isset( $_POST['amount'] ) ? $_POST['amount'] : 0.00 ) ); - - $txn->store(); - - $sub->expire_txns(); //Expire associated transactions for the old subscription - $sub->store(); - } else { - return false; // Nothing we can do here ... so we outta here +class MeprPayPalCommerceGateway extends MeprBasePayPalGateway +{ + /** + * Used in the view to identify the gateway + */ + public function __construct() + { + $this->name = __('PayPal', 'memberpress'); + $this->key = __('paypalcommerce', 'memberpress'); + $this->has_spc_form = true; + $this->set_defaults(); + + // Setup the notification actions for this gateway + $this->notifiers = [ + 'ipn' => 'ipn_listener', + 'cancel' => 'cancel_handler', + 'webhook' => 'webhook_handler', + 'return' => 'return_handler', + ]; + + $this->message_pages = ['cancel' => 'cancel_message']; + } + + public function record_create_subscription() + { + // not needed, subscription will be created with PENDING satatus, before payment done } - MeprUtils::send_failed_txn_notices( $txn ); + public function record_payment_failure() + { + if (isset($_POST['txn_id']) && ( $txn_res = MeprTransaction::get_one_by_trans_num($_POST['txn_id']) ) && isset($txn_res->id)) { + $txn = new MeprTransaction($txn_res->id); + $txn->status = MeprTransaction::$failed_str; + $txn->store(); + } elseif ( + ( isset($_POST['recurring_payment_id']) and + ( $sub = MeprSubscription::get_one_by_subscr_id($_POST['recurring_payment_id']) ) ) or + ( isset($_POST['subscr_id']) and + ( $sub = MeprSubscription::get_one_by_subscr_id($_POST['subscr_id']) ) ) + ) { + $first_txn = $sub->first_txn(); - return $txn; - } + if ($first_txn == false || ! ( $first_txn instanceof MeprTransaction )) { + $first_txn = new MeprTransaction(); + $first_txn->user_id = $sub->user_id; + $first_txn->product_id = $sub->product_id; + $first_txn->coupon_id = $sub->coupon_id; + } - public function record_payment() { - // Not needed, payment will be recorded by webhook handler - } + $txn = new MeprTransaction(); + $txn->user_id = $sub->user_id; + $txn->product_id = $sub->product_id; + $txn->coupon_id = $first_txn->coupon_id; + $txn->txn_type = MeprTransaction::$payment_str; + $txn->status = MeprTransaction::$failed_str; + $txn->subscription_id = $sub->id; + $txn->trans_num = ( isset($_POST['recurring_payment_id']) ? $_POST['recurring_payment_id'] : uniqid() ); + $txn->gateway = $this->id; - public function load( $settings ) { - $this->settings = (object) $settings; - $this->set_defaults(); - } + $txn->set_gross(isset($_POST['mc_gross']) ? $_POST['mc_gross'] : ( isset($_POST['amount']) ? $_POST['amount'] : 0.00 )); - public function log( $data ) { - if ( ! defined( 'WP_MEPR_DEBUG' ) ) { - return; - } + $txn->store(); + + $sub->expire_txns(); // Expire associated transactions for the old subscription + $sub->store(); + } else { + return false; // Nothing we can do here ... so we outta here + } - file_put_contents( WP_CONTENT_DIR . '/paypal-connect.log', print_r( $data, true ) . PHP_EOL, FILE_APPEND ); - } + MeprUtils::send_failed_txn_notices($txn); - protected function set_defaults() { - if ( ! isset( $this->settings ) ) { - $this->settings = array(); + return $txn; } - $this->settings = - (object) array_merge( - array( - 'gateway' => 'MeprPayPalCommerceGateway', - 'id' => $this->generate_id(), - 'label' => '', - 'use_label' => true, - 'icon' => MEPR_IMAGES_URL . '/checkout/paypal.png', - 'use_icon' => true, - 'desc' => __( 'Pay via your PayPal account', 'memberpress' ), - 'use_desc' => true, - 'enable_smart_button' => false, - 'enable_paypal_standard_debug_email' => false, - 'test_client_id' => '', - 'test_client_secret' => '', - 'live_client_id' => '', - 'live_client_secret' => '', - 'test_webhook_id' => '', - 'live_webhook_id' => '', - 'test_merchant_id' => '', - 'live_merchant_id' => '', - 'test_auth_code' => '', - 'live_auth_code' => '', - 'test_email_confirmed' => '', - 'live_email_confirmed' => '', - 'debug' => false - ), - (array) $this->settings - ); - $this->id = $this->settings->id; - $this->label = $this->settings->label; - $this->use_label = $this->settings->use_label; - $this->icon = $this->settings->icon; - $this->use_icon = $this->settings->use_icon; - $this->desc = $this->settings->desc; - $this->use_desc = $this->settings->use_desc; - $this->debug = defined( 'WP_MEPR_DEBUG' ) && WP_MEPR_DEBUG === true; - - if ( $this->is_test_mode() ) { - $this->settings->url = 'https://ipnpb.sandbox.paypal.com/cgi-bin/webscr'; - $this->settings->api_url = 'https://api-3t.sandbox.paypal.com/nvp'; - $this->settings->rest_api_url = 'https://api-m.sandbox.paypal.com'; - } else { - $this->settings->url = 'https://ipnpb.paypal.com/cgi-bin/webscr'; - $this->settings->api_url = 'https://api-3t.paypal.com/nvp'; - $this->settings->rest_api_url = 'https://api.paypal.com'; + public function record_payment() + { + // Not needed, payment will be recorded by webhook handler } - $this->settings->api_version = 69; - - $this->capabilities = array( - 'process-payments', - 'process-refunds', - 'create-subscriptions', - 'cancel-subscriptions', - 'update-subscriptions', - 'suspend-subscriptions', - 'resume-subscriptions', - 'subscription-trial-payment', - 'order-bumps', - ); - } - - /** Used to record a successful recurring payment by the given gateway. It - * should have the ability to record a successful payment or a failure. It is - * this method that should be used when receiving an IPN from PayPal or a - * Silent Post from Authorize.net. - */ - public function record_subscription_payment() { - if ( ! isset( $_POST['recurring_payment_id'] ) && ! isset( $_POST['subscr_id'] ) ) { - return; + public function load($settings) + { + $this->settings = (object) $settings; + $this->set_defaults(); } - if ( isset( $_POST['subscr_id'] ) && ! empty( $_POST['subscr_id'] ) ) { - $sub = MeprSubscription::get_one_by_subscr_id( $_POST['subscr_id'] ); - } else { - $sub = MeprSubscription::get_one_by_subscr_id( $_POST['recurring_payment_id'] ); + public function log($data) + { + if (! defined('WP_MEPR_DEBUG')) { + return; + } + + file_put_contents(WP_CONTENT_DIR . '/paypal-connect.log', print_r($data, true) . PHP_EOL, FILE_APPEND); } - if ( $sub ) { - $timestamp = isset( $_POST['payment_date'] ) ? strtotime( $_POST['payment_date'] ) : time(); - $first_txn = new MeprTransaction( $sub->first_txn_id ); + protected function set_defaults() + { + if (! isset($this->settings)) { + $this->settings = []; + } - if ( ! isset( $first_txn->id ) || empty( $first_txn->id ) ) { - $first_txn = new MeprTransaction(); - $first_txn->user_id = $sub->user_id; - $first_txn->product_id = $sub->product_id; - $first_txn->coupon_id = $sub->coupon_id; - } + $this->settings = + (object) array_merge( + [ + 'gateway' => 'MeprPayPalCommerceGateway', + 'id' => $this->generate_id(), + 'label' => '', + 'use_label' => true, + 'icon' => MEPR_IMAGES_URL . '/checkout/paypal.png', + 'use_icon' => true, + 'desc' => __('Pay via your PayPal account', 'memberpress'), + 'use_desc' => true, + 'enable_smart_button' => false, + 'enable_paypal_standard_debug_email' => false, + 'test_client_id' => '', + 'test_client_secret' => '', + 'live_client_id' => '', + 'live_client_secret' => '', + 'test_webhook_id' => '', + 'live_webhook_id' => '', + 'test_merchant_id' => '', + 'live_merchant_id' => '', + 'test_auth_code' => '', + 'live_auth_code' => '', + 'test_email_confirmed' => '', + 'live_email_confirmed' => '', + 'debug' => false, + ], + (array) $this->settings + ); + $this->id = $this->settings->id; + $this->label = $this->settings->label; + $this->use_label = $this->settings->use_label; + $this->icon = $this->settings->icon; + $this->use_icon = $this->settings->use_icon; + $this->desc = $this->settings->desc; + $this->use_desc = $this->settings->use_desc; + $this->debug = defined('WP_MEPR_DEBUG') && WP_MEPR_DEBUG === true; + + if ($this->is_test_mode()) { + $this->settings->url = 'https://ipnpb.sandbox.paypal.com/cgi-bin/webscr'; + $this->settings->api_url = 'https://api-3t.sandbox.paypal.com/nvp'; + $this->settings->rest_api_url = 'https://api-m.sandbox.paypal.com'; + } else { + $this->settings->url = 'https://ipnpb.paypal.com/cgi-bin/webscr'; + $this->settings->api_url = 'https://api-3t.paypal.com/nvp'; + $this->settings->rest_api_url = 'https://api.paypal.com'; + } - $existing = MeprTransaction::get_one_by_trans_num( $_POST['txn_id'] ); + $this->settings->api_version = 69; + + $this->capabilities = [ + 'process-payments', + 'process-refunds', + 'create-subscriptions', + 'cancel-subscriptions', + 'update-subscriptions', + 'suspend-subscriptions', + 'resume-subscriptions', + 'subscription-trial-payment', + 'order-bumps', + ]; + } - //There's a chance this may have already happened during the return handler, if so let's just get everything up to date on the existing one - if ( $existing != null && isset( $existing->id ) && (int) $existing->id > 0 ) { - $txn = new MeprTransaction( $existing->id ); - $handled = $txn->get_meta('mepr_paypal_notification_handled'); + /** + * Used to record a successful recurring payment by the given gateway. It + * should have the ability to record a successful payment or a failure. It is + * this method that should be used when receiving an IPN from PayPal or a + * Silent Post from Authorize.net. + */ + public function record_subscription_payment() + { + if (! isset($_POST['recurring_payment_id']) && ! isset($_POST['subscr_id'])) { + return; + } - if (!empty($handled)) { - return; + if (isset($_POST['subscr_id']) && ! empty($_POST['subscr_id'])) { + $sub = MeprSubscription::get_one_by_subscr_id($_POST['subscr_id']); + } else { + $sub = MeprSubscription::get_one_by_subscr_id($_POST['recurring_payment_id']); } - } else { - $txn = new MeprTransaction(); - } - //If this is a trial payment, let's just convert the confirmation txn into a payment txn - if ( $this->is_subscr_trial_payment( $sub ) ) { - $txn = $first_txn; //For use below in send notices - $txn->created_at = MeprUtils::ts_to_mysql_date( $timestamp ); - $txn->expires_at = MeprUtils::ts_to_mysql_date( time() + MeprUtils::days( $sub->trial_days ), 'Y-m-d 23:59:59' ); - $txn->gateway = $this->id; - $txn->trans_num = $_POST['txn_id']; - $txn->txn_type = MeprTransaction::$payment_str; - $txn->status = MeprTransaction::$complete_str; - $txn->subscription_id = $sub->id; + if ($sub) { + $timestamp = isset($_POST['payment_date']) ? strtotime($_POST['payment_date']) : time(); + $first_txn = new MeprTransaction($sub->first_txn_id); - if(isset($_POST['mepr_order_id'])) { - $txn->order_id = $_POST['mepr_order_id']; - } + if (! isset($first_txn->id) || empty($first_txn->id)) { + $first_txn = new MeprTransaction(); + $first_txn->user_id = $sub->user_id; + $first_txn->product_id = $sub->product_id; + $first_txn->coupon_id = $sub->coupon_id; + } - $txn->set_gross( $_POST['mc_gross'] ); - $txn->store(); - } else { - $txn->created_at = MeprUtils::ts_to_mysql_date( $timestamp ); - $txn->user_id = $first_txn->user_id; - $txn->product_id = $first_txn->product_id; - $txn->coupon_id = $first_txn->coupon_id; - $txn->gateway = $this->id; - $txn->trans_num = $_POST['txn_id']; - $txn->txn_type = MeprTransaction::$payment_str; - $txn->status = MeprTransaction::$complete_str; - $txn->subscription_id = $sub->id; - - if(isset($_POST['mepr_order_id'])) { - $txn->order_id = $_POST['mepr_order_id']; - } - - $txn->set_gross( $_POST['mc_gross'] ); - $txn->store(); + $existing = MeprTransaction::get_one_by_trans_num($_POST['txn_id']); - //Check that the subscription status is still enabled - if ( $sub->status != MeprSubscription::$active_str ) { - $sub->status = MeprSubscription::$active_str; - $sub->store(); - } + // There's a chance this may have already happened during the return handler, if so let's just get everything up to date on the existing one + if ($existing != null && isset($existing->id) && (int) $existing->id > 0) { + $txn = new MeprTransaction($existing->id); + $handled = $txn->get_meta('mepr_paypal_notification_handled'); + + if (!empty($handled)) { + return; + } + } else { + $txn = new MeprTransaction(); + } + + // If this is a trial payment, let's just convert the confirmation txn into a payment txn + if ($this->is_subscr_trial_payment($sub)) { + $txn = $first_txn; // For use below in send notices + $txn->created_at = MeprUtils::ts_to_mysql_date($timestamp); + $txn->expires_at = MeprUtils::ts_to_mysql_date(time() + MeprUtils::days($sub->trial_days), 'Y-m-d 23:59:59'); + $txn->gateway = $this->id; + $txn->trans_num = $_POST['txn_id']; + $txn->txn_type = MeprTransaction::$payment_str; + $txn->status = MeprTransaction::$complete_str; + $txn->subscription_id = $sub->id; + + if (isset($_POST['mepr_order_id'])) { + $txn->order_id = $_POST['mepr_order_id']; + } + + $txn->set_gross($_POST['mc_gross']); + $txn->store(); + } else { + $txn->created_at = MeprUtils::ts_to_mysql_date($timestamp); + $txn->user_id = $first_txn->user_id; + $txn->product_id = $first_txn->product_id; + $txn->coupon_id = $first_txn->coupon_id; + $txn->gateway = $this->id; + $txn->trans_num = $_POST['txn_id']; + $txn->txn_type = MeprTransaction::$payment_str; + $txn->status = MeprTransaction::$complete_str; + $txn->subscription_id = $sub->id; + + if (isset($_POST['mepr_order_id'])) { + $txn->order_id = $_POST['mepr_order_id']; + } + + $txn->set_gross($_POST['mc_gross']); + $txn->store(); + + // Check that the subscription status is still enabled + if ($sub->status != MeprSubscription::$active_str) { + $sub->status = MeprSubscription::$active_str; + $sub->store(); + } + + // Not waiting for an IPN here bro ... just making it happen even though + // the total occurrences is already capped in record_create_subscription() + $sub->limit_payment_cycles(); + } - // Not waiting for an IPN here bro ... just making it happen even though - // the total occurrences is already capped in record_create_subscription() - $sub->limit_payment_cycles(); - } + $txn->update_meta('mepr_paypal_notification_handled', true); - $txn->update_meta('mepr_paypal_notification_handled', true); + $this->email_status("Subscription Transaction\n" . MeprUtils::object_to_string($txn->rec, true), $this->debug); - $this->email_status( "Subscription Transaction\n" . MeprUtils::object_to_string( $txn->rec, true ), $this->debug ); + MeprUtils::send_transaction_receipt_notices($txn); - MeprUtils::send_transaction_receipt_notices( $txn ); + return $txn; + } - return $txn; + return false; } - return false; - } - - /** Used to send data to a given payment gateway. In gateways which redirect - * before this step is necessary this method should just be left blank. - */ - public function process_payment( $txn ) { - } - - /** - * @param MeprTransaction $txn - * - * @return false|MeprTransaction - * @throws MeprGatewayException - */ - public function process_refund( MeprTransaction $txn ) { - $product = $txn->product(); - - if ( $product->is_one_time_payment() ) { - $txn_number = $txn->trans_num; - $options = [ - 'headers' => [ - 'Content-Type' => 'application/json', - 'PayPal-Partner-Attribution-Id' => MeprPayPalConnectCtrl::PAYPAL_BN_CODE, - 'Authorization' => 'Basic ' . $this->get_pp_basic_auth_token(), - ], - ]; - $response = wp_remote_post( $this->settings->rest_api_url . '/v2/payments/captures/' . $txn_number . '/refund', $options ); - $this->log( $response ); - $response = json_decode( wp_remote_retrieve_body( $response ), true ); - $this->log( $options ); - } else { - $endpoint = '/v1/payments/sale/' . $txn->trans_num . '/refund'; - $options = [ - 'headers' => [ - 'Content-Type' => 'application/json', - 'PayPal-Partner-Attribution-Id' => MeprPayPalConnectCtrl::PAYPAL_BN_CODE, - 'Authorization' => 'Basic ' . $this->get_pp_basic_auth_token(), - ], - ]; - $response = wp_remote_post( $this->settings->rest_api_url . $endpoint, $options ); - $this->log( $response ); - $response = json_decode( wp_remote_retrieve_body( $response ), true ); - $this->log( $options ); - - if ( isset( $response['name'] ) && $response['name'] == 'TRANSACTION_ALREADY_REFUNDED' ) { + /** + * Used to send data to a given payment gateway. In gateways which redirect + * before this step is necessary this method should just be left blank. + */ + public function process_payment($txn) + { + } + + /** + * @param MeprTransaction $txn + * + * @return false|MeprTransaction + * @throws MeprGatewayException + */ + public function process_refund(MeprTransaction $txn) + { + $product = $txn->product(); + + if ($product->is_one_time_payment()) { + $txn_number = $txn->trans_num; + $options = [ + 'headers' => [ + 'Content-Type' => 'application/json', + 'PayPal-Partner-Attribution-Id' => MeprPayPalConnectCtrl::PAYPAL_BN_CODE, + 'Authorization' => 'Basic ' . $this->get_pp_basic_auth_token(), + ], + ]; + $response = wp_remote_post($this->settings->rest_api_url . '/v2/payments/captures/' . $txn_number . '/refund', $options); + $this->log($response); + $response = json_decode(wp_remote_retrieve_body($response), true); + $this->log($options); + } else { + $endpoint = '/v1/payments/sale/' . $txn->trans_num . '/refund'; + $options = [ + 'headers' => [ + 'Content-Type' => 'application/json', + 'PayPal-Partner-Attribution-Id' => MeprPayPalConnectCtrl::PAYPAL_BN_CODE, + 'Authorization' => 'Basic ' . $this->get_pp_basic_auth_token(), + ], + ]; + $response = wp_remote_post($this->settings->rest_api_url . $endpoint, $options); + $this->log($response); + $response = json_decode(wp_remote_retrieve_body($response), true); + $this->log($options); + + if (isset($response['name']) && $response['name'] == 'TRANSACTION_ALREADY_REFUNDED') { + $_POST['parent_txn_id'] = $txn->id; + + return $this->record_refund(); + } + } + + if (isset($response['status']) && $response['status'] !== 'COMPLETED') { + throw new MeprGatewayException(__('Refund request has been done unsuccessfully', 'memberpress')); + } + $_POST['parent_txn_id'] = $txn->id; return $this->record_refund(); - } } - if ( isset( $response['status'] ) && $response['status'] !== 'COMPLETED' ) { - throw new MeprGatewayException( __( 'Refund request has been done unsuccessfully', 'memberpress' ) ); - } + /** + * This method should be used by the class to record a successful refund from + * the gateway. This method should also be used by any webhook requests or Silent Posts. + */ + public function record_refund() + { + $obj = new MeprTransaction($_POST['parent_txn_id']); - $_POST['parent_txn_id'] = $txn->id; + if (! is_null($obj) && (int) $obj->id > 0) { + $txn = $obj; - return $this->record_refund(); - } + // Seriously ... if txn was already refunded what are we doing here? + if ($txn->status == MeprTransaction::$refunded_str) { + return $txn; + } - /** This method should be used by the class to record a successful refund from - * the gateway. This method should also be used by any webhook requests or Silent Posts. - */ - public function record_refund() { - $obj = new MeprTransaction( $_POST['parent_txn_id'] ); + $txn->status = MeprTransaction::$refunded_str; - if ( ! is_null( $obj ) && (int) $obj->id > 0 ) { - $txn = $obj; + $this->email_status("Processing Refund: \n" . MeprUtils::object_to_string($_POST) . "\n Affected Transaction: \n" . MeprUtils::object_to_string($txn), $this->debug); - // Seriously ... if txn was already refunded what are we doing here? - if ( $txn->status == MeprTransaction::$refunded_str ) { - return $txn; - } + $txn->store(); - $txn->status = MeprTransaction::$refunded_str; + MeprUtils::send_refunded_txn_notices($txn); - $this->email_status( "Processing Refund: \n" . MeprUtils::object_to_string( $_POST ) . "\n Affected Transaction: \n" . MeprUtils::object_to_string( $txn ), $this->debug ); + return $txn; + } - $txn->store(); + return false; + } - MeprUtils::send_refunded_txn_notices( $txn ); + // Not needed in PayPal since PayPal supports the trial payment inclusive of the Subscription + public function process_trial_payment($transaction) + { + } - return $txn; + public function record_trial_payment($transaction) + { } - return false; - } - - //Not needed in PayPal since PayPal supports the trial payment inclusive of the Subscription - public function process_trial_payment( $transaction ) { - } - - public function record_trial_payment( $transaction ) { - } - - /** Used to send subscription data to a given payment gateway. In gateways - * which redirect before this step is necessary this method should just be - * left blank. - */ - public function process_create_subscription( $txn ) { - } - - /** - * Process a successful one-time payment - * - * @param MeprTransaction $txn The MemberPress transaction - * @param string $trans_num The transaction number to set - */ - public function handle_one_time_payment(MeprTransaction $txn, $trans_num) { - // Just short circuit if the txn has already completed - if($txn->status == MeprTransaction::$complete_str) { - return; + /** + * Used to send subscription data to a given payment gateway. In gateways + * which redirect before this step is necessary this method should just be + * left blank. + */ + public function process_create_subscription($txn) + { } - $txn->trans_num = $trans_num; - $txn->status = MeprTransaction::$complete_str; - $txn->store(); + /** + * Process a successful one-time payment + * + * @param MeprTransaction $txn The MemberPress transaction + * @param string $trans_num The transaction number to set + */ + public function handle_one_time_payment(MeprTransaction $txn, $trans_num) + { + // Just short circuit if the txn has already completed + if ($txn->status == MeprTransaction::$complete_str) { + return; + } - $prd = $txn->product(); + $txn->trans_num = $trans_num; + $txn->status = MeprTransaction::$complete_str; + $txn->store(); - // This will only work before maybe_cancel_old_sub is run - $upgrade = $txn->is_upgrade(); - $downgrade = $txn->is_downgrade(); + $prd = $txn->product(); - $event_txn = $txn->maybe_cancel_old_sub(); + // This will only work before maybe_cancel_old_sub is run + $upgrade = $txn->is_upgrade(); + $downgrade = $txn->is_downgrade(); - if($prd->period_type == 'lifetime') { - if($upgrade) { - $this->upgraded_sub($txn, $event_txn); - } - else if($downgrade) { - $this->downgraded_sub($txn, $event_txn); - } - else { - $this->new_sub($txn); - } + $event_txn = $txn->maybe_cancel_old_sub(); - MeprUtils::send_signup_notices($txn); - } + if ($prd->period_type == 'lifetime') { + if ($upgrade) { + $this->upgraded_sub($txn, $event_txn); + } elseif ($downgrade) { + $this->downgraded_sub($txn, $event_txn); + } else { + $this->new_sub($txn); + } - MeprUtils::send_transaction_receipt_notices($txn); - } - - /** - * Handle the creation of a new subscription - * - * @param MeprSubscription $sub - * @param string $subscr_id - */ - public function handle_create_subscription(MeprSubscription $sub, $subscr_id) { - $txn = $sub->first_txn(); - - if(!($txn instanceof MeprTransaction)) { - $txn = new MeprTransaction(); - $txn->user_id = $sub->user_id; - $txn->product_id = $sub->product_id; - $txn->gateway = $this->id; - $txn->subscription_id = $sub->id; - } + MeprUtils::send_signup_notices($txn); + } - $sub->subscr_id = $subscr_id; + MeprUtils::send_transaction_receipt_notices($txn); + } + + /** + * Handle the creation of a new subscription + * + * @param MeprSubscription $sub + * @param string $subscr_id + */ + public function handle_create_subscription(MeprSubscription $sub, $subscr_id) + { + $txn = $sub->first_txn(); + + if (!($txn instanceof MeprTransaction)) { + $txn = new MeprTransaction(); + $txn->user_id = $sub->user_id; + $txn->product_id = $sub->product_id; + $txn->gateway = $this->id; + $txn->subscription_id = $sub->id; + } - $this->activate_subscription($txn, $sub); + $sub->subscr_id = $subscr_id; - // This will only work before maybe_cancel_old_sub is run - $upgrade = $sub->is_upgrade(); - $downgrade = $sub->is_downgrade(); + $this->activate_subscription($txn, $sub); - $event_txn = $sub->maybe_cancel_old_sub(); + // This will only work before maybe_cancel_old_sub is run + $upgrade = $sub->is_upgrade(); + $downgrade = $sub->is_downgrade(); - if($upgrade) { - $this->upgraded_sub($sub, $event_txn); - } - else if($downgrade) { - $this->downgraded_sub($sub, $event_txn); - } - else { - $this->new_sub($sub, true); - } + $event_txn = $sub->maybe_cancel_old_sub(); - MeprUtils::send_signup_notices($txn); - } - - /** Used to cancel a subscription by the given gateway. This method should be used - * by the class to record a successful cancellation from the gateway. This method - * should also be used by any IPN requests or Silent Posts. - * - * With PayPal, we bill the outstanding amount of the previous subscription, - * cancel the previous subscription and create a new subscription - */ - public function process_update_subscription( $sub_id ) { - // Account info updated on PayPal.com - } - - /** This method should be used by the class to record a successful cancellation - * from the gateway. This method should also be used by any IPN requests or - * Silent Posts. - */ - public function record_update_subscription() { - // Account info updated on PayPal.com - } - - /** Used to suspend a subscription by the given gateway. - */ - public function process_suspend_subscription( $sub_id ) { - $sub = new MeprSubscription( $sub_id ); - - if ( $sub->status == MeprSubscription::$suspended_str ) { - throw new MeprGatewayException( __( 'This subscription has already been paused.', 'memberpress' ) ); - } + if ($upgrade) { + $this->upgraded_sub($sub, $event_txn); + } elseif ($downgrade) { + $this->downgraded_sub($sub, $event_txn); + } else { + $this->new_sub($sub, true); + } + + MeprUtils::send_signup_notices($txn); + } + + /** + * Used to cancel a subscription by the given gateway. This method should be used + * by the class to record a successful cancellation from the gateway. This method + * should also be used by any IPN requests or Silent Posts. + * + * With PayPal, we bill the outstanding amount of the previous subscription, + * cancel the previous subscription and create a new subscription + */ + public function process_update_subscription($sub_id) + { + // Account info updated on PayPal.com + } + + /** + * This method should be used by the class to record a successful cancellation + * from the gateway. This method should also be used by any IPN requests or + * Silent Posts. + */ + public function record_update_subscription() + { + // Account info updated on PayPal.com + } + + /** + * Used to suspend a subscription by the given gateway. + */ + public function process_suspend_subscription($sub_id) + { + $sub = new MeprSubscription($sub_id); + + if ($sub->status == MeprSubscription::$suspended_str) { + throw new MeprGatewayException(__('This subscription has already been paused.', 'memberpress')); + } - if ( $sub->in_free_trial() ) { - throw new MeprGatewayException( __( 'Sorry, subscriptions cannot be paused during a free trial.', 'memberpress' ) ); + if ($sub->in_free_trial()) { + throw new MeprGatewayException(__('Sorry, subscriptions cannot be paused during a free trial.', 'memberpress')); + } + + $this->update_paypal_payment_profile($sub_id, 'Suspend'); + + $_REQUEST['subscr_id'] = $sub->subscr_id; + $this->record_suspend_subscription(); } - $this->update_paypal_payment_profile( $sub_id, 'Suspend' ); + /** + * This method should be used by the class to record a successful suspension + * from the gateway. + */ + public function record_suspend_subscription() + { + $subscr_id = $_REQUEST['subscr_id']; + $sub = MeprSubscription::get_one_by_subscr_id($subscr_id); - $_REQUEST['subscr_id'] = $sub->subscr_id; - $this->record_suspend_subscription(); - } + if (! $sub) { + return false; + } - /** This method should be used by the class to record a successful suspension - * from the gateway. - */ - public function record_suspend_subscription() { - $subscr_id = $_REQUEST['subscr_id']; - $sub = MeprSubscription::get_one_by_subscr_id( $subscr_id ); + // Seriously ... if sub was already suspended what are we doing here? + if ($sub->status == MeprSubscription::$suspended_str) { + return $sub; + } - if ( ! $sub ) { - return false; + $sub->status = MeprSubscription::$suspended_str; + $sub->store(); + + MeprUtils::send_suspended_sub_notices($sub); + + return $sub; } - // Seriously ... if sub was already suspended what are we doing here? - if ( $sub->status == MeprSubscription::$suspended_str ) { - return $sub; + /** + * Used to suspend a subscription by the given gateway. + */ + public function process_resume_subscription($sub_id) + { + $sub = new MeprSubscription($sub_id); + $this->update_paypal_payment_profile($sub_id, 'Reactivate'); + + $_REQUEST['recurring_payment_id'] = $sub->subscr_id; + $this->record_resume_subscription(); } - $sub->status = MeprSubscription::$suspended_str; - $sub->store(); + /** + * This method should be used by the class to record a successful resuming of + * as subscription from the gateway. + */ + public function record_resume_subscription() + { + // APPARENTLY PAYPAL DOES NOT SEND OUT AN IPN FOR THIS -- SO WE CAN'T ACTUALLY RECORD THIS HERE UGH + // BUT WE DO SET THE SUBSCR STATUS BACK TO ACTIVE WHEN THE NEXT PAYMENT CLEARS + $subscr_id = $_REQUEST['recurring_payment_id']; + $sub = MeprSubscription::get_one_by_subscr_id($subscr_id); - MeprUtils::send_suspended_sub_notices( $sub ); + if (! $sub) { + return false; + } - return $sub; - } + // Seriously ... if sub was already active what are we doing here? + if ($sub->status == MeprSubscription::$active_str) { + return $sub; + } - /** Used to suspend a subscription by the given gateway. - */ - public function process_resume_subscription( $sub_id ) { - $sub = new MeprSubscription( $sub_id ); - $this->update_paypal_payment_profile( $sub_id, 'Reactivate' ); + $sub->status = MeprSubscription::$active_str; + $sub->store(); - $_REQUEST['recurring_payment_id'] = $sub->subscr_id; - $this->record_resume_subscription(); - } + // Check if prior txn is expired yet or not, if so create a temporary txn so the user can access the content immediately + $prior_txn = $sub->latest_txn(); + if ($prior_txn == false || ! ( $prior_txn instanceof MeprTransaction ) || strtotime($prior_txn->expires_at) < time()) { + $txn = new MeprTransaction(); + $txn->subscription_id = $sub->id; + $txn->trans_num = $sub->subscr_id . '-' . uniqid(); + $txn->status = MeprTransaction::$confirmed_str; + $txn->txn_type = MeprTransaction::$subscription_confirmation_str; + $txn->expires_at = MeprUtils::ts_to_mysql_date($sub->get_expires_at()); + $txn->set_subtotal(0.00); // Just a confirmation txn + $txn->store(); + } - /** This method should be used by the class to record a successful resuming of - * as subscription from the gateway. - */ - public function record_resume_subscription() { - //APPARENTLY PAYPAL DOES NOT SEND OUT AN IPN FOR THIS -- SO WE CAN'T ACTUALLY RECORD THIS HERE UGH - //BUT WE DO SET THE SUBSCR STATUS BACK TO ACTIVE WHEN THE NEXT PAYMENT CLEARS - $subscr_id = $_REQUEST['recurring_payment_id']; - $sub = MeprSubscription::get_one_by_subscr_id( $subscr_id ); + MeprUtils::send_resumed_sub_notices($sub); - if ( ! $sub ) { - return false; + return $sub; } - // Seriously ... if sub was already active what are we doing here? - if ( $sub->status == MeprSubscription::$active_str ) { - return $sub; - } + /** + * Used to cancel a subscription by the given gateway. This method should be used + * by the class to record a successful cancellation from the gateway. This method + * should also be used by any IPN requests or Silent Posts. + */ + public function process_cancel_subscription($sub_id) + { + $sub = new MeprSubscription($sub_id); - $sub->status = MeprSubscription::$active_str; - $sub->store(); - - //Check if prior txn is expired yet or not, if so create a temporary txn so the user can access the content immediately - $prior_txn = $sub->latest_txn(); - if ( $prior_txn == false || ! ( $prior_txn instanceof MeprTransaction ) || strtotime( $prior_txn->expires_at ) < time() ) { - $txn = new MeprTransaction(); - $txn->subscription_id = $sub->id; - $txn->trans_num = $sub->subscr_id . '-' . uniqid(); - $txn->status = MeprTransaction::$confirmed_str; - $txn->txn_type = MeprTransaction::$subscription_confirmation_str; - $txn->expires_at = MeprUtils::ts_to_mysql_date( $sub->get_expires_at() ); - $txn->set_subtotal( 0.00 ); // Just a confirmation txn - $txn->store(); + // Should already expire naturally at paypal so we have no need + // to do this when we're "cancelling" because of a natural expiration + if (! isset($_REQUEST['expire']) || isset($_REQUEST['limit_payment_cycles'])) { + $this->update_paypal_payment_profile($sub_id, 'Cancel'); + } + + $_REQUEST['subscr_id'] = $sub->subscr_id; + $this->record_cancel_subscription(); } - MeprUtils::send_resumed_sub_notices( $sub ); + /** + * This method should be used by the class to record a successful cancellation + * from the gateway. This method should also be used by any IPN requests or + * Silent Posts. + */ + public function record_cancel_subscription() + { + // Not sure how/why this would happen but fail silently if it does + if (! isset($_REQUEST['subscr_id']) && ! isset($_REQUEST['recurring_payment_id'])) { + return false; + } - return $sub; - } + $subscr_id = ( isset($_REQUEST['subscr_id']) ) ? $_REQUEST['subscr_id'] : $_REQUEST['recurring_payment_id']; + $sub = MeprSubscription::get_one_by_subscr_id($subscr_id); - /** Used to cancel a subscription by the given gateway. This method should be used - * by the class to record a successful cancellation from the gateway. This method - * should also be used by any IPN requests or Silent Posts. - */ - public function process_cancel_subscription( $sub_id ) { - $sub = new MeprSubscription( $sub_id ); + if (! $sub) { + return false; + } - // Should already expire naturally at paypal so we have no need - // to do this when we're "cancelling" because of a natural expiration - if ( ! isset( $_REQUEST['expire'] ) || isset( $_REQUEST['limit_payment_cycles'] ) ) { - $this->update_paypal_payment_profile( $sub_id, 'Cancel' ); - } + // Seriously ... if sub was already cancelled what are we doing here? + if ($sub->status == MeprSubscription::$cancelled_str) { + return $sub; + } - $_REQUEST['subscr_id'] = $sub->subscr_id; - $this->record_cancel_subscription(); - } - - /** This method should be used by the class to record a successful cancellation - * from the gateway. This method should also be used by any IPN requests or - * Silent Posts. - */ - public function record_cancel_subscription() { - // Not sure how/why this would happen but fail silently if it does - if ( ! isset( $_REQUEST['subscr_id'] ) && ! isset( $_REQUEST['recurring_payment_id'] ) ) { - return false; - } + $sub->status = MeprSubscription::$cancelled_str; + $sub->store(); - $subscr_id = ( isset( $_REQUEST['subscr_id'] ) ) ? $_REQUEST['subscr_id'] : $_REQUEST['recurring_payment_id']; - $sub = MeprSubscription::get_one_by_subscr_id( $subscr_id ); + if (isset($_REQUEST['expire'])) { + $sub->limit_reached_actions(); + } - if ( ! $sub ) { - return false; - } + if (! isset($_REQUEST['silent']) || ( $_REQUEST['silent'] == false )) { + MeprUtils::send_cancelled_sub_notices($sub); + } - // Seriously ... if sub was already cancelled what are we doing here? - if ( $sub->status == MeprSubscription::$cancelled_str ) { - return $sub; + return $sub; } - $sub->status = MeprSubscription::$cancelled_str; - $sub->store(); + /** + * This method is only used when signing up using Smart Buttons + * + * @param MeprTransaction $txn + * @throws Exception + * @throws MeprGatewayException + */ + public function process_signup_form($txn) + { + if (isset($_POST['smart-payment-button']) && $_POST['smart-payment-button']) { + $order_bump_product_ids = isset($_POST['mepr_order_bumps']) && is_array($_POST['mepr_order_bumps']) ? array_map('intval', $_POST['mepr_order_bumps']) : []; + $order_bump_products = MeprCheckoutCtrl::get_order_bump_products($txn->product_id, $order_bump_product_ids); - if ( isset( $_REQUEST['expire'] ) ) { - $sub->limit_reached_actions(); - } + $order_bumps = $this->process_order($txn, $order_bump_products); + + $data = $this->setup_payment_with_paypal_commerce($txn, $order_bumps, true); - if ( ! isset( $_REQUEST['silent'] ) || ( $_REQUEST['silent'] == false ) ) { - MeprUtils::send_cancelled_sub_notices( $sub ); + MeprHooks::do_action('mepr-signup', $txn); + + wp_send_json_success($data); + } } - return $sub; - } + /** + * This gets called on the 'init' hook when the signup form is processed ... + * this is in place so that payment solutions like paypal can redirect + * before any content is rendered. + * + * This method is called when the Single Page Checkout option is disabled. When the option is enabled, + * process_payment_form handles the processing. + * + * @param MeprTransaction $txn + * @throws MeprGatewayException + * @throws Exception + */ + public function display_payment_page($txn) + { + $order_bump_product_ids = isset($_GET['obs']) && is_array($_GET['obs']) ? array_map('intval', $_GET['obs']) : []; + $order_bump_products = MeprCheckoutCtrl::get_order_bump_products($txn->product_id, $order_bump_product_ids); + + $order_bumps = $this->process_order($txn, $order_bump_products); + + $this->setup_payment_with_paypal_commerce($txn, $order_bumps); + } + + /** + * This gets called on wp_enqueue_script and enqueues a set of + * scripts for use on the page containing the payment form + */ + public function enqueue_payment_form_scripts() + { + if (wp_script_is('mepr-paypalcommerce-form', 'enqueued')) { + return; + } - /** - * This method is only used when signing up using Smart Buttons - * - * @param MeprTransaction $txn - * @throws Exception - * @throws MeprGatewayException - */ - public function process_signup_form( $txn ) { - if(isset($_POST['smart-payment-button']) && $_POST['smart-payment-button']) { - $order_bump_product_ids = isset($_POST['mepr_order_bumps']) && is_array($_POST['mepr_order_bumps']) ? array_map('intval', $_POST['mepr_order_bumps']) : []; - $order_bump_products = MeprCheckoutCtrl::get_order_bump_products($txn->product_id, $order_bump_product_ids); + if ($this->is_test_mode()) { + $client_id = $this->settings->test_client_id; + } else { + $client_id = $this->settings->live_client_id; + } + $mepr_options = MeprOptions::fetch(); + $currency_code = strtoupper($mepr_options->currency_code); + if ($this->settings->enable_smart_button == 'on') { + wp_enqueue_script('paypal-sdk-js', 'https://www.paypal.com/sdk/js?vault=true&enable-funding=venmo¤cy=' . $currency_code . '&client-id=' . $client_id, [], null, true); + wp_enqueue_script('mepr-paypalcommerce-form', MEPR_GATEWAYS_URL . '/paypal/form.js', [ + 'paypal-sdk-js', + 'mepr-checkout-js', + 'jquery.payment', + ], MEPR_VERSION, true); + + wp_localize_script('mepr-paypalcommerce-form', 'MeprPayPalCommerceL10n', [ + 'ajax_url' => admin_url('admin-ajax.php'), + ]); + } + } - $order_bumps = $this->process_order($txn, $order_bump_products); + protected function get_paypal_order_object($pp_order_id) + { + $response = wp_remote_get($this->settings->rest_api_url . '/v2/checkout/orders/' . $pp_order_id, [ + 'headers' => [ + 'Content-Type' => 'application/json', + 'PayPal-Partner-Attribution-Id' => MeprPayPalConnectCtrl::PAYPAL_BN_CODE, + 'Authorization' => 'Basic ' . $this->get_pp_basic_auth_token(), + ], + ]); - $data = $this->setup_payment_with_paypal_commerce($txn, $order_bumps, true); + $response = wp_remote_retrieve_body($response); + $response = json_decode($response, true); - MeprHooks::do_action('mepr-signup', $txn); + if (!isset($response['purchase_units'])) { + $this->log($response); + } - wp_send_json_success($data); - } - } - - /** This gets called on the 'init' hook when the signup form is processed ... - * this is in place so that payment solutions like paypal can redirect - * before any content is rendered. - * - * This method is called when the Single Page Checkout option is disabled. When the option is enabled, - * process_payment_form handles the processing. - * - * @param MeprTransaction $txn - * @throws MeprGatewayException - * @throws Exception - */ - public function display_payment_page( $txn ) { - $order_bump_product_ids = isset($_GET['obs']) && is_array($_GET['obs']) ? array_map('intval', $_GET['obs']) : []; - $order_bump_products = MeprCheckoutCtrl::get_order_bump_products($txn->product_id, $order_bump_product_ids); - - $order_bumps = $this->process_order($txn, $order_bump_products); - - $this->setup_payment_with_paypal_commerce($txn, $order_bumps); - } - - /** This gets called on wp_enqueue_script and enqueues a set of - * scripts for use on the page containing the payment form - */ - public function enqueue_payment_form_scripts() { - if ( wp_script_is( 'mepr-paypalcommerce-form', 'enqueued' ) ) { - return; + return $response; } - if ( $this->is_test_mode() ) { - $client_id = $this->settings->test_client_id; - } else { - $client_id = $this->settings->live_client_id; - } - $mepr_options = MeprOptions::fetch(); - $currency_code = strtoupper( $mepr_options->currency_code ); - if ( $this->settings->enable_smart_button == 'on' ) { - wp_enqueue_script( 'paypal-sdk-js', 'https://www.paypal.com/sdk/js?vault=true&enable-funding=venmo¤cy=' . $currency_code . '&client-id=' . $client_id, array(), null, true ); - wp_enqueue_script( 'mepr-paypalcommerce-form', MEPR_GATEWAYS_URL . '/paypal/form.js', array( - 'paypal-sdk-js', - 'mepr-checkout-js', - 'jquery.payment' - ), MEPR_VERSION, true ); - - wp_localize_script('mepr-paypalcommerce-form', 'MeprPayPalCommerceL10n', [ - 'ajax_url' => admin_url('admin-ajax.php'), - ]); - } - } - - protected function get_paypal_order_object( $pp_order_id ) { - $response = wp_remote_get( $this->settings->rest_api_url . '/v2/checkout/orders/' . $pp_order_id, [ - 'headers' => [ - 'Content-Type' => 'application/json', - 'PayPal-Partner-Attribution-Id' => MeprPayPalConnectCtrl::PAYPAL_BN_CODE, - 'Authorization' => 'Basic ' . $this->get_pp_basic_auth_token(), - ] - ] ); - - $response = wp_remote_retrieve_body( $response ); - $response = json_decode( $response, true ); - - if (!isset($response['purchase_units'])) { - $this->log($response); + public function get_pp_basic_auth_token() + { + if ($this->is_test_mode()) { + return base64_encode($this->settings->test_client_id . ':' . $this->settings->test_client_secret); + } else { + return base64_encode($this->settings->live_client_id . ':' . $this->settings->live_client_secret); + } } - return $response; - } + public function get_paypal_subscription_transactions($pp_subscription_id, $start_date = null, $end_date = null) + { + if (empty($start_date) && empty($end_date)) { + $start_date = new DateTime(); + $end_date = new DateTime(); + $end_date->add(new DateInterval('P1D')); + $start_date->sub(new DateInterval('P1D')); + } + $time = 'start_time=' . $start_date->format('Y-m-d') . 'T00:00:00.90Z&end_time=' . $end_date->format('Y-m-d') . 'T23:59:59.90Z'; + + $this->log($this->settings->rest_api_url . '/v1/billing/subscriptions/' . $pp_subscription_id . '/transactions?' . $time); + $response = wp_remote_get($this->settings->rest_api_url . '/v1/billing/subscriptions/' . $pp_subscription_id . '/transactions?' . $time, [ + 'headers' => [ + 'Content-Type' => 'application/json', + 'PayPal-Partner-Attribution-Id' => MeprPayPalConnectCtrl::PAYPAL_BN_CODE, + 'Authorization' => 'Basic ' . $this->get_pp_basic_auth_token(), + ], + ]); - public function get_pp_basic_auth_token() { - if ( $this->is_test_mode() ) { - return base64_encode( $this->settings->test_client_id . ':' . $this->settings->test_client_secret ); - } else { - return base64_encode( $this->settings->live_client_id . ':' . $this->settings->live_client_secret ); - } - } - - public function get_paypal_subscription_transactions( $pp_subscription_id, $start_date = null, $end_date = null) { - if (empty($start_date) && empty($end_date)) { - $start_date = new DateTime(); - $end_date = new DateTime(); - $end_date->add(new DateInterval('P1D')); - $start_date->sub(new DateInterval('P1D')); - } - $time = 'start_time=' . $start_date->format( 'Y-m-d' ) . 'T00:00:00.90Z&end_time=' . $end_date->format( 'Y-m-d' ) . 'T23:59:59.90Z'; - - $this->log( $this->settings->rest_api_url . '/v1/billing/subscriptions/' . $pp_subscription_id . '/transactions?' . $time ); - $response = wp_remote_get( $this->settings->rest_api_url . '/v1/billing/subscriptions/' . $pp_subscription_id . '/transactions?' . $time, [ - 'headers' => [ - 'Content-Type' => 'application/json', - 'PayPal-Partner-Attribution-Id' => MeprPayPalConnectCtrl::PAYPAL_BN_CODE, - 'Authorization' => 'Basic ' . $this->get_pp_basic_auth_token(), - ] - ] ); - - $response = wp_remote_retrieve_body( $response ); - $response = json_decode( $response, true ); - - return $response; - } - - public function get_paypal_subscription_object( $pp_subscription_id ) { - $response = wp_remote_get( $this->settings->rest_api_url . '/v1/billing/subscriptions/' . $pp_subscription_id, [ - 'headers' => [ - 'Content-Type' => 'application/json', - 'PayPal-Partner-Attribution-Id' => MeprPayPalConnectCtrl::PAYPAL_BN_CODE, - 'Authorization' => 'Basic ' . $this->get_pp_basic_auth_token(), - ] - ] ); - - $response = wp_remote_retrieve_body( $response ); - $response = json_decode( $response, true ); - - return $response; - } - - /** - * @param $pp_payment_id - * - * @return array|mixed|string|WP_Error - */ - protected function get_paypal_sale_payment_object( $pp_payment_id ) { - $response = wp_remote_get( $this->settings->rest_api_url . '/v2/payments/captures/' . $pp_payment_id, [ - 'headers' => [ - 'Content-Type' => 'application/json', - 'PayPal-Partner-Attribution-Id' => MeprPayPalConnectCtrl::PAYPAL_BN_CODE, - 'Authorization' => 'Basic ' . $this->get_pp_basic_auth_token(), - ] - ] ); - - $response = wp_remote_retrieve_body( $response ); - $response = json_decode( $response, true ); - - return $response; - } - - /** - * @param $method_id - * @param bool $sandbox - * @param bool $onboarding - * @return bool|string - */ - public static function get_paypal_connect_url( $method_id, $sandbox = false, $onboarding = false ) { - $base_return_url = admin_url( 'admin.php?page=memberpress-account-login&paypal-connect=1&method_id=' . $method_id, false ); - - if($onboarding) { - $base_return_url = add_query_arg([ - 'onboarding' => 'true' - ], $base_return_url); + $response = wp_remote_retrieve_body($response); + $response = json_decode($response, true); + + return $response; } - $error_url = add_query_arg( array( - 'mepr-action' => 'error' - ), $base_return_url ); + public function get_paypal_subscription_object($pp_subscription_id) + { + $response = wp_remote_get($this->settings->rest_api_url . '/v1/billing/subscriptions/' . $pp_subscription_id, [ + 'headers' => [ + 'Content-Type' => 'application/json', + 'PayPal-Partner-Attribution-Id' => MeprPayPalConnectCtrl::PAYPAL_BN_CODE, + 'Authorization' => 'Basic ' . $this->get_pp_basic_auth_token(), + ], + ]); + + $response = wp_remote_retrieve_body($response); + $response = json_decode($response, true); + + return $response; + } + + /** + * @param $pp_payment_id + * + * @return array|mixed|string|WP_Error + */ + protected function get_paypal_sale_payment_object($pp_payment_id) + { + $response = wp_remote_get($this->settings->rest_api_url . '/v2/payments/captures/' . $pp_payment_id, [ + 'headers' => [ + 'Content-Type' => 'application/json', + 'PayPal-Partner-Attribution-Id' => MeprPayPalConnectCtrl::PAYPAL_BN_CODE, + 'Authorization' => 'Basic ' . $this->get_pp_basic_auth_token(), + ], + ]); + + $response = wp_remote_retrieve_body($response); + $response = json_decode($response, true); - if ( $sandbox ) { - $base_return_url = add_query_arg( array( - 'sandbox' => '1' - ), $base_return_url ); + return $response; } - $site_uuid = get_option( 'mepr_authenticator_site_uuid' ); + /** + * @param $method_id + * @param boolean $sandbox + * @param boolean $onboarding + * @return boolean|string + */ + public static function get_paypal_connect_url($method_id, $sandbox = false, $onboarding = false) + { + $base_return_url = admin_url('admin.php?page=memberpress-account-login&paypal-connect=1&method_id=' . $method_id, false); - if ( empty( $site_uuid ) ) { - return false; - } + if ($onboarding) { + $base_return_url = add_query_arg([ + 'onboarding' => 'true', + ], $base_return_url); + } - $mepr_options = MeprOptions::fetch(); - $pm = new self(); - $pm->load( array( 'id' => $method_id ) ); - - $payload = array( - 'method_id' => $pm->id, - 'site_uuid' => $site_uuid, - 'user_uuid' => get_option( 'mepr_authenticator_user_uuid' ), - 'return_url' => $base_return_url, - 'error_url' => $error_url, - 'webhook_url' => $pm->notify_url( 'whk' ), - 'service_webhook_url' => $pm->notify_url( 'paypal-service-whk' ), - 'mp_version' => MEPR_VERSION - ); - - $jwt = MeprAuthenticatorCtrl::generate_jwt( $payload ); - - if ( $sandbox ) { - $service_url = MEPR_PAYPAL_SERVICE_URL . "/sandbox/onboarding/"; - } else { - $service_url = MEPR_PAYPAL_SERVICE_URL . "/onboarding/"; - } + $error_url = add_query_arg([ + 'mepr-action' => 'error', + ], $base_return_url); - return add_query_arg( [ - 'site_uuid' => $site_uuid, - 'method_id' => $method_id, - 'jwt' => $jwt, - ], $service_url ); - } - - /** - * Set up the PayPal transaction - * - * Returns an array with the order/subscription ID and the transaction ID if $return_the_object is true, - * otherwise redirects to PayPal. - * - * @param MeprTransaction $txn The main transaction for the product being purchased - * @param MeprTransaction[] $order_bumps Additional transactions added as order bumps - * @param bool $return_the_object If true, returns an array for the Smart Buttons request, otherwise - * redirects to PayPal - * @return array - * @throws MeprGatewayException - */ - public function setup_payment_with_paypal_commerce($txn, array $order_bumps = array(), $return_the_object = false) { - $mepr_options = MeprOptions::fetch(); - $currency_code = strtoupper($mepr_options->currency_code); - $prd = $txn->product(); - $sub = null; - - if(empty($prd->ID)) { - throw new MeprGatewayException(__('Product not found', 'memberpress')); - } + if ($sandbox) { + $base_return_url = add_query_arg([ + 'sandbox' => '1', + ], $base_return_url); + } + + $site_uuid = get_option('mepr_authenticator_site_uuid'); + + if (empty($site_uuid)) { + return false; + } - $transactions = array_merge([$txn], $order_bumps); + $mepr_options = MeprOptions::fetch(); + $pm = new self(); + $pm->load(['id' => $method_id]); + + $payload = [ + 'method_id' => $pm->id, + 'site_uuid' => $site_uuid, + 'user_uuid' => get_option('mepr_authenticator_user_uuid'), + 'return_url' => $base_return_url, + 'error_url' => $error_url, + 'webhook_url' => $pm->notify_url('whk'), + 'service_webhook_url' => $pm->notify_url('paypal-service-whk'), + 'mp_version' => MEPR_VERSION, + ]; - foreach($transactions as $transaction) { - if(!$transaction->is_one_time_payment()) { - $subscription = $transaction->subscription(); + $jwt = MeprAuthenticatorCtrl::generate_jwt($payload); - if(!($subscription instanceof MeprSubscription)) { - throw new MeprGatewayException(__('Subscription not found', 'memberpress')); + if ($sandbox) { + $service_url = MEPR_PAYPAL_SERVICE_URL . '/sandbox/onboarding/'; + } else { + $service_url = MEPR_PAYPAL_SERVICE_URL . '/onboarding/'; } - if($sub instanceof MeprSubscription) { - throw new MeprGatewayException(__('Multiple subscriptions are not supported', 'memberpress')); + return add_query_arg([ + 'site_uuid' => $site_uuid, + 'method_id' => $method_id, + 'jwt' => $jwt, + ], $service_url); + } + + /** + * Set up the PayPal transaction + * + * Returns an array with the order/subscription ID and the transaction ID if $return_the_object is true, + * otherwise redirects to PayPal. + * + * @param MeprTransaction $txn The main transaction for the product being purchased + * @param MeprTransaction[] $order_bumps Additional transactions added as order bumps + * @param boolean $return_the_object If true, returns an array for the Smart Buttons request, otherwise + * redirects to PayPal + * @return array + * @throws MeprGatewayException + */ + public function setup_payment_with_paypal_commerce($txn, array $order_bumps = [], $return_the_object = false) + { + $mepr_options = MeprOptions::fetch(); + $currency_code = strtoupper($mepr_options->currency_code); + $prd = $txn->product(); + $sub = null; + + if (empty($prd->ID)) { + throw new MeprGatewayException(__('Product not found', 'memberpress')); } - $sub = $subscription; - } + $transactions = array_merge([$txn], $order_bumps); - $transaction->update_meta('is_paypal_commerce', true); - } + foreach ($transactions as $transaction) { + if (!$transaction->is_one_time_payment()) { + $subscription = $transaction->subscription(); + + if (!($subscription instanceof MeprSubscription)) { + throw new MeprGatewayException(__('Subscription not found', 'memberpress')); + } - if($sub instanceof MeprSubscription) { - // Presence of a subscription means we need to use the Subscriptions API - $has_trial = $sub->trial && $sub->trial_days > 0; - $trial_days = $has_trial ? $sub->trial_days : 0; - $trial_total = $has_trial ? (float) $sub->trial_total : 0.00; - $trial_tax_amount = $has_trial ? (float) $sub->trial_tax_amount : 0.00; - $convert_to_trial = false; - $skip_taxes = false; - - foreach($transactions as $transaction) { - if(!$transaction->is_payment_required()) { - continue; - } - elseif($transaction->is_one_time_payment()) { - $trial_total += (float) $transaction->total; - $trial_tax_amount += (float) $transaction->tax_amount; - - if(!$has_trial) { - $convert_to_trial = true; - } - } - - if($txn->tax_rate != $transaction->tax_rate) { - // PayPal doesn't let us have a different tax rate for an order bump, so if an order bump has a different - // tax rate, we will skip using taxes. - $skip_taxes = true; - } - } - - // If there is no trial period and there is an order bump, set the trial days to cover one payment cycle and - // add the first subscription payment to the trial amount - if($convert_to_trial) { - $now = new DateTimeImmutable('now'); - $end = $now->modify(sprintf('+%d %s', $sub->period, $sub->period_type)); - $trial_days = $end->diff($now)->format('%a'); - $trial_total += (float) $sub->total; - $trial_tax_amount += (float) $sub->tax_amount; - } - - if(!$skip_taxes && get_option('mepr_calculate_taxes') && $mepr_options->attr('tax_calc_type') != 'inclusive') { - $trial_amount = $trial_total - $trial_tax_amount; - } - else { - $trial_amount = $trial_total; - } - - $pp_plan_id = $this->get_pp_plan_id($sub, $has_trial || $convert_to_trial, $trial_days, $trial_amount, $skip_taxes); - $pp_subscription = $this->get_pp_subscription($pp_plan_id, $txn, $sub, $return_the_object, $skip_taxes); - $sub->subscr_id = $pp_subscription['id']; - $sub->store(); - - if(isset($pp_subscription['links']) && is_array($pp_subscription['links'])) { - foreach($pp_subscription['links'] as $link) { - if(isset($link['rel']) && $link['rel'] == 'approve' && isset($link['href'])) { - if($return_the_object) { - return [ - 'id' => $pp_subscription['id'], - 'txn_id' => $txn->id, - ]; + if ($sub instanceof MeprSubscription) { + throw new MeprGatewayException(__('Multiple subscriptions are not supported', 'memberpress')); + } + + $sub = $subscription; } - $this->log($link['href']); - MeprUtils::wp_redirect($link['href']); - } + $transaction->update_meta('is_paypal_commerce', true); } - } - throw new MeprGatewayException(__('Could not create PayPal subscription', 'memberpress')); - } - else { - $items = []; - $amount = 0.00; - $tax = 0.00; - $total = 0.00; - - foreach($transactions as $transaction) { - $product = $transaction->product(); - - if(empty($product->ID)) { - throw new MeprGatewayException(__('Product not found', 'memberpress')); - } - - $items[] = [ - 'name' => $product->post_title, - 'unit_amount' => [ - 'currency_code' => $currency_code, - 'value' => (string) $transaction->amount, - ], - 'tax' => [ - 'currency_code' => $currency_code, - 'value' => (string) $transaction->tax_amount, - ], - 'quantity' => 1, - ]; + if ($sub instanceof MeprSubscription) { + // Presence of a subscription means we need to use the Subscriptions API + $has_trial = $sub->trial && $sub->trial_days > 0; + $trial_days = $has_trial ? $sub->trial_days : 0; + $trial_total = $has_trial ? (float) $sub->trial_total : 0.00; + $trial_tax_amount = $has_trial ? (float) $sub->trial_tax_amount : 0.00; + $convert_to_trial = false; + $skip_taxes = false; + + foreach ($transactions as $transaction) { + if (!$transaction->is_payment_required()) { + continue; + } elseif ($transaction->is_one_time_payment()) { + $trial_total += (float) $transaction->total; + $trial_tax_amount += (float) $transaction->tax_amount; + + if (!$has_trial) { + $convert_to_trial = true; + } + } - $amount += (float) $transaction->amount; - $tax += (float) $transaction->tax_amount; - $total += (float) $transaction->total; - } - - $payload = [ - 'intent' => 'CAPTURE', - 'purchase_units' => [ - [ - 'custom_id' => $txn->id, - 'description' => $prd->post_title, - 'items' => $items, - 'amount' => [ - 'currency_code' => $currency_code, - 'value' => (string) $total, - 'breakdown' => [ - 'item_total' => [ - 'currency_code' => $currency_code, - 'value' => (string) $amount, + if ($txn->tax_rate != $transaction->tax_rate) { + // PayPal doesn't let us have a different tax rate for an order bump, so if an order bump has a different + // tax rate, we will skip using taxes. + $skip_taxes = true; + } + } + + // If there is no trial period and there is an order bump, set the trial days to cover one payment cycle and + // add the first subscription payment to the trial amount + if ($convert_to_trial) { + $now = new DateTimeImmutable('now'); + $end = $now->modify(sprintf('+%d %s', $sub->period, $sub->period_type)); + $trial_days = $end->diff($now)->format('%a'); + $trial_total += (float) $sub->total; + $trial_tax_amount += (float) $sub->tax_amount; + } + + if (!$skip_taxes && get_option('mepr_calculate_taxes') && $mepr_options->attr('tax_calc_type') != 'inclusive') { + $trial_amount = $trial_total - $trial_tax_amount; + } else { + $trial_amount = $trial_total; + } + + $pp_plan_id = $this->get_pp_plan_id($sub, $has_trial || $convert_to_trial, $trial_days, $trial_amount, $skip_taxes); + $pp_subscription = $this->get_pp_subscription($pp_plan_id, $txn, $sub, $return_the_object, $skip_taxes); + $sub->subscr_id = $pp_subscription['id']; + $sub->store(); + + if (isset($pp_subscription['links']) && is_array($pp_subscription['links'])) { + foreach ($pp_subscription['links'] as $link) { + if (isset($link['rel']) && $link['rel'] == 'approve' && isset($link['href'])) { + if ($return_the_object) { + return [ + 'id' => $pp_subscription['id'], + 'txn_id' => $txn->id, + ]; + } + + $this->log($link['href']); + MeprUtils::wp_redirect($link['href']); + } + } + } + + throw new MeprGatewayException(__('Could not create PayPal subscription', 'memberpress')); + } else { + $items = []; + $amount = 0.00; + $tax = 0.00; + $total = 0.00; + + foreach ($transactions as $transaction) { + $product = $transaction->product(); + + if (empty($product->ID)) { + throw new MeprGatewayException(__('Product not found', 'memberpress')); + } + + $items[] = [ + 'name' => $product->post_title, + 'unit_amount' => [ + 'currency_code' => $currency_code, + 'value' => (string) $transaction->amount, + ], + 'tax' => [ + 'currency_code' => $currency_code, + 'value' => (string) $transaction->tax_amount, + ], + 'quantity' => 1, + ]; + + $amount += (float) $transaction->amount; + $tax += (float) $transaction->tax_amount; + $total += (float) $transaction->total; + } + + $payload = [ + 'intent' => 'CAPTURE', + 'purchase_units' => [ + [ + 'custom_id' => $txn->id, + 'description' => $prd->post_title, + 'items' => $items, + 'amount' => [ + 'currency_code' => $currency_code, + 'value' => (string) $total, + 'breakdown' => [ + 'item_total' => [ + 'currency_code' => $currency_code, + 'value' => (string) $amount, + ], + 'tax_total' => [ + 'currency_code' => $currency_code, + 'value' => (string) $tax, + ], + ], + ], + ], ], - 'tax_total' => [ - 'currency_code' => $currency_code, - 'value' => (string) $tax, + 'application_context' => [ + 'shipping_preference' => 'NO_SHIPPING', + 'user_action' => 'PAY_NOW', + 'return_url' => add_query_arg(['txn_id' => $txn->id], $this->notify_url('return')), + 'cancel_url' => $this->notify_url('cancel'), ], - ], - ], - ], - ], - 'application_context' => [ - 'shipping_preference' => 'NO_SHIPPING', - 'user_action' => 'PAY_NOW', - 'return_url' => add_query_arg(['txn_id' => $txn->id], $this->notify_url('return')), - 'cancel_url' => $this->notify_url('cancel'), - ], - ]; - - $payload = json_encode( MeprHooks::apply_filters('mepr_paypal_onetime_subscription_args', $payload, $txn), JSON_UNESCAPED_SLASHES ); - - $response = wp_remote_post($this->settings->rest_api_url . '/v2/checkout/orders', [ - 'body' => $payload, - 'headers' => [ - 'Content-Type' => 'application/json', - 'PayPal-Partner-Attribution-Id' => MeprPayPalConnectCtrl::PAYPAL_BN_CODE, - 'Authorization' => 'Basic ' . $this->get_pp_basic_auth_token() - ] - ]); - - $code = wp_remote_retrieve_response_code($response); - $response = json_decode(wp_remote_retrieve_body($response), true); - - $this->log($payload); - $this->log($response); - - if(($code < 200 || $code > 299) || !is_array($response) || !isset($response['links']) || !is_array($response['links'])) { - throw new MeprGatewayException(__('Could not create PayPal Order', 'memberpress')); - } - - foreach($response['links'] as $link) { - if(isset($link['rel']) && $link['rel'] == 'approve' && isset($link['href'])) { - if($return_the_object) { - return [ - 'id' => $response['id'], - 'txn_id' => $txn->id, ]; - } - MeprUtils::wp_redirect(esc_url_raw(add_query_arg('Return', 'TRUE', $link['href']))); + $payload = json_encode(MeprHooks::apply_filters('mepr_paypal_onetime_subscription_args', $payload, $txn), JSON_UNESCAPED_SLASHES); + + $response = wp_remote_post($this->settings->rest_api_url . '/v2/checkout/orders', [ + 'body' => $payload, + 'headers' => [ + 'Content-Type' => 'application/json', + 'PayPal-Partner-Attribution-Id' => MeprPayPalConnectCtrl::PAYPAL_BN_CODE, + 'Authorization' => 'Basic ' . $this->get_pp_basic_auth_token(), + ], + ]); + + $code = wp_remote_retrieve_response_code($response); + $response = json_decode(wp_remote_retrieve_body($response), true); + + $this->log($payload); + $this->log($response); + + if (($code < 200 || $code > 299) || !is_array($response) || !isset($response['links']) || !is_array($response['links'])) { + throw new MeprGatewayException(__('Could not create PayPal Order', 'memberpress')); + } + + foreach ($response['links'] as $link) { + if (isset($link['rel']) && $link['rel'] == 'approve' && isset($link['href'])) { + if ($return_the_object) { + return [ + 'id' => $response['id'], + 'txn_id' => $txn->id, + ]; + } + + MeprUtils::wp_redirect(esc_url_raw(add_query_arg('Return', 'TRUE', $link['href']))); + } + } + + throw new MeprGatewayException(__('Could not create PayPal Order', 'memberpress')); } - } + } - throw new MeprGatewayException(__('Could not create PayPal Order', 'memberpress')); + /** + * Returns the payment form and required fields for the gateway + */ + public function spc_payment_fields() + { + $payment_method = $this; + + return MeprView::get_string('/checkout/MeprPayPalCommerceGateway/payment_form', get_defined_vars()); } - } - - /** - * Returns the payment form and required fields for the gateway - */ - public function spc_payment_fields() { - $payment_method = $this; - - return MeprView::get_string( '/checkout/MeprPayPalCommerceGateway/payment_form', get_defined_vars() ); - } - - /** - * This gets called on the_content and just renders the payment form - */ - public function display_payment_form( $amount, $user, $product_id, $transaction_id ) { - $payment_method = $this; - ?> + + /** + * This gets called on the_content and just renders the payment form + */ + public function display_payment_form($amount, $user, $product_id, $transaction_id) + { + $payment_method = $this; + ?>
     
    - +
    - product_id, $order_bump_product_ids); - - $order_bumps = $this->process_order($txn, $order_bump_products); - - $this->setup_payment_with_paypal_commerce($txn, $order_bumps); - } - - /** - * @param MeprProduct $product - * - * @return string - * @throws MeprGatewayException - */ - public function get_pp_product_id( MeprProduct $product ) { - $args = [ - 'name' => $product->post_title, - 'type' => 'SERVICE', - ]; - - $meta_key = '_mepr_paypal_product_'; - $meta_key .= $this->id . '_'; - - if ( $this->is_test_mode() ) { - $meta_key .= 'test_'; + ID, $meta_key, true ); + /** + * Validates the payment form before a payment is processed + */ + public function validate_payment_form($errors) + { + // PayPal does this on their own form + } + + /** + * Process the payment form + * + * This method is called when the Single Page Checkout option is enabled. When the option is disabled, + * display_payment_page handles the processing. + * + * @param MeprTransaction $txn + * @throws MeprGatewayException + * @throws Exception + */ + public function process_payment_form($txn) + { + $order_bump_product_ids = isset($_POST['mepr_order_bumps']) && is_array($_POST['mepr_order_bumps']) ? array_map('intval', $_POST['mepr_order_bumps']) : []; + $order_bump_products = MeprCheckoutCtrl::get_order_bump_products($txn->product_id, $order_bump_product_ids); + + $order_bumps = $this->process_order($txn, $order_bump_products); + + $this->setup_payment_with_paypal_commerce($txn, $order_bumps); + } + + /** + * @param MeprProduct $product + * + * @return string + * @throws MeprGatewayException + */ + public function get_pp_product_id(MeprProduct $product) + { + $args = [ + 'name' => $product->post_title, + 'type' => 'SERVICE', + ]; - if ( empty( $pp_product_id ) ) { - // Create new pp product id - $responseRaw = wp_remote_post( $this->settings->rest_api_url . '/v1/catalogs/products', [ - 'headers' => [ - 'Authorization' => 'Basic ' . $this->get_pp_basic_auth_token(), - 'PayPal-Partner-Attribution-Id' => MeprPayPalConnectCtrl::PAYPAL_BN_CODE, - 'Content-Type' => 'application/json' - ], - 'body' => json_encode( $args ) - ] ); + $meta_key = '_mepr_paypal_product_'; + $meta_key .= $this->id . '_'; - $response = json_decode( wp_remote_retrieve_body( $responseRaw ), true ); + if ($this->is_test_mode()) { + $meta_key .= 'test_'; + } - if ( isset( $response['id'] ) ) { - $pp_product_id = $response['id']; - update_post_meta( $product->ID, $meta_key, $response['id'] ); + $meta_key .= implode('_', $args); + $meta_key = sanitize_title($meta_key); + $pp_product_id = get_post_meta($product->ID, $meta_key, true); + + if (empty($pp_product_id)) { + // Create new pp product id + $responseRaw = wp_remote_post($this->settings->rest_api_url . '/v1/catalogs/products', [ + 'headers' => [ + 'Authorization' => 'Basic ' . $this->get_pp_basic_auth_token(), + 'PayPal-Partner-Attribution-Id' => MeprPayPalConnectCtrl::PAYPAL_BN_CODE, + 'Content-Type' => 'application/json', + ], + 'body' => json_encode($args), + ]); - return $pp_product_id; - } else { + $response = json_decode(wp_remote_retrieve_body($responseRaw), true); - $this->log( $responseRaw ); - throw new MeprGatewayException( __( 'Could not create PayPal product', 'memberpress' ) ); - } - } + if (isset($response['id'])) { + $pp_product_id = $response['id']; + update_post_meta($product->ID, $meta_key, $response['id']); - return $pp_product_id; - } - - /** - * @param string $pp_plan_id - * @param MeprTransaction $txn - * @param MeprSubscription $sub - * @param bool $return_the_request - * @param bool $skip_taxes - * - * @return array|WP_Error - * @throws MeprGatewayException - */ - public function get_pp_subscription( $pp_plan_id, $txn, MeprSubscription $sub, $return_the_request = false, $skip_taxes = false ) { - $mepr_options = MeprOptions::fetch(); - - $args = [ - 'plan_id' => $pp_plan_id, - 'custom_id' => $sub->id, - "application_context" => [ - "shipping_preference" => "NO_SHIPPING", - "user_action" => "SUBSCRIBE_NOW", - "cancel_url" => $this->notify_url( 'cancel' ), - "return_url" => add_query_arg( [ 'txn_id' => $txn->id ], $this->notify_url( 'return' ) ), - ], - ]; - - if ( get_option( 'mepr_calculate_taxes' ) && !$skip_taxes ) { - if ( $mepr_options->attr( 'tax_calc_type' ) == 'inclusive' ) { - $tax_inclusive = true; - } else { - $tax_inclusive = false; - } - - $args['plan'] = [ - 'taxes' => [ - 'percentage' => $sub->tax_rate, - 'inclusive' => $tax_inclusive, - ], - ]; - } + return $pp_product_id; + } else { + $this->log($responseRaw); + throw new MeprGatewayException(__('Could not create PayPal product', 'memberpress')); + } + } - $args = MeprHooks::apply_filters( 'mepr_paypal_subcription_args', $args, $sub ); - $options = [ - 'headers' => [ - 'Content-Type' => 'application/json', - 'PayPal-Partner-Attribution-Id' => MeprPayPalConnectCtrl::PAYPAL_BN_CODE, - 'Authorization' => 'Basic ' . $this->get_pp_basic_auth_token(), - ], - 'body' => json_encode( $args, JSON_UNESCAPED_SLASHES ), - ]; - $response = wp_remote_post( $this->settings->rest_api_url . '/v1/billing/subscriptions', $options ); - - $raw = wp_remote_retrieve_body( $response ); - $response = json_decode( $raw, true ); - - if ( $return_the_request ) { - return $response; + return $pp_product_id; } - if ( isset( $response['id'] ) ) { - return $response; - } else { - $this->log( $options ); - $this->log( $raw ); - throw new MeprGatewayException( __( 'Could not create PayPal subscription', 'memberpress' ) ); - } - } - - /** - * @param MeprSubscription $sub - * @param bool $trial - * @param bool $trial_days - * @param float $trial_amount - * @param bool $skip_taxes - * @return string - * @throws MeprGatewayException - */ - public function get_pp_plan_id(MeprSubscription $sub, $trial = false, $trial_days = 0, $trial_amount = 0.00, $skip_taxes = false) { - $product = $sub->product(); - $mepr_options = MeprOptions::fetch(); - - if ( $mepr_options->attr( 'tax_calc_type' ) == 'inclusive' || $skip_taxes ) { - $tax_inclusive = true; - $amount = round( $sub->total, 2 ); - } else { - $tax_inclusive = false; - $amount = round( $sub->total - $sub->tax_amount, 2 ); - } + /** + * @param string $pp_plan_id + * @param MeprTransaction $txn + * @param MeprSubscription $sub + * @param boolean $return_the_request + * @param boolean $skip_taxes + * + * @return array|WP_Error + * @throws MeprGatewayException + */ + public function get_pp_subscription($pp_plan_id, $txn, MeprSubscription $sub, $return_the_request = false, $skip_taxes = false) + { + $mepr_options = MeprOptions::fetch(); + + $args = [ + 'plan_id' => $pp_plan_id, + 'custom_id' => $sub->id, + 'application_context' => [ + 'shipping_preference' => 'NO_SHIPPING', + 'user_action' => 'SUBSCRIBE_NOW', + 'cancel_url' => $this->notify_url('cancel'), + 'return_url' => add_query_arg(['txn_id' => $txn->id], $this->notify_url('return')), + ], + ]; - $interval = 'day'; + if (get_option('mepr_calculate_taxes') && !$skip_taxes) { + if ($mepr_options->attr('tax_calc_type') == 'inclusive') { + $tax_inclusive = true; + } else { + $tax_inclusive = false; + } - if ( $sub->period_type == 'months' ) { - $interval = 'month'; - } else if ( $sub->period_type == 'years' ) { - $interval = 'year'; - } else if ( $sub->period_type == 'weeks' ) { - $interval = 'week'; - } + $args['plan'] = [ + 'taxes' => [ + 'percentage' => $sub->tax_rate, + 'inclusive' => $tax_inclusive, + ], + ]; + } - $sub_period = $sub->period; + $args = MeprHooks::apply_filters('mepr_paypal_subcription_args', $args, $sub); + $options = [ + 'headers' => [ + 'Content-Type' => 'application/json', + 'PayPal-Partner-Attribution-Id' => MeprPayPalConnectCtrl::PAYPAL_BN_CODE, + 'Authorization' => 'Basic ' . $this->get_pp_basic_auth_token(), + ], + 'body' => json_encode($args, JSON_UNESCAPED_SLASHES), + ]; + $response = wp_remote_post($this->settings->rest_api_url . '/v1/billing/subscriptions', $options); - if ( $interval == 'day' && $sub_period > 365 ) { - $sub_period = 365; //Truncate at 365 even for a leap year. - } + $raw = wp_remote_retrieve_body($response); + $response = json_decode($raw, true); + + if ($return_the_request) { + return $response; + } - if ( $trial_days > 365 ) { - $trial_days = 365; //Truncate at 365 even for a leap year. + if (isset($response['id'])) { + return $response; + } else { + $this->log($options); + $this->log($raw); + throw new MeprGatewayException(__('Could not create PayPal subscription', 'memberpress')); + } } - $args = array( - 'amount' => $amount, - 'method_id' => $this->id, - 'tax_rate' => $skip_taxes ? 'na' : $sub->tax_rate, - 'tax_inclusive' => $skip_taxes ? 'na' : ($tax_inclusive ? 'yes' : 'no'), - 'test' => $this->is_test_mode() ? 'test' : 'live', - 'interval' => $interval, - 'period' => $sub_period, - 'trial' => $trial ? 'yes' : 'no', - 'trial_total' => $trial_amount, - 'total_cycles' => $sub->limit_cycles_num, - 'trial_days' => $trial_days, - 'interval_count' => $sub_period, - 'currency' => $mepr_options->currency_code, - 'memberpress_product_id' => $product->ID, - ); - - $plan_meta_key = '_mepr_paypal_plan_' . implode( '_', $args ); - $plan_id = get_post_meta( $product->ID, $plan_meta_key, true ); - - if ( empty( $plan_id ) ) { - $billing_cycles = []; - $sequence_number = 1; - - if( $trial ) { - $billing_cycles[] = [ - 'frequency' => [ - 'interval_unit' => 'DAY', - 'interval_count' => $args['trial_days'], - ], - 'pricing_scheme' => [ - 'fixed_price' => [ - 'currency_code' => $mepr_options->currency_code, - 'value' => (string) $trial_amount, - ] - ], - 'tenure_type' => 'TRIAL', - 'sequence' => $sequence_number, - 'total_cycles' => 1, - ]; + /** + * @param MeprSubscription $sub + * @param boolean $trial + * @param boolean $trial_days + * @param float $trial_amount + * @param boolean $skip_taxes + * @return string + * @throws MeprGatewayException + */ + public function get_pp_plan_id(MeprSubscription $sub, $trial = false, $trial_days = 0, $trial_amount = 0.00, $skip_taxes = false) + { + $product = $sub->product(); + $mepr_options = MeprOptions::fetch(); + + if ($mepr_options->attr('tax_calc_type') == 'inclusive' || $skip_taxes) { + $tax_inclusive = true; + $amount = round($sub->total, 2); + } else { + $tax_inclusive = false; + $amount = round($sub->total - $sub->tax_amount, 2); + } - $sequence_number++; - } - - $billing_cycles[] = [ - 'frequency' => [ - 'interval_unit' => strtoupper( $interval ), - 'interval_count' => intval( $sub_period ), - ], - 'pricing_scheme' => [ - 'fixed_price' => [ - 'currency_code' => $mepr_options->currency_code, - 'value' => (string) $amount, - ] - ], - 'tenure_type' => 'REGULAR', - 'sequence' => $sequence_number, - 'total_cycles' => ( $sub->limit_cycles && $sub->limit_cycles_num >= 1 ) ? $sub->limit_cycles_num : 0, - ]; - - $request_args = [ - 'product_id' => $this->get_pp_product_id( $product ), - 'name' => $product->post_title, - 'status' => 'ACTIVE', - 'billing_cycles' => $billing_cycles, - 'payment_preferences' => [ - 'auto_bill_outstanding' => true, - 'payment_failure_threshold' => 3, - ], - ]; - - if(!$skip_taxes) { - $request_args['taxes'] = [ - 'percentage' => $sub->tax_rate, - 'inclusive' => $tax_inclusive, + $interval = 'day'; + + if ($sub->period_type == 'months') { + $interval = 'month'; + } elseif ($sub->period_type == 'years') { + $interval = 'year'; + } elseif ($sub->period_type == 'weeks') { + $interval = 'week'; + } + + $sub_period = $sub->period; + + if ($interval == 'day' && $sub_period > 365) { + $sub_period = 365; // Truncate at 365 even for a leap year. + } + + if ($trial_days > 365) { + $trial_days = 365; // Truncate at 365 even for a leap year. + } + + $args = [ + 'amount' => $amount, + 'method_id' => $this->id, + 'tax_rate' => $skip_taxes ? 'na' : $sub->tax_rate, + 'tax_inclusive' => $skip_taxes ? 'na' : ($tax_inclusive ? 'yes' : 'no'), + 'test' => $this->is_test_mode() ? 'test' : 'live', + 'interval' => $interval, + 'period' => $sub_period, + 'trial' => $trial ? 'yes' : 'no', + 'trial_total' => $trial_amount, + 'total_cycles' => $sub->limit_cycles_num, + 'trial_days' => $trial_days, + 'interval_count' => $sub_period, + 'currency' => $mepr_options->currency_code, + 'memberpress_product_id' => $product->ID, ]; - } - - $request_args = MeprHooks::apply_filters( 'mepr_paypal_plan_args', $request_args, $sub ); - $this->log( 'Sending to pp plan' . print_r( $request_args, true ) ); - - $options = [ - 'headers' => [ - 'Content-Type' => 'application/json', - 'PayPal-Partner-Attribution-Id' => MeprPayPalConnectCtrl::PAYPAL_BN_CODE, - 'Authorization' => 'Basic ' . $this->get_pp_basic_auth_token(), - ], - 'body' => wp_json_encode( $request_args ), - ]; - $response = wp_remote_post( $this->settings->rest_api_url . '/v1/billing/plans', $options ); - $raw = wp_remote_retrieve_body( $response ); - $response = json_decode( $raw, true ); - - if ( isset( $response['id'] ) ) { - $this->log( $options ); - $plan_id = $response['id']; - update_post_meta( $product->ID, $plan_meta_key, $plan_id ); + + $plan_meta_key = '_mepr_paypal_plan_' . implode('_', $args); + $plan_id = get_post_meta($product->ID, $plan_meta_key, true); + + if (empty($plan_id)) { + $billing_cycles = []; + $sequence_number = 1; + + if ($trial) { + $billing_cycles[] = [ + 'frequency' => [ + 'interval_unit' => 'DAY', + 'interval_count' => $args['trial_days'], + ], + 'pricing_scheme' => [ + 'fixed_price' => [ + 'currency_code' => $mepr_options->currency_code, + 'value' => (string) $trial_amount, + ], + ], + 'tenure_type' => 'TRIAL', + 'sequence' => $sequence_number, + 'total_cycles' => 1, + ]; + + $sequence_number++; + } + + $billing_cycles[] = [ + 'frequency' => [ + 'interval_unit' => strtoupper($interval), + 'interval_count' => intval($sub_period), + ], + 'pricing_scheme' => [ + 'fixed_price' => [ + 'currency_code' => $mepr_options->currency_code, + 'value' => (string) $amount, + ], + ], + 'tenure_type' => 'REGULAR', + 'sequence' => $sequence_number, + 'total_cycles' => ( $sub->limit_cycles && $sub->limit_cycles_num >= 1 ) ? $sub->limit_cycles_num : 0, + ]; + + $request_args = [ + 'product_id' => $this->get_pp_product_id($product), + 'name' => $product->post_title, + 'status' => 'ACTIVE', + 'billing_cycles' => $billing_cycles, + 'payment_preferences' => [ + 'auto_bill_outstanding' => true, + 'payment_failure_threshold' => 3, + ], + ]; + + if (!$skip_taxes) { + $request_args['taxes'] = [ + 'percentage' => $sub->tax_rate, + 'inclusive' => $tax_inclusive, + ]; + } + + $request_args = MeprHooks::apply_filters('mepr_paypal_plan_args', $request_args, $sub); + $this->log('Sending to pp plan' . print_r($request_args, true)); + + $options = [ + 'headers' => [ + 'Content-Type' => 'application/json', + 'PayPal-Partner-Attribution-Id' => MeprPayPalConnectCtrl::PAYPAL_BN_CODE, + 'Authorization' => 'Basic ' . $this->get_pp_basic_auth_token(), + ], + 'body' => wp_json_encode($request_args), + ]; + $response = wp_remote_post($this->settings->rest_api_url . '/v1/billing/plans', $options); + $raw = wp_remote_retrieve_body($response); + $response = json_decode($raw, true); + + if (isset($response['id'])) { + $this->log($options); + $plan_id = $response['id']; + update_post_meta($product->ID, $plan_meta_key, $plan_id); + + return $plan_id; + } else { + $this->log($request_args); + $this->log($options); + $this->log($raw); + $this->log($response); + throw new MeprGatewayException(__('Could not create Plan', 'memberpress')); + } + } return $plan_id; - } else { - $this->log( $request_args ); - $this->log( $options ); - $this->log( $raw ); - $this->log( $response ); - throw new MeprGatewayException( __( 'Could not create Plan', 'memberpress' ) ); - } } - return $plan_id; - } + /** + * Displays the form for the given payment gateway on the MemberPress Options page + */ + public function display_options_form() + { + $mepr_options = MeprOptions::fetch(); + $pm = $this; + $upgraded_from_standard = false; - /** Displays the form for the given payment gateway on the MemberPress Options page */ - public function display_options_form() { - $mepr_options = MeprOptions::fetch(); - $pm = $this; - $upgraded_from_standard = false; + if (isset($mepr_options->legacy_integrations[ $this->id ])) { + $upgraded_from_standard = true; + } - if ( isset( $mepr_options->legacy_integrations[ $this->id ] ) ) { - $upgraded_from_standard = true; - } + $debug = defined('WP_MEPR_DEBUG') && WP_MEPR_DEBUG === true; + $settings = $this->settings; + $buffer_settings = get_option('mepr_buff_integrations', []); - $debug = defined( 'WP_MEPR_DEBUG' ) && WP_MEPR_DEBUG === true; - $settings = $this->settings; - $buffer_settings = get_option( 'mepr_buff_integrations', [] ); + if (isset($buffer_settings[ $this->id ])) { + foreach (['test_merchant_id', 'live_merchant_id', 'test_email_confirmed', 'live_email_confirmed'] as $key) { + if (isset($buffer_settings[ $this->id ][ $key ])) { + $settings->{$key} = $buffer_settings[ $this->id ][ $key ]; + $mepr_options->integrations[ $this->id ][ $key ] = $buffer_settings[ $this->id ][ $key ]; + } + } + } - if ( isset( $buffer_settings[ $this->id ] ) ) { - foreach ( [ 'test_merchant_id', 'live_merchant_id', 'test_email_confirmed', 'live_email_confirmed' ] as $key ) { - if ( isset( $buffer_settings[ $this->id ][ $key ] ) ) { - $settings->{$key} = $buffer_settings[ $this->id ][ $key ]; - $mepr_options->integrations[ $this->id ][ $key ] = $buffer_settings[ $this->id ][ $key ]; + $test_client_id_str = "{$mepr_options->integrations_str}[{$this->id}][test_client_id]"; + $test_client_secret_str = "{$mepr_options->integrations_str}[{$this->id}][test_client_secret]"; + $live_client_id_str = "{$mepr_options->integrations_str}[{$this->id}][live_client_id]"; + $live_client_secret_str = "{$mepr_options->integrations_str}[{$this->id}][live_client_secret]"; + $test_webhook_id_str = "{$mepr_options->integrations_str}[{$this->id}][test_webhook_id]"; + $live_webhook_id_str = "{$mepr_options->integrations_str}[{$this->id}][live_webhook_id]"; + $test_merchant_id_str = "{$mepr_options->integrations_str}[{$this->id}][test_merchant_id]"; + $live_merchant_id_str = "{$mepr_options->integrations_str}[{$this->id}][live_merchant_id]"; + $test_email_confirmed_str = "{$mepr_options->integrations_str}[{$this->id}][test_email_confirmed]"; + $live_email_confirmed_str = "{$mepr_options->integrations_str}[{$this->id}][live_email_confirmed]"; + $enable_smart_button_str = "{$mepr_options->integrations_str}[{$this->id}][enable_smart_button]"; + $enable_paypal_standard_debug_email_str = "{$mepr_options->integrations_str}[{$this->id}][enable_paypal_standard_debug_email]"; + + $account_email = get_option('mepr_authenticator_account_email'); + $secret = get_option('mepr_authenticator_secret_token'); + $site_uuid = get_option('mepr_authenticator_site_uuid'); + $payment_id = $this->id; + $enable_smart_button = $settings->enable_smart_button == 'on'; + $enable_paypal_standard_debug_email = $settings->enable_paypal_standard_debug_email == 'on'; + if ($account_email && $secret && $site_uuid) { + $paypal_connect_url_sandbox = self::get_paypal_connect_url($this->id, true); + $paypal_connect_url = self::get_paypal_connect_url($this->id); + } else { + $memberpress_connect_url = MeprAuthenticatorCtrl::get_auth_connect_url(false, $this->id, [ + 'paypal_connect' => true, + 'method_id' => $this->id, + ]); } - } - } - $test_client_id_str = "{$mepr_options->integrations_str}[{$this->id}][test_client_id]"; - $test_client_secret_str = "{$mepr_options->integrations_str}[{$this->id}][test_client_secret]"; - $live_client_id_str = "{$mepr_options->integrations_str}[{$this->id}][live_client_id]"; - $live_client_secret_str = "{$mepr_options->integrations_str}[{$this->id}][live_client_secret]"; - $test_webhook_id_str = "{$mepr_options->integrations_str}[{$this->id}][test_webhook_id]"; - $live_webhook_id_str = "{$mepr_options->integrations_str}[{$this->id}][live_webhook_id]"; - $test_merchant_id_str = "{$mepr_options->integrations_str}[{$this->id}][test_merchant_id]"; - $live_merchant_id_str = "{$mepr_options->integrations_str}[{$this->id}][live_merchant_id]"; - $test_email_confirmed_str = "{$mepr_options->integrations_str}[{$this->id}][test_email_confirmed]"; - $live_email_confirmed_str = "{$mepr_options->integrations_str}[{$this->id}][live_email_confirmed]"; - $enable_smart_button_str = "{$mepr_options->integrations_str}[{$this->id}][enable_smart_button]"; - $enable_paypal_standard_debug_email_str = "{$mepr_options->integrations_str}[{$this->id}][enable_paypal_standard_debug_email]"; - - $account_email = get_option( 'mepr_authenticator_account_email' ); - $secret = get_option( 'mepr_authenticator_secret_token' ); - $site_uuid = get_option( 'mepr_authenticator_site_uuid' ); - $payment_id = $this->id; - $enable_smart_button = $settings->enable_smart_button == 'on'; - $enable_paypal_standard_debug_email = $settings->enable_paypal_standard_debug_email == 'on'; - if ( $account_email && $secret && $site_uuid ) { - $paypal_connect_url_sandbox = self::get_paypal_connect_url( $this->id, true ); - $paypal_connect_url = self::get_paypal_connect_url( $this->id ); - } else { - $memberpress_connect_url = MeprAuthenticatorCtrl::get_auth_connect_url( false, $this->id, [ - 'paypal_connect' => true, - 'method_id' => $this->id - ] ); + $base_return_url = add_query_arg( + [ + 'action' => 'mepr_paypal_connect_update_creds', + '_wpnonce' => wp_create_nonce('paypal-update-creds'), + ], + admin_url('admin-ajax.php') + ); + + $base_return_url_sandbox = add_query_arg( + [ + 'action' => 'mepr_paypal_connect_update_creds_sandbox', + '_wpnonce' => wp_create_nonce('paypal-update-creds'), + ], + admin_url('admin-ajax.php') + ); + $paypal_js_url = 'https://www.paypal.com/webapps/merchantboarding/js/lib/lightbox/partner.js'; + MeprView::render('/admin/gateways/paypal/connect-migrate-prompt', get_defined_vars()); } - $base_return_url = add_query_arg( array( - 'action' => 'mepr_paypal_connect_update_creds', - '_wpnonce' => wp_create_nonce( 'paypal-update-creds' ) - ), - admin_url( 'admin-ajax.php' ) - ); - - $base_return_url_sandbox = add_query_arg( array( - 'action' => 'mepr_paypal_connect_update_creds_sandbox', - '_wpnonce' => wp_create_nonce( 'paypal-update-creds' ) - ), - admin_url( 'admin-ajax.php' ) - ); - $paypal_js_url = 'https://www.paypal.com/webapps/merchantboarding/js/lib/lightbox/partner.js'; - MeprView::render( '/admin/gateways/paypal/connect-migrate-prompt', get_defined_vars() ); - } - - /** Validates the form for the given payment gateway on the MemberPress Options page */ - public function validate_options_form( $errors ) { - $mepr_options = MeprOptions::fetch(); - - return $errors; - } - - /** Displays the update account form on the subscription account page **/ - public function display_update_account_form( $sub_id, $errors = array(), $message = '' ) { - ?> -

    -
    ', '' ); ?>
    - log('IPN received'); - $this->log($_POST); - do_action('mepr_paypal_commerce_ipn_listener_preprocess'); - $this->email_status( "PayPal IPN Recieved\n" . MeprUtils::object_to_string( $_POST, true ) . "\n", $this->debug ); - - if ( $this->validate_ipn() ) { - $mepr_options = MeprOptions::fetch(); - - if ( ! isset( $mepr_options->legacy_integrations[ $this->id ] ) ) { - return false; - } + /** + * Validates the form for the given payment gateway on the MemberPress Options page + */ + public function validate_options_form($errors) + { + $mepr_options = MeprOptions::fetch(); - $standard_gateway = new MeprPayPalStandardGateway(); - $mepr_options->legacy_integrations[ $this->id ]['debug'] = $this->debug; - $standard_gateway->load( $mepr_options->legacy_integrations[ $this->id ] ); + return $errors; + } - return $standard_gateway->process_ipn(); + /** + * Displays the update account form on the subscription account page + **/ + public function display_update_account_form($sub_id, $errors = [], $message = '') + { + ?> +

    +
    ', ''); ?>
    + log('IPN received'); + $this->log($_POST); + do_action('mepr_paypal_commerce_ipn_listener_preprocess'); + $this->email_status("PayPal IPN Recieved\n" . MeprUtils::object_to_string($_POST, true) . "\n", $this->debug); - public function webhook_handler() { - $request = @file_get_contents( 'php://input' ); - $request = json_decode( $request, true ); - $this->log( 'Webhook received' ); - $this->log( $request ); + if ($this->validate_ipn()) { + $mepr_options = MeprOptions::fetch(); - if ( ! isset( $request['event_type'] ) ) { - return; - } + if (! isset($mepr_options->legacy_integrations[ $this->id ])) { + return false; + } - if ( $request['event_type'] == 'BILLING.SUBSCRIPTION.ACTIVATED' ) { - // Only free trials are handled here - if(isset($request['resource']['id'], $request['resource']['custom_id'], $request['resource']['billing_info']) && !isset($request['resource']['billing_info']['last_payment'])) { - $sub = new MeprSubscription((int) $request['resource']['custom_id']); + $standard_gateway = new MeprPayPalStandardGateway(); + $mepr_options->legacy_integrations[ $this->id ]['debug'] = $this->debug; + $standard_gateway->load($mepr_options->legacy_integrations[ $this->id ]); - if($sub->id > 0 && $sub->gateway == $this->id && $sub->trial && $sub->trial_days > 0 && $sub->trial_amount <= 0.00) { - // Check if this is a multi-item purchase - $txn = $sub->first_txn(); + return $standard_gateway->process_ipn(); + } - if($txn instanceof MeprTransaction) { - $order = $txn->order(); - $order_bump_transactions = $order instanceof MeprOrder ? MeprTransaction::get_all_by_order_id_and_gateway($order->id, $this->id, $txn->id) : []; + return false; + } - if($order instanceof MeprOrder && count($order_bump_transactions)) { - foreach($order_bump_transactions as $transaction) { - if($transaction->is_payment_required()) { - // If any order bump required payment, we don't want to record subscription creation here. - // It will be handled by the PAYMENT.SALE.COMPLETED webhook below. - return; - } - } + public function webhook_handler() + { + $request = @file_get_contents('php://input'); + $request = json_decode($request, true); + $this->log('Webhook received'); + $this->log($request); - // If we reach here, payment was not required for any order bump, create free transactions for any free product - foreach($order_bump_transactions as $transaction) { - if(!$transaction->is_payment_required()) { - MeprTransaction::create_free_transaction($transaction, false, sprintf('mi_%d_%s', $order->id, uniqid())); - } - } - } - } - - $this->handle_create_subscription($sub, $request['resource']['id']); - } - } - } elseif ( $request['event_type'] == 'BILLING.SUBSCRIPTION.CANCELLED' || $request['event_type'] == 'BILLING.SUBSCRIPTION.EXPIRED' ) { - $_REQUEST['subscr_id'] = $request['resource']['id']; - $this->record_cancel_subscription(); - } elseif ( $request['event_type'] == 'BILLING.SUBSCRIPTION.SUSPENDED' ) { - $_REQUEST['subscr_id'] = $request['resource']['id']; - $this->record_suspend_subscription(); - } elseif ( $request['event_type'] == 'PAYMENT.CAPTURE.DENIED' ) { - $_POST['txn_id'] = $request['resource']['id']; - $this->record_payment_failure(); - } elseif ( $request['event_type'] == 'PAYMENT.SALE.REFUNDED' ) { - $txn_num = $request['resource']['sale_id']; - $existing_txn = MeprTransaction::get_one_by_trans_num( $txn_num ); - $_POST['parent_txn_id'] = $existing_txn->id; - $this->record_refund(); - } elseif ( in_array( $request['event_type'], [ 'PAYMENT.CAPTURE.REFUNDED', 'PAYMENT.CAPTURE.REFUNDED' ] ) ) { - $links = $request['resource']['links']; - $txn_num = ''; - - foreach ( $links as $link ) { - if ( $link['rel'] == 'up' ) { - $href = explode( '/', $link['href'] ); - $txn_num = array_pop( $href ); - } - } - - $existing_txn = MeprTransaction::get_one_by_trans_num( $txn_num ); - $_POST['parent_txn_id'] = $existing_txn->id; - $this->record_refund(); - } elseif ( $request['event_type'] == 'PAYMENT.SALE.COMPLETED' ) { - $pp_payment = $this->get_paypal_sale_payment_object( $request['resource']['id'] ); - $resource = $request['resource']; - $this->log( 'Processing recurring payment' ); - $this->log( $pp_payment ); - $this->log( $resource ); - - if ( $pp_payment['status'] == 'COMPLETED' && isset( $pp_payment['custom_id'] ) ) { - $this->log( 'Payment confirmed' ); - - $_POST['txn_id'] = $pp_payment['id']; - $_POST['mc_gross'] = $resource['amount']['total']; - $_POST['payment_date'] = $resource['create_time']; - $_POST['subscr_id'] = $_POST['recurring_payment_id'] = $resource['billing_agreement_id']; - - // First see if the subscription has already been set up with the correct I- or S- number - $sub = MeprSubscription::get_one_by_subscr_id($resource['billing_agreement_id']); - - // If no $sub at this point it's safe to assume this is a new signup so let's get the $sub from the custom_id instead - if(!($sub instanceof MeprSubscription)) { - $sub = new MeprSubscription($pp_payment['custom_id']); - - if($sub->id > 0 && $sub->gateway == $this->id && ($txn = $sub->first_txn()) instanceof MeprTransaction && $txn->id > 0) { - // Check if this is a multi-item purchase - $order = $txn->order(); - $order_bump_transactions = $order instanceof MeprOrder ? MeprTransaction::get_all_by_order_id_and_gateway($order->id, $this->id, $txn->id) : []; + if (! isset($request['event_type'])) { + return; + } - if($order instanceof MeprOrder && count($order_bump_transactions)) { - if(!$order->is_complete() && !$order->is_processing()) { - $order->update_meta('processing', true); - $transactions = array_merge([$txn], $order_bump_transactions); - - foreach($transactions as $transaction) { - $trans_num = sprintf('mi_%d_%s', $order->id, uniqid()); - - if(!$transaction->is_payment_required()) { - MeprTransaction::create_free_transaction($transaction, false, $trans_num); - } - elseif($transaction->is_one_time_payment()) { - $this->handle_one_time_payment($transaction, $trans_num); - } - else { - $subscription = $txn->subscription(); - - if(!($subscription instanceof MeprSubscription)) { - continue; + if ($request['event_type'] == 'BILLING.SUBSCRIPTION.ACTIVATED') { + // Only free trials are handled here + if (isset($request['resource']['id'], $request['resource']['custom_id'], $request['resource']['billing_info']) && !isset($request['resource']['billing_info']['last_payment'])) { + $sub = new MeprSubscription((int) $request['resource']['custom_id']); + + if ($sub->id > 0 && $sub->gateway == $this->id && $sub->trial && $sub->trial_days > 0 && $sub->trial_amount <= 0.00) { + // Check if this is a multi-item purchase + $txn = $sub->first_txn(); + + if ($txn instanceof MeprTransaction) { + $order = $txn->order(); + $order_bump_transactions = $order instanceof MeprOrder ? MeprTransaction::get_all_by_order_id_and_gateway($order->id, $this->id, $txn->id) : []; + + if ($order instanceof MeprOrder && count($order_bump_transactions)) { + foreach ($order_bump_transactions as $transaction) { + if ($transaction->is_payment_required()) { + // If any order bump required payment, we don't want to record subscription creation here. + // It will be handled by the PAYMENT.SALE.COMPLETED webhook below. + return; + } + } + + // If we reach here, payment was not required for any order bump, create free transactions for any free product + foreach ($order_bump_transactions as $transaction) { + if (!$transaction->is_payment_required()) { + MeprTransaction::create_free_transaction($transaction, false, sprintf('mi_%d_%s', $order->id, uniqid())); + } + } + } } - $this->handle_create_subscription($subscription, $resource['billing_agreement_id']); + $this->handle_create_subscription($sub, $request['resource']['id']); + } + } + } elseif ($request['event_type'] == 'BILLING.SUBSCRIPTION.CANCELLED' || $request['event_type'] == 'BILLING.SUBSCRIPTION.EXPIRED') { + $_REQUEST['subscr_id'] = $request['resource']['id']; + $this->record_cancel_subscription(); + } elseif ($request['event_type'] == 'BILLING.SUBSCRIPTION.SUSPENDED') { + $_REQUEST['subscr_id'] = $request['resource']['id']; + $this->record_suspend_subscription(); + } elseif ($request['event_type'] == 'PAYMENT.CAPTURE.DENIED') { + $_POST['txn_id'] = $request['resource']['id']; + $this->record_payment_failure(); + } elseif ($request['event_type'] == 'PAYMENT.SALE.REFUNDED') { + $txn_num = $request['resource']['sale_id']; + $existing_txn = MeprTransaction::get_one_by_trans_num($txn_num); + $_POST['parent_txn_id'] = $existing_txn->id; + $this->record_refund(); + } elseif (in_array($request['event_type'], ['PAYMENT.CAPTURE.REFUNDED', 'PAYMENT.CAPTURE.REFUNDED'])) { + $links = $request['resource']['links']; + $txn_num = ''; + + foreach ($links as $link) { + if ($link['rel'] == 'up') { + $href = explode('/', $link['href']); + $txn_num = array_pop($href); + } + } - if($subscription->trial && $subscription->trial_days > 0) { - if($subscription->trial_total > 0) { - $_POST['mc_gross'] = $subscription->trial_total; - } - else { - continue; // Initial payment for a free trial with order bump, we don't want to record a subscription transaction here - } + $existing_txn = MeprTransaction::get_one_by_trans_num($txn_num); + $_POST['parent_txn_id'] = $existing_txn->id; + $this->record_refund(); + } elseif ($request['event_type'] == 'PAYMENT.SALE.COMPLETED') { + $pp_payment = $this->get_paypal_sale_payment_object($request['resource']['id']); + $resource = $request['resource']; + $this->log('Processing recurring payment'); + $this->log($pp_payment); + $this->log($resource); + + if ($pp_payment['status'] == 'COMPLETED' && isset($pp_payment['custom_id'])) { + $this->log('Payment confirmed'); + + $_POST['txn_id'] = $pp_payment['id']; + $_POST['mc_gross'] = $resource['amount']['total']; + $_POST['payment_date'] = $resource['create_time']; + $_POST['subscr_id'] = $_POST['recurring_payment_id'] = $resource['billing_agreement_id']; + + // First see if the subscription has already been set up with the correct I- or S- number + $sub = MeprSubscription::get_one_by_subscr_id($resource['billing_agreement_id']); + + // If no $sub at this point it's safe to assume this is a new signup so let's get the $sub from the custom_id instead + if (!($sub instanceof MeprSubscription)) { + $sub = new MeprSubscription($pp_payment['custom_id']); + + if ($sub->id > 0 && $sub->gateway == $this->id && ($txn = $sub->first_txn()) instanceof MeprTransaction && $txn->id > 0) { + // Check if this is a multi-item purchase + $order = $txn->order(); + $order_bump_transactions = $order instanceof MeprOrder ? MeprTransaction::get_all_by_order_id_and_gateway($order->id, $this->id, $txn->id) : []; + + if ($order instanceof MeprOrder && count($order_bump_transactions)) { + if (!$order->is_complete() && !$order->is_processing()) { + $order->update_meta('processing', true); + $transactions = array_merge([$txn], $order_bump_transactions); + + foreach ($transactions as $transaction) { + $trans_num = sprintf('mi_%d_%s', $order->id, uniqid()); + + if (!$transaction->is_payment_required()) { + MeprTransaction::create_free_transaction($transaction, false, $trans_num); + } elseif ($transaction->is_one_time_payment()) { + $this->handle_one_time_payment($transaction, $trans_num); + } else { + $subscription = $txn->subscription(); + + if (!($subscription instanceof MeprSubscription)) { + continue; + } + + $this->handle_create_subscription($subscription, $resource['billing_agreement_id']); + + if ($subscription->trial && $subscription->trial_days > 0) { + if ($subscription->trial_total > 0) { + $_POST['mc_gross'] = $subscription->trial_total; + } else { + continue; // Initial payment for a free trial with order bump, we don't want to record a subscription transaction here + } + } else { + $_POST['mc_gross'] = $subscription->total; + } + + $_POST['txn_id'] = $trans_num; + $_POST['mepr_order_id'] = $order->id; + + $this->record_subscription_payment(); + } + } + + $order->trans_num = $pp_payment['id']; + $order->status = MeprOrder::$complete_str; + $order->store(); + $order->delete_meta('processing'); + } + + return; + } } - else { - $_POST['mc_gross'] = $subscription->total; - } - - $_POST['txn_id'] = $trans_num; - $_POST['mepr_order_id'] = $order->id; + } + if ($sub instanceof MeprSubscription && $sub->id > 0) { $this->record_subscription_payment(); - } } - - $order->trans_num = $pp_payment['id']; - $order->status = MeprOrder::$complete_str; - $order->store(); - $order->delete_meta('processing'); - } - - return; } - } } - - if($sub instanceof MeprSubscription && $sub->id > 0) { - $this->record_subscription_payment(); - } - } - } - } - - /** Validates the payment form before a payment is processed */ - public function validate_update_account_form( $errors = array() ) { - // We'll have them update their cc info on paypal.com - } - - /** Actually pushes the account update to the payment processor */ - public function process_update_account_form( $sub_id ) { - // We'll have them update their cc info on paypal.com - } - - /** Returns boolean ... whether or not we should be sending in test mode or not */ - public function is_test_mode() { - if ( $this->is_paypal_connected() && ! $this->is_paypal_connected_live() ) { - return true; } - if ( $this->is_paypal_connected_live() ) { - return false; + /** + * Validates the payment form before a payment is processed + */ + public function validate_update_account_form($errors = []) + { + // We'll have them update their cc info on paypal.com } - return true; - } - - public function is_paypal_connected() { - return ! empty( $this->settings->test_client_id ); - } - - public function is_paypal_connected_live() { - return ! empty( $this->settings->live_client_id ); - } + /** + * Actually pushes the account update to the payment processor + */ + public function process_update_account_form($sub_id) + { + // We'll have them update their cc info on paypal.com + } - public function is_paypal_email_confirmed() { - return ! empty( $this->settings->test_email_confirmed ); - } + /** + * Returns boolean ... whether or not we should be sending in test mode or not + */ + public function is_test_mode() + { + if ($this->is_paypal_connected() && ! $this->is_paypal_connected_live()) { + return true; + } - public function is_paypal_email_confirmed_live() { - return ! empty( $this->settings->live_email_confirmed ); - } + if ($this->is_paypal_connected_live()) { + return false; + } - public function force_ssl() { - return false; // redirects off site where ssl is installed - } + return true; + } - /** - * Checks whether the user has a Paypal payment method that uses Paypal Connect - * - * @return boolean - */ - public static function has_method_with_connect_status( $status = 'connected' ) { - $mepr_options = MeprOptions::fetch(); - foreach ( $mepr_options->integrations as $integration ) { + public function is_paypal_connected() + { + return ! empty($this->settings->test_client_id); + } - if ( ! isset( $integration['gateway'] ) || 'MeprPayPalCommerceGateway' !== $integration['gateway'] ) { - continue; - } + public function is_paypal_connected_live() + { + return ! empty($this->settings->live_client_id); + } - return ! empty( $integration['test_client_id'] ) || ! empty( $integration['live_client_id'] ); + public function is_paypal_email_confirmed() + { + return ! empty($this->settings->test_email_confirmed); } - return false; - } - - private function update_paypal_payment_profile( $sub_id, $action = 'cancel' ) { - $action = strtolower( $action ); - $sub = new MeprSubscription( $sub_id ); - - $options = [ - 'headers' => [ - 'Content-Type' => 'application/json', - 'PayPal-Partner-Attribution-Id' => MeprPayPalConnectCtrl::PAYPAL_BN_CODE, - 'Authorization' => 'Basic ' . $this->get_pp_basic_auth_token(), - ], - 'body' => json_encode( [ - 'reason' => esc_html( __( 'On request', 'memberpress' ) ), - ], JSON_UNESCAPED_SLASHES ), - ]; - - if ( $action == 'reactivate' ) { - $action = 'activate'; + public function is_paypal_email_confirmed_live() + { + return ! empty($this->settings->live_email_confirmed); } - $this->log( $options ); - $endpoint = $this->settings->rest_api_url . '/v1/billing/subscriptions/' . $sub->subscr_id . '/' . $action; - $this->log( $endpoint ); + public function force_ssl() + { + return false; // redirects off site where ssl is installed + } - $response = wp_remote_post( $endpoint, $options ); - $response_code = wp_remote_retrieve_response_code( $response ); + /** + * Checks whether the user has a Paypal payment method that uses Paypal Connect + * + * @return boolean + */ + public static function has_method_with_connect_status($status = 'connected') + { + $mepr_options = MeprOptions::fetch(); + foreach ($mepr_options->integrations as $integration) { + if (! isset($integration['gateway']) || 'MeprPayPalCommerceGateway' !== $integration['gateway']) { + continue; + } - $this->log( $response_code ); + return ! empty($integration['test_client_id']) || ! empty($integration['live_client_id']); + } - if ( $response_code < 200 || $response_code >= 300 ) { - throw new MeprGatewayException( __( 'There was a problem, try logging in directly at PayPal to update the status of your recurring profile.', 'memberpress' ) ); + return false; } - $_REQUEST['recurring_payment_id'] = $sub->subscr_id; - } + private function update_paypal_payment_profile($sub_id, $action = 'cancel') + { + $action = strtolower($action); + $sub = new MeprSubscription($sub_id); - public function return_handler() { - $this->email_status( "Paypal Return \$_REQUEST:\n" . MeprUtils::object_to_string( $_REQUEST, true ) . "\n", $this->debug ); - $mepr_options = MeprOptions::fetch(); + $options = [ + 'headers' => [ + 'Content-Type' => 'application/json', + 'PayPal-Partner-Attribution-Id' => MeprPayPalConnectCtrl::PAYPAL_BN_CODE, + 'Authorization' => 'Basic ' . $this->get_pp_basic_auth_token(), + ], + 'body' => json_encode([ + 'reason' => esc_html(__('On request', 'memberpress')), + ], JSON_UNESCAPED_SLASHES), + ]; - $transaction_id = isset($_GET['txn_id']) ? (int) sanitize_text_field(wp_unslash($_GET['txn_id'])) : 0; - $txn = new MeprTransaction($transaction_id); + if ($action == 'reactivate') { + $action = 'activate'; + } - if(empty($txn->id)) { - return; - } + $this->log($options); + $endpoint = $this->settings->rest_api_url . '/v1/billing/subscriptions/' . $sub->subscr_id . '/' . $action; + $this->log($endpoint); - if(empty($txn->get_meta('is_paypal_commerce'))) { - if(!isset($mepr_options->legacy_integrations[$this->id])) { - return; - } + $response = wp_remote_post($endpoint, $options); + $response_code = wp_remote_retrieve_response_code($response); - $standard_gateway = new MeprPayPalStandardGateway(); - $standard_gateway->load($mepr_options->legacy_integrations[$this->id]); - $standard_gateway->return_handler(); + $this->log($response_code); - return; + if ($response_code < 200 || $response_code >= 300) { + throw new MeprGatewayException(__('There was a problem, try logging in directly at PayPal to update the status of your recurring profile.', 'memberpress')); + } + + $_REQUEST['recurring_payment_id'] = $sub->subscr_id; } - try { - $prd = $txn->product(); + public function return_handler() + { + $this->email_status("Paypal Return \$_REQUEST:\n" . MeprUtils::object_to_string($_REQUEST, true) . "\n", $this->debug); + $mepr_options = MeprOptions::fetch(); - $thankyou_page_args = [ - 'membership' => sanitize_title($prd->post_title), - 'membership_id' => $prd->ID, - 'transaction_id' => $txn->id, - ]; + $transaction_id = isset($_GET['txn_id']) ? (int) sanitize_text_field(wp_unslash($_GET['txn_id'])) : 0; + $txn = new MeprTransaction($transaction_id); - $subscription_id = isset($_GET['subscription_id']) ? sanitize_text_field(wp_unslash($_GET['subscription_id'])) : ''; + if (empty($txn->id)) { + return; + } - // Check if this is a multi-item purchase - $order = $txn->order(); - $order_bump_transactions = $order instanceof MeprOrder ? MeprTransaction::get_all_by_order_id_and_gateway($order->id, $this->id, $txn->id) : []; - $transactions = array_merge([$txn], $order_bump_transactions); + if (empty($txn->get_meta('is_paypal_commerce'))) { + if (!isset($mepr_options->legacy_integrations[$this->id])) { + return; + } - if(!empty($subscription_id)) { - // Return from a subscription creation - $subscription = $this->get_paypal_subscription_object($subscription_id); + $standard_gateway = new MeprPayPalStandardGateway(); + $standard_gateway->load($mepr_options->legacy_integrations[$this->id]); + $standard_gateway->return_handler(); - if(!isset($subscription['id'], $subscription['status'])) { - throw new MeprGatewayException(__('Subscription not found', 'memberpress')); + return; } - if( - ($subscription['status'] == 'ACTIVE' && $subscription['billing_info']['next_billing_time']) || - ($subscription['status'] == 'EXPIRED' && $subscription['billing_info']['cycle_executions']) - ) { - // The subscription may have been an order bump, so check all transactions for this order (there will only be one sub) - foreach($transactions as $transaction) { - $sub = $transaction->subscription(); + try { + $prd = $txn->product(); - if($sub instanceof MeprSubscription && $sub->id > 0) { - $this->handle_create_subscription($sub, $sub->subscr_id); - } - } - } - else { - throw new MeprGatewayException(__('Subscription not found', 'memberpress')); - } + $thankyou_page_args = [ + 'membership' => sanitize_title($prd->post_title), + 'membership_id' => $prd->ID, + 'transaction_id' => $txn->id, + ]; - if(!empty($txn->subscription_id)) { - $thankyou_page_args = array_merge($thankyou_page_args, ['subscription_id' => $txn->subscription_id]); - } - } - else { - // Return from a one-time payment - $token = isset($_GET['token']) ? sanitize_text_field(wp_unslash($_GET['token'])) : ''; + $subscription_id = isset($_GET['subscription_id']) ? sanitize_text_field(wp_unslash($_GET['subscription_id'])) : ''; - if(!empty($token)) { - $pp_order = $this->capture_paypal_commerce_order($token); + // Check if this is a multi-item purchase + $order = $txn->order(); + $order_bump_transactions = $order instanceof MeprOrder ? MeprTransaction::get_all_by_order_id_and_gateway($order->id, $this->id, $txn->id) : []; + $transactions = array_merge([$txn], $order_bump_transactions); - if(isset($pp_order['status']) && in_array($pp_order['status'], ['COMPLETED', 'PENDING'], true)) { - $trans_num = !empty($pp_order['purchase_units'][0]['payments']['captures'][0]['id']) ? $pp_order['purchase_units'][0]['payments']['captures'][0]['id'] : $token; + if (!empty($subscription_id)) { + // Return from a subscription creation + $subscription = $this->get_paypal_subscription_object($subscription_id); - if($order instanceof MeprOrder && count($order_bump_transactions)) { - if(!$order->is_complete() && !$order->is_processing()) { - $order->update_meta('processing', true); - $transactions = array_merge([$txn], $order_bump_transactions); + if (!isset($subscription['id'], $subscription['status'])) { + throw new MeprGatewayException(__('Subscription not found', 'memberpress')); + } - foreach($transactions as $transaction) { - if(!$transaction->is_payment_required()) { - MeprTransaction::create_free_transaction($transaction, false, sprintf('mi_%d_%s', $order->id, uniqid())); - } - elseif($transaction->is_one_time_payment()) { - $this->handle_one_time_payment($transaction, sprintf('mi_%d_%s', $order->id, uniqid())); - } + if ( + ($subscription['status'] == 'ACTIVE' && $subscription['billing_info']['next_billing_time']) || + ($subscription['status'] == 'EXPIRED' && $subscription['billing_info']['cycle_executions']) + ) { + // The subscription may have been an order bump, so check all transactions for this order (there will only be one sub) + foreach ($transactions as $transaction) { + $sub = $transaction->subscription(); + + if ($sub instanceof MeprSubscription && $sub->id > 0) { + $this->handle_create_subscription($sub, $sub->subscr_id); + } + } + } else { + throw new MeprGatewayException(__('Subscription not found', 'memberpress')); } - $order->trans_num = $trans_num; - $order->status = MeprOrder::$complete_str; - $order->store(); - $order->delete_meta('processing'); - } - } - else { - $this->handle_one_time_payment($txn, $trans_num); - } - } - elseif(isset($pp_order['details']) && is_array($pp_order['details'])) { - if($pp_order['details'][0]['issue'] != 'ORDER_ALREADY_CAPTURED') { - foreach($transactions as $transaction) { - $transaction->status = MeprTransaction::$failed_str; - $transaction->store(); - } - - throw new MeprGatewayException(__('Payer has not yet approved the Order for payment', 'memberpress')); + if (!empty($txn->subscription_id)) { + $thankyou_page_args = array_merge($thankyou_page_args, ['subscription_id' => $txn->subscription_id]); + } + } else { + // Return from a one-time payment + $token = isset($_GET['token']) ? sanitize_text_field(wp_unslash($_GET['token'])) : ''; + + if (!empty($token)) { + $pp_order = $this->capture_paypal_commerce_order($token); + + if (isset($pp_order['status']) && in_array($pp_order['status'], ['COMPLETED', 'PENDING'], true)) { + $trans_num = !empty($pp_order['purchase_units'][0]['payments']['captures'][0]['id']) ? $pp_order['purchase_units'][0]['payments']['captures'][0]['id'] : $token; + + if ($order instanceof MeprOrder && count($order_bump_transactions)) { + if (!$order->is_complete() && !$order->is_processing()) { + $order->update_meta('processing', true); + $transactions = array_merge([$txn], $order_bump_transactions); + + foreach ($transactions as $transaction) { + if (!$transaction->is_payment_required()) { + MeprTransaction::create_free_transaction($transaction, false, sprintf('mi_%d_%s', $order->id, uniqid())); + } elseif ($transaction->is_one_time_payment()) { + $this->handle_one_time_payment($transaction, sprintf('mi_%d_%s', $order->id, uniqid())); + } + } + + $order->trans_num = $trans_num; + $order->status = MeprOrder::$complete_str; + $order->store(); + $order->delete_meta('processing'); + } + } else { + $this->handle_one_time_payment($txn, $trans_num); + } + } elseif (isset($pp_order['details']) && is_array($pp_order['details'])) { + if ($pp_order['details'][0]['issue'] != 'ORDER_ALREADY_CAPTURED') { + foreach ($transactions as $transaction) { + $transaction->status = MeprTransaction::$failed_str; + $transaction->store(); + } + + throw new MeprGatewayException(__('Payer has not yet approved the Order for payment', 'memberpress')); + } + } else { + throw new MeprGatewayException(__('Order could not be captured', 'memberpress')); + } + } else { + return; + } } - } - else { - throw new MeprGatewayException(__('Order could not be captured', 'memberpress')); - } - } - else { - return; - } - } - MeprUtils::wp_redirect(esc_url_raw($mepr_options->thankyou_page_url($thankyou_page_args))); - } - catch(Exception $e) { - $product = $txn->product(); - $product_url = MeprUtils::get_permalink($product->ID); - $product_url = !empty($product_url) ? $product_url : home_url(); - - MeprUtils::wp_redirect(esc_url_raw(add_query_arg([ - 'errors' => $e->getMessage(), - ], $product_url))); + MeprUtils::wp_redirect(esc_url_raw($mepr_options->thankyou_page_url($thankyou_page_args))); + } catch (Exception $e) { + $product = $txn->product(); + $product_url = MeprUtils::get_permalink($product->ID); + $product_url = !empty($product_url) ? $product_url : home_url(); + + MeprUtils::wp_redirect(esc_url_raw(add_query_arg([ + 'errors' => $e->getMessage(), + ], $product_url))); + } } - } - public function cancel_handler() { - $mepr_options = MeprOptions::fetch(); - // Handled with a GET REQUEST by PayPal - $this->email_status( "Paypal Cancel \$_REQUEST:\n" . MeprUtils::object_to_string( $_REQUEST, true ) . "\n", $this->debug ); + public function cancel_handler() + { + $mepr_options = MeprOptions::fetch(); + // Handled with a GET REQUEST by PayPal + $this->email_status("Paypal Cancel \$_REQUEST:\n" . MeprUtils::object_to_string($_REQUEST, true) . "\n", $this->debug); - MeprHooks::do_action('mepr_paypal_checkout_cancelled_before', $_REQUEST); + MeprHooks::do_action('mepr_paypal_checkout_cancelled_before', $_REQUEST); - if ( isset( $_REQUEST['txn_id'] ) && is_numeric( $_REQUEST['txn_id'] ) ) { - $txn = new MeprTransaction( $_REQUEST['txn_id'] ); + if (isset($_REQUEST['txn_id']) && is_numeric($_REQUEST['txn_id'])) { + $txn = new MeprTransaction($_REQUEST['txn_id']); - // Make sure the txn status is pending - $txn->status = MeprTransaction::$pending_str; - $txn->store(); + // Make sure the txn status is pending + $txn->status = MeprTransaction::$pending_str; + $txn->store(); - if ( $sub = $txn->subscription() ) { - $sub->status = MeprSubscription::$pending_str; - $sub->store(); - } + if ($sub = $txn->subscription()) { + $sub->status = MeprSubscription::$pending_str; + $sub->store(); + } + + if (isset($txn->product_id) && $txn->product_id > 0) { + $prd = new MeprProduct($txn->product_id); + MeprUtils::wp_redirect($this->message_page_url($prd, 'cancel')); + } + } - if ( isset( $txn->product_id ) && $txn->product_id > 0 ) { - $prd = new MeprProduct( $txn->product_id ); - MeprUtils::wp_redirect( $this->message_page_url( $prd, 'cancel' ) ); - } + if (isset($_REQUEST['subscription_id'])) { + $sub = MeprSubscription::get_one_by_subscr_id($_REQUEST['subscription_id']); + $prd = $sub->product(); + MeprUtils::wp_redirect($this->message_page_url($prd, 'cancel')); + } + + // If all else fails, just send them to their account page + MeprUtils::wp_redirect($mepr_options->account_page_url('action=subscriptions')); } - if ( isset( $_REQUEST['subscription_id'] ) ) { - $sub = MeprSubscription::get_one_by_subscr_id($_REQUEST['subscription_id']); - $prd = $sub->product(); - MeprUtils::wp_redirect( $this->message_page_url( $prd, 'cancel' ) ); + public function cancel_message() + { + $mepr_options = MeprOptions::fetch(); + ?> +

    +

    ', '')); ?> +

    + account_page_url( 'action=subscriptions' ) ); - } + protected function capture_paypal_commerce_order($pp_order_id) + { + $response = wp_remote_post($this->settings->rest_api_url . '/v2/checkout/orders/' . $pp_order_id . '/capture', [ + 'headers' => [ + 'Content-Type' => 'application/json', + 'PayPal-Partner-Attribution-Id' => MeprPayPalConnectCtrl::PAYPAL_BN_CODE, + 'Authorization' => 'Basic ' . $this->get_pp_basic_auth_token(), + ], + ]); - public function cancel_message() { - $mepr_options = MeprOptions::fetch(); - ?> -

    -

    ', '' ) ); ?> -

    - settings->rest_api_url . '/v2/checkout/orders/' . $pp_order_id . '/capture', [ - 'headers' => [ - 'Content-Type' => 'application/json', - 'PayPal-Partner-Attribution-Id' => MeprPayPalConnectCtrl::PAYPAL_BN_CODE, - 'Authorization' => 'Basic ' . $this->get_pp_basic_auth_token(), - ], - ] ); - - $response = json_decode( wp_remote_retrieve_body( $response ), true ); - - return $response; - } + $response = json_decode(wp_remote_retrieve_body($response), true); + + return $response; + } } diff --git a/app/gateways/MeprPayPalGateway.php b/app/gateways/MeprPayPalGateway.php index c2b6acf..d33f244 100644 --- a/app/gateways/MeprPayPalGateway.php +++ b/app/gateways/MeprPayPalGateway.php @@ -1,929 +1,1034 @@ name = __("PayPal Express Checkout", 'memberpress'); - $this->key = __('paypalexpress', 'memberpress'); - $this->has_spc_form = false; - - $this->set_defaults(); - - $this->capabilities = array( - 'process-payments', - 'process-refunds', - 'create-subscriptions', - 'cancel-subscriptions', - 'update-subscriptions', - 'suspend-subscriptions', - 'resume-subscriptions', - 'subscription-trial-payment' //The trial payment doesn't have to be processed as a separate one-off like Authorize.net & Stripe - //'send-cc-expirations' - ); - - // Setup the notification actions for this gateway - $this->notifiers = array( - 'ipn' => 'listener', - 'cancel' => 'cancel_handler', - 'return' => 'return_handler' - ); - $this->message_pages = array( 'cancel' => 'cancel_message', 'payment_failed' => 'payment_failed_message' ); - } - - public function load($settings) { - $this->settings = (object)$settings; - $this->set_defaults(); - } - - protected function set_defaults() { - if(!isset($this->settings)) - $this->settings = array(); - - $this->settings = (object)array_merge( - array( - 'gateway' => 'MeprPayPalGateway', - 'id' => $this->generate_id(), - 'label' => '', - 'use_label' => true, - 'icon' => MEPR_IMAGES_URL . '/checkout/paypal.png', - 'use_icon' => true, - 'desc' => __('Pay via your PayPal account', 'memberpress'), - 'use_desc' => true, - 'api_username' => '', - 'api_password' => '', - 'signature' => '', - 'sandbox' => false, - 'debug' => false - ), - (array)$this->settings - ); - - $this->id = $this->settings->id; - $this->label = $this->settings->label; - $this->use_label = $this->settings->use_label; - $this->icon = $this->settings->icon; - $this->use_icon = $this->settings->use_icon; - $this->desc = $this->settings->desc; - $this->use_desc = $this->settings->use_desc; - - if($this->is_test_mode()) { - $this->settings->url = 'https://ipnpb.sandbox.paypal.com/cgi-bin/webscr'; - $this->settings->api_url = 'https://api-3t.sandbox.paypal.com/nvp'; +class MeprPayPalGateway extends MeprBasePayPalGateway +{ + // This is stored with the user meta & the subscription meta + public static $paypal_token_str = '_mepr_paypal_token'; + + /** + * Used in the view to identify the gateway + */ + public function __construct() + { + $this->name = __('PayPal Express Checkout', 'memberpress'); + $this->key = __('paypalexpress', 'memberpress'); + $this->has_spc_form = false; + + $this->set_defaults(); + + $this->capabilities = [ + 'process-payments', + 'process-refunds', + 'create-subscriptions', + 'cancel-subscriptions', + 'update-subscriptions', + 'suspend-subscriptions', + 'resume-subscriptions', + 'subscription-trial-payment', // The trial payment doesn't have to be processed as a separate one-off like Authorize.net & Stripe + // 'send-cc-expirations' + ]; + + // Setup the notification actions for this gateway + $this->notifiers = [ + 'ipn' => 'listener', + 'cancel' => 'cancel_handler', + 'return' => 'return_handler', + ]; + $this->message_pages = [ + 'cancel' => 'cancel_message', + 'payment_failed' => 'payment_failed_message', + ]; } - else { - $this->settings->url = 'https://ipnpb.paypal.com/cgi-bin/webscr'; - $this->settings->api_url = 'https://api-3t.paypal.com/nvp'; + + public function load($settings) + { + $this->settings = (object)$settings; + $this->set_defaults(); } - $this->settings->api_version = 69; + protected function set_defaults() + { + if (!isset($this->settings)) { + $this->settings = []; + } - // An attempt to correct people who paste in spaces along with their credentials - $this->settings->api_username = trim($this->settings->api_username); - $this->settings->api_password = trim($this->settings->api_password); - $this->settings->signature = trim($this->settings->signature); - } + $this->settings = (object)array_merge( + [ + 'gateway' => 'MeprPayPalGateway', + 'id' => $this->generate_id(), + 'label' => '', + 'use_label' => true, + 'icon' => MEPR_IMAGES_URL . '/checkout/paypal.png', + 'use_icon' => true, + 'desc' => __('Pay via your PayPal account', 'memberpress'), + 'use_desc' => true, + 'api_username' => '', + 'api_password' => '', + 'signature' => '', + 'sandbox' => false, + 'debug' => false, + ], + (array)$this->settings + ); + + $this->id = $this->settings->id; + $this->label = $this->settings->label; + $this->use_label = $this->settings->use_label; + $this->icon = $this->settings->icon; + $this->use_icon = $this->settings->use_icon; + $this->desc = $this->settings->desc; + $this->use_desc = $this->settings->use_desc; + + if ($this->is_test_mode()) { + $this->settings->url = 'https://ipnpb.sandbox.paypal.com/cgi-bin/webscr'; + $this->settings->api_url = 'https://api-3t.sandbox.paypal.com/nvp'; + } else { + $this->settings->url = 'https://ipnpb.paypal.com/cgi-bin/webscr'; + $this->settings->api_url = 'https://api-3t.paypal.com/nvp'; + } - /** Override to add the '_ec' */ - public function slug() { - return parent::slug() . '_ec'; - } + $this->settings->api_version = 69; - /** Listens for an incoming connection from PayPal and then handles the request appropriately. */ - public function listener() { - $_POST = wp_unslash($_POST); - $this->email_status("PayPal IPN Recieved\n" . MeprUtils::object_to_string($_POST, true) . "\n", $this->settings->debug); + // An attempt to correct people who paste in spaces along with their credentials + $this->settings->api_username = trim($this->settings->api_username); + $this->settings->api_password = trim($this->settings->api_password); + $this->settings->signature = trim($this->settings->signature); + } - if($this->validate_ipn()) { return $this->process_ipn(); } + /** + * Override to add the '_ec' + */ + public function slug() + { + return parent::slug() . '_ec'; + } - return false; - } + /** + * Listens for an incoming connection from PayPal and then handles the request appropriately. + */ + public function listener() + { + $_POST = wp_unslash($_POST); + $this->email_status("PayPal IPN Recieved\n" . MeprUtils::object_to_string($_POST, true) . "\n", $this->settings->debug); - private function process_ipn() { - $recurring_payment_txn_types = array('recurring_payment', 'subscr_payment', 'recurring_payment_outstanding_payment'); - $failed_txn_types = array('recurring_payment_skipped', 'subscr_failed'); - $payment_status_types = array('denied','expired','failed'); - $refunded_types = array('refunded','reversed','voided'); - $cancel_sub_types = array('recurring_payment_profile_cancel', 'subscr_cancel', 'recurring_payment_suspended_due_to_max_failed_payment'); + if ($this->validate_ipn()) { + return $this->process_ipn(); + } - if( isset($_POST['txn_type']) && in_array(strtolower($_POST['txn_type']), $recurring_payment_txn_types) ) { - $this->record_subscription_payment(); + return false; } - elseif( ( isset($_POST['txn_type']) && in_array(strtolower($_POST['txn_type']), $failed_txn_types) ) || + + private function process_ipn() + { + $recurring_payment_txn_types = ['recurring_payment', 'subscr_payment', 'recurring_payment_outstanding_payment']; + $failed_txn_types = ['recurring_payment_skipped', 'subscr_failed']; + $payment_status_types = ['denied','expired','failed']; + $refunded_types = ['refunded','reversed','voided']; + $cancel_sub_types = ['recurring_payment_profile_cancel', 'subscr_cancel', 'recurring_payment_suspended_due_to_max_failed_payment']; + + if (isset($_POST['txn_type']) && in_array(strtolower($_POST['txn_type']), $recurring_payment_txn_types)) { + $this->record_subscription_payment(); + } elseif ( + ( isset($_POST['txn_type']) && in_array(strtolower($_POST['txn_type']), $failed_txn_types) ) || ( isset($_POST['payment_status']) && in_array(strtolower($_POST['payment_status']), $payment_status_types) ) - ) { - $this->record_payment_failure(); - } - elseif( isset($_POST['txn_type']) && in_array(strtolower($_POST['txn_type']), $cancel_sub_types) ) { - $this->record_cancel_subscription(); - } - elseif( isset($_POST['txn_type']) && strtolower($_POST['txn_type'])=='recurring_payment_suspended' ) { - $this->record_suspend_subscription(); - } - // There is No IPN for recurring payment resumed - elseif( isset($_POST['parent_txn_id']) && !isset($_POST['txn_type']) ) { - if( in_array(strtolower($_POST['payment_status']), $refunded_types) ) { return $this->record_refund(); } - } - //Need to catch INITAMT's WITH THIS HOOK - elseif(isset($_POST['txn_type']) && $_POST['txn_type'] == 'recurring_payment_profile_created') { - $this->maybe_catch_initamt(); + ) { + $this->record_payment_failure(); + } elseif (isset($_POST['txn_type']) && in_array(strtolower($_POST['txn_type']), $cancel_sub_types)) { + $this->record_cancel_subscription(); + } elseif (isset($_POST['txn_type']) && strtolower($_POST['txn_type']) == 'recurring_payment_suspended') { + $this->record_suspend_subscription(); + } elseif (isset($_POST['parent_txn_id']) && !isset($_POST['txn_type'])) { + if (in_array(strtolower($_POST['payment_status']), $refunded_types)) { + return $this->record_refund(); + } + } elseif (isset($_POST['txn_type']) && $_POST['txn_type'] == 'recurring_payment_profile_created') { + // Need to catch INITAMT's WITH THIS HOOK + $this->maybe_catch_initamt(); + } } - } - - public function maybe_catch_initamt() { - if(isset($_POST['initial_payment_amount'], $_POST['initial_payment_status']) && $_POST['initial_payment_amount'] >= 0.00 && strtolower($_POST['initial_payment_status']) == 'completed') { - if(isset($_POST['subscr_id']) && !empty($_POST['subscr_id'])) { - $sub = MeprSubscription::get_one_by_subscr_id($_POST['subscr_id']); - } - else { - $sub = MeprSubscription::get_one_by_subscr_id($_POST['recurring_payment_id']); - } - - if($sub === false || !isset($sub->id) || (int)$sub->id <= 0) { return; } //If this isn't a sub, then why are we here (IPN fwd probably) - - //Just convert the confirmation into the initial payment - $first_txn = $sub->first_txn(); - if($first_txn == false || !($first_txn instanceof MeprTransaction)) { - $first_txn = new MeprTransaction(); - $first_txn->user_id = $sub->user_id; - $first_txn->product_id = $sub->product_id; - $first_txn->coupon_id = $sub->coupon_id; - } - - $first_txn->trans_num = $_POST['initial_payment_txn_id']; - $first_txn->txn_type = MeprTransaction::$payment_str; - $first_txn->status = MeprTransaction::$complete_str; - $first_txn->expires_at = MeprUtils::ts_to_mysql_date(strtotime($_POST['next_payment_date']), 'Y-m-d 23:59:59'); - $first_txn->set_gross($_POST['initial_payment_amount']); - $first_txn->store(); - - //Check that the subscription status is still enabled - if($sub->status != MeprSubscription::$active_str) { - $sub->status = MeprSubscription::$active_str; - $sub->store(); - } - - // Not waiting for an IPN here bro ... just making it happen even though - // the total occurrences is already capped in record_create_subscription() - $sub->limit_payment_cycles(); - $this->email_status("Subscription Transaction - INITAMT\n" . + public function maybe_catch_initamt() + { + if (isset($_POST['initial_payment_amount'], $_POST['initial_payment_status']) && $_POST['initial_payment_amount'] >= 0.00 && strtolower($_POST['initial_payment_status']) == 'completed') { + if (isset($_POST['subscr_id']) && !empty($_POST['subscr_id'])) { + $sub = MeprSubscription::get_one_by_subscr_id($_POST['subscr_id']); + } else { + $sub = MeprSubscription::get_one_by_subscr_id($_POST['recurring_payment_id']); + } + + if ($sub === false || !isset($sub->id) || (int)$sub->id <= 0) { + return; + } //If this isn't a sub, then why are we here (IPN fwd probably) + + // Just convert the confirmation into the initial payment + $first_txn = $sub->first_txn(); + if ($first_txn == false || !($first_txn instanceof MeprTransaction)) { + $first_txn = new MeprTransaction(); + $first_txn->user_id = $sub->user_id; + $first_txn->product_id = $sub->product_id; + $first_txn->coupon_id = $sub->coupon_id; + } + + $first_txn->trans_num = $_POST['initial_payment_txn_id']; + $first_txn->txn_type = MeprTransaction::$payment_str; + $first_txn->status = MeprTransaction::$complete_str; + $first_txn->expires_at = MeprUtils::ts_to_mysql_date(strtotime($_POST['next_payment_date']), 'Y-m-d 23:59:59'); + $first_txn->set_gross($_POST['initial_payment_amount']); + $first_txn->store(); + + // Check that the subscription status is still enabled + if ($sub->status != MeprSubscription::$active_str) { + $sub->status = MeprSubscription::$active_str; + $sub->store(); + } + + // Not waiting for an IPN here bro ... just making it happen even though + // the total occurrences is already capped in record_create_subscription() + $sub->limit_payment_cycles(); + + $this->email_status( + "Subscription Transaction - INITAMT\n" . MeprUtils::object_to_string($first_txn->rec, true), - $this->settings->debug); + $this->settings->debug + ); - MeprUtils::send_transaction_receipt_notices($first_txn); - } - } - - /** Used to record a successful recurring payment by the given gateway. It - * should have the ability to record a successful payment or a failure. It is - * this method that should be used when receiving an IPN from PayPal or a - * Silent Post from Authorize.net. - */ - public function record_subscription_payment() { - if(!isset($_POST['recurring_payment_id']) && !isset($_POST['subscr_id'])) { - return; - } - - if(isset($_POST['subscr_id']) && !empty($_POST['subscr_id'])) { - $sub = MeprSubscription::get_one_by_subscr_id($_POST['subscr_id']); - } - else { - $sub = MeprSubscription::get_one_by_subscr_id($_POST['recurring_payment_id']); + MeprUtils::send_transaction_receipt_notices($first_txn); + } } - if($sub) { - $timestamp = isset($_POST['payment_date']) ? strtotime($_POST['payment_date']) : time(); - $first_txn = $sub->first_txn(); - - if($first_txn == false || !($first_txn instanceof MeprTransaction)) { - $first_txn = new MeprTransaction(); - $first_txn->user_id = $sub->user_id; - $first_txn->product_id = $sub->product_id; - $first_txn->coupon_id = $sub->coupon_id; - } - - //Prevent recording duplicates - $existing_txn = MeprTransaction::get_one_by_trans_num($_POST['txn_id']); - if( isset($existing_txn->id) && - $existing_txn->id > 0 && - in_array($existing_txn->status, array(MeprTransaction::$complete_str, MeprTransaction::$confirmed_str)) ) { - return; - } - - //If this is a trial payment, let's just convert the confirmation txn into a payment txn - //then we won't have to mess with setting expires_at as it was already handled - if($this->is_subscr_trial_payment($sub)) { - $txn = $first_txn; //For use below in send notices - $txn->created_at = MeprUtils::ts_to_mysql_date($timestamp); - $txn->gateway = $this->id; - $txn->trans_num = $_POST['txn_id']; - $txn->txn_type = MeprTransaction::$payment_str; - $txn->status = MeprTransaction::$complete_str; - $txn->subscription_id = $sub->id; - - $txn->set_gross($_POST['mc_gross']); - - $txn->store(); - } - else { - $txn = new MeprTransaction(); - $txn->created_at = MeprUtils::ts_to_mysql_date($timestamp); - $txn->user_id = $first_txn->user_id; - $txn->product_id = $first_txn->product_id; - $txn->coupon_id = $first_txn->coupon_id; - $txn->gateway = $this->id; - $txn->trans_num = $_POST['txn_id']; - $txn->txn_type = MeprTransaction::$payment_str; - $txn->status = MeprTransaction::$complete_str; - $txn->subscription_id = $sub->id; - - $txn->set_gross($_POST['mc_gross']); - - $txn->store(); - - //Check that the subscription status is still enabled - if($sub->status != MeprSubscription::$active_str) { - $sub->status = MeprSubscription::$active_str; - $sub->store(); + /** + * Used to record a successful recurring payment by the given gateway. It + * should have the ability to record a successful payment or a failure. It is + * this method that should be used when receiving an IPN from PayPal or a + * Silent Post from Authorize.net. + */ + public function record_subscription_payment() + { + if (!isset($_POST['recurring_payment_id']) && !isset($_POST['subscr_id'])) { + return; } - // Not waiting for an IPN here bro ... just making it happen even though - // the total occurrences is already capped in record_create_subscription() - $sub->limit_payment_cycles(); - } + if (isset($_POST['subscr_id']) && !empty($_POST['subscr_id'])) { + $sub = MeprSubscription::get_one_by_subscr_id($_POST['subscr_id']); + } else { + $sub = MeprSubscription::get_one_by_subscr_id($_POST['recurring_payment_id']); + } - $this->email_status("Subscription Transaction\n" . + if ($sub) { + $timestamp = isset($_POST['payment_date']) ? strtotime($_POST['payment_date']) : time(); + $first_txn = $sub->first_txn(); + + if ($first_txn == false || !($first_txn instanceof MeprTransaction)) { + $first_txn = new MeprTransaction(); + $first_txn->user_id = $sub->user_id; + $first_txn->product_id = $sub->product_id; + $first_txn->coupon_id = $sub->coupon_id; + } + + // Prevent recording duplicates + $existing_txn = MeprTransaction::get_one_by_trans_num($_POST['txn_id']); + if ( + isset($existing_txn->id) && + $existing_txn->id > 0 && + in_array($existing_txn->status, [MeprTransaction::$complete_str, MeprTransaction::$confirmed_str]) + ) { + return; + } + + // If this is a trial payment, let's just convert the confirmation txn into a payment txn + // then we won't have to mess with setting expires_at as it was already handled + if ($this->is_subscr_trial_payment($sub)) { + $txn = $first_txn; // For use below in send notices + $txn->created_at = MeprUtils::ts_to_mysql_date($timestamp); + $txn->gateway = $this->id; + $txn->trans_num = $_POST['txn_id']; + $txn->txn_type = MeprTransaction::$payment_str; + $txn->status = MeprTransaction::$complete_str; + $txn->subscription_id = $sub->id; + + $txn->set_gross($_POST['mc_gross']); + + $txn->store(); + } else { + $txn = new MeprTransaction(); + $txn->created_at = MeprUtils::ts_to_mysql_date($timestamp); + $txn->user_id = $first_txn->user_id; + $txn->product_id = $first_txn->product_id; + $txn->coupon_id = $first_txn->coupon_id; + $txn->gateway = $this->id; + $txn->trans_num = $_POST['txn_id']; + $txn->txn_type = MeprTransaction::$payment_str; + $txn->status = MeprTransaction::$complete_str; + $txn->subscription_id = $sub->id; + + $txn->set_gross($_POST['mc_gross']); + + $txn->store(); + + // Check that the subscription status is still enabled + if ($sub->status != MeprSubscription::$active_str) { + $sub->status = MeprSubscription::$active_str; + $sub->store(); + } + + // Not waiting for an IPN here bro ... just making it happen even though + // the total occurrences is already capped in record_create_subscription() + $sub->limit_payment_cycles(); + } + + $this->email_status( + "Subscription Transaction\n" . MeprUtils::object_to_string($txn->rec, true), - $this->settings->debug); - - MeprUtils::send_transaction_receipt_notices( $txn ); + $this->settings->debug + ); - return $txn; - } + MeprUtils::send_transaction_receipt_notices($txn); - return false; - } + return $txn; + } - /** Used to record a declined payment. */ - public function record_payment_failure() { - if(isset($_POST['ipn_track_id']) && ($txn_res = MeprTransaction::get_one_by_trans_num($_POST['ipn_track_id'])) && isset($txn_res->id)) { - return false; //We've already recorded this failure duh - don't send more emails - } - elseif(isset($_POST['txn_id']) && ($txn_res = MeprTransaction::get_one_by_trans_num($_POST['txn_id'])) && isset($txn_res->id)) { - $txn = new MeprTransaction($txn_res->id); - $txn->status = MeprTransaction::$failed_str; - $txn->store(); + return false; } - elseif( ( isset($_POST['recurring_payment_id']) and + + /** + * Used to record a declined payment. + */ + public function record_payment_failure() + { + if (isset($_POST['ipn_track_id']) && ($txn_res = MeprTransaction::get_one_by_trans_num($_POST['ipn_track_id'])) && isset($txn_res->id)) { + return false; // We've already recorded this failure duh - don't send more emails + } elseif (isset($_POST['txn_id']) && ($txn_res = MeprTransaction::get_one_by_trans_num($_POST['txn_id'])) && isset($txn_res->id)) { + $txn = new MeprTransaction($txn_res->id); + $txn->status = MeprTransaction::$failed_str; + $txn->store(); + } elseif ( + ( isset($_POST['recurring_payment_id']) and ($sub = MeprSubscription::get_one_by_subscr_id($_POST['recurring_payment_id'])) ) or ( isset($_POST['subscr_id']) and - ($sub = MeprSubscription::get_one_by_subscr_id($_POST['subscr_id'])) ) ) { - $first_txn = $sub->first_txn(); - - if($first_txn == false || !($first_txn instanceof MeprTransaction)) { - $first_txn = new MeprTransaction(); - $first_txn->user_id = $sub->user_id; - $first_txn->product_id = $sub->product_id; - $first_txn->coupon_id = $sub->coupon_id; - } - - $txn = new MeprTransaction(); - $txn->user_id = $sub->user_id; - $txn->product_id = $sub->product_id; - $txn->coupon_id = $first_txn->coupon_id; - $txn->txn_type = MeprTransaction::$payment_str; - $txn->status = MeprTransaction::$failed_str; - $txn->subscription_id = $sub->id; - // if ipn_track_id isn't set then just use uniqid - $txn->trans_num = ( isset($_POST['ipn_track_id']) ? $_POST['ipn_track_id'] : uniqid() ); - $txn->gateway = $this->id; - - $txn->set_gross( isset($_POST['mc_gross']) ? $_POST['mc_gross'] : ( isset($_POST['amount']) ? $_POST['amount'] : 0.00 ) ); - - $txn->store(); - - $sub->expire_txns(); //Expire associated transactions for the old subscription - $sub->store(); - } - else { - return false; // Nothing we can do here ... so we outta here - } + ($sub = MeprSubscription::get_one_by_subscr_id($_POST['subscr_id'])) ) + ) { + $first_txn = $sub->first_txn(); + + if ($first_txn == false || !($first_txn instanceof MeprTransaction)) { + $first_txn = new MeprTransaction(); + $first_txn->user_id = $sub->user_id; + $first_txn->product_id = $sub->product_id; + $first_txn->coupon_id = $sub->coupon_id; + } + + $txn = new MeprTransaction(); + $txn->user_id = $sub->user_id; + $txn->product_id = $sub->product_id; + $txn->coupon_id = $first_txn->coupon_id; + $txn->txn_type = MeprTransaction::$payment_str; + $txn->status = MeprTransaction::$failed_str; + $txn->subscription_id = $sub->id; + // if ipn_track_id isn't set then just use uniqid + $txn->trans_num = ( isset($_POST['ipn_track_id']) ? $_POST['ipn_track_id'] : uniqid() ); + $txn->gateway = $this->id; + + $txn->set_gross(isset($_POST['mc_gross']) ? $_POST['mc_gross'] : ( isset($_POST['amount']) ? $_POST['amount'] : 0.00 )); + + $txn->store(); + + $sub->expire_txns(); // Expire associated transactions for the old subscription + $sub->store(); + } else { + return false; // Nothing we can do here ... so we outta here + } + + MeprUtils::send_failed_txn_notices($txn); - MeprUtils::send_failed_txn_notices($txn); - - return $txn; - } - - /** Used to send data to a given payment gateway. In gateways which redirect - * before this step is necessary this method should just be left blank. - */ - public function process_payment($txn) { - $mepr_options = MeprOptions::fetch(); - $prd = $txn->product(); - $sub = $txn->subscription(); - $usr = $txn->user(); - $tkn = $_REQUEST['token']; - $pid = $_REQUEST['PayerID']; - - $args = MeprHooks::apply_filters('mepr_paypal_ec_payment_args', array( - 'TOKEN' => $tkn, - 'PAYMENTREQUEST_0_PAYMENTACTION' => 'Sale', - 'PAYMENTREQUEST_0_AMT' => $txn->total, - 'PAYMENTREQUEST_0_CURRENCYCODE' => $mepr_options->currency_code, - 'BUTTONSOURCE' => 'Caseproof_SP', - 'PAYERID' => $pid - ), $txn, $sub); - - $this->email_status("DoExpressCheckoutPayment Request:\n".MeprUtils::object_to_string($args,true)."\n", $this->settings->debug); - - $res = $this->send_nvp_request('DoExpressCheckoutPayment', $args); - - if(isset($res['ACK']) && strtolower($res['ACK']) != 'success') { - MeprUtils::wp_redirect($this->message_page_url($prd, 'payment_failed')); + return $txn; } - $this->email_status("DoExpressCheckoutPayment Response:\n".MeprUtils::object_to_string($res,true)."\n", $this->settings->debug); + /** + * Used to send data to a given payment gateway. In gateways which redirect + * before this step is necessary this method should just be left blank. + */ + public function process_payment($txn) + { + $mepr_options = MeprOptions::fetch(); + $prd = $txn->product(); + $sub = $txn->subscription(); + $usr = $txn->user(); + $tkn = $_REQUEST['token']; + $pid = $_REQUEST['PayerID']; + + $args = MeprHooks::apply_filters('mepr_paypal_ec_payment_args', [ + 'TOKEN' => $tkn, + 'PAYMENTREQUEST_0_PAYMENTACTION' => 'Sale', + 'PAYMENTREQUEST_0_AMT' => $txn->total, + 'PAYMENTREQUEST_0_CURRENCYCODE' => $mepr_options->currency_code, + 'BUTTONSOURCE' => 'Caseproof_SP', + 'PAYERID' => $pid, + ], $txn, $sub); + + $this->email_status("DoExpressCheckoutPayment Request:\n" . MeprUtils::object_to_string($args, true) . "\n", $this->settings->debug); + + $res = $this->send_nvp_request('DoExpressCheckoutPayment', $args); + + if (isset($res['ACK']) && strtolower($res['ACK']) != 'success') { + MeprUtils::wp_redirect($this->message_page_url($prd, 'payment_failed')); + } - $_REQUEST['paypal_response'] = $res; - $_REQUEST['transaction'] = $txn; + $this->email_status("DoExpressCheckoutPayment Response:\n" . MeprUtils::object_to_string($res, true) . "\n", $this->settings->debug); - return $this->record_payment(); - } + $_REQUEST['paypal_response'] = $res; + $_REQUEST['transaction'] = $txn; + + return $this->record_payment(); + } - /** Used to record a successful payment by the given gateway. It should have - * the ability to record a successful payment or a failure. It is this method - * that should be used when receiving an IPN from PayPal or a Silent Post - * from Authorize.net. - */ - public function record_payment() { - if(!isset($_REQUEST['paypal_response']) or !isset($_REQUEST['transaction'])) - return false; + /** + * Used to record a successful payment by the given gateway. It should have + * the ability to record a successful payment or a failure. It is this method + * that should be used when receiving an IPN from PayPal or a Silent Post + * from Authorize.net. + */ + public function record_payment() + { + if (!isset($_REQUEST['paypal_response']) or !isset($_REQUEST['transaction'])) { + return false; + } - $res = $_REQUEST['paypal_response']; - $txn = $_REQUEST['transaction']; + $res = $_REQUEST['paypal_response']; + $txn = $_REQUEST['transaction']; - if( $txn->status == MeprTransaction::$complete_str ) - return false; + if ($txn->status == MeprTransaction::$complete_str) { + return false; + } - if(isset($res['PAYMENTINFO_0_PAYMENTSTATUS'])) { - if(strtolower($res['ACK'])=='success' and strtolower($res['PAYMENTINFO_0_PAYMENTSTATUS'])=='completed') { - $txn->trans_num = $res['PAYMENTINFO_0_TRANSACTIONID']; - $txn->txn_type = MeprTransaction::$payment_str; - $txn->status = MeprTransaction::$complete_str; - // This will only work before maybe_cancel_old_sub is run - $upgrade = $txn->is_upgrade(); - $downgrade = $txn->is_downgrade(); + if (isset($res['PAYMENTINFO_0_PAYMENTSTATUS'])) { + if (strtolower($res['ACK']) == 'success' and strtolower($res['PAYMENTINFO_0_PAYMENTSTATUS']) == 'completed') { + $txn->trans_num = $res['PAYMENTINFO_0_TRANSACTIONID']; + $txn->txn_type = MeprTransaction::$payment_str; + $txn->status = MeprTransaction::$complete_str; + // This will only work before maybe_cancel_old_sub is run + $upgrade = $txn->is_upgrade(); + $downgrade = $txn->is_downgrade(); - $event_txn = $txn->maybe_cancel_old_sub(); - $txn->store(); + $event_txn = $txn->maybe_cancel_old_sub(); + $txn->store(); - $this->email_status("Transaction\n" . MeprUtils::object_to_string($txn->rec, true) . "\n", $this->settings->debug); + $this->email_status("Transaction\n" . MeprUtils::object_to_string($txn->rec, true) . "\n", $this->settings->debug); - $prd = $txn->product(); + $prd = $txn->product(); - if( $prd->period_type=='lifetime' ) { - if( $upgrade ) { - $this->upgraded_sub($txn, $event_txn); - } - else if( $downgrade ) { - $this->downgraded_sub($txn, $event_txn); - } - else { - $this->new_sub($txn); - } - - MeprUtils::send_signup_notices( $txn ); - } + if ($prd->period_type == 'lifetime') { + if ($upgrade) { + $this->upgraded_sub($txn, $event_txn); + } elseif ($downgrade) { + $this->downgraded_sub($txn, $event_txn); + } else { + $this->new_sub($txn); + } - MeprUtils::send_transaction_receipt_notices( $txn ); + MeprUtils::send_signup_notices($txn); + } - return $txn; - } - } + MeprUtils::send_transaction_receipt_notices($txn); - return false; - } + return $txn; + } + } - /** This method should be used by the class to record a successful refund from - * the gateway. This method should also be used by any IPN requests or Silent Posts. - */ - public function process_refund(MeprTransaction $txn) { - $mepr_options = MeprOptions::fetch(); + return false; + } - $args = MeprHooks::apply_filters('mepr_paypal_ec_refund_args', array( - 'TRANSACTIONID' => $txn->trans_num, - 'REFUNDTYPE' => 'Full', - 'CURRENCYCODE' => $mepr_options->currency_code - ), $txn); + /** + * This method should be used by the class to record a successful refund from + * the gateway. This method should also be used by any IPN requests or Silent Posts. + */ + public function process_refund(MeprTransaction $txn) + { + $mepr_options = MeprOptions::fetch(); - $this->email_status("RefundTransaction Request:\n".MeprUtils::object_to_string($args,true)."\n", $this->settings->debug); - $res = $this->send_nvp_request('RefundTransaction', $args); - $this->email_status("RefundTransaction Response:\n".MeprUtils::object_to_string($res,true)."\n", $this->settings->debug); + $args = MeprHooks::apply_filters('mepr_paypal_ec_refund_args', [ + 'TRANSACTIONID' => $txn->trans_num, + 'REFUNDTYPE' => 'Full', + 'CURRENCYCODE' => $mepr_options->currency_code, + ], $txn); - if( !isset($res['ACK']) or strtoupper($res['ACK']) != 'SUCCESS' ) - throw new MeprGatewayException( __('The refund was unsuccessful. Please login at PayPal and refund the transaction there.', 'memberpress') ); + $this->email_status("RefundTransaction Request:\n" . MeprUtils::object_to_string($args, true) . "\n", $this->settings->debug); + $res = $this->send_nvp_request('RefundTransaction', $args); + $this->email_status("RefundTransaction Response:\n" . MeprUtils::object_to_string($res, true) . "\n", $this->settings->debug); - $_POST['parent_txn_id'] = $txn->id; - return $this->record_refund(); - } + if (!isset($res['ACK']) or strtoupper($res['ACK']) != 'SUCCESS') { + throw new MeprGatewayException(__('The refund was unsuccessful. Please login at PayPal and refund the transaction there.', 'memberpress')); + } - /** This method should be used by the class to record a successful refund from - * the gateway. This method should also be used by any IPN requests or Silent Posts. - */ - public function record_refund() { - $obj = MeprTransaction::get_one_by_trans_num($_POST['parent_txn_id']); + $_POST['parent_txn_id'] = $txn->id; + return $this->record_refund(); + } - if(!is_null($obj) && (int)$obj->id > 0) + /** + * This method should be used by the class to record a successful refund from + * the gateway. This method should also be used by any IPN requests or Silent Posts. + */ + public function record_refund() { - $txn = new MeprTransaction($obj->id); + $obj = MeprTransaction::get_one_by_trans_num($_POST['parent_txn_id']); - // Seriously ... if txn was already refunded what are we doing here? - if($txn->status == MeprTransaction::$refunded_str) { return $txn; } + if (!is_null($obj) && (int)$obj->id > 0) { + $txn = new MeprTransaction($obj->id); - $txn->status = MeprTransaction::$refunded_str; + // Seriously ... if txn was already refunded what are we doing here? + if ($txn->status == MeprTransaction::$refunded_str) { + return $txn; + } - $this->email_status("Processing Refund: \n" . MeprUtils::object_to_string($_POST) . "\n Affected Transaction: \n" . MeprUtils::object_to_string($txn), $this->settings->debug); + $txn->status = MeprTransaction::$refunded_str; - $txn->store(); + $this->email_status("Processing Refund: \n" . MeprUtils::object_to_string($_POST) . "\n Affected Transaction: \n" . MeprUtils::object_to_string($txn), $this->settings->debug); - MeprUtils::send_refunded_txn_notices($txn); + $txn->store(); - return $txn; - } + MeprUtils::send_refunded_txn_notices($txn); + + return $txn; + } - return false; - } - - //Not needed in PayPal since PayPal supports the trial payment inclusive of the Subscription - public function process_trial_payment($transaction) { } - public function record_trial_payment($transaction) { } - - /** Used to send subscription data to a given payment gateway. In gateways - * which redirect before this step is necessary this method should just be - * left blank. - */ - public function process_create_subscription($txn) { - $mepr_options = MeprOptions::fetch(); - $prd = $txn->product(); - $sub = $txn->subscription(); - $usr = $txn->user(); - $tkn = $sub->token; - - //IMPORTANT - PayPal txn will fail if the descriptions do not match exactly - //so if you change the description here you also need to mirror it - //inside of process_signup_form(). - $desc = $this->paypal_desc($txn); - - // Default to 0 for infinite occurrences - $total_occurrences = $sub->limit_cycles ? $sub->limit_cycles_num : 0; - - //Having issues with subscription start times for our friends in Australia and New Zeland - //There doesn't appear to be any fixes available from PayPal -- so we'll have to allow them to modify - //the start time via this filter if it comes to that. - $gmt_utc_time = MeprHooks::apply_filters('mepr-paypal-express-subscr-start-ts', current_time('timestamp', 1), $this); - - $args = array( - 'TOKEN' => $tkn, - 'PROFILESTARTDATE' => gmdate('Y-m-d\TH:i:s\Z', $gmt_utc_time), - 'DESC' => $desc, - 'BILLINGPERIOD' => $this->paypal_period($prd->period_type), - 'BILLINGFREQUENCY' => $prd->period, - 'TOTALBILLINGCYCLES' => $total_occurrences, - 'AMT' => MeprUtils::format_float($txn->total), - 'CURRENCYCODE' => $mepr_options->currency_code, - 'EMAIL' => $usr->user_email, - 'L_PAYMENTREQUEST_0_ITEMCATEGORY0' => 'Digital', // TODO: Assume this for now? - 'L_PAYMENTREQUEST_0_NAME0' => $prd->post_title, - 'L_PAYMENTREQUEST_0_AMT0' => MeprUtils::format_float($txn->total), - 'L_PAYMENTREQUEST_0_QTY0' => 1 - ); - - // Make sure we don't send a trial > 365 days - if($sub->trial && $sub->trial_days > 365) { - $sub->trial_days = 365; - $sub->store(); + return false; } - if($sub->trial && (!$this->trial_matches_billing_period($txn, $sub))) { // Fix for Ross - $args = array_merge( - array( - 'TRIALBILLINGPERIOD' => 'Day', - 'TRIALBILLINGFREQUENCY' => $sub->trial_days, - 'TRIALAMT' => $sub->trial_total, - 'TRIALTOTALBILLINGCYCLES' => 1 - ), - $args - ); + // Not needed in PayPal since PayPal supports the trial payment inclusive of the Subscription + public function process_trial_payment($transaction) + { } - else { //Charge the initial payment IMMEDIATELY using INITAMT - Only doing this when there's no trial period - $args['INITAMT'] = $args['AMT']; //Set initial amount to the regular amount - $args['FAILEDINITAMTACTION'] = 'CancelOnFailure'; - - //Update the billing cycles as initamt doesn't count - if($total_occurrences >= 1) { - $args['TOTALBILLINGCYCLES'] = ($total_occurrences - 1); - } - - //Now adjust the start date to be one cycle out - switch($args['BILLINGPERIOD']) { - case 'Week': - $args['PROFILESTARTDATE'] = strtotime($args['PROFILESTARTDATE']) + MeprUtils::weeks($args['BILLINGFREQUENCY']); - break; - case 'Month': - $args['PROFILESTARTDATE'] = strtotime($args['PROFILESTARTDATE']) + MeprUtils::months($args['BILLINGFREQUENCY']); - break; - case 'Year': - $args['PROFILESTARTDATE'] = strtotime($args['PROFILESTARTDATE']) + MeprUtils::years($args['BILLINGFREQUENCY']); - break; - default: - $args['PROFILESTARTDATE'] = strtotime($args['PROFILESTARTDATE']) + MeprUtils::days($args['BILLINGFREQUENCY']); - break; - } - - //Convert back to a gmdate - $args['PROFILESTARTDATE'] = gmdate('Y-m-d\TH:i:s\Z', $args['PROFILESTARTDATE']); + public function record_trial_payment($transaction) + { } - $args = MeprHooks::apply_filters('mepr_paypal_ec_create_subscription_args', $args, $txn, $sub); + /** + * Used to send subscription data to a given payment gateway. In gateways + * which redirect before this step is necessary this method should just be + * left blank. + */ + public function process_create_subscription($txn) + { + $mepr_options = MeprOptions::fetch(); + $prd = $txn->product(); + $sub = $txn->subscription(); + $usr = $txn->user(); + $tkn = $sub->token; + + // IMPORTANT - PayPal txn will fail if the descriptions do not match exactly + // so if you change the description here you also need to mirror it + // inside of process_signup_form(). + $desc = $this->paypal_desc($txn); + + // Default to 0 for infinite occurrences + $total_occurrences = $sub->limit_cycles ? $sub->limit_cycles_num : 0; + + // Having issues with subscription start times for our friends in Australia and New Zeland + // There doesn't appear to be any fixes available from PayPal -- so we'll have to allow them to modify + // the start time via this filter if it comes to that. + $gmt_utc_time = MeprHooks::apply_filters('mepr-paypal-express-subscr-start-ts', time(), $this); + + $args = [ + 'TOKEN' => $tkn, + 'PROFILESTARTDATE' => gmdate('Y-m-d\TH:i:s\Z', $gmt_utc_time), + 'DESC' => $desc, + 'BILLINGPERIOD' => $this->paypal_period($prd->period_type), + 'BILLINGFREQUENCY' => $prd->period, + 'TOTALBILLINGCYCLES' => $total_occurrences, + 'AMT' => MeprUtils::format_float($txn->total), + 'CURRENCYCODE' => $mepr_options->currency_code, + 'EMAIL' => $usr->user_email, + 'L_PAYMENTREQUEST_0_ITEMCATEGORY0' => 'Digital', // TODO: Assume this for now? + 'L_PAYMENTREQUEST_0_NAME0' => $prd->post_title, + 'L_PAYMENTREQUEST_0_AMT0' => MeprUtils::format_float($txn->total), + 'L_PAYMENTREQUEST_0_QTY0' => 1, + ]; + + // Make sure we don't send a trial > 365 days + if ($sub->trial && $sub->trial_days > 365) { + $sub->trial_days = 365; + $sub->store(); + } - $this->email_status("Paypal Create Subscription \$args:\n".MeprUtils::object_to_string($args,true)."\n", $this->settings->debug); + if ($sub->trial && (!$this->trial_matches_billing_period($txn, $sub))) { // Fix for Ross + $args = array_merge( + [ + 'TRIALBILLINGPERIOD' => 'Day', + 'TRIALBILLINGFREQUENCY' => $sub->trial_days, + 'TRIALAMT' => $sub->trial_total, + 'TRIALTOTALBILLINGCYCLES' => 1, + ], + $args + ); + } else { // Charge the initial payment IMMEDIATELY using INITAMT - Only doing this when there's no trial period + $args['INITAMT'] = $args['AMT']; // Set initial amount to the regular amount + $args['FAILEDINITAMTACTION'] = 'CancelOnFailure'; + + // Update the billing cycles as initamt doesn't count + if ($total_occurrences >= 1) { + $args['TOTALBILLINGCYCLES'] = ($total_occurrences - 1); + } + + // Now adjust the start date to be one cycle out + switch ($args['BILLINGPERIOD']) { + case 'Week': + $args['PROFILESTARTDATE'] = strtotime($args['PROFILESTARTDATE']) + MeprUtils::weeks($args['BILLINGFREQUENCY']); + break; + case 'Month': + $args['PROFILESTARTDATE'] = strtotime($args['PROFILESTARTDATE']) + MeprUtils::months($args['BILLINGFREQUENCY']); + break; + case 'Year': + $args['PROFILESTARTDATE'] = strtotime($args['PROFILESTARTDATE']) + MeprUtils::years($args['BILLINGFREQUENCY']); + break; + default: + $args['PROFILESTARTDATE'] = strtotime($args['PROFILESTARTDATE']) + MeprUtils::days($args['BILLINGFREQUENCY']); + break; + } + + // Convert back to a gmdate + $args['PROFILESTARTDATE'] = gmdate('Y-m-d\TH:i:s\Z', $args['PROFILESTARTDATE']); + } - $res = $this->send_nvp_request('CreateRecurringPaymentsProfile', $args); + $args = MeprHooks::apply_filters('mepr_paypal_ec_create_subscription_args', $args, $txn, $sub); - $_REQUEST['paypal_response'] = $res; - $_REQUEST['transaction'] = $txn; - $_REQUEST['subscription'] = $sub; + $this->email_status("Paypal Create Subscription \$args:\n" . MeprUtils::object_to_string($args, true) . "\n", $this->settings->debug); - return $this->record_create_subscription(); - } + $res = $this->send_nvp_request('CreateRecurringPaymentsProfile', $args); - //So we can take advantage of INITAMT for trial payments - public function trial_matches_billing_period($txn, $sub) { - if(!$sub->trial) { return false; } - if($sub->trial && $sub->trial_amount <= 0) { return false; } //Don't do this for free trials - if($txn->total <= 0) { return false; } //This shouldn't ever happen - if($sub->price != $sub->trial_amount) { return false; } //Trial amount is different + $_REQUEST['paypal_response'] = $res; + $_REQUEST['transaction'] = $txn; + $_REQUEST['subscription'] = $sub; - //Monthly - if($sub->period_type == 'months' && !($sub->trial_days % 30) && $sub->period == ($sub->trial_days / 30)) { - return true; + return $this->record_create_subscription(); } - //Yearly - if($sub->period_type == 'years' && !($sub->trial_days % 365) && $sub->period == ($sub->trial_days / 365)) { - return true; - } + // So we can take advantage of INITAMT for trial payments + public function trial_matches_billing_period($txn, $sub) + { + if (!$sub->trial) { + return false; + } + if ($sub->trial && $sub->trial_amount <= 0) { + return false; + } //Don't do this for free trials + if ($txn->total <= 0) { + return false; + } //This shouldn't ever happen + if ($sub->price != $sub->trial_amount) { + return false; + } //Trial amount is different + + // Monthly + if ($sub->period_type == 'months' && !($sub->trial_days % 30) && $sub->period == ($sub->trial_days / 30)) { + return true; + } + + // Yearly + if ($sub->period_type == 'years' && !($sub->trial_days % 365) && $sub->period == ($sub->trial_days / 365)) { + return true; + } - return false; - } - - /** Used to record a successful subscription by the given gateway. It should have - * the ability to record a successful subscription or a failure. It is this method - * that should be used when receiving an IPN from PayPal or a Silent Post - * from Authorize.net. - */ - public function record_create_subscription() { - $res = $_REQUEST['paypal_response']; - $sub = $_REQUEST['subscription']; - $this->email_status("Paypal Create Subscription Response \$res:\n".MeprUtils::object_to_string($res,true)."\n", $this->settings->debug); - - if( isset($res['L_ERRORCODE0']) and intval($res['L_ERRORCODE0'])==10004 ) { - $this->send_digital_goods_error_message(); - return false; + return false; } - if(isset($res['PROFILESTATUS']) and ( strtolower($res['PROFILESTATUS']) == 'activeprofile' || strtolower($res['PROFILESTATUS']) == 'pendingprofile' )) { - $timestamp = isset($res['TIMESTAMP']) ? strtotime($res['TIMESTAMP']) : time(); - - $sub->subscr_id=$res['PROFILEID']; - $sub->status=MeprSubscription::$active_str; - $sub->created_at = gmdate('c',$timestamp); - $sub->store(); - - $txn = $sub->first_txn(); - if($txn == false || !($txn instanceof MeprTransaction)) { - $txn = new MeprTransaction(); - $txn->user_id = $sub->user_id; - $txn->product_id = $sub->product_id; - $txn->coupon_id = $sub->coupon_id; - } - - $old_total = $txn->total; - $txn->trans_num = $res['PROFILEID']; - $txn->status = MeprTransaction::$confirmed_str; - $txn->txn_type = MeprTransaction::$subscription_confirmation_str; - $txn->set_subtotal(0.00); // Just a confirmation txn - - // At the very least the subscription confirmation transaction gives - // the user a 24 hour grace period so they can log in even before the - // paypal transaction goes through (paypal batches txns at night) - $mepr_options = MeprOptions::fetch(); - - $txn->created_at = MeprUtils::ts_to_mysql_date($timestamp); - - if ($sub->trial) { - $expires_at = MeprUtils::ts_to_mysql_date($timestamp + MeprUtils::days($sub->trial_days), 'Y-m-d 23:59:59'); - } elseif(!$mepr_options->disable_grace_init_days && $mepr_options->grace_init_days > 0) { - $expires_at = MeprUtils::ts_to_mysql_date($timestamp + MeprUtils::days($mepr_options->grace_init_days), 'Y-m-d 23:59:59'); - } else { - $expires_at = $txn->created_at; // Expire immediately - } - - $txn->expires_at = $expires_at; - $txn->store(true); - - // This will only work before maybe_cancel_old_sub is run - $upgrade = $sub->is_upgrade(); - $downgrade = $sub->is_downgrade(); - - $event_txn = $sub->maybe_cancel_old_sub(); - - $this->email_status( "Subscription Transaction\n" . + /** + * Used to record a successful subscription by the given gateway. It should have + * the ability to record a successful subscription or a failure. It is this method + * that should be used when receiving an IPN from PayPal or a Silent Post + * from Authorize.net. + */ + public function record_create_subscription() + { + $res = $_REQUEST['paypal_response']; + $sub = $_REQUEST['subscription']; + $this->email_status("Paypal Create Subscription Response \$res:\n" . MeprUtils::object_to_string($res, true) . "\n", $this->settings->debug); + + if (isset($res['L_ERRORCODE0']) and intval($res['L_ERRORCODE0']) == 10004) { + $this->send_digital_goods_error_message(); + return false; + } + + if (isset($res['PROFILESTATUS']) and ( strtolower($res['PROFILESTATUS']) == 'activeprofile' || strtolower($res['PROFILESTATUS']) == 'pendingprofile' )) { + $timestamp = isset($res['TIMESTAMP']) ? strtotime($res['TIMESTAMP']) : time(); + + $sub->subscr_id = $res['PROFILEID']; + $sub->status = MeprSubscription::$active_str; + $sub->created_at = gmdate('c', $timestamp); + $sub->store(); + + $txn = $sub->first_txn(); + if ($txn == false || !($txn instanceof MeprTransaction)) { + $txn = new MeprTransaction(); + $txn->user_id = $sub->user_id; + $txn->product_id = $sub->product_id; + $txn->coupon_id = $sub->coupon_id; + } + + $old_total = $txn->total; + $txn->trans_num = $res['PROFILEID']; + $txn->status = MeprTransaction::$confirmed_str; + $txn->txn_type = MeprTransaction::$subscription_confirmation_str; + $txn->set_subtotal(0.00); // Just a confirmation txn + + // At the very least the subscription confirmation transaction gives + // the user a 24 hour grace period so they can log in even before the + // paypal transaction goes through (paypal batches txns at night) + $mepr_options = MeprOptions::fetch(); + + $txn->created_at = MeprUtils::ts_to_mysql_date($timestamp); + + if ($sub->trial) { + $expires_at = MeprUtils::ts_to_mysql_date($timestamp + MeprUtils::days($sub->trial_days), 'Y-m-d 23:59:59'); + } elseif (!$mepr_options->disable_grace_init_days && $mepr_options->grace_init_days > 0) { + $expires_at = MeprUtils::ts_to_mysql_date($timestamp + MeprUtils::days($mepr_options->grace_init_days), 'Y-m-d 23:59:59'); + } else { + $expires_at = $txn->created_at; // Expire immediately + } + + $txn->expires_at = $expires_at; + $txn->store(true); + + // This will only work before maybe_cancel_old_sub is run + $upgrade = $sub->is_upgrade(); + $downgrade = $sub->is_downgrade(); + + $event_txn = $sub->maybe_cancel_old_sub(); + + $this->email_status( + "Subscription Transaction\n" . MeprUtils::object_to_string($txn->rec, true), - $this->settings->debug ); + $this->settings->debug + ); + + // $txn->set_gross($old_total); // Artificially set the old amount for notices + if ($upgrade) { + $this->upgraded_sub($sub, $event_txn); + } elseif ($downgrade) { + $this->downgraded_sub($sub, $event_txn); + } else { + $this->new_sub($sub, true); + } + + MeprUtils::send_signup_notices($txn); + + return [ + 'subscription' => $sub, + 'transaction' => $txn, + ]; + } + } - // $txn->set_gross($old_total); // Artificially set the old amount for notices + /** + * Used to cancel a subscription by the given gateway. This method should be used + * by the class to record a successful cancellation from the gateway. This method + * should also be used by any IPN requests or Silent Posts. + * + * With PayPal, we bill the outstanding amount of the previous subscription, + * cancel the previous subscription and create a new subscription + */ + public function process_update_subscription($sub_id) + { + // Account info updated on PayPal.com + } - if($upgrade) { - $this->upgraded_sub($sub, $event_txn); - } - else if($downgrade) { - $this->downgraded_sub($sub, $event_txn); - } - else { - $this->new_sub($sub, true); - } + /** + * This method should be used by the class to record a successful cancellation + * from the gateway. This method should also be used by any IPN requests or + * Silent Posts. + */ + public function record_update_subscription() + { + // Account info updated on PayPal.com + } + + /** + * Used to suspend a subscription by the given gateway. + */ + public function process_suspend_subscription($sub_id) + { + $sub = new MeprSubscription($sub_id); - MeprUtils::send_signup_notices( $txn ); + if ($sub->status == MeprSubscription::$suspended_str) { + throw new MeprGatewayException(__('This subscription has already been paused.', 'memberpress')); + } + + if ($sub->in_free_trial()) { + throw new MeprGatewayException(__('Sorry, subscriptions cannot be paused during a free trial.', 'memberpress')); + } - return array('subscription' => $sub, 'transaction' => $txn); + $this->update_paypal_payment_profile($sub_id, 'Suspend'); + + $_REQUEST['recurring_payment_id'] = $sub->subscr_id; + $this->record_suspend_subscription(); } - } - - /** Used to cancel a subscription by the given gateway. This method should be used - * by the class to record a successful cancellation from the gateway. This method - * should also be used by any IPN requests or Silent Posts. - * - * With PayPal, we bill the outstanding amount of the previous subscription, - * cancel the previous subscription and create a new subscription - */ - public function process_update_subscription($sub_id) { - // Account info updated on PayPal.com - } - - /** This method should be used by the class to record a successful cancellation - * from the gateway. This method should also be used by any IPN requests or - * Silent Posts. - */ - public function record_update_subscription() { - // Account info updated on PayPal.com - } - - /** Used to suspend a subscription by the given gateway. - */ - public function process_suspend_subscription($sub_id) { - $sub = new MeprSubscription($sub_id); - - if($sub->status == MeprSubscription::$suspended_str) { - throw new MeprGatewayException(__('This subscription has already been paused.', 'memberpress')); + + /** + * This method should be used by the class to record a successful suspension + * from the gateway. + */ + public function record_suspend_subscription() + { + $subscr_id = $_REQUEST['recurring_payment_id']; + $sub = MeprSubscription::get_one_by_subscr_id($subscr_id); + + if (!$sub) { + return false; + } + + // Seriously ... if sub was already suspended what are we doing here? + if ($sub->status == MeprSubscription::$suspended_str) { + return $sub; + } + + $sub->status = MeprSubscription::$suspended_str; + $sub->store(); + + MeprUtils::send_suspended_sub_notices($sub); + + return $sub; } - if($sub->in_free_trial()) { - throw new MeprGatewayException(__('Sorry, subscriptions cannot be paused during a free trial.', 'memberpress')); + /** + * Used to suspend a subscription by the given gateway. + */ + public function process_resume_subscription($sub_id) + { + $sub = new MeprSubscription($sub_id); + // Maybe look into this to do a payment right away + // https://developer.paypal.com/docs/classic/api/merchant/DoReferenceTransaction_API_Operation_NVP/ + $this->update_paypal_payment_profile($sub_id, 'Reactivate'); + + $_REQUEST['recurring_payment_id'] = $sub->subscr_id; + $this->record_resume_subscription(); } - $this->update_paypal_payment_profile($sub_id,'Suspend'); - - $_REQUEST['recurring_payment_id'] = $sub->subscr_id; - $this->record_suspend_subscription(); - } - - /** This method should be used by the class to record a successful suspension - * from the gateway. - */ - public function record_suspend_subscription() { - $subscr_id = $_REQUEST['recurring_payment_id']; - $sub = MeprSubscription::get_one_by_subscr_id($subscr_id); - - if(!$sub) { return false; } - - // Seriously ... if sub was already suspended what are we doing here? - if($sub->status == MeprSubscription::$suspended_str) { return $sub; } - - $sub->status = MeprSubscription::$suspended_str; - $sub->store(); - - MeprUtils::send_suspended_sub_notices($sub); - - return $sub; - } - - /** Used to suspend a subscription by the given gateway. - */ - public function process_resume_subscription($sub_id) { - $sub = new MeprSubscription($sub_id); - //Maybe look into this to do a payment right away - //https://developer.paypal.com/docs/classic/api/merchant/DoReferenceTransaction_API_Operation_NVP/ - $this->update_paypal_payment_profile($sub_id,'Reactivate'); - - $_REQUEST['recurring_payment_id'] = $sub->subscr_id; - $this->record_resume_subscription(); - } - - /** This method should be used by the class to record a successful resuming of - * as subscription from the gateway. - */ - public function record_resume_subscription() { - $subscr_id = $_REQUEST['recurring_payment_id']; - $sub = MeprSubscription::get_one_by_subscr_id($subscr_id); - - if(!$sub) { return false; } - - // Seriously ... if sub was already active what are we doing here? - if($sub->status == MeprSubscription::$active_str) { return $sub; } - - $sub->status = MeprSubscription::$active_str; - $sub->store(); - - //Check if prior txn is expired yet or not, if so create a temporary txn so the user can access the content immediately - $prior_txn = $sub->latest_txn(); - if($prior_txn == false || !($prior_txn instanceof MeprTransaction) || strtotime($prior_txn->expires_at) < time()) { - $txn = new MeprTransaction(); - $txn->subscription_id = $sub->id; - $txn->trans_num = $sub->subscr_id . '-' . uniqid(); - $txn->status = MeprTransaction::$confirmed_str; - $txn->txn_type = MeprTransaction::$subscription_confirmation_str; - $txn->expires_at = MeprUtils::ts_to_mysql_date($sub->get_expires_at()); - $txn->set_subtotal(0.00); // Just a confirmation txn - $txn->store(); + /** + * This method should be used by the class to record a successful resuming of + * as subscription from the gateway. + */ + public function record_resume_subscription() + { + $subscr_id = $_REQUEST['recurring_payment_id']; + $sub = MeprSubscription::get_one_by_subscr_id($subscr_id); + + if (!$sub) { + return false; + } + + // Seriously ... if sub was already active what are we doing here? + if ($sub->status == MeprSubscription::$active_str) { + return $sub; + } + + $sub->status = MeprSubscription::$active_str; + $sub->store(); + + // Check if prior txn is expired yet or not, if so create a temporary txn so the user can access the content immediately + $prior_txn = $sub->latest_txn(); + if ($prior_txn == false || !($prior_txn instanceof MeprTransaction) || strtotime($prior_txn->expires_at) < time()) { + $txn = new MeprTransaction(); + $txn->subscription_id = $sub->id; + $txn->trans_num = $sub->subscr_id . '-' . uniqid(); + $txn->status = MeprTransaction::$confirmed_str; + $txn->txn_type = MeprTransaction::$subscription_confirmation_str; + $txn->expires_at = MeprUtils::ts_to_mysql_date($sub->get_expires_at()); + $txn->set_subtotal(0.00); // Just a confirmation txn + $txn->store(); + } + + MeprUtils::send_resumed_sub_notices($sub); + + return $sub; } - MeprUtils::send_resumed_sub_notices($sub); + /** + * Used to cancel a subscription by the given gateway. This method should be used + * by the class to record a successful cancellation from the gateway. This method + * should also be used by any IPN requests or Silent Posts. + */ + public function process_cancel_subscription($sub_id) + { + $sub = new MeprSubscription($sub_id); - return $sub; - } + // Should already expire naturally at paypal so we have no need + // to do this when we're "cancelling" because of a natural expiration + if (!isset($_REQUEST['expire'])) { + $this->update_paypal_payment_profile($sub_id, 'Cancel'); + } - /** Used to cancel a subscription by the given gateway. This method should be used - * by the class to record a successful cancellation from the gateway. This method - * should also be used by any IPN requests or Silent Posts. - */ - public function process_cancel_subscription($sub_id) { - $sub = new MeprSubscription($sub_id); + $_REQUEST['recurring_payment_id'] = $sub->subscr_id; + $this->record_cancel_subscription(); + } - // Should already expire naturally at paypal so we have no need - // to do this when we're "cancelling" because of a natural expiration - if(!isset($_REQUEST['expire'])) - $this->update_paypal_payment_profile($sub_id,'Cancel'); + /** + * This method should be used by the class to record a successful cancellation + * from the gateway. This method should also be used by any IPN requests or + * Silent Posts. + */ + public function record_cancel_subscription() + { + $subscr_id = $_REQUEST['recurring_payment_id']; + $sub = MeprSubscription::get_one_by_subscr_id($subscr_id); - $_REQUEST['recurring_payment_id'] = $sub->subscr_id; - $this->record_cancel_subscription(); - } + if (!$sub) { + return false; + } - /** This method should be used by the class to record a successful cancellation - * from the gateway. This method should also be used by any IPN requests or - * Silent Posts. - */ - public function record_cancel_subscription() { - $subscr_id = $_REQUEST['recurring_payment_id']; - $sub = MeprSubscription::get_one_by_subscr_id($subscr_id); + // Seriously ... if sub was already cancelled what are we doing here? + if ($sub->status == MeprSubscription::$cancelled_str) { + return $sub; + } - if(!$sub) { return false; } + $sub->status = MeprSubscription::$cancelled_str; + $sub->store(); - // Seriously ... if sub was already cancelled what are we doing here? - if($sub->status == MeprSubscription::$cancelled_str) { return $sub; } + // Expire the grace period (confirmation) if no completed payments have come through + // If sub had a free trial, we shouldn't expire that + if ((int)$sub->txn_count <= 0 && (!$sub->trial || $sub->trial_amount > 0)) { + $sub->expire_txns(); + } - $sub->status = MeprSubscription::$cancelled_str; - $sub->store(); + if (isset($_REQUEST['expire'])) { + $sub->limit_reached_actions(); + } - //Expire the grace period (confirmation) if no completed payments have come through - //If sub had a free trial, we shouldn't expire that - if((int)$sub->txn_count <= 0 && (!$sub->trial || $sub->trial_amount > 0)) { $sub->expire_txns(); } + if (!isset($_REQUEST['silent']) || ($_REQUEST['silent'] == false)) { + MeprUtils::send_cancelled_sub_notices($sub); + } - if(isset($_REQUEST['expire'])) { $sub->limit_reached_actions(); } + return $sub; + } - if(!isset($_REQUEST['silent']) || ($_REQUEST['silent'] == false)) { - MeprUtils::send_cancelled_sub_notices($sub); + public function process_signup_form($txn) + { + // Nothing here yet } - return $sub; - } + /** + * This gets called on the 'init' hook when the signup form is processed ... + * this is in place so that payment solutions like paypal can redirect + * before any content is rendered. + */ + public function display_payment_page($txn) + { + $mepr_options = MeprOptions::fetch(); - public function process_signup_form($txn) { - // Nothing here yet - } + if (isset($txn) and $txn instanceof MeprTransaction) { + $usr = $txn->user(); + $prd = $txn->product(); + } else { + return false; + } - /** This gets called on the 'init' hook when the signup form is processed ... - * this is in place so that payment solutions like paypal can redirect - * before any content is rendered. - */ - public function display_payment_page($txn) { - $mepr_options = MeprOptions::fetch(); + if ($txn->amount <= 0.00) { + // Take care of this in display_payment_page + // MeprTransaction::create_free_transaction($txn); + return $txn->checkout_url(); + } - if(isset($txn) and $txn instanceof MeprTransaction) { - $usr = $txn->user(); - $prd = $txn->product(); - } - else { - return false; + if ($txn->gateway == $this->id) { + $mepr_options = MeprOptions::fetch(); + $invoice = $txn->id . '-' . time(); + $useraction = ''; + + // IMPORTANT - PayPal txn will fail if the descriptions do not match exactly + // so if you change the description here you also need to mirror it + // inside of process_create_subscription(). + $desc = $this->paypal_desc($txn); + + $billing_type = (($prd->is_one_time_payment()) ? 'MerchantInitiatedBilling' : 'RecurringPayments'); + $args = [ + 'PAYMENTREQUEST_0_AMT' => MeprUtils::format_float($txn->total), + 'PAYMENTREQUEST_0_PAYMENTACTION' => 'Sale', // Transaction or order? Transaction I assume? + 'PAYMENTREQUEST_0_DESC' => $desc, // Better way to get description working on lifetimes + 'L_BILLINGAGREEMENTDESCRIPTION0' => $desc, + 'L_BILLINGTYPE0' => $billing_type, + 'RETURNURL' => $this->notify_url('return'), + 'CANCELURL' => $this->notify_url('cancel'), + 'L_BILLINGAGREEMENTCUSTOM0' => $txn->id, // Ignored when RecurringPayments is the Type + 'L_PAYMENTTYPE0' => 'InstantOnly', // Ignored when RecurringPayments is the Type + 'PAYMENTREQUEST_0_CURRENCYCODE' => $mepr_options->currency_code, + 'NOSHIPPING' => 1, /* + , // The following two lines are for payments w/out PayPal account (non recurring) + 'SOLUTIONTYPE' => 'Sole', + 'LANDINGPAGE' => 'Billing' */ + ]; + + $args = MeprHooks::apply_filters('mepr_paypal_express_checkout_args', $args); + $this->email_status( + "MemberPress PayPal Request: \n" . MeprUtils::object_to_string($args, true) . "\n", + $this->settings->debug + ); + + $res = $this->send_nvp_request('SetExpressCheckout', $args); + + $this->email_status( + "PayPal Response Object: \n" . MeprUtils::object_to_string($res, true) . "\n", + $this->settings->debug + ); + + $token = ''; + $ack = strtoupper($res['ACK']); + if ($ack == 'SUCCESS' || $ack == 'SUCCESSWITHWARNING') { + $txn->trans_num = $token = urldecode($res['TOKEN']); + $txn->store(); + + if (!$prd->is_one_time_payment() && ($sub = $txn->subscription())) { + $sub->token = $token; + $sub->store(); + } + + MeprUtils::wp_redirect("{$this->settings->url}?cmd=_express-checkout&token={$token}{$useraction}"); + } else { + throw new Exception(__('The connection to PayPal failed', 'memberpress')); + } + } + + throw new Exception(__('There was a problem completing the transaction', 'memberpress')); } - if($txn->amount <= 0.00) { - // Take care of this in display_payment_page - //MeprTransaction::create_free_transaction($txn); - return $txn->checkout_url(); + /** + * This gets called on wp_enqueue_script and enqueues a set of + * scripts for use on the page containing the payment form + */ + public function enqueue_payment_form_scripts() + { + // No need, handled on the PayPal side } - if($txn->gateway == $this->id) { - $mepr_options = MeprOptions::fetch(); - $invoice = $txn->id . '-' . time(); - $useraction = ''; - - //IMPORTANT - PayPal txn will fail if the descriptions do not match exactly - //so if you change the description here you also need to mirror it - //inside of process_create_subscription(). - $desc = $this->paypal_desc($txn); - - $billing_type = (($prd->is_one_time_payment())?'MerchantInitiatedBilling':'RecurringPayments'); - $args = array( - 'PAYMENTREQUEST_0_AMT' => MeprUtils::format_float($txn->total), - 'PAYMENTREQUEST_0_PAYMENTACTION' => 'Sale', // Transaction or order? Transaction I assume? - 'PAYMENTREQUEST_0_DESC' => $desc, //Better way to get description working on lifetimes - 'L_BILLINGAGREEMENTDESCRIPTION0' => $desc, - 'L_BILLINGTYPE0' => $billing_type, - 'RETURNURL' => $this->notify_url('return'), - 'CANCELURL' => $this->notify_url('cancel'), - 'L_BILLINGAGREEMENTCUSTOM0' => $txn->id, // Ignored when RecurringPayments is the Type - 'L_PAYMENTTYPE0' => 'InstantOnly', // Ignored when RecurringPayments is the Type - 'PAYMENTREQUEST_0_CURRENCYCODE' => $mepr_options->currency_code, - 'NOSHIPPING' => 1 /*, // The following two lines are for payments w/out PayPal account (non recurring) - 'SOLUTIONTYPE' => 'Sole', - 'LANDINGPAGE' => 'Billing' */ - ); - - $args = MeprHooks::apply_filters('mepr_paypal_express_checkout_args', $args); - $this->email_status( "MemberPress PayPal Request: \n" . MeprUtils::object_to_string($args, true) . "\n", - $this->settings->debug ); - - $res = $this->send_nvp_request("SetExpressCheckout", $args); - - $this->email_status( "PayPal Response Object: \n" . MeprUtils::object_to_string($res, true) . "\n", - $this->settings->debug ); - - $token=''; - $ack = strtoupper($res['ACK']); - if($ack=='SUCCESS' || $ack=='SUCCESSWITHWARNING') { - $txn->trans_num = $token = urldecode($res["TOKEN"]); - $txn->store(); - - if(!$prd->is_one_time_payment() && ($sub = $txn->subscription())) { - $sub->token = $token; - $sub->store(); - } + /** + * This gets called on the_content and just renders the payment form + */ + public function display_payment_form($amount, $user, $product_id, $transaction_id) + { + // Handled on the PayPal site so we don't have a need for it here + } - MeprUtils::wp_redirect("{$this->settings->url}?cmd=_express-checkout&token={$token}{$useraction}"); - } - else { - throw new Exception(__('The connection to PayPal failed', 'memberpress')); - } + /** + * Validates the payment form before a payment is processed + */ + public function validate_payment_form($errors) + { + // PayPal does this on their own form } - throw new Exception(__('There was a problem completing the transaction', 'memberpress')); - } - - /** This gets called on wp_enqueue_script and enqueues a set of - * scripts for use on the page containing the payment form - */ - public function enqueue_payment_form_scripts() { - // No need, handled on the PayPal side - } - - /** This gets called on the_content and just renders the payment form - */ - public function display_payment_form($amount, $user, $product_id, $transaction_id) { - // Handled on the PayPal site so we don't have a need for it here - } - - /** Validates the payment form before a payment is processed */ - public function validate_payment_form($errors) { - // PayPal does this on their own form - } - - /** Displays the form for the given payment gateway on the MemberPress Options page */ - public function display_options_form() { - $mepr_options = MeprOptions::fetch(); - - $api_username = trim($this->settings->api_username); - $api_password = trim($this->settings->api_password); - $signature = trim($this->settings->signature); - $sandbox = ($this->settings->sandbox=='on' or $this->settings->sandbox==true); - $debug = ($this->settings->debug=='on' or $this->settings->debug==true); - - ?> + /** + * Displays the form for the given payment gateway on the MemberPress Options page + */ + public function display_options_form() + { + $mepr_options = MeprOptions::fetch(); + + $api_username = trim($this->settings->api_username); + $api_password = trim($this->settings->api_password); + $signature = trim($this->settings->signature); + $sandbox = ($this->settings->sandbox == 'on' or $this->settings->sandbox == true); + $debug = ($this->settings->debug == 'on' or $this->settings->debug == true); + + ?> @@ -947,191 +1052,227 @@ public function display_options_form() { - +
    notify_url('ipn')); ?>
    - integrations_str][$this->id]['api_username']) or - empty($_POST[$mepr_options->integrations_str][$this->id]['api_username']) ) - $errors[] = __("PayPal API Username field can't be blank.", 'memberpress'); - else if( !isset($_POST[$mepr_options->integrations_str][$this->id]['api_password']) or - empty($_POST[$mepr_options->integrations_str][$this->id]['api_password']) ) - $errors[] = __("PayPal API Password field can't be blank.", 'memberpress'); - else if( !isset($_POST[$mepr_options->integrations_str][$this->id]['signature']) or - empty($_POST[$mepr_options->integrations_str][$this->id]['signature']) ) - $errors[] = __("PayPal Signature field can't be blank.", 'memberpress'); - - return $errors; - } - - /** Displays the update account form on the subscription account page **/ - public function display_update_account_form($sub_id, $errors=array(), $message='') { - ?> -

    -
    ', ''); ?>
    - settings->sandbox) and $this->settings->sandbox); - } - - public function force_ssl() { - return false; // redirects off site where ssl is installed - } - - private function send_nvp_request($method_name, $args, $method='post', $blocking=true) { - $mepr_options = MeprOptions::fetch(); - $args = array_merge( - array( - 'METHOD' => $method_name, - 'VERSION' => $this->settings->api_version, - 'PWD' => $this->settings->api_password, - 'USER' => $this->settings->api_username, - 'SIGNATURE' => $this->settings->signature - ), - $args - ); - - $args = MeprHooks::apply_filters('mepr_paypal_ec_send_request_args', $args); - - $arg_array = MeprHooks::apply_filters('mepr_paypal_ec_send_request', array( - 'method' => strtoupper($method), - 'body' => $args, - 'timeout' => 15, - 'httpversion' => '1.1', //PayPal is now requiring this - 'blocking' => $blocking, - 'sslverify' => $mepr_options->sslverify, - 'headers' => array() - )); - - //$this->email_status("Sending Paypal Request\n" . MeprUtils::object_to_string($arg_array, true) . "\n", $this->settings->debug); - $resp = wp_remote_request( $this->settings->api_url, $arg_array ); - //$this->email_status("Got Paypal Response\n" . MeprUtils::object_to_string($resp, true) . "\n", $this->settings->debug); - - // If we're not blocking then the response is irrelevant - // So we'll just return true. - if( $blocking==false ) { return true; } - - if( is_wp_error( $resp ) ) { - throw new MeprHttpException( sprintf( __( 'You had an HTTP error connecting to %s' , 'memberpress'), $this->name ) ); + email_status("Paypal Return \$_REQUEST:\n".MeprUtils::object_to_string($_REQUEST,true)."\n", $this->settings->debug); + /** + * Validates the form for the given payment gateway on the MemberPress Options page + */ + public function validate_options_form($errors) + { + $mepr_options = MeprOptions::fetch(); + + if ( + !isset($_POST[$mepr_options->integrations_str][$this->id]['api_username']) or + empty($_POST[$mepr_options->integrations_str][$this->id]['api_username']) + ) { + $errors[] = __("PayPal API Username field can't be blank.", 'memberpress'); + } elseif ( + !isset($_POST[$mepr_options->integrations_str][$this->id]['api_password']) or + empty($_POST[$mepr_options->integrations_str][$this->id]['api_password']) + ) { + $errors[] = __("PayPal API Password field can't be blank.", 'memberpress'); + } elseif ( + !isset($_POST[$mepr_options->integrations_str][$this->id]['signature']) or + empty($_POST[$mepr_options->integrations_str][$this->id]['signature']) + ) { + $errors[] = __("PayPal Signature field can't be blank.", 'memberpress'); + } - $mepr_options = MeprOptions::fetch(); + return $errors; + } - if((isset($_REQUEST['token']) && ($token = $_REQUEST['token'])) || - (isset($_REQUEST['TOKEN']) && ($token = $_REQUEST['TOKEN']))) { - $obj = MeprTransaction::get_one_by_trans_num($token); + /** + * Displays the update account form on the subscription account page + **/ + public function display_update_account_form($sub_id, $errors = [], $message = '') + { + ?> +

    +
    ', ''); ?>
    + load_data($obj); + /** + * Validates the payment form before a payment is processed + */ + public function validate_update_account_form($errors = []) + { + // We'll have them update their cc info on paypal.com + } - $this->email_status("Paypal Transaction \$txn:\n".MeprUtils::object_to_string($txn,true)."\n", $this->settings->debug); + /** + * Actually pushes the account update to the payment processor + */ + public function process_update_account_form($sub_id) + { + // We'll have them update their cc info on paypal.com + } - try { - $this->process_payment_form($txn); - $txn = new MeprTransaction($txn->id); //Grab the txn again, now that we've updated it - $product = new MeprProduct($txn->product_id); - $sanitized_title = sanitize_title($product->post_title); - $query_params = array('membership' => $sanitized_title, 'trans_num' => $txn->trans_num, 'membership_id' => $product->ID); - if($txn->subscription_id > 0) { - $sub = $txn->subscription(); - $query_params = array_merge($query_params, array('subscr_id' => $sub->subscr_id)); - } + /** + * Returns boolean ... whether or not we should be sending in test mode or not + */ + public function is_test_mode() + { + return (isset($this->settings->sandbox) and $this->settings->sandbox); + } - $thankyou_url = $this->do_thankyou_url( $query_params, $txn ); - MeprUtils::wp_redirect( $thankyou_url ); - } - catch( Exception $e ) { - $prd = $txn->product(); - MeprUtils::wp_redirect($prd->url( '?action=payment_form&txn='.$txn->trans_num.'&message='.$e->getMessage().'&_wpnonce='.wp_create_nonce('mepr_payment_form'))); - } + public function force_ssl() + { + return false; // redirects off site where ssl is installed } - } - public function cancel_handler() { - // Handled with a GET REQUEST by PayPal - $this->email_status("Paypal Cancel \$_REQUEST:\n".MeprUtils::object_to_string($_REQUEST,true)."\n", $this->settings->debug); + private function send_nvp_request($method_name, $args, $method = 'post', $blocking = true) + { + $mepr_options = MeprOptions::fetch(); + $args = array_merge( + [ + 'METHOD' => $method_name, + 'VERSION' => $this->settings->api_version, + 'PWD' => $this->settings->api_password, + 'USER' => $this->settings->api_username, + 'SIGNATURE' => $this->settings->signature, + ], + $args + ); + + $args = MeprHooks::apply_filters('mepr_paypal_ec_send_request_args', $args); + + $arg_array = MeprHooks::apply_filters('mepr_paypal_ec_send_request', [ + 'method' => strtoupper($method), + 'body' => $args, + 'timeout' => 15, + 'httpversion' => '1.1', // PayPal is now requiring this + 'blocking' => $blocking, + 'sslverify' => $mepr_options->sslverify, + 'headers' => [], + ]); + + // $this->email_status("Sending Paypal Request\n" . MeprUtils::object_to_string($arg_array, true) . "\n", $this->settings->debug); + $resp = wp_remote_request($this->settings->api_url, $arg_array); + // $this->email_status("Got Paypal Response\n" . MeprUtils::object_to_string($resp, true) . "\n", $this->settings->debug); + // If we're not blocking then the response is irrelevant + // So we'll just return true. + if ($blocking == false) { + return true; + } - if(isset($_REQUEST['token']) and ($token = $_REQUEST['token'])) { - $txn = MeprTransaction::get_one_by_trans_num($token); - $txn = new MeprTransaction($txn->id); + if (is_wp_error($resp)) { + throw new MeprHttpException(sprintf(__('You had an HTTP error connecting to %s', 'memberpress'), $this->name)); + } else { + return wp_parse_args($resp['body']); + } - // Make sure the txn status is pending - $txn->status = MeprTransaction::$pending_str; - $txn->store(); + return false; + } - if( $sub = $txn->subscription() ) { - $sub->status = MeprSubscription::$pending_str; - $sub->store(); - } + public function return_handler() + { + // Handled with a GET REQUEST by PayPal + $this->email_status("Paypal Return \$_REQUEST:\n" . MeprUtils::object_to_string($_REQUEST, true) . "\n", $this->settings->debug); + + $mepr_options = MeprOptions::fetch(); + + if ( + (isset($_REQUEST['token']) && ($token = $_REQUEST['token'])) || + (isset($_REQUEST['TOKEN']) && ($token = $_REQUEST['TOKEN'])) + ) { + $obj = MeprTransaction::get_one_by_trans_num($token); + + $txn = new MeprTransaction(); + $txn->load_data($obj); + + $this->email_status("Paypal Transaction \$txn:\n" . MeprUtils::object_to_string($txn, true) . "\n", $this->settings->debug); + + try { + $this->process_payment_form($txn); + $txn = new MeprTransaction($txn->id); // Grab the txn again, now that we've updated it + $product = new MeprProduct($txn->product_id); + $sanitized_title = sanitize_title($product->post_title); + $query_params = [ + 'membership' => $sanitized_title, + 'trans_num' => $txn->trans_num, + 'membership_id' => $product->ID, + ]; + if ($txn->subscription_id > 0) { + $sub = $txn->subscription(); + $query_params = array_merge($query_params, ['subscr_id' => $sub->subscr_id]); + } + + $thankyou_url = $this->do_thankyou_url($query_params, $txn); + MeprUtils::wp_redirect($thankyou_url); + } catch (Exception $e) { + $prd = $txn->product(); + MeprUtils::wp_redirect($prd->url('?action=payment_form&txn=' . $txn->trans_num . '&message=' . $e->getMessage() . '&_wpnonce=' . wp_create_nonce('mepr_payment_form'))); + } + } + } - if($txn) { - $prd = new MeprProduct($txn->product_id); - // TODO: Send an abandonment email - MeprUtils::wp_redirect($this->message_page_url($prd,'cancel')); - } - else - MeprUtils::wp_redirect(home_url()); + public function cancel_handler() + { + // Handled with a GET REQUEST by PayPal + $this->email_status("Paypal Cancel \$_REQUEST:\n" . MeprUtils::object_to_string($_REQUEST, true) . "\n", $this->settings->debug); + + if (isset($_REQUEST['token']) and ($token = $_REQUEST['token'])) { + $txn = MeprTransaction::get_one_by_trans_num($token); + $txn = new MeprTransaction($txn->id); + + // Make sure the txn status is pending + $txn->status = MeprTransaction::$pending_str; + $txn->store(); + + if ($sub = $txn->subscription()) { + $sub->status = MeprSubscription::$pending_str; + $sub->store(); + } + + if ($txn) { + $prd = new MeprProduct($txn->product_id); + // TODO: Send an abandonment email + MeprUtils::wp_redirect($this->message_page_url($prd, 'cancel')); + } else { + MeprUtils::wp_redirect(home_url()); + } + } } - } - public function cancel_message() { - $mepr_options = MeprOptions::fetch(); - ?> + public function cancel_message() + { + $mepr_options = MeprOptions::fetch(); + ?>

    -

    ', '')); ?>

    - ', '')); ?>

    + + public function payment_failed_message() + { + $mepr_options = MeprOptions::fetch(); + ?>

    -

    ', '')); ?>

    - ', '')); ?>

    + product_id); + private function paypal_desc($txn) + { + $prd = new MeprProduct($txn->product_id); - if($prd->register_price_action == 'hidden' && !empty($prd->post_title)) - return $prd->post_title; - elseif($prd->register_price_action == 'custom' && !empty($prd->register_price) && !$txn->coupon_id && !$txn->prorated) - return "{$prd->post_title} - " . stripslashes($prd->register_price); - else - return "{$prd->post_title} - " . MeprTransactionsHelper::format_currency($txn); - } + if ($prd->register_price_action == 'hidden' && !empty($prd->post_title)) { + return $prd->post_title; + } elseif ($prd->register_price_action == 'custom' && !empty($prd->register_price) && !$txn->coupon_id && !$txn->prorated) { + return "{$prd->post_title} - " . stripslashes($prd->register_price); + } else { + return "{$prd->post_title} - " . MeprTransactionsHelper::format_currency($txn); + } + } - private function update_paypal_payment_profile($sub_id,$action='cancel') { - $sub = new MeprSubscription($sub_id); + private function update_paypal_payment_profile($sub_id, $action = 'cancel') + { + $sub = new MeprSubscription($sub_id); - $args = MeprHooks::apply_filters('mepr_paypal_ec_update_payment_profile_args', array( - 'PROFILEID' => $sub->subscr_id, - 'ACTION' => $action - ), $sub); + $args = MeprHooks::apply_filters('mepr_paypal_ec_update_payment_profile_args', [ + 'PROFILEID' => $sub->subscr_id, + 'ACTION' => $action, + ], $sub); - $this->email_status( "PayPal Update subscription request: \n" . MeprUtils::object_to_string($args, true) . "\n", - $this->settings->debug ); + $this->email_status( + "PayPal Update subscription request: \n" . MeprUtils::object_to_string($args, true) . "\n", + $this->settings->debug + ); - $res = $this->send_nvp_request('ManageRecurringPaymentsProfileStatus', $args); + $res = $this->send_nvp_request('ManageRecurringPaymentsProfileStatus', $args); - $this->email_status( "PayPal Update subscription response: \n" . MeprUtils::object_to_string($res, true) . "\n", - $this->settings->debug ); + $this->email_status( + "PayPal Update subscription response: \n" . MeprUtils::object_to_string($res, true) . "\n", + $this->settings->debug + ); - if( strtolower($res['ACK']) != 'success' ) - throw new MeprGatewayException( __('There was a problem cancelling, try logging in directly at PayPal to update the status of your recurring profile.', 'memberpress') ); + if (strtolower($res['ACK']) != 'success') { + throw new MeprGatewayException(__('There was a problem cancelling, try logging in directly at PayPal to update the status of your recurring profile.', 'memberpress')); + } - $_REQUEST['recurring_payment_id'] = $sub->subscr_id; - } + $_REQUEST['recurring_payment_id'] = $sub->subscr_id; + } } diff --git a/app/gateways/MeprPayPalProGateway.php b/app/gateways/MeprPayPalProGateway.php index babc567..04e88c1 100644 --- a/app/gateways/MeprPayPalProGateway.php +++ b/app/gateways/MeprPayPalProGateway.php @@ -1,826 +1,922 @@ name = __('PayPal Payments Pro', 'memberpress'); - $this->key = __('paypalpro', 'memberpress'); - $this->has_spc_form = false; - $this->set_defaults(); - - $this->capabilities = array( - 'process-payments', - 'process-refunds', - 'create-subscriptions', - 'cancel-subscriptions', - 'update-subscriptions', - 'suspend-subscriptions', - 'resume-subscriptions', - 'subscription-trial-payment' - ); - - // Setup the notification actions for this gateway - $this->notifiers = array( - 'ipn' => 'listener', - 'cancel' => 'cancel_handler', - 'return' => 'return_handler' - ); - - $this->message_pages = array( 'cancel' => 'cancel_message' ); - } - - public function load($settings) { - $this->settings = (object)$settings; - $this->set_defaults(); - } - - protected function set_defaults() { - if(!isset($this->settings)) { - $this->settings = array(); - } - $this->settings = (object)array_merge( - array( - 'gateway' => 'MeprPayPalProGateway', - 'id' => $this->generate_id(), - 'label' => '', - 'use_label' => true, - 'icon' => MEPR_IMAGES_URL . '/checkout/cards.png', - 'use_icon' => true, - 'desc' => __('Pay with your credit card via PayPal', 'memberpress'), - 'use_desc' => true, - 'api_username' => '', - 'api_password' => '', - 'signature' => '', - 'sandbox' => false, - 'force_ssl' => false, - 'debug' => false - ), - (array)$this->settings - ); - - $this->id = $this->settings->id; - $this->label = $this->settings->label; - $this->use_label = $this->settings->use_label; - $this->icon = $this->settings->icon; - $this->use_icon = $this->settings->use_icon; - $this->desc = $this->settings->desc; - $this->use_desc = $this->settings->use_desc; - - if($this->is_test_mode()) { - $this->settings->url = 'https://ipnpb.sandbox.paypal.com/cgi-bin/webscr'; - $this->settings->api_url = 'https://api-3t.sandbox.paypal.com/nvp'; +if (!defined('ABSPATH')) { + die('You are not allowed to call this page directly.'); +} + +class MeprPayPalProGateway extends MeprBasePayPalGateway +{ + // This is stored with the user meta & the subscription meta + public static $paypal_token_str = '_mepr_paypal_pro_token'; + + public $key; + + /** + * Used in the view to identify the gateway + */ + public function __construct() + { + $this->name = __('PayPal Payments Pro', 'memberpress'); + $this->key = __('paypalpro', 'memberpress'); + $this->has_spc_form = false; + $this->set_defaults(); + + $this->capabilities = [ + 'process-payments', + 'process-refunds', + 'create-subscriptions', + 'cancel-subscriptions', + 'update-subscriptions', + 'suspend-subscriptions', + 'resume-subscriptions', + 'subscription-trial-payment', + ]; + + // Setup the notification actions for this gateway + $this->notifiers = [ + 'ipn' => 'listener', + 'cancel' => 'cancel_handler', + 'return' => 'return_handler', + ]; + + $this->message_pages = ['cancel' => 'cancel_message']; } - else { - $this->settings->url = 'https://ipnpb.paypal.com/cgi-bin/webscr'; - $this->settings->api_url = 'https://api-3t.paypal.com/nvp'; + + public function load($settings) + { + $this->settings = (object)$settings; + $this->set_defaults(); } - $this->settings->api_version = 69; + protected function set_defaults() + { + if (!isset($this->settings)) { + $this->settings = []; + } - // An attempt to correct people who paste in spaces along with their credentials - $this->settings->api_username = trim($this->settings->api_username); - $this->settings->api_password = trim($this->settings->api_password); - $this->settings->signature = trim($this->settings->signature); - } + $this->settings = (object)array_merge( + [ + 'gateway' => 'MeprPayPalProGateway', + 'id' => $this->generate_id(), + 'label' => '', + 'use_label' => true, + 'icon' => MEPR_IMAGES_URL . '/checkout/cards.png', + 'use_icon' => true, + 'desc' => __('Pay with your credit card via PayPal', 'memberpress'), + 'use_desc' => true, + 'api_username' => '', + 'api_password' => '', + 'signature' => '', + 'sandbox' => false, + 'force_ssl' => false, + 'debug' => false, + ], + (array)$this->settings + ); + + $this->id = $this->settings->id; + $this->label = $this->settings->label; + $this->use_label = $this->settings->use_label; + $this->icon = $this->settings->icon; + $this->use_icon = $this->settings->use_icon; + $this->desc = $this->settings->desc; + $this->use_desc = $this->settings->use_desc; + + if ($this->is_test_mode()) { + $this->settings->url = 'https://ipnpb.sandbox.paypal.com/cgi-bin/webscr'; + $this->settings->api_url = 'https://api-3t.sandbox.paypal.com/nvp'; + } else { + $this->settings->url = 'https://ipnpb.paypal.com/cgi-bin/webscr'; + $this->settings->api_url = 'https://api-3t.paypal.com/nvp'; + } - /** Listens for an incoming connection from PayPal and then handles the request appropriately. */ - public function listener() { - $_POST = wp_unslash($_POST); - $this->email_status("PayPal IPN Recieved\n" . MeprUtils::object_to_string($_POST, true) . "\n", $this->settings->debug); + $this->settings->api_version = 69; - if($this->validate_ipn()) { return $this->process_ipn(); } + // An attempt to correct people who paste in spaces along with their credentials + $this->settings->api_username = trim($this->settings->api_username); + $this->settings->api_password = trim($this->settings->api_password); + $this->settings->signature = trim($this->settings->signature); + } - return false; - } + /** + * Listens for an incoming connection from PayPal and then handles the request appropriately. + */ + public function listener() + { + $_POST = wp_unslash($_POST); + $this->email_status("PayPal IPN Recieved\n" . MeprUtils::object_to_string($_POST, true) . "\n", $this->settings->debug); - private function process_ipn() { - if(isset($_POST['txn_type']) && - (strtolower($_POST['txn_type'])=='recurring_payment' || - strtolower($_POST['txn_type'])=='subscr_payment')) { - $this->record_subscription_payment(); - } - else if((isset($_POST['txn_type']) && - in_array(strtolower($_POST['txn_type']), - array('recurring_payment_skipped', - 'subscr_failed'))) || - (isset($_POST['payment_status']) && - in_array(strtolower($_POST['payment_status']), - array('denied','expired','failed')))) { - $this->record_payment_failure(); - } - else if( isset($_POST['txn_type']) && - strtolower($_POST['txn_type'])=='recurring_payment_profile_cancel' ) { - $this->record_cancel_subscription(); - } - else if( isset($_POST['txn_type']) && - strtolower($_POST['txn_type'])=='recurring_payment_suspended' ) { - $this->record_suspend_subscription(); - } - else if(isset($_POST['parent_txn_id']) && !isset($_POST['txn_type'])) { - if(in_array(strtolower($_POST['payment_status']),array('refunded','reversed','voided'))) { - return $this->record_refund(); - } - } - } - - /** Used to record a successful recurring payment by the given gateway. It - * should have the ability to record a successful payment or a failure. It is - * this method that should be used when receiving an IPN from PayPal or a - * Silent Post from Authorize.net. - */ - public function record_subscription_payment() { - if(!isset($_POST['recurring_payment_id']) && !isset($_POST['subscr_id'])) { - return; - } + if ($this->validate_ipn()) { + return $this->process_ipn(); + } - if(isset($_POST['subscr_id']) && !empty($_POST['subscr_id'])) { - $sub = MeprSubscription::get_one_by_subscr_id($_POST['subscr_id']); + return false; } - else { - $sub = MeprSubscription::get_one_by_subscr_id($_POST['recurring_payment_id']); + + private function process_ipn() + { + if ( + isset($_POST['txn_type']) && + (strtolower($_POST['txn_type']) == 'recurring_payment' || + strtolower($_POST['txn_type']) == 'subscr_payment') + ) { + $this->record_subscription_payment(); + } elseif ( + (isset($_POST['txn_type']) && + in_array( + strtolower($_POST['txn_type']), + [ + 'recurring_payment_skipped', + 'subscr_failed', + ] + )) || + (isset($_POST['payment_status']) && + in_array( + strtolower($_POST['payment_status']), + ['denied','expired','failed'] + )) + ) { + $this->record_payment_failure(); + } elseif ( + isset($_POST['txn_type']) && + strtolower($_POST['txn_type']) == 'recurring_payment_profile_cancel' + ) { + $this->record_cancel_subscription(); + } elseif ( + isset($_POST['txn_type']) && + strtolower($_POST['txn_type']) == 'recurring_payment_suspended' + ) { + $this->record_suspend_subscription(); + } elseif (isset($_POST['parent_txn_id']) && !isset($_POST['txn_type'])) { + if (in_array(strtolower($_POST['payment_status']), ['refunded','reversed','voided'])) { + return $this->record_refund(); + } + } } - if($sub) { - $timestamp = isset($_POST['payment_date']) ? strtotime($_POST['payment_date']) : time(); - $first_txn = $sub->first_txn(); - - if($first_txn == false || !($first_txn instanceof MeprTransaction)) { - $first_txn = new MeprTransaction(); - $first_txn->user_id = $sub->user_id; - $first_txn->product_id = $sub->product_id; - $first_txn->coupon_id = $sub->coupon_id; - } - - //Prevent recording duplicates - $existing_txn = MeprTransaction::get_one_by_trans_num($_POST['txn_id']); - if( isset($existing_txn->id) && - $existing_txn->id > 0 && - in_array($existing_txn->status, array(MeprTransaction::$complete_str, MeprTransaction::$confirmed_str)) ) { - return; - } - - //If this is a trial payment, let's just convert the confirmation txn into a payment txn - //then we won't have to mess with setting expires_at as it was already handled - if($this->is_subscr_trial_payment($sub)) { - $txn = $first_txn; //For use below in send notices - $txn->created_at = MeprUtils::ts_to_mysql_date($timestamp); - $txn->gateway = $this->id; - $txn->set_gross($_POST['mc_gross']); - $txn->trans_num = $_POST['txn_id']; - $txn->txn_type = MeprTransaction::$payment_str; - $txn->status = MeprTransaction::$complete_str; - $txn->subscription_id = $sub->id; - $txn->store(); - } - else { - $txn = new MeprTransaction(); - $txn->created_at = MeprUtils::ts_to_mysql_date($timestamp); - $txn->user_id = $first_txn->user_id; - $txn->product_id = $first_txn->product_id; - $txn->coupon_id = $first_txn->coupon_id; - $txn->gateway = $this->id; - $txn->set_gross($_POST['mc_gross']); - $txn->trans_num = $_POST['txn_id']; - $txn->txn_type = MeprTransaction::$payment_str; - $txn->status = MeprTransaction::$complete_str; - $txn->subscription_id = $sub->id; - $txn->store(); - - //Check that the subscription status is still enabled - if($sub->status != MeprSubscription::$active_str) { - $sub->status = MeprSubscription::$active_str; - $sub->store(); + /** + * Used to record a successful recurring payment by the given gateway. It + * should have the ability to record a successful payment or a failure. It is + * this method that should be used when receiving an IPN from PayPal or a + * Silent Post from Authorize.net. + */ + public function record_subscription_payment() + { + if (!isset($_POST['recurring_payment_id']) && !isset($_POST['subscr_id'])) { + return; } - // Not waiting for an IPN here bro ... just making it happen even though - // the total occurrences is already capped in record_create_subscription() - $sub->limit_payment_cycles(); - } + if (isset($_POST['subscr_id']) && !empty($_POST['subscr_id'])) { + $sub = MeprSubscription::get_one_by_subscr_id($_POST['subscr_id']); + } else { + $sub = MeprSubscription::get_one_by_subscr_id($_POST['recurring_payment_id']); + } - $this->email_status("Subscription Transaction\n" . + if ($sub) { + $timestamp = isset($_POST['payment_date']) ? strtotime($_POST['payment_date']) : time(); + $first_txn = $sub->first_txn(); + + if ($first_txn == false || !($first_txn instanceof MeprTransaction)) { + $first_txn = new MeprTransaction(); + $first_txn->user_id = $sub->user_id; + $first_txn->product_id = $sub->product_id; + $first_txn->coupon_id = $sub->coupon_id; + } + + // Prevent recording duplicates + $existing_txn = MeprTransaction::get_one_by_trans_num($_POST['txn_id']); + if ( + isset($existing_txn->id) && + $existing_txn->id > 0 && + in_array($existing_txn->status, [MeprTransaction::$complete_str, MeprTransaction::$confirmed_str]) + ) { + return; + } + + // If this is a trial payment, let's just convert the confirmation txn into a payment txn + // then we won't have to mess with setting expires_at as it was already handled + if ($this->is_subscr_trial_payment($sub)) { + $txn = $first_txn; // For use below in send notices + $txn->created_at = MeprUtils::ts_to_mysql_date($timestamp); + $txn->gateway = $this->id; + $txn->set_gross($_POST['mc_gross']); + $txn->trans_num = $_POST['txn_id']; + $txn->txn_type = MeprTransaction::$payment_str; + $txn->status = MeprTransaction::$complete_str; + $txn->subscription_id = $sub->id; + $txn->store(); + } else { + $txn = new MeprTransaction(); + $txn->created_at = MeprUtils::ts_to_mysql_date($timestamp); + $txn->user_id = $first_txn->user_id; + $txn->product_id = $first_txn->product_id; + $txn->coupon_id = $first_txn->coupon_id; + $txn->gateway = $this->id; + $txn->set_gross($_POST['mc_gross']); + $txn->trans_num = $_POST['txn_id']; + $txn->txn_type = MeprTransaction::$payment_str; + $txn->status = MeprTransaction::$complete_str; + $txn->subscription_id = $sub->id; + $txn->store(); + + // Check that the subscription status is still enabled + if ($sub->status != MeprSubscription::$active_str) { + $sub->status = MeprSubscription::$active_str; + $sub->store(); + } + + // Not waiting for an IPN here bro ... just making it happen even though + // the total occurrences is already capped in record_create_subscription() + $sub->limit_payment_cycles(); + } + + $this->email_status( + "Subscription Transaction\n" . MeprUtils::object_to_string($txn->rec, true), - $this->settings->debug); + $this->settings->debug + ); - MeprUtils::send_transaction_receipt_notices( $txn ); + MeprUtils::send_transaction_receipt_notices($txn); - return $txn; - } - - return false; - } + return $txn; + } - /** Used to record a declined payment. */ - public function record_payment_failure() { - if(isset($_POST['ipn_track_id']) && $txn_res = MeprTransaction::get_one_by_trans_num($_POST['ipn_track_id']) && isset($txn_res->id)) { - return false; //We've already recorded this failure duh - don't send more emails + return false; } - elseif(isset($_POST['txn_id']) && $txn_res = MeprTransaction::get_one_by_trans_num($_POST['txn_id']) && isset($txn_res->id)) { - $txn = new MeprTransaction($txn_res->id); - $txn->status = MeprTransaction::$failed_str; - $txn->store(); - } - elseif((isset($_POST['recurring_payment_id']) && + + /** + * Used to record a declined payment. + */ + public function record_payment_failure() + { + if (isset($_POST['ipn_track_id']) && $txn_res = MeprTransaction::get_one_by_trans_num($_POST['ipn_track_id']) && isset($txn_res->id)) { + return false; // We've already recorded this failure duh - don't send more emails + } elseif (isset($_POST['txn_id']) && $txn_res = MeprTransaction::get_one_by_trans_num($_POST['txn_id']) && isset($txn_res->id)) { + $txn = new MeprTransaction($txn_res->id); + $txn->status = MeprTransaction::$failed_str; + $txn->store(); + } elseif ( + (isset($_POST['recurring_payment_id']) && $sub = MeprSubscription::get_one_by_subscr_id($_POST['recurring_payment_id']) ) || - (isset($_POST['subscr_id']) && - $sub = MeprSubscription::get_one_by_subscr_id($_POST['subscr_id']))) { - $first_txn = $sub->first_txn(); - - if($first_txn == false || !($first_txn instanceof MeprTransaction)) { - $coupon_id = $sub->coupon_id; - } - else { - $coupon_id = $first_txn->coupon_id; - } - - $txn = new MeprTransaction(); - $txn->set_gross(isset($_POST['mc_gross'])?$_POST['mc_gross']:(isset($_POST['amount'])?$_POST['amount']:0.00)); - $txn->user_id = $sub->user_id; - $txn->product_id = $sub->product_id; - $txn->coupon_id = $coupon_id; - $txn->txn_type = MeprTransaction::$payment_str; - $txn->status = MeprTransaction::$failed_str; - $txn->subscription_id = $sub->id; - // if ipn_track_id isn't set then just use uniqid - $txn->trans_num = ( isset($_POST['ipn_track_id']) ? $_POST['ipn_track_id'] : uniqid() ); - $txn->gateway = $this->id; - $txn->store(); - - $sub->expire_txns(); //Expire associated transactions for the old subscription - $sub->store(); - } - else { - return false; // Nothing we can do here ... so we outta here + (isset($_POST['subscr_id']) && + $sub = MeprSubscription::get_one_by_subscr_id($_POST['subscr_id'])) + ) { + $first_txn = $sub->first_txn(); + + if ($first_txn == false || !($first_txn instanceof MeprTransaction)) { + $coupon_id = $sub->coupon_id; + } else { + $coupon_id = $first_txn->coupon_id; + } + + $txn = new MeprTransaction(); + $txn->set_gross(isset($_POST['mc_gross']) ? $_POST['mc_gross'] : (isset($_POST['amount']) ? $_POST['amount'] : 0.00)); + $txn->user_id = $sub->user_id; + $txn->product_id = $sub->product_id; + $txn->coupon_id = $coupon_id; + $txn->txn_type = MeprTransaction::$payment_str; + $txn->status = MeprTransaction::$failed_str; + $txn->subscription_id = $sub->id; + // if ipn_track_id isn't set then just use uniqid + $txn->trans_num = ( isset($_POST['ipn_track_id']) ? $_POST['ipn_track_id'] : uniqid() ); + $txn->gateway = $this->id; + $txn->store(); + + $sub->expire_txns(); // Expire associated transactions for the old subscription + $sub->store(); + } else { + return false; // Nothing we can do here ... so we outta here + } + + MeprUtils::send_failed_txn_notices($txn); + + return $txn; } - MeprUtils::send_failed_txn_notices($txn); + /** + * Used to send data to a given payment gateway. In gateways which redirect + * before this step is necessary this method should just be left blank. + */ + public function process_payment($txn) + { + if (isset($txn) and $txn instanceof MeprTransaction) { + $usr = $txn->user(); + $prd = $txn->product(); + } else { + throw new MeprGatewayException(__('Payment was unsuccessful, please check your payment details and try again.', 'memberpress')); + } - return $txn; - } + $mepr_options = MeprOptions::fetch(); - /** Used to send data to a given payment gateway. In gateways which redirect - * before this step is necessary this method should just be left blank. - */ - public function process_payment($txn) { - if(isset($txn) and $txn instanceof MeprTransaction) { - $usr = $txn->user(); - $prd = $txn->product(); - } - else { - throw new MeprGatewayException(__('Payment was unsuccessful, please check your payment details and try again.', 'memberpress')); - } + $prd = $txn->product(); + $sub = $txn->subscription(); + $usr = $txn->user(); + + $args = MeprHooks::apply_filters('mepr_paypal_pro_payment_args', [ + // Pass payment amount and action + 'AMT' => MeprUtils::format_float($txn->total), + 'CURRENCYCODE' => $mepr_options->currency_code, + 'PAYMENTACTION' => 'Sale', + 'CREDITCARDTYPE' => $_REQUEST['mepr-cc-type'], + 'INVNUM' => $txn->id, + + // Pass Credit Card Info + 'ACCT' => sanitize_text_field($_REQUEST['mepr_cc_num']), + 'EXPDATE' => sprintf('%02d', sanitize_text_field($_REQUEST['mepr_cc_exp_month'])) . sprintf('%04d', sanitize_text_field($_REQUEST['mepr_cc_exp_year'])), + 'CVV2' => $_REQUEST['mepr_cvv_code'], + + // Pass User Info + 'EMAIL' => $usr->user_email, + 'FIRSTNAME' => sanitize_text_field(wp_unslash($_REQUEST['mepr_first_name'])), + 'LASTNAME' => sanitize_text_field(wp_unslash($_REQUEST['mepr_last_name'])), + 'STREET' => sanitize_text_field(wp_unslash($_REQUEST['mepr-address-one'])) . ' ' . sanitize_text_field(wp_unslash($_REQUEST['mepr-address-two'])), + 'CITY' => sanitize_text_field(wp_unslash($_REQUEST['mepr-address-city'])), + 'STATE' => sanitize_text_field(wp_unslash($_REQUEST['mepr-address-state'])), + 'ZIP' => sanitize_text_field(wp_unslash($_REQUEST['mepr-address-zip'])), + 'COUNTRYCODE' => sanitize_text_field(wp_unslash($_REQUEST['mepr-address-country'])), + 'IPADDRESS' => sanitize_text_field(wp_unslash($_SERVER['REMOTE_ADDR'])), + 'BUTTONSOURCE' => 'Caseproof_SP', + ], $txn); + + if (empty($usr->first_name) || empty($usr->last_name)) { + $usr->first_name = sanitize_text_field(wp_unslash($_REQUEST['mepr_first_name'])); + $usr->last_name = sanitize_text_field(wp_unslash($_REQUEST['mepr_last_name'])); + update_user_meta($usr->ID, 'first_name', $usr->first_name); + update_user_meta($usr->ID, 'last_name', $usr->last_name); + } - $mepr_options = MeprOptions::fetch(); - - $prd = $txn->product(); - $sub = $txn->subscription(); - $usr = $txn->user(); - - $args = MeprHooks::apply_filters('mepr_paypal_pro_payment_args', array( - // Pass payment amount and action - 'AMT' => MeprUtils::format_float($txn->total), - 'CURRENCYCODE' => $mepr_options->currency_code, - 'PAYMENTACTION' => 'Sale', - 'CREDITCARDTYPE' => $_REQUEST['mepr-cc-type'], - 'INVNUM' => $txn->id, - - // Pass Credit Card Info - 'ACCT' => sanitize_text_field($_REQUEST['mepr_cc_num']), - 'EXPDATE' => sprintf('%02d', sanitize_text_field($_REQUEST['mepr_cc_exp_month']) ).sprintf('%04d', sanitize_text_field($_REQUEST['mepr_cc_exp_year']) ), - 'CVV2' => $_REQUEST['mepr_cvv_code'], - - // Pass User Info - 'EMAIL' => $usr->user_email, - 'FIRSTNAME' => sanitize_text_field(wp_unslash($_REQUEST['mepr_first_name'])), - 'LASTNAME' => sanitize_text_field(wp_unslash($_REQUEST['mepr_last_name'])), - 'STREET' => sanitize_text_field(wp_unslash($_REQUEST['mepr-address-one'])).' '.sanitize_text_field(wp_unslash($_REQUEST['mepr-address-two'])), - 'CITY' => sanitize_text_field(wp_unslash($_REQUEST['mepr-address-city'])), - 'STATE' => sanitize_text_field(wp_unslash($_REQUEST['mepr-address-state'])), - 'ZIP' => sanitize_text_field(wp_unslash($_REQUEST['mepr-address-zip'])), - 'COUNTRYCODE' => sanitize_text_field(wp_unslash($_REQUEST['mepr-address-country'])), - 'IPADDRESS' => sanitize_text_field(wp_unslash($_SERVER['REMOTE_ADDR'])), - 'BUTTONSOURCE' => 'Caseproof_SP' - ), $txn); - - if(empty($usr->first_name) || empty($usr->last_name)) { - $usr->first_name = sanitize_text_field(wp_unslash($_REQUEST['mepr_first_name'])); - $usr->last_name = sanitize_text_field(wp_unslash($_REQUEST['mepr_last_name'])); - update_user_meta($usr->ID, 'first_name', $usr->first_name); - update_user_meta($usr->ID, 'last_name', $usr->last_name); - } + if (!$usr->address_is_set()) { + $usr->set_address($_REQUEST); + } - if(!$usr->address_is_set()) { $usr->set_address($_REQUEST); } + // $this->email_status("DoDirectPayment Request:\n".MeprUtils::object_to_string($args,true)."\n", $this->settings->debug); + $res = $this->send_nvp_request('DoDirectPayment', $args); + // $this->email_status("DoDirectPayment Response:\n".MeprUtils::object_to_string($res,true)."\n", $this->settings->debug); + if (!isset($res['ACK']) || strtoupper($res['ACK']) != 'SUCCESS') { + throw new MeprGatewayException(sprintf(__('The payment was unsuccessful. %s', 'memberpress'), $this->error_str($res))); + } - //$this->email_status("DoDirectPayment Request:\n".MeprUtils::object_to_string($args,true)."\n", $this->settings->debug); - $res = $this->send_nvp_request('DoDirectPayment', $args); - //$this->email_status("DoDirectPayment Response:\n".MeprUtils::object_to_string($res,true)."\n", $this->settings->debug); + $_REQUEST['paypal_response'] = $res; + $_REQUEST['transaction'] = $txn; - if(!isset($res['ACK']) || strtoupper($res['ACK']) != 'SUCCESS') { - throw new MeprGatewayException(sprintf(__('The payment was unsuccessful. %s', 'memberpress'),$this->error_str($res))); + return $this->record_payment(); } - $_REQUEST['paypal_response'] = $res; - $_REQUEST['transaction'] = $txn; - - return $this->record_payment(); - } + /** + * Used to record a successful payment by the given gateway. It should have + * the ability to record a successful payment or a failure. It is this method + * that should be used when receiving an IPN from PayPal or a Silent Post + * from Authorize.net. + */ + public function record_payment() + { + if (!isset($_REQUEST['paypal_response']) || !isset($_REQUEST['transaction'])) { + return false; + } - /** Used to record a successful payment by the given gateway. It should have - * the ability to record a successful payment or a failure. It is this method - * that should be used when receiving an IPN from PayPal or a Silent Post - * from Authorize.net. - */ - public function record_payment() { - if(!isset($_REQUEST['paypal_response']) || !isset($_REQUEST['transaction'])) { return false; } + $res = $_REQUEST['paypal_response']; + $txn = $_REQUEST['transaction']; - $res = $_REQUEST['paypal_response']; - $txn = $_REQUEST['transaction']; + if ($txn->status == MeprTransaction::$complete_str) { + return false; + } - if($txn->status==MeprTransaction::$complete_str) { return false; } + if (strtolower($res['ACK']) == 'success') { + $txn->trans_num = $res['TRANSACTIONID']; + $txn->txn_type = MeprTransaction::$payment_str; + $txn->status = MeprTransaction::$complete_str; - if(strtolower($res['ACK'])=='success') { - $txn->trans_num = $res['TRANSACTIONID']; - $txn->txn_type = MeprTransaction::$payment_str; - $txn->status = MeprTransaction::$complete_str; + // This will only work before maybe_cancel_old_sub is run + $upgrade = $txn->is_upgrade(); + $downgrade = $txn->is_downgrade(); - // This will only work before maybe_cancel_old_sub is run - $upgrade = $txn->is_upgrade(); - $downgrade = $txn->is_downgrade(); + $event_txn = $txn->maybe_cancel_old_sub(); + $txn->store(); - $event_txn = $txn->maybe_cancel_old_sub(); - $txn->store(); + $this->email_status("Transaction\n" . MeprUtils::object_to_string($txn->rec, true) . "\n", $this->settings->debug); - $this->email_status("Transaction\n" . MeprUtils::object_to_string($txn->rec, true) . "\n", $this->settings->debug); + $prd = $txn->product(); - $prd = $txn->product(); + if ($prd->period_type == 'lifetime') { + if ($upgrade) { + $this->upgraded_sub($txn, $event_txn); + } elseif ($downgrade) { + $this->downgraded_sub($txn, $event_txn); + } else { + $this->new_sub($txn); + } - if( $prd->period_type=='lifetime' ) { - if( $upgrade ) { - $this->upgraded_sub($txn, $event_txn); - } - else if( $downgrade ) { - $this->downgraded_sub($txn, $event_txn); - } - else { - $this->new_sub($txn); - } + MeprUtils::send_signup_notices($txn); + } - MeprUtils::send_signup_notices( $txn ); - } + MeprUtils::send_transaction_receipt_notices($txn); - MeprUtils::send_transaction_receipt_notices( $txn ); + return $txn; + } - return $txn; + return false; } - return false; - } + /** + * This method should be used by the class to record a successful refund from + * the gateway. This method should also be used by any IPN requests or Silent Posts. + */ + public function process_refund(MeprTransaction $txn) + { + $mepr_options = MeprOptions::fetch(); - /** This method should be used by the class to record a successful refund from - * the gateway. This method should also be used by any IPN requests or Silent Posts. - */ - public function process_refund(MeprTransaction $txn) { - $mepr_options = MeprOptions::fetch(); + $args = MeprHooks::apply_filters('mepr_paypal_pro_refund_args', [ + 'TRANSACTIONID' => $txn->trans_num, + 'REFUNDTYPE' => 'Full', + 'CURRENCYCODE' => $mepr_options->currency_code, + ], $txn); - $args = MeprHooks::apply_filters('mepr_paypal_pro_refund_args', array( - 'TRANSACTIONID' => $txn->trans_num, - 'REFUNDTYPE' => 'Full', - 'CURRENCYCODE' => $mepr_options->currency_code - ), $txn); + $this->email_status("RefundTransaction Request:\n" . MeprUtils::object_to_string($args, true) . "\n", $this->settings->debug); - $this->email_status("RefundTransaction Request:\n".MeprUtils::object_to_string($args,true)."\n", $this->settings->debug); + $res = $this->send_nvp_request('RefundTransaction', $args); - $res = $this->send_nvp_request('RefundTransaction', $args); + $this->email_status("RefundTransaction Response:\n" . MeprUtils::object_to_string($res, true) . "\n", $this->settings->debug); - $this->email_status("RefundTransaction Response:\n".MeprUtils::object_to_string($res,true)."\n", $this->settings->debug); + if (!isset($res['ACK']) || strtoupper($res['ACK']) != 'SUCCESS') { + throw new MeprGatewayException(sprintf(__('The refund was unsuccessful. %s', 'memberpress'), $this->error_str($res))); + } - if(!isset($res['ACK']) || strtoupper($res['ACK']) != 'SUCCESS') { - throw new MeprGatewayException(sprintf(__('The refund was unsuccessful. %s', 'memberpress'),$this->error_str($res))); + $_POST['parent_txn_id'] = $txn->id; + return $this->record_refund(); } - $_POST['parent_txn_id'] = $txn->id; - return $this->record_refund(); - } + /** + * This method should be used by the class to record a successful refund from + * the gateway. This method should also be used by any IPN requests or Silent Posts. + */ + public function record_refund() + { + $obj = MeprTransaction::get_one_by_trans_num($_POST['parent_txn_id']); - /** This method should be used by the class to record a successful refund from - * the gateway. This method should also be used by any IPN requests or Silent Posts. - */ - public function record_refund() { - $obj = MeprTransaction::get_one_by_trans_num($_POST['parent_txn_id']); + if (!is_null($obj) && (int)$obj->id > 0) { + $txn = new MeprTransaction($obj->id); - if(!is_null($obj) && (int)$obj->id > 0) - { - $txn = new MeprTransaction($obj->id); + // Seriously ... if txn was already refunded what are we doing here? + if ($txn->status == MeprTransaction::$refunded_str) { + return $txn; + } - // Seriously ... if txn was already refunded what are we doing here? - if($txn->status == MeprTransaction::$refunded_str) { return $txn; } + $txn->status = MeprTransaction::$refunded_str; - $txn->status = MeprTransaction::$refunded_str; + $this->email_status("Processing Refund: \n" . MeprUtils::object_to_string($_POST) . "\n Affected Transaction: \n" . MeprUtils::object_to_string($txn), $this->settings->debug); - $this->email_status("Processing Refund: \n" . MeprUtils::object_to_string($_POST) . "\n Affected Transaction: \n" . MeprUtils::object_to_string($txn), $this->settings->debug); + $txn->store(); - $txn->store(); + MeprUtils::send_refunded_txn_notices($txn); - MeprUtils::send_refunded_txn_notices($txn); + return $txn; + } - return $txn; + return false; } - return false; - } - - //Not needed in PayPal since PayPal supports the trial payment inclusive of the Subscription - public function process_trial_payment($transaction) { } - public function record_trial_payment($transaction) { } - - /** Used to send subscription data to a given payment gateway. In gateways - * which redirect before this step is necessary this method should just be - * left blank. - */ - public function process_create_subscription($txn) { - $mepr_options = MeprOptions::fetch(); - $prd = $txn->product(); - $sub = $txn->subscription(); - $usr = $txn->user(); - $tkn = $sub->token; //Pretty sure this isn't used - - //IMPORTANT - PayPal txn will fail if the descriptions do not match exactly - //so if you change the description here you also need to mirror it - //inside of process_signup_form(). - $desc = $this->paypal_desc($txn); - - // Default to 0 for infinite occurrences - $total_occurrences = $sub->limit_cycles ? $sub->limit_cycles_num : 0; - - //Having issues with subscription start times for our friends in Australia and New Zeland - //There doesn't appear to be any fixes available from PayPal -- so we'll have to allow them to modify - //the start time via this filter if it comes to that. - $gmt_utc_time = MeprHooks::apply_filters('mepr-paypal-website-payments-pro-subscr-start-ts', current_time('timestamp', 1), $this); - - $args = array( - 'PROFILESTARTDATE' => gmdate('Y-m-d\TH:i:s\Z', $gmt_utc_time), - 'DESC' => $desc, - 'BILLINGPERIOD' => $this->paypal_period($prd->period_type), - 'BILLINGFREQUENCY' => $prd->period, - 'TOTALBILLINGCYCLES' => $total_occurrences, - 'AMT' => MeprUtils::format_float($txn->amount), - 'CURRENCYCODE' => $mepr_options->currency_code, - 'EMAIL' => $usr->user_email, - 'ACCT' => sanitize_text_field($_REQUEST['mepr_cc_num']), - 'EXPDATE' => sanitize_text_field($_REQUEST['mepr_cc_exp_month']).sanitize_text_field($_REQUEST['mepr_cc_exp_year']), - 'CVV2' => sanitize_text_field($_REQUEST['mepr_cvv_code']), - 'IPADDRESS' => $_SERVER['REMOTE_ADDR'], - 'FIRSTNAME' => sanitize_text_field($_REQUEST['mepr_first_name']), - 'LASTNAME' => sanitize_text_field($_REQUEST['mepr_last_name']), - 'STREET' => sanitize_text_field($_REQUEST['mepr-address-one']), - 'CITY' => sanitize_text_field($_REQUEST['mepr-address-city']), - 'STATE' => sanitize_text_field($_REQUEST['mepr-address-state']), - 'ZIP' => sanitize_text_field($_REQUEST['mepr-address-zip']), - 'COUNTRYCODE' => sanitize_text_field($_REQUEST['mepr-address-country']) - ); - - if($sub->trial) { - $args = array_merge( - array( - 'TRIALBILLINGPERIOD' => 'Day', - 'TRIALBILLINGFREQUENCY' => $sub->trial_days, - 'TRIALAMT' => $sub->trial_total, - 'TRIALTOTALBILLINGCYCLES' => 1 - ), - $args - ); + // Not needed in PayPal since PayPal supports the trial payment inclusive of the Subscription + public function process_trial_payment($transaction) + { } + public function record_trial_payment($transaction) + { + } + + /** + * Used to send subscription data to a given payment gateway. In gateways + * which redirect before this step is necessary this method should just be + * left blank. + */ + public function process_create_subscription($txn) + { + $mepr_options = MeprOptions::fetch(); + $prd = $txn->product(); + $sub = $txn->subscription(); + $usr = $txn->user(); + $tkn = $sub->token; // Pretty sure this isn't used + + // IMPORTANT - PayPal txn will fail if the descriptions do not match exactly + // so if you change the description here you also need to mirror it + // inside of process_signup_form(). + $desc = $this->paypal_desc($txn); + + // Default to 0 for infinite occurrences + $total_occurrences = $sub->limit_cycles ? $sub->limit_cycles_num : 0; + + // Having issues with subscription start times for our friends in Australia and New Zeland + // There doesn't appear to be any fixes available from PayPal -- so we'll have to allow them to modify + // the start time via this filter if it comes to that. + $gmt_utc_time = MeprHooks::apply_filters('mepr-paypal-website-payments-pro-subscr-start-ts', time(), $this); + + $args = [ + 'PROFILESTARTDATE' => gmdate('Y-m-d\TH:i:s\Z', $gmt_utc_time), + 'DESC' => $desc, + 'BILLINGPERIOD' => $this->paypal_period($prd->period_type), + 'BILLINGFREQUENCY' => $prd->period, + 'TOTALBILLINGCYCLES' => $total_occurrences, + 'AMT' => MeprUtils::format_float($txn->amount), + 'CURRENCYCODE' => $mepr_options->currency_code, + 'EMAIL' => $usr->user_email, + 'ACCT' => sanitize_text_field($_REQUEST['mepr_cc_num']), + 'EXPDATE' => sanitize_text_field($_REQUEST['mepr_cc_exp_month']) . sanitize_text_field($_REQUEST['mepr_cc_exp_year']), + 'CVV2' => sanitize_text_field($_REQUEST['mepr_cvv_code']), + 'IPADDRESS' => $_SERVER['REMOTE_ADDR'], + 'FIRSTNAME' => sanitize_text_field($_REQUEST['mepr_first_name']), + 'LASTNAME' => sanitize_text_field($_REQUEST['mepr_last_name']), + 'STREET' => sanitize_text_field($_REQUEST['mepr-address-one']), + 'CITY' => sanitize_text_field($_REQUEST['mepr-address-city']), + 'STATE' => sanitize_text_field($_REQUEST['mepr-address-state']), + 'ZIP' => sanitize_text_field($_REQUEST['mepr-address-zip']), + 'COUNTRYCODE' => sanitize_text_field($_REQUEST['mepr-address-country']), + ]; + + if ($sub->trial) { + $args = array_merge( + [ + 'TRIALBILLINGPERIOD' => 'Day', + 'TRIALBILLINGFREQUENCY' => $sub->trial_days, + 'TRIALAMT' => $sub->trial_total, + 'TRIALTOTALBILLINGCYCLES' => 1, + ], + $args + ); + } - $args = MeprHooks::apply_filters('mepr_paypal_pro_create_subscriptions_args', $args, $txn, $sub); + $args = MeprHooks::apply_filters('mepr_paypal_pro_create_subscriptions_args', $args, $txn, $sub); - if(!$usr->address_is_set()) { $usr->set_address($_REQUEST); } + if (!$usr->address_is_set()) { + $usr->set_address($_REQUEST); + } + + $this->email_status("Paypal Create Subscription \$args:\n" . MeprUtils::object_to_string($args, true) . "\n", $this->settings->debug); - $this->email_status("Paypal Create Subscription \$args:\n".MeprUtils::object_to_string($args,true)."\n", $this->settings->debug); + $res = $this->send_nvp_request('CreateRecurringPaymentsProfile', $args); - $res = $this->send_nvp_request('CreateRecurringPaymentsProfile', $args); + $_REQUEST['paypal_response'] = $res; + $_REQUEST['transaction'] = $txn; + $_REQUEST['subscription'] = $sub; - $_REQUEST['paypal_response'] = $res; - $_REQUEST['transaction'] = $txn; - $_REQUEST['subscription'] = $sub; + return $this->record_create_subscription(); + } - return $this->record_create_subscription(); - } + /** + * Used to record a successful subscription by the given gateway. It should have + * the ability to record a successful subscription or a failure. It is this method + * that should be used when receiving an IPN from PayPal or a Silent Post + * from Authorize.net. + */ + public function record_create_subscription() + { + $res = $_REQUEST['paypal_response']; + $sub = $_REQUEST['subscription']; + $this->email_status("Paypal Create Subscription Response \$res:\n" . MeprUtils::object_to_string($res, true) . "\n", $this->settings->debug); - /** Used to record a successful subscription by the given gateway. It should have - * the ability to record a successful subscription or a failure. It is this method - * that should be used when receiving an IPN from PayPal or a Silent Post - * from Authorize.net. - */ - public function record_create_subscription() { - $res = $_REQUEST['paypal_response']; - $sub = $_REQUEST['subscription']; - $this->email_status("Paypal Create Subscription Response \$res:\n".MeprUtils::object_to_string($res,true)."\n", $this->settings->debug); + if (isset($res['L_ERRORCODE0']) and intval($res['L_ERRORCODE0']) == 10004) { + $this->send_digital_goods_error_message(); + return false; + } - if( isset($res['L_ERRORCODE0']) and intval($res['L_ERRORCODE0'])==10004 ) { - $this->send_digital_goods_error_message(); - return false; + if (isset($res['PROFILESTATUS']) and strtolower($res['PROFILESTATUS']) == 'activeprofile') { + $timestamp = isset($_POST['TIMESTAMP']) ? strtotime($_POST['TIMESTAMP']) : time(); + + $txn = $sub->first_txn(); + if ($txn == false || !($txn instanceof MeprTransaction)) { + $txn = new MeprTransaction(); + $txn->user_id = $sub->user_id; + $txn->product_id = $sub->product_id; + } + + $old_total = $txn->total; + $txn->trans_num = $res['PROFILEID']; + $txn->status = MeprTransaction::$confirmed_str; + $txn->txn_type = MeprTransaction::$subscription_confirmation_str; + $txn->set_subtotal(0.00); // Just a confirmation txn + + // At the very least the subscription confirmation transaction gives + // the user a 24 hour grace period so they can log in even before the + // paypal transaction goes through (paypal batches txns at night) + $mepr_options = MeprOptions::fetch(); + + $trial_days = ( $sub->trial ? $sub->trial_days : $mepr_options->grace_init_days ); + $txn->created_at = MeprUtils::ts_to_mysql_date($timestamp); + $txn->expires_at = MeprUtils::ts_to_mysql_date($timestamp + MeprUtils::days($trial_days), 'Y-m-d H:i:s'); + $txn->store(); + + $sub->subscr_id = $res['PROFILEID']; + $sub->status = MeprSubscription::$active_str; + $sub->created_at = gmdate('c', $timestamp); + + $sub->cc_last4 = substr($_REQUEST['mepr_cc_num'], -4); + $sub->cc_exp_month = sanitize_text_field($_REQUEST['mepr_cc_exp_month']); + $sub->cc_exp_year = sanitize_text_field($_REQUEST['mepr_cc_exp_year']); + + // This will only work before maybe_cancel_old_sub is run + $upgrade = $sub->is_upgrade(); + $downgrade = $sub->is_downgrade(); + + $event_txn = $sub->maybe_cancel_old_sub(); + $sub->store(); + + $this->email_status( + "Subscription Transaction\n" . + MeprUtils::object_to_string($txn->rec, true), + $this->settings->debug + ); + + // $txn->set_gross($old_total); // Artificially set the old amount for notices + if ($upgrade) { + $this->upgraded_sub($sub, $event_txn); + } elseif ($downgrade) { + $this->downgraded_sub($sub, $event_txn); + } else { + $this->new_sub($sub, true); + } + + MeprUtils::send_signup_notices($txn); + + return [ + 'subscription' => $sub, + 'transaction' => $txn, + ]; + } } - if(isset($res['PROFILESTATUS']) and strtolower($res['PROFILESTATUS'])=='activeprofile') { - $timestamp = isset($_POST['TIMESTAMP']) ? strtotime($_POST['TIMESTAMP']) : time(); + /** + * Used to cancel a subscription by the given gateway. This method should be used + * by the class to record a successful cancellation from the gateway. This method + * should also be used by any IPN requests or Silent Posts. + * + * With PayPal, we bill the outstanding amount of the previous subscription, + * cancel the previous subscription and create a new subscription + */ + public function process_update_subscription($sub_id) + { + $sub = new MeprSubscription($sub_id); - $txn = $sub->first_txn(); - if($txn == false || !($txn instanceof MeprTransaction)) { - $txn = new MeprTransaction(); - $txn->user_id = $sub->user_id; - $txn->product_id = $sub->product_id; - } + $args = MeprHooks::apply_filters('mepr_paypal_pro_update_subscription_args', [ + 'PROFILEID' => $sub->subscr_id, + 'CREDITCARDTYPE' => $_REQUEST['mepr-cc-type'], + 'ACCT' => $_REQUEST['update_cc_num'], + 'EXPDATE' => sprintf('%02d', $_REQUEST['update_cc_exp_month']) . sprintf('%04d', $_REQUEST['update_cc_exp_year']), + 'CVV2' => $_REQUEST['update_cvv_code'], + ], $sub); - $old_total = $txn->total; - $txn->trans_num = $res['PROFILEID']; - $txn->status = MeprTransaction::$confirmed_str; - $txn->txn_type = MeprTransaction::$subscription_confirmation_str; - $txn->set_subtotal(0.00); // Just a confirmation txn + $this->email_status( + "PayPal Update Subscription request: \n" . MeprUtils::object_to_string($args, true) . "\n", + $this->settings->debug + ); - // At the very least the subscription confirmation transaction gives - // the user a 24 hour grace period so they can log in even before the - // paypal transaction goes through (paypal batches txns at night) - $mepr_options = MeprOptions::fetch(); + $res = $this->send_nvp_request('UpdateRecurringPaymentsProfile', $args); - $trial_days = ( $sub->trial ? $sub->trial_days : $mepr_options->grace_init_days ); - $txn->created_at = MeprUtils::ts_to_mysql_date($timestamp); - $txn->expires_at = MeprUtils::ts_to_mysql_date($timestamp + MeprUtils::days($trial_days), 'Y-m-d H:i:s'); - $txn->store(); + if (!isset($res['ACK']) || strtoupper($res['ACK']) != 'SUCCESS') { + throw new MeprGatewayException(sprintf(__('Updating the Credit Card was unsuccessful. %s', 'memberpress'), $this->error_str($res))); + } - $sub->subscr_id=$res['PROFILEID']; - $sub->status=MeprSubscription::$active_str; - $sub->created_at = gmdate('c',$timestamp); + $_REQUEST['res'] = $res; + $_REQUEST['sub_id'] = $sub_id; - $sub->cc_last4 = substr($_REQUEST['mepr_cc_num'],-4); - $sub->cc_exp_month = sanitize_text_field($_REQUEST['mepr_cc_exp_month']); - $sub->cc_exp_year = sanitize_text_field($_REQUEST['mepr_cc_exp_year']); + return $this->record_update_subscription(); + } - // This will only work before maybe_cancel_old_sub is run - $upgrade = $sub->is_upgrade(); - $downgrade = $sub->is_downgrade(); + /** + * This method should be used by the class to record a successful cancellation + * from the gateway. This method should also be used by any IPN requests or + * Silent Posts. + */ + public function record_update_subscription() + { + $sub = new MeprSubscription($_REQUEST['sub_id']); + $sub->cc_last4 = substr($_REQUEST['update_cc_num'], -4); + $sub->cc_exp_month = $_REQUEST['update_cc_exp_month']; + $sub->cc_exp_year = $_REQUEST['update_cc_exp_year']; + $sub->store(); - $event_txn = $sub->maybe_cancel_old_sub(); - $sub->store(); + return $_REQUEST['res']; + } - $this->email_status( "Subscription Transaction\n" . - MeprUtils::object_to_string($txn->rec, true), - $this->settings->debug ); + /** + * Used to suspend a subscription by the given gateway. + */ + public function process_suspend_subscription($sub_id) + { + $sub = new MeprSubscription($sub_id); - // $txn->set_gross($old_total); // Artificially set the old amount for notices + if ($sub->status == MeprSubscription::$suspended_str) { + throw new MeprGatewayException(__('This subscription has already been paused.', 'memberpress')); + } - if($upgrade) { - $this->upgraded_sub($sub, $event_txn); - } - else if($downgrade) { - $this->downgraded_sub($sub, $event_txn); - } - else { - $this->new_sub($sub, true); - } + if ($sub->in_free_trial()) { + throw new MeprGatewayException(__('Sorry, subscriptions cannot be paused during a free trial.', 'memberpress')); + } - MeprUtils::send_signup_notices( $txn ); + $this->update_paypal_payment_profile($sub_id, 'Suspend'); - return array('subscription' => $sub, 'transaction' => $txn); - } - } - - /** Used to cancel a subscription by the given gateway. This method should be used - * by the class to record a successful cancellation from the gateway. This method - * should also be used by any IPN requests or Silent Posts. - * - * With PayPal, we bill the outstanding amount of the previous subscription, - * cancel the previous subscription and create a new subscription - */ - public function process_update_subscription($sub_id) { - $sub = new MeprSubscription($sub_id); - - $args = MeprHooks::apply_filters('mepr_paypal_pro_update_subscription_args', array( - 'PROFILEID' => $sub->subscr_id, - 'CREDITCARDTYPE' => $_REQUEST['mepr-cc-type'], - 'ACCT' => $_REQUEST['update_cc_num'], - 'EXPDATE' => sprintf('%02d',$_REQUEST['update_cc_exp_month']).sprintf('%04d',$_REQUEST['update_cc_exp_year']), - 'CVV2' => $_REQUEST['update_cvv_code'], - ), $sub); - - $this->email_status( "PayPal Update Subscription request: \n" . MeprUtils::object_to_string($args, true) . "\n", - $this->settings->debug ); - - $res = $this->send_nvp_request('UpdateRecurringPaymentsProfile', $args); - - if( !isset($res['ACK']) || strtoupper($res['ACK']) != 'SUCCESS' ) { - throw new MeprGatewayException(sprintf(__('Updating the Credit Card was unsuccessful. %s', 'memberpress'),$this->error_str($res))); + $_REQUEST['recurring_payment_id'] = $sub->subscr_id; + $this->record_suspend_subscription(); } - $_REQUEST['res'] = $res; - $_REQUEST['sub_id'] = $sub_id; - - return $this->record_update_subscription(); - } - - /** This method should be used by the class to record a successful cancellation - * from the gateway. This method should also be used by any IPN requests or - * Silent Posts. - */ - public function record_update_subscription() { - $sub = new MeprSubscription($_REQUEST['sub_id']); - $sub->cc_last4 = substr($_REQUEST['update_cc_num'],-4); - $sub->cc_exp_month = $_REQUEST['update_cc_exp_month']; - $sub->cc_exp_year = $_REQUEST['update_cc_exp_year']; - $sub->store(); - - return $_REQUEST['res']; - } - - /** Used to suspend a subscription by the given gateway. - */ - public function process_suspend_subscription($sub_id) { - $sub = new MeprSubscription($sub_id); - - if($sub->status == MeprSubscription::$suspended_str) { - throw new MeprGatewayException(__('This subscription has already been paused.', 'memberpress')); + /** + * This method should be used by the class to record a successful suspension + * from the gateway. + */ + public function record_suspend_subscription() + { + $subscr_id = $_REQUEST['recurring_payment_id']; + $sub = MeprSubscription::get_one_by_subscr_id($subscr_id); + + if (!$sub) { + return false; + } + + // Seriously ... if sub was already suspended what are we doing here? + if ($sub->status == MeprSubscription::$suspended_str) { + return $sub; + } + + $sub->status = MeprSubscription::$suspended_str; + $sub->store(); + + MeprUtils::send_suspended_sub_notices($sub); + + return $sub; } - if($sub->in_free_trial()) { - throw new MeprGatewayException(__('Sorry, subscriptions cannot be paused during a free trial.', 'memberpress')); + /** + * Used to suspend a subscription by the given gateway. + */ + public function process_resume_subscription($sub_id) + { + $sub = new MeprSubscription($sub_id); + $this->update_paypal_payment_profile($sub_id, 'Reactivate'); + + $_REQUEST['recurring_payment_id'] = $sub->subscr_id; + $this->record_resume_subscription(); } - $this->update_paypal_payment_profile($sub_id,'Suspend'); + /** + * This method should be used by the class to record a successful resuming of + * as subscription from the gateway. + */ + public function record_resume_subscription() + { + $subscr_id = $_REQUEST['recurring_payment_id']; + $sub = MeprSubscription::get_one_by_subscr_id($subscr_id); - $_REQUEST['recurring_payment_id'] = $sub->subscr_id; - $this->record_suspend_subscription(); - } + if (!$sub) { + return false; + } - /** This method should be used by the class to record a successful suspension - * from the gateway. - */ - public function record_suspend_subscription() { - $subscr_id = $_REQUEST['recurring_payment_id']; - $sub = MeprSubscription::get_one_by_subscr_id($subscr_id); + // Seriously ... if sub was already active what are we doing here? + if ($sub->status == MeprSubscription::$active_str) { + return $sub; + } - if(!$sub) { return false; } + $sub->status = MeprSubscription::$active_str; + $sub->store(); - // Seriously ... if sub was already suspended what are we doing here? - if($sub->status == MeprSubscription::$suspended_str) { return $sub; } + // Check if prior txn is expired yet or not, if so create a temporary txn so the user can access the content immediately + $prior_txn = $sub->latest_txn(); + if ($prior_txn == false || !($prior_txn instanceof MeprTransaction) || strtotime($prior_txn->expires_at) < time()) { + $txn = new MeprTransaction(); + $txn->subscription_id = $sub->id; + $txn->trans_num = $sub->subscr_id . '-' . uniqid(); + $txn->status = MeprTransaction::$confirmed_str; + $txn->txn_type = MeprTransaction::$subscription_confirmation_str; + $txn->expires_at = MeprUtils::ts_to_mysql_date($sub->get_expires_at()); + $txn->set_subtotal(0.00); // Just a confirmation txn + $txn->store(); + } - $sub->status = MeprSubscription::$suspended_str; - $sub->store(); + MeprUtils::send_resumed_sub_notices($sub); - MeprUtils::send_suspended_sub_notices($sub); + return $sub; + } - return $sub; - } + /** + * Used to cancel a subscription by the given gateway. This method should be used + * by the class to record a successful cancellation from the gateway. This method + * should also be used by any IPN requests or Silent Posts. + */ + public function process_cancel_subscription($sub_id) + { + $sub = new MeprSubscription($sub_id); - /** Used to suspend a subscription by the given gateway. - */ - public function process_resume_subscription($sub_id) { - $sub = new MeprSubscription($sub_id); - $this->update_paypal_payment_profile($sub_id,'Reactivate'); + // Should already expire naturally at paypal so we have no need + // to do this when we're "cancelling" because of a natural expiration + if (!isset($_REQUEST['expire'])) { + $this->update_paypal_payment_profile($sub_id, 'Cancel'); + } - $_REQUEST['recurring_payment_id'] = $sub->subscr_id; - $this->record_resume_subscription(); - } + $_REQUEST['recurring_payment_id'] = $sub->subscr_id; + $this->record_cancel_subscription(); + } - /** This method should be used by the class to record a successful resuming of - * as subscription from the gateway. - */ - public function record_resume_subscription() { - $subscr_id = $_REQUEST['recurring_payment_id']; - $sub = MeprSubscription::get_one_by_subscr_id($subscr_id); + /** + * This method should be used by the class to record a successful cancellation + * from the gateway. This method should also be used by any IPN requests or + * Silent Posts. + */ + public function record_cancel_subscription() + { + $subscr_id = $_REQUEST['recurring_payment_id']; + $sub = MeprSubscription::get_one_by_subscr_id($subscr_id); - if(!$sub) { return false; } + if (!$sub) { + return false; + } - // Seriously ... if sub was already active what are we doing here? - if($sub->status == MeprSubscription::$active_str) { return $sub; } + // Seriously ... if sub was already cancelled what are we doing here? + if ($sub->status == MeprSubscription::$cancelled_str) { + return $sub; + } - $sub->status = MeprSubscription::$active_str; - $sub->store(); + $sub->status = MeprSubscription::$cancelled_str; + $sub->store(); - //Check if prior txn is expired yet or not, if so create a temporary txn so the user can access the content immediately - $prior_txn = $sub->latest_txn(); - if($prior_txn == false || !($prior_txn instanceof MeprTransaction) || strtotime($prior_txn->expires_at) < time()) { - $txn = new MeprTransaction(); - $txn->subscription_id = $sub->id; - $txn->trans_num = $sub->subscr_id . '-' . uniqid(); - $txn->status = MeprTransaction::$confirmed_str; - $txn->txn_type = MeprTransaction::$subscription_confirmation_str; - $txn->expires_at = MeprUtils::ts_to_mysql_date($sub->get_expires_at()); - $txn->set_subtotal(0.00); // Just a confirmation txn - $txn->store(); + if (isset($_REQUEST['expire'])) { + $sub->limit_reached_actions(); + } + + if (!isset($_REQUEST['silent']) || ($_REQUEST['silent'] == false)) { + MeprUtils::send_cancelled_sub_notices($sub); + } + + return $sub; + } + + /** + * This gets called on the 'init' hook when the signup form is processed ... + * this is in place so that payment solutions like paypal can redirect + * before any content is rendered. + */ + public function process_signup_form($txn) + { + // if($txn->amount <= 0.00) { + // MeprTransaction::create_free_transaction($txn); + // return; + // } } - MeprUtils::send_resumed_sub_notices($sub); - - return $sub; - } - - /** Used to cancel a subscription by the given gateway. This method should be used - * by the class to record a successful cancellation from the gateway. This method - * should also be used by any IPN requests or Silent Posts. - */ - public function process_cancel_subscription($sub_id) { - $sub = new MeprSubscription($sub_id); - - // Should already expire naturally at paypal so we have no need - // to do this when we're "cancelling" because of a natural expiration - if(!isset($_REQUEST['expire'])) - $this->update_paypal_payment_profile($sub_id,'Cancel'); - - $_REQUEST['recurring_payment_id'] = $sub->subscr_id; - $this->record_cancel_subscription(); - } - - /** This method should be used by the class to record a successful cancellation - * from the gateway. This method should also be used by any IPN requests or - * Silent Posts. - */ - public function record_cancel_subscription() { - $subscr_id = $_REQUEST['recurring_payment_id']; - $sub = MeprSubscription::get_one_by_subscr_id($subscr_id); - - if(!$sub) { return false; } - - // Seriously ... if sub was already cancelled what are we doing here? - if($sub->status == MeprSubscription::$cancelled_str) { return $sub; } - - $sub->status = MeprSubscription::$cancelled_str; - $sub->store(); - - if(isset($_REQUEST['expire'])) - $sub->limit_reached_actions(); - - if(!isset($_REQUEST['silent']) || ($_REQUEST['silent']==false)) - MeprUtils::send_cancelled_sub_notices($sub); - - return $sub; - } - - /** This gets called on the 'init' hook when the signup form is processed ... - * this is in place so that payment solutions like paypal can redirect - * before any content is rendered. - */ - public function process_signup_form($txn) { - //if($txn->amount <= 0.00) { - // MeprTransaction::create_free_transaction($txn); - // return; - //} - } - - public function display_payment_page($txn) { - // Nothing here yet - } - - /** This gets called on wp_enqueue_script and enqueues a set of - * scripts for use on the page containing the payment form - */ - public function enqueue_payment_form_scripts() { - wp_enqueue_script('mepr-gateway-checkout', MEPR_JS_URL . '/gateway/checkout.js', array('mepr-checkout-js'), MEPR_VERSION); - } - - /** This gets called on wp_enqueue_script and enqueues a set of - * scripts for use on the front end user account page. - * Can be overridden if custom scripts are necessary. - */ - public function enqueue_user_account_scripts() { - if( MeprUtils::valid_url_param('action','update','GET') && // (routing) Are we on the update credit card page? - MeprUtils::valid_url_param('sub', null, 'GET') && // (routing) Do we have a sub url parameter? - MeprSubscription::exists((int)$_GET['sub']) ) { // Does the subscription exist? - $sub = new MeprSubscription((int)$_GET['sub']); - - // Ensure that the gateway associated with the subscription we're updating is for PayPalPro - if($sub->gateway == $this->id) { - wp_enqueue_script('mepr-default-gateway-checkout-js'); - } + public function display_payment_page($txn) + { + // Nothing here yet } - } - - /** This gets called on the_content and just renders the payment form - */ - public function display_payment_form($amount, $user, $product_id, $txn_id) { - $mepr_options = MeprOptions::fetch(); - $prd = new MeprProduct($product_id); - $coupon = false; - - $txn = new MeprTransaction($txn_id); - $usr = $txn->user(); - - //Artifically set the price of the $prd in case a coupon was used - if($prd->price != $amount) { - $coupon = true; - $prd->price = $amount; + + /** + * This gets called on wp_enqueue_script and enqueues a set of + * scripts for use on the page containing the payment form + */ + public function enqueue_payment_form_scripts() + { + wp_enqueue_script('mepr-gateway-checkout', MEPR_JS_URL . '/gateway/checkout.js', ['mepr-checkout-js'], MEPR_VERSION); } - $invoice = MeprTransactionsHelper::get_invoice($txn); - echo $invoice; + /** + * This gets called on wp_enqueue_script and enqueues a set of + * scripts for use on the front end user account page. + * Can be overridden if custom scripts are necessary. + */ + public function enqueue_user_account_scripts() + { + if ( + MeprUtils::valid_url_param('action', 'update', 'GET') && // (routing) Are we on the update credit card page? + MeprUtils::valid_url_param('sub', null, 'GET') && // (routing) Do we have a sub url parameter? + MeprSubscription::exists((int)$_GET['sub']) + ) { // Does the subscription exist? + $sub = new MeprSubscription((int)$_GET['sub']); + + // Ensure that the gateway associated with the subscription we're updating is for PayPalPro + if ($sub->gateway == $this->id) { + wp_enqueue_script('mepr-default-gateway-checkout-js'); + } + } + } - ?> + /** + * This gets called on the_content and just renders the payment form + */ + public function display_payment_form($amount, $user, $product_id, $txn_id) + { + $mepr_options = MeprOptions::fetch(); + $prd = new MeprProduct($product_id); + $coupon = false; + + $txn = new MeprTransaction($txn_id); + $usr = $txn->user(); + + // Artifically set the price of the $prd in case a coupon was used + if ($prd->price != $amount) { + $coupon = true; + $prd->price = $amount; + } + + $invoice = MeprTransactionsHelper::get_invoice($txn); + echo $invoice; + + ?>
    - +
    @@ -870,23 +966,24 @@ public function display_payment_form($amount, $user, $product_id, $txn_id) {
    - first_name) || empty($usr->last_name)): ?> + first_name) || empty($usr->last_name)) : ?>
    - +
    - +
    - + - address_is_set() ): MeprUsersHelper::render_address_fields(); ?> - + address_is_set()) : + MeprUsersHelper::render_address_fields(); ?> + @@ -902,68 +999,77 @@ public function display_payment_form($amount, $user, $product_id, $txn_id) {
    - user(); - $first_name = sanitize_text_field(wp_unslash($_POST['mepr_first_name'])); - $last_name = sanitize_text_field(wp_unslash($_POST['mepr_last_name'])); + public function process_payment_form($txn) + { + // We're just here to update the user's name if they changed it + $user = $txn->user(); + $first_name = sanitize_text_field(wp_unslash($_POST['mepr_first_name'])); + $last_name = sanitize_text_field(wp_unslash($_POST['mepr_last_name'])); - if($user->first_name != $first_name) { - update_user_meta($user->ID, 'first_name', $first_name); - } + if ($user->first_name != $first_name) { + update_user_meta($user->ID, 'first_name', $first_name); + } + + if ($user->last_name != $last_name) { + update_user_meta($user->ID, 'last_name', $last_name); + } - if($user->last_name != $last_name) { - update_user_meta($user->ID, 'last_name', $last_name); + // Call the parent to handle the rest of this + parent::process_payment_form($txn); } - //Call the parent to handle the rest of this - parent::process_payment_form($txn); - } + /** + * Validates the payment form before a payment is processed + */ + public function validate_payment_form($errors) + { + $mepr_options = MeprOptions::fetch(); - /** Validates the payment form before a payment is processed */ - public function validate_payment_form($errors) { - $mepr_options = MeprOptions::fetch(); + if (!isset($_POST['mepr_transaction_id']) || !is_numeric($_POST['mepr_transaction_id'])) { + $errors[] = __('An unknown error has occurred.', 'memberpress'); + } - if(!isset($_POST['mepr_transaction_id']) || !is_numeric($_POST['mepr_transaction_id'])) - $errors[] = __('An unknown error has occurred.', 'memberpress'); + if ( + !$mepr_options->show_fname_lname && + ( !isset($_POST['mepr_first_name']) || empty($_POST['mepr_first_name']) || + !isset($_POST['mepr_last_name']) || empty($_POST['mepr_last_name']) ) + ) { + $errors[] = __('Your first name and last name must not be blank.', 'memberpress'); + } - if( !$mepr_options->show_fname_lname && - ( !isset($_POST['mepr_first_name']) || empty($_POST['mepr_first_name']) || - !isset($_POST['mepr_last_name']) || empty($_POST['mepr_last_name']) ) ) { - $errors[] = __('Your first name and last name must not be blank.', 'memberpress'); - } + if (!isset($_POST['mepr_cc_num']) || empty($_POST['mepr_cc_num'])) { + $errors[] = __('You must enter your Credit Card number.', 'memberpress'); + } elseif (!$this->is_credit_card_valid($_POST['mepr_cc_num'])) { + $errors[] = __('Your credit card number is invalid.', 'memberpress'); + } - if(!isset($_POST['mepr_cc_num']) || empty($_POST['mepr_cc_num'])) { - $errors[] = __('You must enter your Credit Card number.', 'memberpress'); - } - elseif(!$this->is_credit_card_valid($_POST['mepr_cc_num'])) { - $errors[] = __('Your credit card number is invalid.', 'memberpress'); - } + if (!isset($_POST['mepr_cvv_code']) || empty($_POST['mepr_cvv_code'])) { + $errors[] = __('You must enter your CVV code.', 'memberpress'); + } - if(!isset($_POST['mepr_cvv_code']) || empty($_POST['mepr_cvv_code'])) { - $errors[] = __('You must enter your CVV code.', 'memberpress'); + return $errors; } - return $errors; - } - - /** Displays the form for the given payment gateway on the MemberPress Options page */ - public function display_options_form() { - $mepr_options = MeprOptions::fetch(); + /** + * Displays the form for the given payment gateway on the MemberPress Options page + */ + public function display_options_form() + { + $mepr_options = MeprOptions::fetch(); - $api_username = trim($this->settings->api_username); - $api_password = trim($this->settings->api_password); - $signature = trim($this->settings->signature); - $sandbox = ($this->settings->sandbox=='on' or $this->settings->sandbox==true); - $debug = ($this->settings->debug=='on' or $this->settings->debug==true); - $force_ssl = ($this->settings->force_ssl == 'on' or $this->settings->force_ssl == true); + $api_username = trim($this->settings->api_username); + $api_password = trim($this->settings->api_password); + $signature = trim($this->settings->signature); + $sandbox = ($this->settings->sandbox == 'on' or $this->settings->sandbox == true); + $debug = ($this->settings->debug == 'on' or $this->settings->debug == true); + $force_ssl = ($this->settings->force_ssl == 'on' or $this->settings->force_ssl == true); - ?> + ?> @@ -990,53 +1096,65 @@ public function display_options_form() { - +
    notify_url('ipn')); ?>
    - integrations_str][$this->id]['api_username']) or - empty($_POST[$mepr_options->integrations_str][$this->id]['api_username']) ) - $errors[] = __("PayPal API Username field can't be blank.", 'memberpress'); - else if( !isset($_POST[$mepr_options->integrations_str][$this->id]['api_password']) or - empty($_POST[$mepr_options->integrations_str][$this->id]['api_password']) ) - $errors[] = __("PayPal API Password field can't be blank.", 'memberpress'); - else if( !isset($_POST[$mepr_options->integrations_str][$this->id]['signature']) or - empty($_POST[$mepr_options->integrations_str][$this->id]['signature']) ) - $errors[] = __("PayPal Signature field can't be blank.", 'memberpress'); - - return $errors; - } - - /** Displays the update account form on the subscription account page **/ - public function display_update_account_form($sub_id, $errors=array(), $message='') { - $sub = new MeprSubscription($sub_id); - - $last4 = isset($_POST['update_cc_num']) ? substr($_POST['update_cc_num'],-4) : $sub->cc_last4; - $exp_month = isset($_POST['update_cc_exp_month']) ? $_POST['update_cc_exp_month'] : $sub->cc_exp_month; - $exp_year = isset($_POST['update_cc_exp_year']) ? $_POST['update_cc_exp_year'] : $sub->cc_exp_year; - - // Only include the full cc number if there are errors - if(strtolower($_SERVER['REQUEST_METHOD'])=='post' and empty($errors)) { - $sub->cc_last4 = $last4; - $sub->cc_exp_month = $exp_month; - $sub->cc_exp_year = $exp_year; - $sub->store(); - - unset($_POST['update_cvv_code']); // Unset this for security + cc_last4; + + /** + * Validates the form for the given payment gateway on the MemberPress Options page + */ + public function validate_options_form($errors) + { + $mepr_options = MeprOptions::fetch(); + + if ( + !isset($_POST[$mepr_options->integrations_str][$this->id]['api_username']) or + empty($_POST[$mepr_options->integrations_str][$this->id]['api_username']) + ) { + $errors[] = __("PayPal API Username field can't be blank.", 'memberpress'); + } elseif ( + !isset($_POST[$mepr_options->integrations_str][$this->id]['api_password']) or + empty($_POST[$mepr_options->integrations_str][$this->id]['api_password']) + ) { + $errors[] = __("PayPal API Password field can't be blank.", 'memberpress'); + } elseif ( + !isset($_POST[$mepr_options->integrations_str][$this->id]['signature']) or + empty($_POST[$mepr_options->integrations_str][$this->id]['signature']) + ) { + $errors[] = __("PayPal Signature field can't be blank.", 'memberpress'); + } + + return $errors; } - $ccv_code = isset($_POST['update_cvv_code'])?$_POST['update_cvv_code']:''; - $exp = sprintf('%02d / %d', $exp_month, $exp_year); + /** + * Displays the update account form on the subscription account page + **/ + public function display_update_account_form($sub_id, $errors = [], $message = '') + { + $sub = new MeprSubscription($sub_id); + + $last4 = isset($_POST['update_cc_num']) ? substr($_POST['update_cc_num'], -4) : $sub->cc_last4; + $exp_month = isset($_POST['update_cc_exp_month']) ? $_POST['update_cc_exp_month'] : $sub->cc_exp_month; + $exp_year = isset($_POST['update_cc_exp_year']) ? $_POST['update_cc_exp_year'] : $sub->cc_exp_year; + + // Only include the full cc number if there are errors + if (strtolower($_SERVER['REQUEST_METHOD']) == 'post' and empty($errors)) { + $sub->cc_last4 = $last4; + $sub->cc_exp_month = $exp_month; + $sub->cc_exp_year = $exp_year; + $sub->store(); + + unset($_POST['update_cvv_code']); // Unset this for security + } else { // If there are errors then show the full cc num ... if it's there + $last4 = isset($_POST['update_cc_num']) ? $_POST['update_cc_num'] : $sub->cc_last4; + } + + $ccv_code = isset($_POST['update_cvv_code']) ? $_POST['update_cvv_code'] : ''; + $exp = sprintf('%02d / %d', $exp_month, $exp_year); - ?> + ?>
    @@ -1094,178 +1212,206 @@ public function display_update_account_form($sub_id, $errors=array(), $message='
    - is_credit_card_valid($_POST['update_cc_num'])) - $errors[] = __('Your credit card number is invalid.', 'memberpress'); - - if(!isset($_POST['update_cvv_code']) || empty($_POST['update_cvv_code'])) - $errors[] = __('You must enter your CVV code.', 'memberpress'); - - return $errors; - } - - /** Actually pushes the account update to the payment processor */ - public function process_update_account_form($sub_id) { - return $this->process_update_subscription($sub_id); - } - - /** Returns boolean ... whether or not we should be sending in test mode or not */ - public function is_test_mode() { - return (isset($this->settings->sandbox) and $this->settings->sandbox); - } - - public function force_ssl() { - return (isset($this->settings->force_ssl) and ($this->settings->force_ssl == 'on' or $this->settings->force_ssl == true)); - } - - private function send_nvp_request($method_name, $args, $method='post', $blocking=true) { - $mepr_options = MeprOptions::fetch(); - $args = array_merge( - array( - 'VERSION' => $this->settings->api_version, - 'SIGNATURE' => $this->settings->signature, - 'USER' => $this->settings->api_username, - 'PWD' => $this->settings->api_password, - 'METHOD' => $method_name - ), - $args - ); - - $args = MeprHooks::apply_filters('mepr_paypal_pro_send_request_args', $args); - - $arg_array = MeprHooks::apply_filters('mepr_paypal_pro_send_request', array( - 'method' => strtoupper($method), - 'headers' => array('PAYPAL-NVP' => 'Y'), - 'body' => $args, - 'timeout' => 70, - 'blocking' => $blocking, - 'sslverify' => $mepr_options->sslverify, - 'user-agent' => 'MemberPress', - 'httpversion' => '1.1' //PayPal is now requiring this - )); - - $this->email_status("Sending Paypal Request to {$this->settings->api_url}\n" . MeprUtils::object_to_string($arg_array, true) . "\n" . json_encode($arg_array), $this->settings->debug); - - $resp = wp_remote_post($this->settings->api_url, $arg_array); - - $this->email_status("Got Paypal Response\n" . MeprUtils::object_to_string($resp, true) . "\n", $this->settings->debug); - - // If we're not blocking then the response is irrelevant - // So we'll just return true. - if( $blocking==false ) { return true; } - - if( is_wp_error( $resp ) ) { - throw new MeprHttpException( sprintf( __( 'You had an HTTP error connecting to %s' , 'memberpress'), $this->name ) ); + is_credit_card_valid($_POST['update_cc_num'])) { + $errors[] = __('Your credit card number is invalid.', 'memberpress'); + } + + if (!isset($_POST['update_cvv_code']) || empty($_POST['update_cvv_code'])) { + $errors[] = __('You must enter your CVV code.', 'memberpress'); + } - return $resp_args; + return $errors; } - return false; - } + /** + * Actually pushes the account update to the payment processor + */ + public function process_update_account_form($sub_id) + { + return $this->process_update_subscription($sub_id); + } - public function return_handler() { - // Handled with a GET REQUEST by PayPal - $this->email_status("Paypal Return \$_REQUEST:\n".MeprUtils::object_to_string($_REQUEST,true)."\n", $this->settings->debug); + /** + * Returns boolean ... whether or not we should be sending in test mode or not + */ + public function is_test_mode() + { + return (isset($this->settings->sandbox) and $this->settings->sandbox); + } - $mepr_options = MeprOptions::fetch(); + public function force_ssl() + { + return (isset($this->settings->force_ssl) and ($this->settings->force_ssl == 'on' or $this->settings->force_ssl == true)); + } - if( ( isset($_REQUEST['token']) and ($token = $_REQUEST['token']) ) || - ( isset($_REQUEST['TOKEN']) and ($token = $_REQUEST['TOKEN']) ) ) { - $obj = MeprTransaction::get_one_by_trans_num($token); + private function send_nvp_request($method_name, $args, $method = 'post', $blocking = true) + { + $mepr_options = MeprOptions::fetch(); + $args = array_merge( + [ + 'VERSION' => $this->settings->api_version, + 'SIGNATURE' => $this->settings->signature, + 'USER' => $this->settings->api_username, + 'PWD' => $this->settings->api_password, + 'METHOD' => $method_name, + ], + $args + ); + + $args = MeprHooks::apply_filters('mepr_paypal_pro_send_request_args', $args); + + $arg_array = MeprHooks::apply_filters('mepr_paypal_pro_send_request', [ + 'method' => strtoupper($method), + 'headers' => ['PAYPAL-NVP' => 'Y'], + 'body' => $args, + 'timeout' => 70, + 'blocking' => $blocking, + 'sslverify' => $mepr_options->sslverify, + 'user-agent' => 'MemberPress', + 'httpversion' => '1.1', // PayPal is now requiring this + ]); + + $this->email_status("Sending Paypal Request to {$this->settings->api_url}\n" . MeprUtils::object_to_string($arg_array, true) . "\n" . json_encode($arg_array), $this->settings->debug); + + $resp = wp_remote_post($this->settings->api_url, $arg_array); + + $this->email_status("Got Paypal Response\n" . MeprUtils::object_to_string($resp, true) . "\n", $this->settings->debug); + + // If we're not blocking then the response is irrelevant + // So we'll just return true. + if ($blocking == false) { + return true; + } - $txn = new MeprTransaction(); - $txn->load_data($obj); + if (is_wp_error($resp)) { + throw new MeprHttpException(sprintf(__('You had an HTTP error connecting to %s', 'memberpress'), $this->name)); + } else { + $resp_args = wp_parse_args($resp['body']); - $this->email_status("Paypal Transaction \$txn:\n".MeprUtils::object_to_string($txn,true)."\n", $this->settings->debug); + if (isset($resp_args['ACK']) && strtolower($resp_args['ACK']) != 'success') { + throw new MeprHttpException($resp_args['L_LONGMESSAGE0']); + } - try { - $this->process_payment_form($txn); - $txn = new MeprTransaction($txn->id); //Grab the txn again, now that we've updated it - $product = new MeprProduct($txn->product_id); - $sanitized_title = sanitize_title($product->post_title); - $query_params = array('membership' => $sanitized_title, 'trans_num' => $txn->trans_num, 'membership_id' => $product->ID); - if($txn->subscription_id > 0) { - $sub = $txn->subscription(); - $query_params = array_merge($query_params, array('subscr_id' => $sub->subscr_id)); + return $resp_args; } - $thankyou_url = $this->do_thankyou_url( $query_params, $txn ); - - MeprUtils::wp_redirect( $thankyou_url ); - } - catch( Exception $e ) { - $prd = $txn->product(); - MeprUtils::wp_redirect($prd->url( '?action=payment_form&txn='.$txn->trans_num.'&message='.$e->getMessage().'&_wpnonce='.wp_create_nonce('mepr_payment_form'))); - } + return false; } - } - public function cancel_handler() { - // Handled with a GET REQUEST by PayPal - $this->email_status("Paypal Cancel \$_REQUEST:\n".MeprUtils::object_to_string($_REQUEST,true)."\n", $this->settings->debug); - - if(isset($_REQUEST['token']) and ($token = $_REQUEST['token'])) { - $txn = MeprTransaction::get_one_by_trans_num($token); - $txn = new MeprTransaction($txn->id); + public function return_handler() + { + // Handled with a GET REQUEST by PayPal + $this->email_status("Paypal Return \$_REQUEST:\n" . MeprUtils::object_to_string($_REQUEST, true) . "\n", $this->settings->debug); + + $mepr_options = MeprOptions::fetch(); + + if ( + ( isset($_REQUEST['token']) and ($token = $_REQUEST['token']) ) || + ( isset($_REQUEST['TOKEN']) and ($token = $_REQUEST['TOKEN']) ) + ) { + $obj = MeprTransaction::get_one_by_trans_num($token); + + $txn = new MeprTransaction(); + $txn->load_data($obj); + + $this->email_status("Paypal Transaction \$txn:\n" . MeprUtils::object_to_string($txn, true) . "\n", $this->settings->debug); + + try { + $this->process_payment_form($txn); + $txn = new MeprTransaction($txn->id); // Grab the txn again, now that we've updated it + $product = new MeprProduct($txn->product_id); + $sanitized_title = sanitize_title($product->post_title); + $query_params = [ + 'membership' => $sanitized_title, + 'trans_num' => $txn->trans_num, + 'membership_id' => $product->ID, + ]; + if ($txn->subscription_id > 0) { + $sub = $txn->subscription(); + $query_params = array_merge($query_params, ['subscr_id' => $sub->subscr_id]); + } + + $thankyou_url = $this->do_thankyou_url($query_params, $txn); + + MeprUtils::wp_redirect($thankyou_url); + } catch (Exception $e) { + $prd = $txn->product(); + MeprUtils::wp_redirect($prd->url('?action=payment_form&txn=' . $txn->trans_num . '&message=' . $e->getMessage() . '&_wpnonce=' . wp_create_nonce('mepr_payment_form'))); + } + } + } - // Make sure the txn status is pending - $txn->status = MeprTransaction::$pending_str; - $txn->store(); + public function cancel_handler() + { + // Handled with a GET REQUEST by PayPal + $this->email_status("Paypal Cancel \$_REQUEST:\n" . MeprUtils::object_to_string($_REQUEST, true) . "\n", $this->settings->debug); + + if (isset($_REQUEST['token']) and ($token = $_REQUEST['token'])) { + $txn = MeprTransaction::get_one_by_trans_num($token); + $txn = new MeprTransaction($txn->id); + + // Make sure the txn status is pending + $txn->status = MeprTransaction::$pending_str; + $txn->store(); + + if ($sub = $txn->subscription()) { + $sub->status = MeprSubscription::$pending_str; + $sub->store(); + } + + if ($txn) { + $prd = new MeprProduct($txn->product_id); + // TODO: Send an abandonment email + MeprUtils::wp_redirect($this->message_page_url($prd, 'cancel')); + } else { + MeprUtils::wp_redirect(home_url()); + } + } + } - if( $sub = $txn->subscription() ) { - $sub->status = MeprSubscription::$pending_str; - $sub->store(); - } + public function cancel_message() + { + $mepr_options = MeprOptions::fetch(); + ?> +

    +

    ', '')); ?>

    + product_id); - // TODO: Send an abandonment email - MeprUtils::wp_redirect($this->message_page_url($prd,'cancel')); - } - else - MeprUtils::wp_redirect(home_url()); + public function paypal_period($period_type) + { + if ($period_type == 'months') { + return 'Month'; + } elseif ($period_type == 'years') { + return 'Year'; + } elseif ($period_type == 'weeks') { + return 'Week'; + } else { + return $period_type; + } } - } - public function cancel_message() { - $mepr_options = MeprOptions::fetch(); - ?> -

    -

    ', '')); ?>

    - product_id); - private function paypal_desc($txn) { - $prd = new MeprProduct($txn->product_id); + if ($prd->register_price_action == 'hidden' && !empty($prd->post_title)) { + return $prd->post_title; + } elseif ($prd->register_price_action == 'custom' && !empty($prd->register_price) && !$txn->coupon_id && !$txn->prorated) { + return "{$prd->post_title} - " . stripslashes($prd->register_price); + } else { + return "{$prd->post_title} - " . MeprTransactionsHelper::format_currency($txn); + } + } - if($prd->register_price_action == 'hidden' && !empty($prd->post_title)) - return $prd->post_title; - elseif($prd->register_price_action == 'custom' && !empty($prd->register_price) && !$txn->coupon_id && !$txn->prorated) - return "{$prd->post_title} - " . stripslashes($prd->register_price); - else - return "{$prd->post_title} - " . MeprTransactionsHelper::format_currency($txn); - } + private function update_paypal_payment_profile($sub_id, $action = 'cancel') + { + $sub = new MeprSubscription($sub_id); - private function update_paypal_payment_profile($sub_id,$action='cancel') { - $sub = new MeprSubscription($sub_id); + $args = MeprHooks::apply_filters('mepr_paypal_pro_update_payment_profile_args', [ + 'PROFILEID' => $sub->subscr_id, + 'ACTION' => $action, + ], $sub); - $args = MeprHooks::apply_filters('mepr_paypal_pro_update_payment_profile_args', array( - 'PROFILEID' => $sub->subscr_id, - 'ACTION' => $action - ), $sub); + $this->email_status( + "PayPal Update subscription request: \n" . MeprUtils::object_to_string($args, true) . "\n", + $this->settings->debug + ); - $this->email_status("PayPal Update subscription request: \n" . MeprUtils::object_to_string($args, true) . "\n", - $this->settings->debug); + $res = $this->send_nvp_request('ManageRecurringPaymentsProfileStatus', $args); - $res = $this->send_nvp_request('ManageRecurringPaymentsProfileStatus', $args); + $this->email_status( + "PayPal Update subscription response: \n" . MeprUtils::object_to_string($res, true) . "\n", + $this->settings->debug + ); - $this->email_status("PayPal Update subscription response: \n" . MeprUtils::object_to_string($res, true) . "\n", - $this->settings->debug); + if (strtolower($res['ACK']) != 'success') { + throw new MeprGatewayException(sprintf(__('There was a problem cancelling. %s', 'memberpress'), $this->error_str($res))); + } - if(strtolower($res['ACK']) != 'success') { - throw new MeprGatewayException(sprintf(__('There was a problem cancelling. %s', 'memberpress'),$this->error_str($res))); + $_REQUEST['recurring_payment_id'] = $sub->subscr_id; } - $_REQUEST['recurring_payment_id'] = $sub->subscr_id; - } - - private function error_str($res) { - $error_str = isset($res['L_SEVERITYCODE0']) ? $res['L_SEVERITYCODE0'] : ''; - $error_str .= ' '; - $error_str .= isset($res['L_ERRORCODE0']) ? $res['L_ERRORCODE0'] : ''; - $error_str .= ' '; - $error_str .= isset($res['L_SHORTMESSAGE0']) ? $res['L_SHORTMESSAGE0'] : ''; + private function error_str($res) + { + $error_str = isset($res['L_SEVERITYCODE0']) ? $res['L_SEVERITYCODE0'] : ''; + $error_str .= ' '; + $error_str .= isset($res['L_ERRORCODE0']) ? $res['L_ERRORCODE0'] : ''; + $error_str .= ' '; + $error_str .= isset($res['L_SHORTMESSAGE0']) ? $res['L_SHORTMESSAGE0'] : ''; - return $error_str; - } + return $error_str; + } } diff --git a/app/gateways/MeprPayPalStandardGateway.php b/app/gateways/MeprPayPalStandardGateway.php index 944edb5..93818a4 100644 --- a/app/gateways/MeprPayPalStandardGateway.php +++ b/app/gateways/MeprPayPalStandardGateway.php @@ -1,1129 +1,1204 @@ name = __("PayPal Standard", 'memberpress'); - $this->key = __('paypal', 'memberpress'); - $this->has_spc_form = true; - $this->set_defaults(); - - // Setup the notification actions for this gateway - $this->notifiers = array( 'ipn' => 'listener', - 'cancel' => 'cancel_handler', - 'return' => 'return_handler' ); - $this->message_pages = array('cancel' => 'cancel_message'); - } - - public function load($settings) { - $this->settings = (object)$settings; - $this->set_defaults(); - } - - protected function set_defaults() { - if(!isset($this->settings)) - $this->settings = array(); - - $this->settings = - (object)array_merge( - array( - 'gateway' => 'MeprPayPalStandardGateway', - 'id' => $this->generate_id(), - 'label' => '', - 'use_label' => true, - 'icon' => MEPR_IMAGES_URL . '/checkout/paypal.png', - 'use_icon' => true, - 'desc' => __('Pay via your PayPal account', 'memberpress'), - 'use_desc' => true, - 'paypal_email' => '', - 'advanced_mode' => false, - 'api_username' => '', - 'api_password' => '', - 'signature' => '', - 'sandbox' => false, - 'debug' => false - ), - (array)$this->settings - ); - - $this->id = $this->settings->id; - $this->label = $this->settings->label; - $this->use_label = $this->settings->use_label; - $this->icon = $this->settings->icon; - $this->use_icon = $this->settings->use_icon; - $this->desc = $this->settings->desc; - $this->use_desc = $this->settings->use_desc; - - if($this->is_test_mode()) { - $this->settings->url = 'https://ipnpb.sandbox.paypal.com/cgi-bin/webscr'; - $this->settings->api_url = 'https://api-3t.sandbox.paypal.com/nvp'; - $this->settings->form_url = 'https://www.sandbox.paypal.com/cgi-bin/webscr'; +class MeprPayPalStandardGateway extends MeprBasePayPalGateway +{ + /** + * Used in the view to identify the gateway + */ + public function __construct() + { + $this->name = __('PayPal Standard', 'memberpress'); + $this->key = __('paypal', 'memberpress'); + $this->has_spc_form = true; + $this->set_defaults(); + + // Setup the notification actions for this gateway + $this->notifiers = [ + 'ipn' => 'listener', + 'cancel' => 'cancel_handler', + 'return' => 'return_handler', + ]; + $this->message_pages = ['cancel' => 'cancel_message']; } - else { - $this->settings->url = 'https://ipnpb.paypal.com/cgi-bin/webscr'; - $this->settings->api_url = 'https://api-3t.paypal.com/nvp'; - $this->settings->form_url = 'https://www.paypal.com/cgi-bin/webscr'; + + public function load($settings) + { + $this->settings = (object)$settings; + $this->set_defaults(); } - $this->settings->api_version = 69; - - if($this->settings->advanced_mode == 'on' or $this->settings->advanced_mode == true) { - $this->capabilities = array( - 'process-payments', - 'process-refunds', - 'create-subscriptions', - 'cancel-subscriptions', - 'update-subscriptions', - 'suspend-subscriptions', - 'resume-subscriptions', - 'subscription-trial-payment', //The trial payment doesn't have to be processed as a separate one-off like Authorize.net & Stripe - 'order-bumps', - ); + protected function set_defaults() + { + if (!isset($this->settings)) { + $this->settings = []; + } + + $this->settings = + (object)array_merge( + [ + 'gateway' => 'MeprPayPalStandardGateway', + 'id' => $this->generate_id(), + 'label' => '', + 'use_label' => true, + 'icon' => MEPR_IMAGES_URL . '/checkout/paypal.png', + 'use_icon' => true, + 'desc' => __('Pay via your PayPal account', 'memberpress'), + 'use_desc' => true, + 'paypal_email' => '', + 'advanced_mode' => false, + 'api_username' => '', + 'api_password' => '', + 'signature' => '', + 'sandbox' => false, + 'debug' => false, + ], + (array)$this->settings + ); + + $this->id = $this->settings->id; + $this->label = $this->settings->label; + $this->use_label = $this->settings->use_label; + $this->icon = $this->settings->icon; + $this->use_icon = $this->settings->use_icon; + $this->desc = $this->settings->desc; + $this->use_desc = $this->settings->use_desc; + + if ($this->is_test_mode()) { + $this->settings->url = 'https://ipnpb.sandbox.paypal.com/cgi-bin/webscr'; + $this->settings->api_url = 'https://api-3t.sandbox.paypal.com/nvp'; + $this->settings->form_url = 'https://www.sandbox.paypal.com/cgi-bin/webscr'; + } else { + $this->settings->url = 'https://ipnpb.paypal.com/cgi-bin/webscr'; + $this->settings->api_url = 'https://api-3t.paypal.com/nvp'; + $this->settings->form_url = 'https://www.paypal.com/cgi-bin/webscr'; + } + + $this->settings->api_version = 69; + + if ($this->settings->advanced_mode == 'on' or $this->settings->advanced_mode == true) { + $this->capabilities = [ + 'process-payments', + 'process-refunds', + 'create-subscriptions', + 'cancel-subscriptions', + 'update-subscriptions', + 'suspend-subscriptions', + 'resume-subscriptions', + 'subscription-trial-payment', // The trial payment doesn't have to be processed as a separate one-off like Authorize.net & Stripe + 'order-bumps', + ]; + } else { + $this->capabilities = [ + 'process-payments', + 'create-subscriptions', + 'update-subscriptions', + 'subscription-trial-payment', // The trial payment doesn't have to be processed as a separate one-off like Authorize.net & Stripe + 'order-bumps', + ]; + } + + // An attempt to correct people who paste in spaces along with their credentials + $this->settings->paypal_email = trim($this->settings->paypal_email); } - else { - $this->capabilities = array( - 'process-payments', - 'create-subscriptions', - 'update-subscriptions', - 'subscription-trial-payment', //The trial payment doesn't have to be processed as a separate one-off like Authorize.net & Stripe - 'order-bumps', - ); + + /** + * Listens for an incoming connection from PayPal and then handles the request appropriately. + */ + public function listener() + { + $_POST = wp_unslash($_POST); + $this->email_status("PayPal IPN Recieved\n" . MeprUtils::object_to_string($_POST, true) . "\n", $this->settings->debug); + + if ($this->validate_ipn()) { + return $this->process_ipn(); + } + + return false; } - // An attempt to correct people who paste in spaces along with their credentials - $this->settings->paypal_email = trim($this->settings->paypal_email); - } + public function process_ipn() + { + $recurring_payment_txn_types = ['recurring_payment', 'subscr_payment', 'recurring_payment_outstanding_payment']; + $failed_txn_types = ['recurring_payment_skipped', 'subscr_failed']; + $payment_status_types = ['denied','expired','failed']; + $refunded_types = ['refunded','reversed','voided']; + $cancel_sub_types = ['recurring_payment_profile_cancel', 'subscr_cancel', 'recurring_payment_suspended_due_to_max_failed_payment']; + + if (isset($_POST['txn_type']) && strtolower($_POST['txn_type']) == 'web_accept') { + if ($this->is_ipn_for_me()) { + $txn_id = isset($_POST['item_number']) && is_numeric($_POST['item_number']) ? (int) $_POST['item_number'] : 0; + $txn = new MeprTransaction($txn_id); + + // Check if this is a multi-item purchase + $order = $txn->order(); + $order_bump_transactions = $order instanceof MeprOrder ? MeprTransaction::get_all_by_order_id_and_gateway($order->id, $this->id, $txn->id) : []; + + if ($order instanceof MeprOrder && count($order_bump_transactions)) { + if (!$order->is_complete() && !$order->is_processing()) { + $order->update_meta('processing', true); + $transactions = array_merge([$txn], $order_bump_transactions); + $trans_num = $_POST['txn_id']; + + foreach ($transactions as $transaction) { + $_POST['txn_id'] = sprintf('mi_%d_%s', $order->id, uniqid()); + + if (!$transaction->is_payment_required()) { + MeprTransaction::create_free_transaction($transaction, false, $_POST['txn_id']); + } else { + $_POST['item_number'] = $transaction->id; + $_POST['mc_gross'] = $transaction->total; + + $this->record_payment(); + } + } + + $order->trans_num = $trans_num; + $order->status = MeprOrder::$complete_str; + $order->store(); + $order->delete_meta('processing'); + } - /** Listens for an incoming connection from PayPal and then handles the request appropriately. */ - public function listener() { - $_POST = wp_unslash($_POST); - $this->email_status("PayPal IPN Recieved\n" . MeprUtils::object_to_string($_POST, true) . "\n", $this->settings->debug); + return; + } - if($this->validate_ipn()) { return $this->process_ipn(); } + $this->record_payment(); + } + } elseif (isset($_POST['txn_type']) && strtolower($_POST['txn_type']) == 'subscr_signup') { + // We're only going to use subscr_signup here for free trial periods + // Otherwise the record_create_subscription will be called during subscr_payment + // because PayPal decided it would be great to send the subscr_payment webhook before the subscr_signup DOH! + if (!$this->is_ipn_for_me() || !isset($_POST['item_number'])) { + return; // Need a txn ID + } - return false; - } + $txn = new MeprTransaction($_POST['item_number']); - public function process_ipn() { - $recurring_payment_txn_types = array('recurring_payment', 'subscr_payment', 'recurring_payment_outstanding_payment'); - $failed_txn_types = array('recurring_payment_skipped', 'subscr_failed'); - $payment_status_types = array('denied','expired','failed'); - $refunded_types = array('refunded','reversed','voided'); - $cancel_sub_types = array('recurring_payment_profile_cancel', 'subscr_cancel', 'recurring_payment_suspended_due_to_max_failed_payment'); + if (!isset($txn->id) || empty($txn->id) || (int)$txn->id <= 0) { + return; // No txn + } - if(isset($_POST['txn_type']) && strtolower($_POST['txn_type']) == 'web_accept') { - if($this->is_ipn_for_me()) { - $txn_id = isset($_POST['item_number']) && is_numeric($_POST['item_number']) ? (int) $_POST['item_number'] : 0; - $txn = new MeprTransaction($txn_id); + $sub = new MeprSubscription($txn->subscription_id); - // Check if this is a multi-item purchase - $order = $txn->order(); - $order_bump_transactions = $order instanceof MeprOrder ? MeprTransaction::get_all_by_order_id_and_gateway($order->id, $this->id, $txn->id) : []; + if ($sub && $sub->id > 0 && $sub->trial && $sub->trial_amount <= 0.00) { + // Check if this is a multi-item purchase + $order = $txn->order(); + $order_bump_transactions = $order instanceof MeprOrder ? MeprTransaction::get_all_by_order_id_and_gateway($order->id, $this->id, $txn->id) : []; - if($order instanceof MeprOrder && count($order_bump_transactions)) { - if(!$order->is_complete() && !$order->is_processing()) { - $order->update_meta('processing', true); - $transactions = array_merge([$txn], $order_bump_transactions); - $trans_num = $_POST['txn_id']; + if ($order instanceof MeprOrder && count($order_bump_transactions)) { + foreach ($order_bump_transactions as $transaction) { + if ($transaction->is_payment_required()) { + // If any order bump required payment, we don't want to record subscription creation here. + // It will be handled by the $recurring_payment_txn_types IPN below. + return; + } + } - foreach($transactions as $transaction) { - $_POST['txn_id'] = sprintf('mi_%d_%s', $order->id, uniqid()); + // If we reach here, payment was not required for any order bump, create free transactions for any free product + foreach ($order_bump_transactions as $transaction) { + if (!$transaction->is_payment_required()) { + MeprTransaction::create_free_transaction($transaction, false, sprintf('mi_%d_%s', $order->id, uniqid())); + } + } + } - if(!$transaction->is_payment_required()) { - MeprTransaction::create_free_transaction($transaction, false, $_POST['txn_id']); - } - else { - $_POST['item_number'] = $transaction->id; - $_POST['mc_gross'] = $transaction->total; + $this->record_create_subscription(); + } + } elseif ( + isset($_POST['txn_type'], $_POST['payment_status']) && + in_array(strtolower($_POST['txn_type']), $recurring_payment_txn_types) && + in_array(strtolower($_POST['payment_status']), $payment_status_types) + ) { + $this->record_payment_failure(); + } elseif (isset($_POST['txn_type']) && in_array(strtolower($_POST['txn_type']), $recurring_payment_txn_types)) { + if (!isset($_POST['recurring_payment_id']) && !isset($_POST['subscr_id'])) { + return; + } - $this->record_payment(); - } + // First see if the subscription has already been setup with the correct I- or S- number + if (isset($_POST['subscr_id']) && !empty($_POST['subscr_id'])) { + $sub = MeprSubscription::get_one_by_subscr_id($_POST['subscr_id']); + } else { + $sub = MeprSubscription::get_one_by_subscr_id($_POST['recurring_payment_id']); } - $order->trans_num = $trans_num; - $order->status = MeprOrder::$complete_str; - $order->store(); - $order->delete_meta('processing'); - } + // If no $sub at this point it's safe to assume this is a new signup so let's get the $sub from the $txn instead + // This order of operations to get the $sub will prevent issues between multiple sites sharing the same IPN URL (via IPN FWD ADDON) + if ($sub === false && isset($_POST['item_number'])) { + // CANNOT DO IPN CHECK UNTIL HERE OR IT WILL MESS STUFF UP + if (!$this->is_ipn_for_me()) { + return; + } //This isn't for us, so let's bail + + $txn = new MeprTransaction($_POST['item_number']); + + // Check if this is a multi-item purchase + $order = $txn->order(); + $order_bump_transactions = $order instanceof MeprOrder ? MeprTransaction::get_all_by_order_id_and_gateway($order->id, $this->id, $txn->id) : []; + + if ($order instanceof MeprOrder && count($order_bump_transactions)) { + if (!$order->is_complete() && !$order->is_processing()) { + $order->update_meta('processing', true); + $transactions = array_merge([$txn], $order_bump_transactions); + $trans_num = $_POST['txn_id']; + + foreach ($transactions as $transaction) { + $_POST['item_number'] = $transaction->id; + $_POST['txn_id'] = sprintf('mi_%d_%s', $order->id, uniqid()); + + if (!$transaction->is_payment_required()) { + MeprTransaction::create_free_transaction($transaction, false, $_POST['txn_id']); + } elseif ($transaction->is_one_time_payment()) { + $_POST['mc_gross'] = (float) $transaction->total; + + $this->record_payment(); + } else { + $subscription = $transaction->subscription(); + + if (!($subscription instanceof MeprSubscription)) { + continue; + } + + if ($subscription->gateway == $this->id) { + // The subscription hasn't been set up yet so let's set it up first + if (strpos($subscription->subscr_id, 'S-') === false && strpos($subscription->subscr_id, 'I-') === false) { + $this->record_create_subscription(); + } + + if ($subscription->trial && $subscription->trial_days > 0) { + if ($subscription->trial_total > 0) { + $_POST['mc_gross'] = $subscription->trial_total; + } else { + continue; // Initial payment for a free trial with order bump, we don't want to record a subscription transaction here + } + } else { + $_POST['mc_gross'] = $subscription->total; + } + + $_POST['mepr_order_id'] = $order->id; + + // Record recurring payment + $this->record_subscription_payment(); + } + } + } + + $order->trans_num = $trans_num; + $order->status = MeprOrder::$complete_str; + $order->store(); + $order->delete_meta('processing'); + } - return; - } + return; + } else { + $sub = $txn->subscription(); + } + } - $this->record_payment(); - } - } - elseif(isset($_POST['txn_type']) && strtolower($_POST['txn_type']) == 'subscr_signup') { - //We're only going to use subscr_signup here for free trial periods - //Otherwise the record_create_subscription will be called during subscr_payment - //because PayPal decided it would be great to send the subscr_payment webhook before the subscr_signup DOH! - if(!$this->is_ipn_for_me() || !isset($_POST['item_number'])) { - return; // Need a txn ID - } - - $txn = new MeprTransaction($_POST['item_number']); - - if(!isset($txn->id) || empty($txn->id) || (int)$txn->id <= 0) { - return; // No txn - } - - $sub = new MeprSubscription($txn->subscription_id); - - if($sub && $sub->id > 0 && $sub->trial && $sub->trial_amount <= 0.00) { - // Check if this is a multi-item purchase - $order = $txn->order(); - $order_bump_transactions = $order instanceof MeprOrder ? MeprTransaction::get_all_by_order_id_and_gateway($order->id, $this->id, $txn->id) : []; - - if($order instanceof MeprOrder && count($order_bump_transactions)) { - foreach($order_bump_transactions as $transaction) { - if($transaction->is_payment_required()) { - // If any order bump required payment, we don't want to record subscription creation here. - // It will be handled by the $recurring_payment_txn_types IPN below. - return; + if ($sub !== false && $sub->gateway == $this->id) { + // The subscription hasn't been setup yet so let's set it up first + if (strpos($sub->subscr_id, 'S-') === false && strpos($sub->subscr_id, 'I-') === false) { + $this->record_create_subscription(); // Is it even possible to get here? + } + + // Record recurring payment on existing sub (this bypasses is_ipn_for_me which is needed in case subscriptions were imported from non-MP services) + $this->record_subscription_payment(); + } + } elseif (isset($_POST['parent_txn_id']) && !isset($_POST['txn_type'])) { + if (in_array(strtolower($_POST['payment_status']), $refunded_types)) { + return $this->record_refund(); } - } + } elseif (isset($_POST['txn_type']) && strtolower($_POST['txn_type']) == 'recurring_payment_suspended') { + $this->record_suspend_subscription(); + } elseif (isset($_POST['txn_type']) && in_array(strtolower($_POST['txn_type']), $cancel_sub_types)) { + $this->record_cancel_subscription(); + } elseif ( + ( isset($_POST['txn_type']) && + in_array(strtolower($_POST['txn_type']), $failed_txn_types) ) || + ( isset($_POST['payment_status']) && + in_array(strtolower($_POST['payment_status']), $payment_status_types) ) + ) { + $this->record_payment_failure(); + } + } - // If we reach here, payment was not required for any order bump, create free transactions for any free product - foreach($order_bump_transactions as $transaction) { - if(!$transaction->is_payment_required()) { - MeprTransaction::create_free_transaction($transaction, false, sprintf('mi_%d_%s', $order->id, uniqid())); + public function is_ipn_for_me() + { + // Note: Sometimes PayPal doesn't send the custom field, or it is cutoff and doesn't include the gateway_id + // This prevents transactions from being created in MP. Since the fix is dependent on PayPal, this filter + // is to override the IPN is for me check so customers sites can still operate. + // CAUTION: Should ony be used for customers with this specific issue and that only have 1 PayPal payment gateway setup. + // The same filter is in MeprPayPalCommerceGateway as well. + if (apply_filters('mepr_override_ipn_is_for_me', false)) { + return true; + } + + if (isset($_POST['custom']) && !empty($_POST['custom'])) { + $custom_vars = (array)json_decode(stripslashes($_POST['custom'])); + + if (isset($custom_vars['gateway_id']) && $custom_vars['gateway_id'] == $this->id) { + return true; } - } } - $this->record_create_subscription(); - } - } - elseif( - isset($_POST['txn_type'], $_POST['payment_status']) && - in_array(strtolower($_POST['txn_type']), $recurring_payment_txn_types) && - in_array(strtolower($_POST['payment_status']), $payment_status_types) - ) { - $this->record_payment_failure(); + return false; } - elseif(isset($_POST['txn_type']) && in_array(strtolower($_POST['txn_type']), $recurring_payment_txn_types)) { - if(!isset($_POST['recurring_payment_id']) && !isset($_POST['subscr_id'])) - return; - - // First see if the subscription has already been setup with the correct I- or S- number - if(isset($_POST['subscr_id']) && !empty($_POST['subscr_id'])) - $sub = MeprSubscription::get_one_by_subscr_id($_POST['subscr_id']); - else - $sub = MeprSubscription::get_one_by_subscr_id($_POST['recurring_payment_id']); - - //If no $sub at this point it's safe to assume this is a new signup so let's get the $sub from the $txn instead - //This order of operations to get the $sub will prevent issues between multiple sites sharing the same IPN URL (via IPN FWD ADDON) - if($sub === false && isset($_POST['item_number'])) { - //CANNOT DO IPN CHECK UNTIL HERE OR IT WILL MESS STUFF UP - if(!$this->is_ipn_for_me()) { return; } //This isn't for us, so let's bail - $txn = new MeprTransaction($_POST['item_number']); + /** + * Used to record a successful recurring payment by the given gateway. It + * should have the ability to record a successful payment or a failure. It is + * this method that should be used when receiving an IPN from PayPal or a + * Silent Post from Authorize.net. + */ + public function record_subscription_payment() + { + if (!isset($_POST['recurring_payment_id']) && !isset($_POST['subscr_id'])) { + return; + } - // Check if this is a multi-item purchase - $order = $txn->order(); - $order_bump_transactions = $order instanceof MeprOrder ? MeprTransaction::get_all_by_order_id_and_gateway($order->id, $this->id, $txn->id) : []; + if (isset($_POST['subscr_id']) && !empty($_POST['subscr_id'])) { + $sub = MeprSubscription::get_one_by_subscr_id($_POST['subscr_id']); + } else { + $sub = MeprSubscription::get_one_by_subscr_id($_POST['recurring_payment_id']); + } - if($order instanceof MeprOrder && count($order_bump_transactions)) { - if(!$order->is_complete() && !$order->is_processing()) { - $order->update_meta('processing', true); - $transactions = array_merge([$txn], $order_bump_transactions); - $trans_num = $_POST['txn_id']; + if ($sub) { + $timestamp = isset($_POST['payment_date']) ? strtotime($_POST['payment_date']) : time(); + $first_txn = new MeprTransaction($sub->first_txn_id); - foreach($transactions as $transaction) { - $_POST['item_number'] = $transaction->id; - $_POST['txn_id'] = sprintf('mi_%d_%s', $order->id, uniqid()); + if (!isset($first_txn->id) || empty($first_txn->id)) { + $first_txn = new MeprTransaction(); + $first_txn->user_id = $sub->user_id; + $first_txn->product_id = $sub->product_id; + $first_txn->coupon_id = $sub->coupon_id; + } - if(!$transaction->is_payment_required()) { - MeprTransaction::create_free_transaction($transaction, false, $_POST['txn_id']); - } - elseif($transaction->is_one_time_payment()) { - $_POST['mc_gross'] = (float) $transaction->total; + $existing = MeprTransaction::get_one_by_trans_num($_POST['txn_id']); - $this->record_payment(); - } - else { - $subscription = $transaction->subscription(); + // There's a chance this may have already happened during the return handler, if so let's just get everything up to date on the existing one + if ($existing != null && isset($existing->id) && (int)$existing->id > 0) { + $txn = new MeprTransaction($existing->id); + $handled = $txn->get_meta('mepr_paypal_notification_handled'); - if(!($subscription instanceof MeprSubscription)) { - continue; + if (!empty($handled)) { + return; } + } else { + $txn = new MeprTransaction(); + } - if($subscription->gateway == $this->id) { - // The subscription hasn't been set up yet so let's set it up first - if(strpos($subscription->subscr_id, 'S-') === false && strpos($subscription->subscr_id, 'I-') === false) { - $this->record_create_subscription(); - } + // If this is a trial payment, let's just convert the confirmation txn into a payment txn + if ($this->is_subscr_trial_payment($sub)) { + $txn = $first_txn; // For use below in send notices + $txn->created_at = MeprUtils::ts_to_mysql_date($timestamp); + $txn->expires_at = MeprUtils::ts_to_mysql_date(time() + MeprUtils::days($sub->trial_days), 'Y-m-d 23:59:59'); + $txn->gateway = $this->id; + $txn->trans_num = $_POST['txn_id']; + $txn->txn_type = MeprTransaction::$payment_str; + $txn->status = MeprTransaction::$complete_str; + $txn->subscription_id = $sub->id; + + if (isset($_POST['mepr_order_id'])) { + $txn->order_id = $_POST['mepr_order_id']; + } - if($subscription->trial && $subscription->trial_days > 0) { - if($subscription->trial_total > 0) { - $_POST['mc_gross'] = $subscription->trial_total; - } - else { - continue; // Initial payment for a free trial with order bump, we don't want to record a subscription transaction here - } - } - else { - $_POST['mc_gross'] = $subscription->total; - } + $txn->set_gross($_POST['mc_gross']); + $txn->store(); + } else { + $txn->created_at = MeprUtils::ts_to_mysql_date($timestamp); + $txn->user_id = $first_txn->user_id; + $txn->product_id = $first_txn->product_id; + $txn->coupon_id = $first_txn->coupon_id; + $txn->gateway = $this->id; + $txn->trans_num = $_POST['txn_id']; + $txn->txn_type = MeprTransaction::$payment_str; + $txn->status = MeprTransaction::$complete_str; + $txn->subscription_id = $sub->id; + + if (isset($_POST['mepr_order_id'])) { + $txn->order_id = $_POST['mepr_order_id']; + } - $_POST['mepr_order_id'] = $order->id; + $txn->set_gross($_POST['mc_gross']); + $txn->store(); - // Record recurring payment - $this->record_subscription_payment(); + // Check that the subscription status is still enabled + if ($sub->status != MeprSubscription::$active_str) { + $sub->status = MeprSubscription::$active_str; + $sub->store(); } - } + + // Not waiting for an IPN here bro ... just making it happen even though + // the total occurrences is already capped in record_create_subscription() + $sub->limit_payment_cycles(); } - $order->trans_num = $trans_num; - $order->status = MeprOrder::$complete_str; - $order->store(); - $order->delete_meta('processing'); - } + $txn->update_meta('mepr_paypal_notification_handled', true); - return; - } - else { - $sub = $txn->subscription(); - } - } + $this->email_status("Subscription Transaction\n" . MeprUtils::object_to_string($txn->rec, true), $this->settings->debug); + + MeprUtils::send_transaction_receipt_notices($txn); - if($sub !== false && $sub->gateway == $this->id) { - //The subscription hasn't been setup yet so let's set it up first - if(strpos($sub->subscr_id, 'S-') === false && strpos($sub->subscr_id, 'I-') === false) { - $this->record_create_subscription(); //Is it even possible to get here? + return $txn; } - //Record recurring payment on existing sub (this bypasses is_ipn_for_me which is needed in case subscriptions were imported from non-MP services) - $this->record_subscription_payment(); - } - } - elseif(isset($_POST['parent_txn_id']) && !isset($_POST['txn_type'])) { - if(in_array(strtolower($_POST['payment_status']), $refunded_types)) { - return $this->record_refund(); - } - } - elseif(isset($_POST['txn_type']) && strtolower($_POST['txn_type']) == 'recurring_payment_suspended') { - $this->record_suspend_subscription(); - } - elseif(isset($_POST['txn_type']) && in_array(strtolower($_POST['txn_type']), $cancel_sub_types)) { - $this->record_cancel_subscription(); - } - elseif( ( isset($_POST['txn_type']) && - in_array( strtolower($_POST['txn_type']), $failed_txn_types ) ) || - ( isset($_POST['payment_status']) && - in_array( strtolower($_POST['payment_status']), $payment_status_types ) ) ) { - $this->record_payment_failure(); - } - } - - public function is_ipn_for_me() { - //Note: Sometimes PayPal doesn't send the custom field, or it is cutoff and doesn't include the gateway_id - //This prevents transactions from being created in MP. Since the fix is dependent on PayPal, this filter - //is to override the IPN is for me check so customers sites can still operate. - //CAUTION: Should ony be used for customers with this specific issue and that only have 1 PayPal payment gateway setup. - //The same filter is in MeprPayPalCommerceGateway as well. - if(apply_filters('mepr_override_ipn_is_for_me', false)) { - return true; + return false; } - if(isset($_POST['custom']) && !empty($_POST['custom'])) { - $custom_vars = (array)json_decode(stripslashes($_POST['custom'])); + /** + * Used to record a declined payment. + */ + public function record_payment_failure() + { + if (isset($_POST['ipn_track_id']) && $txn_res = MeprTransaction::get_one_by_trans_num($_POST['ipn_track_id']) && isset($txn_res->id)) { + return false; // We've already recorded this failure duh - don't send more emails + } elseif (isset($_POST['txn_id']) && $txn_res = MeprTransaction::get_one_by_trans_num($_POST['txn_id']) && isset($txn_res->id)) { + $txn = new MeprTransaction($txn_res->id); + $txn->status = MeprTransaction::$failed_str; + $txn->store(); + } elseif ( + ( isset($_POST['recurring_payment_id']) && + ($sub = MeprSubscription::get_one_by_subscr_id($_POST['recurring_payment_id'])) ) || + ( isset($_POST['subscr_id']) && + ($sub = MeprSubscription::get_one_by_subscr_id($_POST['subscr_id'])) ) + ) { + $first_txn = $sub->first_txn(); + if ($first_txn == false || !($first_txn instanceof MeprTransaction)) { + $coupon_id = $sub->coupon_id; + } else { + $coupon_id = $first_txn->coupon_id; + } - if(isset($custom_vars['gateway_id']) && $custom_vars['gateway_id'] == $this->id) { - return true; - } - } + $txn = new MeprTransaction(); + $txn->user_id = $sub->user_id; + $txn->product_id = $sub->product_id; + $txn->coupon_id = $coupon_id; + $txn->txn_type = MeprTransaction::$payment_str; + $txn->status = MeprTransaction::$failed_str; + $txn->subscription_id = $sub->id; + // if ipn_track_id isn't set then just use uniqid + $txn->trans_num = ( isset($_POST['ipn_track_id']) ? $_POST['ipn_track_id'] : uniqid() ); + $txn->gateway = $this->id; + $txn->set_gross((isset($_POST['mc_gross'])) ? $_POST['mc_gross'] : ((isset($_POST['amount'])) ? $_POST['amount'] : 0.00)); + $txn->store(); + + $sub->expire_txns(); // Expire associated transactions for the old subscription + $sub->store(); + } else { + return false; // Nothing we can do here ... so we outta here + } - return false; - } - - /** Used to record a successful recurring payment by the given gateway. It - * should have the ability to record a successful payment or a failure. It is - * this method that should be used when receiving an IPN from PayPal or a - * Silent Post from Authorize.net. - */ - public function record_subscription_payment() { - if(!isset($_POST['recurring_payment_id']) && !isset($_POST['subscr_id'])) { - return; - } + MeprUtils::send_failed_txn_notices($txn); - if(isset($_POST['subscr_id']) && !empty($_POST['subscr_id'])) { - $sub = MeprSubscription::get_one_by_subscr_id($_POST['subscr_id']); - } - else { - $sub = MeprSubscription::get_one_by_subscr_id($_POST['recurring_payment_id']); + return $txn; } - if($sub) { - $timestamp = isset($_POST['payment_date']) ? strtotime($_POST['payment_date']) : time(); - $first_txn = new MeprTransaction($sub->first_txn_id); - - if(!isset($first_txn->id) || empty($first_txn->id)) { - $first_txn = new MeprTransaction(); - $first_txn->user_id = $sub->user_id; - $first_txn->product_id = $sub->product_id; - $first_txn->coupon_id = $sub->coupon_id; - } + /** + * Used to send data to a given payment gateway. In gateways which redirect + * before this step is necessary this method should just be left blank. + */ + public function process_payment($txn) + { + // Handled in the IPN, only record_payment is needed here + } - $existing = MeprTransaction::get_one_by_trans_num($_POST['txn_id']); + /** + * Used to record a successful payment by the given gateway. It should have + * the ability to record a successful payment or a failure. It is this method + * that should be used when receiving an IPN from PayPal or a Silent Post + * from Authorize.net. + */ + public function record_payment() + { + if (!isset($_POST['item_number']) || empty($_POST['item_number'])) { + return false; + } - //There's a chance this may have already happened during the return handler, if so let's just get everything up to date on the existing one - if($existing != null && isset($existing->id) && (int)$existing->id > 0) { - $txn = new MeprTransaction( $existing->id ); - $handled = $txn->get_meta('mepr_paypal_notification_handled'); + $txn = new MeprTransaction($_POST['item_number']); - if (!empty($handled)) { - return; - } - } - else { - $txn = new MeprTransaction(); - } - - //If this is a trial payment, let's just convert the confirmation txn into a payment txn - if($this->is_subscr_trial_payment($sub)) { - $txn = $first_txn; //For use below in send notices - $txn->created_at = MeprUtils::ts_to_mysql_date($timestamp); - $txn->expires_at = MeprUtils::ts_to_mysql_date(time() + MeprUtils::days($sub->trial_days), 'Y-m-d 23:59:59'); - $txn->gateway = $this->id; - $txn->trans_num = $_POST['txn_id']; - $txn->txn_type = MeprTransaction::$payment_str; - $txn->status = MeprTransaction::$complete_str; - $txn->subscription_id = $sub->id; - - if(isset($_POST['mepr_order_id'])) { - $txn->order_id = $_POST['mepr_order_id']; + // The amount can be fudged in the URL with PayPal Standard - so let's make sure no fudgyness is goin' on + if (isset($_POST['mc_gross']) && (float)$_POST['mc_gross'] < (float)$txn->total) { + $txn->amount = (float)$_POST['mc_gross']; + $txn->total = (float)$_POST['mc_gross']; + $txn->tax_amount = 0.00; + $txn->tax_rate = 0.00; + $txn->status = MeprTransaction::$pending_str; + $txn->txn_type = MeprTransaction::$payment_str; + $txn->trans_num = $_POST['txn_id']; + $txn->store(); + + return false; } - $txn->set_gross($_POST['mc_gross']); - $txn->store(); - } - else { - $txn->created_at = MeprUtils::ts_to_mysql_date($timestamp); - $txn->user_id = $first_txn->user_id; - $txn->product_id = $first_txn->product_id; - $txn->coupon_id = $first_txn->coupon_id; - $txn->gateway = $this->id; - $txn->trans_num = $_POST['txn_id']; - $txn->txn_type = MeprTransaction::$payment_str; - $txn->status = MeprTransaction::$complete_str; - $txn->subscription_id = $sub->id; - - if(isset($_POST['mepr_order_id'])) { - $txn->order_id = $_POST['mepr_order_id']; + // Already been here somehow? + if ($txn->status == MeprTransaction::$complete_str && $txn->trans_num == $_POST['txn_id']) { + return false; } - $txn->set_gross($_POST['mc_gross']); - $txn->store(); + if (isset($_POST['payment_status']) && strtolower($_POST['payment_status']) == 'completed') { + $timestamp = isset($_POST['payment_date']) ? strtotime($_POST['payment_date']) : time(); - //Check that the subscription status is still enabled - if($sub->status != MeprSubscription::$active_str) { - $sub->status = MeprSubscription::$active_str; - $sub->store(); - } + $txn->trans_num = $_POST['txn_id']; + $txn->txn_type = MeprTransaction::$payment_str; + $txn->status = MeprTransaction::$complete_str; + $txn->created_at = MeprUtils::ts_to_mysql_date($timestamp); - // Not waiting for an IPN here bro ... just making it happen even though - // the total occurrences is already capped in record_create_subscription() - $sub->limit_payment_cycles(); - } + // This will only work before maybe_cancel_old_sub is run + $upgrade = $txn->is_upgrade(); + $downgrade = $txn->is_downgrade(); - $txn->update_meta('mepr_paypal_notification_handled', true); + $event_txn = $txn->maybe_cancel_old_sub(); + $txn->store(); - $this->email_status("Subscription Transaction\n" . MeprUtils::object_to_string($txn->rec, true), $this->settings->debug); + $this->email_status("Transaction\n" . MeprUtils::object_to_string($txn->rec, true) . "\n", $this->settings->debug); - MeprUtils::send_transaction_receipt_notices($txn); + $prd = $txn->product(); - return $txn; - } + if ($prd->period_type == 'lifetime') { + if ($upgrade) { + $this->upgraded_sub($txn, $event_txn); + } elseif ($downgrade) { + $this->downgraded_sub($txn, $event_txn); + } else { + $this->new_sub($txn); + } - return false; - } + MeprUtils::send_signup_notices($txn); + } - /** Used to record a declined payment. */ - public function record_payment_failure() { - if(isset($_POST['ipn_track_id']) && $txn_res = MeprTransaction::get_one_by_trans_num($_POST['ipn_track_id']) && isset($txn_res->id)) { - return false; //We've already recorded this failure duh - don't send more emails - } - elseif(isset($_POST['txn_id']) && $txn_res = MeprTransaction::get_one_by_trans_num($_POST['txn_id']) && isset($txn_res->id)) { - $txn = new MeprTransaction($txn_res->id); - $txn->status = MeprTransaction::$failed_str; - $txn->store(); - } - elseif( ( isset($_POST['recurring_payment_id']) && - ($sub = MeprSubscription::get_one_by_subscr_id($_POST['recurring_payment_id'])) ) || - ( isset($_POST['subscr_id']) && - ($sub = MeprSubscription::get_one_by_subscr_id($_POST['subscr_id'])) ) ) { - $first_txn = $sub->first_txn(); - if($first_txn == false || !($first_txn instanceof MeprTransaction)) { - $coupon_id = $sub->coupon_id; - } - else { - $coupon_id = $first_txn->coupon_id; - } - - $txn = new MeprTransaction(); - $txn->user_id = $sub->user_id; - $txn->product_id = $sub->product_id; - $txn->coupon_id = $coupon_id; - $txn->txn_type = MeprTransaction::$payment_str; - $txn->status = MeprTransaction::$failed_str; - $txn->subscription_id = $sub->id; - // if ipn_track_id isn't set then just use uniqid - $txn->trans_num = ( isset($_POST['ipn_track_id']) ? $_POST['ipn_track_id'] : uniqid() ); - $txn->gateway = $this->id; - $txn->set_gross((isset($_POST['mc_gross']))?$_POST['mc_gross']:((isset($_POST['amount']))?$_POST['amount']:0.00)); - $txn->store(); - - $sub->expire_txns(); //Expire associated transactions for the old subscription - $sub->store(); + MeprUtils::send_transaction_receipt_notices($txn); + + return $txn; + } + + return false; } - else - return false; // Nothing we can do here ... so we outta here - - MeprUtils::send_failed_txn_notices($txn); - - return $txn; - } - - /** Used to send data to a given payment gateway. In gateways which redirect - * before this step is necessary this method should just be left blank. - */ - public function process_payment($txn) { - //Handled in the IPN, only record_payment is needed here - } - - /** Used to record a successful payment by the given gateway. It should have - * the ability to record a successful payment or a failure. It is this method - * that should be used when receiving an IPN from PayPal or a Silent Post - * from Authorize.net. - */ - public function record_payment() { - if(!isset($_POST['item_number']) || empty($_POST['item_number'])) { return false; } - - $txn = new MeprTransaction($_POST['item_number']); - - //The amount can be fudged in the URL with PayPal Standard - so let's make sure no fudgyness is goin' on - if(isset($_POST['mc_gross']) && (float)$_POST['mc_gross'] < (float)$txn->total) { - $txn->amount = (float)$_POST['mc_gross']; - $txn->total = (float)$_POST['mc_gross']; - $txn->tax_amount = 0.00; - $txn->tax_rate = 0.00; - $txn->status = MeprTransaction::$pending_str; - $txn->txn_type = MeprTransaction::$payment_str; - $txn->trans_num = $_POST['txn_id']; - $txn->store(); - - return false; + + /** + * This method should be used by the class to record a successful refund from + * the gateway. This method should also be used by any IPN requests or Silent Posts. + */ + public function process_refund(MeprTransaction $txn) + { + $mepr_options = MeprOptions::fetch(); + + $args = MeprHooks::apply_filters('mepr_paypal_std_refund_args', [ + 'TRANSACTIONID' => $txn->trans_num, + 'REFUNDTYPE' => 'Full', + 'CURRENCYCODE' => $mepr_options->currency_code, + ], $txn); + + $this->email_status("RefundTransaction Request:\n" . MeprUtils::object_to_string($args, true) . "\n", $this->settings->debug); + $res = $this->send_nvp_request('RefundTransaction', $args); + $this->email_status("RefundTransaction Response:\n" . MeprUtils::object_to_string($res, true) . "\n", $this->settings->debug); + + if (!isset($res['ACK']) or strtoupper($res['ACK']) != 'SUCCESS') { + throw new MeprGatewayException(__('The refund was unsuccessful. Please login at PayPal and refund the transaction there.', 'memberpress')); + } + + $_POST['parent_txn_id'] = $txn->id; + return $this->record_refund(); } - //Already been here somehow? - if($txn->status == MeprTransaction::$complete_str && $txn->trans_num == $_POST['txn_id']) { return false; } + /** + * This method should be used by the class to record a successful refund from + * the gateway. This method should also be used by any IPN requests or Silent Posts. + */ + public function record_refund() + { + $obj = MeprTransaction::get_one_by_trans_num($_POST['parent_txn_id']); - if(isset($_POST['payment_status']) && strtolower($_POST['payment_status']) == 'completed') { - $timestamp = isset($_POST['payment_date']) ? strtotime($_POST['payment_date']) : time(); + if (!is_null($obj) && (int)$obj->id > 0) { + $txn = new MeprTransaction($obj->id); - $txn->trans_num = $_POST['txn_id']; - $txn->txn_type = MeprTransaction::$payment_str; - $txn->status = MeprTransaction::$complete_str; - $txn->created_at = MeprUtils::ts_to_mysql_date($timestamp); + // Seriously ... if txn was already refunded what are we doing here? + if ($txn->status == MeprTransaction::$refunded_str) { + return $txn; + } - // This will only work before maybe_cancel_old_sub is run - $upgrade = $txn->is_upgrade(); - $downgrade = $txn->is_downgrade(); + $txn->status = MeprTransaction::$refunded_str; - $event_txn = $txn->maybe_cancel_old_sub(); - $txn->store(); + $this->email_status("Processing Refund: \n" . MeprUtils::object_to_string($_POST) . "\n Affected Transaction: \n" . MeprUtils::object_to_string($txn), $this->settings->debug); - $this->email_status("Transaction\n" . MeprUtils::object_to_string($txn->rec, true) . "\n", $this->settings->debug); + $txn->store(); - $prd = $txn->product(); + MeprUtils::send_refunded_txn_notices($txn); - if($prd->period_type == 'lifetime') { - if($upgrade) { - $this->upgraded_sub($txn, $event_txn); - } - elseif($downgrade) { - $this->downgraded_sub($txn, $event_txn); - } - else { - $this->new_sub($txn); + return $txn; } - MeprUtils::send_signup_notices($txn); - } + return false; + } - MeprUtils::send_transaction_receipt_notices($txn); + // Not needed in PayPal since PayPal supports the trial payment inclusive of the Subscription + public function process_trial_payment($transaction) + { + } + public function record_trial_payment($transaction) + { + } - return $txn; + /** + * Used to send subscription data to a given payment gateway. In gateways + * which redirect before this step is necessary this method should just be + * left blank. + */ + public function process_create_subscription($txn) + { + // This all happens in the IPN so record_created_subscription is all that's needed } - return false; - } + /** + * Used to record a successful subscription by the given gateway. It should have + * the ability to record a successful subscription or a failure. It is this method + * that should be used when receiving an IPN from PayPal or a Silent Post + * from Authorize.net. + */ + public function record_create_subscription() + { + $mepr_options = MeprOptions::fetch(); - /** This method should be used by the class to record a successful refund from - * the gateway. This method should also be used by any IPN requests or Silent Posts. - */ - public function process_refund(MeprTransaction $txn) { - $mepr_options = MeprOptions::fetch(); + $this->email_status("Paypal Create Subscription Response \$_POST:\n" . MeprUtils::object_to_string($_POST, true) . "\n", $this->settings->debug); - $args = MeprHooks::apply_filters('mepr_paypal_std_refund_args', array( - 'TRANSACTIONID' => $txn->trans_num, - 'REFUNDTYPE' => "Full", - 'CURRENCYCODE' => $mepr_options->currency_code - ), $txn); + $temp_txn = new MeprTransaction($_POST['item_number']); - $this->email_status("RefundTransaction Request:\n".MeprUtils::object_to_string($args,true)."\n", $this->settings->debug); - $res = $this->send_nvp_request('RefundTransaction', $args); - $this->email_status("RefundTransaction Response:\n".MeprUtils::object_to_string($res,true)."\n", $this->settings->debug); + if ((int)$temp_txn->id <= 0) { + return; + } - if(!isset($res['ACK']) or strtoupper($res['ACK']) != 'SUCCESS') - throw new MeprGatewayException(__('The refund was unsuccessful. Please login at PayPal and refund the transaction there.', 'memberpress')); + $sub = $temp_txn->subscription(); - $_POST['parent_txn_id'] = $txn->id; - return $this->record_refund(); - } + if ((int)$sub->id > 0) { + $timestamp = isset($_POST['payment_date']) ? strtotime($_POST['payment_date']) : time(); - /** This method should be used by the class to record a successful refund from - * the gateway. This method should also be used by any IPN requests or Silent Posts. - */ - public function record_refund() { - $obj = MeprTransaction::get_one_by_trans_num($_POST['parent_txn_id']); + $sub->subscr_id = $_POST['subscr_id']; + $sub->status = MeprSubscription::$active_str; + $sub->created_at = gmdate('c', $timestamp); + $sub->store(); - if(!is_null($obj) && (int)$obj->id > 0) - { - $txn = new MeprTransaction($obj->id); + $txn = $sub->first_txn(); - // Seriously ... if txn was already refunded what are we doing here? - if($txn->status == MeprTransaction::$refunded_str) { return $txn; } + if ($txn == false || !($txn instanceof MeprTransaction)) { + $txn = new MeprTransaction(); + $txn->user_id = $sub->user_id; + $txn->product_id = $sub->product_id; + $txn->gateway = $this->id; + $txn->subscription_id = $sub->id; + } - $txn->status = MeprTransaction::$refunded_str; + $txn->created_at = MeprUtils::ts_to_mysql_date($timestamp); - $this->email_status("Processing Refund: \n" . MeprUtils::object_to_string($_POST) . "\n Affected Transaction: \n" . MeprUtils::object_to_string($txn), $this->settings->debug); + // Only set the trans_num on free trial periods (silly, but necessary if the IPN comes in before the return URL is hit) + if ($sub->trial && $sub->trial_amount <= 0.00) { + $txn->trans_num = uniqid(); + } - $txn->store(); + if ($sub->trial) { + $expires_at = MeprUtils::ts_to_mysql_date(time() + MeprUtils::days($sub->trial_days), 'Y-m-d 23:59:59'); + } elseif (!$mepr_options->disable_grace_init_days && $mepr_options->grace_init_days > 0) { + $expires_at = MeprUtils::ts_to_mysql_date(time() + MeprUtils::days($mepr_options->grace_init_days), 'Y-m-d 23:59:59'); + } else { + $expires_at = $txn->created_at; // Expire immediately + } - MeprUtils::send_refunded_txn_notices($txn); + $txn->status = MeprTransaction::$confirmed_str; + $txn->txn_type = MeprTransaction::$subscription_confirmation_str; + $txn->set_subtotal(0.00); // Just a confirmation txn + $txn->expires_at = $expires_at; + $txn->store(true); - return $txn; - } + // This will only work before maybe_cancel_old_sub is run + $upgrade = $sub->is_upgrade(); + $downgrade = $sub->is_downgrade(); - return false; - } - - //Not needed in PayPal since PayPal supports the trial payment inclusive of the Subscription - public function process_trial_payment($transaction) { } - public function record_trial_payment($transaction) { } - - /** Used to send subscription data to a given payment gateway. In gateways - * which redirect before this step is necessary this method should just be - * left blank. - */ - public function process_create_subscription($txn) { - //This all happens in the IPN so record_created_subscription is all that's needed - } - - /** Used to record a successful subscription by the given gateway. It should have - * the ability to record a successful subscription or a failure. It is this method - * that should be used when receiving an IPN from PayPal or a Silent Post - * from Authorize.net. - */ - public function record_create_subscription() { - $mepr_options = MeprOptions::fetch(); - - $this->email_status("Paypal Create Subscription Response \$_POST:\n".MeprUtils::object_to_string($_POST, true)."\n", $this->settings->debug); - - $temp_txn = new MeprTransaction($_POST['item_number']); - - if((int)$temp_txn->id <= 0) { return; } - - $sub = $temp_txn->subscription(); - - if((int)$sub->id > 0) { - $timestamp = isset($_POST['payment_date']) ? strtotime($_POST['payment_date']) : time(); - - $sub->subscr_id = $_POST['subscr_id']; - $sub->status = MeprSubscription::$active_str; - $sub->created_at = gmdate('c',$timestamp); - $sub->store(); - - $txn = $sub->first_txn(); - - if($txn == false || !($txn instanceof MeprTransaction)) { - $txn = new MeprTransaction(); - $txn->user_id = $sub->user_id; - $txn->product_id = $sub->product_id; - $txn->gateway = $this->id; - $txn->subscription_id = $sub->id; - } - - $txn->created_at = MeprUtils::ts_to_mysql_date($timestamp); - - //Only set the trans_num on free trial periods (silly, but necessary if the IPN comes in before the return URL is hit) - if($sub->trial && $sub->trial_amount <= 0.00) { - $txn->trans_num = uniqid(); - } - - if ($sub->trial) { - $expires_at = MeprUtils::ts_to_mysql_date(time() + MeprUtils::days($sub->trial_days), 'Y-m-d 23:59:59'); - } elseif(!$mepr_options->disable_grace_init_days && $mepr_options->grace_init_days > 0) { - $expires_at = MeprUtils::ts_to_mysql_date(time() + MeprUtils::days($mepr_options->grace_init_days), 'Y-m-d 23:59:59'); - } else { - $expires_at = $txn->created_at; // Expire immediately - } - - $txn->status = MeprTransaction::$confirmed_str; - $txn->txn_type = MeprTransaction::$subscription_confirmation_str; - $txn->set_subtotal(0.00); // Just a confirmation txn - $txn->expires_at = $expires_at; - $txn->store(true); - - // This will only work before maybe_cancel_old_sub is run - $upgrade = $sub->is_upgrade(); - $downgrade = $sub->is_downgrade(); - - $event_txn = $sub->maybe_cancel_old_sub(); - - $this->email_status("Subscription Transaction\n" . MeprUtils::object_to_string($txn->rec, true), $this->settings->debug); - - if($upgrade) { - $this->upgraded_sub($sub, $event_txn); - } - else if($downgrade) { - $this->downgraded_sub($sub, $event_txn); - } - else { - $this->new_sub($sub, true); - } - - MeprUtils::send_signup_notices($txn); - - return array('subscription' => $sub, 'transaction' => $txn); + $event_txn = $sub->maybe_cancel_old_sub(); + + $this->email_status("Subscription Transaction\n" . MeprUtils::object_to_string($txn->rec, true), $this->settings->debug); + + if ($upgrade) { + $this->upgraded_sub($sub, $event_txn); + } elseif ($downgrade) { + $this->downgraded_sub($sub, $event_txn); + } else { + $this->new_sub($sub, true); + } + + MeprUtils::send_signup_notices($txn); + + return [ + 'subscription' => $sub, + 'transaction' => $txn, + ]; + } } - } - - /** Used to cancel a subscription by the given gateway. This method should be used - * by the class to record a successful cancellation from the gateway. This method - * should also be used by any IPN requests or Silent Posts. - * - * With PayPal, we bill the outstanding amount of the previous subscription, - * cancel the previous subscription and create a new subscription - */ - public function process_update_subscription($sub_id) { - // Account info updated on PayPal.com - } - - /** This method should be used by the class to record a successful cancellation - * from the gateway. This method should also be used by any IPN requests or - * Silent Posts. - */ - public function record_update_subscription() { - // Account info updated on PayPal.com - } - - /** Used to suspend a subscription by the given gateway. - */ - public function process_suspend_subscription($sub_id) { - $sub = new MeprSubscription($sub_id); - - if($sub->status == MeprSubscription::$suspended_str) { - throw new MeprGatewayException(__('This subscription has already been paused.', 'memberpress')); + + /** + * Used to cancel a subscription by the given gateway. This method should be used + * by the class to record a successful cancellation from the gateway. This method + * should also be used by any IPN requests or Silent Posts. + * + * With PayPal, we bill the outstanding amount of the previous subscription, + * cancel the previous subscription and create a new subscription + */ + public function process_update_subscription($sub_id) + { + // Account info updated on PayPal.com } - if($sub->in_free_trial()) { - throw new MeprGatewayException(__('Sorry, subscriptions cannot be paused during a free trial.', 'memberpress')); + /** + * This method should be used by the class to record a successful cancellation + * from the gateway. This method should also be used by any IPN requests or + * Silent Posts. + */ + public function record_update_subscription() + { + // Account info updated on PayPal.com } - $this->update_paypal_payment_profile($sub_id, 'Suspend'); - - $_REQUEST['recurring_payment_id'] = $sub->subscr_id; - $this->record_suspend_subscription(); - } - - /** This method should be used by the class to record a successful suspension - * from the gateway. - */ - public function record_suspend_subscription() { - $subscr_id = $_REQUEST['recurring_payment_id']; - $sub = MeprSubscription::get_one_by_subscr_id($subscr_id); - - if(!$sub) { return false; } - - // Seriously ... if sub was already suspended what are we doing here? - if($sub->status == MeprSubscription::$suspended_str) { return $sub; } - - $sub->status = MeprSubscription::$suspended_str; - $sub->store(); - - MeprUtils::send_suspended_sub_notices($sub); - - return $sub; - } - - /** Used to suspend a subscription by the given gateway. - */ - public function process_resume_subscription($sub_id) { - $sub = new MeprSubscription($sub_id); - $this->update_paypal_payment_profile($sub_id, 'Reactivate'); - - $_REQUEST['recurring_payment_id'] = $sub->subscr_id; - $this->record_resume_subscription(); - } - - /** This method should be used by the class to record a successful resuming of - * as subscription from the gateway. - */ - public function record_resume_subscription() { - //APPARENTLY PAYPAL DOES NOT SEND OUT AN IPN FOR THIS -- SO WE CAN'T ACTUALLY RECORD THIS HERE UGH - //BUT WE DO SET THE SUBSCR STATUS BACK TO ACTIVE WHEN THE NEXT PAYMENT CLEARS - $subscr_id = $_REQUEST['recurring_payment_id']; - $sub = MeprSubscription::get_one_by_subscr_id($subscr_id); - - if(!$sub) { return false; } - - // Seriously ... if sub was already active what are we doing here? - if($sub->status == MeprSubscription::$active_str) { return $sub; } - - $sub->status = MeprSubscription::$active_str; - $sub->store(); - - //Check if prior txn is expired yet or not, if so create a temporary txn so the user can access the content immediately - $prior_txn = $sub->latest_txn(); - if($prior_txn == false || !($prior_txn instanceof MeprTransaction) || strtotime($prior_txn->expires_at) < time()) { - $txn = new MeprTransaction(); - $txn->subscription_id = $sub->id; - $txn->trans_num = $sub->subscr_id . '-' . uniqid(); - $txn->status = MeprTransaction::$confirmed_str; - $txn->txn_type = MeprTransaction::$subscription_confirmation_str; - $txn->expires_at = MeprUtils::ts_to_mysql_date($sub->get_expires_at()); - $txn->set_subtotal(0.00); // Just a confirmation txn - $txn->store(); + /** + * Used to suspend a subscription by the given gateway. + */ + public function process_suspend_subscription($sub_id) + { + $sub = new MeprSubscription($sub_id); + + if ($sub->status == MeprSubscription::$suspended_str) { + throw new MeprGatewayException(__('This subscription has already been paused.', 'memberpress')); + } + + if ($sub->in_free_trial()) { + throw new MeprGatewayException(__('Sorry, subscriptions cannot be paused during a free trial.', 'memberpress')); + } + + $this->update_paypal_payment_profile($sub_id, 'Suspend'); + + $_REQUEST['recurring_payment_id'] = $sub->subscr_id; + $this->record_suspend_subscription(); } - MeprUtils::send_resumed_sub_notices($sub); - - return $sub; - } - - /** Used to cancel a subscription by the given gateway. This method should be used - * by the class to record a successful cancellation from the gateway. This method - * should also be used by any IPN requests or Silent Posts. - */ - public function process_cancel_subscription($sub_id) { - $sub = new MeprSubscription($sub_id); - - // Should already expire naturally at paypal so we have no need - // to do this when we're "cancelling" because of a natural expiration - if(!isset($_REQUEST['expire'])) - $this->update_paypal_payment_profile($sub_id, 'Cancel'); - - $_REQUEST['subscr_id'] = $sub->subscr_id; - $this->record_cancel_subscription(); - } - - /** This method should be used by the class to record a successful cancellation - * from the gateway. This method should also be used by any IPN requests or - * Silent Posts. - */ - public function record_cancel_subscription() { - // Not sure how/why this would happen but fail silently if it does - if(!isset($_REQUEST['subscr_id']) && !isset($_REQUEST['recurring_payment_id'])) { return false; } - - $subscr_id = (isset($_REQUEST['subscr_id']))?$_REQUEST['subscr_id']:$_REQUEST['recurring_payment_id']; - $sub = MeprSubscription::get_one_by_subscr_id($subscr_id); - - if(!$sub) { return false; } - - // Seriously ... if sub was already cancelled what are we doing here? - if($sub->status == MeprSubscription::$cancelled_str) { return $sub; } - - $sub->status = MeprSubscription::$cancelled_str; - $sub->store(); - - if(isset($_REQUEST['expire'])) - $sub->limit_reached_actions(); - - if(!isset($_REQUEST['silent']) || ($_REQUEST['silent'] == false)) - MeprUtils::send_cancelled_sub_notices($sub); - - return $sub; - } - - public function process_signup_form($txn) { - // Not used - } - - /** This gets called on the 'init' hook when the signup form is processed ... - * this is in place so that payment solutions like paypal can redirect - * before any content is rendered. - * - * @param MeprTransaction $txn - * @throws Exception - */ - public function display_payment_page($txn) { - $order_bump_product_ids = isset($_GET['obs']) && is_array($_GET['obs']) ? array_map('intval', $_GET['obs']) : []; - $order_bump_products = MeprCheckoutCtrl::get_order_bump_products($txn->product_id, $order_bump_product_ids); - - $order_bumps = $this->process_order($txn, $order_bump_products); - - $gateway_payment_args = http_build_query($this->get_gateway_payment_args($txn, $order_bumps)); - $url = $this->settings->form_url . '?' . $gateway_payment_args; - MeprUtils::wp_redirect(str_replace('&', '&', $url)); - } - - /** This gets called on wp_enqueue_script and enqueues a set of - * scripts for use on the page containing the payment form - */ - public function enqueue_payment_form_scripts() { - // No need, handled on the PayPal side - } - - /** - * Returs the payment form and required fields for the gateway - */ - public function spc_payment_fields() { - if($this->settings->use_desc) { - return wpautop(esc_html(trim($this->settings->desc))); + /** + * This method should be used by the class to record a successful suspension + * from the gateway. + */ + public function record_suspend_subscription() + { + $subscr_id = $_REQUEST['recurring_payment_id']; + $sub = MeprSubscription::get_one_by_subscr_id($subscr_id); + + if (!$sub) { + return false; + } + + // Seriously ... if sub was already suspended what are we doing here? + if ($sub->status == MeprSubscription::$suspended_str) { + return $sub; + } + + $sub->status = MeprSubscription::$suspended_str; + $sub->store(); + + MeprUtils::send_suspended_sub_notices($sub); + + return $sub; } - return ''; - } - - /** - * This gets called on the_content and just renders the payment form - * For PayPal Standard we're loading up a hidden form and submitting it with JS - */ - public function display_payment_form($amount, $user, $product_id, $transaction_id) { - // Handled on the PayPal site so we don't have a need for it here - } - - /** Validates the payment form before a payment is processed */ - public function validate_payment_form($errors) { - // PayPal does this on their own form - } - - /** - * Redirects the user to PayPal checkout - * - * @param MeprTransaction $txn - * @throws MeprGatewayException - * @throws Exception - */ - public function process_payment_form($txn) { - $order_bump_product_ids = isset($_POST['mepr_order_bumps']) && is_array($_POST['mepr_order_bumps']) ? array_map('intval', $_POST['mepr_order_bumps']) : []; - $order_bump_products = MeprCheckoutCtrl::get_order_bump_products($txn->product_id, $order_bump_product_ids); - - $order_bumps = $this->process_order($txn, $order_bump_products); - - $gateway_payment_args = http_build_query($this->get_gateway_payment_args($txn, $order_bumps)); - $url = $this->settings->form_url . '?' . $gateway_payment_args; - MeprUtils::wp_redirect(str_replace('&', '&', $url)); - } - - /** - * Get the args to send to PayPal - * - * @param MeprTransaction $txn - * @param MeprTransaction[] $order_bumps - * @throws MeprGatewayException - */ - private function get_gateway_payment_args($txn, array $order_bumps = array()) { - $mepr_options = MeprOptions::fetch(); - $prd = $txn->product(); - $sub = null; - - if(empty($prd->ID)) { - throw new MeprGatewayException(__('Product not found', 'memberpress')); + /** + * Used to suspend a subscription by the given gateway. + */ + public function process_resume_subscription($sub_id) + { + $sub = new MeprSubscription($sub_id); + $this->update_paypal_payment_profile($sub_id, 'Reactivate'); + + $_REQUEST['recurring_payment_id'] = $sub->subscr_id; + $this->record_resume_subscription(); } - $transactions = array_merge([$txn], $order_bumps); + /** + * This method should be used by the class to record a successful resuming of + * as subscription from the gateway. + */ + public function record_resume_subscription() + { + // APPARENTLY PAYPAL DOES NOT SEND OUT AN IPN FOR THIS -- SO WE CAN'T ACTUALLY RECORD THIS HERE UGH + // BUT WE DO SET THE SUBSCR STATUS BACK TO ACTIVE WHEN THE NEXT PAYMENT CLEARS + $subscr_id = $_REQUEST['recurring_payment_id']; + $sub = MeprSubscription::get_one_by_subscr_id($subscr_id); + + if (!$sub) { + return false; + } + + // Seriously ... if sub was already active what are we doing here? + if ($sub->status == MeprSubscription::$active_str) { + return $sub; + } - foreach($transactions as $transaction) { - if(!$transaction->is_one_time_payment()) { - $subscription = $transaction->subscription(); + $sub->status = MeprSubscription::$active_str; + $sub->store(); - if(!($subscription instanceof MeprSubscription)) { - throw new MeprGatewayException(__('Subscription not found', 'memberpress')); + // Check if prior txn is expired yet or not, if so create a temporary txn so the user can access the content immediately + $prior_txn = $sub->latest_txn(); + if ($prior_txn == false || !($prior_txn instanceof MeprTransaction) || strtotime($prior_txn->expires_at) < time()) { + $txn = new MeprTransaction(); + $txn->subscription_id = $sub->id; + $txn->trans_num = $sub->subscr_id . '-' . uniqid(); + $txn->status = MeprTransaction::$confirmed_str; + $txn->txn_type = MeprTransaction::$subscription_confirmation_str; + $txn->expires_at = MeprUtils::ts_to_mysql_date($sub->get_expires_at()); + $txn->set_subtotal(0.00); // Just a confirmation txn + $txn->store(); } - if($sub instanceof MeprSubscription) { - throw new MeprGatewayException(__('Multiple subscriptions are not supported', 'memberpress')); + MeprUtils::send_resumed_sub_notices($sub); + + return $sub; + } + + /** + * Used to cancel a subscription by the given gateway. This method should be used + * by the class to record a successful cancellation from the gateway. This method + * should also be used by any IPN requests or Silent Posts. + */ + public function process_cancel_subscription($sub_id) + { + $sub = new MeprSubscription($sub_id); + + // Should already expire naturally at paypal so we have no need + // to do this when we're "cancelling" because of a natural expiration + if (!isset($_REQUEST['expire'])) { + $this->update_paypal_payment_profile($sub_id, 'Cancel'); } - $sub = $subscription; - } + $_REQUEST['subscr_id'] = $sub->subscr_id; + $this->record_cancel_subscription(); } - //Txn vars - $custom = MeprHooks::apply_filters('mepr_paypal_std_custom_payment_vars', array( - 'gateway_id' => $this->id, - 'ip_address' => $_SERVER['REMOTE_ADDR'] - ), $txn); - - $cancel_url = $this->notify_url('cancel'); - $cancel_delim = MeprUtils::get_delim($cancel_url); - $return_url = $this->notify_url('return'); - $return_delim = MeprUtils::get_delim($return_url); - - $payment_vars = array( - 'cmd' => '_xclick', - 'business' => $this->settings->paypal_email, - 'lc' => $mepr_options->language_code, - 'currency_code' => $mepr_options->currency_code, - 'item_name' => $prd->post_title, - 'item_number' => $txn->id, - 'tax_rate' => MeprUtils::format_float(0.000, 3), - 'return' => $return_url.$return_delim.'mepr_txn_id='.$txn->id, - 'cancel_return' => $cancel_url.$cancel_delim.'txn_id='.$txn->id, - 'no_shipping' => 1, - 'custom' => json_encode($custom), - 'bn' => 'Caseproof_SP' - ); - - if($sub instanceof MeprSubscription) { - $trial_total = 0.00; - $has_trial = $sub->trial && $sub->trial_days > 0; - $convert_to_trial = false; - - if($has_trial) { - $trial_total = (float) $sub->trial_total; - } - - foreach($transactions as $transaction) { - if(!$transaction->is_payment_required()) { - continue; + /** + * This method should be used by the class to record a successful cancellation + * from the gateway. This method should also be used by any IPN requests or + * Silent Posts. + */ + public function record_cancel_subscription() + { + // Not sure how/why this would happen but fail silently if it does + if (!isset($_REQUEST['subscr_id']) && !isset($_REQUEST['recurring_payment_id'])) { + return false; } - elseif($transaction->is_one_time_payment()) { - $trial_total += (float) $transaction->total; - if(!$has_trial) { - $convert_to_trial = true; - } + $subscr_id = (isset($_REQUEST['subscr_id'])) ? $_REQUEST['subscr_id'] : $_REQUEST['recurring_payment_id']; + $sub = MeprSubscription::get_one_by_subscr_id($subscr_id); + + if (!$sub) { + return false; } - } - - // If there is no trial period, and there is an order bump, add the first subscription payment to the trial amount - if($convert_to_trial) { - $trial_total += (float) $sub->total; - } - - $period_type_map = array( - 'days' => 'D', - 'weeks' => 'W', - 'months' => 'M', - 'years' => 'Y' - ); - - //Build the subscription vars - $sub_vars = array( - 'cmd' => '_xclick-subscriptions', - 'src' => 1, - 'sra' => 1, //Attempt to rebill failed txns - 'a3' => $this->format_currency($sub->total), - 'p3' => $sub->period, - 't3' => $period_type_map[$sub->period_type] - ); - - //Handle the limiting of cycles - this is messy with PayPal Standard - if($sub->limit_cycles) { - if($sub->limit_cycles_num > 1) { - $sub_vars['srt'] = $sub->limit_cycles_num; //srt MUST be > 1 + + // Seriously ... if sub was already cancelled what are we doing here? + if ($sub->status == MeprSubscription::$cancelled_str) { + return $sub; } - else { - $sub_vars['src'] = 0; //Tell PayPal not to bill after the first cycle + + $sub->status = MeprSubscription::$cancelled_str; + $sub->store(); + + if (isset($_REQUEST['expire'])) { + $sub->limit_reached_actions(); } - } - - //Handle Trial period stuff - if($has_trial || $convert_to_trial) { - if($convert_to_trial) { - $now = new DateTimeImmutable('now'); - $end = $now->modify(sprintf('+%d %s', $sub->period, $sub->period_type)); - $trial_days = $end->diff($now)->format('%a'); + + if (!isset($_REQUEST['silent']) || ($_REQUEST['silent'] == false)) { + MeprUtils::send_cancelled_sub_notices($sub); } - else { - $trial_days = $sub->trial_days; + + return $sub; + } + + public function process_signup_form($txn) + { + // Not used + } + + /** + * This gets called on the 'init' hook when the signup form is processed ... + * this is in place so that payment solutions like paypal can redirect + * before any content is rendered. + * + * @param MeprTransaction $txn + * @throws Exception + */ + public function display_payment_page($txn) + { + $order_bump_product_ids = isset($_GET['obs']) && is_array($_GET['obs']) ? array_map('intval', $_GET['obs']) : []; + $order_bump_products = MeprCheckoutCtrl::get_order_bump_products($txn->product_id, $order_bump_product_ids); + + $order_bumps = $this->process_order($txn, $order_bump_products); + + $gateway_payment_args = http_build_query($this->get_gateway_payment_args($txn, $order_bumps)); + $url = $this->settings->form_url . '?' . $gateway_payment_args; + MeprUtils::wp_redirect(str_replace('&', '&', $url)); + } + + /** + * This gets called on wp_enqueue_script and enqueues a set of + * scripts for use on the page containing the payment form + */ + public function enqueue_payment_form_scripts() + { + // No need, handled on the PayPal side + } + + /** + * Returs the payment form and required fields for the gateway + */ + public function spc_payment_fields() + { + if ($this->settings->use_desc) { + return wpautop(esc_html(trim($this->settings->desc))); } - $sub_trial_vars = array( - 'a1' => $this->format_currency( $trial_total ), - ); + return ''; + } + + /** + * This gets called on the_content and just renders the payment form + * For PayPal Standard we're loading up a hidden form and submitting it with JS + */ + public function display_payment_form($amount, $user, $product_id, $transaction_id) + { + // Handled on the PayPal site so we don't have a need for it here + } + + /** + * Validates the payment form before a payment is processed + */ + public function validate_payment_form($errors) + { + // PayPal does this on their own form + } + + /** + * Redirects the user to PayPal checkout + * + * @param MeprTransaction $txn + * @throws MeprGatewayException + * @throws Exception + */ + public function process_payment_form($txn) + { + $order_bump_product_ids = isset($_POST['mepr_order_bumps']) && is_array($_POST['mepr_order_bumps']) ? array_map('intval', $_POST['mepr_order_bumps']) : []; + $order_bump_products = MeprCheckoutCtrl::get_order_bump_products($txn->product_id, $order_bump_product_ids); - //Trial Days, Weeks, Months, or Years - if($trial_days <= 90) { - $sub_trial_vars['p1'] = $trial_days; - $sub_trial_vars['t1'] = 'D'; + $order_bumps = $this->process_order($txn, $order_bump_products); + + $gateway_payment_args = http_build_query($this->get_gateway_payment_args($txn, $order_bumps)); + $url = $this->settings->form_url . '?' . $gateway_payment_args; + MeprUtils::wp_redirect(str_replace('&', '&', $url)); + } + + /** + * Get the args to send to PayPal + * + * @param MeprTransaction $txn + * @param MeprTransaction[] $order_bumps + * @throws MeprGatewayException + */ + private function get_gateway_payment_args($txn, array $order_bumps = []) + { + $mepr_options = MeprOptions::fetch(); + $prd = $txn->product(); + $sub = null; + + if (empty($prd->ID)) { + throw new MeprGatewayException(__('Product not found', 'memberpress')); } - else { - if($trial_days % 30 == 0) { //30 days in a month - $sub_trial_vars['p1'] = (int)($trial_days / 30); - $sub_trial_vars['t1'] = 'M'; - } - elseif($trial_days % 365 == 0) { //365 days in a year - $sub_trial_vars['p1'] = (int)($trial_days / 365); - $sub_trial_vars['t1'] = 'Y'; - } - else { //force a round to the nearest week - that's the best we can do here - $sub_trial_vars['p1'] = round((int)$trial_days / 7); - $sub_trial_vars['t1'] = 'W'; - - if(!$convert_to_trial) { - $sub->trial_days = (int)($sub_trial_vars['p1'] * 7); - $sub->store(); + + $transactions = array_merge([$txn], $order_bumps); + + foreach ($transactions as $transaction) { + if (!$transaction->is_one_time_payment()) { + $subscription = $transaction->subscription(); + + if (!($subscription instanceof MeprSubscription)) { + throw new MeprGatewayException(__('Subscription not found', 'memberpress')); + } + + if ($sub instanceof MeprSubscription) { + throw new MeprGatewayException(__('Multiple subscriptions are not supported', 'memberpress')); + } + + $sub = $subscription; } - } } - $sub_vars = array_merge($sub_vars, $sub_trial_vars); + // Txn vars + $custom = MeprHooks::apply_filters('mepr_paypal_std_custom_payment_vars', [ + 'gateway_id' => $this->id, + 'ip_address' => $_SERVER['REMOTE_ADDR'], + ], $txn); + + $cancel_url = $this->notify_url('cancel'); + $cancel_delim = MeprUtils::get_delim($cancel_url); + $return_url = $this->notify_url('return'); + $return_delim = MeprUtils::get_delim($return_url); + + $payment_vars = [ + 'cmd' => '_xclick', + 'business' => $this->settings->paypal_email, + 'lc' => $mepr_options->language_code, + 'currency_code' => $mepr_options->currency_code, + 'item_name' => $prd->post_title, + 'item_number' => $txn->id, + 'tax_rate' => MeprUtils::format_float(0.000, 3), + 'return' => $return_url . $return_delim . 'mepr_txn_id=' . $txn->id, + 'cancel_return' => $cancel_url . $cancel_delim . 'txn_id=' . $txn->id, + 'no_shipping' => 1, + 'custom' => json_encode($custom), + 'bn' => 'Caseproof_SP', + ]; + + if ($sub instanceof MeprSubscription) { + $trial_total = 0.00; + $has_trial = $sub->trial && $sub->trial_days > 0; + $convert_to_trial = false; + + if ($has_trial) { + $trial_total = (float) $sub->trial_total; + } - //Set the RETURN differently since we DON'T get an ITEM NUMBER from PayPal on free trial periods doh! - if($trial_total <= 0.00) { - $sub_vars['return'] = $return_url.$return_delim.'free_trial_txn_id='.$txn->id; - } - } + foreach ($transactions as $transaction) { + if (!$transaction->is_payment_required()) { + continue; + } elseif ($transaction->is_one_time_payment()) { + $trial_total += (float) $transaction->total; - $sub_vars = MeprHooks::apply_filters('mepr_paypal_std_subscription_vars', $sub_vars, $txn, $sub); + if (!$has_trial) { + $convert_to_trial = true; + } + } + } - //Merge payment vars with subscr vars overriding payment vars - $payment_vars = array_merge($payment_vars, $sub_vars); - } - else { - $total = 0.00; + // If there is no trial period, and there is an order bump, add the first subscription payment to the trial amount + if ($convert_to_trial) { + $trial_total += (float) $sub->total; + } - foreach($transactions as $transaction) { - $product = $transaction->product(); + $period_type_map = [ + 'days' => 'D', + 'weeks' => 'W', + 'months' => 'M', + 'years' => 'Y', + ]; + + // Build the subscription vars + $sub_vars = [ + 'cmd' => '_xclick-subscriptions', + 'src' => 1, + 'sra' => 1, // Attempt to rebill failed txns + 'a3' => $this->format_currency($sub->total), + 'p3' => $sub->period, + 't3' => $period_type_map[$sub->period_type], + ]; + + // Handle the limiting of cycles - this is messy with PayPal Standard + if ($sub->limit_cycles) { + if ($sub->limit_cycles_num > 1) { + $sub_vars['srt'] = $sub->limit_cycles_num; // srt MUST be > 1 + } else { + $sub_vars['src'] = 0; // Tell PayPal not to bill after the first cycle + } + } - if(empty($product->ID)) { - throw new MeprGatewayException(__('Product not found', 'memberpress')); - } + // Handle Trial period stuff + if ($has_trial || $convert_to_trial) { + if ($convert_to_trial) { + $now = new DateTimeImmutable('now'); + $end = $now->modify(sprintf('+%d %s', $sub->period, $sub->period_type)); + $trial_days = $end->diff($now)->format('%a'); + } else { + $trial_days = $sub->trial_days; + } + + $sub_trial_vars = [ + 'a1' => $this->format_currency($trial_total), + ]; + + // Trial Days, Weeks, Months, or Years + if ($trial_days <= 90) { + $sub_trial_vars['p1'] = $trial_days; + $sub_trial_vars['t1'] = 'D'; + } else { + if ($trial_days % 30 == 0) { // 30 days in a month + $sub_trial_vars['p1'] = (int)($trial_days / 30); + $sub_trial_vars['t1'] = 'M'; + } elseif ($trial_days % 365 == 0) { // 365 days in a year + $sub_trial_vars['p1'] = (int)($trial_days / 365); + $sub_trial_vars['t1'] = 'Y'; + } else { // force a round to the nearest week - that's the best we can do here + $sub_trial_vars['p1'] = round((int)$trial_days / 7); + $sub_trial_vars['t1'] = 'W'; + + if (!$convert_to_trial) { + $sub->trial_days = (int)($sub_trial_vars['p1'] * 7); + $sub->store(); + } + } + } - $total += (float) $transaction->total; - } + $sub_vars = array_merge($sub_vars, $sub_trial_vars); - $payment_vars = array_merge($payment_vars, [ - 'amount' => $total - ]); - } + // Set the RETURN differently since we DON'T get an ITEM NUMBER from PayPal on free trial periods doh! + if ($trial_total <= 0.00) { + $sub_vars['return'] = $return_url . $return_delim . 'free_trial_txn_id=' . $txn->id; + } + } - $payment_vars = MeprHooks::apply_filters('mepr_paypal_std_payment_vars', $payment_vars, $txn); + $sub_vars = MeprHooks::apply_filters('mepr_paypal_std_subscription_vars', $sub_vars, $txn, $sub); - return $payment_vars; - } + // Merge payment vars with subscr vars overriding payment vars + $payment_vars = array_merge($payment_vars, $sub_vars); + } else { + $total = 0.00; + + foreach ($transactions as $transaction) { + $product = $transaction->product(); + + if (empty($product->ID)) { + throw new MeprGatewayException(__('Product not found', 'memberpress')); + } + + $total += (float) $transaction->total; + } - /** Displays the form for the given payment gateway on the MemberPress Options page */ - public function display_options_form() { - $mepr_options = MeprOptions::fetch(); + $payment_vars = array_merge($payment_vars, [ + 'amount' => $total, + ]); + } - $paypal_email = trim($this->settings->paypal_email); - $api_username = trim($this->settings->api_username); - $api_password = trim($this->settings->api_password); - $signature = trim($this->settings->signature); - $advanced = ($this->settings->advanced_mode == 'on' or $this->settings->advanced_mode == true); - $sandbox = ($this->settings->sandbox == 'on' or $this->settings->sandbox == true); - $debug = ($this->settings->debug == 'on' or $this->settings->debug == true); + $payment_vars = MeprHooks::apply_filters('mepr_paypal_std_payment_vars', $payment_vars, $txn); - ?> - + return $payment_vars; + } + + /** + * Displays the form for the given payment gateway on the MemberPress Options page + */ + public function display_options_form() + { + $mepr_options = MeprOptions::fetch(); + + $paypal_email = trim($this->settings->paypal_email); + $api_username = trim($this->settings->api_username); + $api_password = trim($this->settings->api_password); + $signature = trim($this->settings->signature); + $advanced = ($this->settings->advanced_mode == 'on' or $this->settings->advanced_mode == true); + $sandbox = ($this->settings->sandbox == 'on' or $this->settings->sandbox == true); + $debug = ($this->settings->debug == 'on' or $this->settings->debug == true); + + ?> + "; + $content[] = $block; + } - if($quiz->post_content !== '' && count($questions)) { - $content[] = ''; // adds two newlines below, after existing post content - } + $quiz->post_content = $quiz->post_content . implode("\n\n", $content); + $quiz->store(); - foreach($questions as $question) { - $content[] = ""; + update_post_meta($quiz->ID, '_mepr_learndash_migrated_questions', $migrated_questions); + } } + } - $quiz->post_content = $quiz->post_content . implode("\n\n", $content); - $quiz->store(); + /** + * Get the ID of the MP Lesson or Quiz that has been previously migrated for the given LD Lesson/Topic/Quiz ID. + * + * @param integer $ld_course_id + * @param integer $ld_lesson_id + * @param string $post_type + * @return integer + */ + protected function get_existing_lesson_id($ld_course_id, $ld_lesson_id, $post_type): int + { + global $wpdb; - update_post_meta($quiz->ID, '_mepr_learndash_migrated_questions', $migrated_questions); - } - } - } - - /** - * Get the ID of the MP Lesson or Quiz that has been previously migrated for the given LD Lesson/Topic/Quiz ID. - * - * @param int $ld_course_id - * @param int $ld_lesson_id - * @param string $post_type - * @return int - */ - protected function get_existing_lesson_id($ld_course_id, $ld_lesson_id, $post_type) { - global $wpdb; - - $query = "SELECT p.ID + $query = "SELECT p.ID FROM $wpdb->posts p INNER JOIN $wpdb->postmeta pm1 ON pm1.post_id = p.ID AND pm1.meta_key = '_mepr_learndash_course_id' INNER JOIN $wpdb->postmeta pm2 ON pm2.post_id = p.ID AND pm2.meta_key = '_mepr_learndash_lesson_id' @@ -746,284 +883,301 @@ protected function get_existing_lesson_id($ld_course_id, $ld_lesson_id, $post_ty AND pm1.meta_value = %d AND pm2.meta_value = %d"; - $query = $wpdb->prepare($query, $post_type, $ld_course_id, $ld_lesson_id); - - return (int) $wpdb->get_var($query); - } - - /** - * Get the array of course sections for the given LD Course ID. - * - * @param int $ld_course_id - * @return array - */ - protected function get_course_sections($ld_course_id) { - $course_sections = get_post_meta($ld_course_id, 'course_sections', true); - $sections = []; - - if(!empty($course_sections)) { - $course_sections = json_decode($course_sections); - - if(is_array($course_sections)) { - foreach($course_sections as $course_section) { - if( - is_object($course_section) && - property_exists($course_section, 'ID') && !empty($course_section->ID) && - property_exists($course_section, 'order') && - property_exists($course_section, 'post_title') && !empty($course_section->post_title) - ) { - $sections[(int) $course_section->order] = [ - 'title' => $course_section->post_title, - 'uuid' => substr("ld-course-$ld_course_id-" . dechex($course_section->ID), 0, 40), - 'children' => [] - ]; - } - } + $query = $wpdb->prepare($query, $post_type, $ld_course_id, $ld_lesson_id); + + return (int) $wpdb->get_var($query); + } - // Sort sections in the order they appear - ksort($sections); + /** + * Get the array of course sections for the given LD Course ID. + * + * @param integer $ld_course_id + * @return array + */ + protected function get_course_sections($ld_course_id): array + { + $course_sections = get_post_meta($ld_course_id, 'course_sections', true); + $sections = []; + + if (!empty($course_sections)) { + $course_sections = json_decode($course_sections); + + if (is_array($course_sections)) { + foreach ($course_sections as $course_section) { + if ( + is_object($course_section) && + property_exists($course_section, 'ID') && !empty($course_section->ID) && + property_exists($course_section, 'order') && + property_exists($course_section, 'post_title') && !empty($course_section->post_title) + ) { + $sections[(int) $course_section->order] = [ + 'title' => $course_section->post_title, + 'uuid' => substr("ld-course-$ld_course_id-" . dechex($course_section->ID), 0, 40), + 'children' => [], + ]; + } + } - if(key($sections) !== 0) { - // LD can have lessons outside (before) a section, but this isn't possible in MP. So we force the - // first section to start from index 0 to ensure that all lessons and quizzes are inside a section. - foreach($sections as $key => $section) { - $sections[0] = $section; - unset($sections[$key]); - break; - } + // Sort sections in the order they appear + ksort($sections); - // Re-sort sections after changing the key - ksort($sections); + if (key($sections) !== 0) { + // LD can have lessons outside (before) a section, but this isn't possible in MP. So we force the + // first section to start from index 0 to ensure that all lessons and quizzes are inside a section. + foreach ($sections as $key => $section) { + $sections[0] = $section; + unset($sections[$key]); + break; + } + + // Re-sort sections after changing the key + ksort($sections); + } + } } - } - } - if(empty($sections)) { - $sections[] = [ - 'title' => __('Lessons', 'memberpress'), - 'uuid' => substr("ld-course-$ld_course_id-default", 0, 40), - 'children' => [] - ]; - } + if (empty($sections)) { + $sections[] = [ + 'title' => __('Lessons', 'memberpress'), + 'uuid' => substr("ld-course-$ld_course_id-default", 0, 40), + 'children' => [], + ]; + } - return $sections; - } - - /** - * Populate each section with the IDs for lessons, topics and quizzes contained within it. - * - * @param int $ld_course_id - * @param array $sections - * @return array - */ - protected function populate_course_sections($ld_course_id, $sections) { - $ld_course_steps = get_post_meta($ld_course_id, 'ld_course_steps', true); - $steps = isset($ld_course_steps['steps']['h']) && is_array($ld_course_steps['steps']['h']) ? $ld_course_steps['steps']['h'] : []; - $lessons = isset($steps['sfwd-lessons']) && is_array($steps['sfwd-lessons']) ? $steps['sfwd-lessons'] : []; - $quizzes = isset($steps['sfwd-quiz']) && is_array($steps['sfwd-quiz']) ? $steps['sfwd-quiz'] : []; - - // Add the section dividers into the list of lessons - foreach($sections as $order => $section) { - $lessons = array_slice($lessons, 0, $order, true) + - array("section-$order" => $order) + - array_slice($lessons, $order, null, true); + return $sections; } - // Flatten the course structure into the two-level structure supported by MemberPress - $key = 0; + /** + * Populate each section with the IDs for lessons, topics and quizzes contained within it. + * + * @param integer $ld_course_id + * @param array $sections + * @return array + */ + protected function populate_course_sections($ld_course_id, $sections): array + { + $ld_course_steps = get_post_meta($ld_course_id, 'ld_course_steps', true); + + if (isset($ld_course_steps['steps']['h']) && is_array($ld_course_steps['steps']['h'])) { + $steps = $ld_course_steps['steps']['h']; + } else { + $steps = []; + } + + $lessons = isset($steps['sfwd-lessons']) && is_array($steps['sfwd-lessons']) ? $steps['sfwd-lessons'] : []; - foreach($lessons as $lesson_post_id => $ld_lesson) { - if(is_string($lesson_post_id) && preg_match('/section-\d+/', $lesson_post_id)) { - $key = $ld_lesson; // This is a section divider, move to the next section - continue; - } + // Add the section dividers into the list of lessons + foreach ($sections as $order => $section) { + $lessons = array_slice($lessons, 0, $order, true) + + ["section-$order" => $order] + + array_slice($lessons, $order, null, true); + } - $sections[$key]['children'][] = $lesson_post_id; + // Flatten the course structure into the two-level structure supported by MemberPress + $key = 0; - // Topic that is a child of this ld_lesson - if(isset($ld_lesson['sfwd-topic']) && is_array($ld_lesson['sfwd-topic'])) { - foreach($ld_lesson['sfwd-topic'] as $topic_post_id => $topic) { - $sections[$key]['children'][] = $topic_post_id; + foreach ($lessons as $lesson_post_id => $ld_lesson) { + if (is_string($lesson_post_id) && preg_match('/section-\d+/', $lesson_post_id)) { + $key = $ld_lesson; // This is a section divider, move to the next section + continue; + } - // Quiz that is a child of this topic - if(isset($topic['sfwd-quiz']) && is_array($topic['sfwd-quiz'])) { - foreach($topic['sfwd-quiz'] as $quiz_post_id => $quiz) { - $sections[$key]['children'][] = $quiz_post_id; + $sections[$key]['children'][] = $lesson_post_id; + + // Topic that is a child of this ld_lesson + if (isset($ld_lesson['sfwd-topic']) && is_array($ld_lesson['sfwd-topic'])) { + foreach ($ld_lesson['sfwd-topic'] as $topic_post_id => $topic) { + $sections[$key]['children'][] = $topic_post_id; + + if ($this->is_course_quizzes_addon_active()) { + // Quiz that is a child of this topic + if (isset($topic['sfwd-quiz']) && is_array($topic['sfwd-quiz'])) { + foreach ($topic['sfwd-quiz'] as $quiz_post_id => $quiz) { + $sections[$key]['children'][] = $quiz_post_id; + } + } + } + } + } + + if ($this->is_course_quizzes_addon_active()) { + // Quiz that is a child of this ld_lesson + if (isset($ld_lesson['sfwd-quiz']) && is_array($ld_lesson['sfwd-quiz'])) { + foreach ($ld_lesson['sfwd-quiz'] as $quiz_post_id => $quiz) { + $sections[$key]['children'][] = $quiz_post_id; + } + } } - } } - } - // Quiz that is a child of this ld_lesson - if(isset($ld_lesson['sfwd-quiz']) && is_array($ld_lesson['sfwd-quiz'])) { - foreach($ld_lesson['sfwd-quiz'] as $quiz_post_id => $quiz) { - $sections[$key]['children'][] = $quiz_post_id; + if ($this->is_course_quizzes_addon_active()) { + // End of course quiz(zes) + $quizzes = isset($steps['sfwd-quiz']) && is_array($steps['sfwd-quiz']) ? $steps['sfwd-quiz'] : []; + + foreach ($quizzes as $quiz_post_id => $quiz) { + $sections[$key]['children'][] = $quiz_post_id; + } } - } - } - // End of course quiz(zes) - foreach($quizzes as $quiz_post_id => $quiz) { - $sections[$key]['children'][] = $quiz_post_id; + return $sections; } - return $sections; - } - - /** - * Get the IDs of sections that exist from a previous migration, and delete sections that have been removed. - * - * @param Section[] $course_sections The existing sections for the course. - * @param array $uuids The array of uuids for the sections that will be migrated. - * @return array - */ - protected function filter_existing_sections($course_sections, $uuids) { - $existing_sections = []; - - foreach($course_sections as $existing_section) { - if(in_array($existing_section->uuid, $uuids, true)) { - $existing_sections[$existing_section->uuid] = $existing_section->id; - } - else { - $existing_section->destroy(); - } + /** + * Get the IDs of sections that exist from a previous migration, and delete sections that have been removed. + * + * @param Section[] $course_sections The existing sections for the course. + * @param array $uuids The array of uuids for the sections that will be migrated. + * @return array + */ + protected function filter_existing_sections($course_sections, $uuids): array + { + $existing_sections = []; + + foreach ($course_sections as $existing_section) { + if (in_array($existing_section->uuid, $uuids, true)) { + $existing_sections[$existing_section->uuid] = $existing_section->id; + } else { + $existing_section->destroy(); + } + } + + return $existing_sections; } - return $existing_sections; - } - - /** - * Parse the answer data for a LearnDash question and return an array of answer data. - * - * If LD is not active, we can grab the values from the __PHP_Incomplete_Class. - * - * @param string $answer_data Serialized answer data. - * @return array - */ - protected function parse_question_answer_data($answer_data) { - $answer_data = @unserialize($answer_data); - $answers = []; - - if(is_array($answer_data)) { - foreach($answer_data as $answer) { - if($answer instanceof WpProQuiz_Model_AnswerTypes) { - $answers[] = $answer->get_object_as_array(); + /** + * Parse the answer data for a LearnDash question and return an array of answer data. + * + * If LD is not active, we can grab the values from the __PHP_Incomplete_Class. + * + * @param string $answer_data Serialized answer data. + * @return array + */ + protected function parse_question_answer_data($answer_data): array + { + // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.serialize_unserialize + $answer_data = @unserialize($answer_data); + $answers = []; + + if (is_array($answer_data)) { + foreach ($answer_data as $answer) { + if ($answer instanceof WpProQuiz_Model_AnswerTypes) { + $answers[] = $answer->get_object_as_array(); + } elseif ($answer instanceof __PHP_Incomplete_Class) { + $values = []; + + foreach ($answer as $k => $v) { + $values[$k] = $v; + } + + $answers[] = $values; + } + } } - elseif($answer instanceof __PHP_Incomplete_Class) { - $values = []; - foreach($answer as $k => $v) { - $values[$k] = $v; - } + return $answers; + } - $answers[] = $values; + /** + * Convert the given fill in the blanks answer from LD format to MP format. + * + * @param string $answer + * @return string + */ + protected function convert_fill_blank_answer($answer): string + { + if (!is_string($answer)) { + return ''; } - } - } - return $answers; - } - - /** - * Convert the given fill in the blanks answer from LD format to MP format. - * - * @param string $answer - * @return string - */ - protected function convert_fill_blank_answer($answer) { - if(!is_string($answer)) { - return ''; - } + preg_match_all('#\{(.*?)}#im', $answer, $matches, PREG_SET_ORDER); - preg_match_all('#\{(.*?)}#im', $answer, $matches, PREG_SET_ORDER); + foreach ($matches as $v) { + $text = $v[1]; + $answers = []; - foreach($matches as $v) { - $text = $v[1]; - $answers = []; + if (preg_match_all('#\[(.*?)]#im', $text, $multi_matches)) { + foreach ($multi_matches[1] as $multi_text) { + if (strpos($multi_text, '|') !== false) { + list($multi_text) = explode('|', $multi_text); // Ignore per-answer points + } - if(preg_match_all('#\[(.*?)]#im', $text, $multi_matches)) { - foreach($multi_matches[1] as $multi_text) { - if(strpos($multi_text, '|') !== false) { - list($multi_text) = explode('|', $multi_text); // Ignore per-answer points - } + $answers[] = trim(html_entity_decode($multi_text, ENT_QUOTES)); + } + } elseif (strpos($text, '|') !== false) { + list($text) = explode('|', $text); // Ignore per-answer points + + $answers[] = trim(html_entity_decode($text, ENT_QUOTES)); + } else { + $answers[] = trim(html_entity_decode($text, ENT_QUOTES)); + } - $answers[] = trim(html_entity_decode($multi_text, ENT_QUOTES)); + $pos = strpos($answer, $v[0]); + + if ($pos !== false) { + $answer = substr_replace( + $answer, + sprintf('[%s]', implode(', ', $answers)), + $pos, + strlen($v[0]) + ); + } } - } - elseif(strpos($text, '|') !== false) { - list($text) = explode('|', $text); // Ignore per-answer points - - $answers[] = trim(html_entity_decode($text, ENT_QUOTES)); - } - else { - $answers[] = trim(html_entity_decode($text, ENT_QUOTES)); - } - - $pos = strpos($answer, $v[0]); - - if($pos !== false) { - $answer = substr_replace( - $answer, - sprintf('[%s]', implode(', ', $answers)), - $pos, - strlen($v[0]) - ); - } + + return $answer; } - return $answer; - } - - /** - * Convert the given Assessment answer to options and settings for MP's Likert Scale question. - * - * @param string $answer - * @return array - */ - protected function convert_likert_scale_answer($answer) { - $answer = wp_strip_all_tags($answer); - $options = []; - $settings = [ - 'lowLabel' => '', - 'highLabel' => '', - ]; + /** + * Convert the given Assessment answer to options and settings for MP's Likert Scale question. + * + * @param string $answer + * @return array + */ + protected function convert_likert_scale_answer($answer) + { + $answer = wp_strip_all_tags($answer); + $options = []; + $settings = [ + 'lowLabel' => '', + 'highLabel' => '', + ]; - if(!empty($answer)) { - preg_match_all('#\{(.*?)}#im', $answer, $matches); + if (!empty($answer)) { + preg_match_all('#\{(.*?)}#im', $answer, $matches); - if(!empty($matches[0][0]) && !empty($matches[1][0])) { - $pos = strpos($answer, $matches[0][0]); + if (!empty($matches[0][0]) && !empty($matches[1][0])) { + $pos = strpos($answer, $matches[0][0]); - $settings['lowLabel'] = trim(substr($answer, 0, $pos)); - $settings['highLabel'] = trim(substr($answer, $pos + strlen($matches[0][0]))); + $settings['lowLabel'] = trim(substr($answer, 0, $pos)); + $settings['highLabel'] = trim(substr($answer, $pos + strlen($matches[0][0]))); - preg_match_all('#\[([^|\]]+)]#im', $matches[1][0], $option_matches); + preg_match_all('#\[([^|\]]+)]#im', $matches[1][0], $option_matches); - if(!empty($option_matches[1])) { - foreach($option_matches[1] as $option) { - $options[] = trim($option); - } + if (!empty($option_matches[1])) { + foreach ($option_matches[1] as $option) { + $options[] = trim($option); + } + } + } } - } - } - return [$options, $settings]; - } + return [$options, $settings]; + } - /** - * Migrate user progress. - * - * @param array $data The data for the current request. - */ - protected function migrate_progress(array $data) { - self::before_start(); + /** + * Migrate user progress. + * + * @param array $data The data for the current request. + */ + protected function migrate_progress(array $data) + { + self::before_start(); - list($limit, $offset) = $this->get_request_limit_offset($data, 10); + list($limit, $offset) = $this->get_request_limit_offset($data, 10); - $fetch_batch = function ($limit, $offset) { - global $wpdb; + $fetch_batch = function ($limit, $offset) { + global $wpdb; - $query = "SELECT + $query = "SELECT ua.user_id, ua.activity_started, ua.activity_completed, @@ -1071,62 +1225,84 @@ protected function migrate_progress(array $data) { ORDER BY ua.activity_id ASC LIMIT %d OFFSET %d"; - $user_activity = $wpdb->get_results($wpdb->prepare($query, Lesson::$cpt, Course::$cpt, Lesson::$cpt, $limit, $offset)); + $user_activity = $wpdb->get_results( + $wpdb->prepare( + $query, + Lesson::$cpt, + Course::$cpt, + Lesson::$cpt, + $limit, + $offset + ) + ); - return is_array($user_activity) ? $user_activity : []; - }; + return is_array($user_activity) ? $user_activity : []; + }; - $processor = new MeprBatchMigrator($fetch_batch, $limit, $offset); + $processor = new MeprBatchMigrator($fetch_batch, $limit, $offset); - while($processor->next_batch()) { - $user_activity = $processor->get_items(); + while ($processor->next_batch()) { + $user_activity = $processor->get_items(); - foreach($user_activity as $activity) { - $user_id = (int) $activity->user_id; - $lesson_ids = array_filter([(int) $activity->mp_lesson_id, (int) $activity->mp_parent_lesson_id]); + foreach ($user_activity as $activity) { + $user_id = (int) $activity->user_id; + $lesson_ids = array_filter([(int) $activity->mp_lesson_id, (int) $activity->mp_parent_lesson_id]); - foreach($lesson_ids as $lesson_id) { - if(!UserProgress::has_completed_lesson($user_id, $lesson_id)) { - $user_progress = new UserProgress(); - $user_progress->lesson_id = $lesson_id; - $user_progress->course_id = (int) $activity->mp_course_id; - $user_progress->user_id = $user_id; - $user_progress->created_at = Utils::ts_to_mysql_date((int) $activity->activity_started); - $user_progress->completed_at = Utils::ts_to_mysql_date((int) $activity->activity_completed); - $user_progress->store(); - } + foreach ($lesson_ids as $lesson_id) { + if (!UserProgress::has_completed_lesson($user_id, $lesson_id)) { + $user_progress = new UserProgress(); + $user_progress->lesson_id = $lesson_id; + $user_progress->course_id = (int) $activity->mp_course_id; + $user_progress->user_id = $user_id; + $user_progress->created_at = Utils::ts_to_mysql_date((int) $activity->activity_started); + $user_progress->completed_at = Utils::ts_to_mysql_date((int) $activity->activity_completed); + $user_progress->store(); + } + } + } } - } - } - self::finish(); + self::finish(); - if($processor->has_items()) { - $this->send_success_response($data, [ - 'step' => 'progress', - 'offset' => $processor->get_offset(), - 'limit' => $processor->get_limit(), - 'logs' => $this->logs, - ]); - } + if ($processor->has_items()) { + $this->send_success_response($data, [ + 'step' => 'progress', + 'offset' => $processor->get_offset(), + 'limit' => $processor->get_limit(), + 'logs' => $this->logs, + ]); + } + + if (!$this->is_course_quizzes_addon_active()) { + update_option('mepr_migrator_learndash_completed', true); - $this->send_success_response($data, ['step' => 'attempts', 'logs' => $this->logs]); - } + $this->send_success_response($data, [ + 'status' => 'complete', + 'logs' => $this->logs, + ]); + } else { + $this->send_success_response($data, [ + 'step' => 'attempts', + 'logs' => $this->logs, + ]); + } + } - /** - * Migrate quiz attempts. - * - * @param array $data The data for the current request. - */ - protected function migrate_attempts(array $data) { - self::before_start(); + /** + * Migrate quiz attempts. + * + * @param array $data The data for the current request. + */ + protected function migrate_attempts(array $data) + { + self::before_start(); - list($limit, $offset) = $this->get_request_limit_offset($data, 10); + list($limit, $offset) = $this->get_request_limit_offset($data, 10); - $fetch_batch = function ($limit, $offset) { - global $wpdb; + $fetch_batch = function ($limit, $offset) { + global $wpdb; - $query = "SELECT + $query = "SELECT ua.activity_id, ua.user_id, ua.activity_started, @@ -1188,301 +1364,349 @@ protected function migrate_attempts(array $data) { AND ua.activity_completed IS NOT NULL AND ua.activity_completed > 0 AND ua.activity_id = uam.activity_id - ORDER BY ua.activity_id ASC + ORDER BY ua.activity_id LIMIT %d OFFSET %d"; - $user_activity = $wpdb->get_results($wpdb->prepare($query, Quiz::$cpt, Course::$cpt, Lesson::$cpt, $limit, $offset)); - - return is_array($user_activity) ? $user_activity : []; - }; - - $processor = new MeprBatchMigrator($fetch_batch, $limit, $offset); - $grader_user_id = $this->get_grader_user_id(); - - while($processor->next_batch()) { - $user_activity = $processor->get_items(); - - foreach($user_activity as $activity) { - $user_id = (int) $activity->user_id; - $quiz_id = (int) $activity->mp_lesson_id; - $activity_started = Utils::ts_to_mysql_date((int) $activity->activity_started); - $activity_completed = Utils::ts_to_mysql_date((int) $activity->activity_completed); - $lesson_ids = array_filter([$quiz_id, (int) $activity->mp_parent_lesson_id]); - - foreach($lesson_ids as $lesson_id) { - if(!UserProgress::has_completed_lesson($user_id, $lesson_id)) { - $user_progress = new UserProgress(); - $user_progress->lesson_id = $lesson_id; - $user_progress->course_id = (int) $activity->mp_course_id; - $user_progress->user_id = $user_id; - $user_progress->created_at = $activity_started; - $user_progress->completed_at = $activity_completed; - $user_progress->store(); - } - } + $user_activity = $wpdb->get_results( + $wpdb->prepare( + $query, + Quiz::$cpt, + Course::$cpt, + Lesson::$cpt, + $limit, + $offset + ) + ); - $metadata = $this->get_activity_metadata((int) $activity->activity_id); - $attempt = Attempt::get_one(['quiz_id' => $quiz_id, 'user_id' => $user_id]); + return is_array($user_activity) ? $user_activity : []; + }; + + $processor = new MeprBatchMigrator($fetch_batch, $limit, $offset); + $grader_user_id = $this->get_grader_user_id(); + + while ($processor->next_batch()) { + $user_activity = $processor->get_items(); + + foreach ($user_activity as $activity) { + $user_id = (int) $activity->user_id; + $quiz_id = (int) $activity->mp_lesson_id; + $activity_started = Utils::ts_to_mysql_date((int) $activity->activity_started); + $activity_completed = Utils::ts_to_mysql_date((int) $activity->activity_completed); + $lesson_ids = array_filter([$quiz_id, (int) $activity->mp_parent_lesson_id]); + + foreach ($lesson_ids as $lesson_id) { + if (!UserProgress::has_completed_lesson($user_id, $lesson_id)) { + $user_progress = new UserProgress(); + $user_progress->lesson_id = $lesson_id; + $user_progress->course_id = (int) $activity->mp_course_id; + $user_progress->user_id = $user_id; + $user_progress->created_at = $activity_started; + $user_progress->completed_at = $activity_completed; + $user_progress->store(); + } + } - if(!$attempt instanceof Attempt) { - $attempt = new Attempt(); - } + $metadata = $this->get_activity_metadata((int) $activity->activity_id); + $attempt = Attempt::get_one([ + 'quiz_id' => $quiz_id, + 'user_id' => $user_id, + ]); - $attempt->quiz_id = $quiz_id; - $attempt->user_id = $user_id; - $attempt->status = Attempt::$complete_str; - $attempt->points_awarded = (int) $metadata['points'] ?? 0; - $attempt->points_possible = (int) $metadata['total_points'] ?? 0; - $attempt->score = round($metadata['percentage'] ?? 0); - $attempt->started_at = $activity_started; - $attempt->finished_at = $activity_completed; - $result = $attempt->store(); - - if($result instanceof WP_Error || empty($attempt->id)) { - continue; - } + if (!$attempt instanceof Attempt) { + $attempt = new Attempt(); + } - $statistic_ref_id = $metadata['statistic_ref_id'] ?? 0; + $attempt->quiz_id = $quiz_id; + $attempt->user_id = $user_id; + $attempt->status = Attempt::$complete_str; + $attempt->points_awarded = (int) $metadata['points'] ?? 0; + $attempt->points_possible = (int) $metadata['total_points'] ?? 0; + $attempt->score = round($metadata['percentage'] ?? 0); + $attempt->started_at = $activity_started; + $attempt->finished_at = $activity_completed; + $result = $attempt->store(); + + if ($result instanceof WP_Error || empty($attempt->id)) { + continue; + } - if(empty($statistic_ref_id)) { - continue; - } + $statistic_ref_id = $metadata['statistic_ref_id'] ?? 0; - $migrated_questions = get_post_meta($quiz_id, '_mepr_learndash_migrated_questions', true); - $migrated_questions = is_array($migrated_questions) ? $migrated_questions : []; + if (empty($statistic_ref_id)) { + continue; + } - global $wpdb; + $migrated_questions = get_post_meta($quiz_id, '_mepr_learndash_migrated_questions', true); + $migrated_questions = is_array($migrated_questions) ? $migrated_questions : []; + + global $wpdb; + + $answers = $wpdb->get_results( + $wpdb->prepare( + "SELECT + s.question_id, + s.answer_data, + s.points, + q.points AS points_possible, + q.answer_type + FROM {$wpdb->prefix}learndash_pro_quiz_statistic s + INNER JOIN {$wpdb->prefix}learndash_pro_quiz_question q + ON q.id = s.question_id + WHERE statistic_ref_id = %d + AND q.online = 1", + $statistic_ref_id + ), + ARRAY_A + ); + + foreach ($answers as $answer) { + $ld_question_id = $answer['question_id']; + + if (!empty($migrated_questions[$ld_question_id])) { + $question_id = (int) $migrated_questions[$ld_question_id]; + } else { + $question_id = 0; + } + + if ($question_id) { + Answer::insert_or_replace_answer( + $attempt->id, + $question_id, + $this->get_answer_value($answer, $user_id, $question_id), + $answer['points_possible'], + $answer['points'], + $grader_user_id, + $attempt->finished_at, + $attempt->finished_at + ); + } + } + } + } - $answers = $wpdb->get_results( - $wpdb->prepare( - "SELECT - s.question_id, - s.answer_data, - s.points, - q.points AS points_possible, - q.answer_type - FROM {$wpdb->prefix}learndash_pro_quiz_statistic s - INNER JOIN {$wpdb->prefix}learndash_pro_quiz_question q - ON q.id = s.question_id - WHERE statistic_ref_id = %d - AND q.online = 1", - $statistic_ref_id - ), - ARRAY_A - ); + self::finish(); - foreach($answers as $answer) { - $ld_question_id = $answer['question_id']; - $question_id = !empty($migrated_questions[$ld_question_id]) ? (int) $migrated_questions[$ld_question_id] : 0; - - if($question_id) { - Answer::insert_or_replace_answer( - $attempt->id, - $question_id, - $this->get_answer_value($answer, $user_id, $question_id), - $answer['points_possible'], - $answer['points'], - $grader_user_id, - $attempt->finished_at, - $attempt->finished_at - ); - } + if ($processor->has_items()) { + $this->send_success_response($data, [ + 'step' => 'attempts', + 'offset' => $processor->get_offset(), + 'limit' => $processor->get_limit(), + 'logs' => $this->logs, + ]); } - } - } - self::finish(); + update_option('mepr_migrator_learndash_completed', true); - if($processor->has_items()) { - $this->send_success_response($data, [ - 'step' => 'attempts', - 'offset' => $processor->get_offset(), - 'limit' => $processor->get_limit(), - 'logs' => $this->logs, - ]); + $this->send_success_response($data, [ + 'status' => 'complete', + 'logs' => $this->logs, + ]); } - update_option('mepr_migrator_learndash_completed', true); - - $this->send_success_response($data, ['status' => 'complete', 'logs' => $this->logs]); - } - - /** - * Get metadata for a specific activity. - * - * @param int $activity_id The ID of the activity. - * @return array An array of metadata for the activity. - */ - protected function get_activity_metadata(int $activity_id): array { - global $wpdb; + /** + * Get metadata for a specific activity. + * + * @param integer $activity_id The ID of the activity. + * @return array An array of metadata for the activity. + */ + protected function get_activity_metadata(int $activity_id): array + { + global $wpdb; - $query = "SELECT activity_meta_key, activity_meta_value + $query = "SELECT activity_meta_key, activity_meta_value FROM {$wpdb->prefix}learndash_user_activity_meta WHERE activity_id = %d"; - $metadata = $wpdb->get_results( - $wpdb->prepare($query, $activity_id), - ARRAY_A - ); - - return wp_list_pluck($metadata, 'activity_meta_value', 'activity_meta_key'); - } - - /** - * Convert the answer from LD to MP format. - * - * @param array $answer - * @param int $user_id - * @param int $question_id - * @return array|string - */ - protected function get_answer_value(array $answer, int $user_id, int $question_id) { - $answer_data = json_decode($answer['answer_data'], true); - $value = ''; - - if(is_array($answer_data)) { - switch($answer['answer_type']) { - case 'single': - $index = array_search(1, $answer_data); - - if(is_int($index)) { - $value = (string) $index; - } - break; - case 'multiple': - $question = $this->get_question((int) $question_id); - - if($question instanceof Question && is_array($question->options)) { - $value = []; - - foreach ($answer_data as $key => $selected) { - if($selected && isset($question->options[$key])) { - $value[] = $question->options[$key]; - } - } - } - break; - case 'free_answer': - case 'assessment_answer': - $value = $answer_data[0] ?? ''; - break; - case 'sort_answer': - $question = $this->get_question((int) $question_id); - - if($question instanceof Question && is_array($question->options)) { - $updated = 0; - - foreach($question->options as $key => $value) { - $answer_hash = md5($user_id . $answer['question_id'] . $key); - $index = array_search($answer_hash, $answer_data); - - if(is_int($index)) { - $answer_data[$index] = $value; - $updated++; - } - } + $metadata = $wpdb->get_results( + $wpdb->prepare($query, $activity_id), + ARRAY_A + ); - if($updated == count($answer_data)) { - $value = $answer_data; - } - } - break; - case 'matrix_sort_answer': - $question = $this->get_question((int) $question_id); - - if($question instanceof Question && is_array($question->options) && is_array($question->answer)) { - $updated = 0; - - foreach($question->options as $key => $value) { - $answer_hash = md5($user_id . $answer['question_id'] . $key); - $index = array_search($answer_hash, $answer_data); - - if(is_int($index) && isset($question->answer[$key])) { - $answer_data[$index] = $question->answer[$key]; - $updated++; - } - } + return wp_list_pluck($metadata, 'activity_meta_value', 'activity_meta_key'); + } - if($updated == count($answer_data)) { - $value = $answer_data; + /** + * Convert the answer from LD to MP format. + * + * @param array $answer + * @param integer $user_id + * @param integer $question_id + * @return array|string + */ + protected function get_answer_value(array $answer, int $user_id, int $question_id) + { + $answer_data = json_decode($answer['answer_data'], true); + $value = ''; + + if (is_array($answer_data)) { + switch ($answer['answer_type']) { + case 'single': + $index = array_search(1, $answer_data); + + if (is_int($index)) { + $value = (string) $index; + } + break; + case 'multiple': + $question = $this->get_question($question_id); + + if ($question instanceof Question && is_array($question->options)) { + $value = []; + + foreach ($answer_data as $key => $selected) { + if ($selected && isset($question->options[$key])) { + $value[] = $question->options[$key]; + } + } + } + break; + case 'free_answer': + case 'assessment_answer': + $value = $answer_data[0] ?? ''; + break; + case 'sort_answer': + $question = $this->get_question($question_id); + + if ($question instanceof Question && is_array($question->options)) { + $updated = 0; + + foreach ($question->options as $key => $value) { + $answer_hash = md5($user_id . $answer['question_id'] . $key); + $index = array_search($answer_hash, $answer_data); + + if (is_int($index)) { + $answer_data[$index] = $value; + $updated++; + } + } + + if ($updated == count($answer_data)) { + $value = $answer_data; + } + } + break; + case 'matrix_sort_answer': + $question = $this->get_question($question_id); + + if ($question instanceof Question && is_array($question->options) && is_array($question->answer)) { + $updated = 0; + + foreach ($question->options as $key => $value) { + $answer_hash = md5($user_id . $answer['question_id'] . $key); + $index = array_search($answer_hash, $answer_data); + + if (is_int($index) && isset($question->answer[$key])) { + $answer_data[$index] = $question->answer[$key]; + $updated++; + } + } + + if ($updated == count($answer_data)) { + $value = $answer_data; + } + } + break; + case 'cloze_answer': + $value = $answer_data; + break; + case 'essay': + $value = get_post_field('post_content', $answer_data['graded_id'] ?? 0, 'raw'); + break; } - } - break; - case 'cloze_answer': - $value = $answer_data; - break; - case 'essay': - $value = get_post_field('post_content', $answer_data['graded_id'] ?? 0, 'raw'); - break; - } + } + + return $value; } - return $value; - } - - /** - * Get the default grader user ID. - * - * @return int - */ - protected function get_grader_user_id(): int { - $admins = get_users([ - 'role' => 'administrator', - 'number' => 1, - 'fields' => 'ID', - 'orderby' => 'ID', - 'order' => 'ASC', - ]); - - return isset($admins[0]) ? (int) $admins[0] : 0; - } - - /** - * Get a question instance by ID. - * - * @param int $question_id - * @return Question|null - */ - protected function get_question(int $question_id): ?Question { - $question = wp_cache_get($question_id, 'mepr-migrator-question'); - - if($question instanceof Question) { - return $question; + /** + * Get the default grader user ID. + * + * @return integer + */ + protected function get_grader_user_id(): int + { + $admins = get_users([ + 'role' => 'administrator', + 'number' => 1, + 'fields' => 'ID', + 'orderby' => 'ID', + 'order' => 'ASC', + ]); + + return isset($admins[0]) ? (int) $admins[0] : 0; } - $question = new Question($question_id); + /** + * Get a question instance by ID. + * + * @param integer $question_id + * @return Question|null + */ + protected function get_question(int $question_id): ?Question + { + $question = wp_cache_get($question_id, 'mepr-migrator-question'); + + if ($question instanceof Question) { + return $question; + } - if($question->id > 0) { - wp_cache_add($question_id, $question, 'mepr-migrator-question'); + $question = new Question($question_id); - return $question; - } + if ($question->id > 0) { + wp_cache_add($question_id, $question, 'mepr-migrator-question'); - return null; - } - - /** - * Is the migration from LearnDash to MP Courses possible? - * - * Currently, this just checks for at least one existing LD Course and that the quiz table exists. - * - * @return bool - */ - public static function is_migration_possible(): bool { - static $result; - - if(is_bool($result)) { - return $result; + return $question; + } + + return null; } - global $wpdb; - $mepr_db = MeprDb::fetch(); + /** + * Is the migration from LearnDash to MP Courses possible? + * + * Currently, this just checks for at least one existing LD Course and that the quiz table exists. + * + * @return boolean + */ + public static function is_migration_possible(): bool + { + static $result; + + if (is_bool($result)) { + return $result; + } + + global $wpdb; + $mepr_db = MeprDb::fetch(); + + $ld_course_exists = (bool) $wpdb->get_var( + "SELECT ID FROM $wpdb->posts WHERE post_type = 'sfwd-courses' LIMIT 1" + ); + + $ld_table_exists = $mepr_db->table_exists("{$wpdb->prefix}learndash_pro_quiz_master"); + + $result = $ld_course_exists && $ld_table_exists; - $ld_course_exists = (bool) $wpdb->get_var("SELECT ID FROM $wpdb->posts WHERE post_type = 'sfwd-courses' LIMIT 1"); - $ld_table_exists = $mepr_db->table_exists("{$wpdb->prefix}learndash_pro_quiz_master"); + return $result; + } - $result = $ld_course_exists && $ld_table_exists; + /** + * Is the Courses add-on active? + * + * @return boolean + */ + protected function is_courses_addon_active(): bool + { + return defined('memberpress\\courses\\VERSION'); + } - return $result; - } + /** + * Is the Course Quizzes add-on active? + * + * @return boolean + */ + protected function is_course_quizzes_addon_active(): bool + { + return defined('memberpress\\quizzes\\VERSION'); + } } diff --git a/app/models/MeprCoupon.php b/app/models/MeprCoupon.php index 43b5b65..d87aa8f 100644 --- a/app/models/MeprCoupon.php +++ b/app/models/MeprCoupon.php @@ -1,335 +1,359 @@ discount_types = array('percent', 'dollar'); - $this->timeframe_types = MeprCouponsHelper::get_available_time_frame(); - $this->load_cpt( - $obj, - self::$cpt, - array( - 'use_on_upgrades' => false, - 'should_start' => false, - 'should_expire' => false, - 'starts_on' => null, - 'expires_on' => null, - 'usage_count' => 0, - 'usage_amount' => 0, - 'discount_type' => 'percent', - 'discount_amount' => 0.00, - 'first_payment_discount_type' => 'percent', - 'first_payment_discount_amount' => 0.00, - 'valid_products' => array(), - 'discount_mode' => 'standard', - 'trial_days' => 0, - 'trial_amount' => 0.00, - 'expires_on_timezone' => 0, - 'start_on_timezone' => 0, - 'usage_per_user_count' => 0, - 'usage_per_user_count_timeframe' => 'lifetime' - ) - ); - } - - public function validate() { - $this->validate_is_bool($this->use_on_upgrades, 'use_on_upgrades'); - $this->validate_is_bool($this->should_start, 'should_start'); - $this->validate_is_bool($this->should_expire, 'should_expire'); - if($this->should_start) { $this->validate_is_timestamp($this->starts_on, 'starts_on'); } - if($this->should_expire) { $this->validate_is_timestamp($this->expires_on, 'expires_on'); } - $this->validate_is_numeric($this->usage_count, 0, null, 'usage_count'); - $this->validate_is_numeric($this->usage_amount, 0, null, 'usage_amount'); - $this->validate_is_in_array($this->discount_type, $this->discount_types, 'discount_type'); - $this->validate_is_currency($this->discount_amount, 0, null, 'discount_amount'); - $this->validate_is_array($this->valid_products, 'valid_products'); - - if($this->discount_mode == 'trial-override') { - $this->validate_is_numeric($this->trial_days, 0, null, 'trial_days'); - $this->validate_is_currency($this->trial_amount, 0, null, 'trial_amount'); + +if (!defined('ABSPATH')) { + die('You are not allowed to call this page directly.'); +} + +class MeprCoupon extends MeprCptModel +{ + public static $use_on_upgrades_str = '_mepr_coupons_use_on_upgrades'; + public static $should_start_str = '_mepr_coupons_should_start'; + public static $should_expire_str = '_mepr_coupons_should_expire'; + public static $starts_on_str = '_mepr_coupons_starts_on'; + public static $expires_on_str = '_mepr_coupons_expires_on'; + public static $usage_count_str = '_mepr_coupons_usage_count'; + public static $usage_amount_str = '_mepr_coupons_usage_amount'; + public static $discount_type_str = '_mepr_coupons_discount_type'; + public static $discount_mode_str = '_mepr_coupons_discount_mode'; + public static $discount_amount_str = '_mepr_coupons_discount_amount'; + public static $valid_products_str = '_mepr_coupons_valid_products'; + public static $trial_days_str = '_mepr_coupons_trial_days'; + public static $trial_amount_str = '_mepr_coupons_trial_amount'; + public static $last_run_str = 'mepr_coupons_expire_last_run'; + public static $nonce_str = 'mepr_coupons_nonce'; + public static $starts_on_month_str = 'mepr_coupons_start_month'; + public static $starts_on_day_str = 'mepr_coupons_start_day'; + public static $starts_on_year_str = 'mepr_coupons_start_year'; + public static $expires_on_month_str = 'mepr_coupons_ex_month'; + public static $expires_on_day_str = 'mepr_coupons_ex_day'; + public static $expires_on_year_str = 'mepr_coupons_ex_year'; + public static $expires_on_timezone_str = '_mepr_coupons_expire_timezone'; + public static $start_on_timezone_str = '_mepr_coupons_start_timezone'; + public static $first_payment_discount_type_str = '_mepr_coupons_first_payment_discount_type'; + public static $first_payment_discount_amount_str = '_mepr_coupons_first_payment_discount_amount'; + public static $usage_per_user_count_str = '_mepr_coupons_usage_per_user_count'; + public static $usage_per_user_count_timeframe_str = '_mepr_coupons_usage_per_user_count_timeframe'; + + public static $cpt = 'memberpresscoupon'; + + public $discount_types; + public $timeframe_types; + + /*** + * Instance Methods + ***/ + public function __construct($obj = null) + { + $this->discount_types = ['percent', 'dollar']; + $this->timeframe_types = MeprCouponsHelper::get_available_time_frame(); + $this->load_cpt( + $obj, + self::$cpt, + [ + 'use_on_upgrades' => false, + 'should_start' => false, + 'should_expire' => false, + 'starts_on' => null, + 'expires_on' => null, + 'usage_count' => 0, + 'usage_amount' => 0, + 'discount_type' => 'percent', + 'discount_amount' => 0.00, + 'first_payment_discount_type' => 'percent', + 'first_payment_discount_amount' => 0.00, + 'valid_products' => [], + 'discount_mode' => 'standard', + 'trial_days' => 0, + 'trial_amount' => 0.00, + 'expires_on_timezone' => 0, + 'start_on_timezone' => 0, + 'usage_per_user_count' => 0, + 'usage_per_user_count_timeframe' => 'lifetime', + ] + ); } - $this->validate_is_numeric($this->usage_per_user_count, 0, null, 'usage_per_user_count'); - $this->validate_is_in_array($this->usage_per_user_count_timeframe, array_keys($this->timeframe_types), 'usage_per_user_count_timeframe'); + public function validate() + { + $this->validate_is_bool($this->use_on_upgrades, 'use_on_upgrades'); + $this->validate_is_bool($this->should_start, 'should_start'); + $this->validate_is_bool($this->should_expire, 'should_expire'); + if ($this->should_start) { + $this->validate_is_timestamp($this->starts_on, 'starts_on'); + } + if ($this->should_expire) { + $this->validate_is_timestamp($this->expires_on, 'expires_on'); + } + $this->validate_is_numeric($this->usage_count, 0, null, 'usage_count'); + $this->validate_is_numeric($this->usage_amount, 0, null, 'usage_amount'); + $this->validate_is_in_array($this->discount_type, $this->discount_types, 'discount_type'); + $this->validate_is_currency($this->discount_amount, 0, null, 'discount_amount'); + $this->validate_is_array($this->valid_products, 'valid_products'); + + if ($this->discount_mode == 'trial-override') { + $this->validate_is_numeric($this->trial_days, 0, null, 'trial_days'); + $this->validate_is_currency($this->trial_amount, 0, null, 'trial_amount'); + } - } + $this->validate_is_numeric($this->usage_per_user_count, 0, null, 'usage_per_user_count'); + $this->validate_is_in_array($this->usage_per_user_count_timeframe, array_keys($this->timeframe_types), 'usage_per_user_count_timeframe'); + } - public function get_formatted_products() { - $formatted_array = array(); + public function get_formatted_products() + { + $formatted_array = []; - if(!empty($this->valid_products)) { - foreach($this->valid_products as $p) { - $product = get_post($p); + if (!empty($this->valid_products)) { + foreach ($this->valid_products as $p) { + $product = get_post($p); - if($product) { - $formatted_array[] = $product->post_title; + if ($product) { + $formatted_array[] = $product->post_title; + } + } + } else { + $formatted_array[] = __('None Selected', 'memberpress'); } - } - } - else { - $formatted_array[] = __('None Selected', 'memberpress'); - } - return $formatted_array; - } + return $formatted_array; + } - public static function get_all_active_coupons() { - return MeprCptModel::all('MeprCoupon'); - } + public static function get_all_active_coupons() + { + return MeprCptModel::all('MeprCoupon'); + } - public static function get_one_from_code($code, $ignore_status = false) { - global $wpdb; + public static function get_one_from_code($code, $ignore_status = false) + { + global $wpdb; - //Ignore the status here? - $and_status = "AND post_status = 'publish'"; - if($ignore_status) { - $and_status = ''; - } + // Ignore the status here? + $and_status = "AND post_status = 'publish'"; + if ($ignore_status) { + $and_status = ''; + } - $q = "SELECT ID + $q = "SELECT ID FROM {$wpdb->posts} WHERE post_title = %s AND post_type = %s {$and_status}"; - $id = $wpdb->get_var($wpdb->prepare($q, $code, self::$cpt)); + $id = $wpdb->get_var($wpdb->prepare($q, $code, self::$cpt)); - if(!$id) { - return false; - } - else { - return new MeprCoupon($id); - } - } - - /** - * Whether this coupon can be used for upgrades - * - * @return boolean - */ - public function can_use_on_upgrades() { - return (bool) $this->use_on_upgrades; - } - - public function is_valid($product_id, $user_id = 0) { - //Coupon has reached its usage limit (remember 0 = unlimited) - if($this->usage_amount > 0 and $this->usage_count >= $this->usage_amount) { - return false; + if (!$id) { + return false; + } else { + return new MeprCoupon($id); + } } - // Check if per user coupon usage is enabled. - if ( MeprUtils::is_user_logged_in() && $this->is_usage_per_user_enabled() ) { - $user_id = 0 >= (int) $user_id ? get_current_user_id() : $user_id; - $user_usage = $this->get_user_coupon_usage( $user_id ); - if( false === $user_usage || $user_usage >= $this->usage_per_user_count ) { - return false; - } + /** + * Whether this coupon can be used for upgrades + * + * @return boolean + */ + public function can_use_on_upgrades() + { + return (bool) $this->use_on_upgrades; } - $expire_timestamp = MeprCouponsHelper::convert_timestamp_to_tz( $this->expires_on, $this->expires_on_timezone ); // Converted expiry UTC timestamp to selected timezone stamp. + public function is_valid($product_id, $user_id = 0) + { + // Coupon has reached its usage limit (remember 0 = unlimited) + if ($this->usage_amount > 0 and $this->usage_count >= $this->usage_amount) { + return false; + } - //Coupon has expired - //This doesn't really need to be here but will be more accurate - //than waiting every 12 hours for the expiring cron to run - if($this->should_expire and $expire_timestamp <= time()) { - return false; - } + // Check if per user coupon usage is enabled. + if (MeprUtils::is_user_logged_in() && $this->is_usage_per_user_enabled()) { + $user_id = 0 >= (int) $user_id ? get_current_user_id() : $user_id; + $user_usage = $this->get_user_coupon_usage($user_id); + if (false === $user_usage || $user_usage >= $this->usage_per_user_count) { + return false; + } + } - $start_timestamp = MeprCouponsHelper::convert_timestamp_to_tz( $this->starts_on, $this->start_on_timezone ); // Converted start UTC timestamp to selected timezone stamp. + $expire_timestamp = MeprCouponsHelper::convert_timestamp_to_tz($this->expires_on, $this->expires_on_timezone); // Converted expiry UTC timestamp to selected timezone stamp. - //Coupon hasn't started - if($this->should_start and $start_timestamp > time()) { - return false; - } + // Coupon has expired + // This doesn't really need to be here but will be more accurate + // than waiting every 12 hours for the expiring cron to run + if ($this->should_expire and $expire_timestamp <= time()) { + return false; + } - //Coupon code is not valid for this membership - if(!in_array($product_id, $this->valid_products)) { - return false; - } + $start_timestamp = MeprCouponsHelper::convert_timestamp_to_tz($this->starts_on, $this->start_on_timezone); // Converted start UTC timestamp to selected timezone stamp. + + // Coupon hasn't started + if ($this->should_start and $start_timestamp > time()) { + return false; + } + + // Coupon code is not valid for this membership + if (!in_array($product_id, $this->valid_products)) { + return false; + } - // Can't be used on upgrades, so make sure we're not trying to apply to an upgrade - if ( MeprUtils::is_user_logged_in() && ! $this->can_use_on_upgrades() ) { - $prd = new MeprProduct( $product_id ); - if ( $prd->is_upgrade_or_downgrade_for( get_current_user_id(), 'upgrade' ) ) { - return false; - } + // Can't be used on upgrades, so make sure we're not trying to apply to an upgrade + if (MeprUtils::is_user_logged_in() && ! $this->can_use_on_upgrades()) { + $prd = new MeprProduct($product_id); + if ($prd->is_upgrade_or_downgrade_for(get_current_user_id(), 'upgrade')) { + return false; + } + } + + return apply_filters('mepr_coupon_is_valid', true, $this, $product_id); // If we made it here, the coupon is good } - return apply_filters('mepr_coupon_is_valid', true, $this, $product_id); // If we made it here, the coupon is good - } + // Hmmm...maybe this method should be moved to the Coupon Ctrl instead + public static function is_valid_coupon_code($code, $product_id) + { + $c = self::get_one_from_code($code); - //Hmmm...maybe this method should be moved to the Coupon Ctrl instead - public static function is_valid_coupon_code($code, $product_id) { - $c = self::get_one_from_code($code); + // Coupon does not exist or has expired + if ($c === false) { + return false; + } - //Coupon does not exist or has expired - if($c === false) { - return false; + return $c->is_valid($product_id); } - return $c->is_valid($product_id); - } - - /** - * Gets the coupon's amount - * - * @return int - */ - public function get_first_payment_discount_amount( $prd ) { - return MeprHooks::apply_filters( 'mepr_coupon_get_first_payment_discount_amount', $this->first_payment_discount_amount, $this, $prd ); - } - - /** - * Gets the coupon's amount - * - * @return int - */ - public function get_discount_amount( $prd ) { - return MeprHooks::apply_filters( 'mepr_coupon_get_discount_amount', $this->discount_amount, $this, $prd ); - } - - public function apply_discount($price, $is_first_payment=false, $prd = null) { - if($is_first_payment && $this->discount_mode=='first-payment') { - $discount_amount = $this->get_first_payment_discount_amount( $prd ); - $discount_type = $this->first_payment_discount_type; + /** + * Gets the coupon's amount + * + * @return integer + */ + public function get_first_payment_discount_amount($prd) + { + return MeprHooks::apply_filters('mepr_coupon_get_first_payment_discount_amount', $this->first_payment_discount_amount, $this, $prd); } - else { - $discount_amount = $this->get_discount_amount( $prd ); - $discount_type = $this->discount_type; + + /** + * Gets the coupon's amount + * + * @return integer + */ + public function get_discount_amount($prd) + { + return MeprHooks::apply_filters('mepr_coupon_get_discount_amount', $this->discount_amount, $this, $prd); } - $value = $price; + public function apply_discount($price, $is_first_payment = false, $prd = null) + { + if ($is_first_payment && $this->discount_mode == 'first-payment') { + $discount_amount = $this->get_first_payment_discount_amount($prd); + $discount_type = $this->first_payment_discount_type; + } else { + $discount_amount = $this->get_discount_amount($prd); + $discount_type = $this->discount_type; + } - if($discount_type == 'percent') { - $value = ((1 - ($discount_amount / 100)) * $price); - } - else { - $value = ($price - $discount_amount); - } + $value = $price; - return MeprUtils::format_float(max($value,0)); // must only be precise to 2 points - } + if ($discount_type == 'percent') { + $value = ((1 - ($discount_amount / 100)) * $price); + } else { + $value = ($price - $discount_amount); + } - /** Applies a trial override where appropriate. $obj must be a MeprProduct or MeprSubscription. */ - public function maybe_apply_trial_override(&$obj) { - if($this->discount_type =='percent' && $this->discount_amount == 100) { - $obj->trial = false; - $obj->trial_days = 0; - $obj->trial_amount = 0; - } - else if($this->discount_mode=='trial-override') { - $obj->trial = true; - $obj->trial_days = $this->trial_days; - $obj->trial_amount = MeprUtils::maybe_round_to_minimum_amount($this->trial_amount); - } - else if($this->discount_mode=='first-payment') { - $obj->trial = true; - $obj->trial_days = (($obj instanceof MeprProduct) ? $obj->days_in_my_period() : $obj->days_in_this_period()); - - if ($obj instanceof MeprSubscription) { - $obj->trial_amount = MeprUtils::maybe_round_to_minimum_amount( $this->apply_discount( $obj->product()->price, true, $obj->product() ) ); - } else { - $obj->trial_amount = MeprUtils::maybe_round_to_minimum_amount( $this->apply_discount( $obj->price, true, $obj ) ); - } + return MeprUtils::format_float(max($value, 0)); // must only be precise to 2 points } - // Basically, if the subscription does have a trial period - // because of a coupon then the trial payment should count as one of the limited cycle payments. - if( ($this->discount_mode=='trial-override' || $this->discount_mode=='first-payment') && - $obj instanceof MeprSubscription && - $obj->trial_amount > 0 && - $obj->limit_cycles && - $obj->limit_cycles_num >= 1 && - $obj->prorated_trial - ) { - $obj->limit_cycles_num = $obj->limit_cycles_num - 1; - } - } - - public static function expire_old_coupons_and_cleanup_db() { - global $wpdb; - $date = time(); - $last_run = get_option(self::$last_run_str, 0); //Prevents all this code from executing on every page load - - if(($date - $last_run) > 43200) { //Runs twice a day just to be sure - update_option(self::$last_run_str, $date); - $coupons = self::get_all_active_coupons(); - - if(!empty($coupons)) { - foreach($coupons as $coupon) { - $expire_on = MeprCouponsHelper::convert_timestamp_to_tz( $coupon->expires_on, $coupon->expires_on_timezone ); - if($coupon->should_expire && $date > $expire_on) { - $coupon->mark_as_expired(); - } + /** + * Applies a trial override where appropriate. $obj must be a MeprProduct or MeprSubscription. + */ + public function maybe_apply_trial_override(&$obj) + { + if ($this->discount_type == 'percent' && $this->discount_amount == 100) { + $obj->trial = false; + $obj->trial_days = 0; + $obj->trial_amount = 0; + } elseif ($this->discount_mode == 'trial-override') { + $obj->trial = true; + $obj->trial_days = $this->trial_days; + $obj->trial_amount = MeprUtils::maybe_round_to_minimum_amount($this->trial_amount); + } elseif ($this->discount_mode == 'first-payment') { + $obj->trial = true; + $obj->trial_days = (($obj instanceof MeprProduct) ? $obj->days_in_my_period() : $obj->days_in_this_period()); + + if ($obj instanceof MeprSubscription) { + $obj->trial_amount = MeprUtils::maybe_round_to_minimum_amount($this->apply_discount($obj->product()->price, true, $obj->product())); + } else { + $obj->trial_amount = MeprUtils::maybe_round_to_minimum_amount($this->apply_discount($obj->price, true, $obj)); + } } - } - //While we're in here we should consider deleting auto-draft coupons, waste of db space - $sq1 = "SELECT ID + // Basically, if the subscription does have a trial period + // because of a coupon then the trial payment should count as one of the limited cycle payments. + if ( + ($this->discount_mode == 'trial-override' || $this->discount_mode == 'first-payment') && + $obj instanceof MeprSubscription && + $obj->trial_amount > 0 && + $obj->limit_cycles && + $obj->limit_cycles_num >= 1 && + $obj->prorated_trial + ) { + $obj->limit_cycles_num = $obj->limit_cycles_num - 1; + } + } + + public static function expire_old_coupons_and_cleanup_db() + { + global $wpdb; + $date = time(); + $last_run = get_option(self::$last_run_str, 0); // Prevents all this code from executing on every page load + + if (($date - $last_run) > 43200) { // Runs twice a day just to be sure + update_option(self::$last_run_str, $date); + $coupons = self::get_all_active_coupons(); + + if (!empty($coupons)) { + foreach ($coupons as $coupon) { + $expire_on = MeprCouponsHelper::convert_timestamp_to_tz($coupon->expires_on, $coupon->expires_on_timezone); + if ($coupon->should_expire && $date > $expire_on) { + $coupon->mark_as_expired(); + } + } + } + + // While we're in here we should consider deleting auto-draft coupons, waste of db space + $sq1 = "SELECT ID FROM {$wpdb->posts} - WHERE post_type = '".self::$cpt."' AND + WHERE post_type = '" . self::$cpt . "' AND post_status = 'auto-draft'"; - $sq1_res = $wpdb->get_col($sq1); - if(!empty($sq1_res)) { - $post_ids = implode(',', $sq1_res); - $q1 = "DELETE + $sq1_res = $wpdb->get_col($sq1); + if (!empty($sq1_res)) { + $post_ids = implode(',', $sq1_res); + $q1 = "DELETE FROM {$wpdb->postmeta} WHERE post_id IN ({$post_ids})"; - $q2 = "DELETE + $q2 = "DELETE FROM {$wpdb->posts} - WHERE post_type = '".self::$cpt."' AND + WHERE post_type = '" . self::$cpt . "' AND post_status = 'auto-draft'"; - $wpdb->query($q1); - $wpdb->query($q2); - } + $wpdb->query($q1); + $wpdb->query($q2); + } + } } - } - public function mark_as_expired() { - $post = array('ID' => $this->ID, 'post_status' => 'trash'); + public function mark_as_expired() + { + $post = [ + 'ID' => $this->ID, + 'post_status' => 'trash', + ]; - wp_update_post($post); - } + wp_update_post($post); + } - public function update_usage_count() { - global $wpdb; - $mepr_db = new MeprDb(); - $tcount = 0; + public function update_usage_count() + { + global $wpdb; + $mepr_db = new MeprDb(); + $tcount = 0; - $sq = " + $sq = " SELECT COUNT(DISTINCT subscription_id) FROM {$mepr_db->transactions} WHERE coupon_id = %d @@ -338,12 +362,14 @@ public function update_usage_count() { AND status <> %s; "; - $sq = $wpdb->prepare($sq, $this->ID, MeprTransaction::$payment_str, MeprTransaction::$subscription_confirmation_str, MeprSubscription::$pending_str); + $sq = $wpdb->prepare($sq, $this->ID, MeprTransaction::$payment_str, MeprTransaction::$subscription_confirmation_str, MeprSubscription::$pending_str); - if($sqcount = $wpdb->get_var($sq)) { $tcount += $sqcount; } + if ($sqcount = $wpdb->get_var($sq)) { + $tcount += $sqcount; + } - //Query one-time payments next - $lq = " + // Query one-time payments next + $lq = " SELECT COUNT(*) FROM {$mepr_db->transactions} WHERE coupon_id = %d @@ -352,153 +378,161 @@ public function update_usage_count() { AND status <> %s "; - $lq = $wpdb->prepare($lq, $this->ID, MeprTransaction::$payment_str, MeprTransaction::$pending_str); - - if($lqcount = $wpdb->get_var($lq)) { $tcount += $lqcount; } - - //Update and store - $this->usage_count = $tcount; - $this->store(); - } - - public function store_meta() { - update_post_meta($this->ID, self::$use_on_upgrades_str, $this->use_on_upgrades); - update_post_meta($this->ID, self::$should_start_str, $this->should_start); - update_post_meta($this->ID, self::$should_expire_str, $this->should_expire); - update_post_meta($this->ID, self::$starts_on_str, $this->starts_on); - update_post_meta($this->ID, self::$expires_on_str, $this->expires_on); - update_post_meta($this->ID, self::$usage_count_str, $this->usage_count); - update_post_meta($this->ID, self::$usage_amount_str, $this->usage_amount); - update_post_meta($this->ID, self::$discount_type_str, $this->discount_type); - update_post_meta($this->ID, self::$discount_amount_str, $this->discount_amount); - update_post_meta($this->ID, self::$valid_products_str, $this->valid_products); - update_post_meta($this->ID, self::$discount_mode_str, $this->discount_mode); - update_post_meta($this->ID, self::$trial_days_str, $this->trial_days); - update_post_meta($this->ID, self::$trial_amount_str, $this->trial_amount); - update_post_meta($this->ID, self::$first_payment_discount_type_str, $this->first_payment_discount_type); - update_post_meta($this->ID, self::$first_payment_discount_amount_str, $this->first_payment_discount_amount); - update_post_meta($this->ID, self::$expires_on_timezone_str, $this->expire_timezone); - update_post_meta($this->ID, self::$start_on_timezone_str, $this->start_timezone); - update_post_meta($this->ID, self::$usage_per_user_count_str, $this->usage_per_user_count); - update_post_meta($this->ID, self::$usage_per_user_count_timeframe_str, $this->usage_per_user_count_timeframe); - } - - /** - * Get the Stripe Coupon ID - * - * @param string $gateway_id The gateway ID - * @param string $discount_amount The coupon discount amount - * @param bool $onetime - * @return string|false - */ - public function get_stripe_coupon_id($gateway_id, $discount_amount, $onetime) { - $meta_key = sprintf('_mepr_stripe_coupon_id_%s_%s', $gateway_id, $this->terms_hash($discount_amount)); - - if ($onetime) { - $meta_key = sprintf('_mepr_stripe_onetime_coupon_id_%s_%s', $gateway_id, $this->terms_hash($discount_amount)); - } + $lq = $wpdb->prepare($lq, $this->ID, MeprTransaction::$payment_str, MeprTransaction::$pending_str); - return get_post_meta($this->ID, $meta_key, true); - } - - /** - * Set the Stripe Coupon ID - * - * @param string $gateway_id The gateway ID - * @param string $discount_amount The coupon discount amount - * @param string $coupon_id The Stripe Coupon ID - * @param bool $onetime - */ - public function set_stripe_coupon_id($gateway_id, $discount_amount, $coupon_id, $onetime) { - $meta_key = sprintf('_mepr_stripe_coupon_id_%s_%s', $gateway_id, $this->terms_hash($discount_amount)); - - if ($onetime) { - $meta_key = sprintf('_mepr_stripe_onetime_coupon_id_%s_%s', $gateway_id, $this->terms_hash($discount_amount)); + if ($lqcount = $wpdb->get_var($lq)) { + $tcount += $lqcount; + } + + // Update and store + $this->usage_count = $tcount; + $this->store(); } - update_post_meta($this->ID, $meta_key, $coupon_id); - } - - /** - * Get the hash of the coupon discount terms - * - * If this hash changes then a different Stripe Coupon will be created. - * - * @param string $discount_amount The coupon discount amount - * @return string - */ - private function terms_hash($discount_amount) { - $terms = [ - 'type' => $this->discount_type, - 'amount' => $discount_amount - ]; - - if($this->discount_type != 'percent') { - $mepr_options = MeprOptions::fetch(); - $terms['currency'] = $mepr_options->currency_code; + public function store_meta() + { + update_post_meta($this->ID, self::$use_on_upgrades_str, $this->use_on_upgrades); + update_post_meta($this->ID, self::$should_start_str, $this->should_start); + update_post_meta($this->ID, self::$should_expire_str, $this->should_expire); + update_post_meta($this->ID, self::$starts_on_str, $this->starts_on); + update_post_meta($this->ID, self::$expires_on_str, $this->expires_on); + update_post_meta($this->ID, self::$usage_count_str, $this->usage_count); + update_post_meta($this->ID, self::$usage_amount_str, $this->usage_amount); + update_post_meta($this->ID, self::$discount_type_str, $this->discount_type); + update_post_meta($this->ID, self::$discount_amount_str, $this->discount_amount); + update_post_meta($this->ID, self::$valid_products_str, $this->valid_products); + update_post_meta($this->ID, self::$discount_mode_str, $this->discount_mode); + update_post_meta($this->ID, self::$trial_days_str, $this->trial_days); + update_post_meta($this->ID, self::$trial_amount_str, $this->trial_amount); + update_post_meta($this->ID, self::$first_payment_discount_type_str, $this->first_payment_discount_type); + update_post_meta($this->ID, self::$first_payment_discount_amount_str, $this->first_payment_discount_amount); + update_post_meta($this->ID, self::$expires_on_timezone_str, $this->expire_timezone); + update_post_meta($this->ID, self::$start_on_timezone_str, $this->start_timezone); + update_post_meta($this->ID, self::$usage_per_user_count_str, $this->usage_per_user_count); + update_post_meta($this->ID, self::$usage_per_user_count_timeframe_str, $this->usage_per_user_count_timeframe); } - return md5(serialize($terms)); - } - - /** - * Delete the Stripe Coupon ID - * - * @param string $gateway_id The gateway ID - * @param string $coupon_id The Stripe Coupon ID - */ - public static function delete_stripe_coupon_id($gateway_id, $coupon_id) - { - if(!is_string($coupon_id) || $coupon_id === '') { - return; + /** + * Get the Stripe Coupon ID + * + * @param string $gateway_id The gateway ID + * @param string $discount_amount The coupon discount amount + * @param boolean $onetime + * @return string|false + */ + public function get_stripe_coupon_id($gateway_id, $discount_amount, $onetime) + { + $meta_key = sprintf('_mepr_stripe_coupon_id_%s_%s', $gateway_id, $this->terms_hash($discount_amount)); + + if ($onetime) { + $meta_key = sprintf('_mepr_stripe_onetime_coupon_id_%s_%s', $gateway_id, $this->terms_hash($discount_amount)); + } + + return get_post_meta($this->ID, $meta_key, true); } - global $wpdb; + /** + * Set the Stripe Coupon ID + * + * @param string $gateway_id The gateway ID + * @param string $discount_amount The coupon discount amount + * @param string $coupon_id The Stripe Coupon ID + * @param boolean $onetime + */ + public function set_stripe_coupon_id($gateway_id, $discount_amount, $coupon_id, $onetime) + { + $meta_key = sprintf('_mepr_stripe_coupon_id_%s_%s', $gateway_id, $this->terms_hash($discount_amount)); + + if ($onetime) { + $meta_key = sprintf('_mepr_stripe_onetime_coupon_id_%s_%s', $gateway_id, $this->terms_hash($discount_amount)); + } - $query = $wpdb->prepare( - "SELECT meta_id FROM {$wpdb->postmeta} WHERE meta_key LIKE %s AND meta_value = %s", - $wpdb->esc_like('_mepr_stripe_coupon_id_' . $gateway_id) . '%', - $coupon_id - ); + update_post_meta($this->ID, $meta_key, $coupon_id); + } - $meta_ids = $wpdb->get_col($query); + /** + * Get the hash of the coupon discount terms + * + * If this hash changes then a different Stripe Coupon will be created. + * + * @param string $discount_amount The coupon discount amount + * @return string + */ + private function terms_hash($discount_amount) + { + $terms = [ + 'type' => $this->discount_type, + 'amount' => $discount_amount, + ]; + + if ($this->discount_type != 'percent') { + $mepr_options = MeprOptions::fetch(); + $terms['currency'] = $mepr_options->currency_code; + } - if(is_array($meta_ids) && count($meta_ids)) { - foreach($meta_ids as $meta_id) { - delete_metadata_by_mid('post', $meta_id); - } + return md5(serialize($terms)); } - } - /** - * Check if user coupon usage is active. - * - * @return bool. - */ - public function is_usage_per_user_enabled() { - return MeprHooks::apply_filters( 'mepr_coupon_usage_per_user_enabled', (bool) $this->usage_per_user_count > 0, $this ); - } - /** - * Get the user usage count. - * - * @param int $user_id User ID. - * @param int $coupon_id Coupon ID. - * @param string $time_frame Time frame. - * - * @return int User coupon usage. - */ - public function get_user_coupon_usage( $user_id ) { - if ( 0 >= $user_id ) { - return false; + + /** + * Delete the Stripe Coupon ID + * + * @param string $gateway_id The gateway ID + * @param string $coupon_id The Stripe Coupon ID + */ + public static function delete_stripe_coupon_id($gateway_id, $coupon_id) + { + if (!is_string($coupon_id) || $coupon_id === '') { + return; + } + + global $wpdb; + + $query = $wpdb->prepare( + "SELECT meta_id FROM {$wpdb->postmeta} WHERE meta_key LIKE %s AND meta_value = %s", + $wpdb->esc_like('_mepr_stripe_coupon_id_' . $gateway_id) . '%', + $coupon_id + ); + + $meta_ids = $wpdb->get_col($query); + + if (is_array($meta_ids) && count($meta_ids)) { + foreach ($meta_ids as $meta_id) { + delete_metadata_by_mid('post', $meta_id); + } + } } - global $wpdb; - $mepr_db = new MeprDb(); - $total_count = 0; - $date_query = ''; - if ( 'lifetime' !== $this->usage_per_user_count_timeframe ) { - $date_query = MeprCouponsHelper::get_date_query_from_time_frame($this->usage_per_user_count_timeframe); + /** + * Check if user coupon usage is active. + * + * @return boolean + */ + public function is_usage_per_user_enabled() + { + return MeprHooks::apply_filters('mepr_coupon_usage_per_user_enabled', (bool) $this->usage_per_user_count > 0, $this); } + /** + * Get the user usage count. + * + * @param integer $user_id User ID. + * @param integer $coupon_id Coupon ID. + * @param string $time_frame Time frame. + * + * @return integer User coupon usage. + */ + public function get_user_coupon_usage($user_id) + { + if (0 >= $user_id) { + return false; + } + global $wpdb; + $mepr_db = new MeprDb(); + $total_count = 0; + $date_query = ''; + if ('lifetime' !== $this->usage_per_user_count_timeframe) { + $date_query = MeprCouponsHelper::get_date_query_from_time_frame($this->usage_per_user_count_timeframe); + } - $subscription_query = " + $subscription_query = " SELECT COUNT(DISTINCT subscription_id) FROM {$mepr_db->transactions} WHERE coupon_id = %d @@ -509,11 +543,13 @@ public function get_user_coupon_usage( $user_id ) { $date_query; "; - $subscription_query = $wpdb->prepare($subscription_query, $this->ID, MeprTransaction::$payment_str, MeprTransaction::$subscription_confirmation_str, MeprSubscription::$pending_str, $user_id); - if($subscription_query_count = $wpdb->get_var($subscription_query)) { $total_count += $subscription_query_count; } + $subscription_query = $wpdb->prepare($subscription_query, $this->ID, MeprTransaction::$payment_str, MeprTransaction::$subscription_confirmation_str, MeprSubscription::$pending_str, $user_id); + if ($subscription_query_count = $wpdb->get_var($subscription_query)) { + $total_count += $subscription_query_count; + } - //Query one-time payments next - $lifetime_query = " + // Query one-time payments next + $lifetime_query = " SELECT COUNT(*) FROM {$mepr_db->transactions} WHERE coupon_id = %d @@ -524,9 +560,11 @@ public function get_user_coupon_usage( $user_id ) { $date_query; "; - $lifetime_query = $wpdb->prepare($lifetime_query, $this->ID, MeprTransaction::$payment_str, MeprTransaction::$pending_str, $user_id); + $lifetime_query = $wpdb->prepare($lifetime_query, $this->ID, MeprTransaction::$payment_str, MeprTransaction::$pending_str, $user_id); - if($lifetime_query_count = $wpdb->get_var($lifetime_query)) { $total_count += $lifetime_query_count; } - return MeprHooks::apply_filters( 'mepr_coupon_usage_per_user_count', $total_count, $user_id, $this ); - } + if ($lifetime_query_count = $wpdb->get_var($lifetime_query)) { + $total_count += $lifetime_query_count; + } + return MeprHooks::apply_filters('mepr_coupon_usage_per_user_count', $total_count, $user_id, $this); + } } //End class diff --git a/app/models/MeprDrm.php b/app/models/MeprDrm.php index 1ade8d1..b162c10 100644 --- a/app/models/MeprDrm.php +++ b/app/models/MeprDrm.php @@ -1,13 +1,24 @@ rec = new stdClass(); - $this->rec->id = $id; - } +if (!defined('ABSPATH')) { + die('You are not allowed to call this page directly.'); +} - public function store(){} - public function destroy(){} +class MeprDrm extends MeprBaseModel +{ + /** + * INSTANCE VARIABLES & METHODS + **/ + public function __construct($id) + { + $this->rec = new stdClass(); + $this->rec->id = $id; + } + + public function store() + { + } + public function destroy() + { + } } //End class diff --git a/app/models/MeprEvent.php b/app/models/MeprEvent.php index fd84b6e..d57e471 100644 --- a/app/models/MeprEvent.php +++ b/app/models/MeprEvent.php @@ -1,199 +1,221 @@ initialize( - array( - 'id' => 0, - 'args' => null, - 'event' => 'login', - 'evt_id' => 0, - 'evt_id_type' => 'users', - 'created_at' => null - ), - $obj - ); - } - - public function validate() { - $this->validate_is_numeric($this->evt_id, 0, null, 'evt_id'); - } - - public static function get_one($id, $return_type = OBJECT) { - $mepr_db = new MeprDb(); - $args = compact('id'); - return $mepr_db->get_one_record($mepr_db->events, $args, $return_type); - } - - public static function get_one_by_event_and_evt_id_and_evt_id_type($event, $evt_id, $evt_id_type, $return_type = OBJECT) { - $mepr_db = new MeprDb(); - return $mepr_db->get_one_record($mepr_db->events, compact('event','evt_id','evt_id_type'), $return_type); - } - - public static function get_count() { - $mepr_db = new MeprDb(); - return $mepr_db->get_count($mepr_db->events); - } - - public static function get_count_by_event($event) { - $mepr_db = new MeprDb(); - return $mepr_db->get_count($mepr_db->events, compact('event')); - } - - public static function get_count_by_evt_id_type($evt_id_type) { - $mepr_db = new MeprDb(); - return $mepr_db->get_count($mepr_db->events, compact('evt_id_type')); - } - - public static function get_count_by_event_and_evt_id_and_evt_id_type($event, $evt_id, $evt_id_type) { - $mepr_db = new MeprDb(); - return $mepr_db->get_count($mepr_db->events, compact('event','evt_id','evt_id_type')); - } - - public static function get_all($order_by = '', $limit = '') { - $mepr_db = new MeprDb(); - return $mepr_db->get_records($mepr_db->events, array(), $order_by, $limit); - } - - public static function get_all_by_event($event, $order_by = '', $limit = '') { - $mepr_db = new MeprDb(); - $args = array('event' => $event); - return $mepr_db->get_records($mepr_db->events, $args, $order_by, $limit); - } - - public static function get_all_by_evt_id_type($evt_id_type, $order_by = '', $limit = '') { - $mepr_db = new MeprDb(); - $args = array('evt_id_type' => $evt_id_type); - return $mepr_db->get_records($mepr_db->events, $args, $order_by, $limit); - } - - public function store() { - $mepr_db = new MeprDb(); - - MeprHooks::do_action('mepr-event-pre-store', $this); - - $this->use_existing_if_unique(); - - $vals = (array)$this->rec; - unset($vals['created_at']); // let mepr_db handle this - - if(isset($this->id) and (int)$this->id > 0) { - $mepr_db->update_record( $mepr_db->events, $this->id, $vals ); - MeprHooks::do_action('mepr-event-update', $this); + +if (!defined('ABSPATH')) { + die('You are not allowed to call this page directly.'); +} + +class MeprEvent extends MeprBaseModel +{ + // Supported event types + public static $users_str = 'users'; + public static $transactions_str = 'transactions'; + public static $subscriptions_str = 'subscriptions'; + public static $drm_str = 'drm'; + + // User events + public static $login_event_str = 'login'; + + public function __construct($obj = null) + { + $this->initialize( + [ + 'id' => 0, + 'args' => null, + 'event' => 'login', + 'evt_id' => 0, + 'evt_id_type' => 'users', + 'created_at' => null, + ], + $obj + ); } - else { - $this->id = $mepr_db->create_record( $mepr_db->events, $vals ); - MeprHooks::do_action('mepr-event-create', $this); - MeprHooks::do_action('mepr-event',$this); - MeprHooks::do_action("mepr-evt-{$this->event}",$this); // DEPRECATED - MeprHooks::do_action("mepr-event-{$this->event}",$this); + public function validate() + { + $this->validate_is_numeric($this->evt_id, 0, null, 'evt_id'); } - MeprHooks::do_action('mepr-event-store', $this); + public static function get_one($id, $return_type = OBJECT) + { + $mepr_db = new MeprDb(); + $args = compact('id'); + return $mepr_db->get_one_record($mepr_db->events, $args, $return_type); + } - return $this->id; - } + public static function get_one_by_event_and_evt_id_and_evt_id_type($event, $evt_id, $evt_id_type, $return_type = OBJECT) + { + $mepr_db = new MeprDb(); + return $mepr_db->get_one_record($mepr_db->events, compact('event', 'evt_id', 'evt_id_type'), $return_type); + } - public function destroy() { - $mepr_db = new MeprDb(); + public static function get_count() + { + $mepr_db = new MeprDb(); + return $mepr_db->get_count($mepr_db->events); + } - $id = $this->id; - $args = compact('id'); + public static function get_count_by_event($event) + { + $mepr_db = new MeprDb(); + return $mepr_db->get_count($mepr_db->events, compact('event')); + } - MeprHooks::do_action('mepr_event_destroy', $this); + public static function get_count_by_evt_id_type($evt_id_type) + { + $mepr_db = new MeprDb(); + return $mepr_db->get_count($mepr_db->events, compact('evt_id_type')); + } - return MeprHooks::apply_filters('mepr_delete_event', $mepr_db->delete_records($mepr_db->events, $args), $args); - } + public static function get_count_by_event_and_evt_id_and_evt_id_type($event, $evt_id, $evt_id_type) + { + $mepr_db = new MeprDb(); + return $mepr_db->get_count($mepr_db->events, compact('event', 'evt_id', 'evt_id_type')); + } - // TODO: This is a biggie ... we don't want to send the event object like this - // we need to send the object associated with the event instead. - public function get_data() { - $obj = false; - switch($this->evt_id_type) { - case self::$users_str: - $obj = new MeprUser($this->evt_id); + public static function get_all($order_by = '', $limit = '') + { + $mepr_db = new MeprDb(); + return $mepr_db->get_records($mepr_db->events, [], $order_by, $limit); + } - // If member-deleted event is being passed, make sure we generate some data. - if(!isset($obj->ID) || $obj->ID <= 0) { - if($this->event == 'member-deleted') { - $obj->ID = 0; - $obj->user_email = 'johndoe@email.com'; - $obj->user_login = 'johndoe'; - $obj->first_name = 'John'; - $obj->last_name = 'Doe'; - } - } + public static function get_all_by_event($event, $order_by = '', $limit = '') + { + $mepr_db = new MeprDb(); + $args = ['event' => $event]; + return $mepr_db->get_records($mepr_db->events, $args, $order_by, $limit); + } - break; - case self::$transactions_str: - $obj = new MeprTransaction($this->evt_id); - break; - case self::$subscriptions_str: - $obj = new MeprSubscription($this->evt_id); - break; - default: - return new WP_Error(__('An unsupported Event type was used', 'memberpress')); + public static function get_all_by_evt_id_type($evt_id_type, $order_by = '', $limit = '') + { + $mepr_db = new MeprDb(); + $args = ['evt_id_type' => $evt_id_type]; + return $mepr_db->get_records($mepr_db->events, $args, $order_by, $limit); } - return $obj; - } + public function store() + { + $mepr_db = new MeprDb(); - public function get_args() { - return json_decode($this->args); - } + MeprHooks::do_action('mepr-event-pre-store', $this); - public static function record($event, MeprBaseModel $obj, $args='') { - //Nothing to record? Hopefully this stops some ghost duplicate reminders we are seeing - //Gotta use ->rec here to avoid weird shiz from happening hopefully - if((!isset($obj->rec->id) || !$obj->rec->id) && (!isset($obj->rec->ID) || !$obj->rec->ID)) { return; } + $this->use_existing_if_unique(); - $e = new MeprEvent(); - $e->event = $event; - $e->args = $args; + $vals = (array)$this->rec; + unset($vals['created_at']); // let mepr_db handle this - // Just turn objects into json for fun - if(is_array($args) || is_object($args)) { - $e->args = json_encode($args); - } + if (isset($this->id) and (int)$this->id > 0) { + $mepr_db->update_record($mepr_db->events, $this->id, $vals); + MeprHooks::do_action('mepr-event-update', $this); + } else { + $this->id = $mepr_db->create_record($mepr_db->events, $vals); + MeprHooks::do_action('mepr-event-create', $this); + MeprHooks::do_action('mepr-event', $this); - if($obj instanceof MeprUser) { - $e->evt_id = $obj->rec->ID; - $e->evt_id_type = self::$users_str; + MeprHooks::do_action("mepr-evt-{$this->event}", $this); // DEPRECATED + MeprHooks::do_action("mepr-event-{$this->event}", $this); + } + + MeprHooks::do_action('mepr-event-store', $this); + + return $this->id; } - elseif($obj instanceof MeprTransaction) { - $e->evt_id = $obj->rec->id; - $e->evt_id_type = self::$transactions_str; + + public function destroy() + { + $mepr_db = new MeprDb(); + + $id = $this->id; + $args = compact('id'); + + MeprHooks::do_action('mepr_event_destroy', $this); + + return MeprHooks::apply_filters('mepr_delete_event', $mepr_db->delete_records($mepr_db->events, $args), $args); } - elseif($obj instanceof MeprSubscription) { - $e->evt_id = $obj->rec->id; - $e->evt_id_type = self::$subscriptions_str; + + // TODO: This is a biggie ... we don't want to send the event object like this + // we need to send the object associated with the event instead. + public function get_data() + { + $obj = false; + switch ($this->evt_id_type) { + case self::$users_str: + $obj = new MeprUser($this->evt_id); + + // If member-deleted event is being passed, make sure we generate some data. + if (!isset($obj->ID) || $obj->ID <= 0) { + if ($this->event == 'member-deleted') { + $obj->ID = 0; + $obj->user_email = 'johndoe@email.com'; + $obj->user_login = 'johndoe'; + $obj->first_name = 'John'; + $obj->last_name = 'Doe'; + } + } + + break; + case self::$transactions_str: + $obj = new MeprTransaction($this->evt_id); + break; + case self::$subscriptions_str: + $obj = new MeprSubscription($this->evt_id); + break; + default: + return new WP_Error(__('An unsupported Event type was used', 'memberpress')); + } + + return $obj; } - elseif($obj instanceof MeprDrm) { - $e->evt_id = $obj->rec->id; - $e->evt_id_type = self::$drm_str; + + public function get_args() + { + return json_decode($this->args); } - else { return; } - $e->store(); - } + public static function record($event, MeprBaseModel $obj, $args = '') + { + // Nothing to record? Hopefully this stops some ghost duplicate reminders we are seeing + // Gotta use ->rec here to avoid weird shiz from happening hopefully + if ((!isset($obj->rec->id) || !$obj->rec->id) && (!isset($obj->rec->ID) || !$obj->rec->ID)) { + return; + } + + $e = new MeprEvent(); + $e->event = $event; + $e->args = $args; + + // Just turn objects into json for fun + if (is_array($args) || is_object($args)) { + $e->args = json_encode($args); + } - /** Get the latest object for a given event */ - public static function latest($event) { - global $wpdb; - $mepr_db = new MeprDb(); + if ($obj instanceof MeprUser) { + $e->evt_id = $obj->rec->ID; + $e->evt_id_type = self::$users_str; + } elseif ($obj instanceof MeprTransaction) { + $e->evt_id = $obj->rec->id; + $e->evt_id_type = self::$transactions_str; + } elseif ($obj instanceof MeprSubscription) { + $e->evt_id = $obj->rec->id; + $e->evt_id_type = self::$subscriptions_str; + } elseif ($obj instanceof MeprDrm) { + $e->evt_id = $obj->rec->id; + $e->evt_id_type = self::$drm_str; + } else { + return; + } - $q = $wpdb->prepare(" + $e->store(); + } + + /** + * Get the latest object for a given event + */ + public static function latest($event) + { + global $wpdb; + $mepr_db = new MeprDb(); + + $q = $wpdb->prepare(" SELECT id FROM {$mepr_db->events} WHERE event=%s @@ -201,88 +223,98 @@ public static function latest($event) { LIMIT 1 ", $event); - if(($id = $wpdb->get_var($q))) { - return new MeprEvent($id); - } + if (($id = $wpdb->get_var($q))) { + return new MeprEvent($id); + } - return false; - } + return false; + } - /** Get the tablename for the specific type of event */ - public static function get_tablename($event_type) { - global $wpdb; + /** + * Get the tablename for the specific type of event + */ + public static function get_tablename($event_type) + { + global $wpdb; + + $mepr_db = MeprDb::fetch(); + + if ($event_type == MeprEvent::$users_str) { + return $wpdb->users; + } elseif ($event_type == MeprEvent::$transactions_str) { + return $mepr_db->transactions; + } elseif ($event_type == MeprEvent::$subscriptions_str) { + return $mepr_db->subscriptions; + } + } - $mepr_db = MeprDb::fetch(); + /** + * Gets info from app/data/events.php if it exists. + * + * @return associative array if found or false if not found + */ + private function event_info() + { + $event_data = require(MEPR_DATA_PATH . '/events.php'); + + if (isset($event_data[$this->event])) { + return $event_data[$this->event]; + } - if ( $event_type == MeprEvent::$users_str ) { - return $wpdb->users; - } - else if ( $event_type == MeprEvent::$transactions_str ) { - return $mepr_db->transactions; - } - else if ( $event_type == MeprEvent::$subscriptions_str ) { - return $mepr_db->subscriptions; + return false; } - } - /** Gets info from app/data/events.php if it exists. - * - * @return associative array if found or false if not found - */ - private function event_info() { - $event_data = require(MEPR_DATA_PATH . '/events.php'); - - if(isset($event_data[$this->event])) { - return $event_data[$this->event]; + /** + * Uses app/data/events.php to determine if the current event is + * unique -- if true, only one row can be stored for a given event, + * evt_id & evt_id_type. + * + * @return true/false + */ + private function is_unique() + { + $event_info = $this->event_info(); + return (false !== $event_info && isset($event_info->unique) && $event_info->unique); } - return false; - } - - /** Uses app/data/events.php to determine if the current event is - * unique -- if true, only one row can be stored for a given event, - * evt_id & evt_id_type. - * - * @return true/false - */ - private function is_unique() { - $event_info = $this->event_info(); - return (false !== $event_info && isset($event_info->unique) && $event_info->unique); - } - - /** Copy an existing event id & args if the event is unique and another - * event record with the same event, evt_id & evt_id_type already exists. - * - * @return void - */ - private function use_existing_if_unique() { - if($this->is_unique()) { - $existing_event = self::get_one_by_event_and_evt_id_and_evt_id_type($this->event, $this->evt_id, $this->evt_id_type); - if(!empty($existing_event)) { - $this->id = $existing_event->id; - $this->args = $existing_event->args; - } + /** + * Copy an existing event id & args if the event is unique and another + * event record with the same event, evt_id & evt_id_type already exists. + * + * @return void + */ + private function use_existing_if_unique() + { + if ($this->is_unique()) { + $existing_event = self::get_one_by_event_and_evt_id_and_evt_id_type($this->event, $this->evt_id, $this->evt_id_type); + if (!empty($existing_event)) { + $this->id = $existing_event->id; + $this->args = $existing_event->args; + } + } } - } - /** Get the latest object for a given event and elapsed days */ - public static function latest_by_elapsed_days( $event, $elapsed_days ) { - global $wpdb; - $mepr_db = new MeprDb(); + /** + * Get the latest object for a given event and elapsed days + */ + public static function latest_by_elapsed_days($event, $elapsed_days) + { + global $wpdb; + $mepr_db = new MeprDb(); - $q = $wpdb->prepare(" + $q = $wpdb->prepare(" SELECT id FROM {$mepr_db->events} WHERE event=%s AND created_at >= '%s' - interval %d day ORDER BY id DESC LIMIT 1 - ", $event, MeprUtils::db_now(), $elapsed_days ); + ", $event, MeprUtils::db_now(), $elapsed_days); - if(($id = $wpdb->get_var($q))) { - return new MeprEvent($id); - } + if (($id = $wpdb->get_var($q))) { + return new MeprEvent($id); + } - return false; - } + return false; + } } //End class diff --git a/app/models/MeprGroup.php b/app/models/MeprGroup.php index 2e787cc..1f0ddf1 100644 --- a/app/models/MeprGroup.php +++ b/app/models/MeprGroup.php @@ -1,140 +1,152 @@ default_style_options = array( - 'layout' => 'mepr-vertical', - 'style' => 'mepr-gray', - 'button_size' => 'mepr-medium', - 'bullet_style' => 'mepr-circles', - 'font_style' => 'custom', - 'font_size' => 'custom', - 'button_color' => 'mepr-button-gray' - ); - - $this->load_cpt( - $obj, - self::$cpt, - array( - 'pricing_page_disabled' => false, - 'disable_change_plan_popup' => false, - 'is_upgrade_path' => false, - 'upgrade_path_reset_period' => false, - 'group_theme' => 'minimal_gray_horizontal.css', - 'fallback_membership' => '', - 'page_button_class' => '', - 'page_button_highlighted_class' => '', - 'page_button_disabled_class' => '', - 'alternate_group_url' => '', - 'group_page_style_options' => $this->default_style_options, - 'use_custom_template' => false, - 'custom_template' => '' - ) - ); - - // Ensure defaults get folded in - $this->group_page_style_options = array_merge( - $this->default_style_options, - $this->group_page_style_options - ); - } - - public function validate() { - $this->validate_is_bool($this->pricing_page_disabled, 'pricing_page_disabled'); - $this->validate_is_bool($this->disable_change_plan_popup, 'disable_change_plan_popup'); - - $this->validate_is_bool($this->is_upgrade_path, 'is_upgrade_path'); - $this->validate_is_bool($this->upgrade_path_reset_period, 'upgrade_path_reset_period'); - - $this->validate_is_in_array( - $this->group_theme, - self::group_themes(false,true), - 'group_theme' - ); - - if(!empty($this->alternate_group_url)) { $this->validate_is_url($this->alternate_group_url); } - - $this->validate_is_array($this->default_style_options); - - $this->validate_is_bool($this->use_custom_template, 'use_custom_template'); - - if($this->use_custom_template) { $this->validate_not_empty($this->custom_template); } - - //No need to validate these at this point - //'page_button_class' => '', - //'page_button_highlighted_class' => '', - //'page_button_disabled_class' => '', - } - - public function store_meta() { - $id = $this->ID; - - update_post_meta($id, self::$pricing_page_disabled_str, $this->pricing_page_disabled); - update_post_meta($id, self::$disable_change_plan_popup_str, $this->disable_change_plan_popup); - update_post_meta($id, self::$is_upgrade_path_str, $this->is_upgrade_path); - update_post_meta($id, self::$upgrade_path_reset_period_str, $this->upgrade_path_reset_period); - update_post_meta($id, self::$group_theme_str, $this->group_theme); - update_post_meta($id, self::$fallback_membership_str, $this->fallback_membership); - update_post_meta($id, self::$page_button_class_str, $this->page_button_class); - update_post_meta($id, self::$page_button_highlighted_class_str, $this->page_button_highlighted_class); - update_post_meta($id, self::$page_button_disabled_class_str, $this->page_button_disabled_class); - update_post_meta($id, self::$group_page_style_options_str, $this->group_page_style_options); - update_post_meta($id, self::$alternate_group_url_str, $this->alternate_group_url); - update_post_meta($id, self::$use_custom_template_str, $this->use_custom_template); - update_post_meta($id, self::$custom_template_str, $this->custom_template); - - if($this->is_upgrade_path) { - $products = $this->products(); - - foreach ($products as $product) { - if ((bool)$product->simultaneous_subscriptions) { - $product->simultaneous_subscriptions = false; - $product->save(); + +if (!defined('ABSPATH')) { + die('You are not allowed to call this page directly.'); +} + +class MeprGroup extends MeprCptModel +{ + public static $pricing_page_disabled_str = '_mepr_group_pricing_page_disabled'; + public static $disable_change_plan_popup_str = '_mepr_group_disable_change_plan_popup'; + public static $is_upgrade_path_str = '_mepr_group_is_upgrade_path'; + public static $upgrade_path_reset_period_str = '_mepr_group_upgrade_path_reset_period'; + public static $group_theme_str = '_mepr_group_theme'; + public static $page_button_class_str = '_mepr_page_button_class'; + public static $page_button_highlighted_class_str = '_mepr_page_button_highlighted_class'; + public static $page_button_disabled_class_str = '_mepr_page_button_disabled_class'; + public static $products_str = '_mepr_products'; + public static $group_page_style_options_str = '_mepr_group_page_style_options'; + public static $group_page_layout_str = 'mepr-group-page-layout'; + public static $group_page_style_str = 'mepr-group-page-style'; + public static $group_page_button_size_str = 'mepr-group-page-button-size'; + public static $group_page_bullet_style_str = 'mepr-group-page-bullet-style'; + public static $group_page_font_style_str = 'mepr-group-page-font-style'; + public static $group_page_font_size_str = 'mepr-group-page-font-size'; + public static $group_page_button_color_str = 'mepr-group-page-button-color'; + public static $alternate_group_url_str = '_mepr-alternate-group-url'; + public static $use_custom_template_str = '_mepr_use_custom_template'; + public static $custom_template_str = '_mepr_custom_template'; + public static $fallback_membership_str = '_mepr_fallback_membership'; + + public static $nonce_str = 'mepr_groups_nonce'; + public static $last_run_str = 'mepr_groups_db_cleanup_last_run'; + + public static $cpt = 'memberpressgroup'; + + public $default_style_options; + + public function __construct($obj = null) + { + $this->default_style_options = [ + 'layout' => 'mepr-vertical', + 'style' => 'mepr-gray', + 'button_size' => 'mepr-medium', + 'bullet_style' => 'mepr-circles', + 'font_style' => 'custom', + 'font_size' => 'custom', + 'button_color' => 'mepr-button-gray', + ]; + + $this->load_cpt( + $obj, + self::$cpt, + [ + 'pricing_page_disabled' => false, + 'disable_change_plan_popup' => false, + 'is_upgrade_path' => false, + 'upgrade_path_reset_period' => false, + 'group_theme' => 'minimal_gray_horizontal.css', + 'fallback_membership' => '', + 'page_button_class' => '', + 'page_button_highlighted_class' => '', + 'page_button_disabled_class' => '', + 'alternate_group_url' => '', + 'group_page_style_options' => $this->default_style_options, + 'use_custom_template' => false, + 'custom_template' => '', + ] + ); + + // Ensure defaults get folded in + $this->group_page_style_options = array_merge( + $this->default_style_options, + $this->group_page_style_options + ); + } + + public function validate() + { + $this->validate_is_bool($this->pricing_page_disabled, 'pricing_page_disabled'); + $this->validate_is_bool($this->disable_change_plan_popup, 'disable_change_plan_popup'); + + $this->validate_is_bool($this->is_upgrade_path, 'is_upgrade_path'); + $this->validate_is_bool($this->upgrade_path_reset_period, 'upgrade_path_reset_period'); + + $this->validate_is_in_array( + $this->group_theme, + self::group_themes(false, true), + 'group_theme' + ); + + if (!empty($this->alternate_group_url)) { + $this->validate_is_url($this->alternate_group_url); } - } + + $this->validate_is_array($this->default_style_options); + + $this->validate_is_bool($this->use_custom_template, 'use_custom_template'); + + if ($this->use_custom_template) { + $this->validate_not_empty($this->custom_template); + } + + // No need to validate these at this point + // 'page_button_class' => '', + // 'page_button_highlighted_class' => '', + // 'page_button_disabled_class' => '', } - } - //$return_type should be a string containing 'objects', 'ids', or 'titles' + public function store_meta() + { + $id = $this->ID; + + update_post_meta($id, self::$pricing_page_disabled_str, $this->pricing_page_disabled); + update_post_meta($id, self::$disable_change_plan_popup_str, $this->disable_change_plan_popup); + update_post_meta($id, self::$is_upgrade_path_str, $this->is_upgrade_path); + update_post_meta($id, self::$upgrade_path_reset_period_str, $this->upgrade_path_reset_period); + update_post_meta($id, self::$group_theme_str, $this->group_theme); + update_post_meta($id, self::$fallback_membership_str, $this->fallback_membership); + update_post_meta($id, self::$page_button_class_str, $this->page_button_class); + update_post_meta($id, self::$page_button_highlighted_class_str, $this->page_button_highlighted_class); + update_post_meta($id, self::$page_button_disabled_class_str, $this->page_button_disabled_class); + update_post_meta($id, self::$group_page_style_options_str, $this->group_page_style_options); + update_post_meta($id, self::$alternate_group_url_str, $this->alternate_group_url); + update_post_meta($id, self::$use_custom_template_str, $this->use_custom_template); + update_post_meta($id, self::$custom_template_str, $this->custom_template); + + if ($this->is_upgrade_path) { + $products = $this->products(); + + foreach ($products as $product) { + if ((bool)$product->simultaneous_subscriptions) { + $product->simultaneous_subscriptions = false; + $product->save(); + } + } + } + } - /** - * @param string $return_type - * @return MeprProduct[] - */ - public function products($return_type = 'objects') { - global $wpdb; + // $return_type should be a string containing 'objects', 'ids', or 'titles' - $query = " + /** + * @param string $return_type + * @return MeprProduct[] + */ + public function products($return_type = 'objects') + { + global $wpdb; + + $query = " SELECT ID FROM {$wpdb->posts} AS p JOIN {$wpdb->postmeta} AS pm_group_id ON p.ID = pm_group_id.post_id @@ -147,230 +159,249 @@ public function products($return_type = 'objects') { ORDER BY pm_group_order.meta_value * 1 "; // * 1 = easy way to cast strings as numbers in SQL - $query = $wpdb->prepare($query, MeprProduct::$group_id_str, $this->ID, MeprProduct::$group_order_str, 'publish'); + $query = $wpdb->prepare($query, MeprProduct::$group_id_str, $this->ID, MeprProduct::$group_order_str, 'publish'); - $res = $wpdb->get_col($query); + $res = $wpdb->get_col($query); - $products = array(); + $products = []; - if(is_array($res)) { - foreach($res as $product_id) { - $prd = new MeprProduct($product_id); + if (is_array($res)) { + foreach ($res as $product_id) { + $prd = new MeprProduct($product_id); - if($return_type == 'objects') { - $products[] = $prd; - } - elseif($return_type == 'ids') { - $products[] = $prd->ID; - } - elseif($return_type == 'titles') { - $products[] = $prd->post_title; + if ($return_type == 'objects') { + $products[] = $prd; + } elseif ($return_type == 'ids') { + $products[] = $prd->ID; + } elseif ($return_type == 'titles') { + $products[] = $prd->post_title; + } + } } - } - } - - return $products; - } - /** - * Returns products that can be bought - * @return array MeprProduct[] - */ - public function buyable_products() { - global $wpdb; - $products = array_filter($this->products(), function ($p){ - return $p->can_you_buy_me(); - }); + return $products; + } - return (array) $products; - } + /** + * Returns products that can be bought + * + * @return array MeprProduct[] + */ + public function buyable_products() + { + global $wpdb; + $products = array_filter($this->products(), function ($p) { + return $p->can_you_buy_me(); + }); + + return (array) $products; + } - // Returns the product associated through fallback group - public function fallback_membership() { - global $wpdb; + // Returns the product associated through fallback group + public function fallback_membership() + { + global $wpdb; - $query = $wpdb->prepare(" + $query = $wpdb->prepare( + " SELECT meta_value FROM {$wpdb->postmeta} WHERE post_id = %d AND meta_key = %s ", - $this->ID, - $this::$fallback_membership_str - ); + $this->ID, + $this::$fallback_membership_str + ); - $result = $wpdb->get_var($query); + $result = $wpdb->get_var($query); - if($result) { - $product_id = (int)$result; - return new MeprProduct($product_id); - } - else { - return false; - } - } - //Gets the transaction related to a lifetime membership in a group - //For use during upgrades from lifetime to subscriptions - public function get_old_lifetime_txn($new_prd_id, $user_id) { - $txn_id = false; - $grp_prds = $this->products('ids'); - $usr_txns = MeprTransaction::get_all_by_user_id($user_id, '', '', true); - - //Try and find the old txn and make sure it's not one belonging - //to the membership the user just signed up for - foreach($usr_txns as $txn) { - if(in_array($txn->product_id, $grp_prds) && $txn->product_id != $new_prd_id) { - $txn_id = $txn->id; - } + if ($result) { + $product_id = (int)$result; + return new MeprProduct($product_id); + } else { + return false; + } } + // Gets the transaction related to a lifetime membership in a group + // For use during upgrades from lifetime to subscriptions + public function get_old_lifetime_txn($new_prd_id, $user_id) + { + $txn_id = false; + $grp_prds = $this->products('ids'); + $usr_txns = MeprTransaction::get_all_by_user_id($user_id, '', '', true); + + // Try and find the old txn and make sure it's not one belonging + // to the membership the user just signed up for + foreach ($usr_txns as $txn) { + if (in_array($txn->product_id, $grp_prds) && $txn->product_id != $new_prd_id) { + $txn_id = $txn->id; + } + } - if($txn_id) { - return new MeprTransaction($txn_id); - } - else { - return false; + if ($txn_id) { + return new MeprTransaction($txn_id); + } else { + return false; + } } - } - public static function cleanup_db() { - global $wpdb; - $date = time(); - $last_run = get_option(self::$last_run_str, 0); //Prevents all this code from executing on every page load + public static function cleanup_db() + { + global $wpdb; + $date = time(); + $last_run = get_option(self::$last_run_str, 0); // Prevents all this code from executing on every page load - if(($date - $last_run) > 86400) { //Runs at most once a day - update_option(self::$last_run_str, $date); - $sq1 = "SELECT ID + if (($date - $last_run) > 86400) { // Runs at most once a day + update_option(self::$last_run_str, $date); + $sq1 = "SELECT ID FROM {$wpdb->posts} - WHERE post_type = '".self::$cpt."' AND + WHERE post_type = '" . self::$cpt . "' AND post_status = 'auto-draft'"; - $sq1_res = $wpdb->get_col($sq1); - if(!empty($sq1_res)) { - $post_ids = implode(',', $sq1_res); - $q1 = "DELETE + $sq1_res = $wpdb->get_col($sq1); + if (!empty($sq1_res)) { + $post_ids = implode(',', $sq1_res); + $q1 = "DELETE FROM {$wpdb->postmeta} WHERE post_id IN ({$post_ids})"; - $q2 = "DELETE + $q2 = "DELETE FROM {$wpdb->posts} - WHERE post_type = '".self::$cpt."' AND + WHERE post_type = '" . self::$cpt . "' AND post_status = 'auto-draft'"; - $wpdb->query($q1); - $wpdb->query($q2); - } + $wpdb->query($q1); + $wpdb->query($q2); + } + } + } + + public function get_page_template() + { + if ($this->use_custom_template) { + return locate_template($this->custom_template); + } + + return null; } - } - public function get_page_template() { - if($this->use_custom_template) { - return locate_template($this->custom_template); + /* + public static function template_search_path() { + return array( + 'page_memberpressgroup.php', + 'single-memberpressgroup.php', + 'page.php', + 'custom_template.php', + 'index.php' + ); + } + */ + + public function manual_append_price_boxes() + { + return preg_match('~\[mepr-group-price-boxes~', $this->post_content); } - return null; - } - - /* - public static function template_search_path() { - return array( - 'page_memberpressgroup.php', - 'single-memberpressgroup.php', - 'page.php', - 'custom_template.php', - 'index.php' - ); - } - */ - - public function manual_append_price_boxes() { - return preg_match('~\[mepr-group-price-boxes~',$this->post_content); - } - - public static function is_group_page($post) { - if( is_object($post) && - ( ( property_exists($post, 'post_type' ) && - $post->post_type == MeprGroup::$cpt && + public static function is_group_page($post) + { + if ( + is_object($post) && + ( ( property_exists($post, 'post_type') && + $post->post_type == MeprGroup::$cpt && $grp = new MeprGroup($post->ID) ) || - ( preg_match( - '~\[mepr-group-price-boxes\s+group_id=[\"\\\'](\d+)[\"\\\']~', - $post->post_content, $m ) && + ( preg_match( + '~\[mepr-group-price-boxes\s+group_id=[\"\\\'](\d+)[\"\\\']~', + $post->post_content, + $m + ) && isset($m[1]) && - $grp = new MeprGroup($m[1]) ) ) ) { - return $grp; + $grp = new MeprGroup($m[1]) ) ) + ) { + return $grp; + } + + return false; } - return false; - } + public function group_template() + { + if ( + $this->group_theme != 'custom' && + false !== ($filename = self::find_group_theme($this->group_theme)) + ) { + $template_str = file_get_contents($filename); + preg_match('~MP PLAN TEMPLATE:\s+(\S+)~', $template_str, $m); + + if (isset($m[1])) { + return $m[1]; + } + } - public function group_template() { - if($this->group_theme != 'custom' && - false !== ($filename = self::find_group_theme($this->group_theme))) { - $template_str = file_get_contents($filename); - preg_match('~MP PLAN TEMPLATE:\s+(\S+)~', $template_str, $m); + return ''; + } - if(isset($m[1])) { return $m[1]; } + public static function group_theme_templates_paths() + { + return MeprHooks::apply_filters('mepr_group_theme_templates_paths', [MEPR_CSS_PATH . '/plan_templates']); } - return ''; - } + public static function group_theme_templates($full_paths = false) + { + $paths = self::group_theme_templates_paths(); - public static function group_theme_templates_paths() { - return MeprHooks::apply_filters('mepr_group_theme_templates_paths', array(MEPR_CSS_PATH . '/plan_templates')); - } + $templates = []; + foreach ($paths as $path) { + $templates = array_merge($templates, @glob("{$path}/*.css")); + } - public static function group_theme_templates($full_paths=false) { - $paths = self::group_theme_templates_paths(); + if (!$full_paths) { + // TODO: This could cause issues down the line because we're counting on the theme + // base name being unique across all search paths for the group theme files. + foreach ($templates as $i => $template) { + $templates[$i] = basename($template); + } + } - $templates = array(); - foreach($paths as $path) { - $templates = array_merge($templates, @glob("{$path}/*.css")); + return $templates; } - if(!$full_paths) { - // TODO: This could cause issues down the line because we're counting on the theme - // base name being unique across all search paths for the group theme files. - foreach($templates as $i => $template) { - $templates[$i] = basename($template); - } + public static function group_themes_paths() + { + return MeprHooks::apply_filters('mepr_group_themes_paths', [MEPR_CSS_PATH . '/plans']); } - return $templates; - } - - public static function group_themes_paths() { - return MeprHooks::apply_filters('mepr_group_themes_paths', array(MEPR_CSS_PATH . '/plans')); - } - - public static function find_group_theme($theme) { - $paths = self::group_themes_paths(); - foreach($paths as $path) { - $filepath = $path . '/' . $theme; - if(file_exists($filepath)) { - return $filepath; - } + public static function find_group_theme($theme) + { + $paths = self::group_themes_paths(); + foreach ($paths as $path) { + $filepath = $path . '/' . $theme; + if (file_exists($filepath)) { + return $filepath; + } + } + return false; } - return false; - } - public static function group_themes($full_paths=false,$include_custom=false) { - $paths = self::group_themes_paths(); + public static function group_themes($full_paths = false, $include_custom = false) + { + $paths = self::group_themes_paths(); - $themes = array(); - foreach($paths as $path) { - $themes = array_merge($themes, @glob("{$path}/*.css")); - } + $themes = []; + foreach ($paths as $path) { + $themes = array_merge($themes, @glob("{$path}/*.css")); + } - if(!$full_paths) { - // TODO: This could cause issues down the line because we're counting on the theme - // base name being unique across all search paths for the group theme files. - foreach($themes as $i => $theme) { - $themes[$i] = basename($theme); - } - } + if (!$full_paths) { + // TODO: This could cause issues down the line because we're counting on the theme + // base name being unique across all search paths for the group theme files. + foreach ($themes as $i => $theme) { + $themes[$i] = basename($theme); + } + } - if($include_custom) { - $themes[] = 'custom'; - } + if ($include_custom) { + $themes[] = 'custom'; + } - return $themes; - } + return $themes; + } } //End class diff --git a/app/models/MeprOptions.php b/app/models/MeprOptions.php index 42ddd54..1917961 100644 --- a/app/models/MeprOptions.php +++ b/app/models/MeprOptions.php @@ -1,1338 +1,1527 @@ set_strings(); - $this->set_from_array($options); - $this->set_defaults(); - $this->set_dynamic_attrs(); - $this->wpml_custom_fields(); //Need to store as an array for WPML - this converts back to objects :) - } - - /** - * Fetch the MeprOptions instance - * - * @param bool $force Force a fresh instance - * @return self - */ - public static function fetch($force = false) { - static $mepr_options; - - if(!isset($mepr_options) or $force) { - $mepr_options_array = get_option(MEPR_OPTIONS_SLUG); - - if(!$mepr_options_array) - $mepr_options = new MeprOptions(); // Just grab the defaults - else if(is_object($mepr_options_array) and is_a($mepr_options_array, 'MeprOptions')) { - $mepr_options = $mepr_options_array; - $mepr_options->set_defaults(); - $mepr_options->store(false); // store will convert this back into an array - } - else if(!is_array($mepr_options_array)) - $mepr_options = new MeprOptions(); // Just grab the defaults - else - $mepr_options = new MeprOptions($mepr_options_array); // Sets defaults for unset options +class MeprOptions +{ + public $dynamic_attrs; + + public function __construct($options = []) + { + $this->set_strings(); + $this->set_from_array($options); + $this->set_defaults(); + $this->set_dynamic_attrs(); + $this->wpml_custom_fields(); // Need to store as an array for WPML - this converts back to objects :) + } + + /** + * Fetch the MeprOptions instance + * + * @param boolean $force Force a fresh instance + * @return self + */ + public static function fetch($force = false) + { + static $mepr_options; + + if (!isset($mepr_options) or $force) { + $mepr_options_array = get_option(MEPR_OPTIONS_SLUG); + + if (!$mepr_options_array) { + $mepr_options = new MeprOptions(); // Just grab the defaults + } elseif (is_object($mepr_options_array) and is_a($mepr_options_array, 'MeprOptions')) { + $mepr_options = $mepr_options_array; + $mepr_options->set_defaults(); + $mepr_options->store(false); // store will convert this back into an array + } elseif (!is_array($mepr_options_array)) { + $mepr_options = new MeprOptions(); // Just grab the defaults + } else { + $mepr_options = new MeprOptions($mepr_options_array); // Sets defaults for unset options + } + } + + $mepr_options->set_strings(); // keep strings fresh (not db cached) + $mepr_options->wpml_custom_fields(); // Need to store as an array for WPML - this converts back to objects :) + return MeprHooks::apply_filters('mepr_fetch_options', $mepr_options); } - $mepr_options->set_strings(); //keep strings fresh (not db cached) - $mepr_options->wpml_custom_fields(); //Need to store as an array for WPML - this converts back to objects :) - return MeprHooks::apply_filters('mepr_fetch_options', $mepr_options); - } + public function wpml_custom_fields() + { + if (!empty($this->custom_fields)) { + $new_fields = []; + foreach ($this->custom_fields as $row) { + $row = (object)$row; // Convert rows back to objects + + $new_options = []; + if (!empty($row->options)) { + foreach ($row->options as $option) { + $new_options[] = (object)$option; // Convert options back into objects + } - public function wpml_custom_fields() { - if(!empty($this->custom_fields)) { - $new_fields = array(); - foreach($this->custom_fields as $row) { - $row = (object)$row; //Convert rows back to objects + $row->options = $new_options; + } - $new_options = array(); - if(!empty($row->options)) { - foreach($row->options as $option) { - $new_options[] = (object)$option; //Convert options back into objects - } + $new_fields[] = $row; + } - $row->options = $new_options; + $this->custom_fields = $new_fields; } - $new_fields[] = $row; - } + return $this->custom_fields; + } - $this->custom_fields = $new_fields; + public static function reset() + { + delete_option(MEPR_OPTIONS_SLUG); } - return $this->custom_fields; - } + // This is used to allow permalinks to be retrieved + // Early on in the game + public function populate_rewrite() + { + if (empty($GLOBALS['wp_rewrite'])) { + $GLOBALS['wp_rewrite'] = new WP_Rewrite(); + } + } - public static function reset() { - delete_option(MEPR_OPTIONS_SLUG); - } + public function set_dynamic_attrs() + { + // TODO: We will want to migrate everything to using these dynamic type variables as we continue + $json = file_get_contents(MEPR_DATA_PATH . '/options/dynamic_attrs.json'); + $attrs = json_decode($json, true); + $this->dynamic_attrs = MeprHooks::apply_filters('mepr-options-dynamic-attrs', $attrs); + } - // This is used to allow permalinks to be retrieved - // Early on in the game - public function populate_rewrite() { - if(empty($GLOBALS['wp_rewrite'])) - $GLOBALS['wp_rewrite'] = new WP_Rewrite(); - } + public function set_defaults() + { + $mepr_blogname = MeprUtils::blogname(); - public function set_dynamic_attrs() { - // TODO: We will want to migrate everything to using these dynamic type variables as we continue - $json = file_get_contents(MEPR_DATA_PATH.'/options/dynamic_attrs.json'); - $attrs = json_decode($json,true); - $this->dynamic_attrs = MeprHooks::apply_filters('mepr-options-dynamic-attrs', $attrs); - } + if (!isset($this->legacy_integrations)) { + $this->legacy_integrations = []; + } - public function set_defaults() { - $mepr_blogname = MeprUtils::blogname(); + if (!isset($this->account_page_id)) { + $this->account_page_id = 0; + } - if(!isset($this->legacy_integrations)) { - $this->legacy_integrations = []; - } + if (!isset($this->login_page_id)) { + $this->login_page_id = 0; + } - if(!isset($this->account_page_id)) - $this->account_page_id = 0; + if (!isset($this->thankyou_page_id)) { + $this->thankyou_page_id = 0; + } - if(!isset($this->login_page_id)) - $this->login_page_id = 0; + if (!isset($this->coaching_page_id)) { + $this->coaching_page_id = 0; + } - if(!isset($this->thankyou_page_id)) - $this->thankyou_page_id = 0; + if (!isset($this->force_login_page_url)) { // Forces wp's login_url filter to be overridden with MP login page permalink + $this->force_login_page_url = false; + } - if(!isset($this->coaching_page_id)) - $this->coaching_page_id = 0; + if (!isset($this->login_redirect_url)) { + $this->populate_rewrite(); + $this->login_redirect_url = $this->account_page_url(); + } - if(!isset($this->force_login_page_url)) //Forces wp's login_url filter to be overridden with MP login page permalink - $this->force_login_page_url = false; + if (!isset($this->logout_redirect_url)) { + $this->logout_redirect_url = ''; + } - if(!isset($this->login_redirect_url)) { - $this->populate_rewrite(); - $this->login_redirect_url = $this->account_page_url(); - } + if (!isset($this->disable_mod_rewrite)) { + $this->disable_mod_rewrite = true; + } - if(!isset($this->logout_redirect_url)) - $this->logout_redirect_url = ''; + if (!isset($this->anti_card_testing_enabled)) { + $this->anti_card_testing_enabled = true; + } - if(!isset($this->disable_mod_rewrite)) - $this->disable_mod_rewrite = true; + if (!isset($this->anti_card_testing_ip_method)) { + $this->anti_card_testing_ip_method = ''; + } - if(!isset($this->anti_card_testing_enabled)) { - $this->anti_card_testing_enabled = true; - } + if (!isset($this->anti_card_testing_blocked)) { + $this->anti_card_testing_blocked = []; + } - if(!isset($this->anti_card_testing_ip_method)) { - $this->anti_card_testing_ip_method = ''; - } + if (!isset($this->emails)) { + $this->emails = []; - if(!isset($this->anti_card_testing_blocked)) { - $this->anti_card_testing_blocked = array(); - } + // This is all for Backwards compatibility + $this->emails['MeprAdminSignupEmail'] = []; + $this->emails['MeprAdminReceiptEmail'] = []; + $this->emails['MeprUserWelcomeEmail'] = []; + $this->emails['MeprUserReceiptEmail'] = []; - if(!isset($this->emails)) { - $this->emails = array(); + if (isset($this->admin_email)) { + $this->emails['MeprAdminSignupEmail']['enabled'] = $this->admin_email; + } + if (isset($this->admin_email_subject)) { + $this->emails['MeprAdminSignupEmail']['subject'] = $this->admin_email_subject; + } + if (isset($this->admin_email_body)) { + $this->emails['MeprAdminSignupEmail']['body'] = MeprOptionsHelper::format_plaintext_email($this->admin_email_body); + } - // This is all for Backwards compatibility - $this->emails['MeprAdminSignupEmail'] = array(); - $this->emails['MeprAdminReceiptEmail'] = array(); - $this->emails['MeprUserWelcomeEmail'] = array(); - $this->emails['MeprUserReceiptEmail'] = array(); + if (isset($this->admin_user_receipt_email)) { + $this->emails['MeprAdminReceiptEmail']['enabled'] = $this->admin_user_receipt_email; + } - if(isset($this->admin_email)) { $this->emails['MeprAdminSignupEmail']['enabled'] = $this->admin_email; } - if(isset($this->admin_email_subject)) { $this->emails['MeprAdminSignupEmail']['subject'] = $this->admin_email_subject; } - if(isset($this->admin_email_body)) { $this->emails['MeprAdminSignupEmail']['body'] = MeprOptionsHelper::format_plaintext_email($this->admin_email_body); } + if (isset($this->admin_user_receipt_email_subject)) { + $this->emails['MeprAdminReceiptEmail']['subject'] = $this->admin_user_receipt_email_subject; + } - if(isset($this->admin_user_receipt_email)) { - $this->emails['MeprAdminReceiptEmail']['enabled'] = $this->admin_user_receipt_email; - } + if (isset($this->admin_user_receipt_email_body)) { + $this->emails['MeprAdminReceiptEmail']['body'] = MeprOptionsHelper::format_plaintext_email($this->admin_user_receipt_email_body); + } - if(isset($this->admin_user_receipt_email_subject)) { - $this->emails['MeprAdminReceiptEmail']['subject'] = $this->admin_user_receipt_email_subject; - } + if (isset($this->user_email)) { + $this->emails['MeprUserWelcomeEmail']['enabled'] = $this->user_email; + } + if (isset($this->user_email_subject)) { + $this->emails['MeprUserWelcomeEmail']['subject'] = $this->user_email_subject; + } + if (isset($this->user_email_body)) { + $this->emails['MeprUserWelcomeEmail']['body'] = MeprOptionsHelper::format_plaintext_email($this->user_email_body); + } - if(isset($this->admin_user_receipt_email_body)) { - $this->emails['MeprAdminReceiptEmail']['body'] = MeprOptionsHelper::format_plaintext_email($this->admin_user_receipt_email_body); - } + if (isset($this->user_receipt_email)) { + $this->emails['MeprUserReceiptEmail']['enabled'] = $this->user_receipt_email; + } + if (isset($this->user_receipt_email_subject)) { + $this->emails['MeprUserReceiptEmail']['subject'] = $this->user_receipt_email_subject; + } + if (isset($this->user_receipt_email_body)) { + $this->emails['MeprUserReceiptEmail']['body'] = MeprOptionsHelper::format_plaintext_email($this->user_receipt_email_body); + } + } - if(isset($this->user_email)) { $this->emails['MeprUserWelcomeEmail']['enabled'] = $this->user_email; } - if(isset($this->user_email_subject)) { $this->emails['MeprUserWelcomeEmail']['subject'] = $this->user_email_subject; } - if(isset($this->user_email_body)) { $this->emails['MeprUserWelcomeEmail']['body'] = MeprOptionsHelper::format_plaintext_email($this->user_email_body); } + foreach (MeprEmailFactory::all('MeprBaseOptionsEmail') as $email) { + $classname = get_class($email); + if (!isset($this->emails[$classname])) { + $this->emails[$classname] = $email->defaults; + } + } - if(isset($this->user_receipt_email)) { $this->emails['MeprUserReceiptEmail']['enabled'] = $this->user_receipt_email; } - if(isset($this->user_receipt_email_subject)) { $this->emails['MeprUserReceiptEmail']['subject'] = $this->user_receipt_email_subject; } - if(isset($this->user_receipt_email_body)) { $this->emails['MeprUserReceiptEmail']['body'] = MeprOptionsHelper::format_plaintext_email($this->user_receipt_email_body); } - } + // Account CSS Settings + if (!isset($this->account_css_width)) { + $this->account_css_width = 500; + } - foreach( MeprEmailFactory::all('MeprBaseOptionsEmail') as $email ) { - $classname = get_class($email); - if(!isset($this->emails[$classname])) { - $this->emails[$classname] = $email->defaults; - } - } + if (!isset($this->custom_message)) { + $this->custom_message = sprintf(__('Welcome to %s', 'memberpress'), $mepr_blogname); + } - //Account CSS Settings - if(!isset($this->account_css_width)) - $this->account_css_width = 500; + if ( + $this->thankyou_page_id == 0 or + $this->login_page_id == 0 + ) { + $this->setup_complete = 0; + } else { + $this->setup_complete = 1; + } - if(!isset($this->custom_message)) - $this->custom_message = sprintf(__('Welcome to %s', 'memberpress'), $mepr_blogname); + if (!isset($this->activated_timestamp)) { + $this->activated_timestamp = time(); + } - if( $this->thankyou_page_id == 0 or - $this->login_page_id == 0 ) - $this->setup_complete = 0; - else - $this->setup_complete = 1; + if (!isset($this->currency_code)) { + $this->currency_code = 'USD'; + } - if( !isset($this->activated_timestamp) ) { - $this->activated_timestamp = time(); - } + if (!isset($this->currency_symbol)) { + $this->currency_symbol = '$'; + } - if(!isset($this->currency_code)) - $this->currency_code = 'USD'; + if (!isset($this->currency_symbol_after)) { + $this->currency_symbol_after = false; + } - if(!isset($this->currency_symbol)) - $this->currency_symbol = '$'; + if (!isset($this->language_code)) { + $this->language_code = 'US'; + } - if(!isset($this->currency_symbol_after)) - $this->currency_symbol_after = false; + if (!isset($this->integrations)) { + $this->integrations = []; + } - if(!isset($this->language_code)) - $this->language_code = 'US'; + if (!isset($this->lock_wp_admin)) { + $this->lock_wp_admin = true; + } - if(!isset($this->integrations)) - $this->integrations = array(); + if (!isset($this->enforce_strong_password)) { + $this->enforce_strong_password = 0; // 0 (off), weak, medium, strong + } - if(!isset($this->lock_wp_admin)) - $this->lock_wp_admin = true; + if (!isset($this->disable_wp_registration_form)) { + $this->disable_wp_registration_form = true; + } - if(!isset($this->enforce_strong_password)) - $this->enforce_strong_password = 0; //0 (off), weak, medium, strong + if (!isset($this->disable_wp_admin_bar)) { + $this->disable_wp_admin_bar = true; + } - if(!isset($this->disable_wp_registration_form)) - $this->disable_wp_registration_form = true; + if (!isset($this->pro_rated_upgrades)) { + $this->pro_rated_upgrades = true; + } - if(!isset($this->disable_wp_admin_bar)) - $this->disable_wp_admin_bar = true; + if (!isset($this->disable_checkout_password_fields)) { + $this->disable_checkout_password_fields = false; + } - if(!isset($this->pro_rated_upgrades)) - $this->pro_rated_upgrades = true; + if (!isset($this->enable_spc)) { + $this->enable_spc = false; + } - if(!isset($this->disable_checkout_password_fields)) - $this->disable_checkout_password_fields = false; + if (!isset($this->enable_spc_invoice)) { + $this->enable_spc_invoice = false; + } - if(!isset($this->enable_spc)) - $this->enable_spc = false; + if (!isset($this->coupon_field_enabled)) { + $this->coupon_field_enabled = true; + } - if(!isset($this->enable_spc_invoice)) - $this->enable_spc_invoice = false; + if (!isset($this->require_tos)) { + $this->require_tos = false; + } - if(!isset($this->coupon_field_enabled)) - $this->coupon_field_enabled = true; + if (!isset($this->require_privacy_policy)) { + $this->require_privacy_policy = false; + } - if(!isset($this->require_tos)) - $this->require_tos = false; + if (!isset($this->tos_url)) { + $this->tos_url = ''; + } - if(!isset($this->require_privacy_policy)) - $this->require_privacy_policy = false; + if (!isset($this->tos_title)) { + $this->tos_title = _x('I have read and agree to the Terms Of Service', 'ui', 'memberpress'); // This string is also below, so if we change this wording, we need to change it below also + } - if(!isset($this->tos_url)) - $this->tos_url = ''; + if (!isset($this->privacy_policy_title)) { + $this->privacy_policy_title = _x('This site collects names, emails and other user information. I consent to the terms set forth in the %Privacy Policy%.', 'ui', 'memberpress'); + } - if(!isset($this->tos_title)) - $this->tos_title = _x('I have read and agree to the Terms Of Service', 'ui', 'memberpress'); //This string is also below, so if we change this wording, we need to change it below also + if (!isset($this->mail_send_from_name)) { + $this->mail_send_from_name = MeprUtils::blogname(); + } - if(!isset($this->privacy_policy_title)) - $this->privacy_policy_title = _x('This site collects names, emails and other user information. I consent to the terms set forth in the %Privacy Policy%.', 'ui', 'memberpress'); + if (!isset($this->mail_send_from_email)) { + $this->mail_send_from_email = get_option('admin_email'); + } - if(!isset($this->mail_send_from_name)) - $this->mail_send_from_name = MeprUtils::blogname(); + if (!isset($this->username_is_email)) { + $this->username_is_email = false; + } - if(!isset($this->mail_send_from_email)) - $this->mail_send_from_email = get_option('admin_email'); + if (!isset($this->show_fname_lname)) { + $this->show_fname_lname = true; + } - if(!isset($this->username_is_email)) - $this->username_is_email = false; + if (!isset($this->require_fname_lname)) { + $this->require_fname_lname = false; + } - if(!isset($this->show_fname_lname)) - $this->show_fname_lname = true; + if (!isset($this->show_address_fields)) { + $this->show_address_fields = false; + } - if(!isset($this->require_fname_lname)) - $this->require_fname_lname = false; + if (!isset($this->require_address_fields)) { + $this->require_address_fields = true; + } - if(!isset($this->show_address_fields)) - $this->show_address_fields = false; + if (!isset($this->show_fields_logged_in_purchases)) { + $this->show_fields_logged_in_purchases = true; // Changing this to true as we get way too many emails about this in support + } - if(!isset($this->require_address_fields)) - $this->require_address_fields = true; + $this->set_address_fields(); - if(!isset($this->show_fields_logged_in_purchases)) - $this->show_fields_logged_in_purchases = true; //Changing this to true as we get way too many emails about this in support + if (!isset($this->custom_fields)) { // should be an array of objects but we store as just arrays in the DB for WPML compatibility - see $this->wpml_custom_fields() + $this->custom_fields = []; + } - $this->set_address_fields(); + if (!isset($this->mothership_license)) { + $this->mothership_license = ''; + } - if(!isset($this->custom_fields)) //should be an array of objects but we store as just arrays in the DB for WPML compatibility - see $this->wpml_custom_fields() - $this->custom_fields = array(); + if (!isset($this->edge_updates)) { + $this->edge_updates = false; + } - if(!isset($this->mothership_license)) - $this->mothership_license = ''; + if (!isset($this->auto_updates)) { + $this->auto_updates = false; + } - if(!isset($this->edge_updates)) - $this->edge_updates = false; + if (!isset($this->product_pages_slug)) { + $this->product_pages_slug = 'register'; + } - if(!isset($this->auto_updates)) - $this->auto_updates = false; + if (!isset($this->group_pages_slug)) { + $this->group_pages_slug = 'plans'; + } - if(!isset($this->product_pages_slug)) - $this->product_pages_slug = 'register'; + if (!isset($this->admin_email_addresses)) { + $this->admin_email_addresses = get_option('admin_email'); // default to admin_email + } - if(!isset($this->group_pages_slug)) - $this->group_pages_slug = 'plans'; + if (!isset($this->unauthorized_message)) { + $this->unauthorized_message = MeprHooks::apply_filters('mepr-unauthorized-message', __('You are unauthorized to view this page.', 'memberpress')); + } - if(!isset($this->admin_email_addresses)) - $this->admin_email_addresses = get_option('admin_email'); // default to admin_email + // For backwards compatibility + if (!isset($this->redirect_on_unauthorized)) { + // For backwards compatibility + if ( + isset($this->unauthorized_page_id) and + is_numeric($this->unauthorized_page_id) and + (int)$this->unauthorized_page_id > 0 + ) { + $this->redirect_on_unauthorized = true; + $this->populate_rewrite(); + $this->unauthorized_redirect_url = MeprUtils::get_permalink($this->unauthorized_page_id); + } else { + $this->redirect_on_unauthorized = false; + } + } - if(!isset($this->unauthorized_message)) - $this->unauthorized_message = MeprHooks::apply_filters( 'mepr-unauthorized-message', __( 'You are unauthorized to view this page.', 'memberpress' ) ); + if (!isset($this->unauthorized_redirect_url)) { + $this->populate_rewrite(); + $this->unauthorized_redirect_url = $this->login_page_url(); + } - // For backwards compatibility - if(!isset($this->redirect_on_unauthorized)) { - // For backwards compatibility - if( isset($this->unauthorized_page_id) and - is_numeric($this->unauthorized_page_id) and - (int)$this->unauthorized_page_id > 0 ) { - $this->redirect_on_unauthorized = true; - $this->populate_rewrite(); - $this->unauthorized_redirect_url = MeprUtils::get_permalink($this->unauthorized_page_id); - } - else - $this->redirect_on_unauthorized = false; - } + if (!isset($this->redirect_non_singular)) { + $this->redirect_non_singular = false; + } - if(!isset($this->unauthorized_redirect_url)) { - $this->populate_rewrite(); - $this->unauthorized_redirect_url = $this->login_page_url(); - } + if (!isset($this->redirect_method)) { + $this->redirect_method = 'template_redirect'; + } - if(!isset($this->redirect_non_singular)) - $this->redirect_non_singular = false; + if (!isset($this->unauth_show_excerpts)) { + $this->unauth_show_excerpts = false; + } - if(!isset($this->redirect_method)) { - $this->redirect_method = 'template_redirect'; - } + if (!isset($this->unauth_excerpt_type)) { + $this->unauth_excerpt_type = 'excerpt'; + } - if(!isset($this->unauth_show_excerpts)) - $this->unauth_show_excerpts = false; + if (!isset($this->unauth_excerpt_size)) { + $this->unauth_excerpt_size = 100; + } - if(!isset($this->unauth_excerpt_type)) - $this->unauth_excerpt_type = 'excerpt'; + if (!isset($this->unauth_show_login)) { + $this->unauth_show_login = true; + } - if(!isset($this->unauth_excerpt_size)) - $this->unauth_excerpt_size = 100; + if (!isset($this->authorize_seo_views)) { + $this->authorize_seo_views = false; + } - if(!isset($this->unauth_show_login)) - $this->unauth_show_login = true; + if (!isset($this->seo_unauthorized_noindex)) { + $this->seo_unauthorized_noindex = false; + } - if(!isset($this->authorize_seo_views)) - $this->authorize_seo_views = false; + if (!isset($this->paywall_enabled)) { + $this->paywall_enabled = false; + } - if(!isset($this->seo_unauthorized_noindex)) - $this->seo_unauthorized_noindex = false; + if (!isset($this->paywall_num_free_views)) { + $this->paywall_num_free_views = 1; + } - if(!isset($this->paywall_enabled)) - $this->paywall_enabled = false; + if (!isset($this->disable_summary_email)) { + $this->disable_summary_email = false; + } - if(!isset($this->paywall_num_free_views)) - $this->paywall_num_free_views = 1; + // Option to disable the grace days (Account tab of MP Options now) + if (!isset($this->disable_grace_init_days)) { + $this->disable_grace_init_days = false; + } - if(!isset($this->disable_summary_email)) - $this->disable_summary_email = false; + // How many days will the users get free access before their first + // payment trial days in the membership will override this value + if (isset($this->disable_grace_init_days) && $this->disable_grace_init_days) { + $this->grace_init_days = MeprHooks::apply_filters('mepr-grace-init-days', 0); + } else { + $this->grace_init_days = MeprHooks::apply_filters('mepr-grace-init-days', 1); + } - //Option to disable the grace days (Account tab of MP Options now) - if(!isset($this->disable_grace_init_days)) - $this->disable_grace_init_days = false; + // Do we want some overlap in expirations? + $this->grace_expire_days = MeprHooks::apply_filters('mepr-grace-expire-days', 0); - // How many days will the users get free access before their first - // payment trial days in the membership will override this value - if(isset($this->disable_grace_init_days) && $this->disable_grace_init_days) - $this->grace_init_days = MeprHooks::apply_filters('mepr-grace-init-days', 0); - else - $this->grace_init_days = MeprHooks::apply_filters('mepr-grace-init-days', 1); + if (!isset($this->allow_cancel_subs)) { + $this->allow_cancel_subs = true; + } - // Do we want some overlap in expirations? - $this->grace_expire_days = MeprHooks::apply_filters('mepr-grace-expire-days', 0); + if (!isset($this->allow_suspend_subs)) { + $this->allow_suspend_subs = false; + } - if(!isset($this->allow_cancel_subs)) - $this->allow_cancel_subs = true; + if (!isset($this->disable_global_autoresponder_list)) { + $this->disable_global_autoresponder_list = false; + } - if(!isset($this->allow_suspend_subs)) - $this->allow_suspend_subs = false; + if (!isset($this->opt_in_checked_by_default)) { + $this->opt_in_checked_by_default = false; + } - if(!isset($this->disable_global_autoresponder_list)) - $this->disable_global_autoresponder_list = false; + if (!isset($this->global_styles)) { + $this->global_styles = false; + } - if(!isset($this->opt_in_checked_by_default)) - $this->opt_in_checked_by_default = false; + if (!isset($this->include_email_privacy_link)) { + $this->include_email_privacy_link = false; + } - if(!isset($this->global_styles)) { - $this->global_styles = false; - } + if (!isset($this->sslverify)) { + $this->sslverify = true; + } - if(!isset($this->include_email_privacy_link)) - $this->include_email_privacy_link = false; + if (!isset($this->design_logo_img)) { + $this->design_logo_img = ''; + } - if(!isset($this->sslverify)) { - $this->sslverify = true; - } + if (!isset($this->design_primary_color)) { + $this->design_primary_color = '#06429E'; + } - if(!isset( $this->design_logo_img ) ){ - $this->design_logo_img = ''; - } + if (!isset($this->design_enable_checkout_template)) { + $this->design_enable_checkout_template = false; + } - if(!isset( $this->design_primary_color ) ){ - $this->design_primary_color = '#06429E'; - } + if (!isset($this->design_enable_login_template)) { + $this->design_enable_login_template = false; + } - if(!isset( $this->design_enable_checkout_template ) ){ - $this->design_enable_checkout_template = false; - } + if (!isset($this->design_show_login_welcome_image)) { + $this->design_show_login_welcome_image = false; + } - if(!isset( $this->design_enable_login_template ) ){ - $this->design_enable_login_template = false; - } + if (!isset($this->design_login_welcome_img)) { + $this->design_login_welcome_img = ''; + } - if(!isset( $this->design_show_login_welcome_image ) ){ - $this->design_show_login_welcome_image = false; - } + if (!isset($this->design_enable_account_template)) { + $this->design_enable_account_template = false; + } - if(!isset( $this->design_login_welcome_img ) ){ - $this->design_login_welcome_img = ''; - } + if (!isset($this->design_show_account_welcome_image)) { + $this->design_show_account_welcome_image = false; + } - if(!isset( $this->design_enable_account_template ) ){ - $this->design_enable_account_template = false; - } + if (!isset($this->design_account_welcome_img)) { + $this->design_account_welcome_img = ''; + } - if(!isset( $this->design_show_account_welcome_image ) ){ - $this->design_show_account_welcome_image = false; - } + if (!isset($this->design_enable_courses_template)) { + $this->design_enable_courses_template = ''; + } - if(!isset( $this->design_account_welcome_img ) ){ - $this->design_account_welcome_img = ''; - } + if (!isset($this->design_enable_thankyou_template)) { + $this->design_enable_thankyou_template = false; + } - if(!isset( $this->design_enable_courses_template ) ){ - $this->design_enable_courses_template = ''; - } + if (!isset($this->design_show_thankyou_welcome_image)) { + $this->design_show_thankyou_welcome_image = false; + } - if(!isset( $this->design_enable_thankyou_template ) ){ - $this->design_enable_thankyou_template = false; - } + if (!isset($this->design_thankyou_hide_invoice)) { + $this->design_thankyou_hide_invoice = ''; + } - if(!isset( $this->design_show_thankyou_welcome_image) ){ - $this->design_show_thankyou_welcome_image = false; - } + if (!isset($this->design_thankyou_invoice_message)) { + $this->design_thankyou_invoice_message = ''; + } - if(!isset( $this->design_thankyou_hide_invoice ) ){ - $this->design_thankyou_hide_invoice = ''; - } + if (!isset($this->design_thankyou_welcome_img)) { + $this->design_thankyou_welcome_img = ''; + } - if(!isset( $this->design_thankyou_invoice_message ) ){ - $this->design_thankyou_invoice_message = ''; - } + if (!isset($this->design_enable_pricing_template)) { + $this->design_enable_pricing_template = false; + } - if(!isset( $this->design_thankyou_welcome_img ) ){ - $this->design_thankyou_welcome_img = ''; - } + if (!isset($this->design_pricing_title)) { + $this->design_pricing_title = ''; + } - if(!isset( $this->design_enable_pricing_template ) ){ - $this->design_enable_pricing_template = false; - } + if (!isset($this->design_pricing_cta_color)) { + $this->design_pricing_cta_color = ''; + } - if(!isset( $this->design_pricing_title ) ){ - $this->design_pricing_title = ''; - } + if (!isset($this->design_pricing_subheadline)) { + $this->design_pricing_subheadline = ''; + } - if(!isset( $this->design_pricing_cta_color ) ){ - $this->design_pricing_cta_color = ''; - } + if (!isset($this->rl_enable_coaching_template)) { + $this->rl_enable_coaching_template = true; + } - if(!isset( $this->design_pricing_subheadline ) ){ - $this->design_pricing_subheadline = ''; + if (!isset($this->rl_enable_wp_footer)) { + $this->rl_enable_wp_footer = 'enabled'; + } } - if(!isset( $this->rl_enable_coaching_template ) ){ - $this->rl_enable_coaching_template = true; - } + public function set_strings() + { + $this->account_page_id_str = 'mepr-account-page-id'; + $this->login_page_id_str = 'mepr-login-page-id'; + $this->thankyou_page_id_str = 'mepr-thankyou-page-id'; + $this->coaching_page_id_str = 'mepr-coaching-page-id'; + $this->force_login_page_url_str = 'mepr-force-login-page-url'; + $this->login_redirect_url_str = 'mepr-login-redirect-url'; + $this->logout_redirect_url_str = 'mepr-logout-redirect-url'; + $this->account_css_width_str = 'mepr-account-css-width'; + $this->disable_mod_rewrite_str = 'mepr-disable-mod-rewrite'; + $this->anti_card_testing_enabled_str = 'mepr-anti-card-testing-enabled'; + $this->anti_card_testing_ip_method_str = 'mepr-anti-card-testing-ip-method'; + $this->anti_card_testing_blocked_str = 'mepr-anti-card-testing-blocked'; + $this->admin_email_str = 'mepr-admin-email'; + $this->admin_email_subject_str = 'mepr-admin-email-subject'; + $this->admin_email_body_str = 'mepr-admin-email-body'; + $this->admin_user_receipt_email_str = 'mepr-admin-user-receipt-email'; + $this->admin_user_receipt_email_subject_str = 'mepr-admin-user-receipt-email-subject'; + $this->admin_user_receipt_email_body_str = 'mepr-admin-user-receipt-email-body'; + $this->admin_expirations_sent_email_str = 'mepr-admin-expirations-sent-email'; + $this->admin_expirations_sent_email_subject_str = 'mepr-admin-expirations-sent-email-subject'; + $this->admin_expirations_sent_email_body_str = 'mepr-admin-expirations-sent-email-body'; + $this->user_email_str = 'mepr-user-email'; + $this->user_email_subject_str = 'mepr-user-email-subject'; + $this->user_email_body_str = 'mepr-user-email-body'; + $this->user_receipt_email_str = 'mepr-receipt-user-email'; + $this->user_receipt_email_subject_str = 'mepr-receipt-user-email-subject'; + $this->user_receipt_email_body_str = 'mepr-receipt-user-email-body'; + $this->new_drip_available_str = 'mepr-new-drip-available'; + $this->new_drip_available_subject_str = 'mepr-new-drip-available-subject'; + $this->new_drip_available_body_str = 'mepr-new-drip-available-body'; + $this->custom_message_str = 'mepr-custom-message'; + + $this->currency_code_str = 'mepr-currency-code'; + $this->currency_symbol_str = 'mepr-currency-symbol'; + $this->currency_symbol_after_str = 'mepr-currency-symbol-after'; + $this->language_code_str = 'mepr-language-symbol'; + $this->integrations_str = 'mepr-integrations'; + + $this->lock_wp_admin_str = 'mepr-lock-wp-admin'; + $this->enforce_strong_password_str = 'mepr-enforce-strong-password'; + $this->disable_wp_registration_form_str = 'mepr-disable-wp-registration-form'; + $this->disable_wp_admin_bar_str = 'mepr-disable-wp-admin-bar'; + $this->pro_rated_upgrades_str = 'mepr-pro-rated-upgrades'; + $this->disable_checkout_password_fields_str = 'mepr-disable-checkout-password-fields'; + $this->enable_spc_str = 'mepr-enable-spc'; + $this->enable_spc_invoice_str = 'mepr-enable-spc-invoice'; + $this->coupon_field_enabled_str = 'mepr-coupon-field-enabled'; + $this->require_tos_str = 'mepr-require-tos'; + $this->tos_url_str = 'mepr-tos-url'; + $this->tos_title_str = 'mepr-tos-title'; + $this->mail_send_from_name_str = 'mepr-mail-send-from-name'; + $this->mail_send_from_email_str = 'mepr-mail-send-from-email'; + $this->username_is_email_str = 'mepr-username-is-email'; + $this->require_fname_lname_str = 'mepr-require-fname-lname'; + $this->show_fname_lname_str = 'mepr-show-fname-lname'; + $this->show_address_fields_str = 'mepr-show-address-fields'; + $this->require_address_fields_str = 'mepr-require-address-fields'; + $this->show_fields_logged_in_purchases_str = 'mepr-show-fields-logged-in-purchases'; + $this->custom_fields_str = 'mepr-custom-fields'; + $this->mothership_license_str = 'mepr-mothership-license'; + $this->edge_updates_str = 'mepr-edge-updates'; + $this->auto_updates_str = 'mepr-auto-updates'; + $this->product_pages_slug_str = 'mepr-product-pages-slug'; + $this->group_pages_slug_str = 'mepr-group-pages-slug'; + $this->admin_email_addresses_str = 'mepr-admin-email-addresses'; + $this->unauthorized_message_str = 'mepr-unauthorized-message'; + $this->unauth_show_excerpts_str = 'mepr-unauth-show-excerpts'; + $this->unauth_excerpt_size_str = 'mepr-unauth-excerpt-size'; + $this->unauth_excerpt_type_str = 'mepr-unauth-excerpt-type'; + $this->unauth_show_login_str = 'mepr-unauth-show-login'; + $this->disable_grace_init_days_str = 'mepr-disable-grace-init-days'; + $this->authorize_seo_views_str = 'mepr-authorize-seo-views'; + $this->paywall_enabled_str = 'mepr-paywall-enabled'; + $this->paywall_num_free_views_str = 'mepr-paywall-num-free-views'; + $this->disable_summary_email_str = 'mepr-disable-summary-email'; + $this->seo_unauthorized_noindex_str = 'mepr-seo-unauthorized-noindex'; + $this->emails_str = 'mepr-emails'; + $this->redirect_on_unauthorized_str = 'mepr-redirect-on-unauthorized'; + $this->unauthorized_redirect_url_str = 'mepr-unauthorized-redirect-url'; + $this->redirect_non_singular_str = 'mepr-redirect-non-singular'; + $this->redirect_method_str = 'mepr-redirect-method'; + $this->allow_cancel_subs_str = 'mepr-allow-cancel-subs'; + $this->allow_suspend_subs_str = 'mepr-allow-suspend-subs'; + $this->disable_global_autoresponder_list_str = 'mepr-disable-global-autoresponder-list'; + $this->opt_in_checked_by_default_str = 'mepr-opt-in-checked-by-default'; + $this->global_styles_str = 'mepr-global-styles'; + $this->include_email_privacy_link_str = 'mepr-email-privacy-link'; + $this->require_privacy_policy_str = 'mepr-require-privacy-policy'; + $this->privacy_policy_title_str = 'mepr-privacy-policy-title'; + + $this->design_logo_img_str = 'mepr-design-logo-img'; + $this->design_primary_color_str = 'mepr-design-primary-color'; + $this->design_enable_login_template_str = 'mepr-design-enable-login-template'; + $this->design_show_login_welcome_image_str = 'mepr-design-show-login-welcome-image'; + $this->design_login_welcome_img_str = 'mepr-design-login-welcome-img'; + $this->design_enable_checkout_template_str = 'mepr-design-enable-checkout-template'; + $this->design_enable_account_template_str = 'mepr-design-enable-account-template'; + $this->design_show_account_welcome_image_str = 'mepr-design-show-account-welcome-image'; + $this->design_account_welcome_img_str = 'mepr-design-account-welcome-img'; + $this->design_enable_courses_template_str = 'mepr-design-enable-courses-template'; + $this->design_enable_thankyou_template_str = 'mepr-design-enable-thankyou-template'; + $this->design_show_thankyou_welcome_image_str = 'mepr-design-show-thankyou-welcome-image'; + $this->design_thankyou_welcome_img_str = 'mepr-design-thankyou-welcome-img'; + $this->design_thankyou_hide_invoice_str = 'mepr-design-thankyou-hide-invoice'; + $this->design_thankyou_invoice_message_str = 'mepr-design-thankyou-message'; + $this->design_enable_pricing_template_str = 'mepr-design-enable-pricing-template'; + $this->design_pricing_title_str = 'mepr-design-pricing-title'; + $this->design_pricing_cta_color_str = 'mepr-design-pricing-cta-color'; + $this->design_pricing_subheadline_str = 'mepr-design-pricing-subheadline'; + $this->rl_enable_coaching_template_str = 'mepr-rl-enable-coaching-template'; + $this->design_show_checkout_price_terms_str = 'mepr-design-show-checkout-price-terms'; + $this->rl_enable_wp_footer_str = 'mepr-rl-enable-wp-footer'; + } + + public function validate($params, $errors = []) + { + // Validate all of the integrations ... + if (!empty($params[$this->integrations_str]) and is_array($params[$this->integrations_str])) { + foreach ($params[$this->integrations_str] as $pmt) { + $obj = MeprGatewayFactory::fetch($pmt['gateway'], $pmt); + $errors = $obj->validate_options_form($errors); + } + } - if(!isset( $this->rl_enable_wp_footer) ) { - $this->rl_enable_wp_footer = 'enabled'; - } - } - - public function set_strings() { - $this->account_page_id_str = 'mepr-account-page-id'; - $this->login_page_id_str = 'mepr-login-page-id'; - $this->thankyou_page_id_str = 'mepr-thankyou-page-id'; - $this->coaching_page_id_str = 'mepr-coaching-page-id'; - $this->force_login_page_url_str = 'mepr-force-login-page-url'; - $this->login_redirect_url_str = 'mepr-login-redirect-url'; - $this->logout_redirect_url_str = 'mepr-logout-redirect-url'; - $this->account_css_width_str = 'mepr-account-css-width'; - $this->disable_mod_rewrite_str = 'mepr-disable-mod-rewrite'; - $this->anti_card_testing_enabled_str = 'mepr-anti-card-testing-enabled'; - $this->anti_card_testing_ip_method_str = 'mepr-anti-card-testing-ip-method'; - $this->anti_card_testing_blocked_str = 'mepr-anti-card-testing-blocked'; - $this->admin_email_str = 'mepr-admin-email'; - $this->admin_email_subject_str = 'mepr-admin-email-subject'; - $this->admin_email_body_str = 'mepr-admin-email-body'; - $this->admin_user_receipt_email_str = 'mepr-admin-user-receipt-email'; - $this->admin_user_receipt_email_subject_str = 'mepr-admin-user-receipt-email-subject'; - $this->admin_user_receipt_email_body_str = 'mepr-admin-user-receipt-email-body'; - $this->admin_expirations_sent_email_str = 'mepr-admin-expirations-sent-email'; - $this->admin_expirations_sent_email_subject_str = 'mepr-admin-expirations-sent-email-subject'; - $this->admin_expirations_sent_email_body_str = 'mepr-admin-expirations-sent-email-body'; - $this->user_email_str = 'mepr-user-email'; - $this->user_email_subject_str = 'mepr-user-email-subject'; - $this->user_email_body_str = 'mepr-user-email-body'; - $this->user_receipt_email_str = 'mepr-receipt-user-email'; - $this->user_receipt_email_subject_str = 'mepr-receipt-user-email-subject'; - $this->user_receipt_email_body_str = 'mepr-receipt-user-email-body'; - $this->new_drip_available_str = 'mepr-new-drip-available'; - $this->new_drip_available_subject_str = 'mepr-new-drip-available-subject'; - $this->new_drip_available_body_str = 'mepr-new-drip-available-body'; - $this->custom_message_str = 'mepr-custom-message'; - - $this->currency_code_str = 'mepr-currency-code'; - $this->currency_symbol_str = 'mepr-currency-symbol'; - $this->currency_symbol_after_str = 'mepr-currency-symbol-after'; - $this->language_code_str = 'mepr-language-symbol'; - $this->integrations_str = 'mepr-integrations'; - - $this->lock_wp_admin_str = 'mepr-lock-wp-admin'; - $this->enforce_strong_password_str = 'mepr-enforce-strong-password'; - $this->disable_wp_registration_form_str = 'mepr-disable-wp-registration-form'; - $this->disable_wp_admin_bar_str = 'mepr-disable-wp-admin-bar'; - $this->pro_rated_upgrades_str = 'mepr-pro-rated-upgrades'; - $this->disable_checkout_password_fields_str = 'mepr-disable-checkout-password-fields'; - $this->enable_spc_str = 'mepr-enable-spc'; - $this->enable_spc_invoice_str = 'mepr-enable-spc-invoice'; - $this->coupon_field_enabled_str = 'mepr-coupon-field-enabled'; - $this->require_tos_str = 'mepr-require-tos'; - $this->tos_url_str = 'mepr-tos-url'; - $this->tos_title_str = 'mepr-tos-title'; - $this->mail_send_from_name_str = 'mepr-mail-send-from-name'; - $this->mail_send_from_email_str = 'mepr-mail-send-from-email'; - $this->username_is_email_str = 'mepr-username-is-email'; - $this->require_fname_lname_str = 'mepr-require-fname-lname'; - $this->show_fname_lname_str = 'mepr-show-fname-lname'; - $this->show_address_fields_str = 'mepr-show-address-fields'; - $this->require_address_fields_str = 'mepr-require-address-fields'; - $this->show_fields_logged_in_purchases_str = 'mepr-show-fields-logged-in-purchases'; - $this->custom_fields_str = 'mepr-custom-fields'; - $this->mothership_license_str = 'mepr-mothership-license'; - $this->edge_updates_str = 'mepr-edge-updates'; - $this->auto_updates_str = 'mepr-auto-updates'; - $this->product_pages_slug_str = 'mepr-product-pages-slug'; - $this->group_pages_slug_str = 'mepr-group-pages-slug'; - $this->admin_email_addresses_str = 'mepr-admin-email-addresses'; - $this->unauthorized_message_str = 'mepr-unauthorized-message'; - $this->unauth_show_excerpts_str = 'mepr-unauth-show-excerpts'; - $this->unauth_excerpt_size_str = 'mepr-unauth-excerpt-size'; - $this->unauth_excerpt_type_str = 'mepr-unauth-excerpt-type'; - $this->unauth_show_login_str = 'mepr-unauth-show-login'; - $this->disable_grace_init_days_str = 'mepr-disable-grace-init-days'; - $this->authorize_seo_views_str = 'mepr-authorize-seo-views'; - $this->paywall_enabled_str = 'mepr-paywall-enabled'; - $this->paywall_num_free_views_str = 'mepr-paywall-num-free-views'; - $this->disable_summary_email_str = 'mepr-disable-summary-email'; - $this->seo_unauthorized_noindex_str = 'mepr-seo-unauthorized-noindex'; - $this->emails_str = 'mepr-emails'; - $this->redirect_on_unauthorized_str = 'mepr-redirect-on-unauthorized'; - $this->unauthorized_redirect_url_str = 'mepr-unauthorized-redirect-url'; - $this->redirect_non_singular_str = 'mepr-redirect-non-singular'; - $this->redirect_method_str = 'mepr-redirect-method'; - $this->allow_cancel_subs_str = 'mepr-allow-cancel-subs'; - $this->allow_suspend_subs_str = 'mepr-allow-suspend-subs'; - $this->disable_global_autoresponder_list_str = 'mepr-disable-global-autoresponder-list'; - $this->opt_in_checked_by_default_str = 'mepr-opt-in-checked-by-default'; - $this->global_styles_str = 'mepr-global-styles'; - $this->include_email_privacy_link_str = 'mepr-email-privacy-link'; - $this->require_privacy_policy_str = 'mepr-require-privacy-policy'; - $this->privacy_policy_title_str = 'mepr-privacy-policy-title'; - - $this->design_logo_img_str = 'mepr-design-logo-img'; - $this->design_primary_color_str = 'mepr-design-primary-color'; - $this->design_enable_login_template_str = 'mepr-design-enable-login-template'; - $this->design_show_login_welcome_image_str = 'mepr-design-show-login-welcome-image'; - $this->design_login_welcome_img_str = 'mepr-design-login-welcome-img'; - $this->design_enable_checkout_template_str = 'mepr-design-enable-checkout-template'; - $this->design_enable_account_template_str = 'mepr-design-enable-account-template'; - $this->design_show_account_welcome_image_str = 'mepr-design-show-account-welcome-image'; - $this->design_account_welcome_img_str = 'mepr-design-account-welcome-img'; - $this->design_enable_courses_template_str = 'mepr-design-enable-courses-template'; - $this->design_enable_thankyou_template_str = 'mepr-design-enable-thankyou-template'; - $this->design_show_thankyou_welcome_image_str = 'mepr-design-show-thankyou-welcome-image'; - $this->design_thankyou_welcome_img_str = 'mepr-design-thankyou-welcome-img'; - $this->design_thankyou_hide_invoice_str = 'mepr-design-thankyou-hide-invoice'; - $this->design_thankyou_invoice_message_str = 'mepr-design-thankyou-message'; - $this->design_enable_pricing_template_str = 'mepr-design-enable-pricing-template'; - $this->design_pricing_title_str = 'mepr-design-pricing-title'; - $this->design_pricing_cta_color_str = 'mepr-design-pricing-cta-color'; - $this->design_pricing_subheadline_str = 'mepr-design-pricing-subheadline'; - $this->rl_enable_coaching_template_str = 'mepr-rl-enable-coaching-template'; - $this->design_show_checkout_price_terms_str = 'mepr-design-show-checkout-price-terms'; - $this->rl_enable_wp_footer_str = 'mepr-rl-enable-wp-footer'; - } - - public function validate($params, $errors = array()) { - // Validate all of the integrations ... - if(!empty($params[$this->integrations_str]) and is_array($params[$this->integrations_str])) { - foreach($params[$this->integrations_str] as $pmt) { - $obj = MeprGatewayFactory::fetch($pmt['gateway'], $pmt); - $errors = $obj->validate_options_form($errors); - } - } + if (!isset($params[$this->product_pages_slug_str])) { + $errors[] = __('The Membership Pages Slug must be set', 'memberpress'); + } - if(!isset($params[$this->product_pages_slug_str])) { - $errors[] = __('The Membership Pages Slug must be set', 'memberpress'); - } + if (!isset($params[$this->group_pages_slug_str])) { + $errors[] = __('The Group Pages Slug must be set', 'memberpress'); + } - if(!isset($params[$this->group_pages_slug_str])) { - $errors[] = __('The Group Pages Slug must be set', 'memberpress'); - } + if (!preg_match('#^[a-zA-Z0-9\-]+$#', $params[$this->product_pages_slug_str])) { + $errors[] = __('The Membership Pages Slug must only contain letters, numbers and dashes.', 'memberpress'); + } - if(!preg_match('#^[a-zA-Z0-9\-]+$#',$params[$this->product_pages_slug_str])) { - $errors[] = __('The Membership Pages Slug must only contain letters, numbers and dashes.', 'memberpress'); - } + if (!preg_match('#^[a-zA-Z0-9\-]+$#', $params[$this->group_pages_slug_str])) { + $errors[] = __('The Group Pages Slug must only contain letters, numbers and dashes.', 'memberpress'); + } - if(!preg_match('#^[a-zA-Z0-9\-]+$#',$params[$this->group_pages_slug_str])) { - $errors[] = __('The Group Pages Slug must only contain letters, numbers and dashes.', 'memberpress'); - } + if (!isset($params[$this->admin_email_addresses_str]) || empty($params[$this->admin_email_addresses_str])) { + $errors[] = __('At least one Admin Email Address must be set', 'memberpress'); + } - if(!isset($params[$this->admin_email_addresses_str]) || empty($params[$this->admin_email_addresses_str])) { - $errors[] = __('At least one Admin Email Address must be set', 'memberpress'); - } + // {2,20} should match most of the new TLDs like .academy for example {2,4} was causing failed validation on the new TLDs + if (!preg_match('#^\s*[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,20}(,\s*[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,20})*$#', $params[$this->admin_email_addresses_str])) { + $errors[] = __('The Admin Email Address field must contain 1 or more valid email addresses', 'memberpress'); + } - // {2,20} should match most of the new TLDs like .academy for example {2,4} was causing failed validation on the new TLDs - if(!preg_match('#^\s*[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,20}(,\s*[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,20})*$#', $params[$this->admin_email_addresses_str])) { - $errors[] = __('The Admin Email Address field must contain 1 or more valid email addresses', 'memberpress'); - } + $biz_address_is_set = ( + isset($params['mepr_biz_name']) && !empty($params['mepr_biz_name']) && + isset($params['mepr_biz_address1']) && !empty($params['mepr_biz_address1']) && + isset($params['mepr_biz_city']) && !empty($params['mepr_biz_city']) && + isset($params['mepr_biz_state']) && !empty($params['mepr_biz_state']) && + isset($params['mepr_biz_country']) && !empty($params['mepr_biz_country']) && + isset($params['mepr_biz_postcode']) && !empty($params['mepr_biz_postcode']) + ); - $biz_address_is_set = ( - isset($params['mepr_biz_name']) && !empty($params['mepr_biz_name']) && - isset($params['mepr_biz_address1']) && !empty($params['mepr_biz_address1']) && - isset($params['mepr_biz_city']) && !empty($params['mepr_biz_city']) && - isset($params['mepr_biz_state']) && !empty($params['mepr_biz_state']) && - isset($params['mepr_biz_country']) && !empty($params['mepr_biz_country']) && - isset($params['mepr_biz_postcode']) && !empty($params['mepr_biz_postcode']) - ); - - //Temporarily killing this validation - causing too many support reqs - // if(!$biz_address_is_set) { - // $errors[] = __('You must enter your full Merchant Business Address (found on the Info tab).', 'memberpress'); - // } - - if(isset($params['mepr_biz_state'])) { - $params['mepr_biz_state'] = strtoupper($params['mepr_biz_state']); - } + // Temporarily killing this validation - causing too many support reqs + // if(!$biz_address_is_set) { + // $errors[] = __('You must enter your full Merchant Business Address (found on the Info tab).', 'memberpress'); + // } + if (isset($params['mepr_biz_state'])) { + $params['mepr_biz_state'] = strtoupper($params['mepr_biz_state']); + } - // TODO: We need to figure this one out ... this is still problemmatic for many users - //if(isset($params['mepr_biz_country']) && isset($params['mepr_biz_state']) && - // ($states = MeprUtils::states()) && isset($states[$params['mepr_biz_country']]) && - // !isset($states[$params['mepr_biz_country']][$params['mepr_biz_state']])) { - // $errors[] = __('You must enter a valid state code with your Merchant Business Address (found on the Info tab).', 'memberpress'); - //} - - return $errors; - } - - public function update($params) { - if(isset($params['mepr_biz_state'])) { $params['mepr_biz_state'] = strtoupper($params['mepr_biz_state']); } - - // Set dynamic variables (we should migrate all attrs to use this soon) - $this->set_attrs_from_slugs($params); - - // Page Settings - if(!is_numeric($params[$this->account_page_id_str]) and - preg_match("#^__auto_page:(.*?)$#",$params[$this->account_page_id_str],$matches)) - $this->account_page_id = $_POST[$this->account_page_id_str] = MeprAppHelper::auto_add_page($matches[1]); - else - $this->account_page_id = (int)$params[$this->account_page_id_str]; - - if(!is_numeric($params[$this->login_page_id_str]) and - preg_match("#^__auto_page:(.*?)$#",$params[$this->login_page_id_str],$matches)) - $this->login_page_id = $_POST[$this->login_page_id_str] = MeprAppHelper::auto_add_page($matches[1]); - else - $this->login_page_id = (int)$params[$this->login_page_id_str]; - - if(!is_numeric($params[$this->thankyou_page_id_str]) and - preg_match("#^__auto_page:(.*?)$#",$params[$this->thankyou_page_id_str],$matches)) - $this->thankyou_page_id = $_POST[$this->thankyou_page_id_str] = MeprAppHelper::auto_add_page($matches[1], esc_html__('Your subscription has been set up successfully.', 'memberpress')); - else - $this->thankyou_page_id = (int)$params[$this->thankyou_page_id_str]; - - if(isset($params[$this->coaching_page_id_str])){ - if(!is_numeric($params[$this->coaching_page_id_str]) && - preg_match("#^__auto_page:(.*?)$#",$params[$this->coaching_page_id_str],$matches)): - $this->coaching_page_id = $_POST[$this->coaching_page_id_str] = MeprAppHelper::auto_add_page($matches[1]); - else: - $this->coaching_page_id = (int)$params[$this->coaching_page_id_str]; - endif; + // TODO: We need to figure this one out ... this is still problemmatic for many users + // if(isset($params['mepr_biz_country']) && isset($params['mepr_biz_state']) && + // ($states = MeprUtils::states()) && isset($states[$params['mepr_biz_country']]) && + // !isset($states[$params['mepr_biz_country']][$params['mepr_biz_state']])) { + // $errors[] = __('You must enter a valid state code with your Merchant Business Address (found on the Info tab).', 'memberpress'); + // } + return $errors; } - $this->force_login_page_url = isset($params[$this->force_login_page_url_str]); - - $this->login_redirect_url = (isset($params[$this->login_redirect_url_str]) && !empty($params[$this->login_redirect_url_str]))?trim(stripslashes($params[$this->login_redirect_url_str])):$this->account_page_url(); + public function update($params) + { + if (isset($params['mepr_biz_state'])) { + $params['mepr_biz_state'] = strtoupper($params['mepr_biz_state']); + } - $this->logout_redirect_url = (isset($params[$this->logout_redirect_url_str]) && !empty($params[$this->logout_redirect_url_str]))?trim(stripslashes($params[$this->logout_redirect_url_str])):''; + // Set dynamic variables (we should migrate all attrs to use this soon) + $this->set_attrs_from_slugs($params); + + // Page Settings + if ( + !is_numeric($params[$this->account_page_id_str]) and + preg_match('#^__auto_page:(.*?)$#', $params[$this->account_page_id_str], $matches) + ) { + $this->account_page_id = $_POST[$this->account_page_id_str] = MeprAppHelper::auto_add_page($matches[1]); + } else { + $this->account_page_id = (int)$params[$this->account_page_id_str]; + } - // Notification Settings - $this->emails = array(); + if ( + !is_numeric($params[$this->login_page_id_str]) and + preg_match('#^__auto_page:(.*?)$#', $params[$this->login_page_id_str], $matches) + ) { + $this->login_page_id = $_POST[$this->login_page_id_str] = MeprAppHelper::auto_add_page($matches[1]); + } else { + $this->login_page_id = (int)$params[$this->login_page_id_str]; + } - foreach($params[$this->emails_str] as $email => $vals) { - $this->emails[$email] = array('enabled' => isset($params[$this->emails_str][$email]['enabled']), - 'use_template' => isset($params[$this->emails_str][$email]['use_template']), - 'subject' => trim(stripslashes($params[$this->emails_str][$email]['subject'])), - 'body' => MeprUtils::maybe_wpautop(stripslashes($params[$this->emails_str][$email]['body']))); - } + if ( + !is_numeric($params[$this->thankyou_page_id_str]) and + preg_match('#^__auto_page:(.*?)$#', $params[$this->thankyou_page_id_str], $matches) + ) { + $this->thankyou_page_id = $_POST[$this->thankyou_page_id_str] = MeprAppHelper::auto_add_page($matches[1], esc_html__('Your subscription has been set up successfully.', 'memberpress')); + } else { + $this->thankyou_page_id = (int)$params[$this->thankyou_page_id_str]; + } - $this->disable_mod_rewrite = isset($params[$this->disable_mod_rewrite_str]); - $this->anti_card_testing_enabled = isset($params[$this->anti_card_testing_enabled_str]); - $this->anti_card_testing_ip_method = isset($params[$this->anti_card_testing_ip_method_str]) ? sanitize_text_field(wp_unslash($params[$this->anti_card_testing_ip_method_str])) : ''; - $this->anti_card_testing_blocked = isset($params[$this->anti_card_testing_blocked_str]) && is_string($params[$this->anti_card_testing_blocked_str]) ? array_unique(array_filter(array_map('trim', explode("\n", $params[$this->anti_card_testing_blocked_str])))) : array(); - - $this->custom_message = wp_kses_post(stripslashes($params[$this->custom_message_str])); - $currency_code = stripslashes($params[$this->currency_code_str]); - $currency_code_changed = $this->currency_code != $currency_code; - $this->currency_code = $currency_code; - $this->currency_symbol = stripslashes($params[$this->currency_symbol_str]); - $this->currency_symbol_after = isset($params[$this->currency_symbol_after_str]); - $this->language_code = stripslashes($params[$this->language_code_str]); - - if(isset($params[$this->integrations_str])) { - foreach($params[$this->integrations_str] as $intg_key => $intg) { - $params[$this->integrations_str][$intg_key]['use_icon'] = isset($params[$this->integrations_str][$intg_key]['use_icon']); - $params[$this->integrations_str][$intg_key]['use_label'] = isset($params[$this->integrations_str][$intg_key]['use_label']); - $params[$this->integrations_str][$intg_key]['use_desc'] = isset($params[$this->integrations_str][$intg_key]['use_desc']); - - if(isset($intg['gateway']) && $intg['gateway'] == 'MeprStripeGateway') { - if($currency_code_changed || empty($params[$this->integrations_str][$intg_key]['payment_methods'])) { - // Set the Stripe payment methods to be an empty array if the currency code has changed - // or no payment methods were selected. - $params[$this->integrations_str][$intg_key]['payment_methods'] = []; - } + if (isset($params[$this->coaching_page_id_str])) { + if ( + !is_numeric($params[$this->coaching_page_id_str]) && + preg_match('#^__auto_page:(.*?)$#', $params[$this->coaching_page_id_str], $matches) + ) : + $this->coaching_page_id = $_POST[$this->coaching_page_id_str] = MeprAppHelper::auto_add_page($matches[1]); + else : + $this->coaching_page_id = (int)$params[$this->coaching_page_id_str]; + endif; } - } - } - $this->integrations = (isset($params[$this->integrations_str]))?$params[$this->integrations_str]:array(); - $this->lock_wp_admin = isset($params[$this->lock_wp_admin_str]); - $this->enforce_strong_password = $params[$this->enforce_strong_password_str]; - $this->disable_wp_registration_form = isset($params[$this->disable_wp_registration_form_str]); - $this->disable_wp_admin_bar = isset($params[$this->disable_wp_admin_bar_str]); - $this->pro_rated_upgrades = isset($params[$this->pro_rated_upgrades_str]); - $this->disable_checkout_password_fields = isset($params[$this->disable_checkout_password_fields_str]); - $this->enable_spc = isset($params[$this->enable_spc_str]); - $this->enable_spc_invoice = isset($params[$this->enable_spc_invoice_str]); - $this->coupon_field_enabled = isset($params[$this->coupon_field_enabled_str]); - $this->require_tos = isset($params[$this->require_tos_str]); - $this->tos_url = (isset($params[$this->tos_url_str]))?trim(stripslashes($params[$this->tos_url_str])):''; - $this->tos_title = (isset($params[$this->tos_title_str]) && !empty($params[$this->tos_title_str]))?trim(stripslashes($params[$this->tos_title_str])):_x('I have read and agree to the Terms of Service', 'ui', 'memberpress'); - $this->require_privacy_policy = isset($params[$this->require_privacy_policy_str]); - $this->privacy_policy_title = (isset($params[$this->privacy_policy_title_str]) && !empty($params[$this->privacy_policy_title_str]))?trim(stripslashes($params[$this->privacy_policy_title_str])):_x('This site collects names, emails and other user information. I consent to the terms set forth in the %Privacy Policy%.', 'ui', 'memberpress'); - $this->mail_send_from_name = (isset($params[$this->mail_send_from_name_str]))?trim(stripslashes($params[$this->mail_send_from_name_str])):MeprUtils::blogname(); - $this->mail_send_from_email = (isset($params[$this->mail_send_from_email_str]))?trim(stripslashes($params[$this->mail_send_from_email_str])):get_option('admin_email'); - $this->username_is_email = isset($params[$this->username_is_email_str]); - $this->require_fname_lname = isset($params[$this->require_fname_lname_str]); - $this->show_fname_lname = isset($params[$this->show_fname_lname_str]); - - // This happens on the activate screen't do this here - //$this->mothership_license = stripslashes($params[$this->mothership_license_str]); - $this->product_pages_slug = sanitize_title(stripslashes($params[$this->product_pages_slug_str]), 'register'); - $this->group_pages_slug = sanitize_title(stripslashes($params[$this->group_pages_slug_str]), 'plans'); - $this->admin_email_addresses = $params[$this->admin_email_addresses_str]; - $this->unauthorized_message = wp_kses_post(stripslashes($params[$this->unauthorized_message_str])); - $this->unauth_show_excerpts = isset($params[$this->unauth_show_excerpts_str]); - $this->unauth_excerpt_type = $params[$this->unauth_excerpt_type_str]; - $this->unauth_excerpt_size = $params[$this->unauth_excerpt_size_str]; - $this->unauth_show_login = isset($params[$this->unauth_show_login_str]); - $this->disable_grace_init_days = isset($params[$this->disable_grace_init_days_str]); - $this->authorize_seo_views = isset($params[$this->authorize_seo_views_str]); - $this->seo_unauthorized_noindex = isset($params[$this->seo_unauthorized_noindex_str]); - $this->paywall_enabled = isset($params[$this->paywall_enabled_str]); - $this->paywall_num_free_views = (isset($params[$this->paywall_num_free_views_str]))?(int)$params[$this->paywall_num_free_views_str]:1; - $this->disable_summary_email = isset($params[$this->disable_summary_email_str]); - $this->redirect_on_unauthorized = isset($params[$this->redirect_on_unauthorized_str]); - $this->unauthorized_redirect_url = trim(stripslashes($params[$this->unauthorized_redirect_url_str])); - $this->redirect_non_singular = isset($params[$this->redirect_non_singular_str]); - $this->redirect_method = $params[$this->redirect_method_str]; - $this->allow_cancel_subs = isset($params[$this->allow_cancel_subs_str]); - $this->allow_suspend_subs = isset($params[$this->allow_suspend_subs_str]); - $this->disable_global_autoresponder_list = isset($params[$this->disable_global_autoresponder_list_str]); - $this->opt_in_checked_by_default = isset($params[$this->opt_in_checked_by_default_str]); - $this->global_styles = isset($params[$this->global_styles_str]); - $this->include_email_privacy_link = isset($params[$this->include_email_privacy_link_str]); - - //Signup fields stuff - $this->username_is_email = isset($params[$this->username_is_email_str]); - $this->require_fname_lname = isset($params[$this->require_fname_lname_str]); - $this->show_fname_lname = (isset($params[$this->show_fname_lname_str]) || isset($params[$this->require_fname_lname_str])); - $this->show_fields_logged_in_purchases = isset($params[$this->show_fields_logged_in_purchases_str]); - - // Always show and require address fields when tax calculations are enabled - $this->show_address_fields = (isset($params[$this->show_address_fields_str]) || isset($params[$this->require_address_fields_str]) || isset($params['mepr_calculate_taxes'])); - $this->require_address_fields = (isset($params[$this->require_address_fields_str]) || isset($params['mepr_calculate_taxes'])); - - //We now support address being required -- handle that here - $this->address_fields = $this->update_address_fields_required(); - $this->custom_fields = $this->update_custom_fields($params); - - // design - $this->design_logo_img = absint($params[$this->design_logo_img_str]); - $this->design_primary_color = sanitize_text_field($params[$this->design_primary_color_str]); - $this->design_enable_checkout_template = isset($params[$this->design_enable_checkout_template_str]); - $this->design_enable_login_template = isset($params[$this->design_enable_login_template_str]); - $this->design_show_login_welcome_image = isset($params[$this->design_show_login_welcome_image_str]); - $this->design_login_welcome_img = absint($params[$this->design_login_welcome_img_str]); - $this->design_enable_account_template = isset($params[$this->design_enable_account_template_str]); - $this->design_show_account_welcome_image = isset($params[$this->design_show_account_welcome_image_str]); - $this->design_account_welcome_img = absint($params[$this->design_account_welcome_img_str]); - $this->design_enable_courses_template = isset($params[$this->design_enable_courses_template_str]); - $this->design_enable_thankyou_template = isset($params[$this->design_enable_thankyou_template_str]); - $this->design_show_thankyou_welcome_image = isset($params[$this->design_show_thankyou_welcome_image_str]); - $this->design_thankyou_hide_invoice = isset($params[$this->design_thankyou_hide_invoice_str]); - $this->design_thankyou_invoice_message = wp_kses_post(stripslashes($params[$this->design_thankyou_invoice_message_str])); - $this->design_thankyou_welcome_img = absint($params[$this->design_thankyou_welcome_img_str]); - $this->design_enable_pricing_template = isset($params[$this->design_enable_pricing_template_str]); - $this->design_pricing_title = sanitize_text_field($params[$this->design_pricing_title_str]); - $this->design_pricing_cta_color = sanitize_text_field($params[$this->design_pricing_cta_color_str]); - $this->design_pricing_subheadline = wp_kses_post(stripslashes($params[$this->design_pricing_subheadline_str])); - $this->rl_enable_coaching_template = isset($params[$this->rl_enable_coaching_template_str]); - $this->design_show_checkout_price_terms = isset($params[$this->design_show_checkout_price_terms_str]); - $this->rl_enable_wp_footer = sanitize_text_field($params[$this->rl_enable_wp_footer_str]); - } - - public function update_address_fields_required() { - $new_address_fields = array(); - - foreach($this->address_fields as $line) { - if($line->field_key != 'mepr-address-two') { //Don't set address 2 to required ever - $line->required = $this->require_address_fields; - } + $this->force_login_page_url = isset($params[$this->force_login_page_url_str]); - $new_address_fields[] = $line; - } + $this->login_redirect_url = (isset($params[$this->login_redirect_url_str]) && !empty($params[$this->login_redirect_url_str])) ? trim(stripslashes($params[$this->login_redirect_url_str])) : $this->account_page_url(); - return $new_address_fields; - } + $this->logout_redirect_url = (isset($params[$this->logout_redirect_url_str]) && !empty($params[$this->logout_redirect_url_str])) ? trim(stripslashes($params[$this->logout_redirect_url_str])) : ''; - public function get_custom_field_slugs() { - $slugs = array(); + // Notification Settings + $this->emails = []; - if(empty($this->custom_fields)) - return $slugs; + foreach ($params[$this->emails_str] as $email => $vals) { + $this->emails[$email] = [ + 'enabled' => isset($params[$this->emails_str][$email]['enabled']), + 'use_template' => isset($params[$this->emails_str][$email]['use_template']), + 'subject' => trim(stripslashes($params[$this->emails_str][$email]['subject'])), + 'body' => MeprUtils::maybe_wpautop(stripslashes($params[$this->emails_str][$email]['body'])), + ]; + } - foreach($this->custom_fields as $row) - $slugs[] = $row->field_key; + $this->disable_mod_rewrite = isset($params[$this->disable_mod_rewrite_str]); + $this->anti_card_testing_enabled = isset($params[$this->anti_card_testing_enabled_str]); + $this->anti_card_testing_ip_method = isset($params[$this->anti_card_testing_ip_method_str]) ? sanitize_text_field(wp_unslash($params[$this->anti_card_testing_ip_method_str])) : ''; + $this->anti_card_testing_blocked = isset($params[$this->anti_card_testing_blocked_str]) && is_string($params[$this->anti_card_testing_blocked_str]) ? array_unique(array_filter(array_map('trim', explode("\n", $params[$this->anti_card_testing_blocked_str])))) : []; + + $this->custom_message = wp_kses_post(stripslashes($params[$this->custom_message_str])); + $currency_code = stripslashes($params[$this->currency_code_str]); + $currency_code_changed = $this->currency_code != $currency_code; + $this->currency_code = $currency_code; + $this->currency_symbol = stripslashes($params[$this->currency_symbol_str]); + $this->currency_symbol_after = isset($params[$this->currency_symbol_after_str]); + $this->language_code = stripslashes($params[$this->language_code_str]); + + if (isset($params[$this->integrations_str])) { + foreach ($params[$this->integrations_str] as $intg_key => $intg) { + $params[$this->integrations_str][$intg_key]['use_icon'] = isset($params[$this->integrations_str][$intg_key]['use_icon']); + $params[$this->integrations_str][$intg_key]['use_label'] = isset($params[$this->integrations_str][$intg_key]['use_label']); + $params[$this->integrations_str][$intg_key]['use_desc'] = isset($params[$this->integrations_str][$intg_key]['use_desc']); + + if (isset($intg['gateway']) && $intg['gateway'] == 'MeprStripeGateway') { + if ($currency_code_changed || empty($params[$this->integrations_str][$intg_key]['payment_methods'])) { + // Set the Stripe payment methods to be an empty array if the currency code has changed + // or no payment methods were selected. + $params[$this->integrations_str][$intg_key]['payment_methods'] = []; + } + } + } + } - return $slugs; - } + $this->integrations = (isset($params[$this->integrations_str])) ? $params[$this->integrations_str] : []; + $this->lock_wp_admin = isset($params[$this->lock_wp_admin_str]); + $this->enforce_strong_password = $params[$this->enforce_strong_password_str]; + $this->disable_wp_registration_form = isset($params[$this->disable_wp_registration_form_str]); + $this->disable_wp_admin_bar = isset($params[$this->disable_wp_admin_bar_str]); + $this->pro_rated_upgrades = isset($params[$this->pro_rated_upgrades_str]); + $this->disable_checkout_password_fields = isset($params[$this->disable_checkout_password_fields_str]); + $this->enable_spc = isset($params[$this->enable_spc_str]); + $this->enable_spc_invoice = isset($params[$this->enable_spc_invoice_str]); + $this->coupon_field_enabled = isset($params[$this->coupon_field_enabled_str]); + $this->require_tos = isset($params[$this->require_tos_str]); + $this->tos_url = (isset($params[$this->tos_url_str])) ? trim(stripslashes($params[$this->tos_url_str])) : ''; + $this->tos_title = (isset($params[$this->tos_title_str]) && !empty($params[$this->tos_title_str])) ? trim(stripslashes($params[$this->tos_title_str])) : _x('I have read and agree to the Terms of Service', 'ui', 'memberpress'); + $this->require_privacy_policy = isset($params[$this->require_privacy_policy_str]); + $this->privacy_policy_title = (isset($params[$this->privacy_policy_title_str]) && !empty($params[$this->privacy_policy_title_str])) ? trim(stripslashes($params[$this->privacy_policy_title_str])) : _x('This site collects names, emails and other user information. I consent to the terms set forth in the %Privacy Policy%.', 'ui', 'memberpress'); + $this->mail_send_from_name = (isset($params[$this->mail_send_from_name_str])) ? trim(stripslashes($params[$this->mail_send_from_name_str])) : MeprUtils::blogname(); + $this->mail_send_from_email = (isset($params[$this->mail_send_from_email_str])) ? trim(stripslashes($params[$this->mail_send_from_email_str])) : get_option('admin_email'); + $this->username_is_email = isset($params[$this->username_is_email_str]); + $this->require_fname_lname = isset($params[$this->require_fname_lname_str]); + $this->show_fname_lname = isset($params[$this->show_fname_lname_str]); + + // This happens on the activate screen't do this here + // $this->mothership_license = stripslashes($params[$this->mothership_license_str]); + $this->product_pages_slug = sanitize_title(stripslashes($params[$this->product_pages_slug_str]), 'register'); + $this->group_pages_slug = sanitize_title(stripslashes($params[$this->group_pages_slug_str]), 'plans'); + $this->admin_email_addresses = $params[$this->admin_email_addresses_str]; + $this->unauthorized_message = wp_kses_post(stripslashes($params[$this->unauthorized_message_str])); + $this->unauth_show_excerpts = isset($params[$this->unauth_show_excerpts_str]); + $this->unauth_excerpt_type = $params[$this->unauth_excerpt_type_str]; + $this->unauth_excerpt_size = $params[$this->unauth_excerpt_size_str]; + $this->unauth_show_login = isset($params[$this->unauth_show_login_str]); + $this->disable_grace_init_days = isset($params[$this->disable_grace_init_days_str]); + $this->authorize_seo_views = isset($params[$this->authorize_seo_views_str]); + $this->seo_unauthorized_noindex = isset($params[$this->seo_unauthorized_noindex_str]); + $this->paywall_enabled = isset($params[$this->paywall_enabled_str]); + $this->paywall_num_free_views = (isset($params[$this->paywall_num_free_views_str])) ? (int)$params[$this->paywall_num_free_views_str] : 1; + $this->disable_summary_email = isset($params[$this->disable_summary_email_str]); + $this->redirect_on_unauthorized = isset($params[$this->redirect_on_unauthorized_str]); + $this->unauthorized_redirect_url = trim(stripslashes($params[$this->unauthorized_redirect_url_str])); + $this->redirect_non_singular = isset($params[$this->redirect_non_singular_str]); + $this->redirect_method = $params[$this->redirect_method_str]; + $this->allow_cancel_subs = isset($params[$this->allow_cancel_subs_str]); + $this->allow_suspend_subs = isset($params[$this->allow_suspend_subs_str]); + $this->disable_global_autoresponder_list = isset($params[$this->disable_global_autoresponder_list_str]); + $this->opt_in_checked_by_default = isset($params[$this->opt_in_checked_by_default_str]); + $this->global_styles = isset($params[$this->global_styles_str]); + $this->include_email_privacy_link = isset($params[$this->include_email_privacy_link_str]); + + // Signup fields stuff + $this->username_is_email = isset($params[$this->username_is_email_str]); + $this->require_fname_lname = isset($params[$this->require_fname_lname_str]); + $this->show_fname_lname = (isset($params[$this->show_fname_lname_str]) || isset($params[$this->require_fname_lname_str])); + $this->show_fields_logged_in_purchases = isset($params[$this->show_fields_logged_in_purchases_str]); + + // Always show and require address fields when tax calculations are enabled + $this->show_address_fields = (isset($params[$this->show_address_fields_str]) || isset($params[$this->require_address_fields_str]) || isset($params['mepr_calculate_taxes'])); + $this->require_address_fields = (isset($params[$this->require_address_fields_str]) || isset($params['mepr_calculate_taxes'])); + + // We now support address being required -- handle that here + $this->address_fields = $this->update_address_fields_required(); + $this->custom_fields = $this->update_custom_fields($params); + + // design + $this->design_logo_img = absint($params[$this->design_logo_img_str]); + $this->design_primary_color = sanitize_text_field($params[$this->design_primary_color_str]); + $this->design_enable_checkout_template = isset($params[$this->design_enable_checkout_template_str]); + $this->design_enable_login_template = isset($params[$this->design_enable_login_template_str]); + $this->design_show_login_welcome_image = isset($params[$this->design_show_login_welcome_image_str]); + $this->design_login_welcome_img = absint($params[$this->design_login_welcome_img_str]); + $this->design_enable_account_template = isset($params[$this->design_enable_account_template_str]); + $this->design_show_account_welcome_image = isset($params[$this->design_show_account_welcome_image_str]); + $this->design_account_welcome_img = absint($params[$this->design_account_welcome_img_str]); + $this->design_enable_courses_template = isset($params[$this->design_enable_courses_template_str]); + $this->design_enable_thankyou_template = isset($params[$this->design_enable_thankyou_template_str]); + $this->design_show_thankyou_welcome_image = isset($params[$this->design_show_thankyou_welcome_image_str]); + $this->design_thankyou_hide_invoice = isset($params[$this->design_thankyou_hide_invoice_str]); + $this->design_thankyou_invoice_message = wp_kses_post(stripslashes($params[$this->design_thankyou_invoice_message_str])); + $this->design_thankyou_welcome_img = absint($params[$this->design_thankyou_welcome_img_str]); + $this->design_enable_pricing_template = isset($params[$this->design_enable_pricing_template_str]); + $this->design_pricing_title = sanitize_text_field($params[$this->design_pricing_title_str]); + $this->design_pricing_cta_color = sanitize_text_field($params[$this->design_pricing_cta_color_str]); + $this->design_pricing_subheadline = wp_kses_post(stripslashes($params[$this->design_pricing_subheadline_str])); + $this->rl_enable_coaching_template = isset($params[$this->rl_enable_coaching_template_str]); + $this->design_show_checkout_price_terms = isset($params[$this->design_show_checkout_price_terms_str]); + $this->rl_enable_wp_footer = sanitize_text_field($params[$this->rl_enable_wp_footer_str]); + } + + public function update_address_fields_required() + { + $new_address_fields = []; + + foreach ($this->address_fields as $line) { + if ($line->field_key != 'mepr-address-two') { // Don't set address 2 to required ever + $line->required = $this->require_address_fields; + } - public function get_custom_field($field_key) { - $custom_field = null; + $new_address_fields[] = $line; + } - foreach($this->custom_fields as $custom_field) { - if($custom_field->field_key==$field_key) { - return $custom_field; - } + return $new_address_fields; } - return $custom_field; - } - - public function update_custom_fields($params) { - $fields = array(); - - if(isset($params[$this->custom_fields_str]) && !empty($params[$this->custom_fields_str])) { - $indexes = $params['mepr-custom-fields-index']; - - $used = array(); - - foreach($indexes as $i) { - $name = isset($params[$this->custom_fields_str][$i]['name'])?$params[$this->custom_fields_str][$i]['name']:''; - $slug = ($params[$this->custom_fields_str][$i]['slug'] == 'mepr_none') ? substr( MeprUtils::sanitize_string('mepr_'.$name), 0, 240 ) : substr( $params[$this->custom_fields_str][$i]['slug'], 0, 240 ); //Need to check that this key doesn't already exist in usermeta table - - // Prevent duplicate slugs - if ( in_array( $slug, $used ) ) { - do { - $slug_parts = explode( '-', $slug ); - if ( is_array( $slug_parts ) ) { // We may have a number appended already - $number = array_pop( $slug_parts ); - if ( is_numeric( $number ) ) { // Increment the existing number - $append = (int) $number + 1; - $last_index = count( $slug_parts ) - 1; - $slug_parts[$last_index] .= "-{$append}"; - $slug = implode( '-', $slug_parts ); - } else { // Append 1 - $slug .= '-1'; - } - } else { // Append 1 - $slug .= '-1'; - } - } while ( in_array( $slug, $used ) ); - } - $used[] = $slug; - - $type = $params[$this->custom_fields_str][$i]['type']; - $default = isset($params[$this->custom_fields_str][$i]['default'])?$params[$this->custom_fields_str][$i]['default']:''; - $signup = (isset($params[$this->custom_fields_str][$i]['signup']) || isset($params[$this->custom_fields_str][$i]['required'])); - $show_in_account = isset($params[$this->custom_fields_str][$i]['show_in_account']); - $required = isset($params[$this->custom_fields_str][$i]['required']); - $dropdown_ops = array(); - - if(in_array( $type, array('dropdown','multiselect','radios','checkboxes') ) ) { - $options = $params[$this->custom_fields_str][$i]['option']; - $values = $params[$this->custom_fields_str][$i]['value']; - - foreach($options as $key => $value) { - if(!empty($value)) { - //Due to WPML compat - we're no longer storing these as forced (object)'s - see $this->wpml_custom_fields() - $option_value = sanitize_title($values[$key], sanitize_title($options[$key])); - $option_value = MeprHooks::apply_filters('mepr-custom-field-option-value', $option_value, $values[$key], $options[$key]); - - $dropdown_ops[] = array( - 'option_name' => $options[$key], - 'option_value' => $option_value - ); - } - } - - if(empty($dropdown_ops)) { - $name = ''; //if no dropdown options were entered let's not save this line - } - } - - if($name != '') //If no name was left let's not save this line - //Due to WPML compat - we're no longer storing these as forced (object)'s - see $this->wpml_custom_fields() - $fields[] = array( - 'field_key' => $slug, - 'field_name' => $name, - 'field_type' => $type, - 'default_value' => $default, - 'show_on_signup' => $signup, - 'show_in_account' => $show_in_account, - 'required' => $required, - 'options' => $dropdown_ops - ); - } - } + public function get_custom_field_slugs() + { + $slugs = []; - return $fields; - } + if (empty($this->custom_fields)) { + return $slugs; + } - public function set_from_array($options = array(), $post_array = false) { - if($post_array) { - $this->update($post_array); - } - else { // Set values from array - foreach($options as $key => $value) { - $this->$key = $value; - } + foreach ($this->custom_fields as $row) { + $slugs[] = $row->field_key; + } + + return $slugs; } - } - public function store($validate = true) { - $options = (array)$this; - // Don't want to store any dynamic attributes - unset($options['dynamic_attrs']); + public function get_custom_field($field_key) + { + $custom_field = null; - // This is where we store our dynamic_attrs - $this->store_attrs(); + foreach ($this->custom_fields as $custom_field) { + if ($custom_field->field_key == $field_key) { + return $custom_field; + } + } - if($validate) { - $errors = $this->validate($_POST); + return $custom_field; + } - if(empty($errors)) { - update_option(MEPR_OPTIONS_SLUG, $options); - } + public function update_custom_fields($params) + { + $fields = []; + + if (isset($params[$this->custom_fields_str]) && !empty($params[$this->custom_fields_str])) { + $indexes = $params['mepr-custom-fields-index']; + + $used = []; + + foreach ($indexes as $i) { + $name = isset($params[$this->custom_fields_str][$i]['name']) ? $params[$this->custom_fields_str][$i]['name'] : ''; + $slug = ($params[$this->custom_fields_str][$i]['slug'] == 'mepr_none') ? substr(MeprUtils::sanitize_string('mepr_' . $name), 0, 240) : substr($params[$this->custom_fields_str][$i]['slug'], 0, 240); // Need to check that this key doesn't already exist in usermeta table + + // Prevent duplicate slugs + if (in_array($slug, $used)) { + do { + $slug_parts = explode('-', $slug); + if (is_array($slug_parts)) { // We may have a number appended already + $number = array_pop($slug_parts); + if (is_numeric($number)) { // Increment the existing number + $append = (int) $number + 1; + $last_index = count($slug_parts) - 1; + $slug_parts[$last_index] .= "-{$append}"; + $slug = implode('-', $slug_parts); + } else { // Append 1 + $slug .= '-1'; + } + } else { // Append 1 + $slug .= '-1'; + } + } while (in_array($slug, $used)); + } + $used[] = $slug; + + $type = $params[$this->custom_fields_str][$i]['type']; + $default = isset($params[$this->custom_fields_str][$i]['default']) ? $params[$this->custom_fields_str][$i]['default'] : ''; + $signup = (isset($params[$this->custom_fields_str][$i]['signup']) || isset($params[$this->custom_fields_str][$i]['required'])); + $show_in_account = isset($params[$this->custom_fields_str][$i]['show_in_account']); + $required = isset($params[$this->custom_fields_str][$i]['required']); + $dropdown_ops = []; + + if (in_array($type, ['dropdown','multiselect','radios','checkboxes'])) { + $options = $params[$this->custom_fields_str][$i]['option']; + $values = $params[$this->custom_fields_str][$i]['value']; + + foreach ($options as $key => $value) { + if (!empty($value)) { + // Due to WPML compat - we're no longer storing these as forced (object)'s - see $this->wpml_custom_fields() + $option_value = sanitize_title($values[$key], sanitize_title($options[$key])); + $option_value = MeprHooks::apply_filters('mepr-custom-field-option-value', $option_value, $values[$key], $options[$key]); + + $dropdown_ops[] = [ + 'option_name' => $options[$key], + 'option_value' => $option_value, + ]; + } + } + + if (empty($dropdown_ops)) { + $name = ''; // if no dropdown options were entered let's not save this line + } + } + + if ($name != '') { // If no name was left let's not save this line + // Due to WPML compat - we're no longer storing these as forced (object)'s - see $this->wpml_custom_fields() + $fields[] = [ + 'field_key' => $slug, + 'field_name' => $name, + 'field_type' => $type, + 'default_value' => $default, + 'show_on_signup' => $signup, + 'show_in_account' => $show_in_account, + 'required' => $required, + 'options' => $dropdown_ops, + ]; + } + } + } + + return $fields; + } - return $errors; + public function set_from_array($options = [], $post_array = false) + { + if ($post_array) { + $this->update($post_array); + } else { // Set values from array + foreach ($options as $key => $value) { + $this->$key = $value; + } + } } - update_option(MEPR_OPTIONS_SLUG, $options); - } + public function store($validate = true) + { + $options = (array)$this; + // Don't want to store any dynamic attributes + unset($options['dynamic_attrs']); - public function biz_address_is_set() { - $one = $this->attr('biz_address1'); - //$two = $this->attr('biz_address2'); - $city = $this->attr('biz_city'); - $state = $this->attr('biz_state'); - $country = $this->attr('biz_country'); - $postcode = $this->attr('biz_postcode'); + // This is where we store our dynamic_attrs + $this->store_attrs(); - return (!empty($country) && !empty($postcode) && !empty($state) && !empty($city) && !empty($one)); - } + if ($validate) { + $errors = $this->validate($_POST); - public function payment_method($id = 'default', $include_builtin_gateways = true, $force = false) { - $pmt_methods = $this->payment_methods($include_builtin_gateways, $force); + if (empty($errors)) { + update_option(MEPR_OPTIONS_SLUG, $options); + } - if($id == 'default') { - $keys = array_keys($pmt_methods); - if(isset($keys[0])) { $id = $keys[0]; } - } + return $errors; + } - if(isset($pmt_methods[$id])) { - return $pmt_methods[$id]; + update_option(MEPR_OPTIONS_SLUG, $options); } - return false; - } + public function biz_address_is_set() + { + $one = $this->attr('biz_address1'); + // $two = $this->attr('biz_address2'); + $city = $this->attr('biz_city'); + $state = $this->attr('biz_state'); + $country = $this->attr('biz_country'); + $postcode = $this->attr('biz_postcode'); - public function payment_methods($include_builtin_gateways = true, $force = false) { - static $pmt_methods; + return (!empty($country) && !empty($postcode) && !empty($state) && !empty($city) && !empty($one)); + } - if(!isset($pmt_methods) || $force) { - $pmt_methods = array(); + public function payment_method($id = 'default', $include_builtin_gateways = true, $force = false) + { + $pmt_methods = $this->payment_methods($include_builtin_gateways, $force); - if(isset($this->integrations) and is_array($this->integrations)) { - foreach($this->integrations as $intg_id => $intg_array) { - try { - $pmt_methods[$intg_id] = MeprGatewayFactory::fetch($intg_array['gateway'], $intg_array); - } - catch(Exception $e) { - // Just do nothing for now - } + if ($id == 'default') { + $keys = array_keys($pmt_methods); + if (isset($keys[0])) { + $id = $keys[0]; + } } - } - if($include_builtin_gateways) { - $pmt_methods[MeprTransaction::$free_gateway_str] = - new MeprBaseStaticGateway( MeprTransaction::$free_gateway_str, - __('Free', 'memberpress'), - __('Free', 'memberpress') ); + if (isset($pmt_methods[$id])) { + return $pmt_methods[$id]; + } - $pmt_methods[MeprTransaction::$manual_gateway_str] = - new MeprBaseStaticGateway( MeprTransaction::$manual_gateway_str, - __('Manual', 'memberpress'), - __('Manual', 'memberpress') ); - } + return false; } - return $pmt_methods; - } - - public function pm_count() { - return count($this->integrations); - } + public function payment_methods($include_builtin_gateways = true, $force = false) + { + static $pmt_methods; - public function account_page_url($args = '') { - $link = home_url(); + if (!isset($pmt_methods) || $force) { + $pmt_methods = []; - if(isset($this->account_page_id) && (int)$this->account_page_id > 0) { - $link = MeprUtils::get_permalink($this->account_page_id); + if (isset($this->integrations) and is_array($this->integrations)) { + foreach ($this->integrations as $intg_id => $intg_array) { + try { + $pmt_methods[$intg_id] = MeprGatewayFactory::fetch($intg_array['gateway'], $intg_array); + } catch (Exception $e) { + // Just do nothing for now + } + } + } - $link = MeprHooks::apply_filters('mepr-account-page-permalink', $link); + if ($include_builtin_gateways) { + $pmt_methods[MeprTransaction::$free_gateway_str] = + new MeprBaseStaticGateway( + MeprTransaction::$free_gateway_str, + __('Free', 'memberpress'), + __('Free', 'memberpress') + ); + + $pmt_methods[MeprTransaction::$manual_gateway_str] = + new MeprBaseStaticGateway( + MeprTransaction::$manual_gateway_str, + __('Manual', 'memberpress'), + __('Manual', 'memberpress') + ); + } + } - if(!empty($args)) { - $link .= MeprAppCtrl::get_param_delimiter_char($link).$args; - } + return $pmt_methods; } - return $link; - } + public function pm_count() + { + return count($this->integrations); + } - public function login_page_url($args = '') { - if( isset($this->login_page_id) and - is_numeric($this->login_page_id) and - (int)$this->login_page_id > 0 ) { - $link = MeprUtils::get_permalink($this->login_page_id); + public function account_page_url($args = '') + { + $link = home_url(); - if(!empty($args)) { - return $link.MeprAppCtrl::get_param_delimiter_char($link).$args; - } - else { - return $link; - } - } + if (isset($this->account_page_id) && (int)$this->account_page_id > 0) { + $link = MeprUtils::get_permalink($this->account_page_id); - return home_url(); // default to the home url - } + $link = MeprHooks::apply_filters('mepr-account-page-permalink', $link); - public function thankyou_page_url($args = '') { - $thank_you_page_id = 0; - $args_array = wp_parse_args($args); - $url = home_url(); + if (!empty($args)) { + $link .= MeprAppCtrl::get_param_delimiter_char($link) . $args; + } + } - if(isset($args_array['membership_id'])) { - $product = new MeprProduct($args_array['membership_id']); - if($product->thank_you_page_enabled && $product->thank_you_page_type == 'page') { - // Returns the custom thank you page for the product - $thank_you_page_id = $product->thank_you_page_id; - } - elseif(isset($this->thankyou_page_id)) { - // Returns the default thank you page from the options - $thank_you_page_id = $this->thankyou_page_id; - } - } - elseif(isset($this->thankyou_page_id)) { - // Returns the default thank you page from the options - $thank_you_page_id = $this->thankyou_page_id; + return $link; } - if(isset($thank_you_page_id) && - is_numeric($thank_you_page_id) && - (int)$thank_you_page_id > 0 ) { - $thank_you_page_id = (int)$thank_you_page_id; - $link = MeprUtils::get_permalink($thank_you_page_id); + public function login_page_url($args = '') + { + if ( + isset($this->login_page_id) and + is_numeric($this->login_page_id) and + (int)$this->login_page_id > 0 + ) { + $link = MeprUtils::get_permalink($this->login_page_id); - if(!empty($args)) { - if (is_array($args)) { - $args = http_build_query($args); + if (!empty($args)) { + return $link . MeprAppCtrl::get_param_delimiter_char($link) . $args; + } else { + return $link; + } } - $url = $link.MeprAppCtrl::get_param_delimiter_char($link).$args; - } - else { - $url = $link; - } + + return home_url(); // default to the home url } - return MeprHooks::apply_filters('mepr-thankyou-page-url', $url, $args_array); - } + public function thankyou_page_url($args = '') + { + $thank_you_page_id = 0; + $args_array = wp_parse_args($args); + $url = home_url(); - public function forgot_password_url() - { - $mepr_options = MeprOptions::fetch(); + if (isset($args_array['membership_id'])) { + $product = new MeprProduct($args_array['membership_id']); + if ($product->thank_you_page_enabled && $product->thank_you_page_type == 'page') { + // Returns the custom thank you page for the product + $thank_you_page_id = $product->thank_you_page_id; + } elseif (isset($this->thankyou_page_id)) { + // Returns the default thank you page from the options + $thank_you_page_id = $this->thankyou_page_id; + } + } elseif (isset($this->thankyou_page_id)) { + // Returns the default thank you page from the options + $thank_you_page_id = $this->thankyou_page_id; + } - $login_page_id = (!empty($mepr_options->login_page_id) && $mepr_options->login_page_id > 0)?$mepr_options->login_page_id:0; + if ( + isset($thank_you_page_id) && + is_numeric($thank_you_page_id) && + (int)$thank_you_page_id > 0 + ) { + $thank_you_page_id = (int)$thank_you_page_id; + $link = MeprUtils::get_permalink($thank_you_page_id); + + if (!empty($args)) { + if (is_array($args)) { + $args = http_build_query($args); + } + $url = $link . MeprAppCtrl::get_param_delimiter_char($link) . $args; + } else { + $url = $link; + } + } - if($login_page_id) { - $login_url = $mepr_options->login_page_url(); - $login_delim = MeprAppCtrl::get_param_delimiter_char($login_url); - $forgot_password_url = "{$login_url}{$login_delim}action=forgot_password"; - } - else { - $login_url = home_url('/wp-login.php'); - $forgot_password_url = home_url('/wp-login.php?action=lostpassword'); + return MeprHooks::apply_filters('mepr-thankyou-page-url', $url, $args_array); } - return $forgot_password_url; - } + public function forgot_password_url() + { + $mepr_options = MeprOptions::fetch(); - private function set_address_fields() { - $country_codes = MeprUtils::countries(); - $state_codes = MeprUtils::states(); - $coptions = array(); + $login_page_id = (!empty($mepr_options->login_page_id) && $mepr_options->login_page_id > 0) ? $mepr_options->login_page_id : 0; - foreach( $country_codes as $copt => $cname ) { - $coptions[] = (object)array( 'option_name' => $cname, 'option_value' => $copt ); - } + if ($login_page_id) { + $login_url = $mepr_options->login_page_url(); + $login_delim = MeprAppCtrl::get_param_delimiter_char($login_url); + $forgot_password_url = "{$login_url}{$login_delim}action=forgot_password"; + } else { + $login_url = home_url('/wp-login.php'); + $forgot_password_url = home_url('/wp-login.php?action=lostpassword'); + } - $this->address_fields = array( - (object)array('field_key' => 'mepr-address-one', 'field_name' => _x('Address Line 1', 'ui', 'memberpress'), 'field_type' => 'text', 'default_value' => '', 'show_on_signup' => true, 'required' => $this->require_address_fields), - (object)array('field_key' => 'mepr-address-two', 'field_name' => _x('Address Line 2', 'ui', 'memberpress'), 'field_type' => 'text', 'default_value' => '', 'show_on_signup' => true, 'required' => false), - (object)array('field_key' => 'mepr-address-city', 'field_name' => _x('City', 'ui', 'memberpress'), 'field_type' => 'text', 'default_value' => '', 'show_on_signup' => true, 'required' => $this->require_address_fields), - (object)array('field_key' => 'mepr-address-country', 'field_name' => _x('Country', 'ui', 'memberpress'), 'field_type' => 'countries', 'default_value' => '', 'show_on_signup' => true, 'required' => $this->require_address_fields), - (object)array('field_key' => 'mepr-address-state', 'field_name' => _x('State/Province', 'ui', 'memberpress'), 'field_type' => 'states', 'default_value' => '', 'show_on_signup' => true, 'required' => $this->require_address_fields), - (object)array('field_key' => 'mepr-address-zip', 'field_name' => _x('Zip/Postal Code', 'ui', 'memberpress'), 'field_type' => 'text', 'default_value' => '', 'show_on_signup' => true, 'required' => $this->require_address_fields) - ); - } - - /***** Migrations ... should probably find a better place to - put these but the model makes sense for now I suppose *****/ - public static function migrate_to_new_unauth_system() { - $mepr_options = MeprOptions::fetch(); - - if(!isset($mepr_options->unauthorized_page_id) || (int)$mepr_options->unauthorized_page_id <= 0) { - return; // Short circuit ... only migrate if we need to + return $forgot_password_url; } - $page = get_post($mepr_options->unauthorized_page_id); + private function set_address_fields() + { + $country_codes = MeprUtils::countries(); + $state_codes = MeprUtils::states(); + $coptions = []; - if(!($page instanceof WP_Post)) { - $mepr_options->unauthorized_page_id = 0; //Set the page to 0 so we don't end up here again - $mepr_options->redirect_on_unauthorized = false; - $mepr_options->store(false); - return; - } + foreach ($country_codes as $copt => $cname) { + $coptions[] = (object)[ + 'option_name' => $cname, + 'option_value' => $copt, + ]; + } - $content = stripslashes($page->post_content); //$page->post_content is raw, so let's strip slashes + $this->address_fields = [ + (object)[ + 'field_key' => 'mepr-address-one', + 'field_name' => _x('Address Line 1', 'ui', 'memberpress'), + 'field_type' => 'text', + 'default_value' => '', + 'show_on_signup' => true, + 'required' => $this->require_address_fields, + ], + (object)[ + 'field_key' => 'mepr-address-two', + 'field_name' => _x('Address Line 2', 'ui', 'memberpress'), + 'field_type' => 'text', + 'default_value' => '', + 'show_on_signup' => true, + 'required' => false, + ], + (object)[ + 'field_key' => 'mepr-address-city', + 'field_name' => _x('City', 'ui', 'memberpress'), + 'field_type' => 'text', + 'default_value' => '', + 'show_on_signup' => true, + 'required' => $this->require_address_fields, + ], + (object)[ + 'field_key' => 'mepr-address-country', + 'field_name' => _x('Country', 'ui', 'memberpress'), + 'field_type' => 'countries', + 'default_value' => '', + 'show_on_signup' => true, + 'required' => $this->require_address_fields, + ], + (object)[ + 'field_key' => 'mepr-address-state', + 'field_name' => _x('State/Province', 'ui', 'memberpress'), + 'field_type' => 'states', + 'default_value' => '', + 'show_on_signup' => true, + 'required' => $this->require_address_fields, + ], + (object)[ + 'field_key' => 'mepr-address-zip', + 'field_name' => _x('Zip/Postal Code', 'ui', 'memberpress'), + 'field_type' => 'text', + 'default_value' => '', + 'show_on_signup' => true, + 'required' => $this->require_address_fields, + ], + ]; + } + + /***** + * Migrations ... should probably find a better place to + put these but the model makes sense for now I suppose + *****/ + public static function migrate_to_new_unauth_system() + { + $mepr_options = MeprOptions::fetch(); + + if (!isset($mepr_options->unauthorized_page_id) || (int)$mepr_options->unauthorized_page_id <= 0) { + return; // Short circuit ... only migrate if we need to + } - //It's either empty or there's something on this page - and it's not the unauth shortcode - //so let's put the shortcode on this page, and setup the unauth message - if(empty($content) || (!empty($content) && strstr($content, 'mepr-unauthorized-message') === false)) { - $page->post_content = '[mepr-unauthorized-message]'; - $mepr_options->redirect_on_unauthorized = true; - $mepr_options->unauthorized_redirect_url = MeprUtils::get_permalink($page->ID); - $mepr_options->unauthorized_page_id = 0; //Set the page to 0 so we don't end up here again + $page = get_post($mepr_options->unauthorized_page_id); - if(!empty($content)) { //Only change the unauth message if the user had one on this page already - $mepr_options->unauthorized_message = $content; - } + if (!($page instanceof WP_Post)) { + $mepr_options->unauthorized_page_id = 0; // Set the page to 0 so we don't end up here again + $mepr_options->redirect_on_unauthorized = false; + $mepr_options->store(false); + return; + } - $mepr_options->store(false); - wp_update_post($page); + $content = stripslashes($page->post_content); // $page->post_content is raw, so let's strip slashes - return; - } + // It's either empty or there's something on this page - and it's not the unauth shortcode + // so let's put the shortcode on this page, and setup the unauth message + if (empty($content) || (!empty($content) && strstr($content, 'mepr-unauthorized-message') === false)) { + $page->post_content = '[mepr-unauthorized-message]'; + $mepr_options->redirect_on_unauthorized = true; + $mepr_options->unauthorized_redirect_url = MeprUtils::get_permalink($page->ID); + $mepr_options->unauthorized_page_id = 0; // Set the page to 0 so we don't end up here again - //If we made it here the user has already configured this shiz - //(probably running a beta of 1.1.0 or something) so let's - //just set the unauthorized_page_id to 0 to prevent getting here again - $mepr_options->unauthorized_page_id = 0; //Set the page to 0 so we don't end up here again - $mepr_options->store(false); - } + if (!empty($content)) { // Only change the unauth message if the user had one on this page already + $mepr_options->unauthorized_message = $content; + } - public function attr($attr) { - return $this->get_attr($attr); - } + $mepr_options->store(false); + wp_update_post($page); - public function get_attr($attr) { - $value = ''; + return; + } - if(array_key_exists($attr, get_object_vars($this))) { - $value = $this->$attr; + // If we made it here the user has already configured this shiz + // (probably running a beta of 1.1.0 or something) so let's + // just set the unauthorized_page_id to 0 to prevent getting here again + $mepr_options->unauthorized_page_id = 0; // Set the page to 0 so we don't end up here again + $mepr_options->store(false); } - else if(array_key_exists($attr, $this->dynamic_attrs)) { - $a = $this->attr_config($attr); - if(!empty($a)) { - if(!isset($a['value'])) { - $value = get_option($this->attr_slug($attr), $a['default']); - $this->set_attr($this->attr_slug($attr), $value); - } - else { - $value = $a['value']; - } - } + public function attr($attr) + { + return $this->get_attr($attr); } - $value = stripslashes($value); //single quotes etc causing escape chars to appear when output + public function get_attr($attr) + { + $value = ''; - return MeprHooks::apply_filters('mepr-options-get-dynamic-attribute-'.$attr, $value, $this); - } + if (array_key_exists($attr, get_object_vars($this))) { + $value = $this->$attr; + } elseif (array_key_exists($attr, $this->dynamic_attrs)) { + $a = $this->attr_config($attr); - public function set_attr($attr, $value) { - $value = MeprHooks::apply_filters('mepr-options-set-dynamic-attribute-'.$attr, $value, $this); + if (!empty($a)) { + if (!isset($a['value'])) { + $value = get_option($this->attr_slug($attr), $a['default']); + $this->set_attr($this->attr_slug($attr), $value); + } else { + $value = $a['value']; + } + } + } - if(array_key_exists($attr, get_object_vars($this))) { - $this->$attr = $value; - } - else if(array_key_exists($attr, $this->dynamic_attrs)) { - $this->dynamic_attrs[$attr]['value'] = $value; - } - } + $value = stripslashes($value); // single quotes etc causing escape chars to appear when output - private function set_attrs($attrs) { - foreach($attrs as $attr => $value) { - if(array_key_exists($attr, $this->dynamic_attrs)) { - $this->set_attr($attr, $value); - } + return MeprHooks::apply_filters('mepr-options-get-dynamic-attribute-' . $attr, $value, $this); } - } - private function set_attrs_from_slugs($slugs) { - // Build slug to attr lookup array - $slugs_lookup = array(); - foreach($this->dynamic_attrs as $attr => $info) { - $slugs_lookup[$this->attr_slug($attr)] = $attr; - } + public function set_attr($attr, $value) + { + $value = MeprHooks::apply_filters('mepr-options-set-dynamic-attribute-' . $attr, $value, $this); - foreach($slugs as $slug => $value) { - if(array_key_exists($slug, $slugs_lookup)) { - $attr = $slugs_lookup[$slug]; - $this->set_attr($attr, $value); - } + if (array_key_exists($attr, get_object_vars($this))) { + $this->$attr = $value; + } elseif (array_key_exists($attr, $this->dynamic_attrs)) { + $this->dynamic_attrs[$attr]['value'] = $value; + } } - } - private function store_attr($attr) { - $a = $this->attr_config($attr); - if(!empty($a) && isset($a['value'])) { - update_option($this->attr_slug($attr), $a['value']); + private function set_attrs($attrs) + { + foreach ($attrs as $attr => $value) { + if (array_key_exists($attr, $this->dynamic_attrs)) { + $this->set_attr($attr, $value); + } + } } - } - private function store_attrs() { - foreach($this->dynamic_attrs as $attr => $config) { - $this->store_attr($attr); - } - } + private function set_attrs_from_slugs($slugs) + { + // Build slug to attr lookup array + $slugs_lookup = []; + foreach ($this->dynamic_attrs as $attr => $info) { + $slugs_lookup[$this->attr_slug($attr)] = $attr; + } - private function attr_config($attr) { - if(array_key_exists($attr, $this->dynamic_attrs)) { - return $this->dynamic_attrs[$attr]; + foreach ($slugs as $slug => $value) { + if (array_key_exists($slug, $slugs_lookup)) { + $attr = $slugs_lookup[$slug]; + $this->set_attr($attr, $value); + } + } } - return ''; - } - public function attr_slug($attr) { - if(array_key_exists($attr, $this->dynamic_attrs)) { - if(isset($this->dynamic_attrs['slug'])) { - return $this->dynamic_attrs['slug']; - } - else { - return "mepr_{$attr}"; - } + private function store_attr($attr) + { + $a = $this->attr_config($attr); + if (!empty($a) && isset($a['value'])) { + update_option($this->attr_slug($attr), $a['value']); + } } - return ''; - } - public function attr_default($attr) { - if(array_key_exists($attr, $this->dynamic_attrs)) { - return $this->dynamic_attrs[$attr]['default']; + private function store_attrs() + { + foreach ($this->dynamic_attrs as $attr => $config) { + $this->store_attr($attr); + } } - return ''; - } - public function attr_validations($attr) { - if(array_key_exists($attr, $this->dynamic_attrs)) { - return $this->dynamic_attrs[$attr]['validations']; + private function attr_config($attr) + { + if (array_key_exists($attr, $this->dynamic_attrs)) { + return $this->dynamic_attrs[$attr]; + } + return ''; } - return ''; - } - -/** TODO: Add these once we convert legacy fields to work with dynamic attributes - - public function __get($attr) { - return $this->attr($attr); - } - - public function __set($attr, $value) { - return $this->set_attr($attr, $value); - } - public function __isset($attr) { - if(array_key_exists($attr, $this->dynamic_attrs)) { - return isset($this->dynamic_attrs[$attr]['value']); - } - else { - return isset($this->$attr); + public function attr_slug($attr) + { + if (array_key_exists($attr, $this->dynamic_attrs)) { + if (isset($this->dynamic_attrs['slug'])) { + return $this->dynamic_attrs['slug']; + } else { + return "mepr_{$attr}"; + } + } + return ''; } - } - public function __unset($attr) { - if(array_key_exists($attr, $this->dynamic_attrs)) { - unset($this->dynamic_attrs[$attr]['value']); + public function attr_default($attr) + { + if (array_key_exists($attr, $this->dynamic_attrs)) { + return $this->dynamic_attrs[$attr]['default']; + } + return ''; } - else { - unset($this->$attr); + + public function attr_validations($attr) + { + if (array_key_exists($attr, $this->dynamic_attrs)) { + return $this->dynamic_attrs[$attr]['validations']; + } + return ''; } - } -*/ - /** We just return a JSON encoding of the - * attributes in the model when we try - * to get a string for the options. */ - public function __toString() { - $str_array = (array)$this; + /** + * TODO: Add these once we convert legacy fields to work with dynamic attributes + public function __get($attr) { + return $this->attr($attr); + } + public function __set($attr, $value) { + return $this->set_attr($attr, $value); + } + public function __isset($attr) { + if(array_key_exists($attr, $this->dynamic_attrs)) { + return isset($this->dynamic_attrs[$attr]['value']); + } + else { + return isset($this->$attr); + } + } + public function __unset($attr) { + if(array_key_exists($attr, $this->dynamic_attrs)) { + unset($this->dynamic_attrs[$attr]['value']); + } + else { + unset($this->$attr); + } + } + */ + + /** + * We just return a JSON encoding of the + * attributes in the model when we try + * to get a string for the options. + */ + public function __toString() + { + $str_array = (array)$this; + + // add dynamic attrs + foreach ($this->dynamic_attrs as $attr => $config) { + $str_array[$attr] = $this->get_attr($attr); + } - // add dynamic attrs - foreach($this->dynamic_attrs as $attr => $config) { - $str_array[$attr] = $this->get_attr($attr); + return json_encode($str_array); } - - return json_encode($str_array); - } } //End class diff --git a/app/models/MeprOrder.php b/app/models/MeprOrder.php index 2b3aca3..c328ed0 100644 --- a/app/models/MeprOrder.php +++ b/app/models/MeprOrder.php @@ -9,121 +9,130 @@ * @property string $gateway * @property string $created_at */ -class MeprOrder extends MeprBaseMetaModel { - public static $pending_str = 'pending'; - public static $failed_str = 'failed'; - public static $complete_str = 'complete'; - public static $refunded_str = 'refunded'; - - public function __construct($obj = null) { - parent::__construct($obj); - - $this->initialize( - [ - 'id' => 0, - 'user_id' => null, - 'primary_transaction_id' => 0, - 'trans_num' => MeprOrder::generate_trans_num(), - 'status' => self::$pending_str, - 'gateway' => null, - 'created_at' => null, - ], - $obj - ); - } - - public static function create(MeprOrder $order) { - $mepr_db = new MeprDb(); - - if(is_null($order->created_at) || empty($order->created_at)) { - $order->created_at = MeprUtils::ts_to_mysql_date(time()); +class MeprOrder extends MeprBaseMetaModel +{ + public static $pending_str = 'pending'; + public static $failed_str = 'failed'; + public static $complete_str = 'complete'; + public static $refunded_str = 'refunded'; + + public function __construct($obj = null) + { + parent::__construct($obj); + + $this->initialize( + [ + 'id' => 0, + 'user_id' => null, + 'primary_transaction_id' => 0, + 'trans_num' => MeprOrder::generate_trans_num(), + 'status' => self::$pending_str, + 'gateway' => null, + 'created_at' => null, + ], + $obj + ); } - $args = (array) $order->get_values(); + public static function create(MeprOrder $order) + { + $mepr_db = new MeprDb(); - return MeprHooks::apply_filters('mepr_create_order', $mepr_db->create_record($mepr_db->orders, $args, false), $args, $order->user_id); - } + if (is_null($order->created_at) || empty($order->created_at)) { + $order->created_at = MeprUtils::ts_to_mysql_date(time()); + } - public static function update(MeprOrder $order) { - $mepr_db = new MeprDb(); - $args = (array) $order->get_values(); + $args = (array) $order->get_values(); - return MeprHooks::apply_filters('mepr_update_order', $mepr_db->update_record($mepr_db->orders, $order->id, $args), $args, $order->user_id); - } + return MeprHooks::apply_filters('mepr_create_order', $mepr_db->create_record($mepr_db->orders, $args, false), $args, $order->user_id); + } + + public static function update(MeprOrder $order) + { + $mepr_db = new MeprDb(); + $args = (array) $order->get_values(); + + return MeprHooks::apply_filters('mepr_update_order', $mepr_db->update_record($mepr_db->orders, $order->id, $args), $args, $order->user_id); + } + + public function store() + { + $old_order = new self($this->id); - public function store() { - $old_order = new self($this->id); + if (isset($this->id) && !is_null($this->id) && (int) $this->id > 0) { + $this->id = self::update($this); + } else { + $this->id = self::create($this); + } - if(isset($this->id) && !is_null($this->id) && (int) $this->id > 0) { - $this->id = self::update($this); + MeprHooks::do_action('mepr-order-transition-status', $old_order->status, $this->status, $this); + MeprHooks::do_action('mepr-order-store', $this, $old_order); + MeprHooks::do_action('mepr-order-status-' . $this->status, $this); + + return $this->id; } - else { - $this->id = self::create($this); + + public function destroy() + { + $mepr_db = new MeprDb(); + $id = $this->id; + $args = compact('id'); + + MeprHooks::do_action('mepr_order_destroy', $this); + MeprHooks::do_action('mepr_pre_delete_order', $this); + $result = MeprHooks::apply_filters('mepr_delete_order', $mepr_db->delete_records($mepr_db->orders, $args), $args); + MeprHooks::do_action('mepr_post_delete_order', $this); + + return $result; } - MeprHooks::do_action('mepr-order-transition-status', $old_order->status, $this->status, $this); - MeprHooks::do_action('mepr-order-store', $this, $old_order); - MeprHooks::do_action('mepr-order-status-'.$this->status, $this); - - return $this->id; - } - - public function destroy() { - $mepr_db = new MeprDb(); - $id = $this->id; - $args = compact('id'); - - MeprHooks::do_action('mepr_order_destroy', $this); - MeprHooks::do_action('mepr_pre_delete_order', $this); - $result = MeprHooks::apply_filters('mepr_delete_order', $mepr_db->delete_records($mepr_db->orders, $args), $args); - MeprHooks::do_action('mepr_post_delete_order', $this); - - return $result; - } - - /** - * @return string - */ - public static function generate_trans_num() { - return uniqid('mp-ord-'); - } - - /** - * Get an order by ID - * - * @param int $id The order ID - * @param string $return_type Optional. The required return type. One of OBJECT, ARRAY_A, or ARRAY_N, which - * correspond to an stdClass object, an associative array, or a numeric array, - * respectively. Default OBJECT. - * @return array|object|null Database query result in format specified by $return_type or null on failure. - */ - public static function get_one($id, $return_type = OBJECT) { - $mepr_db = new MeprDb(); - $args = compact('id'); - - return $mepr_db->get_one_record($mepr_db->orders, $args, $return_type); - } - - public function get_transactions() - { - global $wpdb; - $transaction_table = $wpdb->prefix . 'mepr_transactions'; - $mepr_db = new MeprDb(); - - return $mepr_db->get_records($transaction_table, ['order_id' => $this->id]); - } - - /** - * @return bool - */ - public function is_complete() { - return $this->status == MeprOrder::$complete_str; - } - - /** - * @return bool - */ - public function is_processing() { - return $this->get_meta('processing', true) == '1'; - } + /** + * @return string + */ + public static function generate_trans_num() + { + return uniqid('mp-ord-'); + } + + /** + * Get an order by ID + * + * @param integer $id The order ID + * @param string $return_type Optional. The required return type. One of OBJECT, ARRAY_A, or ARRAY_N, which + * correspond to an stdClass object, an associative array, or a numeric array, + * respectively. Default OBJECT. + * @return array|object|null Database query result in format specified by $return_type or null on failure. + */ + public static function get_one($id, $return_type = OBJECT) + { + $mepr_db = new MeprDb(); + $args = compact('id'); + + return $mepr_db->get_one_record($mepr_db->orders, $args, $return_type); + } + + public function get_transactions() + { + global $wpdb; + $transaction_table = $wpdb->prefix . 'mepr_transactions'; + $mepr_db = new MeprDb(); + + return $mepr_db->get_records($transaction_table, ['order_id' => $this->id]); + } + + /** + * @return boolean + */ + public function is_complete() + { + return $this->status == MeprOrder::$complete_str; + } + + /** + * @return boolean + */ + public function is_processing() + { + return $this->get_meta('processing', true) == '1'; + } } diff --git a/app/models/MeprProduct.php b/app/models/MeprProduct.php index 21c7a07..1e90d41 100644 --- a/app/models/MeprProduct.php +++ b/app/models/MeprProduct.php @@ -1,637 +1,671 @@ load_cpt( - $obj, self::$cpt, - array( - 'price' => 0.00, - 'period' => 1, - 'period_type' => 'lifetime', //Default to lifetime to simplify new membership form - 'signup_button_text' => __('Sign Up','memberpress'), - 'limit_cycles' => false, - 'limit_cycles_num' => 2, - 'limit_cycles_action' => 'expire', - 'limit_cycles_expires_after' => 1, - 'limit_cycles_expires_type' => 'days', - 'trial' => false, - 'trial_days' => 1, - 'trial_amount' => 0.00, - 'trial_once' => true, - 'group_id' => 0, - 'group_order' => 0, - 'is_highlighted' => false, - 'plan_code' => '', - //who_can_purchase should be an array of OBJECTS - 'who_can_purchase' => array(), - 'pricing_title' => '', - 'pricing_show_price' => true, - 'pricing_display' => '', - 'custom_price' => '', - 'pricing_heading_txt' => '', - 'pricing_footer_txt' => '', - 'pricing_button_txt' => '', - 'pricing_button_position' => 'footer', - //Pricing benefits should be an array of strings - 'pricing_benefits' => array(), - 'register_price_action' => 'default', - 'register_price' => '', - 'thank_you_page_enabled' => false, - 'thank_you_page_type' => '', - 'thank_you_message' => '', - 'thank_you_page_id' => 0, - 'custom_login_urls_enabled' => false, - 'custom_login_urls_default' => '', - //An array of objects ->url and ->count - 'custom_login_urls' => array(), - 'expire_type' => 'none', - 'expire_after' => 1, - 'expire_unit' => 'days', - 'expire_fixed' => '', - 'tax_exempt' => false, - 'tax_class' => 'standard', - 'allow_renewal' => false, - 'access_url' => '', - 'emails' => array(), - 'disable_address_fields' => false, //Mostly for free products - 'simultaneous_subscriptions' => false, - 'use_custom_template' => false, - 'custom_template' => '', - 'customize_payment_methods' => false, - 'custom_payment_methods' => array(), - 'customize_profile_fields' => false, - 'custom_profile_fields' => array(), - 'cannot_purchase_message' => _x('You don\'t have access to purchase this item.', 'ui', 'memberpress'), - ) - ); - - $this->period_types = array('weeks','months','years','lifetime'); - $this->limit_cycles_actions = array('expire','lifetime', 'expires_after'); - $this->register_price_actions = array('default', 'hidden', 'custom'); - $this->expire_types = array('none','delay','fixed'); - $this->expire_units = array('days','weeks','months','years'); - $this->pricing_displays = array('auto','custom','none'); - - if(empty($this->pricing_display) && isset($this->pricing_show_price)) { - $this->pricing_display = $this->pricing_show_price ? 'auto' : 'none'; - } - } - - public function validate() { - $this->validate_not_empty($this->post_title, 'title'); - $this->validate_is_currency($this->price, 0.00, null, 'price'); - $this->validate_is_numeric($this->period, 1, 12, 'period'); - $this->validate_is_in_array($this->period_type, $this->period_types, 'period_type'); - $this->validate_not_empty($this->signup_button_text, 'signup_button_text'); - - $this->validate_is_bool($this->limit_cycles, 'limit_cycles'); - if($this->limit_cycles) { - $this->validate_is_numeric($this->limit_cycles_num, 1, null, 'limit_cycles_num'); - $this->validate_is_in_array($this->limit_cycles_action, $this->limit_cycles_actions, 'limit_cycles_action'); - } + public function __construct($obj = null) + { + $this->load_cpt( + $obj, + self::$cpt, + [ + 'price' => 0.00, + 'period' => 1, + 'period_type' => 'lifetime', // Default to lifetime to simplify new membership form + 'signup_button_text' => __('Sign Up', 'memberpress'), + 'limit_cycles' => false, + 'limit_cycles_num' => 2, + 'limit_cycles_action' => 'expire', + 'limit_cycles_expires_after' => 1, + 'limit_cycles_expires_type' => 'days', + 'trial' => false, + 'trial_days' => 1, + 'trial_amount' => 0.00, + 'trial_once' => true, + 'group_id' => 0, + 'group_order' => 0, + 'is_highlighted' => false, + 'plan_code' => '', + // who_can_purchase should be an array of OBJECTS + 'who_can_purchase' => [], + 'pricing_title' => '', + 'pricing_show_price' => true, + 'pricing_display' => '', + 'custom_price' => '', + 'pricing_heading_txt' => '', + 'pricing_footer_txt' => '', + 'pricing_button_txt' => '', + 'pricing_button_position' => 'footer', + // Pricing benefits should be an array of strings + 'pricing_benefits' => [], + 'register_price_action' => 'default', + 'register_price' => '', + 'thank_you_page_enabled' => false, + 'thank_you_page_type' => '', + 'thank_you_message' => '', + 'thank_you_page_id' => 0, + 'custom_login_urls_enabled' => false, + 'custom_login_urls_default' => '', + // An array of objects ->url and ->count + 'custom_login_urls' => [], + 'expire_type' => 'none', + 'expire_after' => 1, + 'expire_unit' => 'days', + 'expire_fixed' => '', + 'tax_exempt' => false, + 'tax_class' => 'standard', + 'allow_renewal' => false, + 'access_url' => '', + 'emails' => [], + 'disable_address_fields' => false, // Mostly for free products + 'simultaneous_subscriptions' => false, + 'use_custom_template' => false, + 'custom_template' => '', + 'customize_payment_methods' => false, + 'custom_payment_methods' => [], + 'customize_profile_fields' => false, + 'custom_profile_fields' => [], + 'cannot_purchase_message' => _x('You don\'t have access to purchase this item.', 'ui', 'memberpress'), + ] + ); + + $this->period_types = ['weeks','months','years','lifetime']; + $this->limit_cycles_actions = ['expire','lifetime', 'expires_after']; + $this->register_price_actions = ['default', 'hidden', 'custom']; + $this->expire_types = ['none','delay','fixed']; + $this->expire_units = ['days','weeks','months','years']; + $this->pricing_displays = ['auto','custom','none']; - $this->validate_is_bool($this->trial, 'trial'); - if($this->trial) { - $this->validate_is_numeric($this->trial_days, 0.00, null, 'trial_days'); - $this->validate_is_currency($this->trial_amount, 0.00, null, 'trial_amount'); - $this->validate_is_bool($this->trial_once, 'trial_once'); + if (empty($this->pricing_display) && isset($this->pricing_show_price)) { + $this->pricing_display = $this->pricing_show_price ? 'auto' : 'none'; + } } - $this->validate_is_numeric($this->group_id, 0, null, 'group_id'); - $this->validate_is_numeric($this->group_order, 0, null, 'group_order'); + public function validate() + { + $this->validate_not_empty($this->post_title, 'title'); + $this->validate_is_currency($this->price, 0.00, null, 'price'); + $this->validate_is_numeric($this->period, 1, 12, 'period'); + $this->validate_is_in_array($this->period_type, $this->period_types, 'period_type'); + $this->validate_not_empty($this->signup_button_text, 'signup_button_text'); + + $this->validate_is_bool($this->limit_cycles, 'limit_cycles'); + if ($this->limit_cycles) { + $this->validate_is_numeric($this->limit_cycles_num, 1, null, 'limit_cycles_num'); + $this->validate_is_in_array($this->limit_cycles_action, $this->limit_cycles_actions, 'limit_cycles_action'); + } + + $this->validate_is_bool($this->trial, 'trial'); + if ($this->trial) { + $this->validate_is_numeric($this->trial_days, 0.00, null, 'trial_days'); + $this->validate_is_currency($this->trial_amount, 0.00, null, 'trial_amount'); + $this->validate_is_bool($this->trial_once, 'trial_once'); + } + + $this->validate_is_numeric($this->group_id, 0, null, 'group_id'); + $this->validate_is_numeric($this->group_order, 0, null, 'group_order'); + + $this->validate_is_bool($this->is_highlighted, 'is_highlighted'); + $this->validate_is_array($this->who_can_purchase, 'who_can_purchase'); + // $this->validate_is_bool($this->pricing_show_price, 'pricing_show_price'); + $this->validate_is_in_array($this->pricing_display, $this->pricing_displays, 'pricing_display'); + + // No need to validate these at this time + // 'pricing_title' => '', + // 'pricing_heading_txt' => '', + // 'pricing_footer_txt' => '', + // 'pricing_button_txt' => '', + // $this->validate_is_array($this->pricing_benefits, 'pricing_benefits'); + $this->validate_is_in_array($this->register_price_action, $this->register_price_actions, 'register_price_action'); + // if($this->register_price_action) { $this->validate_not_empty($this->register_price, 'register_price'); } + $this->validate_is_bool($this->thank_you_page_enabled, 'thank_you_page_enabled'); + + // No need to validate + // 'thank_you_message' => '', + $this->validate_is_bool($this->custom_login_urls_enabled, 'custom_login_urls_enabled'); + + // No need to validate + // 'custom_login_urls_default' => '', + if ($this->custom_login_urls_enabled) { + $this->validate_is_array($this->custom_login_urls, 'custom_login_urls'); + } + + $this->validate_is_in_array($this->expire_type, $this->expire_types, 'expire_type'); + if ($this->expire_type == 'delay') { + $this->validate_is_numeric($this->expire_after, 1, null, 'expire_after'); + $this->validate_is_in_array($this->expire_unit, $this->expire_units, 'expire_unit'); + } elseif ($this->expire_type == 'fixed') { + $this->validate_is_date($this->expire_fixed, 'expire_fixed'); + } - $this->validate_is_bool($this->is_highlighted, 'is_highlighted'); - $this->validate_is_array($this->who_can_purchase, 'who_can_purchase'); - //$this->validate_is_bool($this->pricing_show_price, 'pricing_show_price'); - $this->validate_is_in_array($this->pricing_display, $this->pricing_displays, 'pricing_display'); + $this->validate_is_bool($this->tax_exempt, 'tax_exempt'); + $this->validate_is_bool($this->allow_renewal, 'allow_renewal'); - // No need to validate these at this time - //'pricing_title' => '', - //'pricing_heading_txt' => '', - //'pricing_footer_txt' => '', - //'pricing_button_txt' => '', - //$this->validate_is_array($this->pricing_benefits, 'pricing_benefits'); + if (!empty($this->access_url)) { + $this->validate_is_url($this->access_url, 'access_url'); + } - $this->validate_is_in_array($this->register_price_action, $this->register_price_actions, 'register_price_action'); - //if($this->register_price_action) { $this->validate_not_empty($this->register_price, 'register_price'); } - $this->validate_is_bool($this->thank_you_page_enabled, 'thank_you_page_enabled'); + $this->validate_is_array($this->emails, 'emails'); - // No need to validate - // 'thank_you_message' => '', + $this->validate_is_bool($this->simultaneous_subscriptions, 'simultaneous_subscriptions'); - $this->validate_is_bool($this->custom_login_urls_enabled, 'custom_login_urls_enabled'); + $this->validate_is_bool($this->use_custom_template, 'use_custom_template'); + if ($this->use_custom_template) { + $this->validate_not_empty($this->custom_template, 'custom_template'); + } - // No need to validate - // 'custom_login_urls_default' => '', + $this->validate_is_bool($this->customize_payment_methods, 'customize_payment_methods'); + if ($this->customize_payment_methods) { + $this->validate_is_array($this->custom_payment_methods, 'custom_payment_methods'); + } + + $this->validate_is_bool($this->customize_profile_fields, 'customize_profile_fields'); + if ($this->customize_profile_fields) { + $this->validate_is_array($this->custom_profile_fields, 'custom_profile_fields'); + } + } + + public function store_meta() + { + $id = $this->ID; + + update_post_meta($id, self::$price_str, MeprUtils::format_float($this->price)); + update_post_meta($id, self::$period_str, $this->period); + update_post_meta($id, self::$period_type_str, $this->period_type); + update_post_meta($id, self::$signup_button_text_str, $this->signup_button_text); + update_post_meta($id, self::$limit_cycles_str, $this->limit_cycles); + update_post_meta($id, self::$limit_cycles_num_str, $this->limit_cycles_num); + update_post_meta($id, self::$limit_cycles_action_str, $this->limit_cycles_action); + update_post_meta($id, self::$limit_cycles_expires_after_str, $this->limit_cycles_expires_after); + update_post_meta($id, self::$limit_cycles_expires_type_str, $this->limit_cycles_expires_type); + update_post_meta($id, self::$trial_str, $this->trial); + update_post_meta($id, self::$trial_days_str, $this->trial_days); + update_post_meta($id, self::$trial_amount_str, $this->trial_amount); + update_post_meta($id, self::$trial_once_str, $this->trial_once); + update_post_meta($id, self::$group_id_str, $this->group_id); + update_post_meta($id, self::$group_order_str, $this->group_order); + update_post_meta($id, self::$who_can_purchase_str, $this->who_can_purchase); + update_post_meta($id, self::$is_highlighted_str, $this->is_highlighted); + update_post_meta($id, self::$pricing_title_str, $this->pricing_title); + update_post_meta($id, self::$pricing_display_str, $this->pricing_display); + update_post_meta($id, self::$custom_price_str, $this->custom_price); + update_post_meta($id, self::$pricing_heading_txt_str, $this->pricing_heading_txt); + update_post_meta($id, self::$pricing_footer_txt_str, $this->pricing_footer_txt); + update_post_meta($id, self::$pricing_button_txt_str, $this->pricing_button_txt); + update_post_meta($id, self::$pricing_button_position_str, $this->pricing_button_position); + update_post_meta($id, self::$pricing_benefits_str, $this->pricing_benefits); + update_post_meta($id, self::$register_price_action_str, $this->register_price_action); + update_post_meta($id, self::$register_price_str, $this->register_price); + update_post_meta($id, self::$thank_you_page_enabled_str, $this->thank_you_page_enabled); + update_post_meta($id, self::$thank_you_page_type_str, $this->thank_you_page_type); + update_post_meta($id, self::$thank_you_message_str, $this->thank_you_message); + update_post_meta($id, self::$thank_you_page_id_str, $this->thank_you_page_id); + update_post_meta($id, self::$custom_login_urls_enabled_str, $this->custom_login_urls_enabled); + update_post_meta($id, self::$custom_login_urls_default_str, $this->custom_login_urls_default); + update_post_meta($id, self::$custom_login_urls_str, $this->custom_login_urls); + update_post_meta($id, self::$expire_type_str, $this->expire_type); + update_post_meta($id, self::$expire_after_str, $this->expire_after); + update_post_meta($id, self::$expire_unit_str, $this->expire_unit); + update_post_meta($id, self::$expire_fixed_str, $this->expire_fixed); + update_post_meta($id, self::$tax_exempt_str, $this->tax_exempt); + update_post_meta($id, self::$tax_class_str, $this->tax_class); + update_post_meta($id, self::$allow_renewal_str, $this->allow_renewal); + update_post_meta($id, self::$access_url_str, $this->access_url); + update_post_meta($id, self::$emails_str, $this->emails); + update_post_meta($id, self::$disable_address_fields_str, $this->disable_address_fields); // mostly for free products + update_post_meta($id, self::$simultaneous_subscriptions_str, $this->simultaneous_subscriptions); + update_post_meta($id, self::$use_custom_template_str, $this->use_custom_template); + update_post_meta($id, self::$custom_template_str, $this->custom_template); + update_post_meta($id, self::$customize_payment_methods_str, $this->customize_payment_methods); + update_post_meta($id, self::$customize_profile_fields_str, $this->customize_profile_fields); + update_post_meta($id, self::$cannot_purchase_message_str, $this->cannot_purchase_message); + update_post_meta($id, self::$plan_code_str, $this->plan_code); + + if ($this->customize_payment_methods) { + update_post_meta($id, self::$custom_payment_methods_str, $this->custom_payment_methods); + } else { + delete_post_meta($id, self::$custom_payment_methods_str); + } + + if ($this->customize_profile_fields) { + update_post_meta($id, self::$custom_profile_fields_str, $this->custom_profile_fields); + } else { + delete_post_meta($id, self::$custom_profile_fields_str); + } + } - if($this->custom_login_urls_enabled) { $this->validate_is_array($this->custom_login_urls,'custom_login_urls'); } + public function is_prorated() + { + $mepr_options = MeprOptions::fetch(); + return($mepr_options->pro_rated_upgrades and $this->is_upgrade_or_downgrade()); + } - $this->validate_is_in_array($this->expire_type, $this->expire_types, 'expire_type'); - if($this->expire_type=='delay') { - $this->validate_is_numeric($this->expire_after, 1, null, 'expire_after'); - $this->validate_is_in_array($this->expire_unit, $this->expire_units, 'expire_unit'); + /** + * Whether this product is tax exempt + * + * @return boolean + */ + public function is_tax_exempt() + { + return (bool) $this->tax_exempt; } - else if($this->expire_type=='fixed') { - $this->validate_is_date($this->expire_fixed, 'expire_fixed'); + + public static function get_one($id) + { + $post = get_post($id); + + if (is_null($post)) { + return false; + } else { + return new MeprProduct($post->ID); + } } - $this->validate_is_bool($this->tax_exempt, 'tax_exempt'); - $this->validate_is_bool($this->allow_renewal, 'allow_renewal'); - - if(!empty($this->access_url)) { $this->validate_is_url($this->access_url, 'access_url'); } - - $this->validate_is_array($this->emails, 'emails'); - - $this->validate_is_bool($this->simultaneous_subscriptions, 'simultaneous_subscriptions'); - - $this->validate_is_bool($this->use_custom_template, 'use_custom_template'); - if($this->use_custom_template) { $this->validate_not_empty($this->custom_template, 'custom_template'); } - - $this->validate_is_bool($this->customize_payment_methods, 'customize_payment_methods'); - if($this->customize_payment_methods) { $this->validate_is_array($this->custom_payment_methods, 'custom_payment_methods'); } - - $this->validate_is_bool($this->customize_profile_fields, 'customize_profile_fields'); - if($this->customize_profile_fields) { $this->validate_is_array($this->custom_profile_fields, 'custom_profile_fields'); } - } - - public function store_meta() { - $id = $this->ID; - - update_post_meta($id, self::$price_str, MeprUtils::format_float($this->price)); - update_post_meta($id, self::$period_str, $this->period); - update_post_meta($id, self::$period_type_str, $this->period_type); - update_post_meta($id, self::$signup_button_text_str, $this->signup_button_text); - update_post_meta($id, self::$limit_cycles_str, $this->limit_cycles); - update_post_meta($id, self::$limit_cycles_num_str, $this->limit_cycles_num); - update_post_meta($id, self::$limit_cycles_action_str, $this->limit_cycles_action); - update_post_meta($id, self::$limit_cycles_expires_after_str, $this->limit_cycles_expires_after); - update_post_meta($id, self::$limit_cycles_expires_type_str, $this->limit_cycles_expires_type); - update_post_meta($id, self::$trial_str, $this->trial); - update_post_meta($id, self::$trial_days_str, $this->trial_days); - update_post_meta($id, self::$trial_amount_str, $this->trial_amount); - update_post_meta($id, self::$trial_once_str, $this->trial_once); - update_post_meta($id, self::$group_id_str, $this->group_id); - update_post_meta($id, self::$group_order_str, $this->group_order); - update_post_meta($id, self::$who_can_purchase_str, $this->who_can_purchase); - update_post_meta($id, self::$is_highlighted_str, $this->is_highlighted); - update_post_meta($id, self::$pricing_title_str, $this->pricing_title); - update_post_meta($id, self::$pricing_display_str, $this->pricing_display); - update_post_meta($id, self::$custom_price_str, $this->custom_price); - update_post_meta($id, self::$pricing_heading_txt_str, $this->pricing_heading_txt); - update_post_meta($id, self::$pricing_footer_txt_str, $this->pricing_footer_txt); - update_post_meta($id, self::$pricing_button_txt_str, $this->pricing_button_txt); - update_post_meta($id, self::$pricing_button_position_str, $this->pricing_button_position); - update_post_meta($id, self::$pricing_benefits_str, $this->pricing_benefits); - update_post_meta($id, self::$register_price_action_str, $this->register_price_action); - update_post_meta($id, self::$register_price_str, $this->register_price); - update_post_meta($id, self::$thank_you_page_enabled_str, $this->thank_you_page_enabled); - update_post_meta($id, self::$thank_you_page_type_str, $this->thank_you_page_type); - update_post_meta($id, self::$thank_you_message_str, $this->thank_you_message); - update_post_meta($id, self::$thank_you_page_id_str, $this->thank_you_page_id); - update_post_meta($id, self::$custom_login_urls_enabled_str, $this->custom_login_urls_enabled); - update_post_meta($id, self::$custom_login_urls_default_str, $this->custom_login_urls_default); - update_post_meta($id, self::$custom_login_urls_str, $this->custom_login_urls); - update_post_meta($id, self::$expire_type_str, $this->expire_type); - update_post_meta($id, self::$expire_after_str, $this->expire_after); - update_post_meta($id, self::$expire_unit_str, $this->expire_unit); - update_post_meta($id, self::$expire_fixed_str, $this->expire_fixed); - update_post_meta($id, self::$tax_exempt_str, $this->tax_exempt); - update_post_meta($id, self::$tax_class_str, $this->tax_class); - update_post_meta($id, self::$allow_renewal_str, $this->allow_renewal); - update_post_meta($id, self::$access_url_str, $this->access_url); - update_post_meta($id, self::$emails_str, $this->emails); - update_post_meta($id, self::$disable_address_fields_str, $this->disable_address_fields); //mostly for free products - update_post_meta($id, self::$simultaneous_subscriptions_str, $this->simultaneous_subscriptions); - update_post_meta($id, self::$use_custom_template_str, $this->use_custom_template); - update_post_meta($id, self::$custom_template_str, $this->custom_template); - update_post_meta($id, self::$customize_payment_methods_str, $this->customize_payment_methods); - update_post_meta($id, self::$customize_profile_fields_str, $this->customize_profile_fields); - update_post_meta($id, self::$cannot_purchase_message_str, $this->cannot_purchase_message); - update_post_meta($id, self::$plan_code_str, $this->plan_code); - - if($this->customize_payment_methods) - update_post_meta($id, self::$custom_payment_methods_str, $this->custom_payment_methods); - else - delete_post_meta($id, self::$custom_payment_methods_str); - - if($this->customize_profile_fields) - update_post_meta($id, self::$custom_profile_fields_str, $this->custom_profile_fields); - else - delete_post_meta($id, self::$custom_profile_fields_str); - } - - public function is_prorated() { - $mepr_options = MeprOptions::fetch(); - return($mepr_options->pro_rated_upgrades and $this->is_upgrade_or_downgrade()); - } - - /** - * Whether this product is tax exempt - * - * @return boolean - */ - public function is_tax_exempt() { - return (boolean) $this->tax_exempt; - } - - public static function get_one($id) { - $post = get_post($id); - - if(is_null($post)) - return false; - else - return new MeprProduct($post->ID); - } - - public static function get_all() { - global $wpdb; - - $q = $wpdb->prepare(" + public static function get_all() + { + global $wpdb; + + $q = $wpdb->prepare( + " SELECT ID FROM {$wpdb->posts} WHERE post_type=%s AND post_status=%s ", - self::$cpt, - 'publish' - ); + self::$cpt, + 'publish' + ); - $ids = $wpdb->get_col($q); + $ids = $wpdb->get_col($q); - $memberships = array(); - foreach($ids as $id) { - $memberships[] = new MeprProduct($id); - } + $memberships = []; + foreach ($ids as $id) { + $memberships[] = new MeprProduct($id); + } - return $memberships; - } - - /** - * Get the total number of published products - * - * @return int - */ - public static function count() { - global $wpdb; - - $query = $wpdb->prepare( - "SELECT COUNT(*) FROM {$wpdb->posts} WHERE post_type = %s AND post_status = 'publish'", - self::$cpt - ); - - return (int) $wpdb->get_var($query); - } - - /** This presents the price as a float, based on the information contained in - * $this, the user_id and $coupon_code passed to it. - * - * If a user_id and a coupon code is present just adjust the price based on - * the user first (if any) and then apply the coupon to the remaining price. - * - * Coupon code needs to be validated using MeprCoupon::is_valid_coupon_code() - * before passing a code to this method - */ - public function adjusted_price($coupon_code = null, $prorate = true) { - $current_user = MeprUtils::get_currentuserinfo(); - $product_price = $this->price; - $mepr_options = MeprOptions::fetch(); - - //Note to future self, we do not want to validate the coupon - //here as it causes major issues if the coupon has expired - //or has reached its usage count max. See notes above this method. - if(!empty($coupon_code)) { - $coupon = MeprCoupon::get_one_from_code($coupon_code); - - if($coupon !== false) { - $product_price = $coupon->apply_discount($product_price, $this->is_one_time_payment(), $this); - } + return $memberships; } - if(!empty($_REQUEST['ca']) || !empty($_REQUEST['mpca_corporate_account_id'])) { - // Signup is a Corporate Account validated in validate_ca_signup and associate_sub_account. - $product_price = 0.00; - } - elseif($prorate && $this->is_one_time_payment() && $this->is_prorated()) { - $grp = $this->group(); - - //Calculate days in this new period - $days_in_new_period = $this->days_in_my_period(); - - if(($old_sub = $current_user->subscription_in_group($grp->ID))) { - $lt = $old_sub->latest_txn(); - - if($lt != false && $lt instanceof MeprTransaction && $lt->id > 0) { - $r = MeprUtils::calculate_proration( - $lt->amount, - $product_price, - $old_sub->days_in_this_period(), - $days_in_new_period, - $old_sub->days_till_expiration(), - $grp->upgrade_path_reset_period, - $old_sub - ); - - //Don't update this below if the latest payment was for 0.00 - if(MeprUtils::format_float($lt->amount) > 0.00) { - $product_price = $r->proration; - } - } - } - //Don't update this below if the latest payment was for 0.00 - elseif($prorate && ($txn = $current_user->lifetime_subscription_in_group($grp->ID)) && MeprUtils::format_float($txn->amount) > 0.00) { - $r = MeprUtils::calculate_proration( - $txn->amount, - $product_price, - $txn->days_in_this_period(), - $days_in_new_period, - $txn->days_till_expiration(), - $grp->upgrade_path_reset_period + /** + * Get the total number of published products + * + * @return integer + */ + public static function count() + { + global $wpdb; + + $query = $wpdb->prepare( + "SELECT COUNT(*) FROM {$wpdb->posts} WHERE post_type = %s AND post_status = 'publish'", + self::$cpt ); - $product_price = $r->proration; - } + return (int) $wpdb->get_var($query); } - $product_price = MeprHooks::apply_filters('mepr_adjusted_price', $product_price, $coupon_code, $this); + /** + * This presents the price as a float, based on the information contained in + * $this, the user_id and $coupon_code passed to it. + * + * If a user_id and a coupon code is present just adjust the price based on + * the user first (if any) and then apply the coupon to the remaining price. + * + * Coupon code needs to be validated using MeprCoupon::is_valid_coupon_code() + * before passing a code to this method + */ + public function adjusted_price($coupon_code = null, $prorate = true) + { + $current_user = MeprUtils::get_currentuserinfo(); + $product_price = $this->price; + $mepr_options = MeprOptions::fetch(); + + // Note to future self, we do not want to validate the coupon + // here as it causes major issues if the coupon has expired + // or has reached its usage count max. See notes above this method. + if (!empty($coupon_code)) { + $coupon = MeprCoupon::get_one_from_code($coupon_code); + + if ($coupon !== false) { + $product_price = $coupon->apply_discount($product_price, $this->is_one_time_payment(), $this); + } + } + + if (!empty($_REQUEST['ca']) || !empty($_REQUEST['mpca_corporate_account_id'])) { + // Signup is a Corporate Account validated in validate_ca_signup and associate_sub_account. + $product_price = 0.00; + } elseif ($prorate && $this->is_one_time_payment() && $this->is_prorated()) { + $grp = $this->group(); + + // Calculate days in this new period + $days_in_new_period = $this->days_in_my_period(); + + if (($old_sub = $current_user->subscription_in_group($grp->ID))) { + $lt = $old_sub->latest_txn(); + + if ($lt != false && $lt instanceof MeprTransaction && $lt->id > 0) { + $r = MeprUtils::calculate_proration( + $lt->amount, + $product_price, + $old_sub->days_in_this_period(), + $days_in_new_period, + $old_sub->days_till_expiration(), + $grp->upgrade_path_reset_period, + $old_sub + ); + + // Don't update this below if the latest payment was for 0.00 + if (MeprUtils::format_float($lt->amount) > 0.00) { + $product_price = $r->proration; + } + } + } elseif ($prorate && ($txn = $current_user->lifetime_subscription_in_group($grp->ID)) && MeprUtils::format_float($txn->amount) > 0.00) { + // Don't update this below if the latest payment was for 0.00 + $r = MeprUtils::calculate_proration( + $txn->amount, + $product_price, + $txn->days_in_this_period(), + $days_in_new_period, + $txn->days_till_expiration(), + $grp->upgrade_path_reset_period + ); + + $product_price = $r->proration; + } + } - return MeprUtils::format_float($product_price); - } + $product_price = MeprHooks::apply_filters('mepr_adjusted_price', $product_price, $coupon_code, $this); - public function is_payment_required($coupon_code = null) { - if($coupon_code && !MeprCoupon::is_valid_coupon_code($coupon_code, $this->ID)) { - $coupon_code = null; + return MeprUtils::format_float($product_price); } - return $this->adjusted_price($coupon_code) > 0.00; - } + public function is_payment_required($coupon_code = null) + { + if ($coupon_code && !MeprCoupon::is_valid_coupon_code($coupon_code, $this->ID)) { + $coupon_code = null; + } - public function days_in_my_period($default = 'lifetime') { - $now = time(); - $new_expires_at = $this->get_expires_at($now); + return $this->adjusted_price($coupon_code) > 0.00; + } - if(is_null($new_expires_at)) { return $default; } + public function days_in_my_period($default = 'lifetime') + { + $now = time(); + $new_expires_at = $this->get_expires_at($now); - $future_date = new DateTime(gmdate('Y-m-d', $new_expires_at)); - $current_date = new DateTime(gmdate('Y-m-d', $now)); - $timediff = $current_date->diff($future_date); + if (is_null($new_expires_at)) { + return $default; + } - return $timediff->days; //# of days difference - } + $future_date = new DateTime(gmdate('Y-m-d', $new_expires_at)); + $current_date = new DateTime(gmdate('Y-m-d', $now)); + $timediff = $current_date->diff($future_date); - /** Gets the value for 'expires_at' for the given created_at time for this membership. */ - public function get_expires_at($created_at = null, $check_user = true) - { - $mepr_options = MeprOptions::fetch(); + return $timediff->days; // # of days difference + } - if(is_null($created_at)) { $created_at = time(); } + /** + * Gets the value for 'expires_at' for the given created_at time for this membership. + */ + public function get_expires_at($created_at = null, $check_user = true) + { + $mepr_options = MeprOptions::fetch(); - $user = MeprUtils::get_currentuserinfo(); - $expires_at = $created_at; - $period = $this->period; + if (is_null($created_at)) { + $created_at = time(); + } - switch($this->period_type) - { - case 'days': - $expires_at += MeprUtils::days($period) + MeprUtils::days($mepr_options->grace_expire_days); - break; - case 'weeks': - $expires_at += MeprUtils::weeks($period) + MeprUtils::days($mepr_options->grace_expire_days); - break; - case 'months': - $expires_at += MeprUtils::months($period, $created_at) + MeprUtils::days($mepr_options->grace_expire_days); - break; - case 'years': - $expires_at += MeprUtils::years($period, $created_at) + MeprUtils::days($mepr_options->grace_expire_days); - break; - default: // one-time payment - if($this->expire_type=='delay') { - if($check_user && MeprUtils::is_user_logged_in()) { - //Handle renewals - if($this->is_renewal() && ($last_txn = $this->get_last_active_txn($user->ID))) { - $expires_at = $created_at = strtotime($last_txn->expires_at); - } - } + $user = MeprUtils::get_currentuserinfo(); + $expires_at = $created_at; + $period = $this->period; - switch($this->expire_unit) { - case 'days': - $expires_at += MeprUtils::days($this->expire_after); + switch ($this->period_type) { + case 'days': + $expires_at += MeprUtils::days($period) + MeprUtils::days($mepr_options->grace_expire_days); break; - case 'weeks': - $expires_at += MeprUtils::weeks($this->expire_after); + case 'weeks': + $expires_at += MeprUtils::weeks($period) + MeprUtils::days($mepr_options->grace_expire_days); break; - case 'months': - $expires_at += MeprUtils::months($this->expire_after, $created_at); + case 'months': + $expires_at += MeprUtils::months($period, $created_at) + MeprUtils::days($mepr_options->grace_expire_days); break; - case 'years': - $expires_at += MeprUtils::years($this->expire_after, $created_at); - } - } - elseif($this->expire_type=='fixed') { - $expires_at = strtotime( $this->expire_fixed ); - $now = time(); - //Make sure we adjust the year if the membership is a renewable type and the user forgot to bump up the year - if($this->allow_renewal) { - while($now > $expires_at) { - $expires_at += MeprUtils::years(1); - } - } - //Actually handle renewals - if($check_user && $this->is_renewal() && ($last_txn = $this->get_last_active_txn($user->ID))) { - $expires_at_date = date_create($last_txn->expires_at, new DateTimeZone('UTC')); - - if($expires_at_date instanceof DateTime) { - $expires_at_date->modify('+1 year'); - $expires_at = $expires_at_date->format('U'); - } - else { - $expires_at = strtotime($last_txn->expires_at) + MeprUtils::years(1); - } - } - } - else { // lifetime - $expires_at = null; - } - } + case 'years': + $expires_at += MeprUtils::years($period, $created_at) + MeprUtils::days($mepr_options->grace_expire_days); + break; + default: // one-time payment + if ($this->expire_type == 'delay') { + if ($check_user && MeprUtils::is_user_logged_in()) { + // Handle renewals + if ($this->is_renewal() && ($last_txn = $this->get_last_active_txn($user->ID))) { + $expires_at = $created_at = strtotime($last_txn->expires_at); + } + } + + switch ($this->expire_unit) { + case 'days': + $expires_at += MeprUtils::days($this->expire_after); + break; + case 'weeks': + $expires_at += MeprUtils::weeks($this->expire_after); + break; + case 'months': + $expires_at += MeprUtils::months($this->expire_after, $created_at); + break; + case 'years': + $expires_at += MeprUtils::years($this->expire_after, $created_at); + } + } elseif ($this->expire_type == 'fixed') { + $expires_at = strtotime($this->expire_fixed); + $now = time(); + // Make sure we adjust the year if the membership is a renewable type and the user forgot to bump up the year + if ($this->allow_renewal) { + while ($now > $expires_at) { + $expires_at += MeprUtils::years(1); + } + } + // Actually handle renewals + if ($check_user && $this->is_renewal() && ($last_txn = $this->get_last_active_txn($user->ID))) { + $expires_at_date = date_create($last_txn->expires_at, new DateTimeZone('UTC')); + + if ($expires_at_date instanceof DateTime) { + $expires_at_date->modify('+1 year'); + $expires_at = $expires_at_date->format('U'); + } else { + $expires_at = strtotime($last_txn->expires_at) + MeprUtils::years(1); + } + } + } else { // lifetime + $expires_at = null; + } + } - return $expires_at; - } + return $expires_at; + } - public static function get_pricing_page_product_ids() - { - global $wpdb; + public static function get_pricing_page_product_ids() + { + global $wpdb; - $q = "SELECT p.ID, p.menu_order + $q = "SELECT p.ID, p.menu_order FROM {$wpdb->postmeta} AS m INNER JOIN {$wpdb->posts} AS p ON p.ID = m.post_id WHERE m.meta_key = %s AND m.meta_value = 1 ORDER BY p.menu_order, p.ID"; - return $wpdb->get_col($wpdb->prepare($q, self::$show_on_pricing_str)); - } + return $wpdb->get_col($wpdb->prepare($q, self::$show_on_pricing_str)); + } - public function is_one_time_payment() { - return MeprHooks::apply_filters('mepr_product_is_one_time_payment', $this->period_type == 'lifetime' || $this->price == 0.00); - } + public function is_one_time_payment() + { + return MeprHooks::apply_filters('mepr_product_is_one_time_payment', $this->period_type == 'lifetime' || $this->price == 0.00); + } - //Just for checking if the membership type is a renewable type - //Not to be used when is_renewal() should be used instead - public function is_renewable() { - return (($this->expire_type=='delay' || $this->expire_type=='fixed') && $this->allow_renewal); - } + // Just for checking if the membership type is a renewable type + // Not to be used when is_renewal() should be used instead + public function is_renewable() + { + return (($this->expire_type == 'delay' || $this->expire_type == 'fixed') && $this->allow_renewal); + } - public function is_renewal() { - global $user_ID; + public function is_renewal() + { + global $user_ID; - if(MeprUtils::is_user_logged_in()) { - $user = new MeprUser($user_ID); - } + if (MeprUtils::is_user_logged_in()) { + $user = new MeprUser($user_ID); + } - return (MeprUtils::is_user_logged_in() && + return (MeprUtils::is_user_logged_in() && $user->is_already_subscribed_to($this->ID) && - ($this->expire_type=='delay' || $this->expire_type=='fixed') && + ($this->expire_type == 'delay' || $this->expire_type == 'fixed') && $this->allow_renewal); - } + } - public function can_you_buy_me() { - global $user_ID; + public function can_you_buy_me() + { + global $user_ID; - if( ! is_null( ( $override = MeprHooks::apply_filters( 'mepr-can-you-buy-me-override', null, $this ) ) ) ) { - return $override; - } + if (! is_null(( $override = MeprHooks::apply_filters('mepr-can-you-buy-me-override', null, $this) ))) { + return $override; + } - // Admins can see & purchase anything - if(MeprUtils::is_logged_in_and_an_admin()) { - return true; - } + // Admins can see & purchase anything + if (MeprUtils::is_logged_in_and_an_admin()) { + return true; + } - if(MeprUtils::is_user_logged_in()) { - $user = new MeprUser($user_ID); - } + if (MeprUtils::is_user_logged_in()) { + $user = new MeprUser($user_ID); + } - //Make sure user hasn't already subscribed to this membership first - if(MeprUtils::is_user_logged_in() && - $user->is_already_subscribed_to($this->ID) && - !$this->simultaneous_subscriptions && - !$this->allow_renewal) { - return false; - } + // Make sure user hasn't already subscribed to this membership first + if ( + MeprUtils::is_user_logged_in() && + $user->is_already_subscribed_to($this->ID) && + !$this->simultaneous_subscriptions && + !$this->allow_renewal + ) { + return false; + } - if(empty($this->who_can_purchase)) { - return true; //No rules exist so everyone can purchase - } + if (empty($this->who_can_purchase)) { + return true; // No rules exist so everyone can purchase + } - // Do not allow upgrades/downgrades during a pro-rated trial period from a previous upgrade/downgrade - if( MeprUtils::is_user_logged_in() && isset($user) && $user && ($group = $this->group()) !== false) { - $sub_in_group = $user->subscription_in_group($group->ID); - if($sub_in_group !== false && $sub_in_group->prorated_trial && $sub_in_group->in_trial()) { - return MeprHooks::apply_filters('mepr-allow-multiple-upgrades-downgrades', false, $user, $sub_in_group, $this); - } - } + // Do not allow upgrades/downgrades during a pro-rated trial period from a previous upgrade/downgrade + if (MeprUtils::is_user_logged_in() && isset($user) && $user && ($group = $this->group()) !== false) { + $sub_in_group = $user->subscription_in_group($group->ID); + if ($sub_in_group !== false && $sub_in_group->prorated_trial && $sub_in_group->in_trial()) { + return MeprHooks::apply_filters('mepr-allow-multiple-upgrades-downgrades', false, $user, $sub_in_group, $this); + } + } - foreach($this->who_can_purchase as $who) { - //Give Developers a chance to hook in here - //Return true or false if you run your own custom handling here - //Otherwise return string 'no_custom' if MemberPress should handle the processing - if(($custom = MeprHooks::apply_filters('mepr-who-can-purchase-custom-check', 'no_custom', $who, $this)) !== 'no_custom' && is_bool($custom)) { - return $custom; - } + foreach ($this->who_can_purchase as $who) { + // Give Developers a chance to hook in here + // Return true or false if you run your own custom handling here + // Otherwise return string 'no_custom' if MemberPress should handle the processing + if (($custom = MeprHooks::apply_filters('mepr-who-can-purchase-custom-check', 'no_custom', $who, $this)) !== 'no_custom' && is_bool($custom)) { + return $custom; + } - if($who->user_type == 'disabled') { - return false; - } + if ($who->user_type == 'disabled') { + return false; + } - if($who->user_type == 'everyone') { - return true; - } + if ($who->user_type == 'everyone') { + return true; + } - if($who->user_type == 'guests' && !MeprUtils::is_user_logged_in()) { - return true; //If not a logged in member they can purchase - } + if ($who->user_type == 'guests' && !MeprUtils::is_user_logged_in()) { + return true; // If not a logged in member they can purchase + } - if($who->user_type == 'members' && MeprUtils::is_user_logged_in()) { - if($user->can_user_purchase($who, $this->ID)) { - return true; + if ($who->user_type == 'members' && MeprUtils::is_user_logged_in()) { + if ($user->can_user_purchase($who, $this->ID)) { + return true; + } + } } - } - } - return false; //If we make it here, nothing applied so let's return false - } + return false; // If we make it here, nothing applied so let's return false + } - // This is really only used for annual renewals currently - // It has some special code for fallback transactions (Downgrade Path feature of Groups) - // So be careful when using this function if you need to includ fallback transactions - public function get_last_active_txn($user_id) { - global $wpdb; - $mepr_db = new MeprDb(); + // This is really only used for annual renewals currently + // It has some special code for fallback transactions (Downgrade Path feature of Groups) + // So be careful when using this function if you need to includ fallback transactions + public function get_last_active_txn($user_id) + { + global $wpdb; + $mepr_db = new MeprDb(); - $q = "SELECT tr.id AS id + $q = "SELECT tr.id AS id FROM {$mepr_db->transactions} AS tr WHERE tr.user_id=%d AND tr.product_id=%d @@ -640,7 +674,7 @@ public function get_last_active_txn($user_id) { ORDER BY tr.created_at DESC LIMIT 1"; - $lq = "SELECT tr.id AS id + $lq = "SELECT tr.id AS id FROM {$mepr_db->transactions} AS tr WHERE tr.user_id=%d AND tr.product_id=%d @@ -650,216 +684,246 @@ public function get_last_active_txn($user_id) { ORDER BY tr.created_at DESC LIMIT 1"; - $q = $wpdb->prepare($q, $user_id, $this->ID, MeprTransaction::$complete_str, MeprUtils::db_now()); - $lq = $wpdb->prepare($lq, $user_id, $this->ID, MeprTransaction::$complete_str, MeprUtils::db_lifetime(), MeprTransaction::$fallback_str); + $q = $wpdb->prepare($q, $user_id, $this->ID, MeprTransaction::$complete_str, MeprUtils::db_now()); + $lq = $wpdb->prepare($lq, $user_id, $this->ID, MeprTransaction::$complete_str, MeprUtils::db_lifetime(), MeprTransaction::$fallback_str); - if(($txn_id = $wpdb->get_var($lq))) { // Try for lifetimes - return new MeprTransaction($txn_id); - } - else if(($txn_id = $wpdb->get_var($q))) { // Try for expiring - return new MeprTransaction($txn_id); + if (($txn_id = $wpdb->get_var($lq))) { // Try for lifetimes + return new MeprTransaction($txn_id); + } elseif (($txn_id = $wpdb->get_var($q))) { // Try for expiring + return new MeprTransaction($txn_id); + } else { + // TODO: Maybe throw an exception here at some point + return false; + } } - else { - // TODO: Maybe throw an exception here at some point - return false; + + public function group() + { + // Don't do static caching stuff here + if (!isset($this->group_id) or empty($this->group_id)) { + return false; + } + + return new MeprGroup($this->group_id); } - } - public function group() { - //Don't do static caching stuff here - if(!isset($this->group_id) or empty($this->group_id)) { return false; } + public function group_url() + { + if ( + $grp = $this->group() and + !$grp->pricing_page_disabled + ) { + return $grp->url(); + } else { + return $this->url(); + } + } - return new MeprGroup($this->group_id); - } + // Determines if this is a membership upgrade + public function is_upgrade() + { + return $this->is_upgrade_or_downgrade('upgrade'); + } - public function group_url() { - if( $grp = $this->group() and - !$grp->pricing_page_disabled ) { - return $grp->url(); + // Determines if this is a membership downgrade + public function is_downgrade() + { + return $this->is_upgrade_or_downgrade('downgrade'); } - else { - return $this->url(); + + // Determines if this is a membership upgrade for a certain user + public function is_upgrade_for($user_id) + { + return $this->is_upgrade_or_downgrade_for($user_id, 'upgrade'); } - } - - // Determines if this is a membership upgrade - public function is_upgrade() - { - return $this->is_upgrade_or_downgrade('upgrade'); - } - - // Determines if this is a membership downgrade - public function is_downgrade() { - return $this->is_upgrade_or_downgrade('downgrade'); - } - - // Determines if this is a membership upgrade for a certain user - public function is_upgrade_for($user_id) { - return $this->is_upgrade_or_downgrade_for($user_id, 'upgrade'); - } - - // Determines if this is a membership downgrade for a certain user - public function is_downgrade_for($user_id) { - return $this->is_upgrade_or_downgrade_for($user_id, 'downgrade'); - } - - public function is_upgrade_or_downgrade($type=false, $usr=false) { - if($usr === false) { - $usr = MeprUtils::get_currentuserinfo(); + + // Determines if this is a membership downgrade for a certain user + public function is_downgrade_for($user_id) + { + return $this->is_upgrade_or_downgrade_for($user_id, 'downgrade'); } - return ($usr && $this->is_upgrade_or_downgrade_for($usr->ID, $type)); // Must be an upgrade/downgrade for the user - } - // Determines if this is a membership upgrade - public function is_upgrade_or_downgrade_for($user_id, $type = false) { - $usr = new MeprUser($user_id); - $grp = $this->group(); + public function is_upgrade_or_downgrade($type = false, $usr = false) + { + if ($usr === false) { + $usr = MeprUtils::get_currentuserinfo(); + } + return ($usr && $this->is_upgrade_or_downgrade_for($usr->ID, $type)); // Must be an upgrade/downgrade for the user + } - // not part of a group ... not an upgrade - if(!$grp) { return false; } + // Determines if this is a membership upgrade + public function is_upgrade_or_downgrade_for($user_id, $type = false) + { + $usr = new MeprUser($user_id); + $grp = $this->group(); - // no upgrade path here ... not an upgrade - if(!$grp->is_upgrade_path) { return false; } + // not part of a group ... not an upgrade + if (!$grp) { + return false; + } - $prds = $usr->active_product_subscriptions('products', true); + // no upgrade path here ... not an upgrade + if (!$grp->is_upgrade_path) { + return false; + } - if(!empty($prds)) { - foreach($prds as $p) { - if( $g = $p->group() and $g instanceof MeprGroup and - $g->ID == $grp->ID and $this->ID != $p->ID ) { - if( $type===false ) - return true; - else if( $type == 'upgrade' ) - return $this->group_order > $p->group_order; - else if( $type == 'downgrade' ) - return $this->group_order < $p->group_order; + $prds = $usr->active_product_subscriptions('products', true); + + if (!empty($prds)) { + foreach ($prds as $p) { + if ( + $g = $p->group() and $g instanceof MeprGroup and + $g->ID == $grp->ID and $this->ID != $p->ID + ) { + if ($type === false) { + return true; + } elseif ($type == 'upgrade') { + return $this->group_order > $p->group_order; + } elseif ($type == 'downgrade') { + return $this->group_order < $p->group_order; + } + } + } } - } - } - return false; - } + return false; + } - public static function cleanup_db() { - global $wpdb; - $date = time(); - $last_run = get_option(self::$last_run_str, 0); //Prevents all this code from executing on every page load + public static function cleanup_db() + { + global $wpdb; + $date = time(); + $last_run = get_option(self::$last_run_str, 0); // Prevents all this code from executing on every page load - if(($date - $last_run) > 86400) { //Runs once at most once a day - update_option(self::$last_run_str, $date); - $sq1 = "SELECT ID + if (($date - $last_run) > 86400) { // Runs once at most once a day + update_option(self::$last_run_str, $date); + $sq1 = "SELECT ID FROM {$wpdb->posts} - WHERE post_type = '".self::$cpt."' AND + WHERE post_type = '" . self::$cpt . "' AND post_status = 'auto-draft'"; - $sq1_res = $wpdb->get_col($sq1); - if(!empty($sq1_res)) { - $post_ids = implode(',', $sq1_res); - $q1 = "DELETE + $sq1_res = $wpdb->get_col($sq1); + if (!empty($sq1_res)) { + $post_ids = implode(',', $sq1_res); + $q1 = "DELETE FROM {$wpdb->postmeta} WHERE post_id IN ({$post_ids})"; - $q2 = "DELETE + $q2 = "DELETE FROM {$wpdb->posts} - WHERE post_type = '".self::$cpt."' AND + WHERE post_type = '" . self::$cpt . "' AND post_status = 'auto-draft'"; - $wpdb->query($q1); - $wpdb->query($q2); - } + $wpdb->query($q1); + $wpdb->query($q2); + } + } } - } - public function get_page_template() { - if($this->use_custom_template) { - return locate_template($this->custom_template); - } + public function get_page_template() + { + if ($this->use_custom_template) { + return locate_template($this->custom_template); + } - return null; - } + return null; + } - /* - public static function template_search_path() { - return array( 'page_memberpressproduct.php', + /* + public static function template_search_path() { + return array( 'page_memberpressproduct.php', 'single-memberpressproduct.php', 'page.php', 'custom_template.php', 'single.php', 'index.php' ); - } - */ - - public function payment_methods() { - $mepr_options = MeprOptions::fetch(); + } + */ - $pms = $mepr_options->payment_methods(); + public function payment_methods() + { + $mepr_options = MeprOptions::fetch(); - unset($pms['free']); - unset($pms['manual']); + $pms = $mepr_options->payment_methods(); - $pmkeys = array_keys($pms); + unset($pms['free']); + unset($pms['manual']); - if( isset($this->custom_payment_methods) and - !is_null($this->custom_payment_methods) and - is_array($this->custom_payment_methods) ) { - return array_intersect($this->custom_payment_methods, $pmkeys); - } + $pmkeys = array_keys($pms); - return $pmkeys; - } + if ( + isset($this->custom_payment_methods) and + !is_null($this->custom_payment_methods) and + is_array($this->custom_payment_methods) + ) { + return array_intersect($this->custom_payment_methods, $pmkeys); + } - public function edit_url($args = '') { - if(isset($this->ID) && $this->post_type==self::$cpt) { - return get_edit_post_link($this->ID); + return $pmkeys; } - else { - return ''; + + public function edit_url($args = '') + { + if (isset($this->ID) && $this->post_type == self::$cpt) { + return get_edit_post_link($this->ID); + } else { + return ''; + } } - } - public function url($args = '', $modify_if_https = false) { - if(isset($this->ID)) { - $url = MeprUtils::get_permalink($this->ID).$args; + public function url($args = '', $modify_if_https = false) + { + if (isset($this->ID)) { + $url = MeprUtils::get_permalink($this->ID) . $args; - if(MeprUtils::is_ssl() && $modify_if_https) { - $url = preg_replace('!^http:!','https:',$url); - } - $url = MeprHooks::apply_filters('mepr-product-url', $url, $this, $args, $modify_if_https); + if (MeprUtils::is_ssl() && $modify_if_https) { + $url = preg_replace('!^http:!', 'https:', $url); + } + $url = MeprHooks::apply_filters('mepr-product-url', $url, $this, $args, $modify_if_https); - return $url; - } - else { - return ''; + return $url; + } else { + return ''; + } } - } - public function manual_append_signup() { - return preg_match('~\[\s*mepr-(product|membership)-registration-form\s*\]~',$this->post_content); - } + public function manual_append_signup() + { + return preg_match('~\[\s*mepr-(product|membership)-registration-form\s*\]~', $this->post_content); + } - public function custom_profile_fields() { - $mepr_options = MeprOptions::fetch(); - $fields = array(); + public function custom_profile_fields() + { + $mepr_options = MeprOptions::fetch(); + $fields = []; - if(!$this->customize_profile_fields) - return $mepr_options->custom_fields; + if (!$this->customize_profile_fields) { + return $mepr_options->custom_fields; + } - foreach($mepr_options->custom_fields as $row) - if(in_array($row->field_key, $this->custom_profile_fields)) - $fields[] = $row; + foreach ($mepr_options->custom_fields as $row) { + if (in_array($row->field_key, $this->custom_profile_fields)) { + $fields[] = $row; + } + } - return $fields; - } + return $fields; + } - /** This function is to be used to determine if a trial should be allowed. - * The current idea here is that if the user has - */ - public function trial_is_expired() { - global $wpdb; + /** + * This function is to be used to determine if a trial should be allowed. + * The current idea here is that if the user has + */ + public function trial_is_expired() + { + global $wpdb; - $mepr_db = MeprDb::fetch(); + $mepr_db = MeprDb::fetch(); - if($this->trial && MeprUtils::is_user_logged_in() && - ($current_user = MeprUtils::get_currentuserinfo())) { - $q = $wpdb->prepare(" + if ( + $this->trial && MeprUtils::is_user_logged_in() && + ($current_user = MeprUtils::get_currentuserinfo()) + ) { + $q = $wpdb->prepare( + " SELECT COUNT(*) FROM {$mepr_db->transactions} AS t WHERE t.user_id=%d @@ -867,212 +931,232 @@ public function trial_is_expired() { AND t.txn_type IN (%s,%s) AND t.status IN (%s,%s,%s) ", - $current_user->ID, - $this->ID, - MeprTransaction::$subscription_confirmation_str, - MeprTransaction::$payment_str, - MeprTransaction::$complete_str, - MeprTransaction::$refunded_str, - MeprTransaction::$confirmed_str - ); - - $already_trialled = $wpdb->get_var($q); - - return ($already_trialled > 0); - } + $current_user->ID, + $this->ID, + MeprTransaction::$subscription_confirmation_str, + MeprTransaction::$payment_str, + MeprTransaction::$complete_str, + MeprTransaction::$refunded_str, + MeprTransaction::$confirmed_str + ); + + $already_trialled = $wpdb->get_var($q); + + return ($already_trialled > 0); + } - return false; - } + return false; + } - public static function is_product_page($post) { - $return = false; + public static function is_product_page($post) + { + $return = false; - if( is_object($post) && - ( ( property_exists( $post, 'post_type' ) && + if ( + is_object($post) && + ( ( property_exists($post, 'post_type') && $post->post_type == MeprProduct::$cpt && $prd = new MeprProduct($post->ID) ) || - ( preg_match( '~\[mepr-(product|membership)-registration-form\s+(product_)?id=[\"\\\'](\d+)[\"\\\']~', - $post->post_content, $m ) && - isset($m[1]) && $prd = new MeprProduct( $m[1] ) ) ) ) { - $return = $prd; + ( preg_match( + '~\[mepr-(product|membership)-registration-form\s+(product_)?id=[\"\\\'](\d+)[\"\\\']~', + $post->post_content, + $m + ) && + isset($m[1]) && $prd = new MeprProduct($m[1]) ) ) + ) { + $return = $prd; + } + + return MeprHooks::apply_filters('mepr-is-product-page', $return, $post); } - return MeprHooks::apply_filters( 'mepr-is-product-page', $return, $post ); - } + public static function get_highest_menu_order_active_membership_by_user($user_id) + { + global $wpdb; + + $user = new MeprUser($user_id); + + if ((int)$user->ID === 0) { + return false; + } + + $active_memberships = array_unique($user->active_product_subscriptions('ids'), true); - public static function get_highest_menu_order_active_membership_by_user( $user_id ) { - global $wpdb; + if (empty($active_memberships)) { + return false; + } - $user = new MeprUser( $user_id ); + $in = '%d'; + if (count($active_memberships) > 1) { + $placeholders = array_fill(0, count($active_memberships), '%d'); + $in = implode(',', $placeholders); // Convert to comma separated string if > 1 + } - if( (int)$user->ID === 0 ) { return false; } + $q = $wpdb->prepare("SELECT ID FROM {$wpdb->posts} WHERE ID IN({$in}) ORDER BY menu_order DESC, ID DESC LIMIT 1", $active_memberships); - $active_memberships = array_unique( $user->active_product_subscriptions( 'ids' ), true ); + $result = $wpdb->get_var($q); - if( empty( $active_memberships ) ) { return false; } + return ( ! is_null($result) ) ? $result : false; + } - $in = '%d'; - if( count( $active_memberships ) > 1 ) { - $placeholders = array_fill( 0, count( $active_memberships ), '%d' ); - $in = implode( ',', $placeholders ); // Convert to comma separated string if > 1 + /** + * Get the Stripe Product ID + * + * @param string $gateway_id The gateway ID + * @return string|false + */ + public function get_stripe_product_id($gateway_id) + { + return get_post_meta($this->ID, '_mepr_stripe_product_id_' . $gateway_id, true); } - $q = $wpdb->prepare( "SELECT ID FROM {$wpdb->posts} WHERE ID IN({$in}) ORDER BY menu_order DESC, ID DESC LIMIT 1", $active_memberships ); - - $result = $wpdb->get_var( $q ); - - return ( ! is_null( $result ) ) ? $result : false; - } - - /** - * Get the Stripe Product ID - * - * @param string $gateway_id The gateway ID - * @return string|false - */ - public function get_stripe_product_id($gateway_id) { - return get_post_meta($this->ID, '_mepr_stripe_product_id_' . $gateway_id, true); - } - - /** - * Get the Stripe Initial payment product ID - * - * @param string $gateway_id The gateway ID - * @return string|false - */ - public function get_stripe_initial_payment_product_id($gateway_id) { - return get_post_meta($this->ID, '_mepr_stripe_initial_payment_product_id_' . $gateway_id, true); - } - - /** - * Set the Stripe Product ID - * - * @param string $gateway_id The gateway ID - * @param string $product_id The Stripe Product ID - */ - public function set_stripe_product_id($gateway_id, $product_id) { - update_post_meta($this->ID, '_mepr_stripe_product_id_' . $gateway_id, $product_id); - } - - /** - * Set the Stripe Initial Payment Product ID - * - * @param string $gateway_id The gateway ID - * @param string $product_id The Stripe Product ID - */ - public function set_stripe_initial_payment_product_id($gateway_id, $product_id) { - update_post_meta($this->ID, '_mepr_stripe_initial_payment_product_id_' . $gateway_id, $product_id); - } - - /** - * Get the Stripe Plan ID for this product's current payment terms - * - * @param string $gateway_id The gateway ID - * @param string $amount The payment amount (excluding tax) - * @return string|false - */ - public function get_stripe_plan_id($gateway_id, $amount) { - $meta_key = sprintf('_mepr_stripe_plan_id_%s_%s', $gateway_id, $this->terms_hash($amount)); - - $plan_id = get_post_meta($this->ID, $meta_key, true); - - return MeprHooks::apply_filters('mepr-product-get-stripe-plan-id', $plan_id, $this, $gateway_id, $amount); - } - - /** - * Set the Stripe Plan ID for this product's current payment terms - * - * @param string $gateway_id The gateway ID - * @param string $amount The payment amount (excluding tax) - * @param string $plan_id The Stripe Plan ID - */ - public function set_stripe_plan_id($gateway_id, $amount, $plan_id) { - $meta_key = sprintf('_mepr_stripe_plan_id_%s_%s', $gateway_id, $this->terms_hash($amount)); - - update_post_meta($this->ID, $meta_key, $plan_id); - } - - /** - * Get the hash of the payment terms - * - * If this hash changes then a different Stripe Plan will be created. - * - * @param string $amount - * @return string - */ - private function terms_hash($amount) { - $mepr_options = MeprOptions::fetch(); - - $terms = [ - 'currency' => $mepr_options->currency_code, - 'amount' => $amount, - 'period' => $this->period, - 'period_type' => $this->period_type - ]; - - return md5(serialize($terms)); - } - - /** - * Delete the Stripe Product ID - * - * @param string $gateway_id The gateway ID - * @param string $product_id The Stripe Product ID - */ - public static function delete_stripe_product_id($gateway_id, $product_id) { - if(is_string($product_id) && $product_id !== '') { - delete_metadata('post', null, '_mepr_stripe_product_id_' . $gateway_id, $product_id, true); + /** + * Get the Stripe Initial payment product ID + * + * @param string $gateway_id The gateway ID + * @return string|false + */ + public function get_stripe_initial_payment_product_id($gateway_id) + { + return get_post_meta($this->ID, '_mepr_stripe_initial_payment_product_id_' . $gateway_id, true); } - } - - /** - * Delete the Stripe Plan ID - * - * @param string $gateway_id The gateway ID - * @param string $plan_id The Stripe Plan ID - */ - public static function delete_stripe_plan_id($gateway_id, $plan_id) { - if(!is_string($plan_id) || $plan_id === '') { - return; + + /** + * Set the Stripe Product ID + * + * @param string $gateway_id The gateway ID + * @param string $product_id The Stripe Product ID + */ + public function set_stripe_product_id($gateway_id, $product_id) + { + update_post_meta($this->ID, '_mepr_stripe_product_id_' . $gateway_id, $product_id); } - global $wpdb; + /** + * Set the Stripe Initial Payment Product ID + * + * @param string $gateway_id The gateway ID + * @param string $product_id The Stripe Product ID + */ + public function set_stripe_initial_payment_product_id($gateway_id, $product_id) + { + update_post_meta($this->ID, '_mepr_stripe_initial_payment_product_id_' . $gateway_id, $product_id); + } - $query = $wpdb->prepare( - "SELECT meta_id FROM {$wpdb->postmeta} WHERE meta_key LIKE %s AND meta_value = %s", - $wpdb->esc_like('_mepr_stripe_plan_id_' . $gateway_id) . '%', - $plan_id - ); + /** + * Get the Stripe Plan ID for this product's current payment terms + * + * @param string $gateway_id The gateway ID + * @param string $amount The payment amount (excluding tax) + * @return string|false + */ + public function get_stripe_plan_id($gateway_id, $amount) + { + $meta_key = sprintf('_mepr_stripe_plan_id_%s_%s', $gateway_id, $this->terms_hash($amount)); - $meta_ids = $wpdb->get_col($query); + $plan_id = get_post_meta($this->ID, $meta_key, true); - if(is_array($meta_ids) && count($meta_ids)) { - foreach($meta_ids as $meta_id) { - delete_metadata_by_mid('post', $meta_id); - } + return MeprHooks::apply_filters('mepr-product-get-stripe-plan-id', $plan_id, $this, $gateway_id, $amount); } - } - - /** - * Get the array of order bumps chosen for this product - * - * @return MeprProduct[] - */ - public function get_order_bumps() { - $product_ids = get_post_meta($this->ID, '_mepr_order_bumps', true); - $order_bumps = []; - - if(is_array($product_ids)) { - foreach($product_ids as $product_id) { - $product = new MeprProduct((int) $product_id); - - if($product->ID > 0) { - $order_bumps[] = $product; + + /** + * Set the Stripe Plan ID for this product's current payment terms + * + * @param string $gateway_id The gateway ID + * @param string $amount The payment amount (excluding tax) + * @param string $plan_id The Stripe Plan ID + */ + public function set_stripe_plan_id($gateway_id, $amount, $plan_id) + { + $meta_key = sprintf('_mepr_stripe_plan_id_%s_%s', $gateway_id, $this->terms_hash($amount)); + + update_post_meta($this->ID, $meta_key, $plan_id); + } + + /** + * Get the hash of the payment terms + * + * If this hash changes then a different Stripe Plan will be created. + * + * @param string $amount + * @return string + */ + private function terms_hash($amount) + { + $mepr_options = MeprOptions::fetch(); + + $terms = [ + 'currency' => $mepr_options->currency_code, + 'amount' => $amount, + 'period' => $this->period, + 'period_type' => $this->period_type, + ]; + + return md5(serialize($terms)); + } + + /** + * Delete the Stripe Product ID + * + * @param string $gateway_id The gateway ID + * @param string $product_id The Stripe Product ID + */ + public static function delete_stripe_product_id($gateway_id, $product_id) + { + if (is_string($product_id) && $product_id !== '') { + delete_metadata('post', null, '_mepr_stripe_product_id_' . $gateway_id, $product_id, true); } - } } - return $order_bumps; - } + /** + * Delete the Stripe Plan ID + * + * @param string $gateway_id The gateway ID + * @param string $plan_id The Stripe Plan ID + */ + public static function delete_stripe_plan_id($gateway_id, $plan_id) + { + if (!is_string($plan_id) || $plan_id === '') { + return; + } + + global $wpdb; + + $query = $wpdb->prepare( + "SELECT meta_id FROM {$wpdb->postmeta} WHERE meta_key LIKE %s AND meta_value = %s", + $wpdb->esc_like('_mepr_stripe_plan_id_' . $gateway_id) . '%', + $plan_id + ); + $meta_ids = $wpdb->get_col($query); + + if (is_array($meta_ids) && count($meta_ids)) { + foreach ($meta_ids as $meta_id) { + delete_metadata_by_mid('post', $meta_id); + } + } + } + + /** + * Get the array of order bumps chosen for this product + * + * @return MeprProduct[] + */ + public function get_order_bumps() + { + $product_ids = get_post_meta($this->ID, '_mepr_order_bumps', true); + $order_bumps = []; + + if (is_array($product_ids)) { + foreach ($product_ids as $product_id) { + $product = new MeprProduct((int) $product_id); + + if ($product->ID > 0) { + $order_bumps[] = $product; + } + } + } + + return $order_bumps; + } } //End class diff --git a/app/models/MeprReminder.php b/app/models/MeprReminder.php index 1349adf..f62c4b8 100644 --- a/app/models/MeprReminder.php +++ b/app/models/MeprReminder.php @@ -1,202 +1,226 @@ load_cpt( - $obj, - self::$cpt, - array( - 'trigger_length' => 1, - 'trigger_interval' => 'days', - 'trigger_timing' => 'before', - 'trigger_event' => 'sub-expires', - 'filter_products' => false, //Send only for specific memberships? - 'products' => array(), //Empty array means ALL memberships - 'emails' => array() - ) - ); - - $this->trigger_intervals = array('hours','days','weeks','months','years'); - $this->trigger_timings = array('before','after'); - $this->trigger_events = array( - 'sub-expires', - 'sub-renews', - 'cc-expires', - 'member-signup', - 'signup-abandoned', - 'sub-trial-ends' - ); - - $this->event_actions = array(); - foreach($this->trigger_events as $e) { - foreach($this->trigger_timings as $t) { - $this->event_actions[] = "mepr-event-{$t}-{$e}-reminder"; - } - } - } - - public function validate() { - $this->validate_is_numeric($this->trigger_length, 0, null, 'trigger_length'); - $this->validate_is_in_array($this->trigger_interval, $this->trigger_intervals, 'trigger_interval'); - $this->validate_is_in_array($this->trigger_timing, $this->trigger_timings, 'trigger_timings'); - $this->validate_is_in_array($this->trigger_event, $this->trigger_events, 'trigger_events'); - $this->validate_is_bool($this->filter_products, 'filter_products'); - $this->validate_is_array($this->products, 'products'); - $this->validate_is_array($this->emails, 'emails'); - } - - public function events() { - } - - public function trigger_event_name() { - switch ($this->trigger_event) { - case 'sub-expires': return __('Subscription Expires', 'memberpress'); - case 'sub-renews': return __('Subscription Renews', 'memberpress'); - case 'cc-expires': return __('Credit Card Expires', 'memberpress'); - case 'member-signup': return __('Member Signs Up', 'memberpress'); - case 'signup-abandoned': return __('Sign Up Abandoned', 'memberpress'); - case 'sub-trial-ends': return __('Subscription Trial Ending', 'memberpress'); - default: return $this->trigger_event; + +if (!defined('ABSPATH')) { + die('You are not allowed to call this page directly.'); +} + +class MeprReminder extends MeprCptModel +{ + public static $trigger_length_str = 'mepr_trigger_length'; + public static $trigger_interval_str = 'mepr_trigger_interval'; + public static $trigger_timing_str = 'mepr_trigger_timing'; + public static $trigger_event_str = 'mepr_trigger_event'; + public static $filter_products_str = '_mepr_reminder_filter_products_str'; + public static $products_str = '_mepr_reminder_products'; + public static $emails_str = '_mepr_emails'; + + public static $nonce_str = 'mepr_reminders_nonce'; + public static $last_run_str = 'mepr_reminders_db_cleanup_last_run'; + + public static $cpt = 'mp-reminder'; + + public $trigger_intervals, $trigger_timings, $trigger_events, $event_actions; + + /*** + * Instance Methods + ***/ + public function __construct($obj = null) + { + $this->load_cpt( + $obj, + self::$cpt, + [ + 'trigger_length' => 1, + 'trigger_interval' => 'days', + 'trigger_timing' => 'before', + 'trigger_event' => 'sub-expires', + 'filter_products' => false, // Send only for specific memberships? + 'products' => [], // Empty array means ALL memberships + 'emails' => [], + ] + ); + + $this->trigger_intervals = ['hours','days','weeks','months','years']; + $this->trigger_timings = ['before','after']; + $this->trigger_events = [ + 'sub-expires', + 'sub-renews', + 'cc-expires', + 'member-signup', + 'signup-abandoned', + 'sub-trial-ends', + ]; + + $this->event_actions = []; + foreach ($this->trigger_events as $e) { + foreach ($this->trigger_timings as $t) { + $this->event_actions[] = "mepr-event-{$t}-{$e}-reminder"; + } + } } - } - public function get_trigger_interval_str() { - return MeprUtils::period_type_name( $this->trigger_interval, $this->trigger_length ); - } + public function validate() + { + $this->validate_is_numeric($this->trigger_length, 0, null, 'trigger_length'); + $this->validate_is_in_array($this->trigger_interval, $this->trigger_intervals, 'trigger_interval'); + $this->validate_is_in_array($this->trigger_timing, $this->trigger_timings, 'trigger_timings'); + $this->validate_is_in_array($this->trigger_event, $this->trigger_events, 'trigger_events'); + $this->validate_is_bool($this->filter_products, 'filter_products'); + $this->validate_is_array($this->products, 'products'); + $this->validate_is_array($this->emails, 'emails'); + } - public function store_meta() { - global $wpdb; - $skip_name_override = false; - $id = $this->ID; + public function events() + { + } - if(isset($_POST["post_title"]) && !empty($_POST["post_title"])) { - $skip_name_override = true; + public function trigger_event_name() + { + switch ($this->trigger_event) { + case 'sub-expires': + return __('Subscription Expires', 'memberpress'); + case 'sub-renews': + return __('Subscription Renews', 'memberpress'); + case 'cc-expires': + return __('Credit Card Expires', 'memberpress'); + case 'member-signup': + return __('Member Signs Up', 'memberpress'); + case 'signup-abandoned': + return __('Sign Up Abandoned', 'memberpress'); + case 'sub-trial-ends': + return __('Subscription Trial Ending', 'memberpress'); + default: + return $this->trigger_event; + } } - if (!$skip_name_override) { - $title = sprintf(__('%d %s %s %s', 'memberpress'), - $this->trigger_length, - strtolower($this->get_trigger_interval_str()), - $this->trigger_timing, - $this->trigger_event_name()); - - // Direct SQL so we don't issue any actions / filters - // in WP itself that could get us in an infinite loop - $sql = "UPDATE {$wpdb->posts} SET post_title=%s WHERE ID=%d"; - $sql = $wpdb->prepare($sql, $title, $id); - $wpdb->query($sql); + public function get_trigger_interval_str() + { + return MeprUtils::period_type_name($this->trigger_interval, $this->trigger_length); } - update_post_meta( $id, self::$trigger_length_str, $this->trigger_length ); - update_post_meta( $id, self::$trigger_interval_str, $this->trigger_interval ); - update_post_meta( $id, self::$trigger_timing_str, $this->trigger_timing ); - update_post_meta( $id, self::$trigger_event_str, $this->trigger_event ); - update_post_meta( $id, self::$filter_products_str, $this->filter_products ); - update_post_meta( $id, self::$products_str, $this->products ); - update_post_meta( $id, self::$emails_str, $this->emails ); - } - - // Singularize and capitalize - private function db_trigger_interval() { - return strtoupper( substr( $this->trigger_interval, 0, -1 ) ); - } - - public function get_formatted_products() { - $formatted_array = array(); - - if($this->filter_products && isset($this->products) && is_array($this->products) && !empty($this->products)) { - foreach($this->products as $product_id) { - $product = get_post($product_id); - - if(isset($product->post_title) && !empty($product->post_title)) { - $formatted_array[] = $product->post_title; + public function store_meta() + { + global $wpdb; + $skip_name_override = false; + $id = $this->ID; + + if (isset($_POST['post_title']) && !empty($_POST['post_title'])) { + $skip_name_override = true; + } + + if (!$skip_name_override) { + $title = sprintf( + __('%1$d %2$s %3$s %4$s', 'memberpress'), + $this->trigger_length, + strtolower($this->get_trigger_interval_str()), + $this->trigger_timing, + $this->trigger_event_name() + ); + + // Direct SQL so we don't issue any actions / filters + // in WP itself that could get us in an infinite loop + $sql = "UPDATE {$wpdb->posts} SET post_title=%s WHERE ID=%d"; + $sql = $wpdb->prepare($sql, $title, $id); + $wpdb->query($sql); } - } + + update_post_meta($id, self::$trigger_length_str, $this->trigger_length); + update_post_meta($id, self::$trigger_interval_str, $this->trigger_interval); + update_post_meta($id, self::$trigger_timing_str, $this->trigger_timing); + update_post_meta($id, self::$trigger_event_str, $this->trigger_event); + update_post_meta($id, self::$filter_products_str, $this->filter_products); + update_post_meta($id, self::$products_str, $this->products); + update_post_meta($id, self::$emails_str, $this->emails); } - else { //If empty, then All products - $formatted_array[] = __("All Memberships", 'memberpress'); + + // Singularize and capitalize + private function db_trigger_interval() + { + return strtoupper(substr($this->trigger_interval, 0, -1)); } - return $formatted_array; - } + public function get_formatted_products() + { + $formatted_array = []; + + if ($this->filter_products && isset($this->products) && is_array($this->products) && !empty($this->products)) { + foreach ($this->products as $product_id) { + $product = get_post($product_id); + + if (isset($product->post_title) && !empty($product->post_title)) { + $formatted_array[] = $product->post_title; + } + } + } else { // If empty, then All products + $formatted_array[] = __('All Memberships', 'memberpress'); + } - public function get_query_products($join_name) { - if($this->filter_products && is_array($this->products) && !empty($this->products)) { - $product_ids = implode(',', $this->products); - return "AND {$join_name} IN({$product_ids})"; + return $formatted_array; } - return ''; - } + public function get_query_products($join_name) + { + if ($this->filter_products && is_array($this->products) && !empty($this->products)) { + $product_ids = implode(',', $this->products); + return "AND {$join_name} IN({$product_ids})"; + } + + return ''; + } - // Used for Subscription Expiration Reminders - public function get_next_expiring_txn() { - global $wpdb; - $mepr_db = new MeprDb(); + // Used for Subscription Expiration Reminders + public function get_next_expiring_txn() + { + global $wpdb; + $mepr_db = new MeprDb(); - $unit = $this->db_trigger_interval(); - $op = ( $this->trigger_timing=='before' ? 'DATE_SUB' : 'DATE_ADD' ); + $unit = $this->db_trigger_interval(); + $op = ( $this->trigger_timing == 'before' ? 'DATE_SUB' : 'DATE_ADD' ); - //Make sure we're only grabbing from valid product ID's for this reminder yo - //If $this->products is empty, then we should send for all product_id's - $and_products = $this->get_query_products('tr.product_id'); + // Make sure we're only grabbing from valid product ID's for this reminder yo + // If $this->products is empty, then we should send for all product_id's + $and_products = $this->get_query_products('tr.product_id'); - $query = $wpdb->prepare( - // Get all info about expiring transactions - "SELECT tr.* FROM {$mepr_db->transactions} AS tr\n" . + $query = $wpdb->prepare( + // Get all info about expiring transactions + "SELECT tr.* FROM {$mepr_db->transactions} AS tr\n" . - // Lifetimes don't expire - "WHERE tr.expires_at <> %s\n" . + // Lifetimes don't expire + "WHERE tr.expires_at <> %s\n" . - //Make sure only real users are grabbed - "AND tr.user_id > 0\n" . + // Make sure only real users are grabbed + "AND tr.user_id > 0\n" . - // Make sure that only transactions that are - // complete or (confirmed and in a free trial) get picked up - "AND ( tr.status = %s + // Make sure that only transactions that are + // complete or (confirmed and in a free trial) get picked up + "AND ( tr.status = %s OR ( tr.status = %s AND ( SELECT sub.trial FROM {$mepr_db->subscriptions} AS sub WHERE sub.id = tr.subscription_id AND sub.trial_amount = 0.00 ) = 1 ) )\n" . - // Determine if expiration is accurate based on the subscription - // If sub_id is 0 then treat as expiration - "AND ( tr.subscription_id = 0 OR + // Determine if expiration is accurate based on the subscription + // If sub_id is 0 then treat as expiration + "AND ( tr.subscription_id = 0 OR ( SELECT sub.status FROM {$mepr_db->subscriptions} AS sub WHERE sub.id = tr.subscription_id ) IN (%s, %s) )\n" . - // Ensure that we're in the 2 day window after the expiration / trigger - "AND {$op}( tr.expires_at, INTERVAL {$this->trigger_length} {$unit} ) <= %s + // Ensure that we're in the 2 day window after the expiration / trigger + "AND {$op}( tr.expires_at, INTERVAL {$this->trigger_length} {$unit} ) <= %s AND DATE_ADD( {$op}( tr.expires_at, INTERVAL {$this->trigger_length} {$unit} ), INTERVAL 2 DAY ) >= %s\n" . - // Make sure that if our timing is beforehand - // then we don't send after the expiration - ( $this->trigger_timing=='before' ? $wpdb->prepare("AND tr.expires_at >= %s\n", MeprUtils::db_now()) : '' ) . + // Make sure that if our timing is beforehand + // then we don't send after the expiration + ( $this->trigger_timing == 'before' ? $wpdb->prepare("AND tr.expires_at >= %s\n", MeprUtils::db_now()) : '' ) . - // Let's make sure the reminder event hasn't already fired ... - // This will ensure that we don't send a second reminder - "AND ( SELECT ev.id + // Let's make sure the reminder event hasn't already fired ... + // This will ensure that we don't send a second reminder + "AND ( SELECT ev.id FROM {$mepr_db->events} AS ev WHERE ev.evt_id=tr.id AND ev.evt_id_type='transactions' @@ -204,84 +228,84 @@ public function get_next_expiring_txn() { AND ev.args=%d LIMIT 1 ) IS NULL\n" . - // Let's make sure we're not sending expire reminders - // when your subscription is being upgraded or downgraded - "AND ( SELECT ev2.id + // Let's make sure we're not sending expire reminders + // when your subscription is being upgraded or downgraded + "AND ( SELECT ev2.id FROM {$mepr_db->events} AS ev2 WHERE ev2.evt_id=tr.id AND ev2.evt_id_type='transactions' AND ev2.event='subscription-changed' LIMIT 1 ) IS NULL\n" . - // Let's make sure this is the latest transaction for the subscription - // in case there is a more recent transaction that expires later - "AND ( tr.subscription_id = 0 + // Let's make sure this is the latest transaction for the subscription + // in case there is a more recent transaction that expires later + "AND ( tr.subscription_id = 0 OR tr.id = ( SELECT tr2.id FROM {$mepr_db->transactions} AS tr2 WHERE tr2.subscription_id = tr.subscription_id ORDER BY tr2.expires_at DESC LIMIT 1 ) )\n" . - "{$and_products} " . + "{$and_products} " . - // We're just getting one of these at a time ... we need the oldest one first - "ORDER BY tr.expires_at + // We're just getting one of these at a time ... we need the oldest one first + "ORDER BY tr.expires_at LIMIT 1\n", + MeprUtils::db_lifetime(), + MeprTransaction::$complete_str, + MeprTransaction::$confirmed_str, + MeprSubscription::$cancelled_str, + MeprSubscription::$suspended_str, + MeprUtils::db_now(), + MeprUtils::db_now(), + "{$this->trigger_timing}-{$this->trigger_event}-reminder", + $this->ID + ); + + $res = $wpdb->get_row($query); + + return $res; + } - MeprUtils::db_lifetime(), - MeprTransaction::$complete_str, - MeprTransaction::$confirmed_str, - MeprSubscription::$cancelled_str, - MeprSubscription::$suspended_str, - MeprUtils::db_now(), - MeprUtils::db_now(), - "{$this->trigger_timing}-{$this->trigger_event}-reminder", - $this->ID - ); - - $res = $wpdb->get_row($query); - - return $res; - } - - // Used for After Subscription Renews Reminders - public function get_renewed_txn() { - global $wpdb; - $mepr_db = new MeprDb(); + // Used for After Subscription Renews Reminders + public function get_renewed_txn() + { + global $wpdb; + $mepr_db = new MeprDb(); - $unit = $this->db_trigger_interval(); + $unit = $this->db_trigger_interval(); - //Make sure we're only grabbing from valid product ID's for this reminder yo - //If $this->products is empty, then we should send for all product_id's - $and_products = $this->get_query_products('tr.product_id'); + // Make sure we're only grabbing from valid product ID's for this reminder yo + // If $this->products is empty, then we should send for all product_id's + $and_products = $this->get_query_products('tr.product_id'); - $query = $wpdb->prepare( - // Get all info about renewing transactions - "SELECT tr.* FROM {$mepr_db->transactions} AS tr\n" . + $query = $wpdb->prepare( + // Get all info about renewing transactions + "SELECT tr.* FROM {$mepr_db->transactions} AS tr\n" . - // Lifetimes don't renew - "WHERE tr.expires_at <> %s\n" . + // Lifetimes don't renew + "WHERE tr.expires_at <> %s\n" . - //Make sure it's recurring - "AND tr.subscription_id > 0\n" . + // Make sure it's recurring + "AND tr.subscription_id > 0\n" . - //Make sure only real users are grabbed - "AND tr.user_id > 0\n" . + // Make sure only real users are grabbed + "AND tr.user_id > 0\n" . - // Make sure that only transactions that are - // complete get picked up - "AND tr.status = %s \n" . + // Make sure that only transactions that are + // complete get picked up + "AND tr.status = %s \n" . - // Ensure that we're defined timeframe - // Giving it a 2 days buffer to the past - "AND DATE_ADD( tr.created_at, INTERVAL {$this->trigger_length} {$unit} ) <= %s AND DATE_ADD( + // Ensure that we're defined timeframe + // Giving it a 2 days buffer to the past + "AND DATE_ADD( tr.created_at, INTERVAL {$this->trigger_length} {$unit} ) <= %s AND DATE_ADD( DATE_ADD( tr.created_at, INTERVAL {$this->trigger_length} {$unit} ), INTERVAL 2 DAY) >= %s\n" . - "AND tr.created_at <= %s\n " . + "AND tr.created_at <= %s\n " . - // Let's make sure the reminder event hasn't already fired ... - // This will ensure that we don't send a second reminder - "AND ( SELECT ev.id + // Let's make sure the reminder event hasn't already fired ... + // This will ensure that we don't send a second reminder + "AND ( SELECT ev.id FROM {$mepr_db->events} AS ev WHERE ev.evt_id=tr.id AND ev.evt_id_type='transactions' @@ -289,76 +313,76 @@ public function get_renewed_txn() { AND ev.args=%d LIMIT 1 ) IS NULL\n" . - "{$and_products} " . + "{$and_products} " . - // We're just getting one of these at a time ... we need the oldest one first - "ORDER BY tr.created_at + // We're just getting one of these at a time ... we need the oldest one first + "ORDER BY tr.created_at LIMIT 1\n", + MeprUtils::db_lifetime(), + MeprTransaction::$complete_str, + MeprUtils::db_now(), + MeprUtils::db_now(), + MeprUtils::db_now(), + "{$this->trigger_timing}-{$this->trigger_event}-reminder", + $this->ID + ); + $res = $wpdb->get_row($query); + + return $res; + } - MeprUtils::db_lifetime(), - MeprTransaction::$complete_str, - MeprUtils::db_now(), - MeprUtils::db_now(), - MeprUtils::db_now(), - "{$this->trigger_timing}-{$this->trigger_event}-reminder", - $this->ID - ); - $res = $wpdb->get_row($query); - - return $res; - } - - // Used for Before Subscription Renews Reminders - public function get_next_renewing_txn() { - global $wpdb; - $mepr_db = new MeprDb(); + // Used for Before Subscription Renews Reminders + public function get_next_renewing_txn() + { + global $wpdb; + $mepr_db = new MeprDb(); - $unit = $this->db_trigger_interval(); + $unit = $this->db_trigger_interval(); - //Make sure we're only grabbing from valid product ID's for this reminder yo - //If $this->products is empty, then we should send for all product_id's - $and_products = $this->get_query_products('tr.product_id'); + // Make sure we're only grabbing from valid product ID's for this reminder yo + // If $this->products is empty, then we should send for all product_id's + $and_products = $this->get_query_products('tr.product_id'); - $query = $wpdb->prepare( - // Get all info about renewing transactions - "SELECT tr.* FROM {$mepr_db->transactions} AS tr\n" . + $query = $wpdb->prepare( + // Get all info about renewing transactions + "SELECT tr.* FROM {$mepr_db->transactions} AS tr\n" . - // Lifetimes don't renew - "WHERE tr.expires_at <> %s\n" . + // Lifetimes don't renew + "WHERE tr.expires_at <> %s\n" . - //Make sure it's recurring - "AND tr.subscription_id <> 0\n" . + // Make sure it's recurring + "AND tr.subscription_id <> 0\n" . - //Make sure only real users are grabbed - "AND tr.user_id > 0\n" . + // Make sure only real users are grabbed + "AND tr.user_id > 0\n" . - // Make sure that only transactions that are - // complete or (confirmed and in a free trial) get picked up - "AND ( tr.status = %s + // Make sure that only transactions that are + // complete or (confirmed and in a free trial) get picked up + "AND ( tr.status = %s OR ( tr.status = %s AND ( SELECT sub.trial FROM {$mepr_db->subscriptions} AS sub WHERE sub.id = tr.subscription_id AND sub.trial_amount = 0.00 ) = 1 ) )\n" . - // Determine if renewal is accurate based on the subscription - "AND ( SELECT sub.status + // Determine if renewal is accurate based on the subscription + "AND ( SELECT sub.status FROM {$mepr_db->subscriptions} AS sub WHERE sub.id = tr.subscription_id ) = %s\n" . - // Ensure that we're in the 2 day window after the renewal / trigger - "AND DATE_SUB( tr.expires_at, INTERVAL {$this->trigger_length} {$unit} ) <= %s + // Ensure that we're in the 2 day window after the renewal / trigger + "AND DATE_SUB( tr.expires_at, INTERVAL {$this->trigger_length} {$unit} ) <= %s AND DATE_ADD( DATE_SUB( tr.expires_at, INTERVAL {$this->trigger_length} {$unit} ), INTERVAL 2 DAY ) >= %s\n" . - // Make sure that if our timing is beforehand - // then we don't send after the renewal - "AND tr.expires_at >= %s\n" . + // Make sure that if our timing is beforehand + // then we don't send after the renewal + "AND tr.expires_at >= %s\n" . - // Let's make sure the reminder event hasn't already fired ... - // This will ensure that we don't send a second reminder - "AND ( SELECT ev.id + // Let's make sure the reminder event hasn't already fired ... + // This will ensure that we don't send a second reminder + "AND ( SELECT ev.id FROM {$mepr_db->events} AS ev WHERE ev.evt_id=tr.id AND ev.evt_id_type='transactions' @@ -366,83 +390,85 @@ public function get_next_renewing_txn() { AND ev.args=%d LIMIT 1 ) IS NULL\n" . - // Let's make sure we're not sending renewal reminders - // when your subscription is being upgraded or downgraded - "AND ( SELECT ev2.id + // Let's make sure we're not sending renewal reminders + // when your subscription is being upgraded or downgraded + "AND ( SELECT ev2.id FROM {$mepr_db->events} AS ev2 WHERE ev2.evt_id=tr.id AND ev2.evt_id_type='transactions' AND ev2.event='subscription-changed' LIMIT 1 ) IS NULL\n" . - // Let's make sure this is the latest transaction for the subscription - // in case there is a more recent transaction that expires later - "AND tr.id = ( SELECT tr2.id + // Let's make sure this is the latest transaction for the subscription + // in case there is a more recent transaction that expires later + "AND tr.id = ( SELECT tr2.id FROM {$mepr_db->transactions} AS tr2 WHERE tr2.subscription_id = tr.subscription_id ORDER BY tr2.expires_at DESC LIMIT 1 )\n" . - "{$and_products} " . + "{$and_products} " . - // We're just getting one of these at a time ... we need the oldest one first - "ORDER BY tr.expires_at + // We're just getting one of these at a time ... we need the oldest one first + "ORDER BY tr.expires_at LIMIT 1\n", + MeprUtils::db_lifetime(), + MeprTransaction::$complete_str, + MeprTransaction::$confirmed_str, + MeprSubscription::$active_str, + MeprUtils::db_now(), + MeprUtils::db_now(), + MeprUtils::db_now(), + "{$this->trigger_timing}-{$this->trigger_event}-reminder", + $this->ID + ); + + $res = $wpdb->get_row($query); + + return $res; + } + + public function get_next_member_signup() + { + global $wpdb; + $mepr_db = new MeprDb(); - MeprUtils::db_lifetime(), - MeprTransaction::$complete_str, - MeprTransaction::$confirmed_str, - MeprSubscription::$active_str, - MeprUtils::db_now(), - MeprUtils::db_now(), - MeprUtils::db_now(), - "{$this->trigger_timing}-{$this->trigger_event}-reminder", - $this->ID - ); - - $res = $wpdb->get_row($query); - - return $res; - } - - public function get_next_member_signup() { - global $wpdb; - $mepr_db = new MeprDb(); - - $unit = $this->db_trigger_interval(); - - // Sorry, don't want to incur any temporal paradoxes here - if( $this->trigger_timing==='before' ) { return false; } - - //Make sure we're only grabbing from valid product ID's for this reminder yo - //If $this->products is empty, then we should send for all product_id's - $and_products = $this->get_query_products('txn.product_id'); - - // Find transactions where - // status = complete or confirmed - // no other complete & unexpired txns for this user - $query = $wpdb->prepare( - // Just select the actual transaction id - "SELECT txn.id FROM {$mepr_db->transactions} AS txn " . - - // Make sure that only transactions that are - // complete or (confirmed and in a free trial) get picked up - "WHERE ( txn.status = %s + $unit = $this->db_trigger_interval(); + + // Sorry, don't want to incur any temporal paradoxes here + if ($this->trigger_timing === 'before') { + return false; + } + + // Make sure we're only grabbing from valid product ID's for this reminder yo + // If $this->products is empty, then we should send for all product_id's + $and_products = $this->get_query_products('txn.product_id'); + + // Find transactions where + // status = complete or confirmed + // no other complete & unexpired txns for this user + $query = $wpdb->prepare( + // Just select the actual transaction id + "SELECT txn.id FROM {$mepr_db->transactions} AS txn " . + + // Make sure that only transactions that are + // complete or (confirmed and in a free trial) get picked up + "WHERE ( txn.status = %s OR ( txn.status = %s AND ( SELECT sub.trial FROM {$mepr_db->subscriptions} AS sub WHERE sub.id = txn.subscription_id AND sub.trial_amount = 0.00 ) = 1 ) ) " . - // We don't send on fallback txn - " AND txn.txn_type <> %s " . - // Ensure we grab transactions that are after the trigger period - "AND DATE_ADD( + // We don't send on fallback txn + ' AND txn.txn_type <> %s ' . + // Ensure we grab transactions that are after the trigger period + "AND DATE_ADD( txn.created_at, INTERVAL {$this->trigger_length} {$unit} ) <= %s " . - // Give it a 2 day buffer period so we don't send for really old transactions - "AND DATE_ADD( + // Give it a 2 day buffer period so we don't send for really old transactions + "AND DATE_ADD( DATE_ADD( txn.created_at, INTERVAL {$this->trigger_length} {$unit} @@ -450,8 +476,8 @@ public function get_next_member_signup() { INTERVAL 2 DAY ) >= %s " . - // Make sure this is the *first* complete transaction - "AND ( SELECT txn2.id + // Make sure this is the *first* complete transaction + "AND ( SELECT txn2.id FROM {$mepr_db->transactions} AS txn2 WHERE txn2.user_id = txn.user_id AND ( txn2.status = %s @@ -459,13 +485,13 @@ public function get_next_member_signup() { AND ( SELECT sub.trial FROM {$mepr_db->subscriptions} AS sub WHERE sub.id = txn2.subscription_id AND sub.trial_amount = 0.00 ) = 1 ) ) - ".$this->get_query_products('txn2.product_id')." + " . $this->get_query_products('txn2.product_id') . ' AND txn2.created_at < txn.created_at LIMIT 1 - ) IS NULL " . + ) IS NULL ' . - // Don't send this twice yo ... for this user - "AND ( SELECT ev.id + // Don't send this twice yo ... for this user + "AND ( SELECT ev.id FROM {$mepr_db->events} AS ev WHERE ev.evt_id=txn.id AND ev.evt_id_type='transactions' @@ -474,58 +500,59 @@ public function get_next_member_signup() { LIMIT 1 ) IS NULL " . - "{$and_products} " . + "{$and_products} " . + + // Select the oldest transaction + 'ORDER BY txn.created_at ASC LIMIT 1', + MeprTransaction::$complete_str, + MeprTransaction::$confirmed_str, + MeprTransaction::$fallback_str, + MeprUtils::db_now(), + MeprUtils::db_now(), + MeprTransaction::$complete_str, + MeprTransaction::$confirmed_str, + "{$this->trigger_timing}-{$this->trigger_event}-reminder", + $this->ID + ); + $res = $wpdb->get_var($query); + return $res; + } - // Select the oldest transaction - "ORDER BY txn.created_at ASC LIMIT 1", - - MeprTransaction::$complete_str, - MeprTransaction::$confirmed_str, - MeprTransaction::$fallback_str, - MeprUtils::db_now(), - MeprUtils::db_now(), - MeprTransaction::$complete_str, - MeprTransaction::$confirmed_str, - "{$this->trigger_timing}-{$this->trigger_event}-reminder", - $this->ID - ); - $res = $wpdb->get_var($query); - return $res; - } - - public function get_next_abandoned_signup() { - global $wpdb; - $mepr_db = new MeprDb(); - - $unit = $this->db_trigger_interval(); - - // Sorry, don't want to incur any temporal paradoxes here - if( $this->trigger_timing==='before' ) { return false; } - - //Make sure we're only grabbing from valid product ID's for this reminder yo - //If $this->products is empty, then we should send for all product_id's - $and_products = $this->get_query_products('txn.product_id'); - - // Find transactions where - // status = pending - // no other complete & unexpired membership for this user - $query = $wpdb->prepare( - - // Just grab the transaction id - "SELECT txn.id FROM {$mepr_db->transactions} AS txn " . - - // Ensure that we only select 'pending' transactions - "WHERE txn.status=%s " . - - // Make sure the alotted time has passed - // before allowing to be selected - "AND DATE_ADD( + public function get_next_abandoned_signup() + { + global $wpdb; + $mepr_db = new MeprDb(); + + $unit = $this->db_trigger_interval(); + + // Sorry, don't want to incur any temporal paradoxes here + if ($this->trigger_timing === 'before') { + return false; + } + + // Make sure we're only grabbing from valid product ID's for this reminder yo + // If $this->products is empty, then we should send for all product_id's + $and_products = $this->get_query_products('txn.product_id'); + + // Find transactions where + // status = pending + // no other complete & unexpired membership for this user + $query = $wpdb->prepare( + // Just grab the transaction id + "SELECT txn.id FROM {$mepr_db->transactions} AS txn " . + + // Ensure that we only select 'pending' transactions + 'WHERE txn.status=%s ' . + + // Make sure the alotted time has passed + // before allowing to be selected + "AND DATE_ADD( txn.created_at, INTERVAL {$this->trigger_length} {$unit} ) <= %s " . - // Add in the 2 day buffer period - "AND DATE_ADD( + // Add in the 2 day buffer period + "AND DATE_ADD( DATE_ADD( txn.created_at, INTERVAL {$this->trigger_length} {$unit} @@ -533,28 +560,28 @@ public function get_next_abandoned_signup() { INTERVAL 2 DAY ) >= %s " . - // Ensure that there's no completed or confirmed transaction that - // was created after the pending one ... if they came back and - // completed their transaction then it's not abandoned ... hahaha - "AND ( SELECT txn2.id + // Ensure that there's no completed or confirmed transaction that + // was created after the pending one ... if they came back and + // completed their transaction then it's not abandoned ... hahaha + "AND ( SELECT txn2.id FROM {$mepr_db->transactions} AS txn2 WHERE txn2.user_id = txn.user_id AND txn2.product_id = txn.product_id AND txn2.status IN (%s,%s) - ".$this->get_query_products('txn2.product_id')." + " . $this->get_query_products('txn2.product_id') . ' AND txn2.created_at > txn.created_at LIMIT 1 - ) IS NULL " . + ) IS NULL ' . - // Ensure that this reminder is only sent for the primary transaction for multi-item purchases - "AND ( + // Ensure that this reminder is only sent for the primary transaction for multi-item purchases + "AND ( txn.order_id = 0 OR txn.id = ( SELECT ord.primary_transaction_id FROM {$mepr_db->orders} AS ord WHERE ord.id = txn.order_id ) )" . - // Don't want to send this reminder twice so make sure there's no - // reminder that has already been sent for this bro - "AND ( SELECT ev.id + // Don't want to send this reminder twice so make sure there's no + // reminder that has already been sent for this bro + "AND ( SELECT ev.id FROM {$mepr_db->events} AS ev WHERE ev.evt_id=txn.id AND ev.evt_id_type='transactions' @@ -563,48 +590,47 @@ public function get_next_abandoned_signup() { LIMIT 1 ) IS NULL " . - "{$and_products} " . - - // Get the *oldest* applicable transaction - "ORDER BY txn.created_at ASC LIMIT 1", - - MeprTransaction::$pending_str, - MeprUtils::db_now(), - MeprUtils::db_now(), - MeprTransaction::$complete_str, - MeprTransaction::$confirmed_str, - "{$this->trigger_timing}-{$this->trigger_event}-reminder", - $this->ID - ); + "{$and_products} " . + + // Get the *oldest* applicable transaction + 'ORDER BY txn.created_at ASC LIMIT 1', + MeprTransaction::$pending_str, + MeprUtils::db_now(), + MeprUtils::db_now(), + MeprTransaction::$complete_str, + MeprTransaction::$confirmed_str, + "{$this->trigger_timing}-{$this->trigger_event}-reminder", + $this->ID + ); + + // echo "User Abandoned Query:\n\n"; + // echo $query . "\n\n"; + $res = $wpdb->get_var($query); + return $res; + } - //echo "User Abandoned Query:\n\n"; - //echo $query . "\n\n"; + public function get_next_expired_cc() + { + global $wpdb; + $mepr_db = new MeprDb(); - $res = $wpdb->get_var($query); - return $res; - } + $unit = $this->db_trigger_interval(); + $op = ( $this->trigger_timing == 'before' ? 'DATE_SUB' : 'DATE_ADD' ); - public function get_next_expired_cc() { - global $wpdb; - $mepr_db = new MeprDb(); + // We want to get expiring subscriptions + $not = ( ( $this->trigger_event == 'sub-expires' ) ? ' ' : ' NOT ' ); - $unit = $this->db_trigger_interval(); - $op = ( $this->trigger_timing=='before' ? 'DATE_SUB' : 'DATE_ADD' ); + // Make sure we're only grabbing from valid product ID's for this reminder yo + // If $this->products is empty, then we should send for all product_id's + $and_products = $this->get_query_products('sub.product_id'); - // We want to get expiring subscriptions - $not = ( ( $this->trigger_event == 'sub-expires' ) ? ' ' : ' NOT ' ); + // Expiring Transactions + $query = + // Just grab the sub.id for any subscription with an expiring transaction + "SELECT sub.id FROM {$mepr_db->subscriptions} AS sub " . - //Make sure we're only grabbing from valid product ID's for this reminder yo - //If $this->products is empty, then we should send for all product_id's - $and_products = $this->get_query_products('sub.product_id'); - - // Expiring Transactions - $query = - // Just grab the sub.id for any subscription with an expiring transaction - "SELECT sub.id FROM {$mepr_db->subscriptions} AS sub " . - - // Make sure we only send out reminders for folks with ACTIVE subscriptions - "WHERE sub.status = %s " . + // Make sure we only send out reminders for folks with ACTIVE subscriptions + 'WHERE sub.status = %s ' . // Subtract or add if the reminder is before or after // The concat is just to piece together the date @@ -657,60 +683,60 @@ public function get_next_expired_cc() { "{$and_products} " . // Just ignore subs with no cc_exp date - "AND sub.cc_exp_month IS NOT NULL - AND sub.cc_exp_month <> \"\" + 'AND sub.cc_exp_month IS NOT NULL + AND sub.cc_exp_month <> "" AND sub.cc_exp_year IS NOT NULL - AND sub.cc_exp_year <> \"\" - "; + AND sub.cc_exp_year <> "" + '; - // Get the *oldest* valid cc expiration first - "ORDER BY CAST(sub.cc_exp_year AS UNSIGNED) ASC, + // Get the *oldest* valid cc expiration first + 'ORDER BY CAST(sub.cc_exp_year AS UNSIGNED) ASC, CAST(sub.cc_exp_month AS UNSIGNED) ASC - LIMIT 1"; - - $query = $wpdb->prepare( - $query, - MeprSubscription::$active_str, - MeprUtils::db_now(), - MeprUtils::db_now(), - "{$this->trigger_timing}-{$this->trigger_event}-reminder", - $this->ID - ); + LIMIT 1'; - //echo "{$query}\n"; + $query = $wpdb->prepare( + $query, + MeprSubscription::$active_str, + MeprUtils::db_now(), + MeprUtils::db_now(), + "{$this->trigger_timing}-{$this->trigger_event}-reminder", + $this->ID + ); - $res = $wpdb->get_var($query); + // echo "{$query}\n"; + $res = $wpdb->get_var($query); - return $res; - } + return $res; + } - public function get_next_trial_ends_subs() { - global $wpdb; + public function get_next_trial_ends_subs() + { + global $wpdb; - if( $this->trigger_length < 0 ){ - return false; // bail out. - } + if ($this->trigger_length < 0) { + return false; // bail out. + } - $mepr_db = new MeprDb(); + $mepr_db = new MeprDb(); - $unit = $this->db_trigger_interval(); - $op = ( $this->trigger_timing =='before' ? 'DATE_SUB' : 'DATE_ADD' ); + $unit = $this->db_trigger_interval(); + $op = ( $this->trigger_timing == 'before' ? 'DATE_SUB' : 'DATE_ADD' ); - //Make sure we're only grabbing from valid product ID's for this reminder yo - //If $this->products is empty, then we should send for all product_id's - $and_products = $this->get_query_products('sub.product_id'); + // Make sure we're only grabbing from valid product ID's for this reminder yo + // If $this->products is empty, then we should send for all product_id's + $and_products = $this->get_query_products('sub.product_id'); - // Make sure we only send out reminders for folks with the following status: - $subs_statuses = array( MeprSubscription::$active_str, MeprSubscription::$cancelled_str, MeprSubscription::$suspended_str ); - $in_sub_status = implode( "','", $subs_statuses ); + // Make sure we only send out reminders for folks with the following status: + $subs_statuses = [MeprSubscription::$active_str, MeprSubscription::$cancelled_str, MeprSubscription::$suspended_str]; + $in_sub_status = implode("','", $subs_statuses); - // Expiring Trial Subscriptions - $query = - // Just grab the trial_days sub.id for any subscription with an expiring transaction - "SELECT sub.id FROM {$mepr_db->subscriptions} AS sub " . + // Expiring Trial Subscriptions + $query = + // Just grab the trial_days sub.id for any subscription with an expiring transaction + "SELECT sub.id FROM {$mepr_db->subscriptions} AS sub " . - // Calculate trial end date with trial days for comparison - "WHERE sub.status IN ('{$in_sub_status}') " . + // Calculate trial end date with trial days for comparison + "WHERE sub.status IN ('{$in_sub_status}') " . // Ensure that we're in the 2 day window. "AND DATE_ADD( @@ -720,7 +746,7 @@ public function get_next_trial_ends_subs() { "AND {$op}( DATE_ADD( sub.created_at, INTERVAL trial_days DAY), INTERVAL {$this->trigger_length} {$unit} ) <= %s " . // Trials not expired yet - "AND DATE_ADD( sub.created_at, INTERVAL trial_days DAY) >= %s " . + 'AND DATE_ADD( sub.created_at, INTERVAL trial_days DAY) >= %s ' . // check that we haven't already sent a reminder for this // subscription *and* specific expiration date @@ -736,71 +762,65 @@ public function get_next_trial_ends_subs() { "{$and_products} " . // Just trials subs - "AND sub.trial = 1 AND sub.trial_days > 0 AND sub.prorated_trial = 0 " . - - // Get the *oldest* valid trial subs first - "ORDER BY sub.created_at ASC - LIMIT 1"; - - $query = $wpdb->prepare( - $query, - MeprUtils::db_now(), - MeprUtils::db_now(), - MeprUtils::db_now(), - "{$this->trigger_timing}-{$this->trigger_event}-reminder", - $this->rec->ID - ); - - $res = $wpdb->get_var($query); - - return $res; - } - - // Used for drips and expirations - //public function get_next_drip( $rule, $timing, $length, $interval, $event='sub-expires' ) { - // global $wpdb; - // - // $mepr_db = new MeprDb(); - // - // if($reminder->trigger_interval=='days') - // $unit = 'DAY'; - // else if($reminder->trigger_interval=='weeks') - // $unit = 'WEEK'; - // else if($reminder->trigger_interval=='months') - // $unit = 'MONTH'; - // else if($reminder->trigger_interval=='years') - // $unit = 'YEAR'; - // - // $op = ($reminder->trigger_timing='before')?'DATE_SUB':'DATE_ADD'; - // - // // We want to get expiring subscriptions - // $not = $expiring ? ' ' : ' NOT '; - - // $query = "SELECT (SELECT user) as user, " . - // "(SELECT products) as products, " . - // "(Calculate date) as date " . - // "FROM {$wpdb->posts} AS p " . - // "WHERE p.post_status='publish' " . - // "AND p.post_type=%s " . - // "AND p.ID=%d"; - - // // Rule: - - // // drip_enabled - // // drip_sequential - // // drip_amount - // // drip_unit - // // drip_after - // // drip_after_fixed - - // // User registers - // // Fixed date - // // Transaction for specific membership - - // // Expiring Transactions - - // $res = $wpdb->get_row($query); - // - // return $res; - //} + 'AND sub.trial = 1 AND sub.trial_days > 0 AND sub.prorated_trial = 0 ' . + + // Get the *oldest* valid trial subs first + 'ORDER BY sub.created_at ASC + LIMIT 1'; + + $query = $wpdb->prepare( + $query, + MeprUtils::db_now(), + MeprUtils::db_now(), + MeprUtils::db_now(), + "{$this->trigger_timing}-{$this->trigger_event}-reminder", + $this->rec->ID + ); + + $res = $wpdb->get_var($query); + + return $res; + } + + // Used for drips and expirations + // public function get_next_drip( $rule, $timing, $length, $interval, $event='sub-expires' ) { + // global $wpdb; + // + // $mepr_db = new MeprDb(); + // + // if($reminder->trigger_interval=='days') + // $unit = 'DAY'; + // else if($reminder->trigger_interval=='weeks') + // $unit = 'WEEK'; + // else if($reminder->trigger_interval=='months') + // $unit = 'MONTH'; + // else if($reminder->trigger_interval=='years') + // $unit = 'YEAR'; + // + // $op = ($reminder->trigger_timing='before')?'DATE_SUB':'DATE_ADD'; + // + // We want to get expiring subscriptions + // $not = $expiring ? ' ' : ' NOT '; + // $query = "SELECT (SELECT user) as user, " . + // "(SELECT products) as products, " . + // "(Calculate date) as date " . + // "FROM {$wpdb->posts} AS p " . + // "WHERE p.post_status='publish' " . + // "AND p.post_type=%s " . + // "AND p.ID=%d"; + // Rule: + // drip_enabled + // drip_sequential + // drip_amount + // drip_unit + // drip_after + // drip_after_fixed + // User registers + // Fixed date + // Transaction for specific membership + // Expiring Transactions + // $res = $wpdb->get_row($query); + // + // return $res; + // } } //End class diff --git a/app/models/MeprRule.php b/app/models/MeprRule.php index 788519b..cf7abfc 100644 --- a/app/models/MeprRule.php +++ b/app/models/MeprRule.php @@ -1,1420 +1,1558 @@ __('Membership', 'memberpress'), - 'value' => 'membership' - ), - array( - 'label' => __('Member', 'memberpress'), - 'value' => 'member' - ), - array( - 'label' => __('Role', 'memberpress'), - 'value' => 'role' - ), - array( - 'label' => __('Capability', 'memberpress'), - 'value' => 'capability' - ) - ); - } - - public static function mepr_access_operators() { - return array( - array( - 'label' => __('Is', 'memberpress'), - 'value' => 'is' - ) - ); - } - - /*** Instance Methods ***/ - public function __construct($obj = null) { - $this->load_cpt( - $obj, - self::$cpt, - array( - 'mepr_type' => 'all', - 'mepr_content' => '', - 'is_mepr_content_regexp' => false, - 'drip_enabled' => false, - 'drip_amount' => 0, - 'drip_unit' => 'days', - 'drip_after' => 'registers', - 'drip_after_fixed' => '', - 'expires_enabled' => false, - 'expires_amount' => 0, - 'expires_unit' => 'days', - 'expires_after' => 'registers', - 'expires_after_fixed' => '', - 'unauth_excerpt_type' => 'default', - 'unauth_excerpt_size' => 100, - 'unauth_message_type' => 'default', - 'unauth_message' => '', - 'unauth_login' => 'default', - 'unauth_modern_paywall' => false, - 'auto_gen_title' => true - ) - ); - - $this->drip_expire_units = array('days','weeks','months','years'); - $this->drip_expire_afters = array('registers','fixed','rule-products'); - $this->unauth_excerpt_types = array('default','hide','more','excerpt','custom'); - $this->unauth_message_types = array('default','hide','custom'); - $this->unauth_login_types = array('default','show','hide'); - } - - public function validate() { - //$this->validate_is_array($this->emails, 'emails'); - - $rule_types = array_keys(MeprRule::get_types()); - $this->validate_is_in_array($this->mepr_type, $rule_types, 'mepr_type'); - $this->validate_is_bool($this->is_mepr_content_regexp, 'is_mepr_content_regexp'); - - $this->validate_is_bool($this->drip_enabled, 'drip_enabled'); - $this->validate_is_numeric($this->drip_amount, 0, null, 'drip_amount'); - $this->validate_is_in_array($this->drip_unit, $this->drip_expire_units, 'drip_unit'); - $this->validate_is_in_array($this->drip_after, $this->drip_expire_afters, 'drip_after'); - - if($this->drip_after=='fixed') { - $this->validate_is_date($this->drip_after_fixed, 'drip_after_fixed'); - } - $this->validate_is_bool($this->expires_enabled, 'expires_enabled'); - $this->validate_is_numeric($this->expires_amount, 0, null, 'expires_amount'); - $this->validate_is_in_array($this->expires_unit, $this->drip_expire_units, 'expires_unit'); - $this->validate_is_in_array($this->expires_after, $this->drip_expire_afters, 'expires_after'); +if (!defined('ABSPATH')) { + die('You are not allowed to call this page directly.'); +} + +class MeprRule extends MeprCptModel +{ + public static $mepr_type_str = '_mepr_rules_type'; + public static $mepr_content_str = '_mepr_rules_content'; + public static $is_mepr_content_regexp_str = '_is_mepr_rules_content_regexp'; + public static $drip_enabled_str = '_mepr_rules_drip_enabled'; + public static $drip_amount_str = '_mepr_rules_drip_amount'; + public static $drip_unit_str = '_mepr_rules_drip_unit'; + public static $drip_after_str = '_mepr_rules_drip_after'; + public static $drip_after_fixed_str = '_mepr_rules_drip_after_fixed'; + public static $expires_enabled_str = '_mepr_rules_expires_enabled'; + public static $expires_amount_str = '_mepr_rules_expires_amount'; + public static $expires_unit_str = '_mepr_rules_expires_unit'; + public static $expires_after_str = '_mepr_rules_expires_after'; + public static $expires_after_fixed_str = '_mepr_rules_expires_after_fixed'; + public static $unauth_excerpt_type_str = '_mepr_rules_unauth_excerpt_type'; + public static $unauth_excerpt_size_str = '_mepr_rules_unauth_excerpt_size'; + public static $unauth_message_type_str = '_mepr_rules_unauth_message_type'; + public static $unauth_message_str = '_mepr_rules_unath_message'; + public static $unauth_login_str = '_mepr_rules_unath_login'; + public static $auto_gen_title_str = '_mepr_auto_gen_title'; + + public static $mepr_nonce_str = 'mepr_rules_nonce'; + public static $last_run_str = 'mepr_rules_db_cleanup_last_run'; + public static $unauth_modern_paywall_str = '_mepr_rules_unath_modern_paywall'; + + public $drip_expire_units, $drip_expire_afters, $unauth_excerpt_types, $unauth_message_types, $unauth_login_types; + + public static $cpt = 'memberpressrule'; + public static $all_rules; + + public static function mepr_access_types() + { + return [ + [ + 'label' => __('Membership', 'memberpress'), + 'value' => 'membership', + ], + [ + 'label' => __('Member', 'memberpress'), + 'value' => 'member', + ], + [ + 'label' => __('Role', 'memberpress'), + 'value' => 'role', + ], + [ + 'label' => __('Capability', 'memberpress'), + 'value' => 'capability', + ], + ]; + } + + public static function mepr_access_operators() + { + return [ + [ + 'label' => __('Is', 'memberpress'), + 'value' => 'is', + ], + ]; + } + + /*** + * Instance Methods + ***/ + public function __construct($obj = null) + { + $this->load_cpt( + $obj, + self::$cpt, + [ + 'mepr_type' => 'all', + 'mepr_content' => '', + 'is_mepr_content_regexp' => false, + 'drip_enabled' => false, + 'drip_amount' => 0, + 'drip_unit' => 'days', + 'drip_after' => 'registers', + 'drip_after_fixed' => '', + 'expires_enabled' => false, + 'expires_amount' => 0, + 'expires_unit' => 'days', + 'expires_after' => 'registers', + 'expires_after_fixed' => '', + 'unauth_excerpt_type' => 'default', + 'unauth_excerpt_size' => 100, + 'unauth_message_type' => 'default', + 'unauth_message' => '', + 'unauth_login' => 'default', + 'unauth_modern_paywall' => false, + 'auto_gen_title' => true, + ] + ); - if($this->expires_after=='fixed') { - $this->validate_is_date($this->expires_after_fixed, 'expires_after_fixed'); + $this->drip_expire_units = ['days','weeks','months','years']; + $this->drip_expire_afters = ['registers','fixed','rule-products']; + $this->unauth_excerpt_types = ['default','hide','more','excerpt','custom']; + $this->unauth_message_types = ['default','hide','custom']; + $this->unauth_login_types = ['default','show','hide']; } - $this->validate_is_in_array($this->unauth_excerpt_type, $this->unauth_excerpt_types, 'unauth_excerpt_type'); - $this->validate_is_numeric($this->unauth_excerpt_size, 0, null, 'unauth_excerpt_size'); - $this->validate_is_in_array($this->unauth_message_type, $this->unauth_message_types, 'unauth_message_type'); - - //$this->validate_is_bool($this->unauth_message, 'unauth_message' => '', - - $this->validate_is_in_array($this->unauth_login, $this->unauth_login_types, 'unauth_login'); + public function validate() + { + // $this->validate_is_array($this->emails, 'emails'); + $rule_types = array_keys(MeprRule::get_types()); + $this->validate_is_in_array($this->mepr_type, $rule_types, 'mepr_type'); + $this->validate_is_bool($this->is_mepr_content_regexp, 'is_mepr_content_regexp'); - $this->validate_is_bool($this->auto_gen_title, 'auto_gen_title'); - $this->validate_is_bool($this->unauth_modern_paywall, 'unauth_modern_paywall'); - } + $this->validate_is_bool($this->drip_enabled, 'drip_enabled'); + $this->validate_is_numeric($this->drip_amount, 0, null, 'drip_amount'); + $this->validate_is_in_array($this->drip_unit, $this->drip_expire_units, 'drip_unit'); + $this->validate_is_in_array($this->drip_after, $this->drip_expire_afters, 'drip_after'); - public static function get_types() { - global $wp_taxonomies, $wp_post_types; - - $mepr_options = MeprOptions::fetch(); + if ($this->drip_after == 'fixed') { + $this->validate_is_date($this->drip_after_fixed, 'drip_after_fixed'); + } - static $types; + $this->validate_is_bool($this->expires_enabled, 'expires_enabled'); + $this->validate_is_numeric($this->expires_amount, 0, null, 'expires_amount'); + $this->validate_is_in_array($this->expires_unit, $this->drip_expire_units, 'expires_unit'); + $this->validate_is_in_array($this->expires_after, $this->drip_expire_afters, 'expires_after'); - if(!isset($types) or empty($types)) { - $types = array( - 'all' => array(), - 'post' => array( - 'all_posts' => __('All Posts', 'memberpress'), - 'single_post' => __('A Single Post', 'memberpress'), - 'category' => __('Posts Categorized', 'memberpress'), - 'tag' => __('Posts Tagged', 'memberpress') - ), - 'page' => array( - 'all_pages' => __('All Pages', 'memberpress'), - 'single_page' => __('A Single Page', 'memberpress'), - 'parent_page' => __('Child Pages of', 'memberpress') - ) - ); + if ($this->expires_after == 'fixed') { + $this->validate_is_date($this->expires_after_fixed, 'expires_after_fixed'); + } - $cpts = get_post_types(array("public" => true, "_builtin" => false), 'objects'); - unset($cpts['memberpressproduct']); + $this->validate_is_in_array($this->unauth_excerpt_type, $this->unauth_excerpt_types, 'unauth_excerpt_type'); + $this->validate_is_numeric($this->unauth_excerpt_size, 0, null, 'unauth_excerpt_size'); + $this->validate_is_in_array($this->unauth_message_type, $this->unauth_message_types, 'unauth_message_type'); + + // $this->validate_is_bool($this->unauth_message, 'unauth_message' => '', + $this->validate_is_in_array($this->unauth_login, $this->unauth_login_types, 'unauth_login'); + + $this->validate_is_bool($this->auto_gen_title, 'auto_gen_title'); + $this->validate_is_bool($this->unauth_modern_paywall, 'unauth_modern_paywall'); + } + + public static function get_types() + { + global $wp_taxonomies, $wp_post_types; + + $mepr_options = MeprOptions::fetch(); + + static $types; + + if (!isset($types) or empty($types)) { + $types = [ + 'all' => [], + 'post' => [ + 'all_posts' => __('All Posts', 'memberpress'), + 'single_post' => __('A Single Post', 'memberpress'), + 'category' => __('Posts Categorized', 'memberpress'), + 'tag' => __('Posts Tagged', 'memberpress'), + ], + 'page' => [ + 'all_pages' => __('All Pages', 'memberpress'), + 'single_page' => __('A Single Page', 'memberpress'), + 'parent_page' => __('Child Pages of', 'memberpress'), + ], + ]; + + $cpts = get_post_types([ + 'public' => true, + '_builtin' => false, + ], 'objects'); + unset($cpts['memberpressproduct']); + + $cpts = MeprHooks::apply_filters('mepr-rules-cpts', $cpts); + + foreach ($cpts as $type_name => $cpt) { + $types[$type_name] = [ + "all_{$type_name}" => sprintf(__('All %s', 'memberpress'), $cpt->labels->name), + "single_{$type_name}" => sprintf(__('A Single %s', 'memberpress'), $cpt->labels->singular_name), + ]; + + if ($cpt->hierarchical) { + $types[$type_name]["parent_{$type_name}"] = sprintf(__('Child %s of', 'memberpress'), $cpt->labels->name); + } + } - $cpts = MeprHooks::apply_filters('mepr-rules-cpts', $cpts); + $txs = [ + 'category' => $wp_taxonomies['category'], + 'post_tag' => $wp_taxonomies['post_tag'], + ]; + + $txs = array_merge($txs, get_taxonomies([ + 'public' => true, + '_builtin' => false, + ], 'objects')); + + $cpts['post'] = $wp_post_types['post']; + $cpts['page'] = $wp_post_types['page']; + + foreach ($txs as $tax_name => $tx) { + if ($tax_name == 'post_tag') { + $types['all']["all_tax_{$tax_name}"] = __('All Content Tagged', 'memberpress'); + } elseif ($tax_name == 'category') { + $types['all']["all_tax_{$tax_name}"] = __('All Content Categorized', 'memberpress'); + } else { + $types['all']["all_tax_{$tax_name}"] = sprintf(__('All Content with %1$s', 'memberpress'), $tx->labels->singular_name); + } + + foreach ($tx->object_type as $cpt_slug) { + if ($cpt_slug == 'memberpressproduct') { + continue; + } + + if (!isset($cpts[$cpt_slug])) { + continue; + } //https://secure.helpscout.net/conversation/81248048/6880/ + + $cpt = $cpts[$cpt_slug]; + + if ($tax_name == 'post_tag') { + if ($cpt_slug != 'post') { // Already setup for post + $types[$cpt_slug]["tax_{$tax_name}||cpt_{$cpt_slug}"] = sprintf(__('%1$s Tagged', 'memberpress'), $cpt->labels->name); + } + } elseif ($tax_name == 'category') { + if ($cpt_slug != 'post') { // Already setup for post + $types[$cpt_slug]["tax_{$tax_name}||cpt_{$cpt_slug}"] = sprintf(__('%1$s Categorized', 'memberpress'), $cpt->labels->name); + } + } else { + $types[$cpt_slug]["tax_{$tax_name}||cpt_{$cpt_slug}"] = sprintf(__('%1$s with %2$s', 'memberpress'), $cpt->labels->name, $tx->labels->singular_name); + } + } + } - foreach($cpts as $type_name => $cpt) { - $types[$type_name] = array( - "all_{$type_name}" => sprintf(__('All %s', 'memberpress'), $cpt->labels->name), - "single_{$type_name}" => sprintf(__('A Single %s', 'memberpress'), $cpt->labels->singular_name) - ); + $all_types = ['all' => __('All Content', 'memberpress')]; + foreach ($types as $type_array) { + $all_types = array_merge($all_types, $type_array); + } - if($cpt->hierarchical) - $types[$type_name]["parent_{$type_name}"] = sprintf(__('Child %s of', 'memberpress'), $cpt->labels->name); - } + $all_types = MeprHooks::apply_filters('mepr-rule-types-before-partial', $all_types); - $txs = array( - 'category' => $wp_taxonomies['category'], - 'post_tag' => $wp_taxonomies['post_tag'] - ); + $all_types = array_merge( + $all_types, + [ + 'partial' => __('Partial', 'memberpress'), + 'custom' => __('Custom URI', 'memberpress'), + ] + ); - $txs = array_merge( $txs, get_taxonomies( array( 'public' => true, '_builtin' => false ), 'objects' ) ); + $types = $all_types; + } - $cpts['post'] = $wp_post_types['post']; - $cpts['page'] = $wp_post_types['page']; + return $types; + } - foreach( $txs as $tax_name => $tx ) { - if($tax_name=='post_tag') - $types['all']["all_tax_{$tax_name}"] = __('All Content Tagged', 'memberpress'); - else if($tax_name=='category') - $types['all']["all_tax_{$tax_name}"] = __('All Content Categorized', 'memberpress'); - else - $types['all']["all_tax_{$tax_name}"] = sprintf(__('All Content with %1$s', 'memberpress'), $tx->labels->singular_name); + public static function public_post_types() + { + $types = get_post_types(['public' => true]); + unset($types['attachment']); + unset($types[MeprProduct::$cpt]); + return array_values($types); + } - foreach( $tx->object_type as $cpt_slug ) { - if( $cpt_slug == 'memberpressproduct' ) { continue; } + public static function get_contents_array($type) + { + static $contents; - if( !isset($cpts[$cpt_slug]) ) { continue; } //https://secure.helpscout.net/conversation/81248048/6880/ + if (!isset($contents)) { + $contents = []; + } - $cpt = $cpts[$cpt_slug]; + if (isset($contents[$type])) { + return $contents[$type]; + } - if($tax_name=='post_tag') { - if( $cpt_slug != 'post' ) // Already setup for post - $types[$cpt_slug]["tax_{$tax_name}||cpt_{$cpt_slug}"] = sprintf(__('%1$s Tagged', 'memberpress'), $cpt->labels->name); - } - else if($tax_name=='category') { - if( $cpt_slug != 'post' ) // Already setup for post - $types[$cpt_slug]["tax_{$tax_name}||cpt_{$cpt_slug}"] = sprintf(__('%1$s Categorized', 'memberpress'), $cpt->labels->name); - } - else - $types[$cpt_slug]["tax_{$tax_name}||cpt_{$cpt_slug}"] = sprintf(__('%1$s with %2$s', 'memberpress'), $cpt->labels->name, $tx->labels->singular_name); + if (preg_match('#^single_(.*?)$#', $type, $matches)) { + $contents[$type] = self::get_single_array($matches[1]); + return $contents[$type]; + } elseif (preg_match('#^parent_(.*?)$#', $type, $matches)) { + $contents[$type] = self::get_parent_array($matches[1]); + return $contents[$type]; + } elseif ($type == 'category') { + $contents[$type] = self::get_category_array(); + return $contents[$type]; + } elseif ($type == 'tag') { + $contents[$type] = self::get_tag_array(); + return $contents[$type]; + } elseif (preg_match('#^tax_(.*?)\|\|cpt_(.*?)$#', $type, $matches) || preg_match('#^all_tax_(.*?)$#', $type, $matches)) { + $contents[$type] = self::get_tax_array($matches[1]); + return $contents[$type]; + } elseif ($type == 'partial' || $type == 'custom') { + $contents[$type] = false; } - } - $all_types = array('all' => __('All Content', 'memberpress')); - foreach( $types as $type_array ) { $all_types = array_merge( $all_types, $type_array ); } + return MeprHooks::apply_filters('mepr-rule-contents-array', $contents, $type); + } + + public static function search_content($type, $search = '') + { + if (preg_match('#^single_(.*?)$#', $type, $matches)) { + return self::search_singles($matches[1], $search); + } elseif (preg_match('#^parent_(.*?)$#', $type, $matches)) { + return self::search_parents($matches[1], $search); + } elseif ($type == 'category') { + return self::search_categories($search); + } elseif ($type == 'tag') { + return self::search_tags($search); + } elseif ( + preg_match('#^tax_(.*?)\|\|cpt_(.*?)$#', $type, $matches) or + preg_match('#^all_tax_(.*?)$#', $type, $matches) + ) { + return self::search_taxs($matches[1], $search); + } - $all_types = MeprHooks::apply_filters('mepr-rule-types-before-partial', $all_types); + return MeprHooks::apply_filters('mepr-rule-search-content', false, $type, $search); + } + + public static function get_content($type, $id) + { + if (preg_match('#^single_(.*?)$#', $type, $matches)) { + return self::get_single($matches[1], $id); + } elseif (preg_match('#^parent_(.*?)$#', $type, $matches)) { + return self::get_parent($matches[1], $id); + } elseif ($type == 'category') { + return self::get_category($id); + } elseif ($type == 'tag') { + return self::get_tag($id); + } elseif ( + preg_match('#^tax_(.*?)\|\|cpt_(.*?)$#', $type, $matches) or + preg_match('#^all_tax_(.*?)$#', $type, $matches) + ) { + return self::get_tax($matches[1], $id); + } - $all_types = array_merge( - $all_types, - array( 'partial' => __('Partial', 'memberpress'), - 'custom' => __('Custom URI', 'memberpress') ) - ); + return MeprHooks::apply_filters('mepr-rule-content', false, $type, $id); + } + + public static function type_has_contents($type) + { + if (preg_match('#^single_(.*?)$#', $type, $matches)) { + return self::singles_have_contents($matches[1]); + } elseif (preg_match('#^parent_(.*?)$#', $type, $matches)) { + return self::parents_have_contents($matches[1]); + } elseif ($type == 'category') { + return self::categories_have_contents(); + } elseif ($type == 'tag') { + return self::tags_have_contents(); + } elseif ( + preg_match('#^tax_(.*?)\|\|cpt_(.*?)$#', $type, $matches) or + preg_match('#^all_tax_(.*?)$#', $type, $matches) + ) { + return self::taxs_have_contents($matches[1]); + } - $types = $all_types; + return MeprHooks::apply_filters('mepr-rule-has-content', false, $type); } - return $types; - } + public static function singles_have_contents($type) + { + $counts = wp_count_posts($type); - public static function public_post_types() { - $types = get_post_types( array( 'public' => true ) ); - unset( $types['attachment'] ); - unset( $types[MeprProduct::$cpt] ); - return array_values($types); - } + if (isset($counts->future) && (int)$counts->future > 0) { + return ( ((int)$counts->future + (int)$counts->publish) > 0 ); + } else { + return ( (int)$counts->publish > 0 ); + } + } - public static function get_contents_array($type) { - static $contents; + public static function get_single_array($type) + { + global $wpdb; - if(!isset($contents)) { $contents = array(); } + $lookup = $wpdb->get_results("SELECT ID, post_title FROM {$wpdb->posts} WHERE post_type = '{$type}'", OBJECT_K); - if(isset($contents[$type])) { return $contents[$type]; } + if (empty($lookup)) { + return []; + } - if(preg_match('#^single_(.*?)$#', $type, $matches)) { - $contents[$type] = self::get_single_array($matches[1]); - return $contents[$type]; - } - elseif(preg_match('#^parent_(.*?)$#', $type, $matches)) { - $contents[$type] = self::get_parent_array($matches[1]); - return $contents[$type]; - } - elseif($type == 'category') { - $contents[$type] = self::get_category_array(); - return $contents[$type]; - } - elseif($type == 'tag') { - $contents[$type] = self::get_tag_array(); - return $contents[$type]; - } - elseif(preg_match('#^tax_(.*?)\|\|cpt_(.*?)$#', $type, $matches) || preg_match('#^all_tax_(.*?)$#', $type, $matches)) { - $contents[$type] = self::get_tax_array($matches[1]); - return $contents[$type]; - } - elseif($type == 'partial' || $type == 'custom') { - $contents[$type] = false; - } + foreach ($lookup as $id => $obj) { + $lookup[$id] = stripslashes($obj->post_title); + } - return MeprHooks::apply_filters('mepr-rule-contents-array', $contents, $type); - } - - public static function search_content($type, $search = '') { - if(preg_match('#^single_(.*?)$#', $type, $matches)) - return self::search_singles($matches[1],$search); - elseif(preg_match('#^parent_(.*?)$#', $type, $matches)) - return self::search_parents($matches[1],$search); - elseif($type == 'category') - return self::search_categories($search); - elseif($type == 'tag') - return self::search_tags($search); - elseif( preg_match('#^tax_(.*?)\|\|cpt_(.*?)$#', $type, $matches) or - preg_match('#^all_tax_(.*?)$#', $type, $matches) ) - return self::search_taxs($matches[1],$search); - - return MeprHooks::apply_filters('mepr-rule-search-content', false, $type, $search); - } - - public static function get_content($type,$id) { - if(preg_match('#^single_(.*?)$#', $type, $matches)) - return self::get_single($matches[1],$id); - elseif(preg_match('#^parent_(.*?)$#', $type, $matches)) - return self::get_parent($matches[1],$id); - elseif($type == 'category') - return self::get_category($id); - elseif($type == 'tag') - return self::get_tag($id); - elseif( preg_match('#^tax_(.*?)\|\|cpt_(.*?)$#', $type, $matches) or - preg_match('#^all_tax_(.*?)$#', $type, $matches) ) - return self::get_tax($matches[1],$id); - - return MeprHooks::apply_filters('mepr-rule-content', false, $type, $id); - } - - public static function type_has_contents($type) { - if(preg_match('#^single_(.*?)$#', $type, $matches)) - return self::singles_have_contents($matches[1]); - elseif(preg_match('#^parent_(.*?)$#', $type, $matches)) - return self::parents_have_contents($matches[1]); - elseif($type == 'category') - return self::categories_have_contents(); - elseif($type == 'tag') - return self::tags_have_contents(); - elseif( preg_match('#^tax_(.*?)\|\|cpt_(.*?)$#', $type, $matches) or - preg_match('#^all_tax_(.*?)$#', $type, $matches) ) - return self::taxs_have_contents($matches[1]); - - return MeprHooks::apply_filters('mepr-rule-has-content', false, $type); - } - - public static function singles_have_contents($type) { - $counts = wp_count_posts($type); - - if(isset($counts->future) && (int)$counts->future > 0) { - return ( ((int)$counts->future + (int)$counts->publish) > 0 ); - } - else { - return ( (int)$counts->publish > 0 ); + return $lookup; } - } - public static function get_single_array($type) { - global $wpdb; - - $lookup = $wpdb->get_results("SELECT ID, post_title FROM {$wpdb->posts} WHERE post_type = '{$type}'", OBJECT_K); + public static function search_singles($type, $search = '', $limit = 25) + { + global $wpdb; + $query = 'SELECT p.ID AS id, p.post_title AS label ' . + "FROM {$wpdb->posts} AS p " . + 'WHERE p.post_type=%s ' . + 'AND (p.post_status=%s || p.post_status=%s) '; + + if (!empty($search)) { + $query .= 'AND ( p.ID LIKE %s OR p.post_title LIKE %s ) '; + $query .= "LIMIT {$limit}"; + $query = $wpdb->prepare($query, $type, 'publish', 'future', "%{$search}%", "%{$search}%"); + } else { + $query .= "LIMIT {$limit}"; + $query = $wpdb->prepare($query, $type, 'publish', 'future'); + } - if(empty($lookup)) { return array(); } + $query = MeprHooks::apply_filters('mepr-search-singles-query', $query, $type, $search); - foreach($lookup as $id => $obj) { - $lookup[$id] = stripslashes($obj->post_title); + return array_map( + function ($i) { + $i->slug = preg_replace('!' . preg_quote(home_url(), '!') . '!', '', MeprUtils::get_permalink($i->id)); + $i->desc = "ID: {$i->id} | Slug: {$i->slug}"; + return $i; + }, + $wpdb->get_results($query) + ); } - return $lookup; - } - - public static function search_singles($type,$search='',$limit=25) { - global $wpdb; - $query = "SELECT p.ID AS id, p.post_title AS label " . + public static function get_single($type, $id) + { + global $wpdb; + $query = 'SELECT p.ID AS id, p.post_title AS label ' . "FROM {$wpdb->posts} AS p " . - "WHERE p.post_type=%s " . - "AND (p.post_status=%s || p.post_status=%s) "; + 'WHERE p.post_type=%s ' . + 'AND (p.post_status=%s || p.post_status=%s) ' . + 'AND p.ID=%d ' . + 'LIMIT 1'; - if(!empty($search)) { - $query .= "AND ( p.ID LIKE %s OR p.post_title LIKE %s ) "; - $query .= "LIMIT {$limit}"; - $query = $wpdb->prepare( $query, $type, 'publish', 'future', "%{$search}%", "%{$search}%" ); - } - else { - $query .= "LIMIT {$limit}"; - $query = $wpdb->prepare( $query, $type, 'publish', 'future' ); - } + $query = $wpdb->prepare($query, $type, 'publish', 'future', $id); - $query = MeprHooks::apply_filters('mepr-search-singles-query', $query, $type, $search); + $i = $wpdb->get_row($query); + if ($i == false) { + return false; + } - return array_map( function($i) { $i->slug = preg_replace('!' . preg_quote(home_url(), '!') . '!', '', MeprUtils::get_permalink($i->id)); $i->desc = "ID: {$i->id} | Slug: {$i->slug}"; - return $i; - }, - $wpdb->get_results($query) ); - } - - public static function get_single($type,$id) { - global $wpdb; - $query = "SELECT p.ID AS id, p.post_title AS label " . - "FROM {$wpdb->posts} AS p " . - "WHERE p.post_type=%s " . - "AND (p.post_status=%s || p.post_status=%s) " . - "AND p.ID=%d " . - "LIMIT 1"; - - $query = $wpdb->prepare($query, $type, 'publish', 'future', $id); - $i = $wpdb->get_row($query); - if($i == false) { return false; } - - $i->slug = preg_replace('!'.preg_quote(home_url(), '!') . '!', '', MeprUtils::get_permalink($i->id)); - $i->desc = "ID: {$i->id} | Slug: {$i->slug}"; + return $i; + } - return $i; - } + /** + * Get the total number of published rules + * + * @return integer + */ + public static function count() + { + global $wpdb; - /** - * Get the total number of published rules - * - * @return int - */ - public static function count() { - global $wpdb; + $query = $wpdb->prepare( + "SELECT COUNT(*) FROM {$wpdb->posts} WHERE post_type = %s AND post_status = 'publish'", + self::$cpt + ); - $query = $wpdb->prepare( - "SELECT COUNT(*) FROM {$wpdb->posts} WHERE post_type = %s AND post_status = 'publish'", - self::$cpt - ); + return (int) $wpdb->get_var($query); + } - return (int) $wpdb->get_var($query); - } + public static function parents_have_contents($type = 'page') + { + return self::singles_have_contents($type); + } - public static function parents_have_contents($type='page') { - return self::singles_have_contents($type); - } + public static function get_parent_array($type = 'page') + { + return self::get_single_array($type); + } - public static function get_parent_array($type='page') { - return self::get_single_array($type); - } + public static function search_parents($type = 'page', $search = '', $limit = 25) + { + return self::search_singles($type, $search, $limit); + } - public static function search_parents($type='page',$search='',$limit=25) { - return self::search_singles($type,$search,$limit); - } + public static function get_parent($type, $id) + { + return self::get_single($type, $id); + } - public static function get_parent($type,$id) { - return self::get_single($type,$id); - } + public static function categories_have_contents() + { + return ( wp_count_terms('category', ['hide_empty' => 0]) > 0 ); + } - public static function categories_have_contents() { - return ( wp_count_terms( 'category', array('hide_empty' => 0) ) > 0 ); - } + public static function get_category_array() + { + $category_contents = get_categories(['hide_empty' => 0]); + $contents = []; - public static function get_category_array() { - $category_contents = get_categories(array('hide_empty' => 0)); - $contents = array(); + foreach ($category_contents as $category) { + $contents[$category->term_id] = $category->name; + } - foreach($category_contents as $category) { - $contents[$category->term_id] = $category->name; + return $contents; } - return $contents; - } - - public static function search_terms( $tax, $search='', $limit=25 ) { - global $wpdb; - $query = "SELECT t.term_id AS id, t.name AS label, t.slug AS slug " . + public static function search_terms($tax, $search = '', $limit = 25) + { + global $wpdb; + $query = 'SELECT t.term_id AS id, t.name AS label, t.slug AS slug ' . "FROM {$wpdb->terms} AS t " . - "JOIN {$wpdb->term_taxonomy} AS tx ". - "ON t.term_id=tx.term_id " . - "AND tx.taxonomy=%s "; - - if(!empty($search)) { - $query .= "WHERE ( t.term_id LIKE %s OR t.name LIKE %s OR t.slug LIKE %s OR tx.description LIKE %s ) "; - $query .= "LIMIT {$limit}"; - $s = "%{$search}%"; - $query = $wpdb->prepare( $query, $tax, $s, $s, $s, $s ); - } - else { - $query .= "LIMIT {$limit}"; - $query = $wpdb->prepare( $query, $tax ); - } + "JOIN {$wpdb->term_taxonomy} AS tx " . + 'ON t.term_id=tx.term_id ' . + 'AND tx.taxonomy=%s '; + + if (!empty($search)) { + $query .= 'WHERE ( t.term_id LIKE %s OR t.name LIKE %s OR t.slug LIKE %s OR tx.description LIKE %s ) '; + $query .= "LIMIT {$limit}"; + $s = "%{$search}%"; + $query = $wpdb->prepare($query, $tax, $s, $s, $s, $s); + } else { + $query .= "LIMIT {$limit}"; + $query = $wpdb->prepare($query, $tax); + } - return array_map( function($i) { - $i->desc = "ID: {$i->id} | Slug: {$i->slug}"; - return $i; - }, - $wpdb->get_results($query) ); - } + return array_map( + function ($i) { + $i->desc = "ID: {$i->id} | Slug: {$i->slug}"; + return $i; + }, + $wpdb->get_results($query) + ); + } - public static function get_term( $tax, $id ) { - global $wpdb; - $query = "SELECT t.term_id AS id, t.name AS label, t.slug AS slug " . + public static function get_term($tax, $id) + { + global $wpdb; + $query = 'SELECT t.term_id AS id, t.name AS label, t.slug AS slug ' . "FROM {$wpdb->terms} AS t " . - "JOIN {$wpdb->term_taxonomy} AS tx ". - "ON t.term_id=tx.term_id " . - "AND tx.taxonomy=%s " . - "WHERE t.term_id=%d " . - "LIMIT 1"; + "JOIN {$wpdb->term_taxonomy} AS tx " . + 'ON t.term_id=tx.term_id ' . + 'AND tx.taxonomy=%s ' . + 'WHERE t.term_id=%d ' . + 'LIMIT 1'; + + $query = $wpdb->prepare($query, $tax, $id); + $i = $wpdb->get_row($query); + if ($i == false) { + return false; + } - $query = $wpdb->prepare( $query, $tax, $id ); - $i = $wpdb->get_row($query); - if($i==false) { return false; } + $i->desc = "ID: {$i->id} | Slug: {$i->slug}"; - $i->desc = "ID: {$i->id} | Slug: {$i->slug}"; + return $i; + } - return $i; - } + public static function search_categories($search, $limit = 25) + { + return self::search_terms('category', $search, $limit); + } - public static function search_categories($search,$limit=25) { - return self::search_terms( 'category', $search, $limit ); - } + public static function get_category($id) + { + return self::get_term('category', $id); + } - public static function get_category($id) { - return self::get_term( 'category', $id ); - } + public static function tags_have_contents() + { + return ( wp_count_terms('post_tag', ['get' => 'all']) > 0 ); + } - public static function tags_have_contents() { - return ( wp_count_terms( 'post_tag', array('get' => 'all') ) > 0 ); - } + public static function get_tag_array() + { + $tag_contents = get_tags(['get' => 'all']); + $contents = []; - public static function get_tag_array() { - $tag_contents = get_tags(array('get' => 'all')); - $contents = array(); + foreach ($tag_contents as $tag) { + $contents[$tag->term_id] = $tag->name; + } - foreach($tag_contents as $tag) { - $contents[$tag->term_id] = $tag->name; + return $contents; } - return $contents; - } + public static function search_tags($search, $limit = 25) + { + return self::search_terms('post_tag', $search, $limit); + } - public static function search_tags($search,$limit=25) { - return self::search_terms( 'post_tag', $search, $limit ); - } + public static function get_tag($id) + { + return self::get_term('post_tag', $id); + } - public static function get_tag($id) { - return self::get_term( 'post_tag', $id ); - } + public static function taxs_have_contents($tax) + { + return ( wp_count_terms($tax, ['get' => 'all']) > 0 ); + } - public static function taxs_have_contents($tax) { - return ( wp_count_terms( $tax, array('get' => 'all') ) > 0 ); - } + public static function get_tax_array($tax) + { + $contents = []; + $tax_contents = get_terms($tax, ['get' => 'all']); - public static function get_tax_array($tax) { - $contents = array(); - $tax_contents = get_terms($tax,array('get' => 'all')); + if (!is_wp_error($tax_contents)) { + foreach ($tax_contents as $tk => $t) { + $contents[$t->term_id] = $t->name; + } + } - if( !is_wp_error($tax_contents) ) { - foreach($tax_contents as $tk => $t) { - $contents[$t->term_id] = $t->name; - } + return $contents; } - return $contents; - } - - public static function search_taxs($tax, $search,$limit=25) { - return self::search_terms( $tax, $search, $limit ); - } + public static function search_taxs($tax, $search, $limit = 25) + { + return self::search_terms($tax, $search, $limit); + } - public static function get_tax($tax, $id) { - return self::get_term( $tax, $id ); - } + public static function get_tax($tax, $id) + { + return self::get_term($tax, $id); + } - // We just assume this will only be called on posts that are the correct type - public static function is_exception_to_rule( $post, $rule, $exceptions = array() ) { - $rule_exceptions = explode( ',', preg_replace( '#\s#', '', $rule->mepr_content ) ); - $exceptions = array_unique ( array_merge($rule_exceptions, $exceptions) ); - return in_array( $post->ID, $exceptions ); - } + // We just assume this will only be called on posts that are the correct type + public static function is_exception_to_rule($post, $rule, $exceptions = []) + { + $rule_exceptions = explode(',', preg_replace('#\s#', '', $rule->mepr_content)); + $exceptions = array_unique(array_merge($rule_exceptions, $exceptions)); + return in_array($post->ID, $exceptions); + } - //Make sure that we don't lock down the unauthorized URL if redirect on unauthorized is selected - // public static function is_unauthorized_url() { + // Make sure that we don't lock down the unauthorized URL if redirect on unauthorized is selected + // public static function is_unauthorized_url() { // global $post; // $mepr_options = MeprOptions::fetch(); - // if($mepr_options->redirect_on_unauthorized) { // $current_url = MeprUtils::get_permalink($post->ID); - // if(stristr($current_url, $mepr_options->unauthorized_redirect_url) !== false) { // return true; // } // } - // return false; - // } - - // TODO: Create a convenience function calling this in MeprProduct once it's in place - public static function get_rules($context) { - $post_rules = array(); + // } + // TODO: Create a convenience function calling this in MeprProduct once it's in place + public static function get_rules($context) + { + $post_rules = []; - if(!isset(self::$all_rules)) { - $all_rule_posts = MeprCptModel::all('MeprRule'); + if (!isset(self::$all_rules)) { + $all_rule_posts = MeprCptModel::all('MeprRule'); - self::$all_rules = array(); + self::$all_rules = []; - foreach($all_rule_posts as $curr_post) { - if($curr_post->post_type == self::$cpt) { - self::$all_rules[] = new MeprRule($curr_post->ID); + foreach ($all_rule_posts as $curr_post) { + if ($curr_post->post_type == self::$cpt) { + self::$all_rules[] = new MeprRule($curr_post->ID); + } + } } - } - } - foreach(self::$all_rules as $curr_rule) { - if (isset($curr_rule->ID)) { //Occassionally some how this loop ends up with nulled out rules which causes issues. This check will prevent that from happening - if(is_a($context, 'WP_Post') && $curr_rule->mepr_type != 'custom') { - if( $curr_rule->mepr_type == 'all' ) { - // We're going to add this rule immediately if it's set to all and it's not an exception - if( !self::is_exception_to_rule( $context, $curr_rule ) ) { $post_rules[] = $curr_rule; } - } - elseif(preg_match('#^all_tax_(.*?)$#', $curr_rule->mepr_type, $matches)) { - if( has_term( $curr_rule->mepr_content, $matches[1], $context->ID ) ) - $post_rules[] = $curr_rule; - } - elseif(preg_match('#^all_(.*?)$#', $curr_rule->mepr_type, $matches)) { - if( preg_match('#^'.preg_quote($context->post_type).'s?$#', $matches[1]) && - !self::is_exception_to_rule( $context, $curr_rule ) ) { - $post_rules[] = $curr_rule; - } - } - elseif(preg_match('#^single_(.*?)$#', $curr_rule->mepr_type, $matches)) { - if( $context->post_type == $matches[1] && - $context->ID == $curr_rule->mepr_content ) { - $post_rules[] = $curr_rule; + foreach (self::$all_rules as $curr_rule) { + if (isset($curr_rule->ID)) { // Occassionally some how this loop ends up with nulled out rules which causes issues. This check will prevent that from happening + if (is_a($context, 'WP_Post') && $curr_rule->mepr_type != 'custom') { + if ($curr_rule->mepr_type == 'all') { + // We're going to add this rule immediately if it's set to all and it's not an exception + if (!self::is_exception_to_rule($context, $curr_rule)) { + $post_rules[] = $curr_rule; + } + } elseif (preg_match('#^all_tax_(.*?)$#', $curr_rule->mepr_type, $matches)) { + if (has_term($curr_rule->mepr_content, $matches[1], $context->ID)) { + $post_rules[] = $curr_rule; + } + } elseif (preg_match('#^all_(.*?)$#', $curr_rule->mepr_type, $matches)) { + if ( + preg_match('#^' . preg_quote($context->post_type) . 's?$#', $matches[1]) && + !self::is_exception_to_rule($context, $curr_rule) + ) { + $post_rules[] = $curr_rule; + } + } elseif (preg_match('#^single_(.*?)$#', $curr_rule->mepr_type, $matches)) { + if ( + $context->post_type == $matches[1] && + $context->ID == $curr_rule->mepr_content + ) { + $post_rules[] = $curr_rule; + } + } elseif (preg_match('#^parent_(.*?)$#', $curr_rule->mepr_type, $matches)) { + if ( + $context->post_type == $matches[1] /* + && + $context->post_parent == $curr_rule->mepr_content */ + ) { + $ancestors = get_post_ancestors($context->ID); + + // Let's protect all lineage of the parent page + if (in_array($curr_rule->mepr_content, $ancestors, false)) { + $post_rules[] = $curr_rule; + } + } + } elseif ($curr_rule->mepr_type == 'category') { + if (in_category($curr_rule->mepr_content, $context->ID)) { + $post_rules[] = $curr_rule; + } + } elseif ($curr_rule->mepr_type == 'tag') { + if (has_tag($curr_rule->mepr_content, $context->ID)) { + $post_rules[] = $curr_rule; + } + } elseif (preg_match('#^tax_(.*?)\|\|cpt_(.*?)$#', $curr_rule->mepr_type, $matches)) { + if ( + $context->post_type == $matches[2] && + has_term($curr_rule->mepr_content, $matches[1], $context->ID) + ) { + $post_rules[] = $curr_rule; + } + } + + $post_rules = MeprHooks::apply_filters('mepr-extend-post-rules', $post_rules, $curr_rule, $context); + } elseif ($curr_rule->mepr_type == 'custom' && is_string($context)) { + $uri = empty($context) ? esc_url($_SERVER['REQUEST_URI']) : $context; + $uri = html_entity_decode($uri); // Needed to decode & and other html entities + + if ( + ($curr_rule->is_mepr_content_regexp && preg_match('~' . $curr_rule->mepr_content . '~i', $uri)) || + (!$curr_rule->is_mepr_content_regexp && strpos($uri, $curr_rule->mepr_content) === 0) + ) { + $post_rules[] = $curr_rule; + } + } + $post_rules = MeprHooks::apply_filters('mepr-extend-rules', $post_rules, $curr_rule, $context); } - } - elseif(preg_match('#^parent_(.*?)$#', $curr_rule->mepr_type, $matches)) { - if( $context->post_type == $matches[1] /* && - $context->post_parent == $curr_rule->mepr_content */ ) { - $ancestors = get_post_ancestors($context->ID); - - //Let's protect all lineage of the parent page - if(in_array($curr_rule->mepr_content, $ancestors, false)) { - $post_rules[] = $curr_rule; - } + } //End foreach + + return $post_rules; + } + + // TODO: Move to MeprProduct once it's in place + // Returns: ["membership" => [1,2], "member" =>["foo"]] + public static function get_access_list($post) /*tested*/ + { + $access_array = []; + $rules = MeprRule::get_rules($post); + + foreach ($rules as $rule) { + foreach ($rule->access_conditions() as $condition) { + if (!isset($access_array[$condition->access_type])) { + $access_array[$condition->access_type] = []; + } + // Make sure they're unique + if (!in_array($condition->access_condition, $access_array[$condition->access_type])) { + array_push($access_array[$condition->access_type], $condition->access_condition); + } } - } - elseif($curr_rule->mepr_type == 'category') { - if(in_category($curr_rule->mepr_content, $context->ID)) - $post_rules[] = $curr_rule; - } - elseif($curr_rule->mepr_type == 'tag') { - if(has_tag($curr_rule->mepr_content, $context->ID)) - $post_rules[] = $curr_rule; - } - elseif(preg_match('#^tax_(.*?)\|\|cpt_(.*?)$#', $curr_rule->mepr_type, $matches)) { - if( $context->post_type == $matches[2] && - has_term( $curr_rule->mepr_content, $matches[1], $context->ID ) ) { - $post_rules[] = $curr_rule; - } - } + } + + return $access_array; + } - $post_rules = MeprHooks::apply_filters('mepr-extend-post-rules', $post_rules, $curr_rule, $context); + public static function is_locked_for_user($user, $context) + { + // the content is not locked regardless of whether or not + // a user is logged in so let's just return here okay? + $rules = MeprRule::get_rules($context); + if (empty($rules)) { + return false; + } + if (!isset($user->ID) || $user->ID == 0) { + return true; } - elseif($curr_rule->mepr_type == 'custom' && is_string($context)) { - $uri = empty($context) ? esc_url($_SERVER['REQUEST_URI']) : $context; - $uri = html_entity_decode($uri); // Needed to decode & and other html entities - if( ($curr_rule->is_mepr_content_regexp && preg_match('~'.$curr_rule->mepr_content.'~i', $uri)) || - (!$curr_rule->is_mepr_content_regexp && strpos($uri, $curr_rule->mepr_content) === 0) ) { - $post_rules[] = $curr_rule; - } + foreach ($rules as $rule) { + if ($user->has_access_from_rule($rule->ID)) { + if ($rule->has_dripped($user->ID)) { + if (!$rule->has_expired($user->ID)) { + return MeprHooks::apply_filters('mepr-content-locked-for-user', false, $rule, $context, $rules); + } + } + } } - $post_rules = MeprHooks::apply_filters('mepr-extend-rules', $post_rules, $curr_rule, $context); - } - } //End foreach - return $post_rules; - } + return true; + } - // TODO: Move to MeprProduct once it's in place - // Returns: ["membership" => [1,2], "member" =>["foo"]] - public static function get_access_list($post) /*tested*/ - { - $access_array = array(); - $rules = MeprRule::get_rules($post); + public static function is_uri_locked($uri) + { + $mepr_options = MeprOptions::fetch(); + $current_post = MeprUtils::get_current_post(); - foreach($rules as $rule) { - foreach($rule->access_conditions() as $condition) { - if(!isset($access_array[$condition->access_type])) { - $access_array[$condition->access_type] = array(); + static $is_locked; + $md5_uri = (string)md5($uri); // Used as the key for the $is_locked array + + if (!isset($is_locked) || !is_array($is_locked)) { + $is_locked = []; } - // Make sure they're unique - if(!in_array($condition->access_condition, $access_array[$condition->access_type])) { - array_push($access_array[$condition->access_type], $condition->access_condition); + + if (isset($is_locked) && !empty($is_locked) && isset($is_locked[$md5_uri]) && is_bool($is_locked[$md5_uri])) { + return $is_locked[$md5_uri]; } - } - } - return $access_array; - } - - public static function is_locked_for_user($user, $context) - { - // the content is not locked regardless of whether or not - // a user is logged in so let's just return here okay? - $rules = MeprRule::get_rules($context); - if(empty($rules)) { return false; } - if(!isset($user->ID) || $user->ID == 0) { return true; } - - foreach($rules as $rule) { - if($user->has_access_from_rule($rule->ID)) { - if($rule->has_dripped($user->ID)) { - if(!$rule->has_expired($user->ID)) { - return MeprHooks::apply_filters('mepr-content-locked-for-user', false, $rule, $context, $rules); - } - } - } - } + if (isset($_GET['action']) && $_GET['action'] == 'mepr_unauthorized' && $current_post !== false && $current_post->ID == $mepr_options->login_page_id) { + $is_locked[$md5_uri] = false; + return $is_locked[$md5_uri]; // Don't override the login page content duh! + } - return true; - } + if (MeprUtils::is_logged_in_and_an_admin()) { + $is_locked[$md5_uri] = false; + return $is_locked[$md5_uri]; // If user is an admin, let's not go on. + } - public static function is_uri_locked($uri) { - $mepr_options = MeprOptions::fetch(); - $current_post = MeprUtils::get_current_post(); + $rules = MeprRule::get_rules($uri); - static $is_locked; - $md5_uri = (string)md5($uri); //Used as the key for the $is_locked array + // the content is not locked regardless of whether or not + // a user is logged in so let's just return here okay? + if (empty($rules)) { + $is_locked[$md5_uri] = false; + return $is_locked[$md5_uri]; + } - if(!isset($is_locked) || !is_array($is_locked)) { - $is_locked = array(); - } + if (MeprUtils::is_user_logged_in()) { + $user = MeprUtils::get_currentuserinfo(); + $is_locked[$md5_uri] = self::is_locked_for_user($user, $uri); - if(isset($is_locked) && !empty($is_locked) && isset($is_locked[$md5_uri]) && is_bool($is_locked[$md5_uri])) { - return $is_locked[$md5_uri]; - } + MeprHooks::do_action('mepr-user-unauthorized'); // This one will be called for all events where the user is blocked by a rule + MeprHooks::do_action('mepr-member-unauthorized', $user); // More specific (member means logged in user) + MeprHooks::do_action('mepr-member-unauthorized-for-uri', $user, $uri); // Further specific-ness - yes I can make up words! (member means logged in user) - if(isset($_GET['action']) && $_GET['action'] == 'mepr_unauthorized' && $current_post !== false && $current_post->ID == $mepr_options->login_page_id) { - $is_locked[$md5_uri] = false; - return $is_locked[$md5_uri]; //Don't override the login page content duh! - } + return $is_locked[$md5_uri]; + } else { + $is_locked[$md5_uri] = true; - if(MeprUtils::is_logged_in_and_an_admin()) { - $is_locked[$md5_uri] = false; - return $is_locked[$md5_uri]; //If user is an admin, let's not go on. + MeprHooks::do_action('mepr-user-unauthorized'); // This one will be called for all events where the user is blocked by a rule + MeprHooks::do_action('mepr-guest-unauthorized-for-uri', $uri); // Further specific-ness - yes I can make up words! (guest means NOT logged in user) + + return $is_locked[$md5_uri]; // If there are rules on this content and the user isn't logged in -- it's locked + } } - $rules = MeprRule::get_rules($uri); + // TODO: Move to MeprProduct once it's in place + public static function is_locked($post) + { + /*tested*/ + $mepr_options = MeprOptions::fetch(); + static $is_locked; - // the content is not locked regardless of whether or not - // a user is logged in so let's just return here okay? - if(empty($rules)) { - $is_locked[$md5_uri] = false; - return $is_locked[$md5_uri]; - } + if (!isset($is_locked) || !is_array($is_locked)) { + $is_locked = []; + } - if(MeprUtils::is_user_logged_in()) { - $user = MeprUtils::get_currentuserinfo(); - $is_locked[$md5_uri] = self::is_locked_for_user($user, $uri); + if (isset($is_locked) && !empty($is_locked) && isset($is_locked[$post->ID]) && is_bool($is_locked[$post->ID])) { + return $is_locked[$post->ID]; + } - MeprHooks::do_action('mepr-user-unauthorized'); //This one will be called for all events where the user is blocked by a rule - MeprHooks::do_action('mepr-member-unauthorized', $user); //More specific (member means logged in user) - MeprHooks::do_action('mepr-member-unauthorized-for-uri', $user, $uri); //Further specific-ness - yes I can make up words! (member means logged in user) + // If user is an admin, let's not go on. + if (MeprUtils::is_logged_in_and_an_admin()) { + $is_locked[$post->ID] = false; + return $is_locked[$post->ID]; + } - return $is_locked[$md5_uri]; - } - else { - $is_locked[$md5_uri] = true; + // Can't rule the login page lest we end up in an infinite loop + if ($post->ID == $mepr_options->login_page_id) { + $is_locked[$post->ID] = false; + return $is_locked[$post->ID]; + } - MeprHooks::do_action('mepr-user-unauthorized'); //This one will be called for all events where the user is blocked by a rule - MeprHooks::do_action('mepr-guest-unauthorized-for-uri', $uri); //Further specific-ness - yes I can make up words! (guest means NOT logged in user) + $rules = MeprRule::get_rules($post); - return $is_locked[$md5_uri]; // If there are rules on this content and the user isn't logged in -- it's locked - } - } + // the content is not locked regardless of wether or not + // a user is logged in so let's just return here okay? + if (empty($rules)) { + $is_locked[$post->ID] = false; + return $is_locked[$post->ID]; + } - // TODO: Move to MeprProduct once it's in place - public static function is_locked($post) { /*tested*/ - $mepr_options = MeprOptions::fetch(); - static $is_locked; + if (MeprUtils::is_user_logged_in()) { + $user = MeprUtils::get_currentuserinfo(); + $is_locked[$post->ID] = self::is_locked_for_user($user, $post); - if(!isset($is_locked) || !is_array($is_locked)) { - $is_locked = array(); - } + MeprHooks::do_action('mepr-user-unauthorized'); // This one will be called for all events where the user is blocked by a rule + MeprHooks::do_action('mepr-member-unauthorized', $user); // More specific (member means logged in user) + MeprHooks::do_action('mepr-member-unauthorized-for-content', $user, $post); // Further specific-ness - yes I can make up words! (member means logged in user) - if(isset($is_locked) && !empty($is_locked) && isset($is_locked[$post->ID]) && is_bool($is_locked[$post->ID])) { - return $is_locked[$post->ID]; - } + return $is_locked[$post->ID]; + } else { + $is_locked[$post->ID] = true; - //If user is an admin, let's not go on. - if(MeprUtils::is_logged_in_and_an_admin()) { - $is_locked[$post->ID] = false; - return $is_locked[$post->ID]; - } + MeprHooks::do_action('mepr-user-unauthorized'); // This one will be called for all events where the user is blocked by a rule + MeprHooks::do_action('mepr-guest-unauthorized-for-content', $post); // Further specific-ness - yes I can make up words! (guest means NOT logged in user) - // Can't rule the login page lest we end up in an infinite loop - if($post->ID == $mepr_options->login_page_id) { - $is_locked[$post->ID] = false; - return $is_locked[$post->ID]; + return $is_locked[$post->ID]; // If there are rules on this content and the user isn't logged in -- it's locked + } } - $rules = MeprRule::get_rules($post); + public static function get_custom_unauth_message_from_rule_ids($rule_ids) + { + $mepr_options = MeprOptions::fetch(); + $unauth_message = '
    ' . wpautop(do_shortcode($mepr_options->unauthorized_message)) . '
    '; - // the content is not locked regardless of wether or not - // a user is logged in so let's just return here okay? - if(empty($rules)) { - $is_locked[$post->ID] = false; - return $is_locked[$post->ID]; - } + if (empty($rule_ids)) { + return $unauth_message; + } - if(MeprUtils::is_user_logged_in()) { - $user = MeprUtils::get_currentuserinfo(); - $is_locked[$post->ID] = self::is_locked_for_user($user, $post); + foreach ($rule_ids as $rule_id) { + $rule = new MeprRule($rule_id); - MeprHooks::do_action('mepr-user-unauthorized'); //This one will be called for all events where the user is blocked by a rule - MeprHooks::do_action('mepr-member-unauthorized', $user); //More specific (member means logged in user) - MeprHooks::do_action('mepr-member-unauthorized-for-content', $user, $post); //Further specific-ness - yes I can make up words! (member means logged in user) + // If more than one rules has a custom unauthorized message, the last will win here + if ($rule->unauth_message_type == 'custom' && !empty($rule->unauth_message)) { + $unauth_message = '
    ' . wpautop(do_shortcode($rule->unauth_message)) . '
    '; + } + } - return $is_locked[$post->ID]; + return $unauth_message; } - else { - $is_locked[$post->ID] = true; - MeprHooks::do_action('mepr-user-unauthorized'); //This one will be called for all events where the user is blocked by a rule - MeprHooks::do_action('mepr-guest-unauthorized-for-content', $post); //Further specific-ness - yes I can make up words! (guest means NOT logged in user) + public function has_dripped($user_id = false) + { + if (!$user_id) { + $user_id = get_current_user_id(); + } - return $is_locked[$post->ID]; // If there are rules on this content and the user isn't logged in -- it's locked - } - } + if (!$this->drip_enabled) { + return true; + } //If the drip is disabled, then let's kill this thing - public static function get_custom_unauth_message_from_rule_ids($rule_ids) { - $mepr_options = MeprOptions::fetch(); - $unauth_message = '
    '.wpautop(do_shortcode($mepr_options->unauthorized_message)).'
    '; + if ($this->drip_after == 'registers') { + $registered_ts = MeprUtils::db_date_to_ts(MeprUser::get_user_registration_date($user_id)); - if(empty($rule_ids)) { return $unauth_message; } + return $this->has_time_passed($registered_ts, $this->drip_unit, $this->drip_amount); + } - foreach($rule_ids as $rule_id) { - $rule = new MeprRule($rule_id); + if ($this->drip_after == 'fixed' && !empty($this->drip_after_fixed)) { + $fixed_ts = strtotime($this->drip_after_fixed); + $has_dripped_fixed = $this->has_time_passed($fixed_ts, $this->drip_unit, $this->drip_amount, true); - // If more than one rules has a custom unauthorized message, the last will win here - if($rule->unauth_message_type == 'custom' && !empty($rule->unauth_message)) { - $unauth_message = '
    '.wpautop(do_shortcode($rule->unauth_message)).'
    '; - } - } + return MeprHooks::apply_filters('mepr-rule-has-dripped-fixed', $has_dripped_fixed, $this); + } - return $unauth_message; - } + // Any product associated with this rule + if ($this->drip_after == 'rule-products') { + $products = []; - public function has_dripped($user_id = false) { - if(!$user_id) { - $user_id = get_current_user_id(); - } + $mepr_access = $this->mepr_access; + foreach ($mepr_access['membership'] as $prod_id) { + $products[] = new MeprProduct($prod_id); + } + } else { + $products = [new MeprProduct($this->drip_after)]; + } - if(!$this->drip_enabled) { return true; } //If the drip is disabled, then let's kill this thing + foreach ($products as $product) { + if (!isset($product->ID) || (int)$product->ID <= 0) { + continue; + } //The membership doesn't exist anymore, so let's ignore this rule - if($this->drip_after == 'registers') { - $registered_ts = MeprUtils::db_date_to_ts(MeprUser::get_user_registration_date($user_id)); + if (!$user_id) { + continue; + } //Not logged in - return $this->has_time_passed($registered_ts, $this->drip_unit, $this->drip_amount); - } + $purchased_ts = MeprUtils::db_date_to_ts(MeprUser::get_user_product_signup_date($user_id, $product->ID)); - if($this->drip_after == 'fixed' && !empty($this->drip_after_fixed)) { - $fixed_ts = strtotime($this->drip_after_fixed); - $has_dripped_fixed = $this->has_time_passed($fixed_ts, $this->drip_unit, $this->drip_amount, true); + if (!$purchased_ts) { + continue; + } //User hasn't purchased this membership - return MeprHooks::apply_filters('mepr-rule-has-dripped-fixed', $has_dripped_fixed, $this); + if ($this->has_time_passed($purchased_ts, $this->drip_unit, $this->drip_amount)) { + return true; + } + } + + return false; // If we made it here the user doens't have access } - //Any product associated with this rule - if($this->drip_after == 'rule-products') { - $products = array(); + // Returns: ["membership" => [1,2], "member" =>["foo"]] + public function mgm_mepr_access($mgm, $val = '') + { + $access_array = []; - $mepr_access = $this->mepr_access; - foreach($mepr_access['membership'] as $prod_id) { - $products[] = new MeprProduct($prod_id); - } - } - else { - $products = array(new MeprProduct($this->drip_after)); + switch ($mgm) { + case 'get': + foreach ($this->access_conditions() as $condition) { + if (!isset($access_array[$condition->access_type])) { + $access_array[$condition->access_type] = []; + } + array_push($access_array[$condition->access_type], $condition->access_condition); + } + + return $access_array; + default: + return []; + } } - foreach($products as $product) { - if(!isset($product->ID) || (int)$product->ID <= 0) { continue; } //The membership doesn't exist anymore, so let's ignore this rule + public function has_expired($user_id = false) + { + if (!$user_id) { + $user_id = get_current_user_id(); + } - if (!$user_id) { continue; } //Not logged in + if (!$this->expires_enabled) { + return false; + } //If the expiration is disabled, then let's kill this thing - $purchased_ts = MeprUtils::db_date_to_ts(MeprUser::get_user_product_signup_date($user_id, $product->ID)); + if ($this->expires_after == 'registers') { + $registered_ts = MeprUtils::db_date_to_ts(MeprUser::get_user_registration_date($user_id)); - if(!$purchased_ts) { continue; } //User hasn't purchased this membership + return $this->has_time_passed($registered_ts, $this->expires_unit, $this->expires_amount); + } - if($this->has_time_passed($purchased_ts, $this->drip_unit, $this->drip_amount)) { - return true; - } - } + if ($this->expires_after == 'fixed' && !empty($this->expires_after_fixed)) { + $fixed_ts = strtotime($this->expires_after_fixed); - return false; //If we made it here the user doens't have access - } + return $this->has_time_passed($fixed_ts, $this->expires_unit, $this->expires_amount, true); + } - // Returns: ["membership" => [1,2], "member" =>["foo"]] - public function mgm_mepr_access($mgm, $val='') { - $access_array = array(); + // Any product associated with this rule + if ($this->expires_after == 'rule-products') { + $products = []; - switch($mgm) { - case 'get': - foreach($this->access_conditions() as $condition) { - if(!isset($access_array[$condition->access_type])) { - $access_array[$condition->access_type] = array(); - } - array_push($access_array[$condition->access_type], $condition->access_condition); + $mepr_access = $this->mepr_access; + foreach ($mepr_access['membership'] as $prod_id) { + $products[] = new MeprProduct($prod_id); + } + } else { + $products = [new MeprProduct($this->expires_after)]; } - return $access_array; - default: - return array(); - } - } - - public function has_expired($user_id = false) { - if(!$user_id) { - $user_id = get_current_user_id(); - } + foreach ($products as $product) { + if (!isset($product->ID) || (int)$product->ID <= 0) { + continue; + } //The membership doesn't exist anymore, so let's ignore this rule - if(!$this->expires_enabled) { return false; } //If the expiration is disabled, then let's kill this thing + if (!$user_id) { + continue; + } - if($this->expires_after == 'registers') { - $registered_ts = MeprUtils::db_date_to_ts(MeprUser::get_user_registration_date($user_id)); + $purchased_ts = MeprUtils::db_date_to_ts(MeprUser::get_user_product_signup_date($user_id, $product->ID)); - return $this->has_time_passed($registered_ts, $this->expires_unit, $this->expires_amount); - } + if (!$purchased_ts) { + continue; + } //User hasn't purchased this - if($this->expires_after == 'fixed' && !empty($this->expires_after_fixed)) { - $fixed_ts = strtotime($this->expires_after_fixed); + if (!$this->has_time_passed($purchased_ts, $this->expires_unit, $this->expires_amount)) { + return false; + } + } - return $this->has_time_passed($fixed_ts, $this->expires_unit, $this->expires_amount, true); + // If we made it here the user doesn't have access + return true; } - //Any product associated with this rule - if($this->expires_after == 'rule-products') { - $products = array(); + // Should probably put this in Utils at some point + public function has_time_passed($ts, $unit, $amount, $is_fixed = false) + { + // Convert $ts to the start of the day, so drips/expirations don't come in at odd hours throughout the day + if (!$is_fixed) { + // $datetime = gmdate('Y-m-d 00:00:01', $ts); + // $ts = strtotime($datetime); + $datetime = gmdate('Y-m-d H:i:s', $ts); + $datetime = get_date_from_gmt($datetime, 'Y-m-d 00:00:01'); // Convert to local WP timezone + $ts = (int) get_gmt_from_date($datetime, 'U'); // Now back to a unix timestamp + } - $mepr_access = $this->mepr_access; - foreach($mepr_access['membership'] as $prod_id) { - $products[] = new MeprProduct($prod_id); - } - } - else { - $products = array(new MeprProduct($this->expires_after)); - } + switch ($unit) { + case 'days': + $days_ts = MeprUtils::days($amount); + if ((time() - $ts) > $days_ts) { + return true; + } + break; + case 'weeks': + $weeks_ts = MeprUtils::weeks($amount); + if ((time() - $ts) > $weeks_ts) { + return true; + } + break; + case 'months': + $months_ts = MeprUtils::months($amount, $ts); + if ((time() - $ts) > $months_ts) { + return true; + } + break; + case 'years': + $years_ts = MeprUtils::years($amount, $ts); + if ((time() - $ts) > $years_ts) { + return true; + } + break; + } - foreach($products as $product) { - if(!isset($product->ID) || (int)$product->ID <= 0) { continue; } //The membership doesn't exist anymore, so let's ignore this rule + return false; + } - if(!$user_id) { continue; } + public function get_formatted_accesses() + { + $formatted_array = []; + + foreach ($this->mepr_access as $access_key => $access_values) { + if ($access_key == 'membership') { + foreach ($access_values as $access) { + $product = get_post($access); + if ($product) { + $formatted_array[] = $product->post_title; + } + } + } else { + $formatted_array = array_merge($formatted_array, $access_values); + } + } - $purchased_ts = MeprUtils::db_date_to_ts(MeprUser::get_user_product_signup_date($user_id, $product->ID)); + return $formatted_array; + } - if(!$purchased_ts) { continue; } //User hasn't purchased this + public function access_conditions() + { + $mepr_db = new MeprDb(); - if(!$this->has_time_passed($purchased_ts, $this->expires_unit, $this->expires_amount)) { - return false; - } + return $mepr_db->get_records($mepr_db->rule_access_conditions, ['rule_id' => $this->ID]); } - //If we made it here the user doesn't have access - return true; - } - - //Should probably put this in Utils at some point - public function has_time_passed($ts, $unit, $amount, $is_fixed = false) { - //Convert $ts to the start of the day, so drips/expirations don't come in at odd hours throughout the day - if(!$is_fixed) { - //$datetime = gmdate('Y-m-d 00:00:01', $ts); - //$ts = strtotime($datetime); - $datetime = gmdate('Y-m-d H:i:s', $ts); - $datetime = get_date_from_gmt($datetime, 'Y-m-d 00:00:01'); // Convert to local WP timezone - $ts = (int) get_gmt_from_date($datetime, 'U'); // Now back to a unix timestamp - } + public function delete_access_conditions() + { + $mepr_db = new MeprDb(); - switch($unit) { - case 'days': - $days_ts = MeprUtils::days($amount); - if((time() - $ts) > $days_ts) - return true; - break; - case 'weeks': - $weeks_ts = MeprUtils::weeks($amount); - if((time() - $ts) > $weeks_ts) - return true; - break; - case 'months': - $months_ts = MeprUtils::months($amount, $ts); - if((time() - $ts) > $months_ts) - return true; - break; - case 'years': - $years_ts = MeprUtils::years($amount, $ts); - if((time() - $ts) > $years_ts) - return true; - break; + return $mepr_db->delete_records($mepr_db->rule_access_conditions, ['rule_id' => $this->ID]); } - return false; - } - - public function get_formatted_accesses() { - $formatted_array = array(); + public static function get_time_units() + { + return [ + __('day(s)', 'memberpress') => 'days', + __('week(s)', 'memberpress') => 'weeks', + __('month(s)', 'memberpress') => 'months', + __('year(s)', 'memberpress') => 'years', + ]; + } - foreach($this->mepr_access as $access_key => $access_values) { - if($access_key == 'membership') { - foreach($access_values as $access) { - $product = get_post($access); - if($product) { - $formatted_array[] = $product->post_title; - } + public static function get_expires_after($type, $fixed_date = null) + { + switch ($type) { + case 'registers': + return __('member registers', 'memberpress'); + break; + case 'fixed': + return MeprAppHelper::format_date($fixed_date); + break; + case 'rule-products': + return __('member purchases any membership for this rule', 'memberpress'); + break; + default: + $product = new MeprProduct($type); + return sprintf(__('member purchases %s', 'memberpress'), $product->post_title); + break; } - } - else { - $formatted_array = array_merge($formatted_array, $access_values); - } } - return $formatted_array; - } - - public function access_conditions() { - $mepr_db = new MeprDb(); - - return $mepr_db->get_records($mepr_db->rule_access_conditions, array('rule_id' => $this->ID)); - } - - public function delete_access_conditions() { - $mepr_db = new MeprDb(); - - return $mepr_db->delete_records($mepr_db->rule_access_conditions, array('rule_id' => $this->ID)); - } - - public static function get_time_units() { - return array( - __('day(s)', 'memberpress') => 'days', - __('week(s)', 'memberpress') => 'weeks', - __('month(s)', 'memberpress') => 'months', - __('year(s)', 'memberpress') => 'years' - ); - } - - public static function get_expires_after($type, $fixed_date = null) { - switch ($type) { - case 'registers': - return __('member registers', 'memberpress'); - break; - case 'fixed': - return MeprAppHelper::format_date($fixed_date); - break; - case 'rule-products': - return __('member purchases any membership for this rule', 'memberpress'); - break; - default: - $product = new MeprProduct($type); - return sprintf(__('member purchases %s', 'memberpress'), $product->post_title); - break; - } - } - - public function store_meta() { - update_post_meta($this->ID, self::$mepr_type_str, $this->mepr_type); - update_post_meta($this->ID, self::$mepr_content_str, $this->mepr_content); - update_post_meta($this->ID, self::$is_mepr_content_regexp_str, $this->is_mepr_content_regexp); - - update_post_meta($this->ID, self::$drip_enabled_str, $this->drip_enabled); - update_post_meta($this->ID, self::$drip_amount_str, $this->drip_amount); - update_post_meta($this->ID, self::$drip_unit_str, $this->drip_unit); - update_post_meta($this->ID, self::$drip_after_fixed_str, $this->drip_after_fixed); - update_post_meta($this->ID, self::$drip_after_str, $this->drip_after); - update_post_meta($this->ID, self::$expires_enabled_str, $this->expires_enabled); - update_post_meta($this->ID, self::$expires_amount_str, $this->expires_amount); - update_post_meta($this->ID, self::$expires_unit_str, $this->expires_unit); - update_post_meta($this->ID, self::$expires_after_str, $this->expires_after); - update_post_meta($this->ID, self::$expires_after_fixed_str, $this->expires_after_fixed); - update_post_meta($this->ID, self::$unauth_excerpt_type_str, $this->unauth_excerpt_type); - update_post_meta($this->ID, self::$unauth_excerpt_size_str, $this->unauth_excerpt_size); - update_post_meta($this->ID, self::$unauth_message_type_str, $this->unauth_message_type); - update_post_meta($this->ID, self::$unauth_message_str, $this->unauth_message); - update_post_meta($this->ID, self::$unauth_login_str, $this->unauth_login); - update_post_meta($this->ID, self::$unauth_modern_paywall_str, $this->unauth_modern_paywall); - update_post_meta($this->ID, self::$auto_gen_title_str, $this->auto_gen_title); - } - - public static function cleanup_db() /*dontTest*/ - { - global $wpdb; - - $date = time(); - $last_run = get_option(self::$last_run_str, 0); //Prevents all this code from executing on every page load - - if(($date - $last_run) > 86400) { - update_option(self::$last_run_str, $date); - $sq1 = "SELECT ID + public function store_meta() + { + update_post_meta($this->ID, self::$mepr_type_str, $this->mepr_type); + update_post_meta($this->ID, self::$mepr_content_str, $this->mepr_content); + update_post_meta($this->ID, self::$is_mepr_content_regexp_str, $this->is_mepr_content_regexp); + + update_post_meta($this->ID, self::$drip_enabled_str, $this->drip_enabled); + update_post_meta($this->ID, self::$drip_amount_str, $this->drip_amount); + update_post_meta($this->ID, self::$drip_unit_str, $this->drip_unit); + update_post_meta($this->ID, self::$drip_after_fixed_str, $this->drip_after_fixed); + update_post_meta($this->ID, self::$drip_after_str, $this->drip_after); + update_post_meta($this->ID, self::$expires_enabled_str, $this->expires_enabled); + update_post_meta($this->ID, self::$expires_amount_str, $this->expires_amount); + update_post_meta($this->ID, self::$expires_unit_str, $this->expires_unit); + update_post_meta($this->ID, self::$expires_after_str, $this->expires_after); + update_post_meta($this->ID, self::$expires_after_fixed_str, $this->expires_after_fixed); + update_post_meta($this->ID, self::$unauth_excerpt_type_str, $this->unauth_excerpt_type); + update_post_meta($this->ID, self::$unauth_excerpt_size_str, $this->unauth_excerpt_size); + update_post_meta($this->ID, self::$unauth_message_type_str, $this->unauth_message_type); + update_post_meta($this->ID, self::$unauth_message_str, $this->unauth_message); + update_post_meta($this->ID, self::$unauth_login_str, $this->unauth_login); + update_post_meta($this->ID, self::$unauth_modern_paywall_str, $this->unauth_modern_paywall); + update_post_meta($this->ID, self::$auto_gen_title_str, $this->auto_gen_title); + } + + public static function cleanup_db() /*dontTest*/ + { + global $wpdb; + + $date = time(); + $last_run = get_option(self::$last_run_str, 0); // Prevents all this code from executing on every page load + + if (($date - $last_run) > 86400) { + update_option(self::$last_run_str, $date); + $sq1 = "SELECT ID FROM {$wpdb->posts} - WHERE post_type = '".self::$cpt."' AND + WHERE post_type = '" . self::$cpt . "' AND post_status = 'auto-draft'"; - $sq1_res = $wpdb->get_col($sq1); - if(!empty($sq1_res)) { - $post_ids = implode(',', $sq1_res); + $sq1_res = $wpdb->get_col($sq1); + if (!empty($sq1_res)) { + $post_ids = implode(',', $sq1_res); - $q1 = "DELETE + $q1 = "DELETE FROM {$wpdb->postmeta} WHERE post_id IN ({$post_ids})"; - $q2 = "DELETE + $q2 = "DELETE FROM {$wpdb->posts} - WHERE post_type = '".self::$cpt."' AND + WHERE post_type = '" . self::$cpt . "' AND post_status = 'auto-draft'"; - $wpdb->query($q1); - $wpdb->query($q2); - } - } - } - - /** This returns the directory where rule files will be stored - * for use with the rewrite (via .htaccess) system. - */ - public static function rewrite_rule_file_dir($escape = false) { - $rule_file_path_array = wp_upload_dir(); - $rule_file_path = $rule_file_path_array['basedir']; - $rule_file_dir = "{$rule_file_path}/mepr/rules"; - - if(!is_dir($rule_file_dir)) // Make sure it exists - @mkdir($rule_file_dir, 0777, true); - - //Use forward slashes (works in windows too) - $rule_file_dir = str_replace('\\', "/", $rule_file_dir); - - if($escape) - $rule_file_dir = str_replace(array(' ', '(', ')'), array("\ ", "\(", "\)"), $rule_file_dir); - - return $rule_file_dir; - } - - //THESE TWO FUNCTIONS SHOULD PROBABLY BE DEPRECATED AT SOME POINT - //IN FAVOR OF THE current_user_can('memberpress-authorized') SYSTEM BLAIR PUT IN PLACE INSTEAD - //PHP Snippet wrapper (returns opposite of is_protected_by_rule() - public static function is_allowed_by_rule($rule_id) { - return !(self::is_protected_by_rule($rule_id)); - } - - //PHP Snippet Code - public static function is_protected_by_rule($rule_id) { - $current_post = MeprUtils::get_current_post(); - - //Check if we've been given sanitary input, if not this snippet - //is no good so let's return false here - if(!is_numeric($rule_id) || (int)$rule_id <= 0) - return false; - - //Check if user is logged in - if(!MeprUtils::is_user_logged_in()) - return true; - - //No sense loading the rule until we know the user is logged in - $rule = new MeprRule($rule_id); - - //If rule doesn't exist, has no memberships associated with it, or - //we're an Admin let's return the full content - if(!isset($rule->ID) || (int)$rule->ID <= 0 || MeprUtils::is_mepr_admin()) - return false; - - //Make sure this page/post/cpt is not in the "except" list of an all_* Rule - //TODO -- really need to take the "except" list into consideration here using $current_post if it's set - - //Now we know the user is logged in and the rule is valid - //let's see if they have access through memberships or members rule conditions - $user = MeprUtils::get_currentuserinfo(); - return (false === $user->has_access_from_rule($rule->ID)); - } - - public static function get_global_unauth_settings() { - $mepr_options = MeprOptions::fetch(); - - return (object)array( - 'excerpt_type' => ($mepr_options->unauth_show_excerpts?$mepr_options->unauth_excerpt_type:'hide'), - 'excerpt_size' => $mepr_options->unauth_excerpt_size, - 'excerpt' => '', - 'message_type' => 'custom', - 'message' => $mepr_options->unauthorized_message, - 'unauth_login' => $mepr_options->unauth_show_login, - 'show_login' => ($mepr_options->unauth_show_login == 'show'), - 'modern_paywall' => false - ); - } - - public static function get_post_unauth_settings($post) { - // Get values - $unauth_message_type = get_post_meta( $post->ID, '_mepr_unauthorized_message_type', true ); - $unauth_message = get_post_meta( $post->ID, '_mepr_unauthorized_message', true ); - $unauth_login = get_post_meta( $post->ID, '_mepr_unauth_login', true ); - $unauth_excerpt_type = get_post_meta( $post->ID, '_mepr_unauth_excerpt_type', true ); - $unauth_excerpt_size = get_post_meta( $post->ID, '_mepr_unauth_excerpt_size', true ); - - // Get defaults - $unauth_message_type = (($unauth_message_type != '')?$unauth_message_type:'default'); - $unauth_message = (($unauth_message != '')?$unauth_message:''); - $unauth_login = (($unauth_login != '')?$unauth_login:'default'); - $unauth_excerpt_type = (($unauth_excerpt_type != '')?$unauth_excerpt_type:'default'); - $unauth_excerpt_size = (($unauth_excerpt_size != '')?$unauth_excerpt_size:100); - - return (object)compact( - 'unauth_message_type', 'unauth_message', 'unauth_login', - 'unauth_excerpt_type', 'unauth_excerpt_size' - ); - } - - public static function get_unauth_settings_for($post) { - $mepr_options = MeprOptions::fetch(); - - $unauth = (object)array(); - $global_settings = self::get_global_unauth_settings(); - $post_settings = self::get_post_unauth_settings($post); - - $rules = MeprRule::get_rules($post); - - //Don't allow these settings to work on the account page without a Rule being attached to it - if(empty($rules) && $post->ID != $mepr_options->account_page_id) { return $global_settings; } //If we're gonna return global settings, let's make sure they're all there and that they match what would've been returned in by the $unauth object below. Should probably fix $post_settings to use the same var names as well, but since we don't return $post_settings ever, we should be ok for now. - - // TODO: Make this a bit more sophisticated? For now just pick the first rule. - if(isset($rules[0])) - $rule = $rules[0]; - else - $rule = new MeprRule(); - - // - Excerpts - if($post_settings->unauth_excerpt_type != 'default') { - $unauth->excerpt_type = $post_settings->unauth_excerpt_type; - $unauth->excerpt_size = (int) $post_settings->unauth_excerpt_size; - } - elseif($rule->unauth_excerpt_type != 'default') { - $unauth->excerpt_type = $rule->unauth_excerpt_type; - $unauth->excerpt_size = (int) $rule->unauth_excerpt_size; + $wpdb->query($q1); + $wpdb->query($q2); + } + } } - else { - $unauth->excerpt_type = $global_settings->excerpt_type; - $unauth->excerpt_size = (int) $global_settings->excerpt_size; + + /** + * This returns the directory where rule files will be stored + * for use with the rewrite (via .htaccess) system. + */ + public static function rewrite_rule_file_dir($escape = false) + { + $rule_file_path_array = wp_upload_dir(); + $rule_file_path = $rule_file_path_array['basedir']; + $rule_file_dir = "{$rule_file_path}/mepr/rules"; + + if (!is_dir($rule_file_dir)) { // Make sure it exists + @mkdir($rule_file_dir, 0777, true); + } + + // Use forward slashes (works in windows too) + $rule_file_dir = str_replace('\\', '/', $rule_file_dir); + + if ($escape) { + $rule_file_dir = str_replace([' ', '(', ')'], ['\ ', '\(', '\)'], $rule_file_dir); + } + + return $rule_file_dir; } - // Set the actual Excerpt based on the type & size - if($unauth->excerpt_type == 'custom') { - // Let's avoid recursion here people - if(MeprUser::manually_place_account_form($post)) { - $content = preg_replace('/\[mepr[-_]account[-_]form\]/','',$post->post_content); - } - else { - $content = $post->post_content; - } - - $unauth->excerpt = MeprUtils::format_content($content); - - if($unauth->excerpt_size) { //if set to 0, return the whole post -- though why protect it all in this case? - $unauth->excerpt = strip_tags($unauth->excerpt); - //mbstring? - $unauth->excerpt = (extension_loaded('mbstring'))?mb_substr($unauth->excerpt, 0, $unauth->excerpt_size):substr($unauth->excerpt, 0, $unauth->excerpt_size); - //Re-add

    's back in below to preserve some formatting at least - $unauth->excerpt = wpautop($unauth->excerpt . "..."); - } + // THESE TWO FUNCTIONS SHOULD PROBABLY BE DEPRECATED AT SOME POINT + // IN FAVOR OF THE current_user_can('memberpress-authorized') SYSTEM BLAIR PUT IN PLACE INSTEAD + // PHP Snippet wrapper (returns opposite of is_protected_by_rule() + public static function is_allowed_by_rule($rule_id) + { + return !(self::is_protected_by_rule($rule_id)); } - elseif($unauth->excerpt_type == 'more') { //Show till the more tag - $pos = (extension_loaded('mbstring'))?mb_strpos($post->post_content, ' - access_url) && !empty($prd->access_url)): ?> + access_url) && !empty($prd->access_url)) : ?> post_title, $txn)); ?> - + post_title, $txn)); ?> - + label) : _x('Unknown', 'ui', 'memberpress')); ?> status)); ?> trans_num); ?> - +

    - - << + + << - - >> + + >>
    -
    +if (!empty($subscriptions)) { + $alt = false; + ?>
    @@ -21,183 +23,173 @@ sub_type) == 'transaction') { - $is_sub = false; - $txn = $sub = new MeprTransaction($s->id); - $pm = $txn->payment_method(); - $prd = $txn->product(); - $group = $prd->group(); - $default = _x('Never', 'ui', 'memberpress'); - if($txn->txn_type == MeprTransaction::$fallback_str && $mepr_current_user->subscription_in_group($group)) { - //Skip fallback transactions when user has an active sub in the fallback group - continue; - } - } - else { - $is_sub = true; - $sub = new MeprSubscription($s->id); - $txn = $sub->latest_txn(); - $pm = $sub->payment_method(); - $prd = $sub->product(); - $group = $prd->group(); + foreach ($subscriptions as $s) : + if (trim($s->sub_type) == 'transaction') { + $is_sub = false; + $txn = $sub = new MeprTransaction($s->id); + $pm = $txn->payment_method(); + $prd = $txn->product(); + $group = $prd->group(); + $default = _x('Never', 'ui', 'memberpress'); + if ($txn->txn_type == MeprTransaction::$fallback_str && $mepr_current_user->subscription_in_group($group)) { + // Skip fallback transactions when user has an active sub in the fallback group + continue; + } + } else { + $is_sub = true; + $sub = new MeprSubscription($s->id); + $txn = $sub->latest_txn(); + $pm = $sub->payment_method(); + $prd = $sub->product(); + $group = $prd->group(); - if($txn == false || !($txn instanceof MeprTransaction) || $txn->id <= 0) { - $default = _x('Unknown', 'ui', 'memberpress'); - } - else if(trim($txn->expires_at) == MeprUtils::db_lifetime() or empty($txn->expires_at)) { - $default = _x('Never', 'ui', 'memberpress'); + if ($txn == false || !($txn instanceof MeprTransaction) || $txn->id <= 0) { + $default = _x('Unknown', 'ui', 'memberpress'); + } elseif (trim($txn->expires_at) == MeprUtils::db_lifetime() or empty($txn->expires_at)) { + $default = _x('Never', 'ui', 'memberpress'); + } else { + $default = _x('Unknown', 'ui', 'memberpress'); + } } - else { - $default = _x('Unknown', 'ui', 'memberpress'); - } - } - $mepr_options = MeprOptions::fetch(); - $alt = !$alt; // Facilitiates the alternating lines - ?> - + $mepr_options = MeprOptions::fetch(); + $alt = !$alt; // Facilitiates the alternating lines + ?> + @@ -208,19 +200,18 @@
    - ' . _x('You have no active subscriptions to display.', 'ui', 'memberpress') . ''; + ' . _x('You have no active subscriptions to display.', 'ui', 'memberpress') . ''; } MeprHooks::do_action('mepr_account_subscriptions', $mepr_current_user); diff --git a/app/views/account/subscriptions_widget.php b/app/views/account/subscriptions_widget.php index 52401c7..caeb6dc 100644 --- a/app/views/account/subscriptions_widget.php +++ b/app/views/account/subscriptions_widget.php @@ -1,71 +1,73 @@ - + - +
    -active_product_subscriptions('products'); +active_product_subscriptions('products'); - if(empty($subs)) { ?> + if (empty($subs)) { ?>
    - ID, $prd->ID); - $subs_sorted[$prd->ID] = $created_at; - } elseif ($order_by == 'title') { - $subs_sorted[$prd->ID] = $prd->post_title; - } - } - if($order_by == 'date') { - if($order == 'asc') { - asort($subs_sorted); - } else { - arsort($subs_sorted); - } - } elseif($order_by == 'title') { - if($order == 'desc') { - arsort($subs_sorted); - } else { - asort($subs_sorted); - } - } - $subs_sorted = array_keys($subs_sorted); + if (!empty($order_by)) { + $subs_sorted = []; + foreach ($subs as $prd) { + if ($order_by == 'date') { + $created_at = MeprUser::get_user_product_signup_date($user->ID, $prd->ID); + $subs_sorted[$prd->ID] = $created_at; + } elseif ($order_by == 'title') { + $subs_sorted[$prd->ID] = $prd->post_title; + } + } + if ($order_by == 'date') { + if ($order == 'asc') { + asort($subs_sorted); + } else { + arsort($subs_sorted); + } + } elseif ($order_by == 'title') { + if ($order == 'desc') { + arsort($subs_sorted); + } else { + asort($subs_sorted); + } + } + $subs_sorted = array_keys($subs_sorted); - $subs = array(); - foreach($subs_sorted as $sub) { - $subs[] = new MeprProduct($sub); - } - } - ?> + $subs = []; + foreach ($subs_sorted as $sub) { + $subs[] = new MeprProduct($sub); + } + } + ?>
      - ID, $prev_dups, false)) { - $prev_dups[] = $prd->ID; + ID, $prev_dups, false)) { + $prev_dups[] = $prd->ID; - if($use_access_url && !empty($prd->access_url)) { ?> + if ($use_access_url && !empty($prd->access_url)) { ?> - +
    • post_title; ?>
    • - +
    -

    - - +
    diff --git a/app/views/admin/about/1-2-4.php b/app/views/admin/about/1-2-4.php index 38bbff0..31495ae 100644 --- a/app/views/admin/about/1-2-4.php +++ b/app/views/admin/about/1-2-4.php @@ -1,4 +1,6 @@ - +

    @@ -10,6 +12,10 @@  • 
     • 

    -

    ', '', '' ); ?>

    +

    ', + '', + '' +); ?>

    diff --git a/app/views/admin/about/index.php b/app/views/admin/about/index.php index 89c513c..d3a3233 100644 --- a/app/views/admin/about/index.php +++ b/app/views/admin/about/index.php @@ -1 +1,3 @@ - + + +
    - + - + - + @@ -84,7 +86,7 @@
    - + diff --git a/app/views/admin/addons/ui.php b/app/views/admin/addons/ui.php index 30937a1..9a8a926 100644 --- a/app/views/admin/addons/ui.php +++ b/app/views/admin/addons/ui.php @@ -1,4 +1,6 @@ - +
    @@ -8,23 +10,22 @@ ', esc_url(admin_url('admin.php?page=memberpress-addons&refresh=true'))), - '' + esc_html__('Improve your memberships with our premium add-ons. Missing an add-on that you think you should be able to see? Click the %1$sRefresh Add-ons%2$s button above.', 'memberpress'), + sprintf('', esc_url(admin_url('admin.php?page=memberpress-addons&refresh=true'))), + '' ); - ?> + ?>

    - - +
    $info) : + foreach ($addons as $slug => $info) : $info = (object) $info; $status_label = ''; $action_class = 'mepr-addon-action'; @@ -32,19 +33,19 @@ $installed = isset($info->extra_info->directory) && is_dir(WP_PLUGIN_DIR . '/' . $info->extra_info->directory); $active = isset($info->extra_info->main_file) && is_plugin_active($info->extra_info->main_file); - if($installed && $active) { - $status = 'active'; - $status_label = esc_html__('Active', 'memberpress'); - } elseif(!$installed && $info->installable) { - $status = 'download'; - $status_label = esc_html__('Not Installed', 'memberpress'); - } elseif($installed && !$active) { - $status = 'inactive'; - $status_label = esc_html__('Inactive', 'memberpress'); + if ($installed && $active) { + $status = 'active'; + $status_label = esc_html__('Active', 'memberpress'); + } elseif (!$installed && $info->installable) { + $status = 'download'; + $status_label = esc_html__('Not Installed', 'memberpress'); + } elseif ($installed && !$active) { + $status = 'inactive'; + $status_label = esc_html__('Inactive', 'memberpress'); } else { - $status = 'upgrade'; + $status = 'upgrade'; } - ?> + ?>
    @@ -56,45 +57,39 @@
    - - +
    %s', - $status_label - ) + esc_html__('Status: %s', 'memberpress'), + sprintf( + '%s', + $status_label + ) ); - ?> + ?>
    - + + - - - +
    - - + - - + - - + - @@ -111,7 +106,6 @@
    -

    diff --git a/app/views/admin/admin-notification.php b/app/views/admin/admin-notification.php index c322d5c..499e6a0 100644 --- a/app/views/admin/admin-notification.php +++ b/app/views/admin/admin-notification.php @@ -3,7 +3,7 @@

    - +

    + // wp_admin_css( 'install', true ); + // wp_admin_css( 'ie', true ); + ?> @@ -37,7 +39,7 @@

     

    - +

     

    diff --git a/app/views/admin/db/upgrade_needed.php b/app/views/admin/db/upgrade_needed.php index 23df8eb..13d1223 100644 --- a/app/views/admin/db/upgrade_needed.php +++ b/app/views/admin/db/upgrade_needed.php @@ -1,10 +1,12 @@ - + - + <?php _e('Memberpress needs to upgrade your database', 'memberpress'); ?> @@ -25,18 +27,18 @@ var upgrade_db_success = function() { window.location.href = ' 'mepr_db_upgrade_success') + 'admin-ajax.php', + ['db_upgrade_success', 'mepr_db_upgrade_nonce'], + ['action' => 'mepr_db_upgrade_success'] ); ?>'; }; var upgrade_db_not_needed = function() { window.location.href = ' 'mepr_db_upgrade_not_needed') + 'admin-ajax.php', + ['db_upgrade_not_needed', 'mepr_db_upgrade_nonce'], + ['action' => 'mepr_db_upgrade_not_needed'] ); ?>'; }; @@ -48,9 +50,9 @@ if(error_count >= retries) { window.location.href = ' 'mepr_db_upgrade_error') + 'admin-ajax.php', + ['db_upgrade_error', 'mepr_db_upgrade_nonce'], + ['action' => 'mepr_db_upgrade_error'] ); ?>'; } else { @@ -177,11 +179,11 @@ function(data) { ?> + ?>">

    -->

    or diff --git a/app/views/admin/db/upgrade_not_needed.php b/app/views/admin/db/upgrade_not_needed.php index 463c47e..527efad 100644 --- a/app/views/admin/db/upgrade_not_needed.php +++ b/app/views/admin/db/upgrade_not_needed.php @@ -1,16 +1,18 @@ - + - + <?php _e('MemberPress database upgrade not needed', 'memberpress'); ?> + // wp_admin_css( 'install', true ); + // wp_admin_css( 'ie', true ); + ?> diff --git a/app/views/admin/db/upgrade_success.php b/app/views/admin/db/upgrade_success.php index 40c2c05..093264f 100644 --- a/app/views/admin/db/upgrade_success.php +++ b/app/views/admin/db/upgrade_success.php @@ -1,16 +1,18 @@ - + - + <?php _e('MemberPress database upgrade was successful', 'memberpress'); ?> + // wp_admin_css( 'install', true ); + // wp_admin_css( 'ie', true ); + ?> diff --git a/app/views/admin/drm/email.php b/app/views/admin/drm/email.php index fc529b4..73f3323 100644 --- a/app/views/admin/drm/email.php +++ b/app/views/admin/drm/email.php @@ -1,4 +1,6 @@ - + @@ -18,7 +20,7 @@

    - +

    diff --git a/app/views/admin/drm/index.php b/app/views/admin/drm/index.php index 89c513c..d3a3233 100644 --- a/app/views/admin/drm/index.php +++ b/app/views/admin/drm/index.php @@ -1 +1,3 @@ - + @@ -7,33 +9,33 @@
    -

    ALERT! MemberPress Backend is Deactivated', 'memberpress' ); ?>

    +

    ALERT! MemberPress Backend is Deactivated', 'memberpress'); ?>

    -

    +

      -
    • -
    • -
    • +
    • +
    • +
    -

    +

    - - - - - + + + + +

    -

    Account Page, and enter it below:', 'memberpress' ); ?>

    +

    Account Page, and enter it below:', 'memberpress'); ?>

    -
    -
    +
    +
    - +

    @@ -50,7 +52,7 @@ $('.mepr-notice-modal').remove(); $('#wpbody').remove(); }); - + $('body').on('click', '#mepr-drm-btn-without-license', function (e) { e.preventDefault(); var $button = $(this); @@ -60,7 +62,7 @@ dataType: 'json', data: { action: 'mepr_drm_use_without_license', - _ajax_nonce: '' + _ajax_nonce: '' } }) .done(function (response) { @@ -81,7 +83,7 @@ }); }); - + $('body').on('click', '#mepr-drm-activate-license-key', function (e) { e.preventDefault(); @@ -99,7 +101,7 @@ $('.mepr-drm-messages').hide(); var buttonText = $button.val(); $button.val( '...' ); - var generic_message = ''; + var generic_message = ''; $.ajax({ url: ajaxurl, @@ -107,7 +109,7 @@ dataType: 'json', data: { action: 'mepr_drm_activate_license', - _ajax_nonce: '', + _ajax_nonce: '', key: key } }) @@ -123,7 +125,7 @@ $('.mepr-key-success').show(); $('#mepr-drm-form').remove(); setTimeout(function(){ - window.location.href=""; + window.location.href=""; }, 3000); } }) diff --git a/app/views/admin/drm/modal_fee.php b/app/views/admin/drm/modal_fee.php index 9ea41f1..cfb0384 100644 --- a/app/views/admin/drm/modal_fee.php +++ b/app/views/admin/drm/modal_fee.php @@ -1,22 +1,24 @@ -
    -

    ALERT! MemberPress is running without a license', 'memberpress' ); ?>

    +

    ALERT! MemberPress is running without a license', 'memberpress'); ?>

    - +

    -

    Account Page, and enter it below:', 'memberpress' ); ?>

    +

    Account Page, and enter it below:', 'memberpress'); ?>

    -
    -
    +
    +
    @@ -51,7 +53,7 @@ $('.mepr-drm-messages').hide(); var buttonText = $button.val(); $button.val( '...' ); - var generic_message = ''; + var generic_message = ''; $.ajax({ url: ajaxurl, @@ -59,7 +61,7 @@ dataType: 'json', data: { action: 'mepr_drm_activate_license', - _ajax_nonce: '', + _ajax_nonce: '', key: key } }) @@ -75,7 +77,7 @@ $('.mepr-key-success').show(); $('#mepr-drm-form').remove(); setTimeout(function(){ - window.location.href=""; + window.location.href=""; }, 3000); } }) diff --git a/app/views/admin/drm/notices/fee_notice.php b/app/views/admin/drm/notices/fee_notice.php index 402f049..9f59b28 100644 --- a/app/views/admin/drm/notices/fee_notice.php +++ b/app/views/admin/drm/notices/fee_notice.php @@ -1,11 +1,13 @@ - +
    -

    +

    - +

    \ No newline at end of file diff --git a/app/views/admin/drm/notices/index.php b/app/views/admin/drm/notices/index.php index 89c513c..d3a3233 100644 --- a/app/views/admin/drm/notices/index.php +++ b/app/views/admin/drm/notices/index.php @@ -1 +1,3 @@ - + +

    -

    +

    - +

    diff --git a/app/views/admin/drm/notices/low_warning.php b/app/views/admin/drm/notices/low_warning.php index 8462384..7f9330e 100644 --- a/app/views/admin/drm/notices/low_warning.php +++ b/app/views/admin/drm/notices/low_warning.php @@ -1,18 +1,20 @@ - +

    - +
      -
    • Pricing Page.', 'memberpress' ), $drm_info['pricing_link'] ); ?>
    • -
    • Click here to enter and activate your new license key.', 'memberpress'), $drm_info['activation_link'] ); ?>
    • -
    • +
    • Pricing Page.', 'memberpress'), $drm_info['pricing_link']); ?>
    • +
    • Click here to enter and activate your new license key.', 'memberpress'), $drm_info['activation_link']); ?>
    • +
    - +
      -
    • Account Page.', 'memberpress' ), $drm_info['account_link'] ); ?>
    • -
    • Click here to enter and activate it.', 'memberpress'), $drm_info['activation_link'] ); ?>
    • -
    • +
    • Account Page.', 'memberpress'), $drm_info['account_link']); ?>
    • +
    • Click here to enter and activate it.', 'memberpress'), $drm_info['activation_link']); ?>
    • +
    - +
    \ No newline at end of file diff --git a/app/views/admin/drm/notices/medium_warning.php b/app/views/admin/drm/notices/medium_warning.php index 6ab3928..85638f0 100644 --- a/app/views/admin/drm/notices/medium_warning.php +++ b/app/views/admin/drm/notices/medium_warning.php @@ -1,19 +1,21 @@ - +

    - +
      -
    • Pricing Page.', 'memberpress' ), $drm_info['pricing_link'] ); ?>
    • -
    • Click here to enter and activate your new license key.', 'memberpress'), $drm_info['activation_link'] ); ?>
    • -
    • +
    • Pricing Page.', 'memberpress'), $drm_info['pricing_link']); ?>
    • +
    • Click here to enter and activate your new license key.', 'memberpress'), $drm_info['activation_link']); ?>
    • +
    - +
      -
    • Account Page.', 'memberpress' ), $drm_info['account_link'] ); ?>
    • -
    • Click here to enter and activate it.', 'memberpress'), $drm_info['activation_link'] ); ?>
    • -
    • +
    • Account Page.', 'memberpress'), $drm_info['account_link']); ?>
    • +
    • Click here to enter and activate it.', 'memberpress'), $drm_info['activation_link']); ?>
    • +
    - +
    \ No newline at end of file diff --git a/app/views/admin/drm/notices/notice.php b/app/views/admin/drm/notices/notice.php index baaa3b6..4ed6f34 100644 --- a/app/views/admin/drm/notices/notice.php +++ b/app/views/admin/drm/notices/notice.php @@ -1,9 +1,11 @@ - -
    +
    \ No newline at end of file diff --git a/app/views/admin/emails/options.php b/app/views/admin/emails/options.php index b2bc57f..646f34d 100644 --- a/app/views/admin/emails/options.php +++ b/app/views/admin/emails/options.php @@ -1,15 +1,19 @@ - +
    - dashed_name(), - $email->title, - $email->description ); ?> + dashed_name(), + $email->title, + $email->description + ); ?>

  • - body(), - $email->field_name('body', true), - array( 'textarea_name' => $email->field_name('body') ) - ); ?> + body(), + $email->field_name('body', true), + ['textarea_name' => $email->field_name('body')] + ); ?>
  • @@ -61,9 +66,11 @@ class="mepr-reset-email button" id="field_name('use_template', true); ?>"use_template()); ?>/> - dashed_name() . '-template', - __('Default Email Template', 'memberpress'), - __('When this is checked the body of this email will be wrapped in the default email template.', 'memberpress') ); ?> + dashed_name() . '-template', + __('Default Email Template', 'memberpress'), + __('When this is checked the body of this email will be wrapped in the default email template.', 'memberpress') + ); ?>
  • diff --git a/app/views/admin/errors.php b/app/views/admin/errors.php index 3e63dec..2259991 100644 --- a/app/views/admin/errors.php +++ b/app/views/admin/errors.php @@ -1,15 +1,17 @@ - + - +
      - +
    - +

    diff --git a/app/views/admin/gateways/paypal/connect-migrate-prompt.php b/app/views/admin/gateways/paypal/connect-migrate-prompt.php index f12e530..6c83a97 100644 --- a/app/views/admin/gateways/paypal/connect-migrate-prompt.php +++ b/app/views/admin/gateways/paypal/connect-migrate-prompt.php @@ -1,7 +1,9 @@
    @@ -16,19 +18,19 @@
    PayPal logo
    - is_paypal_connected() or $pm->is_paypal_connected_live() ) { ?> - is_paypal_connected() ) { ?> + is_paypal_connected() or $pm->is_paypal_connected_live()) { ?> + is_paypal_connected()) { ?>

    - - test_merchant_id ) ) { ?> + + test_merchant_id)) { ?>
    - test_merchant_id ) ) ?> - + test_merchant_id)) ?> +

    - is_paypal_email_confirmed() ) { ?> + is_paypal_email_confirmed()) { ?>

    - + + data-verify-url="" + x-on:click="verifyEmail">

    - - + + - is_paypal_connected_live() ) { ?> + is_paypal_connected_live()) { ?>

    - - live_merchant_id ) ) { ?> + + live_merchant_id)) { ?>
    - live_merchant_id ) ) ?> - + live_merchant_id)) ?> +

    - is_paypal_email_confirmed_live() ) { ?> + is_paypal_email_confirmed_live()) { ?>

    - + + data-verify-url="" + >

    - - - + + +

    - - live_merchant_id ) ) { ?> + + live_merchant_id)) { ?>
    - live_merchant_id ) ) ?> + live_merchant_id)) ?>

    @@ -81,19 +83,19 @@
    • + src="/Check_Mark.svg"/>
    • + src="/Check_Mark.svg"/>
    • + src="/Check_Mark.svg"/>
    • + src="/Check_Mark.svg"/>
    @@ -101,22 +103,22 @@
    • + src="/Check_Mark.svg"/>
    • + src="/Check_Mark.svg"/>
    • + src="/Check_Mark.svg"/>
    - test_auth_code ) || ! empty( $settings->live_auth_code ) ) { ?> + test_auth_code) || ! empty($settings->live_auth_code)) { ?>

    - - - -

    - -

    - - - />  - + data-method-id=""> + - test_auth_code ) && empty( $settings->live_auth_code ) ) { ?> + +

    + +

    + + + /> + + + + test_auth_code) && empty($settings->live_auth_code)) { ?>
    - is_paypal_connected_live() && ! $pm->is_paypal_connected() && isset( $memberpress_connect_url )) { ?> + is_paypal_connected_live() && ! $pm->is_paypal_connected() && isset($memberpress_connect_url)) { ?> - + - - is_paypal_connected_live() ) { - $disconnect_confirm_msg = __( 'Are you sure you want to disconnect? Your future renewing payments will not track properly...', 'memberpress' ); - ?> + + is_paypal_connected_live()) { + $disconnect_confirm_msg = __('Are you sure you want to disconnect? Your future renewing payments will not track properly...', 'memberpress'); + ?> - - + data-disconnect-confirm-msg="" + data-method-id="" + data-mepr-disconnect-paypal="1"> + + - + - - - - is_paypal_connected() ) { ?> - + + + + is_paypal_connected()) { ?> + - - + data-method-id="" + data-disconnect-confirm-msg="" + data-mepr-disconnect-paypal="1"> + + - + - - - + + +
    is_paypal_connected_live() || $pm->is_paypal_connected()) { ?> -  /> +  /> - is_paypal_connected_live() && ! $pm->is_paypal_connected() ) { ?> + is_paypal_connected_live() && ! $pm->is_paypal_connected()) { ?>
    @@ -291,7 +291,7 @@ class="button mepr-paypal-rollback-button" }, 2500); } }"> - +
    diff --git a/app/views/admin/gateways/stripe/checkboxes.php b/app/views/admin/gateways/stripe/checkboxes.php index c54e3c0..c17253a 100644 --- a/app/views/admin/gateways/stripe/checkboxes.php +++ b/app/views/admin/gateways/stripe/checkboxes.php @@ -2,20 +2,20 @@ $classes = ''; -if ( ! isset( $_GET['display-keys'] ) && ! isset( $_COOKIE['mepr_stripe_display_keys'] ) && ! defined( 'MEPR_DISABLE_STRIPE_CONNECT' ) ) { - $classes = 'class="mepr-hidden"'; +if (! isset($_GET['display-keys']) && ! isset($_COOKIE['mepr_stripe_display_keys']) && ! defined('MEPR_DISABLE_STRIPE_CONNECT')) { + $classes = 'class="mepr-hidden"'; } ?> - + - + - + - > + > - > + > diff --git a/app/views/admin/gateways/stripe/connect-migrate-prompt.php b/app/views/admin/gateways/stripe/connect-migrate-prompt.php index 4e689ba..4b48d6f 100644 --- a/app/views/admin/gateways/stripe/connect-migrate-prompt.php +++ b/app/views/admin/gateways/stripe/connect-migrate-prompt.php @@ -2,121 +2,135 @@ $classes = ''; -if ( ! isset( $_GET['display-keys'] ) && ! isset( $_COOKIE['mepr_stripe_display_keys'] ) && ! defined( 'MEPR_DISABLE_STRIPE_CONNECT' ) ) { - $classes = 'class="mepr-hidden"'; +if (! isset($_GET['display-keys']) && ! isset($_COOKIE['mepr_stripe_display_keys']) && ! defined('MEPR_DISABLE_STRIPE_CONNECT')) { + $classes = 'class="mepr-hidden"'; } ?> - - 'mepr_stripe_connect_refresh', 'method-id' => $id, '_wpnonce' => wp_create_nonce('stripe-refresh') ), admin_url('admin-ajax.php') ); - $disconnect_url = add_query_arg( array( 'action' => 'mepr_stripe_connect_disconnect', 'method-id' => $id, '_wpnonce' => wp_create_nonce('stripe-disconnect') ), admin_url('admin-ajax.php') ); - $disconnect_confirm_msg = __( 'Disconnecting from this Stripe Account will block webhooks from being processed, and prevent MemberPress subscriptions associated with it from working.', 'memberpress' ); - ?> + + 'mepr_stripe_connect_refresh', + 'method-id' => $id, + '_wpnonce' => wp_create_nonce('stripe-refresh'), + ], admin_url('admin-ajax.php')); + $disconnect_url = add_query_arg([ + 'action' => 'mepr_stripe_connect_disconnect', + 'method-id' => $id, + '_wpnonce' => wp_create_nonce('stripe-disconnect'), + ], admin_url('admin-ajax.php')); + $disconnect_confirm_msg = __('Disconnecting from this Stripe Account will block webhooks from being processed, and prevent MemberPress subscriptions associated with it from working.', 'memberpress'); + ?>
    - - - - ', $service_account_name, '' ); ?> + + + + ', $service_account_name, ''); ?>   > + class="stripe-btn mepr_stripe_refresh_button button-secondary"> - +
    - +
    Stripe logo
    -

    +

    />
    @@ -36,13 +36,13 @@ ', - '' + esc_html__('Some of these payment methods have limitations. %1$sClick here%2$s to learn more.', 'memberpress'), + '', + '' ); ?>

    - $payment_method) : ?> + $payment_method) : ?>
    - - __('Apple Pay', 'memberpress'), - 'google_pay' => __('Google Pay', 'memberpress'), - ]; - ?> - $domain_payment_method_label) : ?> - + + __('Apple Pay', 'memberpress'), + 'google_pay' => __('Google Pay', 'memberpress'), + ]; + ?> + $domain_payment_method_label) : ?> +
    - - + +
    - +
    @@ -83,13 +83,13 @@
    />
    />
      -
    • -
    • -
    • +
    • +
    • +
      -
    • -
    • -
    • +
    • +
    • +

    - <?php esc_attr_e( '"> + <?php esc_attr_e('">

    - +
    -

    -

    +

    +

    - <?php esc_attr_e( '"> + <?php esc_attr_e('">

    - - + +
    Stripe logo
    -

    +

      -
    • -
    • -
    • +
    • +
    • +
      -
    • -
    • -
    • +
    • +
    • +
    - <?php _e( '"> + <?php _e('">

    diff --git a/app/views/admin/gateways/stripe/keys.php b/app/views/admin/gateways/stripe/keys.php index a517773..444740b 100644 --- a/app/views/admin/gateways/stripe/keys.php +++ b/app/views/admin/gateways/stripe/keys.php @@ -3,38 +3,38 @@ $classes = ''; $show_keys = false; -if ( ! isset( $_GET['display-keys'] ) && ! isset( $_COOKIE['mepr_stripe_display_keys'] ) && ! defined( 'MEPR_DISABLE_STRIPE_CONNECT' ) ) { - $classes = 'class="mepr-hidden"'; +if (! isset($_GET['display-keys']) && ! isset($_COOKIE['mepr_stripe_display_keys']) && ! defined('MEPR_DISABLE_STRIPE_CONNECT')) { + $classes = 'class="mepr-hidden"'; } else { - $show_keys = true; + $show_keys = true; } ?> - +
    @@ -63,7 +63,7 @@ - - - + + +
    diff --git a/app/views/admin/get_started.php b/app/views/admin/get_started.php index de585fc..eecf6ab 100644 --- a/app/views/admin/get_started.php +++ b/app/views/admin/get_started.php @@ -1,30 +1,32 @@ - + diff --git a/app/views/admin/groups/custom_page_template_form.php b/app/views/admin/groups/custom_page_template_form.php index bc4b519..20b9ca9 100644 --- a/app/views/admin/groups/custom_page_template_form.php +++ b/app/views/admin/groups/custom_page_template_form.php @@ -1,11 +1,13 @@ - +
    use_custom_template); ?> />

    - custom_template ); ?> + custom_template); ?>
    diff --git a/app/views/admin/groups/form.php b/app/views/admin/groups/form.php index 3790de5..984e037 100644 --- a/app/views/admin/groups/form.php +++ b/app/views/admin/groups/form.php @@ -1,4 +1,6 @@ - +
    @@ -8,11 +10,11 @@ + ?> @@ -106,10 +110,12 @@
    is_upgrade_path); ?> /> @@ -30,11 +32,11 @@ + ?> upgrade_path_reset_period); ?> /> @@ -47,18 +49,20 @@

    + ?>

    ')); - ?> + MeprAppHelper::info_tooltip( + 'mepr-group-products-list', + __('Memberships', 'memberpress'), + sprintf(__('Here you can add/remove memberships from this group pricing page.%1$s%1$sThe order of the memberships is important here. Order the memberships so that the lowest tier membership is at the top of the list and the highest tier membership is at the bottom, with the other memberships in order in between.', 'memberpress'), '
    ') + ); + ?>

    Settings > ReadyLaunch tab. ReadyLaunch can currently only show 5 Membership plans per Group.', 'memberpress'); ?>
    @@ -81,20 +85,24 @@ disable_change_plan_popup); ?> /> + MeprAppHelper::info_tooltip( + 'mepr-disable-change-plan-pop-up', + __('Disable Change Plan Pop-Up', 'memberpress'), + __('This will take the user to the Group pricing page when they click on Change Plan instead of showing them the quick selection pop-up.', 'memberpress') + ); + ?>

    pricing_page_disabled); ?> />
    You can optionally provide an alternate URL to take the member to if they try to visit this page.', 'memberpress')); - ?> + MeprAppHelper::info_tooltip( + 'mepr-group-disable-pricing-page', + __('Disable Pricing Page', 'memberpress'), + __('This will disable the pricing page from being accessed on the front end of your site. It will return a 404 (not found) page if a user attempts to access it.

    You can optionally provide an alternate URL to take the member to if they try to visit this page.', 'memberpress') + ); + ?>
    -
    +
    + MeprAppHelper::info_tooltip( + 'mepr_transaction_product_id', + __('Initial Membership', 'memberpress'), + __('Select the initial membership you\'d like your new member to be part of.', 'memberpress') + ); + ?> - product_id, 'transaction_expires_at', 'transaction_product_id' ); ?> + product_id, 'transaction_expires_at', 'transaction_product_id'); ?>
    + MeprAppHelper::info_tooltip( + 'mepr_transaction_trans_num', + __('Transaction Number', 'memberpress'), + __('This is typically the id of the transaction that is generated by the gateway. Typically this value won\'t need to be changed unless you\'re referencing a transaction that happened outside of MemberPress for this user and membership.', 'memberpress') + ); + ?> @@ -119,10 +125,12 @@ + MeprAppHelper::info_tooltip( + 'mepr_transaction_amount', + __('Amount', 'memberpress'), + __('This is the amount the member initially paid for the membership. Unless money has changed hands outside of MemberPress (and it\'s being recorded here) then we recommend this value stay at zero so your repots will remain accurate.', 'memberpress') + ); + ?> currency_symbol; ?> @@ -141,11 +149,12 @@ + ?> gateway); ?> @@ -158,50 +167,51 @@ created_at, - 'transaction_created_at' + 'transaction[created_at]', + $transaction->created_at, + 'transaction_created_at' ); - ?> + ?>
    + ?> expires_at, - 'transaction_expires_at' + 'transaction[expires_at]', + 'transaction_product_id', + 'transaction_created_at', + $transaction->expires_at, + 'transaction_expires_at' ); - ?> + ?>
     
    -

    - - */ ?> +
     
    +

    + + */ ?>

    diff --git a/app/views/admin/members/row.php b/app/views/admin/members/row.php index c44ba0d..7436f39 100644 --- a/app/views/admin/members/row.php +++ b/app/views/admin/members/row.php @@ -1,240 +1,240 @@ + // Open the line + ?> - $column_display_name) { - //Style attributes for each col - $class = "class=\"{$column_name} column-{$column_name}\""; - $style = ""; - if(in_array($column_name, $hidden)) { - $style = ' style="display:none;"'; - } - $attributes = $class.$style; + $column_display_name) { + // Style attributes for each col + $class = "class=\"{$column_name} column-{$column_name}\""; + $style = ''; + if (in_array($column_name, $hidden)) { + $style = ' style="display:none;"'; + } + $attributes = $class . $style; - //$editlink = admin_url('user-edit.php?user_id='.(int)$rec->ID); - //$deletelink = admin_url('user-edit.php?user_id='.(int)$rec->ID); - $deletelink = wp_nonce_url( "users.php?action=delete&user={$rec->ID}", 'bulk-users' ); - $editlink = esc_url( add_query_arg( 'wp_http_referer', urlencode( wp_unslash( $_SERVER['REQUEST_URI'] ) ), get_edit_user_link( $rec->ID ) ) ); + // $editlink = admin_url('user-edit.php?user_id='.(int)$rec->ID); + // $deletelink = admin_url('user-edit.php?user_id='.(int)$rec->ID); + $deletelink = wp_nonce_url("users.php?action=delete&user={$rec->ID}", 'bulk-users'); + $editlink = esc_url(add_query_arg('wp_http_referer', urlencode(wp_unslash($_SERVER['REQUEST_URI'])), get_edit_user_link($rec->ID))); - //Display the cell - switch($column_name) { - case 'col_id': - ?> + // Display the cell + switch ($column_name) { + case 'col_id': + ?> >ID); ?> - + >email, 32); ?> - + >name); ?> - + > email, 32); ?>
    "> | - "> +
    - + >email); ?> - ID); - if($mepr_user->is_active()) { - $status = '' . __('Active', 'memberpress') . ''; - } - else if($mepr_user->has_expired()) { - $status = '' . __('Inactive', 'memberpress') . ''; - } - else { - $status = '' . __('None', 'memberpress') . ''; - } + ID); + if ($mepr_user->is_active()) { + $status = '' . __('Active', 'memberpress') . ''; + } elseif ($mepr_user->has_expired()) { + $status = '' . __('Inactive', 'memberpress') . ''; + } else { + $status = '' . __('None', 'memberpress') . ''; + } - ?> + ?> > - + >txn_count); ?> - + >expired_txn_count); ?> - + >active_txn_count); ?> - + >sub_count); ?> - username)); - $sub_counts = array( - __('Enabled', 'memberpress') => 'active', - __('Stopped', 'memberpress') => 'cancelled', - __('Pending', 'memberpress') => 'pending', - __('Paused', 'memberpress') => 'suspended', - ); - ?> + username)); + $sub_counts = [ + __('Enabled', 'memberpress') => 'active', + __('Stopped', 'memberpress') => 'cancelled', + __('Pending', 'memberpress') => 'pending', + __('Paused', 'memberpress') => 'suspended', + ]; + ?> > - $status) { - $status_count = "{$status}_sub_count"; - if($rec->$status_count > 0) { - ?> + $status) { + $status_count = "{$status}_sub_count"; + if ($rec->$status_count > 0) { + ?> - + - username)); - $other_count = $rec->txn_count; - ?> + username)); + $other_count = $rec->txn_count; + ?> > - active_txn_count > 0) { - $other_count = $other_count - $rec->active_txn_count; - ?> - - expired_txn_count > 0) { - $other_count = $other_count - $rec->expired_txn_count; - ?> - - trial_txn_count > 0) { - ?> -
    trial_txn_count, __('Trial', 'memberpress')); ?>
    - 0) { - ?> -
    - + active_txn_count > 0) { + $other_count = $other_count - $rec->active_txn_count; + ?> + + expired_txn_count > 0) { + $other_count = $other_count - $rec->expired_txn_count; + ?> + + trial_txn_count > 0) { + ?> +
    trial_txn_count, __('Trial', 'memberpress')); ?>
    + 0) { + ?> +
    + - + > - + >pending_sub_count); ?> - + >active_sub_count); ?> - + >suspended_sub_count); ?> - + >cancelled_sub_count); ?> - memberships)) { - $ids = explode(',',$rec->memberships); - foreach($ids as $id) { - $membership = new MeprProduct($id); - $titles[] = esc_html($membership->post_title); - } - } - ?> + memberships)) { + $ids = explode(',', $rec->memberships); + foreach ($ids as $id) { + $membership = new MeprProduct($id); + $titles[] = esc_html($membership->post_title); + } + } + ?> > - inactive_memberships)) { - $ids = explode(',',$rec->inactive_memberships); + inactive_memberships)) { + $ids = explode(',', $rec->inactive_memberships); - foreach($ids as $id) { - $membership = new MeprProduct($id); - $inactive_titles[] = esc_html($membership->post_title); - } - } - ?> + foreach ($ids as $id) { + $membership = new MeprProduct($id); + $inactive_titles[] = esc_html($membership->post_title); + } + } + ?> > - - >total_spent,true,false); ?> - + + >total_spent, true, false); ?> + >last_login_date, __('Never', 'memberpress')); ?> - + >login_count); ?> - + >registered); ?> - + - - + @@ -20,15 +20,15 @@ + if (isset($_REQUEST['status']) || isset($_REQUEST['membership'])) { + $uri = $_SERVER['REQUEST_URI']; + $uri = preg_replace('/[\?&]status=[^&]*/', '', $uri); + $uri = preg_replace('/[\?&]membership=[^&]*/', '', $uri); + ?> [x] - + ?> diff --git a/app/views/admin/must_configure.php b/app/views/admin/must_configure.php index 7adbc00..468996f 100644 --- a/app/views/admin/must_configure.php +++ b/app/views/admin/must_configure.php @@ -1,3 +1,5 @@ - + -
    MemberPress hasn\'t been configured yet. Go to the MemberPress %1$soptions page%2$s to get it setup.', 'memberpress'), '',''); ?>
    +
    MemberPress hasn\'t been configured yet. Go to the MemberPress %1$soptions page%2$s to get it setup.', 'memberpress'), '', ''); ?>
    diff --git a/app/views/admin/onboarding/active_license.php b/app/views/admin/onboarding/active_license.php index c57d663..bd9b1a2 100644 --- a/app/views/admin/onboarding/active_license.php +++ b/app/views/admin/onboarding/active_license.php @@ -1,4 +1,6 @@ - +
    @@ -6,22 +8,21 @@ + ?>
    @@ -48,13 +49,13 @@ ', - esc_html($li['activation_count']), - esc_html(ucwords($li['max_activations'])), - '' + esc_html__('%1$s%2$d of %3$s%4$s sites have been activated with this license key', 'memberpress'), + '', + esc_html($li['activation_count']), + esc_html(ucwords($li['max_activations'])), + '' ); - ?> + ?>
    diff --git a/app/views/admin/onboarding/complete.php b/app/views/admin/onboarding/complete.php index b8ee40f..14d2e98 100644 --- a/app/views/admin/onboarding/complete.php +++ b/app/views/admin/onboarding/complete.php @@ -1,4 +1,6 @@ - +

    @@ -12,12 +14,12 @@ @@ -29,12 +31,12 @@
    diff --git a/app/views/admin/onboarding/content-search-results.php b/app/views/admin/onboarding/content-search-results.php index 186249a..2889a32 100644 --- a/app/views/admin/onboarding/content-search-results.php +++ b/app/views/admin/onboarding/content-search-results.php @@ -1,7 +1,9 @@ - - + +
    - +
    @@ -95,11 +97,11 @@

    - + > - + @@ -111,11 +113,11 @@

    - + > - + @@ -127,11 +129,11 @@

    - + > - + @@ -143,11 +145,11 @@

    - + > - + @@ -157,8 +159,8 @@



    Want a feature your membership level doesn’t support? No worries! You’ll get the chance to upgrade later in the onboarding wizard.' + __('If your subscription level allows, the following plugins will be installed automatically: %s', 'memberpress'), + '

    Want a feature your membership level doesn’t support? No worries! You’ll get the chance to upgrade later in the onboarding wizard.' ); - ?> + ?>

    diff --git a/app/views/admin/onboarding/finish.php b/app/views/admin/onboarding/finish.php index abd7cbe..b18b60b 100644 --- a/app/views/admin/onboarding/finish.php +++ b/app/views/admin/onboarding/finish.php @@ -1,2 +1,6 @@ - +
    \ No newline at end of file diff --git a/app/views/admin/onboarding/index.php b/app/views/admin/onboarding/index.php index 89c513c..d3a3233 100644 --- a/app/views/admin/onboarding/index.php +++ b/app/views/admin/onboarding/index.php @@ -1 +1,3 @@ - + + - +

    - +

    - +

    - +

    - - + +

    - + diff --git a/app/views/admin/onboarding/membership.php b/app/views/admin/onboarding/membership.php index dc1d61a..acd69e4 100644 --- a/app/views/admin/onboarding/membership.php +++ b/app/views/admin/onboarding/membership.php @@ -1,4 +1,6 @@ - +

    @@ -13,17 +15,17 @@

    -
    +

    -
    +

    -
    +
    @@ -87,11 +89,11 @@ Memberships%2$s', 'memberpress'), - '', - '' + esc_html__('More advanced options are available in %1$sMemberPress > Memberships%2$s', 'memberpress'), + '', + '' ); - ?> + ?>
    diff --git a/app/views/admin/onboarding/nav/complete.php b/app/views/admin/onboarding/nav/complete.php index 29eefbf..890c738 100644 --- a/app/views/admin/onboarding/nav/complete.php +++ b/app/views/admin/onboarding/nav/complete.php @@ -1,10 +1,12 @@ - + 'Oxv63nivZEw', - 'step' => '8', -)); +MeprView::render('/admin/onboarding/video', [ + 'youtube_video_id' => 'Oxv63nivZEw', + 'step' => '8', +]); ?>
    diff --git a/app/views/admin/onboarding/nav/content.php b/app/views/admin/onboarding/nav/content.php index aff757b..cdc6455 100644 --- a/app/views/admin/onboarding/nav/content.php +++ b/app/views/admin/onboarding/nav/content.php @@ -1,10 +1,12 @@ - + 'M3ISOBtHXdA', - 'step' => '3', -)); +MeprView::render('/admin/onboarding/video', [ + 'youtube_video_id' => 'M3ISOBtHXdA', + 'step' => '3', +]); ?>
    diff --git a/app/views/admin/onboarding/nav/features.php b/app/views/admin/onboarding/nav/features.php index b47ef94..1362298 100644 --- a/app/views/admin/onboarding/nav/features.php +++ b/app/views/admin/onboarding/nav/features.php @@ -1,4 +1,6 @@ - +
    diff --git a/app/views/admin/onboarding/nav/finish.php b/app/views/admin/onboarding/nav/finish.php index 6d31e39..4953833 100644 --- a/app/views/admin/onboarding/nav/finish.php +++ b/app/views/admin/onboarding/nav/finish.php @@ -1,4 +1,6 @@ - +
    diff --git a/app/views/admin/onboarding/nav/index.php b/app/views/admin/onboarding/nav/index.php index 89c513c..d3a3233 100644 --- a/app/views/admin/onboarding/nav/index.php +++ b/app/views/admin/onboarding/nav/index.php @@ -1 +1,3 @@ - + +
    diff --git a/app/views/admin/onboarding/nav/membership.php b/app/views/admin/onboarding/nav/membership.php index 6a0730c..6a55ec6 100644 --- a/app/views/admin/onboarding/nav/membership.php +++ b/app/views/admin/onboarding/nav/membership.php @@ -1,9 +1,11 @@ - + 'M3SgL6GqEsE', - 'step' => '4', -)); +MeprView::render('/admin/onboarding/video', [ + 'youtube_video_id' => 'M3SgL6GqEsE', + 'step' => '4', +]); ?>
    diff --git a/app/views/admin/onboarding/nav/payments.php b/app/views/admin/onboarding/nav/payments.php index 0aaabaf..ebd5537 100644 --- a/app/views/admin/onboarding/nav/payments.php +++ b/app/views/admin/onboarding/nav/payments.php @@ -1,17 +1,17 @@ - + integrations) && empty($saved_gateway_id)) { +if (!empty($mepr_options->integrations) && empty($saved_gateway_id)) { $state = 1; // skip without confirmation - } - elseif(!empty($saved_gateway_id)) { +} elseif (!empty($saved_gateway_id)) { $state = 2; // continue - } - else { +} else { $state = 3; // skip with confirmation - } +} ?>
    > diff --git a/app/views/admin/onboarding/nav/rules.php b/app/views/admin/onboarding/nav/rules.php index 5178a06..165b849 100644 --- a/app/views/admin/onboarding/nav/rules.php +++ b/app/views/admin/onboarding/nav/rules.php @@ -1,9 +1,11 @@ - + 'H7gjIpm9byw', - 'step' => '5', -)); +MeprView::render('/admin/onboarding/video', [ + 'youtube_video_id' => 'H7gjIpm9byw', + 'step' => '5', +]); ?>
    diff --git a/app/views/admin/onboarding/parts/content_popup.php b/app/views/admin/onboarding/parts/content_popup.php index 46fb251..3a3d540 100644 --- a/app/views/admin/onboarding/parts/content_popup.php +++ b/app/views/admin/onboarding/parts/content_popup.php @@ -36,10 +36,10 @@ Courses%2$s', 'memberpress'), - '', - '' + esc_html__('More advanced options are available in %1$sMemberPress > Courses%2$s', 'memberpress'), + '', + '' ); - ?> + ?>
    \ No newline at end of file diff --git a/app/views/admin/onboarding/parts/finish.php b/app/views/admin/onboarding/parts/finish.php index 71239e9..05aed18 100644 --- a/app/views/admin/onboarding/parts/finish.php +++ b/app/views/admin/onboarding/parts/finish.php @@ -1,220 +1,222 @@ - + $addons_installed, - 'addons_not_installed' => $addons_not_installed, - 'payment_gateway' => $mepr_onboarding_payment_gateway, - ) + [ + 'addons_installed' => $addons_installed, + 'addons_not_installed' => $addons_not_installed, + 'payment_gateway' => $mepr_onboarding_payment_gateway, + ] ); -if( $upgrade_type === 'memberpress-elite' ) { - $upgrade_type = 'memberpress-pro-5'; +if ($upgrade_type === 'memberpress-elite') { + $upgrade_type = 'memberpress-pro-5'; } -if(!empty($upgraded_edition) && !empty($current_license) && $upgraded_edition != $current_license) { - // The user upgraded to another edition, but it has not processed yet - ?> -

    +if (!empty($upgraded_edition) && !empty($current_license) && $upgraded_edition != $current_license) { + // The user upgraded to another edition, but it has not processed yet + ?> +

    - +

    - 1, - ); - - if ( in_array( 'easy-affiliate', $features_data['addons_not_installed'], true ) ) { - $finish_description = esc_html__( 'To unlock selected features, upgrade with Easy Affiliate.', 'memberpress' ); - $pricing_url_args['doea'] = 1; - } - - if ( in_array( 'memberpress-coachkit', $features_data['addons_not_installed'], true ) ) { - $finish_description = esc_html__( 'To unlock selected features, upgrade with CoachKit™.', 'memberpress' ); - $pricing_url_args['dock'] = 1; - } - - if( isset($pricing_url_args['doea']) && isset($pricing_url_args['dock']) ) { - $finish_description = esc_html__( 'To unlock selected features, upgrade with CoachKit™ and Easy Affiliate.', 'memberpress' ); - } - - $pricing_url_args['return_url'] = urlencode( admin_url( 'admin.php?page=memberpress-onboarding&step=7&onboarding=1' ) ); - - $pricing_url = add_query_arg( - $pricing_url_args, - $pricing_url - ); - - if ( ! empty( $addons_installed ) ) { - $finish_description = ''; - $pricing_url = ''; - } - - $target = ''; - if( 1 == count($features_data['addons_not_installed']) - && in_array( 'easy-affiliate', $features_data['addons_not_installed'], true ) - && ( MeprUtils::is_pro_edition($current_license) || MeprUtils::is_elite_edition($current_license) ) - ){ - $finish_description = ''; - $pricing_url = 'https://easyaffiliate.com/ipob/pricing/'; - $cta_data['label'] = esc_html__( 'Purchase Easy Affiliate', 'memberpress' ); - $target = ' target="_blank"'; - } - - if( 1 == count($features_data['addons_not_installed']) - && in_array( 'memberpress-coachkit', $features_data['addons_not_installed'], true ) - && ( MeprUtils::is_pro_edition($current_license) || MeprUtils::is_elite_edition($current_license) ) - ){ - $finish_description = ''; - $pricing_url = 'https://memberpress.com/sign-in/?redirect_to=/register/coachkit-add-on/'; - $cta_data['label'] = esc_html__( 'Purchase CoachKit™', 'memberpress' ); - $target = ' target="_blank"'; - } - - $is_plan_upgrade_required = true; - if( - MeprUtils::is_elite_edition(MEPR_EDITION) || MeprUtils::is_pro_edition(MEPR_EDITION) || - MeprUtils::is_elite_edition($current_license) || MeprUtils::is_pro_edition($current_license) - ) { - $is_plan_upgrade_required = false; - $finish_description = ''; - } - - $multiple_ctas = array(); - if( false === $is_plan_upgrade_required && ! empty($addons_not_installed) ) { - $finish_description = sprintf( esc_html__( 'You have selected following features:', 'memberpress' ), $cta_data['token']); - if( in_array( 'easy-affiliate', $addons_not_installed, true )){ - $multiple_ctas['ea'] = array(); - $multiple_ctas['ea']['url'] = 'https://memberpress.com/sign-in/?redirect_to=/register/easy-affiliate-pro/'; - $multiple_ctas['ea']['label'] = esc_html__( 'Purchase Easy Affiliate', 'memberpress' ); + 1, + ]; + + if (in_array('easy-affiliate', $features_data['addons_not_installed'], true)) { + $finish_description = esc_html__('To unlock selected features, upgrade with Easy Affiliate.', 'memberpress'); + $pricing_url_args['doea'] = 1; + } + + if (in_array('memberpress-coachkit', $features_data['addons_not_installed'], true)) { + $finish_description = esc_html__('To unlock selected features, upgrade with CoachKit™.', 'memberpress'); + $pricing_url_args['dock'] = 1; } - if( in_array( 'memberpress-coachkit', $addons_not_installed, true )){ - $multiple_ctas['ck'] = array(); - $multiple_ctas['ck']['url'] = 'https://memberpress.com/sign-in/?redirect_to=/register/coachkit-add-on/'; - $multiple_ctas['ck']['label'] = esc_html__( 'Purchase CoachKit™', 'memberpress' ); + if (isset($pricing_url_args['doea']) && isset($pricing_url_args['dock'])) { + $finish_description = esc_html__('To unlock selected features, upgrade with CoachKit™ and Easy Affiliate.', 'memberpress'); } - } - // if license mismatched and upgrade still required, show them the upgrade CTA interface. - if ( $pricing_url != '' && $current_license != $upgrade_type ) : ?> -

    -

    + $pricing_url_args['return_url'] = urlencode(admin_url('admin.php?page=memberpress-onboarding&step=7&onboarding=1')); -
    - $addon_slug ) : - $mepr_active_class = 'mepr-wizard-feature-activatedx'; - if ( in_array( $addon_slug, $addons_installed, true ) ) { - $mepr_active_class = 'mepr-wizard-feature-activated'; + $pricing_url = add_query_arg( + $pricing_url_args, + $pricing_url + ); + + if (! empty($addons_installed)) { + $finish_description = ''; + $pricing_url = ''; + } + + $target = ''; + if ( + 1 == count($features_data['addons_not_installed']) + && in_array('easy-affiliate', $features_data['addons_not_installed'], true) + && ( MeprUtils::is_pro_edition($current_license) || MeprUtils::is_elite_edition($current_license) ) + ) { + $finish_description = ''; + $pricing_url = 'https://easyaffiliate.com/ipob/pricing/'; + $cta_data['label'] = esc_html__('Purchase Easy Affiliate', 'memberpress'); + $target = ' target="_blank"'; + } + + if ( + 1 == count($features_data['addons_not_installed']) + && in_array('memberpress-coachkit', $features_data['addons_not_installed'], true) + && ( MeprUtils::is_pro_edition($current_license) || MeprUtils::is_elite_edition($current_license) ) + ) { + $finish_description = ''; + $pricing_url = 'https://memberpress.com/sign-in/?redirect_to=/register/coachkit-add-on/'; + $cta_data['label'] = esc_html__('Purchase CoachKit™', 'memberpress'); + $target = ' target="_blank"'; + } + + $is_plan_upgrade_required = true; + if ( + MeprUtils::is_elite_edition(MEPR_EDITION) || MeprUtils::is_pro_edition(MEPR_EDITION) || + MeprUtils::is_elite_edition($current_license) || MeprUtils::is_pro_edition($current_license) + ) { + $is_plan_upgrade_required = false; + $finish_description = ''; + } + + $multiple_ctas = []; + if (false === $is_plan_upgrade_required && ! empty($addons_not_installed)) { + $finish_description = sprintf(esc_html__('You have selected following features:', 'memberpress'), $cta_data['token']); + if (in_array('easy-affiliate', $addons_not_installed, true)) { + $multiple_ctas['ea'] = []; + $multiple_ctas['ea']['url'] = 'https://memberpress.com/sign-in/?redirect_to=/register/easy-affiliate-pro/'; + $multiple_ctas['ea']['label'] = esc_html__('Purchase Easy Affiliate', 'memberpress'); } - $addons_installation_message = ''; - $addons_installation_message_class = ''; - if ( in_array( $addon_slug, $addons_upgrade_failed, true ) ) { - $addons_installation_message = esc_html__( 'Unable to install. Please download and install manually.', 'memberpress' ); - $addons_installation_message_class = 'error'; + if (in_array('memberpress-coachkit', $addons_not_installed, true)) { + $multiple_ctas['ck'] = []; + $multiple_ctas['ck']['url'] = 'https://memberpress.com/sign-in/?redirect_to=/register/coachkit-add-on/'; + $multiple_ctas['ck']['label'] = esc_html__('Purchase CoachKit™', 'memberpress'); } - ?> + } + + // if license mismatched and upgrade still required, show them the upgrade CTA interface. + if ($pricing_url != '' && $current_license != $upgrade_type) : ?> +

    +

    + +
    + $addon_slug) : + $mepr_active_class = 'mepr-wizard-feature-activatedx'; + if (in_array($addon_slug, $addons_installed, true)) { + $mepr_active_class = 'mepr-wizard-feature-activated'; + } + + $addons_installation_message = ''; + $addons_installation_message_class = ''; + if (in_array($addon_slug, $addons_upgrade_failed, true)) { + $addons_installation_message = esc_html__('Unable to install. Please download and install manually.', 'memberpress'); + $addons_installation_message_class = 'error'; + } + ?>
    -
    -

    -

    +
    +

    +

    - + - +
    -

    +

    - +
    - + - + - +
    - - - + + +
    - + - '; - $editions = MeprUtils::is_incorrect_edition_installed(); - if ( $editions ) { - echo ''; - } - ?> -

    -

    + '; + $editions = MeprUtils::is_incorrect_edition_installed(); + if ($editions) { + echo ''; + } + ?> +

    +

    - $addon_slug ) : ?> -
    + $addon_slug) : ?> +
    -

    +

    -
    +
    - - - - + + + + - +
    -

    +

    - +

    - +
    - - + -

    + ?> +

    - +

    diff --git a/app/views/admin/onboarding/payments.php b/app/views/admin/onboarding/payments.php index 48f65b0..75cea89 100644 --- a/app/views/admin/onboarding/payments.php +++ b/app/views/admin/onboarding/payments.php @@ -1,12 +1,14 @@ - + payment_method('default', false); $onboarding_gateway = get_option('mepr_onboarding_payment_gateway'); - if(empty($onboarding_gateway) && $existing_gateway instanceof MeprBaseGateway) { +if (empty($onboarding_gateway) && $existing_gateway instanceof MeprBaseGateway) { update_option('mepr_onboarding_payment_gateway', $existing_gateway->id); - } +} ?>

    @@ -72,7 +74,7 @@
    - +

    @@ -87,11 +89,11 @@ ', - '' + esc_html__('* Pricing for Stripe Tax API starts at 0.50 USD per transaction, where you\'re registered to collect taxes. This includes 10 calculation API calls per transaction and is priced at 0.05 USD per additional calculation API call beyond 10. To learn more, visit the %1$sStripe Tax pricing page%2$s.', 'memberpress'), + '', + '' ); - ?> + ?>

    @@ -100,13 +102,13 @@ ', - '', - '', - '' + __('In the Stripe dashboard, please ensure that %1$sStripe Tax is enabled%2$s and that a %3$sRegistration is added%4$s for each location where tax should be collected.', 'memberpress'), + '', + '', + '', + '' ); - ?> + ?>

    @@ -115,13 +117,13 @@ ', - '', - '', - '' + esc_html__('In the Stripe dashboard, please ensure that %1$sStripe Tax is enabled%2$s and that a %3$sRegistration is added%4$s for each location where tax should be collected.', 'memberpress'), + '', + '', + '', + '' ); - ?> + ?>

    diff --git a/app/views/admin/onboarding/rules.php b/app/views/admin/onboarding/rules.php index 58efa2f..c8645be 100644 --- a/app/views/admin/onboarding/rules.php +++ b/app/views/admin/onboarding/rules.php @@ -1,4 +1,6 @@ - +

    @@ -13,12 +15,12 @@

    -
    +

    -
    +
    @@ -33,8 +35,8 @@

    + $rules_data = MeprOnboardingHelper::get_rules_step_data(); + ?>
    @@ -51,11 +53,11 @@ Rules%2$s', 'memberpress'), - '', - '' + esc_html__('More advanced options are available in %1$sMemberPress > Rules%2$s', 'memberpress'), + '', + '' ); - ?> + ?>
    diff --git a/app/views/admin/onboarding/video.php b/app/views/admin/onboarding/video.php index c99f9db..e331304 100644 --- a/app/views/admin/onboarding/video.php +++ b/app/views/admin/onboarding/video.php @@ -1,4 +1,6 @@ - + diff --git a/app/views/admin/onboarding/welcome.php b/app/views/admin/onboarding/welcome.php index 8de686f..2805b5a 100644 --- a/app/views/admin/onboarding/welcome.php +++ b/app/views/admin/onboarding/welcome.php @@ -1,4 +1,6 @@ - +
    - +

    The Most Powerful WordPress Membership Plugin ... Without the Hidden Costs

    Join thousands of professionals who have together sold over $1 billion in memberships.

    - -
    -
    Elite
    -
    normally $999
    -
    - $ - 499.50 - / year -
    -
    $499.50 savings*
    -

    Critical for serious 7-Figure Creators to accelerate growth and revenue.

    - Get Started -
    -
    Everything in Pro, and:
    -
    Use on up to 5 Sites
    -
    Sell Coaching Programs with CoachKit™
    -
    Exclusive Elite Add-Ons*
    - -
    -
    -
    */ ?> + +
    +
    Elite
    +
    normally $999
    +
    + $ + 499.50 + / year +
    +
    $499.50 savings*
    +

    Critical for serious 7-Figure Creators to accelerate growth and revenue.

    + Get Started +
    +
    Everything in Pro, and:
    +
    Use on up to 5 Sites
    +
    Sell Coaching Programs with CoachKit™
    +
    Exclusive Elite Add-Ons*
    + +
    +
    +
    */ ?> - +
    @@ -157,9 +159,9 @@
    - + - +
    Pro
    @@ -181,7 +183,7 @@
    - +
    diff --git a/app/views/admin/onboarding/wizard.php b/app/views/admin/onboarding/wizard.php index 8c2223d..62e3dfd 100644 --- a/app/views/admin/onboarding/wizard.php +++ b/app/views/admin/onboarding/wizard.php @@ -1,4 +1,6 @@ - +
    $step) { + foreach ($steps as $key => $step) { $extra_class = ''; - if($key == 0) { - $li = get_site_transient('mepr_license_info'); + if ($key == 0) { + $li = get_site_transient('mepr_license_info'); - if(!$li) { - $extra_class = ' mepr-hidden'; - } + if (!$li) { + $extra_class = ' mepr-hidden'; + } } printf( - '
    ', - $key + 1, - $extra_class + '
    ', + $key + 1, + $extra_class ); - if(file_exists($step['nav'])) { - require $step['nav']; + if (file_exists($step['nav'])) { + require $step['nav']; } echo '
    '; - } + } ?>
    diff --git a/app/views/admin/options/account_login.php b/app/views/admin/options/account_login.php index 6ef57de..0a6375c 100644 --- a/app/views/admin/options/account_login.php +++ b/app/views/admin/options/account_login.php @@ -1,4 +1,6 @@ - +

    diff --git a/app/views/admin/options/active_license.php b/app/views/admin/options/active_license.php index e3f5916..5e01f29 100644 --- a/app/views/admin/options/active_license.php +++ b/app/views/admin/options/active_license.php @@ -1,24 +1,26 @@ - +

    %1$s%3$s

    ', - sprintf( + '

    %1$s%3$s

    ', + sprintf( /* translators: %1$s: the license edition, %2$s: the installed edition, %3$s: open link tag, %4$s: close link tag */ - esc_html__('This License Key is for %1$s, but %2$s is installed. %3$sClick here%4$s to install the correct edition for the license (%1$s).', 'memberpress'), - '' . esc_html($editions['license']['name']) . '', - '' . esc_html($editions['installed']['name']) . '', - '', - '' - ), - esc_url(MEPR_IMAGES_URL . '/square-loader.gif'), - esc_html__('Loading...', 'memberpress') + esc_html__('This License Key is for %1$s, but %2$s is installed. %3$sClick here%4$s to install the correct edition for the license (%1$s).', 'memberpress'), + '' . esc_html($editions['license']['name']) . '', + '' . esc_html($editions['installed']['name']) . '', + '', + '' + ), + esc_url(MEPR_IMAGES_URL . '/square-loader.gif'), + esc_html__('Loading...', 'memberpress') ); - } +} ?>

    @@ -41,13 +43,13 @@ ', - esc_html($li['activation_count']), - esc_html(ucwords($li['max_activations'])), - '' + esc_html__('%1$s%2$d of %3$s%4$s sites have been activated with this license key', 'memberpress'), + '', + esc_html($li['activation_count']), + esc_html(ucwords($li['max_activations'])), + '' ); - ?> + ?> diff --git a/app/views/admin/options/courses_migrator.php b/app/views/admin/options/courses_migrator.php index 8fcf61f..4c9033c 100644 --- a/app/views/admin/options/courses_migrator.php +++ b/app/views/admin/options/courses_migrator.php @@ -1,9 +1,11 @@ - +
    - +
    - +

    @@ -13,7 +15,7 @@

    - +

    diff --git a/app/views/admin/options/custom_fields_row.php b/app/views/admin/options/custom_fields_row.php index 125aebb..80e9a2d 100644 --- a/app/views/admin/options/custom_fields_row.php +++ b/app/views/admin/options/custom_fields_row.php @@ -1,9 +1,11 @@ - +
  • - + - + @@ -30,7 +32,7 @@ show_on_signup); ?> /> -    show_in_account)?$line->show_in_account:$blank_line[0]->show_in_account); ?> /> +    show_in_account) ? $line->show_in_account : $blank_line[0]->show_in_account); ?> />    required); ?> /> @@ -39,14 +41,13 @@ - - field_key)): ?> + + field_key)) : ?>

    field_key; ?>

  • diff --git a/app/views/admin/options/edge_updates.php b/app/views/admin/options/edge_updates.php index 8d538bb..3d97469 100644 --- a/app/views/admin/options/edge_updates.php +++ b/app/views/admin/options/edge_updates.php @@ -1,4 +1,6 @@ - +
    edge_updates); ?>/>  <?php esc_attr_e('Loading...', 'memberpress'); ?>
    diff --git a/app/views/admin/options/form.php b/app/views/admin/options/form.php index 39dd4ac..3cd9d2e 100644 --- a/app/views/admin/options/form.php +++ b/app/views/admin/options/form.php @@ -1,13 +1,15 @@ - +

    + MeprHooks::do_action('mepr_before_options_form'); + ?>
    @@ -33,18 +35,18 @@
    -
    '.MEPR_VERSION.''); ?>
    +
    ' . MEPR_VERSION . ''); ?>
    @@ -65,7 +67,7 @@ *: login_page_id_str, $mepr_options->login_page_id, __('Login', 'memberpress')); ?> - + *: coaching_page_id_str, $mepr_options->coaching_page_id, __('Coaching', 'memberpress')); ?> @@ -75,21 +77,23 @@

    -
    " . - __('Note: It isn\'t recommended that you change these values if you already have existing groups and membership pages on a production membership site because all your urls for them will change (WordPress will attempt to redirect from old urls to new urls).', 'memberpress') ); ?> +
    ' . + __('Note: It isn\'t recommended that you change these values if you already have existing groups and membership pages on a production membership site because all your urls for them will change (WordPress will attempt to redirect from old urls to new urls).', 'memberpress') + ); ?>

    - + - + @@ -102,15 +106,19 @@
    redirect_on_unauthorized); ?> /> -
    When this is checked, unauthorized visits will be redirected to a url, otherwise the unauthorized message and login form will appear on the page.", 'memberpress') ); ?> +
    When this is checked, unauthorized visits will be redirected to a url, otherwise the unauthorized message and login form will appear on the page.', 'memberpress') + ); ?>
    -
    However, this does not work with all WordPress themes. If you find that the unauthorized redirection is not happening when enabled try switching this to "init" instead.', 'memberpress') ); ?> +
    However, this does not work with all WordPress themes. If you find that the unauthorized redirection is not happening when enabled try switching this to "init" instead.', 'memberpress') + ); ?> redirect_non_singular); ?> /> - +

    - +
    [mepr-unauthorized-message] shortcode on this unauthorized page (assuming this url points to a page on this site).', 'memberpress'); ?> @@ -139,13 +151,14 @@
    unauth_excerpt_type_str, - $mepr_options->unauth_excerpt_type, - $mepr_options->unauth_excerpt_size_str, - $mepr_options->unauth_excerpt_size, - true - ); - ?> + MeprOptionsHelper::display_show_excerpts_dropdown( + $mepr_options->unauth_excerpt_type_str, + $mepr_options->unauth_excerpt_type, + $mepr_options->unauth_excerpt_size_str, + $mepr_options->unauth_excerpt_size, + true + ); + ?>
     
    @@ -156,9 +169,11 @@
    - +
    unauthorized_message, $mepr_options->unauthorized_message_str); ?> @@ -198,9 +213,11 @@ - +
    @@ -253,18 +270,22 @@ disable_grace_init_days); ?> /> -
    ' . __('If you would like to make them wait for the payment to clear before they are allowed to access the site, then enable this option.', 'memberpress') ); ?> +
    ' . __('If you would like to make them wait for the payment to clear before they are allowed to access the site, then enable this option.', 'memberpress') + ); ?>
    - +
    @@ -273,22 +294,24 @@ enable_spc); ?> /> - +
    - +
    @@ -334,10 +357,10 @@
    - + - - ' . __('Privacy Policy Page', 'memberpress') . ''), 'memberpress'); ?> + + ' . __('Privacy Policy Page', 'memberpress') . ''), 'memberpress'); ?>
    @@ -345,9 +368,11 @@
    - privacy_policy_title_str, - __('Privacy Policy Checkbox Title', 'memberpress'), - __('This will appear next to the Privacy Policy checkbox on membership registration forms. The text between % characters will become the link text to your Privacy Policy page.', 'memberpress') ); ?> + privacy_policy_title_str, + __('Privacy Policy Checkbox Title', 'memberpress'), + __('This will appear next to the Privacy Policy checkbox on membership registration forms. The text between % characters will become the link text to your Privacy Policy page.', 'memberpress') + ); ?>
    @@ -362,19 +387,25 @@ force_login_page_url); ?> /> - +

        - +

    - +

    @@ -434,9 +465,11 @@

    - +

      @@ -449,45 +482,52 @@

      payment_methods( true, true ); + $objs = $mepr_options->payment_methods(true, true); $no_method = true; - foreach( $objs as $pm_id => $obj ) { - if( $obj instanceof MeprBaseRealGateway ) { - MeprView::render( "/admin/options/gateway", get_defined_vars() ); - $no_method = false; - } + foreach ($objs as $pm_id => $obj) { + if ($obj instanceof MeprBaseRealGateway) { + MeprView::render('/admin/options/gateway', get_defined_vars()); + $no_method = false; + } } ?>
      -

      +

      - - - - -
      + + + + +

      - +

      - +

      @@ -498,9 +538,11 @@
      include_email_privacy_link); ?> /> - include_email_privacy_link_str, - __('Privacy Policy Link', 'memberpress'), - __("When this option is checked, a link to your site's Privacy Policy page will be included in all email communication.", 'memberpress') ); ?> + include_email_privacy_link_str, + __('Privacy Policy Link', 'memberpress'), + __("When this option is checked, a link to your site's Privacy Policy page will be included in all email communication.", 'memberpress') + ); ?>

      @@ -518,15 +560,19 @@
      disable_global_autoresponder_list); ?> /> - +

      opt_in_checked_by_default); ?> /> - - + +

      @@ -538,9 +584,11 @@

      - +

    @@ -636,9 +684,11 @@ @@ -24,7 +24,7 @@
    - + currency_symbol_after); ?> /> @@ -654,9 +704,11 @@
    -
    ' . __('Note: This option should only be enabled if MemberPress compatibility with your theme requires its usage.', 'memberpress') . '' ); ?> +
    ' . __('Note: This option should only be enabled if MemberPress compatibility with your theme requires its usage.', 'memberpress') . '' + ); ?>
    global_styles); ?> /> @@ -672,9 +724,11 @@
    -
    " . __("Note: This does not work with caching enabled on your site. So avoid enabling this when using WP Super Cache, W3TC, WP Engine hosting, GoDaddy's Managed WordPress -- or any other plugin/webhost that caches your pages.", 'memberpress') ); ?> +
    ' . __("Note: This does not work with caching enabled on your site. So avoid enabling this when using WP Super Cache, W3TC, WP Engine hosting, GoDaddy's Managed WordPress -- or any other plugin/webhost that caches your pages.", 'memberpress') + ); ?>
    authorize_seo_views); ?> /> @@ -689,9 +743,11 @@
    - + seo_unauthorized_noindex); ?> /> @@ -706,9 +762,11 @@
    - + paywall_enabled); ?> /> @@ -724,9 +782,11 @@
    - + @@ -743,9 +803,11 @@
    - disable_summary_email_str, - __('Disable Weekly Summary Email', 'memberpress'), - __("A weekly summary email is sent from MemberPress with a report on the revenue and transactions from the previous week. You can stop the email being sent by checking this option.", 'memberpress') ); ?> + disable_summary_email_str, + __('Disable Weekly Summary Email', 'memberpress'), + __('A weekly summary email is sent from MemberPress with a report on the revenue and transactions from the previous week. You can stop the email being sent by checking this option.', 'memberpress') + ); ?> disable_summary_email); ?> /> @@ -761,9 +823,11 @@
    - + disable_mod_rewrite); ?> /> diff --git a/app/views/admin/options/gateway.php b/app/views/admin/options/gateway.php index 69ba28c..2d19228 100644 --- a/app/views/admin/options/gateway.php +++ b/app/views/admin/options/gateway.php @@ -1,4 +1,6 @@ - +
    @@ -14,15 +16,15 @@
    settings->gateway) && isset($obj->settings->saved) && $obj->settings->saved): ?> + if (isset($obj->settings->gateway) && isset($obj->settings->saved) && $obj->settings->saved) : ?> integrations_str}[{$obj->id}][gateway]"; ?>" /> name; ?> - icon)): ?> + icon)) : ?>
    - - integrations_str}[{$obj->id}][gateway]", isset($obj->settings->gateway)?$obj->settings->gateway:'', $obj->id); + + integrations_str}[{$obj->id}][gateway]", isset($obj->settings->gateway) ? $obj->settings->gateway : '', $obj->id); endif; ?>
    @@ -37,9 +39,10 @@
    display_options_form(); - ?> + if ($obj instanceof MeprBaseRealGateway) { + $obj->display_options_form(); + } + ?>
    diff --git a/app/views/admin/options/inactive_license.php b/app/views/admin/options/inactive_license.php index fc32dab..13daf4d 100644 --- a/app/views/admin/options/inactive_license.php +++ b/app/views/admin/options/inactive_license.php @@ -1,14 +1,16 @@ - +

    MemberPress.com', - 'MemberPress.com/login' + esc_html__('You must have a License Key to enable automatic updates for MemberPress, access the admin areas of MemberPress, and receive support. If you don\'t have a License please go to %1$s to get one. If you do have a license you can login at %2$s to manage your licenses and site activations.', 'memberpress'), + 'MemberPress.com', + 'MemberPress.com/login' ); - ?> + ?>

    diff --git a/app/views/admin/options/index.php b/app/views/admin/options/index.php index 89c513c..d3a3233 100644 --- a/app/views/admin/options/index.php +++ b/app/views/admin/options/index.php @@ -1 +1,3 @@ - + +

    diff --git a/app/views/admin/popups/deactivation_survey.php b/app/views/admin/popups/deactivation_survey.php index 76bd4ec..9264a41 100644 --- a/app/views/admin/popups/deactivation_survey.php +++ b/app/views/admin/popups/deactivation_survey.php @@ -18,7 +18,7 @@ - + diff --git a/app/views/admin/popups/senddata.php b/app/views/admin/popups/senddata.php deleted file mode 100644 index 29127c1..0000000 --- a/app/views/admin/popups/senddata.php +++ /dev/null @@ -1,18 +0,0 @@ - - -
    -

    -

    -

    -

    anonymous usage data back to our developers so you can help us continue to refine this awesome membership plugin.', 'memberpress'); ?>

    - -

    -
     
    - -
    - -
     
    - -
    - -
    diff --git a/app/views/admin/privacy/privacy_policy.php b/app/views/admin/privacy/privacy_policy.php index e980fcf..82480b5 100644 --- a/app/views/admin/privacy/privacy_policy.php +++ b/app/views/admin/privacy/privacy_policy.php @@ -1,4 +1,8 @@ - +

    Introduction

    diff --git a/app/views/admin/products/advanced.php b/app/views/admin/products/advanced.php index f41547c..709012f 100644 --- a/app/views/admin/products/advanced.php +++ b/app/views/admin/products/advanced.php @@ -1,12 +1,16 @@ - +

    -
    '.__('When set, the Membership Access URL will make this membership name clickable when a member views their Subscriptions/Payments tabs on the Account page.', 'memberpress')); - ?> +
    ' . __('When set, the Membership Access URL will make this membership name clickable when a member views their Subscriptions/Payments tabs on the Account page.', 'memberpress') + ); + ?>   
    @@ -15,19 +19,21 @@
    @@ -40,29 +46,35 @@ custom_login_urls_enabled); ?> /> + MeprAppHelper::info_tooltip( + 'mepr-customize-login-redirections', + __('Custom Login Redirect URLs', 'memberpress'), + __('By default MemberPress will redirect members to the page setup in options. This will allow you to override that default behavior and customize it any way you want.', 'memberpress') + ); + ?>

    ' . __('Note: This overrides the Login Redirect URL setting in the MemberPress Options. Leave this option blank to use the Login Redirect URL in the MemberPress Options instead.', 'memberpress')); - ?> + MeprAppHelper::info_tooltip( + 'mepr-custom-login-urls-default', + __('Default Login Redirect URL', 'memberpress'), + __('If the member logging in has an active Subscription to this membership and none of the custom URLs below apply, they will be redirected to this URL after logging in.', 'memberpress') . '

    ' . __('Note: This overrides the Login Redirect URL setting in the MemberPress Options. Leave this option blank to use the Login Redirect URL in the MemberPress Options instead.', 'memberpress') + ); + ?>
    + MeprAppHelper::info_tooltip( + 'mepr-add-custom-login-urls', + __('Add Custom Login Redirect URLs', 'memberpress'), + __('This allows you to redirect a user to different URLs based on how many times they have already logged in. So for example, if you wanted to show upsell pages to members on their 3rd and 6th logins, you can do that by setting the URL to send the member to when they login for the 3rd time and again on the 6th time.', 'memberpress') + ); + ?>
      - custom_login_urls)): ?> - custom_login_urls as $url): ?> + custom_login_urls)) : ?> + custom_login_urls as $url) : ?>
    • - - + +
    + */ ?>

    @@ -56,10 +58,10 @@ value="expire_after; ?>" />

    @@ -101,24 +103,27 @@ class="mepr-date-picker"

    - +

    - +

    - trial) && $product->trial)?'checked="checked"':''; ?> + trial) && $product->trial) ? 'checked="checked"' : ''; ?>

    /> - +

    @@ -138,15 +143,15 @@ class="mepr-date-picker"

    trial_once); ?> />

    - limit_cycles) && $product->limit_cycles)?'checked="checked"':''; ?> + limit_cycles) && $product->limit_cycles) ? 'checked="checked"' : ''; ?>

    />

    @@ -163,9 +168,9 @@ class="mepr-date-picker"

    @@ -177,10 +182,10 @@ class="mepr-date-picker" value="limit_cycles_expires_after; ?>" />
    @@ -190,5 +195,5 @@ class="mepr-date-picker"
    - + diff --git a/app/views/admin/products/index.php b/app/views/admin/products/index.php index 89c513c..d3a3233 100644 --- a/app/views/admin/products/index.php +++ b/app/views/admin/products/index.php @@ -1 +1,3 @@ - + +

    @@ -6,11 +8,11 @@ ', - '' + esc_html__('%1$sTurn every sale into an upsell opportunity!%2$s Don\'t miss the chance to skyrocket your revenue with MemberPress Order Bumps – the game-changing tool that lets you automatically offer tempting add-ons to your customers right at checkout.', 'memberpress'), + '', + '' ); - ?> + ?>

    @@ -19,11 +21,11 @@ ', - '' + esc_html__('Level up your earning potential with the %1$sPro-exclusive Order Bumps%2$s feature today!', 'memberpress'), + '', + '' ); - ?> + ?>

    diff --git a/app/views/admin/products/permissions.php b/app/views/admin/products/permissions.php index dfb6ec3..fbc27f3 100644 --- a/app/views/admin/products/permissions.php +++ b/app/views/admin/products/permissions.php @@ -1,17 +1,21 @@ - + group(); -if($group !== false && $group instanceof MeprGroup && $group->is_upgrade_path) { - $product_in_upgrade_path = true; +if ($group !== false && $group instanceof MeprGroup && $group->is_upgrade_path) { + $product_in_upgrade_path = true; } else { - $product_in_upgrade_path = false; + $product_in_upgrade_path = false; } ?>

    - +
    simultaneous_subscriptions); ?> /> diff --git a/app/views/admin/products/price_box.php b/app/views/admin/products/price_box.php index b0c33aa..ddcfc50 100644 --- a/app/views/admin/products/price_box.php +++ b/app/views/admin/products/price_box.php @@ -1,4 +1,6 @@ - +
    @@ -11,10 +13,12 @@ is_highlighted); ?> /> Highlighted: Make this a Highlighted option on the Group Pricing Page. This makes it stand-out from the other listed memberships.', 'memberpress')); - ?> + MeprAppHelper::info_tooltip( + 'mepr-pricing-page-highlight', + __('Highlight', 'memberpress'), + __('Highlighted: Make this a Highlighted option on the Group Pricing Page. This makes it stand-out from the other listed memberships.', 'memberpress') + ); + ?>

    @@ -26,15 +30,15 @@ + ?>
    @@ -66,22 +70,21 @@

    - +

    - design_enable_pricing_template ) && $mepr_options->design_enable_pricing_template ) { ?> - + design_enable_pricing_template) && $mepr_options->design_enable_pricing_template) { ?>
    diff --git a/app/views/admin/products/product_options_meta_box.php b/app/views/admin/products/product_options_meta_box.php index e2f29dd..e25f333 100644 --- a/app/views/admin/products/product_options_meta_box.php +++ b/app/views/admin/products/product_options_meta_box.php @@ -1,11 +1,13 @@ - +
    - + diff --git a/app/views/admin/readylaunch/checkout.php b/app/views/admin/readylaunch/checkout.php index dc15d8c..9efbe48 100644 --- a/app/views/admin/readylaunch/checkout.php +++ b/app/views/admin/readylaunch/checkout.php @@ -13,7 +13,7 @@
    - +
    diff --git a/app/views/admin/readylaunch/coaching.php b/app/views/admin/readylaunch/coaching.php index 96d00b5..cfc5965 100644 --- a/app/views/admin/readylaunch/coaching.php +++ b/app/views/admin/readylaunch/coaching.php @@ -76,7 +76,7 @@
    - +
    diff --git a/app/views/admin/readylaunch/login.php b/app/views/admin/readylaunch/login.php index 84cc7e7..24346e1 100644 --- a/app/views/admin/readylaunch/login.php +++ b/app/views/admin/readylaunch/login.php @@ -81,7 +81,7 @@
    - +
    diff --git a/app/views/admin/readylaunch/options.php b/app/views/admin/readylaunch/options.php index 091e807..0741daa 100644 --- a/app/views/admin/readylaunch/options.php +++ b/app/views/admin/readylaunch/options.php @@ -1,5 +1,5 @@ @@ -70,7 +70,7 @@

    - +

    diff --git a/app/views/admin/readylaunch/pricing.php b/app/views/admin/readylaunch/pricing.php index 42f85f0..94193c5 100644 --- a/app/views/admin/readylaunch/pricing.php +++ b/app/views/admin/readylaunch/pricing.php @@ -7,10 +7,10 @@

    - +

    - +

    Please note, only the first 5 listed memberships will be included in the pricing page.

    @@ -20,45 +20,45 @@
    @@ -66,7 +66,7 @@
    -
    -
    -
    - +
    diff --git a/app/views/admin/readylaunch/thankyou.php b/app/views/admin/readylaunch/thankyou.php index 80ce004..8627e49 100644 --- a/app/views/admin/readylaunch/thankyou.php +++ b/app/views/admin/readylaunch/thankyou.php @@ -96,26 +96,26 @@ true, - 'quicktags' => false, - 'textarea_rows' => 20, - ); - wp_editor($mepr_options->design_thankyou_invoice_message, $mepr_options->design_thankyou_invoice_message_str, $editor_config); - ?> + $editor_config = [ + 'teeny' => true, + 'quicktags' => false, + 'textarea_rows' => 20, + ]; + wp_editor($mepr_options->design_thankyou_invoice_message, $mepr_options->design_thankyou_invoice_message_str, $editor_config); + ?>
    - +
    diff --git a/app/views/admin/reminders/emails.php b/app/views/admin/reminders/emails.php index c55e924..6b55388 100644 --- a/app/views/admin/reminders/emails.php +++ b/app/views/admin/reminders/emails.php @@ -1,11 +1,11 @@
    - $reminder->ID)) ); ?> + $reminder->ID]]); ?>
    filter_products); ?> /> - +
    - +
    products); ?>
    diff --git a/app/views/admin/reminders/index.php b/app/views/admin/reminders/index.php index 89c513c..d3a3233 100644 --- a/app/views/admin/reminders/index.php +++ b/app/views/admin/reminders/index.php @@ -1 +1,3 @@ - + + trigger_timing}_{$reminder->trigger_event}"; ?> diff --git a/app/views/admin/reports/all_time_info_blocks.php b/app/views/admin/reports/all_time_info_blocks.php index a8a0e4e..ece348a 100644 --- a/app/views/admin/reports/all_time_info_blocks.php +++ b/app/views/admin/reports/all_time_info_blocks.php @@ -1,4 +1,6 @@ - +
    diff --git a/app/views/admin/reports/index.php b/app/views/admin/reports/index.php index 89c513c..d3a3233 100644 --- a/app/views/admin/reports/index.php +++ b/app/views/admin/reports/index.php @@ -1 +1,3 @@ - + +

    - 7)); ?> + 7]); ?>
    - +
    - +
    - 8)); ?> + 8]); ?>
    @@ -38,16 +40,16 @@
    - +
    - +
    - +
    @@ -57,14 +59,14 @@
    - +
    - +
    - 8)); ?> + 8]); ?>
    @@ -76,16 +78,16 @@
    - +
    - +
    - +
    @@ -95,14 +97,14 @@
    - +
    - +
    - 8)); ?> + 8]); ?>
    @@ -110,7 +112,7 @@
    -
    +
    +
    diff --git a/app/views/admin/reports/month_table.php b/app/views/admin/reports/month_table.php index 7908918..59113eb 100644 --- a/app/views/admin/reports/month_table.php +++ b/app/views/admin/reports/month_table.php @@ -1,4 +1,6 @@ - + @@ -24,46 +26,61 @@ $taxes_dataset = MeprReports::get_taxes_dataset($curr_month, $curr_year, $curr_product); $refunds_dataset = MeprReports::get_refunds_dataset($curr_month, $curr_year, $curr_product); - foreach($records as $r) { - $revenue = isset($revenue_dataset[$r->day]) ? (float) $revenue_dataset[$r->day] : 0.00; - $taxes = isset($taxes_dataset[$r->day]) ? (float) $taxes_dataset[$r->day] : 0.00; - $refunds = isset($refunds_dataset[$r->day]) ? (float) $refunds_dataset[$r->day] : 0.00; + foreach ($records as $r) { + $revenue = isset($revenue_dataset[$r->day]) ? (float) $revenue_dataset[$r->day] : 0.00; + $taxes = isset($taxes_dataset[$r->day]) ? (float) $taxes_dataset[$r->day] : 0.00; + $refunds = isset($refunds_dataset[$r->day]) ? (float) $refunds_dataset[$r->day] : 0.00; - $all = (float)($revenue + $refunds + $taxes); - $alternate = ( $row_index++ % 2 ? '' : 'alternate' ); - ?> + $all = (float)($revenue + $refunds + $taxes); + $alternate = ( $row_index++ % 2 ? '' : 'alternate' ); + ?> - - - - + + + + - @@ -75,10 +92,18 @@ - - - - + + + +
    - + day, $curr_year, get_option('date_format')); ?> - - p; $pTotal += $r->p; ?> + + p; + $pTotal += $r->p; ?> - - f; $fTotal += $r->f; ?> + + f; + $fTotal += $r->f; ?> - - c; $cTotal += $r->c; ?> + + c; + $cTotal += $r->c; ?> - - r; $rTotal += $r->r; ?> + + r; + $rTotal += $r->r; ?> >>>>>>>>
    >>>>>>>>
    @@ -86,15 +111,15 @@ diff --git a/app/views/admin/reports/overall_info_blocks.php b/app/views/admin/reports/overall_info_blocks.php index ec28b37..0a86f21 100644 --- a/app/views/admin/reports/overall_info_blocks.php +++ b/app/views/admin/reports/overall_info_blocks.php @@ -1,4 +1,6 @@ - +
    @@ -33,7 +35,10 @@

    -

    @@ -43,4 +48,6 @@

    -*/ ?> \ No newline at end of file + */ + +?> \ No newline at end of file diff --git a/app/views/admin/reports/skeleton_info_blocks.php b/app/views/admin/reports/skeleton_info_blocks.php index 07af4c7..8e541dd 100644 --- a/app/views/admin/reports/skeleton_info_blocks.php +++ b/app/views/admin/reports/skeleton_info_blocks.php @@ -1,6 +1,8 @@ - + +for ($i = 0; $i < $count; $i++) : ?>

     

    diff --git a/app/views/admin/reports/skeleton_pie.php b/app/views/admin/reports/skeleton_pie.php index 568b6d8..b64890c 100644 --- a/app/views/admin/reports/skeleton_pie.php +++ b/app/views/admin/reports/skeleton_pie.php @@ -1,4 +1,8 @@ - +
    diff --git a/app/views/admin/reports/skeleton_table.php b/app/views/admin/reports/skeleton_table.php index 74e5ba0..7b18679 100644 --- a/app/views/admin/reports/skeleton_table.php +++ b/app/views/admin/reports/skeleton_table.php @@ -1,4 +1,6 @@ - + @@ -17,7 +19,7 @@ diff --git a/app/views/admin/reports/summary_email.php b/app/views/admin/reports/summary_email.php index d5c4534..cd78a1e 100644 --- a/app/views/admin/reports/summary_email.php +++ b/app/views/admin/reports/summary_email.php @@ -1,4 +1,6 @@ - + @@ -21,15 +23,15 @@ %s - %s', - esc_html(MeprUtils::date('l, F j', $last_week_start, $utc)), - esc_html(MeprUtils::date('l, F j', $last_week_end, $utc)) - ) + esc_html__('Here\'s the summary report for %1$s for the week of %2$s. Enjoy!', 'memberpress'), + esc_html($site), + sprintf( + '%s - %s', + esc_html(MeprUtils::date('l, F j', $last_week_start, $utc)), + esc_html(MeprUtils::date('l, F j', $last_week_end, $utc)) + ) ); - ?> + ?>

    @@ -122,7 +124,7 @@
    - +
    - +
    @@ -138,11 +140,11 @@ ', esc_url(admin_url('admin.php?page=memberpress-options#mepr-general'))), - '' + esc_html__('P.S. Want to unsubscribe from these emails? %1$sClick here to access the MemberPress settings%2$s where you can disable the Weekly Summary Email.', 'memberpress'), + sprintf('', esc_url(admin_url('admin.php?page=memberpress-options#mepr-general'))), + '' ); - ?> + ?>

    diff --git a/app/views/admin/reports/svg_loader.php b/app/views/admin/reports/svg_loader.php index 85a3dbe..4faf649 100644 --- a/app/views/admin/reports/svg_loader.php +++ b/app/views/admin/reports/svg_loader.php @@ -1,4 +1,8 @@ - + diff --git a/app/views/admin/reports/year_info_blocks.php b/app/views/admin/reports/year_info_blocks.php index 175b435..3f1e43a 100644 --- a/app/views/admin/reports/year_info_blocks.php +++ b/app/views/admin/reports/year_info_blocks.php @@ -1,4 +1,6 @@ - +
    diff --git a/app/views/admin/reports/year_table.php b/app/views/admin/reports/year_table.php index 78f9cce..ea811c3 100644 --- a/app/views/admin/reports/year_table.php +++ b/app/views/admin/reports/year_table.php @@ -1,4 +1,6 @@ - +
    @@ -25,47 +27,62 @@ $refunds_dataset = MeprReports::get_refunds_dataset(false, $curr_year, $curr_product); $collected_dataset = MeprReports::get_collected_dataset(false, $curr_year, $curr_product); - foreach($records as $r) { - $revenue = isset($revenue_dataset[$r->month]) ? (float) $revenue_dataset[$r->month] : 0.00; - $taxes = isset($taxes_dataset[$r->month]) ? (float) $taxes_dataset[$r->month] : 0.00; - $refunds = isset($refunds_dataset[$r->month]) ? (float) $refunds_dataset[$r->month] : 0.00; - $collected = isset($collected_dataset[$r->month]) ? (float) $collected_dataset[$r->month] : 0.00; - $all = (float)($revenue + $refunds + $taxes); - $alternate = ( $row_index++ % 2 ? '' : 'alternate' ); - $r->day = ''; - ?> + foreach ($records as $r) { + $revenue = isset($revenue_dataset[$r->month]) ? (float) $revenue_dataset[$r->month] : 0.00; + $taxes = isset($taxes_dataset[$r->month]) ? (float) $taxes_dataset[$r->month] : 0.00; + $refunds = isset($refunds_dataset[$r->month]) ? (float) $refunds_dataset[$r->month] : 0.00; + $collected = isset($collected_dataset[$r->month]) ? (float) $collected_dataset[$r->month] : 0.00; + $all = (float)($revenue + $refunds + $taxes); + $alternate = ( $row_index++ % 2 ? '' : 'alternate' ); + $r->day = ''; + ?> - - - - + + + + - @@ -77,10 +94,18 @@ - - - - + + + +
    - + month, 1, $curr_year, 'm/Y'); ?> - - p; $pTotal += $r->p; ?> + + p; + $pTotal += $r->p; ?> - - f; $fTotal += $r->f; ?> + + f; + $fTotal += $r->f; ?> - - c; $cTotal += $r->c; ?> + + c; + $cTotal += $r->c; ?> - - r; $rTotal += $r->r; ?> + + r; + $rTotal += $r->r; ?> >>>>>>>>
    >>>>>>>>
    @@ -88,15 +113,15 @@ diff --git a/app/views/admin/rules/access_row.php b/app/views/admin/rules/access_row.php index d5778b3..6157459 100644 --- a/app/views/admin/rules/access_row.php +++ b/app/views/admin/rules/access_row.php @@ -1,44 +1,46 @@ - +
    - 0): ?> + 0) : ?> - +  
    access_type); - else: + MeprRulesHelper::access_types_dropdown($access_condition->access_type); + else : ?>
    access_type, $access_condition->access_operator); - else: - MeprRulesHelper::access_operators_dropdown(); - endif; + else : + MeprRulesHelper::access_operators_dropdown(); + endif; ?>
    access_type, $access_condition->access_condition); - else: - MeprRulesHelper::access_conditions_dropdown(); - endif; + else : + MeprRulesHelper::access_conditions_dropdown(); + endif; ?>
    - 0): ?> + 0) : ?>
    diff --git a/app/views/admin/rules/drip_form.php b/app/views/admin/rules/drip_form.php index 7142c84..2f5892a 100644 --- a/app/views/admin/rules/drip_form.php +++ b/app/views/admin/rules/drip_form.php @@ -1,5 +1,7 @@ diff --git a/app/views/admin/rules/form.php b/app/views/admin/rules/form.php index a705452..68acf32 100644 --- a/app/views/admin/rules/form.php +++ b/app/views/admin/rules/form.php @@ -1,42 +1,46 @@ - +

    - +

    - mepr_type, 'mepr_show_content_dropdown( \''.MeprRule::$mepr_content_str.'\', this.value )'); ?>:  + mepr_type, 'mepr_show_content_dropdown( \'' . MeprRule::$mepr_content_str . '\', this.value )'); ?>:  - mepr_content, $rule->mepr_type, array(MeprRule::$is_mepr_content_regexp_str => $rule->is_mepr_content_regexp)); ?> + mepr_content, $rule->mepr_type, [MeprRule::$is_mepr_content_regexp_str => $rule->is_mepr_content_regexp]); ?>

    ','')); ?> + 'mepr-rule-access-conditions', + __('Access Conditions', 'memberpress'), + sprintf(__('If %1$sany%2$s of these conditions match for a logged-in user then he / she will be granted access to the protected content for this rule -- otherwise he / she will be denied.', 'memberpress'), '', '') + ); ?>

    $access_condition) { - MeprRulesHelper::access_row($access_condition, $ac_index); + } else { + foreach ($rule_access_conditions as $ac_index => $access_condition) { + MeprRulesHelper::access_row($access_condition, $ac_index); } - } + } ?>
     
    @@ -56,7 +60,7 @@
    ID}')): ?>"); ?> - "); ?> + '); ?>

    @@ -67,14 +71,14 @@
    - + - +
    - +
    diff --git a/app/views/admin/rules/index.php b/app/views/admin/rules/index.php index 89c513c..d3a3233 100644 --- a/app/views/admin/rules/index.php +++ b/app/views/admin/rules/index.php @@ -1 +1,3 @@ - +

    + foreach ($rules as $rule) : + ?> - + -
    post_title; ?>ID}&action=edit"); ?>" class="button">ID}&action=edit"); ?>" class="button">
    @@ -22,14 +22,14 @@

    + foreach ($product_ids as $product_id) : + $product = new MeprProduct($product_id); + ?> - + -
    post_title; ?>  ID}&action=edit"); ?>" class="button">  ID}&action=edit"); ?>" class="button">
    @@ -39,13 +39,13 @@

    + foreach ($members as $member) : + ?> - + -
    ID}&action=edit"); ?>" class="button">ID}&action=edit"); ?>" class="button">
    diff --git a/app/views/admin/rules/unauth_meta_box.php b/app/views/admin/rules/unauth_meta_box.php index 020b3a5..594168f 100644 --- a/app/views/admin/rules/unauth_meta_box.php +++ b/app/views/admin/rules/unauth_meta_box.php @@ -2,24 +2,32 @@

    - unauth_modern_paywall); ?> value="1" /> - + unauth_modern_paywall); ?> value="1" /> +
    unauth_excerpt_type, - MeprRule::$unauth_excerpt_size_str, $rule->unauth_excerpt_size ); + MeprOptionsHelper::display_show_excerpts_dropdown( + MeprRule::$unauth_excerpt_type_str, + $rule->unauth_excerpt_type, + MeprRule::$unauth_excerpt_size_str, + $rule->unauth_excerpt_size + ); ?>
    unauth_message_type, - MeprRule::$unauth_message_str, $rule->unauth_message ); + MeprOptionsHelper::display_unauth_message_dropdown( + MeprRule::$unauth_message_type_str, + $rule->unauth_message_type, + MeprRule::$unauth_message_str, + $rule->unauth_message + ); ?>
    unauth_login ); + MeprOptionsHelper::display_unauth_login_dropdown(MeprRule::$unauth_login_str, $rule->unauth_login); ?>
    diff --git a/app/views/admin/stripe_checkout_deprecated.php b/app/views/admin/stripe_checkout_deprecated.php index d98ccec..b09218d 100644 --- a/app/views/admin/stripe_checkout_deprecated.php +++ b/app/views/admin/stripe_checkout_deprecated.php @@ -1,12 +1,14 @@ - +
    ', - '' + esc_html__('The %1$sUse Stripe Checkout (Beta)%2$s option is deprecated, please go to the Payments tab and disable it.', 'memberpress'), + '', + '' ); - ?> + ?>
    diff --git a/app/views/admin/subscriptions/edit.php b/app/views/admin/subscriptions/edit.php index a1f9676..249c895 100644 --- a/app/views/admin/subscriptions/edit.php +++ b/app/views/admin/subscriptions/edit.php @@ -1,9 +1,11 @@ - +

    - +
    @@ -17,7 +19,7 @@ id; ?> - + diff --git a/app/views/admin/subscriptions/form.php b/app/views/admin/subscriptions/form.php index fc763e4..34748a0 100644 --- a/app/views/admin/subscriptions/form.php +++ b/app/views/admin/subscriptions/form.php @@ -1,7 +1,9 @@ - + - + @@ -22,10 +24,13 @@ - 'title', 'order' => 'ASC')); ?> + 'title', + 'order' => 'ASC', +]); ?>

    @@ -55,7 +60,7 @@ -

    +

    @@ -63,10 +68,10 @@

    @@ -83,7 +88,7 @@ - created_at ); ?> + created_at); ?>

    diff --git a/app/views/admin/subscriptions/index.php b/app/views/admin/subscriptions/index.php index 89c513c..d3a3233 100644 --- a/app/views/admin/subscriptions/index.php +++ b/app/views/admin/subscriptions/index.php @@ -1 +1,3 @@ - +
    diff --git a/app/views/admin/subscriptions/new.php b/app/views/admin/subscriptions/new.php index bda7d08..2b9f23c 100644 --- a/app/views/admin/subscriptions/new.php +++ b/app/views/admin/subscriptions/new.php @@ -1,15 +1,17 @@ - +

    - +
    - +

    diff --git a/app/views/admin/subscriptions/row.php b/app/views/admin/subscriptions/row.php index e9dd997..cf27467 100644 --- a/app/views/admin/subscriptions/row.php +++ b/app/views/admin/subscriptions/row.php @@ -1,229 +1,226 @@ + ?> - $column_display_name) - { - //Style attributes for each col - $class = "class=\"{$column_name} column-{$column_name}\""; - $style = ""; - if(in_array($column_name, $hidden)) - $style = ' style="display:none;"'; - $attributes = $class.$style; + $column_display_name) { + // Style attributes for each col + $class = "class=\"{$column_name} column-{$column_name}\""; + $style = ''; + if (in_array($column_name, $hidden)) { + $style = ' style="display:none;"'; + } + $attributes = $class . $style; - $editlink = admin_url('user-edit.php?user_id='.(int)$rec->user_id); + $editlink = admin_url('user-edit.php?user_id=' . (int)$rec->user_id); - //Display the cell - switch($column_name) - { - case 'col_id': - case 'col_txn_id': - ?> + // Display the cell + switch ($column_name) { + case 'col_id': + case 'col_txn_id': + ?> >id); ?> - + >created_at); ?> - id}"); - $add_url = admin_url("admin.php?page=memberpress-trans&action=new&subscription={$rec->id}"); - ?> + id}"); + $add_url = admin_url("admin.php?page=memberpress-trans&action=new&subscription={$rec->id}"); + ?> >subscr_id; ?> <?php _e('Loading...', 'memberpress'); ?>

    | | - id); - if($sub->can('suspend-subscriptions')): - if($sub->status==MeprSubscription::$active_str) - { $hide_suspend = ''; $hide_resume = ' mepr-hidden'; } - else if($sub->status==MeprSubscription::$suspended_str) - { $hide_suspend = ' mepr-hidden'; $hide_resume = ''; } - else { $hide_suspend = $hide_resume = ' mepr-hidden'; } + id); + if ($sub->can('suspend-subscriptions')) : + if ($sub->status == MeprSubscription::$active_str) { + $hide_suspend = ''; + $hide_resume = ' mepr-hidden'; + } elseif ($sub->status == MeprSubscription::$suspended_str) { + $hide_suspend = ' mepr-hidden'; + $hide_resume = ''; + } else { + $hide_suspend = $hide_resume = ' mepr-hidden'; + } - ?> + ?> | | - + ?> - | + | - status==MeprSubscription::$active_str and $sub->can('cancel-subscriptions')): - ?> + status == MeprSubscription::$active_str and $sub->can('cancel-subscriptions')) : + ?> | - +
    - sub_type === 'transaction' && $rec->id > 0) { - $txn = new MeprTransaction($rec->id); - if ($txn->txn_type === 'sub_account') { - $include_confirmations = '&include-confirmations'; - } - } - ?> + sub_type === 'transaction' && $rec->id > 0) { + $txn = new MeprTransaction($rec->id); + if ($txn->txn_type === 'sub_account') { + $include_confirmations = '&include-confirmations'; + } + } + ?> > - subscr_id; ?> + subscr_id; ?> - id}"); - ?> + id}"); + ?> > txn_count; ?> - + > - user_id)): ?> + user_id)) : ?> ">member); ?> - - - + + + - payment_method($rec->gateway); - if($pm) { - $pm_str = "{$pm->label} ({$pm->name})"; - } - else { - $pm_str = ucwords($rec->gateway); - } - ?> + payment_method($rec->gateway); + if ($pm) { + $pm_str = "{$pm->label} ({$pm->name})"; + } else { + $pm_str = ucwords($rec->gateway); + } + ?> > - product_id) { - $prd = new MeprProduct($rec->product_id); - $product_link = ''.$rec->product_name.''; - } - else { - $product_link = __('Unknown', 'memberpress'); - } + product_id) { + $prd = new MeprProduct($rec->product_id); + $product_link = '' . $rec->product_name . ''; + } else { + $product_link = __('Unknown', 'memberpress'); + } - ?> + ?> > - + > - lifetime) { - $txn = new MeprTransaction($rec->id); - echo MeprTransactionsHelper::format_currency($txn); - } - elseif($rec->status == MeprSubscription::$pending_str) { - $prd = new MeprProduct($rec->product_id); - $sub = new MeprSubscription(); - $sub->load_product_vars($prd); - $sub->coupon_id = $rec->coupon_id; - echo MeprSubscriptionsHelper::format_currency($sub,true,false); - } - else { - $sub = new MeprSubscription($rec->id); - $txn = $sub->latest_txn(); + lifetime) { + $txn = new MeprTransaction($rec->id); + echo MeprTransactionsHelper::format_currency($txn); + } elseif ($rec->status == MeprSubscription::$pending_str) { + $prd = new MeprProduct($rec->product_id); + $sub = new MeprSubscription(); + $sub->load_product_vars($prd); + $sub->coupon_id = $rec->coupon_id; + echo MeprSubscriptionsHelper::format_currency($sub, true, false); + } else { + $sub = new MeprSubscription($rec->id); + $txn = $sub->latest_txn(); - if(false != $txn && $txn instanceof MeprTransaction) { - echo MeprTransactionsHelper::format_currency($txn); - } - else { - echo MeprSubscriptionsHelper::format_currency($sub); - } - } - ?> + if (false != $txn && $txn instanceof MeprTransaction) { + echo MeprTransactionsHelper::format_currency($txn); + } else { + echo MeprSubscriptionsHelper::format_currency($sub); + } + } + ?> - + >active); ?> - expires_at) ? 0 : strtotime($rec->expires_at); - $lifetime = (MeprAppHelper::format_date($rec->expires_at, 0) == 0); + expires_at) ? 0 : strtotime($rec->expires_at); + $lifetime = (MeprAppHelper::format_date($rec->expires_at, 0) == 0); - $expired_class = ''; - if(!$lifetime and $expire_ts < current_time('timestamp')) { - $expired_class = 'class="mepr-inactive"'; - } + $expired_class = ''; + if (!$lifetime and $expire_ts < current_time('timestamp')) { + $expired_class = 'class="mepr-inactive"'; + } - if($table->lifetime) { - $default = __('Never','memberpress'); - } - else { - $sub = new MeprSubscription($rec->id); - $txn = $sub->latest_txn(); + if ($table->lifetime) { + $default = __('Never', 'memberpress'); + } else { + $sub = new MeprSubscription($rec->id); + $txn = $sub->latest_txn(); - if($txn == false || !($txn instanceof MeprTransaction) || $txn->id <= 0) { - $default = __('Unknown','memberpress'); - } - else if(trim($txn->expires_at) == MeprUtils::db_lifetime() || empty($txn->expires_at)) { - $default = __('Never','memberpress'); - } - else { - $default = __('Unknown','memberpress'); - } - } + if ($txn == false || !($txn instanceof MeprTransaction) || $txn->id <= 0) { + $default = __('Unknown', 'memberpress'); + } elseif (trim($txn->expires_at) == MeprUtils::db_lifetime() || empty($txn->expires_at)) { + $default = __('Never', 'memberpress'); + } else { + $default = __('Unknown', 'memberpress'); + } + } - ?> + ?> >>expires_at, $default); ?> - + > - status,'subscription'); ?> + status, 'subscription'); ?>
    - id, - __('Editing Subscription Status', 'memberpress'), - __("Modifying the Auto Rebill status here will change the status of the Subscription ONLY on your site, not at the Gateway itself. To cancel a Subscription, either you or the member must click on Cancel.", 'memberpress')); - ?> + id, + __('Editing Subscription Status', 'memberpress'), + __('Modifying the Auto Rebill status here will change the status of the Subscription ONLY on your site, not at the Gateway itself. To cancel a Subscription, either you or the member must click on Cancel.', 'memberpress') + ); + ?> - + @@ -18,7 +18,7 @@ @@ -26,16 +26,16 @@ + if (isset($_REQUEST['status']) || isset($_REQUEST['membership'])) { + $uri = $_SERVER['REQUEST_URI']; + $uri = preg_replace('/[\?&]status=[^&]*/', '', $uri); + $uri = preg_replace('/[\?&]membership=[^&]*/', '', $uri); + $uri = preg_replace('/[\?&]gateway=[^&]*/', '', $uri); + ?> [x] - + ?> diff --git a/app/views/admin/subscriptions/tabs.php b/app/views/admin/subscriptions/tabs.php index bbe37c4..756fc29 100644 --- a/app/views/admin/subscriptions/tabs.php +++ b/app/views/admin/subscriptions/tabs.php @@ -1,5 +1,9 @@
     
    diff --git a/app/views/admin/support/index.php b/app/views/admin/support/index.php index 89c513c..d3a3233 100644 --- a/app/views/admin/support/index.php +++ b/app/views/admin/support/index.php @@ -1 +1,3 @@ - + +
    diff --git a/app/views/admin/table_controls.php b/app/views/admin/table_controls.php index c635d0b..038f756 100644 --- a/app/views/admin/table_controls.php +++ b/app/views/admin/table_controls.php @@ -1,8 +1,10 @@ diff --git a/app/views/admin/taxes/avalara_options.php b/app/views/admin/taxes/avalara_options.php index 7ca512e..2eb31d0 100644 --- a/app/views/admin/taxes/avalara_options.php +++ b/app/views/admin/taxes/avalara_options.php @@ -1,11 +1,15 @@ - + -
    NOTE: This will override any tax rates for the US you\'ve imported via CSV.', 'memberpress')); - ?> +
    NOTE: This will override any tax rates for the US you\'ve imported via CSV.', 'memberpress') + ); + ?> /> @@ -21,9 +25,9 @@ @@ -34,9 +38,9 @@ diff --git a/app/views/admin/taxes/options.php b/app/views/admin/taxes/options.php index 2f4f624..88c6e54 100644 --- a/app/views/admin/taxes/options.php +++ b/app/views/admin/taxes/options.php @@ -1,4 +1,8 @@ - +

    @@ -6,10 +10,13 @@ - $column_display_name) - { - //Style attributes for each col - $class = "class=\"{$column_name} column-{$column_name}\""; - $style = ""; - if(in_array($column_name, $hidden)) - $style = ' style="display:none;"'; - $attributes = $class.$style; + $column_display_name) { + // Style attributes for each col + $class = "class=\"{$column_name} column-{$column_name}\""; + $style = ''; + if (in_array($column_name, $hidden)) { + $style = ' style="display:none;"'; + } + $attributes = $class . $style; - $editlink = admin_url('user-edit.php?user_id='.(int)$rec->user_id); + $editlink = admin_url('user-edit.php?user_id=' . (int)$rec->user_id); - //Display the cell - switch($column_name) { - case 'col_id': - ?> + // Display the cell + switch ($column_name) { + case 'col_id': + ?> - + - - - + + + - product_id) { - $prd = new MeprProduct($rec->product_id); - $product_link = ''.$rec->product_name.''; - } - else { - $product_link = __('Unknown', 'memberpress'); - } + product_id) { + $prd = new MeprProduct($rec->product_id); + $product_link = '' . $rec->product_name . ''; + } else { + $product_link = __('Unknown', 'memberpress'); + } - ?> + ?> - + - + - +else : + _e('None', 'memberpress'); +endif; +?> - + - total == 0.00 && $rec->amount > 0.00 ): ?> + total == 0.00 && $rec->amount > 0.00) : ?> - + - + break; + case 'col_status': + ?> - first_name) && empty($rec->last_name)) { - $full_name = __('Unknown', 'memberpress'); - } - else if(empty($rec->first_name) && !empty($rec->last_name)) { - $full_name = stripslashes($rec->last_name); - } - else if(!empty($rec->first_name) && empty($rec->last_name)) { - $full_name = stripslashes($rec->first_name); - } - else { - $full_name = stripslashes($rec->last_name).', '.stripslashes($rec->first_name); - } - ?> + first_name) && empty($rec->last_name)) { + $full_name = __('Unknown', 'memberpress'); + } elseif (empty($rec->first_name) && !empty($rec->last_name)) { + $full_name = stripslashes($rec->last_name); + } elseif (!empty($rec->first_name) && empty($rec->last_name)) { + $full_name = stripslashes($rec->first_name); + } else { + $full_name = stripslashes($rec->last_name) . ', ' . stripslashes($rec->first_name); + } + ?> - + - - + @@ -18,7 +18,7 @@ @@ -26,16 +26,16 @@ + if (isset($_REQUEST['status']) || isset($_REQUEST['membership'])) { + $uri = $_SERVER['REQUEST_URI']; + $uri = preg_replace('/[\?&]status=[^&]*/', '', $uri); + $uri = preg_replace('/[\?&]membership=[^&]*/', '', $uri); + $uri = preg_replace('/[\?&]gateway=[^&]*/', '', $uri); + ?> [x] - + ?> diff --git a/app/views/admin/transactions/trans_form.php b/app/views/admin/transactions/trans_form.php index 7e6d6e6..985ac7c 100644 --- a/app/views/admin/transactions/trans_form.php +++ b/app/views/admin/transactions/trans_form.php @@ -1,20 +1,22 @@ - + - + @@ -23,10 +25,13 @@ @@ -64,10 +69,10 @@ @@ -92,7 +97,7 @@ @@ -100,8 +105,8 @@ diff --git a/app/views/admin/unauthorized.php b/app/views/admin/unauthorized.php index 25dfc01..92d9c2d 100644 --- a/app/views/admin/unauthorized.php +++ b/app/views/admin/unauthorized.php @@ -1,4 +1,6 @@ - +

    diff --git a/app/views/admin/unauthorized_meta_box.php b/app/views/admin/unauthorized_meta_box.php index a94f192..866f95f 100644 --- a/app/views/admin/unauthorized_meta_box.php +++ b/app/views/admin/unauthorized_meta_box.php @@ -1,25 +1,35 @@ - +
    + MeprOptionsHelper::display_show_excerpts_dropdown( + '_mepr_unauth_excerpt_type', + $unauth_excerpt_type, + '_mepr_unauth_excerpt_size', + $unauth_excerpt_size + ); + ?>
    + MeprOptionsHelper::display_unauth_message_dropdown( + '_mepr_unauthorized_message_type', + $unauthorized_message_type, + '_mepr_unauthorized_message', + $unauthorized_message + ); + ?>
    + MeprOptionsHelper::display_unauth_login_dropdown('_mepr_unauth_login', $unauth_login); + ?>
    - + diff --git a/app/views/admin/update/activation_warning.php b/app/views/admin/update/activation_warning.php index dd17286..6c79afc 100644 --- a/app/views/admin/update/activation_warning.php +++ b/app/views/admin/update/activation_warning.php @@ -1,7 +1,15 @@ - - + +
    - -
    MemberPress doesn\'t have a valid license key installed. Go to the MemberPress %1$ssettings page%2$s to activate your license or go to %3$smemberpress.com%4$s to get one.', 'memberpress'), '','','',''); ?>
    + + +
    MemberPress doesn\'t have a valid license key installed. Go to the MemberPress %1$ssettings page%2$s to activate your license or go to %3$smemberpress.com%4$s to get one.', 'memberpress'), '', '', '', ''); ?>
    diff --git a/app/views/admin/update/index.php b/app/views/admin/update/index.php index 89c513c..d3a3233 100644 --- a/app/views/admin/update/index.php +++ b/app/views/admin/update/index.php @@ -1 +1,3 @@ - + +
    -

    +

    - + /> @@ -25,9 +32,13 @@
    - Exclusive: The customer will pay the price of the membership plus the cost of the tax.

    Inclusive: The customer will pay just the price of the membership but will include the tax.', 'memberpress')); ?> + Exclusive: The customer will pay the price of the membership plus the cost of the tax.

    Inclusive: The customer will pay just the price of the membership but will include the tax.', 'memberpress') + ); + ?>
    - +
    - + @@ -93,17 +112,16 @@ - $tax_rate ): ?> + $tax_rate) : ?> > - - - - + + + + - @@ -111,29 +129,28 @@
     
    - - + ['clear_tax_rates', 'mepr_taxes_nonce'], + [ + 'page' => 'memberpress-options', + 'action' => 'clear_tax_rates', + ] + ); + ?> + +

    - -
    + +
    tax_country)?'*':$tax_rate->tax_country; ?>tax_state)?'*':$tax_rate->tax_state; ?>postcodes)?'*':$tax_rate->postcodes; ?>cities)?'*':$tax_rate->cities; ?>tax_country) ? '*' : $tax_rate->tax_country; ?>tax_state) ? '*' : $tax_rate->tax_state; ?>postcodes) ? '*' : $tax_rate->postcodes; ?>cities) ? '*' : $tax_rate->cities; ?> tax_rate, 3); ?>% tax_desc; ?> tax_priority; ?>
    @@ -141,9 +158,12 @@ - + @@ -21,39 +25,39 @@ diff --git a/app/views/admin/taxes/vat_options.php b/app/views/admin/taxes/vat_options.php index cbe6bc6..86c420e 100644 --- a/app/views/admin/taxes/vat_options.php +++ b/app/views/admin/taxes/vat_options.php @@ -1,11 +1,15 @@ - + - > + > @@ -41,22 +41,21 @@ diff --git a/app/views/admin/transactions/edit_trans.php b/app/views/admin/transactions/edit_trans.php index d32ea61..7b311bb 100644 --- a/app/views/admin/transactions/edit_trans.php +++ b/app/views/admin/transactions/edit_trans.php @@ -1,26 +1,32 @@ - +

    payment_method($txn->gateway); - if(!is_object($pm)) - $pm = (object)array('label' => __('Unknown', 'memberpress'), 'name' => __('Deleted Gateway', 'memberpress')); - ?> + $pm = $mepr_options->payment_method($txn->gateway); + if (!is_object($pm)) { + $pm = (object)[ + 'label' => __('Unknown', 'memberpress'), + 'name' => __('Deleted Gateway', 'memberpress'), + ]; + } + ?>
    - id > 0): ?> + id > 0) : ?>
    - diff --git a/app/views/admin/taxes/stripe_tax_options.php b/app/views/admin/taxes/stripe_tax_options.php index d37d453..bde75dd 100644 --- a/app/views/admin/taxes/stripe_tax_options.php +++ b/app/views/admin/taxes/stripe_tax_options.php @@ -1,24 +1,27 @@ - +
    + ?> - +

    />
    @@ -30,18 +33,18 @@
    @@ -52,11 +55,11 @@ ', - '', - '', - '' + esc_html__('In the Stripe dashboard, please ensure that %1$sStripe Tax is enabled%2$s and that a %3$sRegistration is added%4$s for each location where tax should be collected.', 'memberpress'), + '', + '', + '', + '' ); ?>

    diff --git a/app/views/admin/taxes/taxjar_options.php b/app/views/admin/taxes/taxjar_options.php index fc9cce7..445cedb 100644 --- a/app/views/admin/taxes/taxjar_options.php +++ b/app/views/admin/taxes/taxjar_options.php @@ -1,14 +1,18 @@ - +
    -
    NOTE: This will override any tax rates for the US you\'ve imported via CSV.', 'memberpress')); - ?> +
    NOTE: This will override any tax rates for the US you\'ve imported via CSV.', 'memberpress') + ); + ?>
    - /> + />
    - +
    - +
    - /> + />
    - + /> @@ -20,13 +24,15 @@
    - + @@ -35,9 +41,11 @@
    - + /> @@ -46,20 +54,24 @@
    - + />
    - + /> diff --git a/app/views/admin/taxes/vat_profile_fields.php b/app/views/admin/taxes/vat_profile_fields.php index 7f1497c..2f5ce42 100644 --- a/app/views/admin/taxes/vat_profile_fields.php +++ b/app/views/admin/taxes/vat_profile_fields.php @@ -1,4 +1,6 @@ - +
    @@ -6,32 +8,30 @@ + if (MeprUtils::is_logged_in_and_an_admin()) { + ?>
    - > + > - > + >
    -
    - +
    id; ?>
    diff --git a/app/views/admin/transactions/index.php b/app/views/admin/transactions/index.php index 89c513c..d3a3233 100644 --- a/app/views/admin/transactions/index.php +++ b/app/views/admin/transactions/index.php @@ -1 +1,3 @@ - +
    -

    +

    - ID > 0): ?> + ID > 0) : ?>

    - : + : post_title; ?>

    diff --git a/app/views/admin/transactions/new_trans.php b/app/views/admin/transactions/new_trans.php index 457abce..c966a98 100644 --- a/app/views/admin/transactions/new_trans.php +++ b/app/views/admin/transactions/new_trans.php @@ -1,15 +1,17 @@ - +

    - +
    - +

    diff --git a/app/views/admin/transactions/row.php b/app/views/admin/transactions/row.php index ca4134c..c678ad6 100644 --- a/app/views/admin/transactions/row.php +++ b/app/views/admin/transactions/row.php @@ -1,205 +1,203 @@ + // Open the line + ?>

    >id; ?> >created_at); ?>>expires_at, __('Never','memberpress')); ?>>expires_at, __('Never', 'memberpress')); ?> > - user_id)): ?> + user_id)) : ?> ">user_login); ?> - - - + + + > >gateway); ?> > - >trans_num; ?> <?php _e('Loading...', 'memberpress'); ?> + >trans_num; ?> <?php _e('Loading...', 'memberpress'); ?>
    - - status, [\MeprTransaction::$pending_str, \MeprTransaction::$failed_str])) { ?> + + status, [\MeprTransaction::$pending_str, \MeprTransaction::$failed_str])) { ?> | | + data-value="id; ?>"> | | - + data-value="id; ?>"> | + | - id); - if($txn->can('process-refunds')): - ?> + target="_blank"> | + id); + if ($txn->can('process-refunds')) : + ?> | - subscription()) && $sub->status==MeprSubscription::$active_str and $sub->can('cancel-subscriptions')): - ?> + subscription()) && $sub->status == MeprSubscription::$active_str and $sub->can('cancel-subscriptions')) : + ?> | - +
    >sub_id)): - ?> - subscr_id; ?> + >sub_id)) : + ?> + subscr_id; ?> >amount, true, false); ?> >tax_amount, true, false); ?>>amount, true, false); ?> >total, true, false); ?>>
    - id, - __('Editing Transaction Status', 'memberpress'), - __("Changing the status here will ONLY change the status on your site, not at the Gateway itself. To cancel a Transaction either you or the member should click the Cancel link next to the Subscription. You must use your Payment Gateway's web interface to refund a transaction.", 'memberpress')); - ?> + id, + __('Editing Transaction Status', 'memberpress'), + __("Changing the status here will ONLY change the status on your site, not at the Gateway itself. To cancel a Transaction either you or the member should click the Cancel link next to the Subscription. You must use your Payment Gateway's web interface to refund a transaction.", 'memberpress') + ); + ?>
    - +
    > - +
    - +

    - +

    - 'title', 'order' => 'ASC')); ?> + 'title', + 'order' => 'ASC', +]); ?>

    @@ -56,7 +61,7 @@
    -

    +

    - created_at ); ?> + created_at); ?>

    - expires_at ); ?> + expires_at); ?>

    -

    Note: Blank indicates a lifetime expiration.','memberpress'); ?>

    +

    Note: Blank indicates a lifetime expiration.', 'memberpress'); ?>

    - - - - - - - - - - + + + + + + +
    - - - - /> -
    - - - - > -
    + + + + > +
    + + + + + + +
    + + + + > +
    diff --git a/app/views/admin/users/extra_profile_fields.php b/app/views/admin/users/extra_profile_fields.php index 1eeb335..f082eb7 100644 --- a/app/views/admin/users/extra_profile_fields.php +++ b/app/views/admin/users/extra_profile_fields.php @@ -1,40 +1,40 @@ - +

    - require_privacy_policy): ?> + require_privacy_policy) : ?> - require_tos): ?> + require_tos) : ?> @@ -44,13 +44,12 @@ @@ -58,16 +57,16 @@ + if (MeprUtils::is_mepr_admin()) { // Allow admins to see + ?> @@ -86,10 +85,10 @@ user_message, MeprUser::$user_message_str); ?> - + ?>
    ID, 'mepr_agree_to_privacy_policy', false)) { - _e('User has consented to the Privacy Policy', 'memberpress'); + if (get_user_meta($user->ID, 'mepr_agree_to_privacy_policy', false)) { + _e('User has consented to the Privacy Policy', 'memberpress'); + } else { + _e('User has NOT consented to the Privacy Policy', 'memberpress'); } - else { - _e('User has NOT consented to the Privacy Policy', 'memberpress'); - } - ?> + ?>
    ID, 'mepr_agree_to_tos', false)) { - _e('User has consented to the Terms of Service', 'memberpress'); - } - else { - _e('User has NOT consented to the Terms of Service', 'memberpress'); + if (get_user_meta($user->ID, 'mepr_agree_to_tos', false)) { + _e('User has consented to the Terms of Service', 'memberpress'); + } else { + _e('User has NOT consented to the Terms of Service', 'memberpress'); } - ?> + ?>
    ID, 'mepr-geo-country', true)) { + if ($geo_country = get_user_meta($user->ID, 'mepr-geo-country', true)) { $countries = MeprUtils::countries(false); printf($countries[$geo_country]); - } - else { + } else { _e('Unknown', 'memberpress'); - } + } ?>

    - +
    - +
    diff --git a/app/views/admin/users/index.php b/app/views/admin/users/index.php index 89c513c..d3a3233 100644 --- a/app/views/admin/users/index.php +++ b/app/views/admin/users/index.php @@ -1 +1,3 @@ - + +
    /> + MeprAppHelper::info_tooltip( + 'mepr-page-manual-login-form', + __('Manually place Login form', 'memberpress'), + __('By default MemberPress will append the login form to the end of the Login page. If you would like to show it in a different place on the Login page check this box then copy and paste the shortcode that appears below where you want it to appear on the page. The [mepr-login-form] shortcode can be used on any page or post on your site to display a login form.', 'memberpress') + ); + ?>
    [mepr-login-form use_redirect="true"]
    - +
    diff --git a/app/views/admin/users/search.php b/app/views/admin/users/search.php index a1327d3..b2429c0 100644 --- a/app/views/admin/users/search.php +++ b/app/views/admin/users/search.php @@ -1,5 +1,5 @@ user_email, 30 ) ?>user_login ?>user_email ?>
    */ ?> - - user_login, "\n"; ?> - + + user_login, "\n"; ?> + + @@ -45,20 +47,20 @@
    - +
    + ?>">
    diff --git a/app/views/admin/widgets/index.php b/app/views/admin/widgets/index.php index 89c513c..d3a3233 100644 --- a/app/views/admin/widgets/index.php +++ b/app/views/admin/widgets/index.php @@ -1 +1,3 @@ - + +
    diff --git a/app/views/checkout/MeprAuthorizeProfileGateway/payment_gateway_fields.php b/app/views/checkout/MeprAuthorizeProfileGateway/payment_gateway_fields.php index 57c5f84..1307e09 100644 --- a/app/views/checkout/MeprAuthorizeProfileGateway/payment_gateway_fields.php +++ b/app/views/checkout/MeprAuthorizeProfileGateway/payment_gateway_fields.php @@ -1,5 +1,5 @@ -
    diff --git a/app/views/checkout/MeprPayPalCommerceGateway/payment_form.php b/app/views/checkout/MeprPayPalCommerceGateway/payment_form.php index 8701b54..22cd064 100644 --- a/app/views/checkout/MeprPayPalCommerceGateway/payment_form.php +++ b/app/views/checkout/MeprPayPalCommerceGateway/payment_form.php @@ -1,22 +1,22 @@ notify_url( 'return' ); +$success_url = $payment_method->notify_url('return'); $smart_payment_on = $payment_method->settings->enable_smart_button == 'on'; static $unique_suffix = 0; $unique_suffix++; -if($payment_method->settings->use_desc) { - echo wpautop(esc_html(trim($payment_method->settings->desc))); +if ($payment_method->settings->use_desc) { + echo wpautop(esc_html(trim($payment_method->settings->desc))); } ?> - +
    diff --git a/app/views/checkout/MeprStripeGateway/payment_form.php b/app/views/checkout/MeprStripeGateway/payment_form.php index d6df4eb..2d6e4e5 100644 --- a/app/views/checkout/MeprStripeGateway/payment_form.php +++ b/app/views/checkout/MeprStripeGateway/payment_form.php @@ -1,20 +1,22 @@ -settings->stripe_checkout_enabled == 'on'): ?> - +settings->stripe_checkout_enabled == 'on') : ?> + - settings->use_desc) : ?> + settings->use_desc) : ?>
    - + - - + +
    diff --git a/app/views/checkout/MeprStripeGateway/payment_gateway_fields.php b/app/views/checkout/MeprStripeGateway/payment_gateway_fields.php index 3a328a4..774dd8c 100644 --- a/app/views/checkout/MeprStripeGateway/payment_gateway_fields.php +++ b/app/views/checkout/MeprStripeGateway/payment_gateway_fields.php @@ -1,25 +1,27 @@ show_fname_lname && MeprHooks::apply_filters('mepr_stripe_populate_name_fields', true)) { - printf( - '', - esc_attr($user->first_name) - ); - - printf( - '', - esc_attr($user->last_name) - ); +if (!defined('ABSPATH')) { + die('You are not allowed to call this page directly.'); } -if($mepr_options->show_address_fields && MeprHooks::apply_filters('mepr_stripe_populate_address_fields', true)) { - foreach($mepr_options->address_fields as $address_field) { +if ($mepr_options->show_fname_lname && MeprHooks::apply_filters('mepr_stripe_populate_name_fields', true)) { printf( - '', - esc_attr(str_replace('mepr-', 'card-', $address_field->field_key)), - esc_attr(get_user_meta($user->ID, $address_field->field_key, true)) + '', + esc_attr($user->first_name) ); - } + + printf( + '', + esc_attr($user->last_name) + ); +} + +if ($mepr_options->show_address_fields && MeprHooks::apply_filters('mepr_stripe_populate_address_fields', true)) { + foreach ($mepr_options->address_fields as $address_field) { + printf( + '', + esc_attr(str_replace('mepr-', 'card-', $address_field->field_key)), + esc_attr(get_user_meta($user->ID, $address_field->field_key, true)) + ); + } } diff --git a/app/views/checkout/form.php b/app/views/checkout/form.php index 82d310d..5d31489 100644 --- a/app/views/checkout/form.php +++ b/app/views/checkout/form.php @@ -1,4 +1,6 @@ - + ID); ?> @@ -7,15 +9,15 @@ - + - + ID); ?> - +
    @@ -23,36 +25,40 @@
    - register_price_action != 'hidden') && MeprHooks::apply_filters('mepr_checkout_show_terms', true, $product) ): ?> + register_price_action != 'hidden') && MeprHooks::apply_filters('mepr_checkout_show_terms', true, $product)) : ?>
    is_one_time_payment() ? _x('Price:', 'ui', 'memberpress') : _x('Terms:', 'ui', 'memberpress')); ?>
    - +
    ID); ?> - show_fields_logged_in_purchases)) && - $mepr_options->show_fname_lname): ?> + $mepr_options->show_fname_lname +) : ?>
    - +
    - require_fname_lname)?'required':''; ?> /> + require_fname_lname) ? 'required' : ''; ?> />
    - +
    - require_fname_lname)?'required':''; ?> /> + require_fname_lname) ? 'required' : ''; ?> />
    - + @@ -60,47 +66,47 @@ ID); ?> show_fields_logged_in_purchases)) { + if (!MeprUtils::is_user_logged_in() || (MeprUtils::is_user_logged_in() && $mepr_options->show_fields_logged_in_purchases)) { MeprUsersHelper::render_custom_fields($product, 'signup', $unique_suffix); - } + } ?> ID); ?> - + - + - username_is_email): ?> + username_is_email) : ?>
    - +
    - +
    - +
    - - ID); ?> - disable_checkout_password_fields === false): ?> + + ID); ?> + disable_checkout_password_fields === false) : ?>
    - -
    @@ -111,26 +117,26 @@
    - -
    - - ID); ?> - + + ID); ?> + - + ID); ?> - plan_code)): ?> - coupon_field_enabled): ?> + plan_code)) : ?> + coupon_field_enabled) : ?> - +
    @@ -139,30 +145,30 @@ <?php _ex('Loading icon', 'ui', 'memberpress'); ?> - +
    - +
    - - - + + +
    payment_methods(), $product); ?>
    - - + + - require_tos): ?> + require_tos) : ?> - require_privacy_policy && $privacy_page_link = MeprAppHelper::privacy_policy_page_link()): ?> + require_privacy_policy && $privacy_page_link = MeprAppHelper::privacy_policy_page_link()) : ?>
    - + ID); ?>
     
    @@ -181,7 +187,7 @@ - 0 || !$payment_required): ?> + 0 || !$payment_required) : ?> diff --git a/app/views/checkout/invoice.php b/app/views/checkout/invoice.php index d1862f7..2938c07 100644 --- a/app/views/checkout/invoice.php +++ b/app/views/checkout/invoice.php @@ -1,12 +1,14 @@ - +
    - + ?> +
    @@ -16,32 +18,32 @@ - + - + - + - + - + - +   - + - - - + + +   @@ -49,24 +51,24 @@ - 0.00 || $invoice['tax']['percent'] > 0 ): ?> + 0.00 || $invoice['tax']['percent'] > 0) : ?> - +   - + - +   - + - + - +   diff --git a/app/views/checkout/invoice_order_bumps.php b/app/views/checkout/invoice_order_bumps.php index ccc2428..5b21040 100644 --- a/app/views/checkout/invoice_order_bumps.php +++ b/app/views/checkout/invoice_order_bumps.php @@ -1,6 +1,8 @@ - +
    - +
    @@ -10,55 +12,55 @@ - + - + - + - + - + - +   - + - + - +   - + - - 0 || $tax_item['percent'] > 0) : ?> + + 0 || $tax_item['percent'] > 0) : ?> - +   - +
    - + - - + + - +   diff --git a/app/views/checkout/payment_form.php b/app/views/checkout/payment_form.php index c3f444f..97aeb3d 100644 --- a/app/views/checkout/payment_form.php +++ b/app/views/checkout/payment_form.php @@ -1,4 +1,6 @@ - +
    settings->gateway}/payment_gateway_fields", get_defined_vars()); ?>
    diff --git a/app/views/checkout/signup_row.php b/app/views/checkout/signup_row.php index 76b1e1a..0b93464 100644 --- a/app/views/checkout/signup_row.php +++ b/app/views/checkout/signup_row.php @@ -1,11 +1,13 @@ - +
    - field_type != 'checkbox'): ?> + field_type != 'checkbox') : ?>
    - + required) /*here for email custom fields that are not required*/ ? printf(_x('%s is Required', 'ui', 'memberpress'), stripslashes($line->field_name)) : printf(_x('%s is not valid', 'ui', 'memberpress'), stripslashes($line->field_name)); ?>
    - +
    diff --git a/app/views/checkout/spc_form.php b/app/views/checkout/spc_form.php index 51c8916..305f4fd 100644 --- a/app/views/checkout/spc_form.php +++ b/app/views/checkout/spc_form.php @@ -1,4 +1,6 @@ - + ID); ?> @@ -6,29 +8,29 @@ - " /> + - + - + ID); ?> - register_price_action != 'hidden') && MeprHooks::apply_filters('mepr_checkout_show_terms',true,$product) ): ?> + register_price_action != 'hidden') && MeprHooks::apply_filters('mepr_checkout_show_terms', true, $product)) : ?>
    is_one_time_payment() ? _x('Price:', 'ui', 'memberpress') : _x('Terms:', 'ui', 'memberpress')); ?>
    - +
    ID); ?> - +
    @@ -36,24 +38,28 @@
    - show_fields_logged_in_purchases)) && - $mepr_options->show_fname_lname): ?> + $mepr_options->show_fname_lname +) : ?>
    - +
    - require_fname_lname)?'required':''; ?> /> + require_fname_lname) ? 'required' : ''; ?> />
    - +
    - require_fname_lname)?'required':''; ?> /> + require_fname_lname) ? 'required' : ''; ?> />
    - + @@ -61,47 +67,47 @@ ID); ?> show_fields_logged_in_purchases)) { + if (!MeprUtils::is_user_logged_in() || (MeprUtils::is_user_logged_in() && $mepr_options->show_fields_logged_in_purchases)) { MeprUsersHelper::render_custom_fields($product, 'signup', $unique_suffix); - } + } ?> ID); ?> - + - + - username_is_email): ?> + username_is_email) : ?>
    - +
    - +
    - +
    - - ID); ?> - disable_checkout_password_fields === false): ?> + + ID); ?> + disable_checkout_password_fields === false) : ?>
    - -
    @@ -112,26 +118,26 @@
    - -
    - - ID); ?> - + + ID); ?> + - + ID); ?> - plan_code)): ?> - coupon_field_enabled): ?> + plan_code)) : ?> + coupon_field_enabled) : ?> - +
    @@ -140,29 +146,29 @@ <?php _e('Loading...', 'memberpress'); ?> - +
    - +
    - - - + + + - ID); ?> + ID); ?> - enable_spc_invoice): ?> + enable_spc_invoice) : ?>
    <?php _e('Loading...', 'memberpress'); ?> -
    +
    - + - ID); ?> + ID); ?>
    - 1): ?> + 1) : ?>

    @@ -171,16 +177,16 @@
    - 1): ?> + 1) : ?>
    - - + + - enable_spc_invoice && $product->adjusted_price($mepr_coupon_code) <= 0.00 && false == ( isset($_GET['ca']) && class_exists('MPCA_Corporate_Account') )) { ?> + enable_spc_invoice && $product->adjusted_price($mepr_coupon_code) <= 0.00 && false == ( isset($_GET['ca']) && class_exists('MPCA_Corporate_Account') )) { ?>
    <?php _e('Loading...', 'memberpress'); ?> @@ -189,7 +195,7 @@
    - require_tos): ?> + require_tos) : ?>
    - require_privacy_policy && $privacy_page_link = MeprAppHelper::privacy_policy_page_link()): ?> + require_privacy_policy && $privacy_page_link = MeprAppHelper::privacy_policy_page_link()) : ?>
    - + ID); ?>
     
    @@ -217,7 +223,7 @@ - 0 || !$payment_required): ?> + 0 || !$payment_required) : ?> diff --git a/app/views/emails/admin_cancelled_sub.php b/app/views/emails/admin_cancelled_sub.php index a92d047..a49db6b 100644 --- a/app/views/emails/admin_cancelled_sub.php +++ b/app/views/emails/admin_cancelled_sub.php @@ -1,4 +1,6 @@ - +