diff --git a/core/modules/migrate_drupal_ui/tests/src/Functional/d6/Upgrade6Test.php b/core/modules/migrate_drupal_ui/tests/src/Functional/d6/Upgrade6Test.php index d14d64ca56..0f5742ab23 100644 --- a/core/modules/migrate_drupal_ui/tests/src/Functional/d6/Upgrade6Test.php +++ b/core/modules/migrate_drupal_ui/tests/src/Functional/d6/Upgrade6Test.php @@ -94,7 +94,7 @@ protected function getEntityCounts() { 'search_page' => 2, 'shortcut' => 2, 'shortcut_set' => 1, - 'action' => 33, + 'action' => 34, 'menu' => 8, 'path_alias' => 8, 'taxonomy_term' => 15, diff --git a/core/modules/migrate_drupal_ui/tests/src/Functional/d7/Upgrade7Test.php b/core/modules/migrate_drupal_ui/tests/src/Functional/d7/Upgrade7Test.php index fa601ee9de..efd07d9630 100644 --- a/core/modules/migrate_drupal_ui/tests/src/Functional/d7/Upgrade7Test.php +++ b/core/modules/migrate_drupal_ui/tests/src/Functional/d7/Upgrade7Test.php @@ -97,7 +97,7 @@ protected function getEntityCounts() { 'search_page' => 2, 'shortcut' => 6, 'shortcut_set' => 2, - 'action' => 27, + 'action' => 28, 'menu' => 7, 'taxonomy_term' => 25, 'taxonomy_vocabulary' => 8, diff --git a/core/modules/user/config/schema/user.schema.yml b/core/modules/user/config/schema/user.schema.yml index 2f9bda44f2..6f9be423a6 100644 --- a/core/modules/user/config/schema/user.schema.yml +++ b/core/modules/user/config/schema/user.schema.yml @@ -150,6 +150,10 @@ action.configuration.user_remove_role_action: type: string label: 'The ID of the role to remove' +action.configuration.user_welcome_message_action: + type: action_configuration_default + label: 'Send the welcome message to selected users configuration' + action.configuration.user_unblock_user_action: type: action_configuration_default label: 'Unblock the selected users configuration' diff --git a/core/modules/user/src/ProfileForm.php b/core/modules/user/src/ProfileForm.php index f98437bb0f..3e1b798ca2 100644 --- a/core/modules/user/src/ProfileForm.php +++ b/core/modules/user/src/ProfileForm.php @@ -35,6 +35,28 @@ protected function actions(array $form, FormStateInterface $form_state) { $element['delete']['#url'] = $route_info; } + // Determine the user approval method. + $user_register = \Drupal::configFactory()->get('user.settings')->get('register'); + switch ($user_register) { + case UserInterface::REGISTER_VISITORS_ADMINISTRATIVE_APPROVAL: + // Only add 'Send awaiting approval' button when user settings allow + // visitors creating accounts with required administrator approval. + $send_awaiting_approval = TRUE; + break; + + default: + $send_awaiting_approval = FALSE; + } + + if (!$this->entity->isActive() && !$send_awaiting_approval) { + return $element; + } + + $element['send']['#type'] = 'submit'; + $element['send']['#value'] = $this->entity->isActive() ? $this->t('Send welcome message') : $this->t('Send awaiting approval message'); + $element['send']['#submit'] = ['::editSendSubmit']; + $element['send']['#access'] = $this->entity->getEmail() && $this->currentUser()->hasPermission('administer users'); + return $element; } @@ -49,4 +71,56 @@ public function save(array $form, FormStateInterface $form_state) { $this->messenger()->addStatus($this->t('The changes have been saved.')); } + /** + * Provides a submit handler for the 'Send welcome message' button. + */ + public function editSendSubmit(array $form, FormStateInterface $form_state) { + $account = $this->entity; + + if (!$account->isActive()) { + $op = 'register_pending_approval'; + } + else { + // Determine the user approval method. + switch (\Drupal::config('user.settings')->get('register')) { + case UserInterface::REGISTER_ADMINISTRATORS_ONLY: + $op = 'register_admin_created'; + break; + + case UserInterface::REGISTER_VISITORS: + default: + $op = 'register_no_approval_required'; + } + } + + // Notify the user via email. + $mail = _user_mail_notify($op, $account); + + // Log the mail. + if (!empty($mail)) { + $this->getLogger('user') + ->notice('Welcome message has been sent to %name at %email.', [ + '%name' => $account->getAccountName(), + '%email' => $account->getEmail(), + ]); + $this->messenger() + ->addMessage($this->t('Welcome message has been sent to %name at %email', [ + '%name' => $account->getAccountName(), + '%email' => $account->getEmail(), + ])); + } + else { + $this->getLogger('user') + ->notice('There was an error sending the welcome message to %name at %email', [ + '%name' => $account->getAccountName(), + '%email' => $account->getEmail(), + ]); + $this->messenger() + ->addMessage($this->t('There was an error sending the welcome message to %name at %email', [ + '%name' => $account->getAccountName(), + '%email' => $account->getEmail(), + ]), 'error'); + } + } + } diff --git a/core/modules/user/tests/src/Functional/UserAdminTest.php b/core/modules/user/tests/src/Functional/UserAdminTest.php index 8fedf64f7c..efa59fef66 100644 --- a/core/modules/user/tests/src/Functional/UserAdminTest.php +++ b/core/modules/user/tests/src/Functional/UserAdminTest.php @@ -47,7 +47,6 @@ private static function getLinkSelectorForUser(UserInterface $user): string { * Registers a user and deletes it. */ public function testUserAdmin() { - $config = $this->config('user.settings'); $user_a = $this->drupalCreateUser(); $user_a->name = 'User A'; $user_a->mail = $this->randomMachineName() . '@example.com'; @@ -121,7 +120,7 @@ public function testUserAdmin() { $edit = []; $edit['action'] = 'user_block_user_action'; $edit['user_bulk_form[4]'] = TRUE; - $config + $this->config('user.settings') ->set('notify.status_blocked', TRUE) ->save(); $this->drupalGet('admin/people', [ @@ -188,10 +187,8 @@ public function testNotificationEmailAddress() { $this->assertSession()->responseContains('id="edit-mail-notification-address"'); $this->drupalLogout(); - // Test custom user registration approval email address(es). - $config = $this->config('user.settings'); // Allow users to register with admin approval. - $config + $this->config('user.settings') ->set('verify_mail', TRUE) ->set('register', UserInterface::REGISTER_VISITORS_ADMINISTRATIVE_APPROVAL) ->save(); @@ -229,4 +226,191 @@ public function testNotificationEmailAddress() { $this->assertCount(1, $user_mail, 'New user mail to user is sent from configured Notification Email address'); } + /** + * Tests the sending of a welcome email notification. + * + * @dataProvider welcomeNotifications + */ + public function testSendWelcomeEmailNotification($register_mode, $mail_id) { + $admin_user = $this->drupalCreateUser(['administer users']); + $this->drupalLogin($admin_user); + + $this->config('user.settings') + ->set('register', $register_mode) + ->save(); + + // Create an active user. + $test_user = $this->drupalCreateUser(); + $test_user->save(); + + // Send the welcome message. + $this->drupalGet('user/' . $test_user->id() . '/edit'); + $this->submitForm(['status' => 0], 'Send welcome message'); + $mails = $this->getMails(); + + // Assert that only one mail was sent to test_user. + $this->assertCount(1, $mails); + + $this->assertEquals($mails[0]['to'], $test_user->getEmail(), 'Activation mail send to user'); + $this->assertEquals($mails[0]['id'], $mail_id); + } + + /** + * Collection of alternatives registrations configurations. + * + * @return array + * List of registration config & expected user mail. + */ + public function welcomeNotifications() { + return [ + [ + UserInterface::REGISTER_VISITORS, + 'user_register_no_approval_required', + ], + [ + UserInterface::REGISTER_ADMINISTRATORS_ONLY, + 'user_register_admin_created', + ], + [ + UserInterface::REGISTER_VISITORS_ADMINISTRATIVE_APPROVAL, + 'user_register_no_approval_required', + ], + ]; + } + + /** + * Tests the sending of a waiting approval email notification. + * + * @dataProvider awaitingApprovalNotifications + */ + public function testSendAwaitingApprovalEmailNotification($register_mode, $mail_id) { + $admin_user = $this->drupalCreateUser(['administer users']); + $this->drupalLogin($admin_user); + + $this->config('user.settings') + ->set('register', $register_mode) + ->save(); + + // Create a blocked user. + $blocked_user = $this->drupalCreateUser()->block(); + $blocked_user->save(); + + $this->drupalGet('user/' . $blocked_user->id() . '/edit'); + $this->submitForm(['status' => 0], 'Send awaiting approval message'); + $mails = $this->getMails(); + + // Assert that an email was sent to a blocked user and admin. + $this->assertCount(2, $mails); + $this->assertEquals($mails[0]['to'], $blocked_user->getEmail(), 'Awaiting approval mail send to user'); + $this->assertEquals($mails[0]['id'], $mail_id); + $this->assertEquals($mails[1]['to'], 'simpletest@example.com', 'Pending admin approval mail send to admin'); + $this->assertEquals($mails[1]['id'], 'user_register_pending_approval_admin'); + } + + /** + * Returns a collection of alternative registration configurations. + * + * @return array + * List of registration config & expected user mail. + */ + public function awaitingApprovalNotifications() { + return [ + [ + UserInterface::REGISTER_VISITORS, + 'user_register_pending_approval', + ], + [ + UserInterface::REGISTER_ADMINISTRATORS_ONLY, + 'user_register_pending_approval', + ], + [ + UserInterface::REGISTER_VISITORS_ADMINISTRATIVE_APPROVAL, + 'user_register_pending_approval', + ], + ]; + } + + /** + * Tests the bulk action sending of welcome emails notifications. + */ + public function testBulkSendWelcomeEmailsNotifications() { + $admin_user = $this->drupalCreateUser(['administer users']); + $this->drupalLogin($admin_user); + + // Create users at a certain timestamp to fix order of "Member for". + $user_a = $this->drupalCreateUser(); + $user_a->created = 1363219200; + $user_a->save(); + $user_b = $this->drupalCreateUser(); + $user_b->created = 1394755200; + $user_b->save(); + $user_c = $this->drupalCreateUser(); + $user_c->created = 1426291200; + $user_c->save(); + + // Send welcome message to user_b and user_c. + $edit = [ + 'action' => 'user_welcome_message_action', + 'user_bulk_form[3]' => TRUE, + 'user_bulk_form[2]' => TRUE, + ]; + $this->drupalGet('admin/people'); + $this->submitForm($edit, 'Apply to selected items'); + $mails = $this->getMails(); + + // Assert that welcome emails were sent to user_b and user_c. + $this->assertCount(2, $mails); + $this->assertEquals($mails[0]['to'], $user_c->getEmail(), 'Activation mail send to user C'); + $this->assertEquals($mails[0]['id'], 'user_register_no_approval_required'); + $this->assertEquals($mails[1]['to'], $user_b->getEmail(), 'Activation mail send to user B'); + $this->assertEquals($mails[1]['id'], 'user_register_no_approval_required'); + } + + /** + * Tests the bulk action sending of waiting approval email notification. + */ + public function testBulkSendAwaitingApprovalEmailNotification() { + $admin_user = $this->drupalCreateUser(['administer users']); + $this->drupalLogin($admin_user); + + // Create users at a certain timestamp to fix order of "Member for". + $user_a = $this->drupalCreateUser()->block(); + $user_a->created = 1363219200; + $user_a->save(); + $user_b = $this->drupalCreateUser()->block(); + $user_b->created = 1394755200; + $user_b->save(); + $user_c = $this->drupalCreateUser()->block(); + $user_c->created = 1426291200; + $user_c->save(); + + // Send waiting approval mail to user_b & user_c. + $edit = [ + 'action' => 'user_welcome_message_action', + 'user_bulk_form[3]' => TRUE, + 'user_bulk_form[2]' => TRUE, + ]; + $this->drupalGet('admin/people'); + $this->submitForm($edit, ('Apply to selected items')); + $mails = $this->getMails(); + + // Assert that welcome emails were sent to user_b and user_c. + // The admin should be notified twice. + $this->assertCount(4, $mails); + + // Assert that awaiting approval and notification emails have been sent to + // user_b and user_c. + $this->assertEquals($mails[0]['id'], 'user_register_pending_approval'); + $this->assertEquals($mails[0]['to'], $user_c->getEmail(), 'Awaiting approval mail send to user C'); + // Admin notification about user C notification send. + $this->assertEquals($mails[1]['id'], 'user_register_pending_approval_admin'); + $this->assertEquals($mails[1]['to'], 'simpletest@example.com', 'Pending admin approval mail send to admin'); + // Awaiting approval mail send to user B. + $this->assertEquals($mails[2]['id'], 'user_register_pending_approval'); + $this->assertEquals($mails[2]['to'], $user_b->getEmail(), 'Awaiting approval mail send to user B'); + // Admin notification about user B notification send. + $this->assertEquals($mails[3]['id'], 'user_register_pending_approval_admin'); + $this->assertEquals($mails[3]['to'], 'simpletest@example.com', 'Pending admin approval mail send to admin'); + } + } diff --git a/core/modules/user/user.post_update.php b/core/modules/user/user.post_update.php index 14662df8ef..f47070ecb0 100644 --- a/core/modules/user/user.post_update.php +++ b/core/modules/user/user.post_update.php @@ -9,6 +9,8 @@ use Drupal\Core\StringTranslation\PluralTranslatableMarkup; use Drupal\user\Entity\Role; +use Drupal\system\Entity\Action; + /** * Implements hook_removed_post_updates(). */ @@ -47,3 +49,17 @@ function user_post_update_update_roles(&$sandbox = NULL) { ); } } + +/** + * Add an action to send activation emails to multiple users. + */ +function user_post_update_add_send_action() { + $action = Action::create([ + 'id' => 'user_welcome_message_action', + 'type' => 'user', + 'label' => 'Send the welcome message to the selected user(s)', + 'configuration' => [], + 'plugin' => 'user_welcome_message_action', + ]); + $action->trustData()->save(); +}