From 21f163df275355753812d2d13f1ffcbf042615a0 Mon Sep 17 00:00:00 2001 From: "Kyle B. Johnson" Date: Tue, 21 Nov 2023 14:56:13 -0500 Subject: [PATCH] Feature: All donors are registered as users (only applies to v3 forms) (#7103) Co-authored-by: Jon Waldstein --- .../Controllers/DonateController.php | 12 +++++ src/DonationForms/Properties/FormSettings.php | 6 ++- src/Donors/Actions/CreateUserFromDonor.php | 44 +++++++++++++++++++ .../SendDonorUserRegistrationNotification.php | 34 ++++++++++++++ .../FailedDonorUserCreationException.php | 21 +++++++++ src/Donors/ServiceProvider.php | 40 ++++++++++++++--- 6 files changed, 149 insertions(+), 8 deletions(-) create mode 100644 src/Donors/Actions/CreateUserFromDonor.php create mode 100644 src/Donors/Actions/SendDonorUserRegistrationNotification.php create mode 100644 src/Donors/Exceptions/FailedDonorUserCreationException.php diff --git a/src/DonationForms/Controllers/DonateController.php b/src/DonationForms/Controllers/DonateController.php index 200d560e19..45be8376f8 100644 --- a/src/DonationForms/Controllers/DonateController.php +++ b/src/DonationForms/Controllers/DonateController.php @@ -20,6 +20,7 @@ class DonateController /** * First we create a donation and/or subscription, then move on to the gateway processing * + * @unreleased Pass the form ID to match updated signature for getOrCreateDonor(). * @since 3.0.0 * * @return void @@ -28,6 +29,7 @@ class DonateController public function donate(DonateControllerData $formData, PaymentGateway $gateway) { $donor = $this->getOrCreateDonor( + $formData->formId, $formData->wpUserId, $formData->email, $formData->firstName, @@ -76,8 +78,10 @@ public function donate(DonateControllerData $formData, PaymentGateway $gateway) } /** + * @unreleased Added $formId to the signature for passing to do_action hooks. * @since 3.0.0 * + * @param int $formId * @param int|null $userId * @param string $donorEmail * @param string $firstName @@ -87,6 +91,7 @@ public function donate(DonateControllerData $formData, PaymentGateway $gateway) * @throws Exception */ private function getOrCreateDonor( + int $formId, int $userId, string $donorEmail, string $firstName, @@ -115,6 +120,13 @@ private function getOrCreateDonor( 'email' => $donorEmail, 'userId' => $userId ?: null ]); + + /** + * @unreleased Add a new do_action hook to differentiate when a v3 form creates a new donor. + * @param Donor $donor + * @param int $formId + */ + do_action('givewp_donate_controller_donor_created', $donor, $formId); } return $donor; diff --git a/src/DonationForms/Properties/FormSettings.php b/src/DonationForms/Properties/FormSettings.php index c1c640eaa2..09f40f84a6 100644 --- a/src/DonationForms/Properties/FormSettings.php +++ b/src/DonationForms/Properties/FormSettings.php @@ -63,9 +63,10 @@ class FormSettings implements Arrayable, Jsonable */ public $goalAmount; /** + * @unreleased Added registrationNotification property. * @var string */ - public $registration; + public $registrationNotification; /** * @var string */ @@ -175,6 +176,7 @@ class FormSettings implements Arrayable, Jsonable public $pdfSettings; /** + * @unreleased Added registrationNotification * @since 3.0.0 */ public static function fromArray(array $array): self @@ -200,7 +202,7 @@ public static function fromArray(array $array): self $self->primaryColor = $array['primaryColor'] ?? '#69b86b'; $self->secondaryColor = $array['secondaryColor'] ?? '#f49420'; $self->goalAmount = $array['goalAmount'] ?? 0; - $self->registration = $array['registration'] ?? 'none'; + $self->registrationNotification = $array['registrationNotification'] ?? false; $self->customCss = $array['customCss'] ?? ''; $self->pageSlug = $array['pageSlug'] ?? ''; $self->goalAchievedMessage = $array['goalAchievedMessage'] ?? __( diff --git a/src/Donors/Actions/CreateUserFromDonor.php b/src/Donors/Actions/CreateUserFromDonor.php new file mode 100644 index 0000000000..52a198afee --- /dev/null +++ b/src/Donors/Actions/CreateUserFromDonor.php @@ -0,0 +1,44 @@ + $donor->email, + 'user_pass' => wp_generate_password(), + 'user_email' => $donor->email, + 'first_name' => $donor->firstName, + 'last_name' => $donor->lastName, + 'role' => give_get_option( 'donor_default_user_role', 'give_donor' ), + ], + $donor + )); + + if(!is_wp_error($userIdOrError)) { + $donor->userId = $userIdOrError; + } else { + throw new FailedDonorUserCreationException( + $donor, + 0, + new \Exception($userIdOrError->get_error_message()) + ); + } + + do_action('givewp_donor_user_created', $donor); + + $donor->save(); + + return $donor; + } +} diff --git a/src/Donors/Actions/SendDonorUserRegistrationNotification.php b/src/Donors/Actions/SendDonorUserRegistrationNotification.php new file mode 100644 index 0000000000..4ab8ba552e --- /dev/null +++ b/src/Donors/Actions/SendDonorUserRegistrationNotification.php @@ -0,0 +1,34 @@ +email = $email; + $this->email->init(); + } + + public function __invoke(Donor $donor) + { + // Enable the `donor-register` (legacy) email notification. + add_filter( "give_donor-register_is_email_notification_active", '__return_true' ); + + // For legacy email notifications `setup_email_notification()` calls `send_email_notification()`. + $this->email->setup_email_notification($donor->userId, [ + 'email' => $donor->email + ]); + } +} diff --git a/src/Donors/Exceptions/FailedDonorUserCreationException.php b/src/Donors/Exceptions/FailedDonorUserCreationException.php new file mode 100644 index 0000000000..3665c64fea --- /dev/null +++ b/src/Donors/Exceptions/FailedDonorUserCreationException.php @@ -0,0 +1,21 @@ +donor = $donor; + } +} diff --git a/src/Donors/ServiceProvider.php b/src/Donors/ServiceProvider.php index 0baa60f267..6d1ec658ec 100644 --- a/src/Donors/ServiceProvider.php +++ b/src/Donors/ServiceProvider.php @@ -2,11 +2,16 @@ namespace Give\Donors; +use Give\DonationForms\Models\DonationForm; +use Give\Donors\Actions\CreateUserFromDonor; +use Give\Donors\Actions\SendDonorUserRegistrationNotification; use Give\Donors\CustomFields\Controllers\DonorDetailsController; +use Give\Donors\Exceptions\FailedDonorUserCreationException; use Give\Donors\ListTable\DonorsListTable; use Give\Donors\Models\Donor; use Give\Donors\Repositories\DonorRepositoryProxy; use Give\Helpers\Hooks; +use Give\Log\Log; use Give\ServiceProviders\ServiceProvider as ServiceProviderInterface; use Give_Donor as LegacyDonor; @@ -22,7 +27,7 @@ class ServiceProvider implements ServiceProviderInterface public function register() { give()->singleton('donors', DonorRepositoryProxy::class); - give()->singleton(DonorsListTable::class, function() { + give()->singleton(DonorsListTable::class, function () { $listTable = new DonorsListTable(); Hooks::doAction('givewp_donors_list_table', $listTable); @@ -38,19 +43,18 @@ public function boot() $userId = get_current_user_id(); $showLegacy = get_user_meta($userId, '_give_donors_archive_show_legacy', true); // only register new admin page if user hasn't chosen to use the old one - if(empty($showLegacy)) { + if (empty($showLegacy)) { Hooks::addAction('admin_menu', DonorsAdminPage::class, 'registerMenuItem', 30); if (DonorsAdminPage::isShowing()) { Hooks::addAction('admin_enqueue_scripts', DonorsAdminPage::class, 'loadScripts'); } - } - elseif(DonorsAdminPage::isShowing()) - { - Hooks::addAction( 'admin_head', DonorsAdminPage::class, 'renderReactSwitch'); + } elseif (DonorsAdminPage::isShowing()) { + Hooks::addAction('admin_head', DonorsAdminPage::class, 'renderReactSwitch'); } $this->addCustomFieldsToDonorDetails(); + $this->enforceDonorsAsUsers(); } /** @@ -65,4 +69,28 @@ private function addCustomFieldsToDonorDetails() echo (new DonorDetailsController())->show($donor); }); } + + /** + * Hook into the donor creation process to ensure that donors are also users. + * @unreleased + */ + protected function enforceDonorsAsUsers() + { + add_action('givewp_donate_controller_donor_created', function (Donor $donor, $formId) { + if (!$donor->userId) { + try { + give(CreateUserFromDonor::class)->__invoke($donor); + + if (DonationForm::find($formId)->settings->registrationNotification) { + give(SendDonorUserRegistrationNotification::class)->__invoke($donor); + } + } catch (FailedDonorUserCreationException $e) { + Log::error($e->getLogMessage(), [ + 'donor' => $donor, + 'previous' => $e->getPrevious(), + ]); + } + } + }, 10, 2); + } }