1<?php 2 3namespace Drupal\contact; 4 5use Drupal\Component\Datetime\TimeInterface; 6use Drupal\Core\Datetime\DateFormatterInterface; 7use Drupal\Core\Entity\ContentEntityForm; 8use Drupal\Core\Entity\EntityRepositoryInterface; 9use Drupal\Core\Entity\EntityTypeBundleInfoInterface; 10use Drupal\Core\Flood\FloodInterface; 11use Drupal\Core\Form\FormStateInterface; 12use Drupal\Core\Language\LanguageManagerInterface; 13use Symfony\Component\DependencyInjection\ContainerInterface; 14 15/** 16 * Form controller for contact message forms. 17 * 18 * @internal 19 */ 20class MessageForm extends ContentEntityForm { 21 22 /** 23 * The message being used by this form. 24 * 25 * @var \Drupal\contact\MessageInterface 26 */ 27 protected $entity; 28 29 /** 30 * The flood control mechanism. 31 * 32 * @var \Drupal\Core\Flood\FloodInterface 33 */ 34 protected $flood; 35 36 /** 37 * The language manager service. 38 * 39 * @var \Drupal\Core\Language\LanguageManagerInterface 40 */ 41 protected $languageManager; 42 43 /** 44 * The contact mail handler service. 45 * 46 * @var \Drupal\contact\MailHandlerInterface 47 */ 48 protected $mailHandler; 49 50 /** 51 * The date formatter service. 52 * 53 * @var \Drupal\Core\Datetime\DateFormatterInterface 54 */ 55 protected $dateFormatter; 56 57 /** 58 * Constructs a MessageForm object. 59 * 60 * @param \Drupal\Core\Entity\EntityRepositoryInterface $entity_repository 61 * The entity repository. 62 * @param \Drupal\Core\Flood\FloodInterface $flood 63 * The flood control mechanism. 64 * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager 65 * The language manager service. 66 * @param \Drupal\contact\MailHandlerInterface $mail_handler 67 * The contact mail handler service. 68 * @param \Drupal\Core\Datetime\DateFormatterInterface $date_formatter 69 * The date service. 70 * @param \Drupal\Core\Entity\EntityTypeBundleInfoInterface $entity_type_bundle_info 71 * The entity type bundle service. 72 * @param \Drupal\Component\Datetime\TimeInterface $time 73 * The time service. 74 */ 75 public function __construct(EntityRepositoryInterface $entity_repository, FloodInterface $flood, LanguageManagerInterface $language_manager, MailHandlerInterface $mail_handler, DateFormatterInterface $date_formatter, EntityTypeBundleInfoInterface $entity_type_bundle_info = NULL, TimeInterface $time = NULL) { 76 parent::__construct($entity_repository, $entity_type_bundle_info, $time); 77 $this->flood = $flood; 78 $this->languageManager = $language_manager; 79 $this->mailHandler = $mail_handler; 80 $this->dateFormatter = $date_formatter; 81 } 82 83 /** 84 * {@inheritdoc} 85 */ 86 public static function create(ContainerInterface $container) { 87 return new static( 88 $container->get('entity.repository'), 89 $container->get('flood'), 90 $container->get('language_manager'), 91 $container->get('contact.mail_handler'), 92 $container->get('date.formatter'), 93 $container->get('entity_type.bundle.info'), 94 $container->get('datetime.time') 95 ); 96 } 97 98 /** 99 * {@inheritdoc} 100 */ 101 public function form(array $form, FormStateInterface $form_state) { 102 $user = $this->currentUser(); 103 $message = $this->entity; 104 $form = parent::form($form, $form_state, $message); 105 $form['#attributes']['class'][] = 'contact-form'; 106 107 if (!empty($message->preview)) { 108 $form['preview'] = [ 109 '#theme_wrappers' => ['container__preview'], 110 '#attributes' => ['class' => ['preview']], 111 ]; 112 $form['preview']['message'] = $this->entityTypeManager->getViewBuilder('contact_message')->view($message, 'full'); 113 } 114 115 $form['name'] = [ 116 '#type' => 'textfield', 117 '#title' => $this->t('Your name'), 118 '#maxlength' => 255, 119 '#required' => TRUE, 120 ]; 121 $form['mail'] = [ 122 '#type' => 'email', 123 '#title' => $this->t('Your email address'), 124 '#required' => TRUE, 125 ]; 126 if ($user->isAnonymous()) { 127 $form['#attached']['library'][] = 'core/drupal.form'; 128 $form['#attributes']['data-user-info-from-browser'] = TRUE; 129 } 130 // Do not allow authenticated users to alter the name or email values to 131 // prevent the impersonation of other users. 132 else { 133 $form['name']['#type'] = 'item'; 134 $form['name']['#value'] = $user->getDisplayName(); 135 $form['name']['#required'] = FALSE; 136 $form['name']['#plain_text'] = $user->getDisplayName(); 137 138 $form['mail']['#type'] = 'item'; 139 $form['mail']['#value'] = $user->getEmail(); 140 $form['mail']['#required'] = FALSE; 141 $form['mail']['#plain_text'] = $user->getEmail(); 142 } 143 144 // The user contact form has a preset recipient. 145 if ($message->isPersonal()) { 146 $form['recipient'] = [ 147 '#type' => 'item', 148 '#title' => $this->t('To'), 149 '#value' => $message->getPersonalRecipient()->id(), 150 'name' => [ 151 '#theme' => 'username', 152 '#account' => $message->getPersonalRecipient(), 153 ], 154 ]; 155 } 156 157 $form['copy'] = [ 158 '#type' => 'checkbox', 159 '#title' => $this->t('Send yourself a copy'), 160 // Do not allow anonymous users to send themselves a copy, because it can 161 // be abused to spam people. 162 '#access' => $user->isAuthenticated(), 163 ]; 164 return $form; 165 } 166 167 /** 168 * {@inheritdoc} 169 */ 170 public function actions(array $form, FormStateInterface $form_state) { 171 $elements = parent::actions($form, $form_state); 172 $elements['submit']['#value'] = $this->t('Send message'); 173 $elements['preview'] = [ 174 '#type' => 'submit', 175 '#value' => $this->t('Preview'), 176 '#submit' => ['::submitForm', '::preview'], 177 ]; 178 return $elements; 179 } 180 181 /** 182 * Form submission handler for the 'preview' action. 183 */ 184 public function preview(array $form, FormStateInterface $form_state) { 185 $message = $this->entity; 186 $message->preview = TRUE; 187 $form_state->setRebuild(); 188 } 189 190 /** 191 * {@inheritdoc} 192 */ 193 public function validateForm(array &$form, FormStateInterface $form_state) { 194 $message = parent::validateForm($form, $form_state); 195 196 // Check if flood control has been activated for sending emails. 197 if (!$this->currentUser()->hasPermission('administer contact forms') && (!$message->isPersonal() || !$this->currentUser()->hasPermission('administer users'))) { 198 $limit = $this->config('contact.settings')->get('flood.limit'); 199 $interval = $this->config('contact.settings')->get('flood.interval'); 200 201 if (!$this->flood->isAllowed('contact', $limit, $interval)) { 202 $form_state->setErrorByName('', $this->t('You cannot send more than %limit messages in @interval. Try again later.', [ 203 '%limit' => $limit, 204 '@interval' => $this->dateFormatter->formatInterval($interval), 205 ])); 206 } 207 } 208 209 return $message; 210 } 211 212 /** 213 * {@inheritdoc} 214 */ 215 public function save(array $form, FormStateInterface $form_state) { 216 $message = $this->entity; 217 $user = $this->currentUser(); 218 // Save the message. In core this is a no-op but should contrib wish to 219 // implement message storage, this will make the task of swapping in a real 220 // storage controller straight-forward. 221 $message->save(); 222 $this->mailHandler->sendMailMessages($message, $user); 223 $contact_form = $message->getContactForm(); 224 225 $this->flood->register('contact', $this->config('contact.settings')->get('flood.interval')); 226 if ($submission_message = $contact_form->getMessage()) { 227 $this->messenger()->addStatus($submission_message); 228 } 229 230 // To avoid false error messages caused by flood control, redirect away from 231 // the contact form; either to the contacted user account or the front page. 232 if ($message->isPersonal() && $user->hasPermission('access user profiles')) { 233 $form_state->setRedirectUrl($message->getPersonalRecipient()->toUrl()); 234 } 235 else { 236 $form_state->setRedirectUrl($contact_form->getRedirectUrl()); 237 } 238 } 239 240} 241