user_api-1.0.0-beta1/modules/user_api_email_confirm/user_api_email_confirm.module
modules/user_api_email_confirm/user_api_email_confirm.module
<?php
/**
* @file
* Module file.
*/
declare(strict_types=1);
use Drupal\Component\Render\PlainTextOutput;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\user\UserInterface;
/**
* Update translations.
*
* Update config_translation definitions to allow translation
* of user_api_email_confirm emails in user admin form.
*/
function user_api_email_confirm_config_translation_info(&$definitions) {
if (array_key_exists('entity.user.admin_form', $definitions)) {
$definitions['entity.user.admin_form']['names'][] = 'user_api_email_confirm.settings';
$definitions['entity.user.admin_form']['names'][] = 'user_api_email_confirm.mail';
}
}
/**
* Implements hook_form_user_admin_settings_alter().
*/
function user_api_email_confirm_form_user_admin_settings_alter(&$form, FormStateInterface $form_state, $form_id) {
$config = \Drupal::config('user_api_email_confirm.settings');
$mailConfig = \Drupal::config('user_api_email_confirm.mail');
$email_token_help = new TranslatableMarkup('Available variables are: [site:name], [site:url], [site:mail], [user:display-name], [user:account-name], [user:mail], [site:login-url], [site:url-brief], [user:edit-url], [user:one-time-login-url], [user:mail-change-url], [user:cancel-url].');
$form['mail_change'] = [
'#type' => 'details',
'#title' => new TranslatableMarkup('Account email changing'),
'#collapsible' => TRUE,
'#collapsed' => TRUE,
'#group' => 'email',
'#weight' => 11,
];
$form['mail_change']['mail_change_notification'] = [
'#type' => 'details',
'#tree' => TRUE,
'#title' => new TranslatableMarkup('Notification of old email'),
'#description' => new TranslatableMarkup("Edit the email message sent to the user's old email address when the email address is changed.") . ' ' . $email_token_help,
'#open' => TRUE,
];
$form['mail_change']['mail_change_notification']['enabled'] = [
'#type' => 'checkbox',
'#title' => new TranslatableMarkup('Notify user when email changes'),
'#default_value' => $config->get('notify.mail_change_notification'),
];
$states = [
'invisible' => [
'input[name="mail_change_notification[enabled]"]' => ['checked' => FALSE],
],
];
$form['mail_change']['mail_change_notification']['subject'] = [
'#type' => 'textfield',
'#title' => new TranslatableMarkup('Subject'),
'#default_value' => $mailConfig->get('mail_change_notification.subject'),
'#maxlength' => 180,
'#states' => $states,
];
$form['mail_change']['mail_change_notification']['body'] = [
'#type' => 'textarea',
'#title' => new TranslatableMarkup('Body'),
'#default_value' => $mailConfig->get('mail_change_notification.body'),
'#rows' => 12,
'#states' => $states,
];
$form['mail_change']['mail_change_verification'] = [
'#type' => 'details',
'#tree' => TRUE,
'#title' => new TranslatableMarkup('Verification of new email'),
'#description' => new TranslatableMarkup("Edit the email message sent to user's new email address when the email address is changed.") . ' ' . $email_token_help,
'#open' => TRUE,
];
$form['mail_change']['mail_change_verification']['enabled'] = [
'#type' => 'checkbox',
'#title' => new TranslatableMarkup('Require email verification when a user changes their email address'),
'#default_value' => $config->get('notify.mail_change_verification'),
];
$states = [
'invisible' => [
'input[name="mail_change_verification[enabled]"]' => ['checked' => FALSE],
],
];
$form['mail_change']['mail_change_verification']['subject'] = [
'#type' => 'textfield',
'#title' => new TranslatableMarkup('Subject'),
'#default_value' => $mailConfig->get('mail_change_verification.subject'),
'#maxlength' => 180,
'#states' => $states,
];
$form['mail_change']['mail_change_verification']['body'] = [
'#type' => 'textarea',
'#title' => new TranslatableMarkup('Body'),
'#default_value' => $mailConfig->get('mail_change_verification.body'),
'#rows' => 12,
'#states' => $states,
];
$form['#submit'][] = '_user_api_email_confirm_form_user_admin_settings_submit';
}
/**
* Custom form submit handler for user admin form.
*/
function _user_api_email_confirm_form_user_admin_settings_submit(array &$form, FormStateInterface $form_state) {
\Drupal::configFactory()->getEditable('user_api_email_confirm.settings')
->set('notify.mail_change_notification', $form_state->getValue([
'mail_change_notification',
'enabled',
]))
->set('notify.mail_change_verification', $form_state->getValue([
'mail_change_verification',
'enabled',
]))
->save();
\Drupal::configFactory()->getEditable('user_api_email_confirm.mail')
->set('mail_change_notification', [
'subject' => $form_state->getValue(['mail_change_notification', 'subject']),
'body' => $form_state->getValue(['mail_change_notification', 'body']),
])
->set('mail_change_verification', [
'subject' => $form_state->getValue(['mail_change_verification', 'subject']),
'body' => $form_state->getValue(['mail_change_verification', 'body']),
])
->save();
}
/**
* Implements hook_form_user_alter().
*/
function user_api_email_confirm_form_user_form_alter(&$form, FormStateInterface $form_state, $form_id) {
$form['#validate'][] = '_user_api_email_confirm_form_user_form_validate';
}
/**
* Custom form validation handler for user form.
*/
function _user_api_email_confirm_form_user_form_validate(array &$form, FormStateInterface $form_state) {
/** @var \Drupal\user\UserInterface $subject */
$subject = \Drupal::request()->get('user');
if (!$subject) {
return;
}
// Only care about updates from the same user.
if ($subject->id() !== \Drupal::currentUser()->id()) {
return;
}
if ($subject->getEmail() !== $form_state->getValue('mail')) {
$form_state->setErrorByName('mail', new TranslatableMarkup('Cannot directly update the email without verification!'));
}
}
/**
* Implements hook_user_presave().
*
* This functions as a last-resort to prohibit directly changing the email
* address by the user itself. By default, this behavior should be handled
* by the user form.
*/
function user_api_email_confirm_user_presave(EntityInterface $entity) {
// We only handle entity updates.
if ($entity instanceof UserInterface && !$entity->isNew()) {
/** @var \Drupal\user\UserInterface $oldUser */
/* @phpstan-ignore-next-line */
$oldUser = $entity->original;
/** @var \Drupal\user\UserInterface $oldUser */
$user = $entity;
// Only check for updates done by the user itself.
if (\Drupal::currentUser()->id() !== $user->id()) {
return;
}
// Allow the rest resource to update mails.
if (\Drupal::request()->get('_route') === 'rest.user_api_email_confirm_set_email.POST') {
return;
}
$requireVerification = \Drupal::config('user_api_email_confirm.settings')->get('notify.mail_change_verification');
// Intercept if user is trying to directly change email but
// verification is required.
if ($requireVerification && $oldUser->getEmail() !== $user->getEmail()) {
throw new \Exception('Cannot change email directly!');
}
}
}
/**
* Implements hook_verification_provider_hash_supported_operations_alter().
*/
function user_api_email_confirm_verification_hash_supported_operations_alter(&$operations) {
$operations[] = 'set-email';
}
/**
* Implements hook_verification_provider_hash_timeout_alter().
*/
function user_api_email_confirm_verification_hash_timeout_alter(&$timeout, $operation) {
if ($operation === 'set-email') {
$timeout = (int) (\Drupal::config('user_api_email_confirm.settings')->get('mail_change_timeout') ?? 86400);
}
}
/**
* Implements hook_mail().
*/
function user_api_email_confirm_mail($key, &$message, $params) {
$token_service = \Drupal::token();
$language_manager = \Drupal::languageManager();
$langcode = $message['langcode'];
$variables = ['user' => $params['account']];
$language = $language_manager->getLanguage($langcode);
$original_language = $language_manager->getConfigOverrideLanguage();
$language_manager->setConfigOverrideLanguage($language);
$mail_config = \Drupal::config('user_api_email_confirm.mail');
$token_options = [
'langcode' => $langcode,
'callback' => 'user_mail_tokens',
'clear' => TRUE,
];
$message['subject'] .= PlainTextOutput::renderFromHtml($token_service->replace($mail_config->get($key . '.subject'), $variables, $token_options));
$message['body'][] = $token_service->replace($mail_config->get($key . '.body'), $variables, $token_options);
$language_manager->setConfigOverrideLanguage($original_language);
}
/**
* Custom user email notification.
*
* This is basically the same as _user_mail_notify() but altered
* to work with the emails introduced by this module.
*
* @param string $op
* The operation being performed on the account. Possible values:
* - 'mail_change_verification': Verification email sent to the user
* new email address to confirm ownership.
* - 'mail_change_notification': Info email sent to the user's current
* email address to inform about the email change.
* @param \Drupal\Core\Session\AccountInterface $account
* The user object of the account being notified. Must contain at
* least the fields 'uid', 'name', and 'mail'.
*
* @return array
* An array containing various information about the message.
* See \Drupal\Core\Mail\MailManagerInterface::mail() for details.
*
* @see _user_mail_notify()
*/
function _user_api_email_confirm_mail_notify($op, AccountInterface $account) {
if (\Drupal::config('user_api_email_confirm.settings')->get('notify.' . $op)) {
$params['account'] = $account;
$langcode = $account->getPreferredLangcode();
// Get the custom site notification email to use as the from email address
// if it has been set.
$site_mail = \Drupal::config('system.site')->get('mail_notification');
// If the custom site notification email has not been set, we use the site
// default for this.
if (empty($site_mail)) {
$site_mail = \Drupal::config('system.site')->get('mail');
}
if (empty($site_mail)) {
$site_mail = ini_get('sendmail_from');
}
$mail = \Drupal::service('plugin.manager.mail')->mail('user_api_email_confirm', $op, $account->getEmail(), $langcode, $params, $site_mail);
}
return empty($mail) ? NULL : $mail['result'];
}
/**
* Implements hook_magic_code_user_mail_token_operations_alter().
*/
function user_api_email_confirm_magic_code_user_mail_token_operations_alter(&$operations) {
$operations[] = 'set-email';
}
