contacts-8.x-1.x-dev/src/PseudoEmailField.php

src/PseudoEmailField.php
<?php

namespace Drupal\contacts;

use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\DependencyInjection\DependencySerializationTrait;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\Element\Email;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\user\UserInterface;

/**
 * Encapsulates customisations related to the pseudo email field.
 *
 * The pseudo email field is an Extra Field that can be used to expose the
 * user's email address on user Form Modes (normally it always includes the
 * password too) as well as Profile Form Displays and Profile Form Modes.
 *
 * If configured to show on profile form modes, then it will validate that
 * the email is not in use and allow you to change the email address via
 * the profile. By default this is only used on the contacts_dashboard and
 * add_contact form displays.
 */
class PseudoEmailField {

  use StringTranslationTrait;
  use DependencySerializationTrait;

  /**
   * Entity type manager.
   *
   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
   */
  protected $entityTypeManager;

  /**
   * Config factory.
   *
   * @var \Drupal\Core\Config\ConfigFactoryInterface
   */
  protected $configFactory;

  /**
   * Instantiates the pseudo email helper class.
   *
   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
   *   Entity type manager.
   * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
   *   Config factory.
   */
  public function __construct(EntityTypeManagerInterface $entity_type_manager, ConfigFactoryInterface $config_factory) {
    $this->entityTypeManager = $entity_type_manager;
    $this->configFactory = $config_factory;
  }

  /**
   * Defines the extra field definitions.
   *
   * @param array $fields
   *   Array to which the field definitions should be added.
   *
   * @see contacts_entity_extra_field_info
   */
  public function extraFieldDefinitions(array &$fields) {
    // Allow exposing the email on User form/display without other account info.
    $fields['user']['user']['form']['mail'] = [
      'label' => t('Email'),
      'description' => t('Contacts user mail form element.'),
      'weight' => -10,
      'visible' => FALSE,
    ];

    // Alter the original account field to indicate that it also includes email.
    $fields['user']['user']['form']['account']['label'] = $this->t('@original (includes email)', [
      '@original' => $fields['user']['user']['form']['account']['label'],
    ]);

    // Also allow exposing the email from Profile forms/display too.
    $fields['profile']['crm_org']['display']['contacts_mail'] = [
      'label' => t('Email'),
      'description' => t('Contacts user mail form element.'),
      'weight' => -10,
      'visible' => FALSE,
    ];
    $fields['profile']['crm_indiv']['display']['contacts_mail'] = [
      'label' => t('Email'),
      'description' => t('Contacts user mail form element.'),
      'weight' => -10,
      'visible' => FALSE,
    ];
    $fields['profile']['crm_org']['form']['contacts_mail'] = [
      'label' => t('Email'),
      'description' => t('Contacts user mail form element.'),
      'weight' => -10,
      'visible' => FALSE,
    ];
    $fields['profile']['crm_indiv']['form']['contacts_mail'] = [
      'label' => t('Email'),
      'description' => t('Contacts user mail form element.'),
      'weight' => -10,
      'visible' => FALSE,
    ];
  }

  /**
   * Alters the user form.
   *
   * @param array $form
   *   Form.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   State.
   * @param string $form_id
   *   Form id.
   *
   * @see contacts_form_user_form_alter
   */
  public function userFormAlter(array &$form, FormStateInterface $form_state, $form_id) {
    $display = $this->getFormDisplay($form_state);
    // Only add mail if the component is shown and the account is not.
    if ($display->getComponent('mail') && !$display->getComponent('account') && isset($form['account'])) {
      $form['mail'] = $form['account']['mail'];
    }

    // Don't allow non-admins to make other people admins.
    if (isset($form['account']['roles']['#options']['administrator']) && !in_array('administrator', \Drupal::currentUser()->getRoles())) {
      $form['account']['roles']['administrator']['#disabled'] = TRUE;
    }
  }

  /**
   * Alters the Profile form to include the pseudo email field.
   *
   * @param array $form
   *   Form.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   State.
   * @param string $form_id
   *   Form id.
   *
   * @see contacts_form_profile_form_alter
   */
  public function profileFormAlter(array &$form, FormStateInterface $form_state, $form_id) {
    $display = $this->getFormDisplay($form_state);
    $profile = $this->getProfile($form_state);

    // Mail isn't a field on profile directly but Contacts allows a pseudo mail
    // to be added which links to the profile owner. Generate the markup for
    // this field if the form display is configured to include it.
    if ($display->getComponent('contacts_mail')) {
      if ($user = $profile->getOwner()) {
        /** @var \Drupal\user\UserInterface $user */
        // Prevent editing the user's email in the situation where the user
        // being edited is an administrator and the current user who is
        // making the change doesn't have access to change their record. Note
        // that normally we don't care about whether we have access to
        // update the target user because we are essentially treating the email
        // as a field on the profile (not the user), so as the editor has
        // access to the profile then we allow it. But we're making a special
        // case for if the target user is an admin.
        $restrict_access = !$user->access('update') && $this->isUserAdmin($user);

        $form['contacts_mail'] = [
          '#type' => 'email',
          '#title' => $this->t('Email address'),
          '#required' => $this->isEmailRequired($profile->bundle()),
          '#default_value' => $user->getEmail(),
          '#disabled' => $restrict_access,
          '#element_validate' => [
            [Email::class, 'validateEmail'],
            [$this, 'validateProfileEmail'],
          ],
        ];

        // Add in additional submission step that saves the email change.
        $submit_handler = [$this, 'saveProfileEmail'];
        array_unshift($form['actions']['submit']['#submit'], $submit_handler);
      }
      else {
        // Existing profile with no owner - orphaned.
        $form['contacts_mail'] = [
          '#type' => 'item',
          '#title' => $this->t('Email address'),
          '#markup' => $this->t('This profile is not associated with a user.'),
        ];
      }
    }
  }

  /**
   * Validation callback for the pseudo mail field.
   *
   * @param array $element
   *   Mail element.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   Form state.
   * @param array $complete_form
   *   Form.
   */
  public function validateProfileEmail(array &$element, FormStateInterface $form_state, array &$complete_form) {
    // If our pseudo-email field is present, we need to validate that the email
    // address is not in use. Note that this only covers existing profiles.
    $display = $this->getFormDisplay($form_state);
    $profile = $this->getProfile($form_state);

    $mail = trim($element['#value']);
    $user_id = $profile->getOwnerId();

    if ($user_id) {
      /** @var \Drupal\user\UserInterface $user */
      // Load the user afresh as we need to set the email in order to validate
      // and we want to ensure this change is isolated from the user instance
      // currently stored within getOwner().
      $user = $this->entityTypeManager
        ->getStorage('user')
        ->loadUnchanged($user_id);
    }
    else {
      $user = $this->entityTypeManager
        ->getStorage('user')
        ->create();
    }

    $user->setEmail($mail);
    $violations = $user->validate();

    foreach ($violations->getByField('mail') as $violation) {
      /** @var \Symfony\Component\Validator\ConstraintViolationInterface $violation */
      $form_state->setError($element, $violation->getMessage());
    }
    $display->flagWidgetsErrorsFromViolations($violations, $complete_form, $form_state);
  }

  /**
   * Saves changed emails on the profile.
   *
   * @param array $form
   *   Form.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   State.
   */
  public function saveProfileEmail(array &$form, FormStateInterface $form_state) {
    // If the email address has been changed (via the pseudo mail field) then
    // change this on the corresponding user too.
    $profile = $this->getProfile($form_state);
    $mail = $form_state->getValue('contacts_mail');

    /** @var \Drupal\user\UserInterface $user */
    $user = $profile->getOwner();

    if ($mail !== $user->getEmail()) {
      $user->setEmail($mail);
      $user->save();
    }
  }

  /**
   * Gets the form display.
   *
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The current state of the form.
   *
   * @return \Drupal\Core\Entity\Display\EntityFormDisplayInterface
   *   The current form display.
   */
  private function getFormDisplay(FormStateInterface $form_state) {
    return $form_state->get('form_display');
  }

  /**
   * Checks if the email field is required for a specific profile type.
   *
   * @param string $profile_type_id
   *   Profile type.
   *
   * @return bool
   *   TRUE if the email is required, otherwise FALSE.
   */
  private function isEmailRequired($profile_type_id) {
    $required_roles = $this->configFactory
      ->get('contacts.configuration')
      ->get('email_required_roles');

    /** @var \Drupal\profile\Entity\ProfileType $profile_type */
    $profile_type = $this->entityTypeManager
      ->getStorage('profile_type')
      ->load($profile_type_id);

    foreach (array_filter($profile_type->getRoles()) as $role_id) {
      if (in_array($role_id, $required_roles)) {
        return TRUE;
      }
    }

    return FALSE;
  }

  /**
   * Checks if a user has any admin roles.
   *
   * @param \Drupal\user\UserInterface $user
   *   User to check.
   *
   * @return bool
   *   TRUE if the user has at least 1 admin role.
   */
  private function isUserAdmin(UserInterface $user) {
    /** @var \Drupal\user\Entity\Role[] $roles */
    $roles = $this->entityTypeManager
      ->getStorage('user_role')
      ->loadMultiple($user->getRoles());

    foreach ($roles as $role) {
      if ($role->isAdmin()) {
        return TRUE;
      }
    }
    return FALSE;
  }

  /**
   * Gets the current profile instance being edited in the form.
   *
   * @return \Drupal\profile\Entity\Profile
   *   The profile being edited.
   */
  protected function getProfile(FormStateInterface $form_state) {
    return $form_state->getFormObject()->getEntity();
  }

}

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc