updated core and modules

This commit is contained in:
Bachir Soussi Chiadmi
2017-09-09 16:27:51 +02:00
parent 027aa99b32
commit 41863f872c
7810 changed files with 318922 additions and 83631 deletions
@@ -6,3 +6,5 @@ label: 'Personal contact form'
recipients: { }
reply: ''
weight: 0
message: 'Your message has been sent.'
redirect: ''
@@ -22,6 +22,12 @@ contact.form.*:
weight:
type: integer
label: 'Weight'
message:
type: label
label: 'Message displayed to user on submission'
redirect:
type: path
label: 'Redirect path on submission'
contact.settings:
type: config_object
+3 -3
View File
@@ -6,8 +6,8 @@ package: Core
# core: 8.x
configure: entity.contact_form.collection
# Information added by Drupal.org packaging script on 2016-08-03
version: '8.1.8'
# Information added by Drupal.org packaging script on 2017-08-16
version: '8.3.7'
core: '8.x'
project: 'drupal'
datestamp: 1470233790
datestamp: 1502903957
+36 -31
View File
@@ -21,17 +21,17 @@ function contact_help($route_name, RouteMatchInterface $route_match) {
$contact_page = \Drupal::url('entity.contact_form.collection');
$output = '';
$output .= '<h3>' . t('About') . '</h3>';
$output .= '<p>' . t('The Contact module allows visitors to contact registered users on your site, using the personal contact form, and also allows you to set up site-wide contact forms. For more information, see the <a href=":contact">online documentation for the Contact module</a>.', array(':contact' => 'https://www.drupal.org/documentation/modules/contact')) . '</p>';
$output .= '<p>' . t('The Contact module allows visitors to contact registered users on your site, using the personal contact form, and also allows you to set up site-wide contact forms. For more information, see the <a href=":contact">online documentation for the Contact module</a>.', [':contact' => 'https://www.drupal.org/documentation/modules/contact']) . '</p>';
$output .= '<h3>' . t('Uses') . '</h3>';
$output .= '<dl>';
$output .= '<dt>' . t('Using the personal contact form') . '</dt>';
$output .= '<dd>' . t("Site visitors can email registered users on your site by using the personal contact form, without knowing or learning the email address of the recipient. When a site visitor is viewing a user profile, the viewer will see a <em>Contact</em> tab or link, which leads to the personal contact form. The personal contact link is not shown when you are viewing your own profile, and users must have both <em>View user information</em> (to see user profiles) and <em>Use users' personal contact forms</em> permission to see the link. The user whose profile is being viewed must also have their personal contact form enabled (this is a user account setting); viewers with <em>Administer users</em> permission can bypass this setting.") . '</dd>';
$output .= '<dt>' . t('Configuring contact forms') . '</dt>';
$output .= '<dd>' . t('On the <a href=":contact_admin">Contact forms page</a>, you can configure the fields and display of the personal contact form, and you can set up one or more site-wide contact forms. Each site-wide contact form has a machine name, a label, and one or more defined recipients; when a site visitor submits the form, the field values are sent to those recipients.', array(':contact_admin' => $contact_page)) . '</dd>';
$output .= '<dd>' . t('On the <a href=":contact_admin">Contact forms page</a>, you can configure the fields and display of the personal contact form, and you can set up one or more site-wide contact forms. Each site-wide contact form has a machine name, a label, and one or more defined recipients; when a site visitor submits the form, the field values are sent to those recipients.', [':contact_admin' => $contact_page]) . '</dd>';
$output .= '<dt>' . t('Linking to contact forms') . '</dt>';
$output .= '<dd>' . t('One site-wide contact form can be designated as the default contact form. If you choose to designate a default form, the <em>Contact</em> menu link in the <em>Footer</em> menu will link to it. You can modify this link from the <a href=":menu-settings">Menus page</a> if you have the Menu UI module installed. You can also create links to other contact forms; the URL for each form you have set up has format <em>contact/machine_name_of_form</em>.', array(':menu-settings' => $menu_page)) . '</p>';
$output .= '<dd>' . t('One site-wide contact form can be designated as the default contact form. If you choose to designate a default form, the <em>Contact</em> menu link in the <em>Footer</em> menu will link to it. You can modify this link from the <a href=":menu-settings">Menus page</a> if you have the Menu UI module installed. You can also create links to other contact forms; the URL for each form you have set up has format <em>contact/machine_name_of_form</em>.', [':menu-settings' => $menu_page]) . '</p>';
$output .= '<dt>' . t('Adding content to contact forms') . '</dt>';
$output .= '<dd>' . t('From the <a href=":contact_admin">Contact forms page</a>, you can configure the fields to be shown on contact forms, including their labels and help text. If you would like other content (such as text or images) to appear on a contact form, use a block. You can create and edit blocks on the <a href=":blocks">Block layout page</a>, if the Block module is installed.', array(':blocks' => $block_page, ':contact_admin' => $contact_page)) . '</dd>';
$output .= '<dd>' . t('From the <a href=":contact_admin">Contact forms page</a>, you can configure the fields to be shown on contact forms, including their labels and help text. If you would like other content (such as text or images) to appear on a contact form, use a block. You can create and edit blocks on the <a href=":blocks">Block layout page</a>, if the Block module is installed.', [':blocks' => $block_page, ':contact_admin' => $contact_page]) . '</dd>';
$output .= '</dl>';
return $output;
@@ -50,37 +50,42 @@ function contact_entity_type_alter(array &$entity_types) {
* Implements hook_entity_extra_field_info().
*/
function contact_entity_extra_field_info() {
$fields = array();
foreach (array_keys(entity_get_bundles('contact_message')) as $bundle) {
$fields['contact_message'][$bundle]['form']['name'] = array(
$fields = [];
foreach (array_keys(\Drupal::service('entity_type.bundle.info')->getBundleInfo('contact_message')) as $bundle) {
$fields['contact_message'][$bundle]['form']['name'] = [
'label' => t('Sender name'),
'description' => t('Text'),
'weight' => -50,
);
$fields['contact_message'][$bundle]['form']['mail'] = array(
];
$fields['contact_message'][$bundle]['form']['mail'] = [
'label' => t('Sender email'),
'description' => t('Email'),
'weight' => -40,
);
];
if ($bundle == 'personal') {
$fields['contact_message'][$bundle]['form']['recipient'] = array(
$fields['contact_message'][$bundle]['form']['recipient'] = [
'label' => t('Recipient username'),
'description' => t('User'),
'weight' => -30,
);
];
}
$fields['contact_message'][$bundle]['form']['copy'] = array(
$fields['contact_message'][$bundle]['form']['preview'] = [
'label' => t('Preview sender message'),
'description' => t('Preview'),
'weight' => 40,
];
$fields['contact_message'][$bundle]['form']['copy'] = [
'label' => t('Send copy to sender'),
'description' => t('Option'),
'weight' => 50,
);
];
}
$fields['user']['user']['form']['contact'] = array(
$fields['user']['user']['form']['contact'] = [
'label' => t('Contact settings'),
'description' => t('Contact module form element.'),
'weight' => 5,
);
];
return $fields;
}
@@ -113,21 +118,21 @@ function contact_mail($key, &$message, $params) {
$sender = $params['sender'];
$language = \Drupal::languageManager()->getLanguage($message['langcode']);
$variables = array(
$variables = [
'@site-name' => \Drupal::config('system.site')->get('name'),
'@subject' => $contact_message->getSubject(),
'@form' => !empty($params['contact_form']) ? $params['contact_form']->label() : NULL,
'@form-url' => \Drupal::url('<current>', [], ['absolute' => TRUE, 'language' => $language]),
'@sender-name' => $sender->getDisplayName(),
);
];
if ($sender->isAuthenticated()) {
$variables['@sender-url'] = $sender->url('canonical', array('absolute' => TRUE, 'language' => $language));
$variables['@sender-url'] = $sender->url('canonical', ['absolute' => TRUE, 'language' => $language]);
}
else {
$variables['@sender-url'] = $params['sender']->getEmail();
}
$options = array('langcode' => $language->getId());
$options = ['langcode' => $language->getId()];
switch ($key) {
case 'page_mail':
@@ -145,10 +150,10 @@ function contact_mail($key, &$message, $params) {
case 'user_mail':
case 'user_copy':
$variables += array(
$variables += [
'@recipient-name' => $params['recipient']->getDisplayName(),
'@recipient-edit-url' => $params['recipient']->url('edit-form', array('absolute' => TRUE, 'language' => $language)),
);
'@recipient-edit-url' => $params['recipient']->url('edit-form', ['absolute' => TRUE, 'language' => $language]),
];
$message['subject'] .= t('[@site-name] @subject', $variables, $options);
$message['body'][] = t('Hello @recipient-name,', $variables, $options);
$message['body'][] = t("@sender-name (@sender-url) has sent you a message via your contact form at @site-name.", $variables, $options);
@@ -167,22 +172,22 @@ function contact_mail($key, &$message, $params) {
* @see \Drupal\user\ProfileForm::form()
*/
function contact_form_user_form_alter(&$form, FormStateInterface $form_state) {
$form['contact'] = array(
$form['contact'] = [
'#type' => 'details',
'#title' => t('Contact settings'),
'#open' => TRUE,
'#weight' => 5,
);
];
$account = $form_state->getFormObject()->getEntity();
if (!\Drupal::currentUser()->isAnonymous() && $account->id()) {
$account_data = \Drupal::service('user.data')->get('contact', $account->id(), 'enabled');
}
$form['contact']['contact'] = array(
$form['contact']['contact'] = [
'#type' => 'checkbox',
'#title' => t('Personal contact form'),
'#default_value' => isset($account_data) ? $account_data : \Drupal::config('contact.settings')->get('user_default_enabled'),
'#description' => t('Allow other users to contact you via a personal contact form which keeps your email address hidden. Note that some privileged users such as site administrators are still able to contact you even if you choose to disable this feature.'),
);
];
$form['actions']['submit']['#submit'][] = 'contact_user_profile_form_submit';
}
@@ -204,18 +209,18 @@ function contact_user_profile_form_submit($form, FormStateInterface $form_state)
* @see \Drupal\user\AccountSettingsForm
*/
function contact_form_user_admin_settings_alter(&$form, FormStateInterface $form_state) {
$form['contact'] = array(
$form['contact'] = [
'#type' => 'details',
'#title' => t('Contact settings'),
'#open' => TRUE,
'#weight' => 0,
);
$form['contact']['contact_default_status'] = array(
];
$form['contact']['contact_default_status'] = [
'#type' => 'checkbox',
'#title' => t('Enable the personal contact form by default for new users'),
'#description' => t('Changing this setting will not affect existing users.'),
'#default_value' => \Drupal::configFactory()->getEditable('contact.settings')->get('user_default_enabled'),
);
];
// Add submit handler to save contact configuration.
$form['#submit'][] = 'contact_form_user_admin_settings_submit';
}
@@ -0,0 +1,22 @@
<?php
/**
* @file
* Post update functions for Contact.
*/
use Drupal\contact\Entity\ContactForm;
/**
* Initialize 'message' and 'redirect' field values to 'contact_form' entities.
*/
function contact_post_update_add_message_redirect_field_to_contact_form() {
/** @var \Drupal\contact\ContactFormInterface $contact */
foreach (ContactForm::loadMultiple() as $contact) {
$contact
->setMessage('Your message has been sent.')
->setRedirectPath('')
->save();
}
}
+4 -4
View File
@@ -9,11 +9,11 @@
* Implements hook_views_data_alter().
*/
function contact_views_data_alter(&$data) {
$data['users']['contact'] = array(
'field' => array(
$data['users']['contact'] = [
'field' => [
'title' => t('Contact link'),
'help' => t('Provide a simple link to the user contact page.'),
'id' => 'contact_link',
),
);
],
];
}
@@ -11,9 +11,9 @@ process:
plugin: machine_name
source: category
-
plugin: dedupe_entity
entity_type: user_role
field: cid
plugin: make_unique_entity_field
entity_type: contact_form
field: id
length: 32
label: category
recipients: recipients
@@ -11,7 +11,7 @@ process:
user_default_enabled: contact_default_status
'flood/limit': contact_hourly_threshold
default_form:
plugin: migration
plugin: migration_lookup
migration: contact_category
source: default_category
destination:
@@ -11,7 +11,7 @@ process:
user_default_enabled: contact_default_status
'flood/limit': contact_threshold_limit
default_form:
plugin: migration
plugin: migration_lookup
migration: contact_category
source: default_category
destination:
@@ -2,6 +2,7 @@
namespace Drupal\contact;
use Drupal\Component\Utility\Unicode;
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\Core\Entity\EntityForm;
@@ -9,6 +10,8 @@ use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Form\ConfigFormBaseTrait;
use Drupal\Core\Form\FormStateInterface;
use Egulias\EmailValidator\EmailValidator;
use Drupal\Core\Path\PathValidatorInterface;
use Drupal\Core\Render\Element\PathElement;
/**
* Base form for contact form edit forms.
@@ -23,14 +26,24 @@ class ContactFormEditForm extends EntityForm implements ContainerInjectionInterf
*/
protected $emailValidator;
/**
* The path validator.
*
* @var \Drupal\Core\Path\PathValidatorInterface
*/
protected $pathValidator;
/**
* Constructs a new ContactFormEditForm.
*
* @param \Egulias\EmailValidator\EmailValidator $email_validator
* The email validator.
* @param \Drupal\Core\Path\PathValidatorInterface $path_validator
* The path validator service.
*/
public function __construct(EmailValidator $email_validator) {
$this->emailValidator = $email_validator;
public function __construct(EmailValidator $email_validator, PathValidatorInterface $path_validator) {
$this->emailValidator = $email_validator;
$this->pathValidator = $path_validator;
}
/**
@@ -38,7 +51,8 @@ class ContactFormEditForm extends EntityForm implements ContainerInjectionInterf
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('email.validator')
$container->get('email.validator'),
$container->get('path.validator')
);
}
@@ -58,47 +72,60 @@ class ContactFormEditForm extends EntityForm implements ContainerInjectionInterf
$contact_form = $this->entity;
$default_form = $this->config('contact.settings')->get('default_form');
$form['label'] = array(
$form['label'] = [
'#type' => 'textfield',
'#title' => $this->t('Label'),
'#maxlength' => 255,
'#default_value' => $contact_form->label(),
'#description' => $this->t("Example: 'website feedback' or 'product information'."),
'#required' => TRUE,
);
$form['id'] = array(
];
$form['id'] = [
'#type' => 'machine_name',
'#default_value' => $contact_form->id(),
'#maxlength' => EntityTypeInterface::BUNDLE_MAX_LENGTH,
'#machine_name' => array(
'#machine_name' => [
'exists' => '\Drupal\contact\Entity\ContactForm::load',
),
],
'#disabled' => !$contact_form->isNew(),
);
$form['recipients'] = array(
];
$form['recipients'] = [
'#type' => 'textarea',
'#title' => $this->t('Recipients'),
'#default_value' => implode(', ', $contact_form->getRecipients()),
'#description' => $this->t("Example: 'webmaster@example.com' or 'sales@example.com,support@example.com' . To specify multiple recipients, separate each email address with a comma."),
'#required' => TRUE,
);
$form['reply'] = array(
];
$form['message'] = [
'#type' => 'textarea',
'#title' => $this->t('Message'),
'#default_value' => $contact_form->getMessage(),
'#description' => $this->t('The message to display to the user after submission of this form. Leave blank for no message.'),
];
$form['redirect'] = [
'#type' => 'path',
'#title' => $this->t('Redirect path'),
'#convert_path' => PathElement::CONVERT_NONE,
'#default_value' => $contact_form->getRedirectPath(),
'#description' => $this->t('Path to redirect the user to after submission of this form. For example, type "/about" to redirect to that page. Use a relative path with a slash in front.'),
];
$form['reply'] = [
'#type' => 'textarea',
'#title' => $this->t('Auto-reply'),
'#default_value' => $contact_form->getReply(),
'#description' => $this->t('Optional auto-reply. Leave empty if you do not want to send the user an auto-reply message.'),
);
$form['weight'] = array(
];
$form['weight'] = [
'#type' => 'weight',
'#title' => $this->t('Weight'),
'#default_value' => $contact_form->getWeight(),
'#description' => $this->t('When listing forms, those with lighter (smaller) weights get listed before forms with heavier (larger) weights. Forms with equal weights are sorted alphabetically.'),
);
$form['selected'] = array(
];
$form['selected'] = [
'#type' => 'checkbox',
'#title' => $this->t('Make this the default form'),
'#default_value' => $default_form === $contact_form->id(),
);
];
return $form;
}
@@ -115,10 +142,16 @@ class ContactFormEditForm extends EntityForm implements ContainerInjectionInterf
foreach ($recipients as &$recipient) {
$recipient = trim($recipient);
if (!$this->emailValidator->isValid($recipient)) {
$form_state->setErrorByName('recipients', $this->t('%recipient is an invalid email address.', array('%recipient' => $recipient)));
$form_state->setErrorByName('recipients', $this->t('%recipient is an invalid email address.', ['%recipient' => $recipient]));
}
}
$form_state->setValue('recipients', $recipients);
$redirect_url = $form_state->getValue('redirect');
if ($redirect_url && $this->pathValidator->isValid($redirect_url)) {
if (Unicode::substr($redirect_url, 0, 1) !== '/') {
$form_state->setErrorByName('redirect', $this->t('The path should start with /.'));
}
}
}
/**
@@ -130,13 +163,14 @@ class ContactFormEditForm extends EntityForm implements ContainerInjectionInterf
$contact_settings = $this->config('contact.settings');
$edit_link = $this->entity->link($this->t('Edit'));
$view_link = $contact_form->link($contact_form->label(), 'canonical');
if ($status == SAVED_UPDATED) {
drupal_set_message($this->t('Contact form %label has been updated.', array('%label' => $contact_form->label())));
$this->logger('contact')->notice('Contact form %label has been updated.', array('%label' => $contact_form->label(), 'link' => $edit_link));
drupal_set_message($this->t('Contact form %label has been updated.', ['%label' => $view_link]));
$this->logger('contact')->notice('Contact form %label has been updated.', ['%label' => $contact_form->label(), 'link' => $edit_link]);
}
else {
drupal_set_message($this->t('Contact form %label has been added.', array('%label' => $contact_form->label())));
$this->logger('contact')->notice('Contact form %label has been added.', array('%label' => $contact_form->label(), 'link' => $edit_link));
drupal_set_message($this->t('Contact form %label has been added.', ['%label' => $view_link]));
$this->logger('contact')->notice('Contact form %label has been added.', ['%label' => $contact_form->label(), 'link' => $edit_link]);
}
// Update the default form.
@@ -9,6 +9,14 @@ use Drupal\Core\Config\Entity\ConfigEntityInterface;
*/
interface ContactFormInterface extends ConfigEntityInterface {
/**
* Returns the message to be displayed to user.
*
* @return string
* A user message.
*/
public function getMessage();
/**
* Returns list of recipient email addresses.
*
@@ -17,6 +25,24 @@ interface ContactFormInterface extends ConfigEntityInterface {
*/
public function getRecipients();
/**
* Returns the path for redirect.
*
* @return string
* The redirect path.
*/
public function getRedirectPath();
/**
* Returns the url object for redirect path.
*
* Empty redirect property results a url object of front page.
*
* @return \Drupal\core\Url
* The redirect url object.
*/
public function getRedirectUrl();
/**
* Returns an auto-reply message to send to the message author.
*
@@ -33,6 +59,16 @@ interface ContactFormInterface extends ConfigEntityInterface {
*/
public function getWeight();
/**
* Sets the message to be displayed to the user.
*
* @param string $message
* The message to display after form is submitted.
*
* @return $this
*/
public function setMessage($message);
/**
* Sets list of recipient email addresses.
*
@@ -43,6 +79,16 @@ interface ContactFormInterface extends ConfigEntityInterface {
*/
public function setRecipients($recipients);
/**
* Sets the redirect path.
*
* @param string $redirect
* The desired path.
*
* @return $this
*/
public function setRedirectPath($redirect);
/**
* Sets an auto-reply message to send to the message author.
*
@@ -64,9 +64,9 @@ class ContactController extends ControllerBase {
// If there are no forms, do not display the form.
if (empty($contact_form)) {
if ($this->currentUser()->hasPermission('administer contact forms')) {
drupal_set_message($this->t('The contact form has not been configured. <a href=":add">Add one or more forms</a> .', array(
':add' => $this->url('contact.form_add'))), 'error');
return array();
drupal_set_message($this->t('The contact form has not been configured. <a href=":add">Add one or more forms</a> .', [
':add' => $this->url('contact.form_add')]), 'error');
return [];
}
else {
throw new NotFoundHttpException();
@@ -76,9 +76,9 @@ class ContactController extends ControllerBase {
$message = $this->entityManager()
->getStorage('contact_message')
->create(array(
->create([
'contact_form' => $contact_form->id(),
));
]);
$form = $this->entityFormBuilder()->getForm($message);
$form['#title'] = $contact_form->label();
@@ -106,13 +106,13 @@ class ContactController extends ControllerBase {
throw new NotFoundHttpException();
}
$message = $this->entityManager()->getStorage('contact_message')->create(array(
$message = $this->entityManager()->getStorage('contact_message')->create([
'contact_form' => 'personal',
'recipient' => $user->id(),
));
]);
$form = $this->entityFormBuilder()->getForm($message);
$form['#title'] = $this->t('Contact @username', array('@username' => $user->getDisplayName()));
$form['#title'] = $this->t('Contact @username', ['@username' => $user->getDisplayName()]);
$form['#cache']['contexts'][] = 'user.permissions';
return $form;
}
@@ -4,6 +4,7 @@ namespace Drupal\contact\Entity;
use Drupal\Core\Config\Entity\ConfigEntityBundleBase;
use Drupal\contact\ContactFormInterface;
use Drupal\Core\Url;
/**
* Defines the contact form entity.
@@ -39,6 +40,8 @@ use Drupal\contact\ContactFormInterface;
* "recipients",
* "reply",
* "weight",
* "message",
* "redirect",
* }
* )
*/
@@ -58,12 +61,26 @@ class ContactForm extends ConfigEntityBundleBase implements ContactFormInterface
*/
protected $label;
/**
* The message displayed to user on form submission.
*
* @var string
*/
protected $message;
/**
* List of recipient email addresses.
*
* @var array
*/
protected $recipients = array();
protected $recipients = [];
/**
* The path to redirect to on form submission.
*
* @var string
*/
protected $redirect;
/**
* An auto-reply message.
@@ -79,6 +96,21 @@ class ContactForm extends ConfigEntityBundleBase implements ContactFormInterface
*/
protected $weight = 0;
/**
* {@inheritdoc}
*/
public function getMessage() {
return $this->message;
}
/**
* {@inheritdoc}
*/
public function setMessage($message) {
$this->message = $message;
return $this;
}
/**
* {@inheritdoc}
*/
@@ -94,6 +126,34 @@ class ContactForm extends ConfigEntityBundleBase implements ContactFormInterface
return $this;
}
/**
* {@inheritdoc}
*/
public function getRedirectPath() {
return $this->redirect;
}
/**
* {@inheritdoc}
*/
public function getRedirectUrl() {
if ($this->redirect) {
$url = Url::fromUserInput($this->redirect);
}
else {
$url = Url::fromRoute('<front>');
}
return $url;
}
/**
* {@inheritdoc}
*/
public function setRedirectPath($redirect) {
$this->redirect = $redirect;
return $this;
}
/**
* {@inheritdoc}
*/
+15 -24
View File
@@ -130,24 +130,15 @@ class Message extends ContentEntityBase implements MessageInterface {
* {@inheritdoc}
*/
public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
$fields['contact_form'] = BaseFieldDefinition::create('entity_reference')
->setLabel(t('Form ID'))
->setDescription(t('The ID of the associated form.'))
->setSetting('target_type', 'contact_form')
->setRequired(TRUE);
/** @var \Drupal\Core\Field\BaseFieldDefinition[] $fields */
$fields = parent::baseFieldDefinitions($entity_type);
$fields['uuid'] = BaseFieldDefinition::create('uuid')
->setLabel(t('UUID'))
->setDescription(t('The message UUID.'))
->setReadOnly(TRUE);
$fields['contact_form']->setLabel(t('Form ID'))
->setDescription(t('The ID of the associated form.'));
$fields['langcode'] = BaseFieldDefinition::create('language')
->setLabel(t('Language'))
->setDescription(t('The message language code.'))
->setDisplayOptions('form', array(
'type' => 'language_select',
'weight' => 2,
));
$fields['uuid']->setDescription(t('The message UUID.'));
$fields['langcode']->setDescription(t('The message language code.'));
$fields['name'] = BaseFieldDefinition::create('string')
->setLabel(t("The sender's name"))
@@ -162,29 +153,29 @@ class Message extends ContentEntityBase implements MessageInterface {
->setLabel(t('Subject'))
->setRequired(TRUE)
->setSetting('max_length', 100)
->setDisplayOptions('form', array(
->setDisplayOptions('form', [
'type' => 'string_textfield',
'weight' => -10,
))
])
->setDisplayConfigurable('form', TRUE);
// The text of the contact message.
$fields['message'] = BaseFieldDefinition::create('string_long')
->setLabel(t('Message'))
->setRequired(TRUE)
->setDisplayOptions('form', array(
->setDisplayOptions('form', [
'type' => 'string_textarea',
'weight' => 0,
'settings' => array(
'settings' => [
'rows' => 12,
),
))
],
])
->setDisplayConfigurable('form', TRUE)
->setDisplayOptions('view', array(
->setDisplayOptions('view', [
'type' => 'string',
'weight' => 0,
'label' => 'above',
))
])
->setDisplayConfigurable('view', TRUE);
$fields['copy'] = BaseFieldDefinition::create('boolean')
+14 -7
View File
@@ -73,7 +73,7 @@ class MailHandler implements MailHandlerInterface {
public function sendMailMessages(MessageInterface $message, AccountInterface $sender) {
// Clone the sender, as we make changes to mail and name properties.
$sender_cloned = clone $this->userStorage->load($sender->id());
$params = array();
$params = [];
$current_langcode = $this->languageManager->getCurrentLanguage()->getId();
$recipient_langcode = $this->languageManager->getDefaultLanguage()->getId();
$contact_form = $message->getContactForm();
@@ -86,7 +86,7 @@ class MailHandler implements MailHandlerInterface {
// For the email message, clarify that the sender name is not verified; it
// could potentially clash with a username on this site.
$sender_cloned->name = $this->t('@name (not verified)', array('@name' => $message->getSenderName()));
$sender_cloned->name = $this->t('@name (not verified)', ['@name' => $message->getSenderName()]);
}
// Build email parameters.
@@ -122,22 +122,29 @@ class MailHandler implements MailHandlerInterface {
if (!$message->isPersonal() && $contact_form->getReply()) {
// User contact forms do not support an auto-reply message, so this
// message always originates from the site.
$this->mailManager->mail('contact', 'page_autoreply', $sender_cloned->getEmail(), $current_langcode, $params);
if (!$sender_cloned->getEmail()) {
$this->logger->error('Error sending auto-reply, missing sender e-mail address in %contact_form', [
'%contact_form' => $contact_form->label(),
]);
}
else {
$this->mailManager->mail('contact', 'page_autoreply', $sender_cloned->getEmail(), $current_langcode, $params);
}
}
if (!$message->isPersonal()) {
$this->logger->notice('%sender-name (@sender-from) sent an email regarding %contact_form.', array(
$this->logger->notice('%sender-name (@sender-from) sent an email regarding %contact_form.', [
'%sender-name' => $sender_cloned->getUsername(),
'@sender-from' => $sender_cloned->getEmail(),
'%contact_form' => $contact_form->label(),
));
]);
}
else {
$this->logger->notice('%sender-name (@sender-from) sent %recipient-name an email.', array(
$this->logger->notice('%sender-name (@sender-from) sent %recipient-name an email.', [
'%sender-name' => $sender_cloned->getUsername(),
'@sender-from' => $sender_cloned->getEmail(),
'%recipient-name' => $message->getPersonalRecipient()->getUsername(),
));
]);
}
}
+41 -30
View File
@@ -2,9 +2,11 @@
namespace Drupal\contact;
use Drupal\Component\Datetime\TimeInterface;
use Drupal\Core\Datetime\DateFormatterInterface;
use Drupal\Core\Entity\ContentEntityForm;
use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
use Drupal\Core\Flood\FloodInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Language\LanguageManagerInterface;
@@ -63,9 +65,13 @@ class MessageForm extends ContentEntityForm {
* The contact mail handler service.
* @param \Drupal\Core\Datetime\DateFormatterInterface $date_formatter
* The date service.
* @param \Drupal\Core\Entity\EntityTypeBundleInfoInterface $entity_type_bundle_info
* The entity type bundle service.
* @param \Drupal\Component\Datetime\TimeInterface $time
* The time service.
*/
public function __construct(EntityManagerInterface $entity_manager, FloodInterface $flood, LanguageManagerInterface $language_manager, MailHandlerInterface $mail_handler, DateFormatterInterface $date_formatter) {
parent::__construct($entity_manager);
public function __construct(EntityManagerInterface $entity_manager, FloodInterface $flood, LanguageManagerInterface $language_manager, MailHandlerInterface $mail_handler, DateFormatterInterface $date_formatter, EntityTypeBundleInfoInterface $entity_type_bundle_info = NULL, TimeInterface $time = NULL) {
parent::__construct($entity_manager, $entity_type_bundle_info, $time);
$this->flood = $flood;
$this->languageManager = $language_manager;
$this->mailHandler = $mail_handler;
@@ -81,7 +87,9 @@ class MessageForm extends ContentEntityForm {
$container->get('flood'),
$container->get('language_manager'),
$container->get('contact.mail_handler'),
$container->get('date.formatter')
$container->get('date.formatter'),
$container->get('entity_type.bundle.info'),
$container->get('datetime.time')
);
}
@@ -95,24 +103,24 @@ class MessageForm extends ContentEntityForm {
$form['#attributes']['class'][] = 'contact-form';
if (!empty($message->preview)) {
$form['preview'] = array(
'#theme_wrappers' => array('container__preview'),
'#attributes' => array('class' => array('preview')),
);
$form['preview'] = [
'#theme_wrappers' => ['container__preview'],
'#attributes' => ['class' => ['preview']],
];
$form['preview']['message'] = $this->entityManager->getViewBuilder('contact_message')->view($message, 'full');
}
$form['name'] = array(
$form['name'] = [
'#type' => 'textfield',
'#title' => $this->t('Your name'),
'#maxlength' => 255,
'#required' => TRUE,
);
$form['mail'] = array(
];
$form['mail'] = [
'#type' => 'email',
'#title' => $this->t('Your email address'),
'#required' => TRUE,
);
];
if ($user->isAnonymous()) {
$form['#attached']['library'][] = 'core/drupal.form';
$form['#attributes']['data-user-info-from-browser'] = TRUE;
@@ -121,9 +129,9 @@ class MessageForm extends ContentEntityForm {
// prevent the impersonation of other users.
else {
$form['name']['#type'] = 'item';
$form['name']['#value'] = $user->getUsername();
$form['name']['#value'] = $user->getDisplayName();
$form['name']['#required'] = FALSE;
$form['name']['#plain_text'] = $user->getUsername();
$form['name']['#plain_text'] = $user->getDisplayName();
$form['mail']['#type'] = 'item';
$form['mail']['#value'] = $user->getEmail();
@@ -133,24 +141,24 @@ class MessageForm extends ContentEntityForm {
// The user contact form has a preset recipient.
if ($message->isPersonal()) {
$form['recipient'] = array(
$form['recipient'] = [
'#type' => 'item',
'#title' => $this->t('To'),
'#value' => $message->getPersonalRecipient()->id(),
'name' => array(
'name' => [
'#theme' => 'username',
'#account' => $message->getPersonalRecipient(),
),
);
],
];
}
$form['copy'] = array(
$form['copy'] = [
'#type' => 'checkbox',
'#title' => $this->t('Send yourself a copy'),
// Do not allow anonymous users to send themselves a copy, because it can
// be abused to spam people.
'#access' => $user->isAuthenticated(),
);
];
return $form;
}
@@ -160,11 +168,11 @@ class MessageForm extends ContentEntityForm {
public function actions(array $form, FormStateInterface $form_state) {
$elements = parent::actions($form, $form_state);
$elements['submit']['#value'] = $this->t('Send message');
$elements['preview'] = array(
$elements['preview'] = [
'#type' => 'submit',
'#value' => $this->t('Preview'),
'#submit' => array('::submitForm', '::preview'),
);
'#submit' => ['::submitForm', '::preview'],
];
return $elements;
}
@@ -189,10 +197,10 @@ class MessageForm extends ContentEntityForm {
$interval = $this->config('contact.settings')->get('flood.interval');
if (!$this->flood->isAllowed('contact', $limit, $interval)) {
$form_state->setErrorByName('', $this->t('You cannot send more than %limit messages in @interval. Try again later.', array(
$form_state->setErrorByName('', $this->t('You cannot send more than %limit messages in @interval. Try again later.', [
'%limit' => $limit,
'@interval' => $this->dateFormatter->formatInterval($interval),
)));
]));
}
}
@@ -205,10 +213,17 @@ class MessageForm extends ContentEntityForm {
public function save(array $form, FormStateInterface $form_state) {
$message = $this->entity;
$user = $this->currentUser();
// Save the message. In core this is a no-op but should contrib wish to
// implement message storage, this will make the task of swapping in a real
// storage controller straight-forward.
$message->save();
$this->mailHandler->sendMailMessages($message, $user);
$contact_form = $message->getContactForm();
$this->flood->register('contact', $this->config('contact.settings')->get('flood.interval'));
drupal_set_message($this->t('Your message has been sent.'));
if ($submission_message = $contact_form->getMessage()) {
drupal_set_message($submission_message);
}
// To avoid false error messages caused by flood control, redirect away from
// the contact form; either to the contacted user account or the front page.
@@ -216,12 +231,8 @@ class MessageForm extends ContentEntityForm {
$form_state->setRedirectUrl($message->getPersonalRecipient()->urlInfo());
}
else {
$form_state->setRedirect('<front>');
$form_state->setRedirectUrl($contact_form->getRedirectUrl());
}
// Save the message. In core this is a no-op but should contrib wish to
// implement message storage, this will make the task of swapping in a real
// storage controller straight-forward.
$message->save();
}
}
@@ -23,25 +23,6 @@ class MessageViewBuilder extends EntityViewBuilder {
return $build;
}
/**
* {@inheritdoc}
*/
public function buildComponents(array &$build, array $entities, array $displays, $view_mode) {
parent::buildComponents($build, $entities, $displays, $view_mode);
foreach ($entities as $id => $entity) {
// Add the message extra field, if enabled.
$display = $displays[$entity->bundle()];
if ($entity->getMessage() && $display->getComponent('message')) {
$build[$id]['message'] = array(
'#type' => 'item',
'#title' => t('Message'),
'#plain_text' => $entity->getMessage(),
);
}
}
}
/**
* {@inheritdoc}
*/
@@ -54,7 +35,7 @@ class MessageViewBuilder extends EntityViewBuilder {
// convert DIVs correctly.
foreach (Element::children($build) as $key) {
if (isset($build[$key]['#label_display']) && $build[$key]['#label_display'] == 'above') {
$build[$key] += array('#prefix' => '');
$build[$key] += ['#prefix' => ''];
$build[$key]['#prefix'] = $build[$key]['#title'] . ":\n";
$build[$key]['#label_display'] = 'hidden';
}
@@ -20,14 +20,14 @@ class ContactCategory extends DrupalSqlBase {
*/
public function query() {
$query = $this->select('contact', 'c')
->fields('c', array(
->fields('c', [
'cid',
'category',
'recipients',
'reply',
'weight',
'selected',
)
]
);
$query->orderBy('c.cid');
return $query;
@@ -45,14 +45,14 @@ class ContactCategory extends DrupalSqlBase {
* {@inheritdoc}
*/
public function fields() {
return array(
return [
'cid' => $this->t('Primary Key: Unique category ID.'),
'category' => $this->t('Category name.'),
'recipients' => $this->t('Comma-separated list of recipient email addresses.'),
'reply' => $this->t('Text of the auto-reply message.'),
'weight' => $this->t("The category's weight."),
'selected' => $this->t('Flag to indicate whether or not category is selected by default. (1 = Yes, 0 = No)'),
);
];
}
/**
@@ -42,8 +42,8 @@ class ContactLink extends LinkBase {
$this->options['alter']['make_link'] = TRUE;
$this->options['alter']['url'] = $this->getUrlInfo($row);
$title = $this->t('Contact %user', array('%user' => $entity->label()));
$this->options['alter']['attributes'] = array('title' => $title);
$title = $this->t('Contact %user', ['%user' => $entity->label()]);
$this->options['alter']['attributes'] = ['title' => $title];
if (!empty($this->options['text'])) {
return $this->options['text'];
@@ -0,0 +1,53 @@
<?php
namespace Drupal\contact\Tests\Update;
use Drupal\system\Tests\Update\UpdatePathTestBase;
/**
* Tests contact update path.
*
* @group contact
*/
class ContactUpdateTest extends UpdatePathTestBase {
/**
* {@inheritdoc}
*/
protected function setDatabaseDumpFiles() {
$this->databaseDumpFiles = [
__DIR__ . '/../../../../system/tests/fixtures/update/drupal-8.bare.standard.php.gz',
];
}
/**
* Tests contact_form updates.
*
* @see contact_post_update_add_message_redirect_field_to_contact_form()
*/
public function testPostUpdateContactFormFields() {
// Check that contact_form does not have fields redirect and message.
$config_factory = \Drupal::configFactory();
// Check that contact_form entities are more than zero.
$contact_forms = $config_factory->listAll('contact.form.');
$this->assertTrue(count($contact_forms), 'There are contact forms to update.');
foreach ($contact_forms as $contact_config_name) {
$contact_form_data = $config_factory->get($contact_config_name)->get();
$this->assertFalse(isset($contact_form_data['message']), 'Prior to running the update the "message" key does not exist.');
$this->assertFalse(isset($contact_form_data['redirect']), 'Prior to running the update the "redirect" key does not exist.');
}
// Run updates.
$this->runUpdates();
// Check that the contact_form entities have been updated.
foreach ($contact_forms as $contact_config_name) {
$contact_form_data = $config_factory->get($contact_config_name)->get();
$this->assertTrue(isset($contact_form_data['message']), 'After running the update the "message" key exists.');
$this->assertEqual('Your message has been sent.', $contact_form_data['message']);
$this->assertTrue(isset($contact_form_data['redirect']), 'After running the update the "redirect" key exists.');
$this->assertEqual('', $contact_form_data['redirect']);
}
}
}
@@ -11,23 +11,23 @@
// Update the default category to that it is not selected.
db_update('contact')
->fields(array('selected' => '0'))
->fields(['selected' => '0'])
->condition('cid', '1')
->execute();
// Add a custom contact category.
db_insert('contact')->fields(array(
db_insert('contact')->fields([
'category',
'recipients',
'reply',
'weight',
'selected'
))
->values(array(
])
->values([
'category' => 'Upgrade test',
'recipients' => 'test1@example.com,test2@example.com',
'reply' => 'Test reply',
'weight' => 1,
'selected' => 1,
))
->execute();
])
->execute();
@@ -8,8 +8,8 @@ dependencies:
- contact
- user
# Information added by Drupal.org packaging script on 2016-08-03
version: '8.1.8'
# Information added by Drupal.org packaging script on 2017-08-16
version: '8.3.7'
core: '8.x'
project: 'drupal'
datestamp: 1470233790
datestamp: 1502903957
@@ -15,7 +15,7 @@ use Drupal\Core\Form\FormStateInterface;
*/
function contact_storage_test_entity_base_field_info(EntityTypeInterface $entity_type) {
if ($entity_type->id() == 'contact_message') {
$fields = array();
$fields = [];
$fields['id'] = BaseFieldDefinition::create('integer')
->setLabel(t('Message ID'))
@@ -48,12 +48,12 @@ function contact_storage_test_entity_type_alter(array &$entity_types) {
function contact_storage_test_form_contact_form_form_alter(&$form, FormStateInterface $form_state) {
/** @var \Drupal\contact\ContactFormInterface $contact_form */
$contact_form = $form_state->getFormObject()->getEntity();
$form['send_a_pony'] = array(
$form['send_a_pony'] = [
'#type' => 'checkbox',
'#title' => t('Send submitters a voucher for a free pony.'),
'#description' => t('Enable to send an additional email with a free pony voucher to anyone who submits the form.'),
'#default_value' => $contact_form->getThirdPartySetting('contact_storage_test', 'send_a_pony', FALSE),
);
];
$form['#entity_builders'][] = 'contact_storage_test_contact_form_form_builder';
}
/**
@@ -5,3 +5,5 @@ reply: ''
weight: 0
status: true
langcode: en
message: 'Your message has been sent.'
redirect: ''
@@ -7,8 +7,8 @@ package: Testing
dependencies:
- contact
# Information added by Drupal.org packaging script on 2016-08-03
version: '8.1.8'
# Information added by Drupal.org packaging script on 2017-08-16
version: '8.3.7'
core: '8.x'
project: 'drupal'
datestamp: 1470233790
datestamp: 1502903957
@@ -9,8 +9,8 @@ dependencies:
- views
- user
# Information added by Drupal.org packaging script on 2016-08-03
version: '8.1.8'
# Information added by Drupal.org packaging script on 2017-08-16
version: '8.3.7'
core: '8.x'
project: 'drupal'
datestamp: 1470233790
datestamp: 1502903957
@@ -111,7 +111,7 @@ display:
entity_type: user
filters:
status:
value: true
value: '1'
table: users_field_data
field: status
id: status
@@ -0,0 +1,35 @@
<?php
namespace Drupal\Tests\contact\Functional;
use Drupal\Tests\BrowserTestBase;
/**
* Tests contact form textfields are present if authenticated.
*
* @group contact
*/
class ContactAuthenticatedUserTest extends BrowserTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['contact'];
/**
* Tests that name and email fields are not present for authenticated users.
*/
public function testContactSiteWideTextfieldsLoggedInTestCase() {
$this->drupalLogin($this->drupalCreateUser(['access site-wide contact form']));
$this->drupalGet('contact');
// Ensure that there is no textfield for name.
$this->assertFalse($this->xpath('//input[@name=:name]', [':name' => 'name']));
// Ensure that there is no textfield for email.
$this->assertFalse($this->xpath('//input[@name=:name]', [':name' => 'mail']));
}
}
@@ -0,0 +1,64 @@
<?php
namespace Drupal\Tests\contact\Functional;
use Drupal\Tests\BrowserTestBase;
/**
* Tests contact messages with language module.
*
* This is to ensure that a contact form by default does not show the language
* select, but it does so when it's enabled from the content language settings
* page.
*
* @group contact
*/
class ContactLanguageTest extends BrowserTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = [
'contact',
'language',
'contact_test',
];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
// Create and log in administrative user.
$admin_user = $this->drupalCreateUser([
'access site-wide contact form',
'administer languages',
]);
$this->drupalLogin($admin_user);
}
/**
* Tests configuration options with language enabled.
*/
public function testContactLanguage() {
// Ensure that contact form by default does not show the language select.
$this->drupalGet('contact');
$this->assertResponse(200, 'The page exists');
$this->assertNoField('edit-langcode-0-value');
// Enable language select from content language settings page.
$settings_path = 'admin/config/regional/content-language';
$edit['entity_types[contact_message]'] = TRUE;
$edit['settings[contact_message][feedback][settings][language][language_alterable]'] = TRUE;
$this->drupalPostForm($settings_path, $edit, t('Save configuration'));
// Ensure that contact form now shows the language select.
$this->drupalGet('contact');
$this->assertResponse(200, 'The page exists');
$this->assertField('edit-langcode-0-value');
}
}
@@ -0,0 +1,323 @@
<?php
namespace Drupal\Tests\contact\Functional;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Component\Render\PlainTextOutput;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Test\AssertMailTrait;
use Drupal\system\Tests\Cache\AssertPageCacheContextsAndTagsTrait;
use Drupal\Tests\BrowserTestBase;
use Drupal\user\RoleInterface;
/**
* Tests personal contact form functionality.
*
* @group contact
*/
class ContactPersonalTest extends BrowserTestBase {
use AssertMailTrait;
use AssertPageCacheContextsAndTagsTrait;
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['contact', 'dblog'];
/**
* A user with some administrative permissions.
*
* @var \Drupal\user\UserInterface
*/
private $adminUser;
/**
* A user with permission to view profiles and access user contact forms.
*
* @var \Drupal\user\UserInterface
*/
private $webUser;
/**
* A user without any permissions.
*
* @var \Drupal\user\UserInterface
*/
private $contactUser;
protected function setUp() {
parent::setUp();
// Create an admin user.
$this->adminUser = $this->drupalCreateUser(['administer contact forms', 'administer users', 'administer account settings', 'access site reports']);
// Create some normal users with their contact forms enabled by default.
$this->config('contact.settings')->set('user_default_enabled', TRUE)->save();
$this->webUser = $this->drupalCreateUser(['access user profiles', 'access user contact forms']);
$this->contactUser = $this->drupalCreateUser();
}
/**
* Tests that mails for contact messages are correctly sent.
*/
public function testSendPersonalContactMessage() {
// Ensure that the web user's email needs escaping.
$mail = $this->webUser->getUsername() . '&escaped@example.com';
$this->webUser->setEmail($mail)->save();
$this->drupalLogin($this->webUser);
$this->drupalGet('user/' . $this->contactUser->id() . '/contact');
$this->assertEscaped($mail);
$message = $this->submitPersonalContact($this->contactUser);
$mails = $this->getMails();
$this->assertEqual(1, count($mails));
$mail = $mails[0];
$this->assertEqual($mail['to'], $this->contactUser->getEmail());
$this->assertEqual($mail['from'], $this->config('system.site')->get('mail'));
$this->assertEqual($mail['reply-to'], $this->webUser->getEmail());
$this->assertEqual($mail['key'], 'user_mail');
$variables = [
'@site-name' => $this->config('system.site')->get('name'),
'@subject' => $message['subject[0][value]'],
'@recipient-name' => $this->contactUser->getDisplayName(),
];
$subject = PlainTextOutput::renderFromHtml(t('[@site-name] @subject', $variables));
$this->assertEqual($mail['subject'], $subject, 'Subject is in sent message.');
$this->assertTrue(strpos($mail['body'], 'Hello ' . $variables['@recipient-name']) !== FALSE, 'Recipient name is in sent message.');
$this->assertTrue(strpos($mail['body'], $this->webUser->getDisplayName()) !== FALSE, 'Sender name is in sent message.');
$this->assertTrue(strpos($mail['body'], $message['message[0][value]']) !== FALSE, 'Message body is in sent message.');
// Check there was no problems raised during sending.
$this->drupalLogout();
$this->drupalLogin($this->adminUser);
// Verify that the correct watchdog message has been logged.
$this->drupalGet('/admin/reports/dblog');
$placeholders = [
'@sender_name' => $this->webUser->username,
'@sender_email' => $this->webUser->getEmail(),
'@recipient_name' => $this->contactUser->getUsername()
];
$this->assertRaw(SafeMarkup::format('@sender_name (@sender_email) sent @recipient_name an email.', $placeholders));
// Ensure an unescaped version of the email does not exist anywhere.
$this->assertNoRaw($this->webUser->getEmail());
}
/**
* Tests access to the personal contact form.
*/
public function testPersonalContactAccess() {
// Test allowed access to admin user's contact form.
$this->drupalLogin($this->webUser);
$this->drupalGet('user/' . $this->adminUser->id() . '/contact');
$this->assertResponse(200);
// Check the page title is properly displayed.
$this->assertRaw(t('Contact @username', ['@username' => $this->adminUser->getDisplayName()]));
// Test denied access to admin user's own contact form.
$this->drupalLogout();
$this->drupalLogin($this->adminUser);
$this->drupalGet('user/' . $this->adminUser->id() . '/contact');
$this->assertResponse(403);
// Test allowed access to user with contact form enabled.
$this->drupalLogin($this->webUser);
$this->drupalGet('user/' . $this->contactUser->id() . '/contact');
$this->assertResponse(200);
// Test that there is no access to personal contact forms for users
// without an email address configured.
$original_email = $this->contactUser->getEmail();
$this->contactUser->setEmail(FALSE)->save();
$this->drupalGet('user/' . $this->contactUser->id() . '/contact');
$this->assertResponse(404, 'Not found (404) returned when visiting a personal contact form for a user with no email address');
// Test that the 'contact tab' does not appear on the user profiles
// for users without an email address configured.
$this->drupalGet('user/' . $this->contactUser->id());
$contact_link = '/user/' . $this->contactUser->id() . '/contact';
$this->assertResponse(200);
$this->assertNoLinkByHref ($contact_link, 'The "contact" tab is hidden on profiles for users with no email address');
// Restore original email address.
$this->contactUser->setEmail($original_email)->save();
// Test denied access to the user's own contact form.
$this->drupalGet('user/' . $this->webUser->id() . '/contact');
$this->assertResponse(403);
// Test always denied access to the anonymous user contact form.
$this->drupalGet('user/0/contact');
$this->assertResponse(403);
// Test that anonymous users can access the contact form.
$this->drupalLogout();
user_role_grant_permissions(RoleInterface::ANONYMOUS_ID, ['access user contact forms']);
$this->drupalGet('user/' . $this->contactUser->id() . '/contact');
$this->assertResponse(200);
// Test that anonymous users can access admin user's contact form.
$this->drupalGet('user/' . $this->adminUser->id() . '/contact');
$this->assertResponse(200);
$this->assertCacheContext('user');
// Revoke the personal contact permission for the anonymous user.
user_role_revoke_permissions(RoleInterface::ANONYMOUS_ID, ['access user contact forms']);
$this->drupalGet('user/' . $this->contactUser->id() . '/contact');
$this->assertResponse(403);
$this->assertCacheContext('user');
$this->drupalGet('user/' . $this->adminUser->id() . '/contact');
$this->assertResponse(403);
// Disable the personal contact form.
$this->drupalLogin($this->adminUser);
$edit = ['contact_default_status' => FALSE];
$this->drupalPostForm('admin/config/people/accounts', $edit, t('Save configuration'));
$this->assertText(t('The configuration options have been saved.'), 'Setting successfully saved.');
$this->drupalLogout();
// Re-create our contacted user with personal contact forms disabled by
// default.
$this->contactUser = $this->drupalCreateUser();
// Test denied access to a user with contact form disabled.
$this->drupalLogin($this->webUser);
$this->drupalGet('user/' . $this->contactUser->id() . '/contact');
$this->assertResponse(403);
// Test allowed access for admin user to a user with contact form disabled.
$this->drupalLogin($this->adminUser);
$this->drupalGet('user/' . $this->contactUser->id() . '/contact');
$this->assertResponse(200);
// Re-create our contacted user as a blocked user.
$this->contactUser = $this->drupalCreateUser();
$this->contactUser->block();
$this->contactUser->save();
// Test that blocked users can still be contacted by admin.
$this->drupalGet('user/' . $this->contactUser->id() . '/contact');
$this->assertResponse(200);
// Test that blocked users cannot be contacted by non-admins.
$this->drupalLogin($this->webUser);
$this->drupalGet('user/' . $this->contactUser->id() . '/contact');
$this->assertResponse(403);
// Test enabling and disabling the contact page through the user profile
// form.
$this->drupalGet('user/' . $this->webUser->id() . '/edit');
$this->assertNoFieldChecked('edit-contact--2');
$this->assertFalse(\Drupal::service('user.data')->get('contact', $this->webUser->id(), 'enabled'), 'Personal contact form disabled');
$this->drupalPostForm(NULL, ['contact' => TRUE], t('Save'));
$this->assertFieldChecked('edit-contact--2');
$this->assertTrue(\Drupal::service('user.data')->get('contact', $this->webUser->id(), 'enabled'), 'Personal contact form enabled');
// Test with disabled global default contact form in combination with a user
// that has the contact form enabled.
$this->config('contact.settings')->set('user_default_enabled', FALSE)->save();
$this->contactUser = $this->drupalCreateUser();
\Drupal::service('user.data')->set('contact', $this->contactUser->id(), 'enabled', 1);
$this->drupalGet('user/' . $this->contactUser->id() . '/contact');
$this->assertResponse(200);
}
/**
* Tests the personal contact form flood protection.
*/
public function testPersonalContactFlood() {
$flood_limit = 3;
$this->config('contact.settings')->set('flood.limit', $flood_limit)->save();
$this->drupalLogin($this->webUser);
// Submit contact form with correct values and check flood interval.
for ($i = 0; $i < $flood_limit; $i++) {
$this->submitPersonalContact($this->contactUser);
$this->assertText(t('Your message has been sent.'), 'Message sent.');
}
// Submit contact form one over limit.
$this->submitPersonalContact($this->contactUser);
$this->assertRaw(t('You cannot send more than %number messages in @interval. Try again later.', ['%number' => $flood_limit, '@interval' => \Drupal::service('date.formatter')->formatInterval($this->config('contact.settings')->get('flood.interval'))]), 'Normal user denied access to flooded contact form.');
// Test that the admin user can still access the contact form even though
// the flood limit was reached.
$this->drupalLogin($this->adminUser);
$this->assertNoText('Try again later.', 'Admin user not denied access to flooded contact form.');
}
/**
* Tests the personal contact form based access when an admin adds users.
*/
public function testAdminContact() {
user_role_grant_permissions(RoleInterface::ANONYMOUS_ID, ['access user contact forms']);
$this->checkContactAccess(200);
$this->checkContactAccess(403, FALSE);
$config = $this->config('contact.settings');
$config->set('user_default_enabled', FALSE);
$config->save();
$this->checkContactAccess(403);
}
/**
* Creates a user and then checks contact form access.
*
* @param int $response
* The expected response code.
* @param bool $contact_value
* (optional) The value the contact field should be set too.
*/
protected function checkContactAccess($response, $contact_value = NULL) {
$this->drupalLogin($this->adminUser);
$this->drupalGet('admin/people/create');
if ($this->config('contact.settings')->get('user_default_enabled', TRUE)) {
$this->assertFieldChecked('edit-contact--2');
}
else {
$this->assertNoFieldChecked('edit-contact--2');
}
$name = $this->randomMachineName();
$edit = [
'name' => $name,
'mail' => $this->randomMachineName() . '@example.com',
'pass[pass1]' => $pass = $this->randomString(),
'pass[pass2]' => $pass,
'notify' => FALSE,
];
if (isset($contact_value)) {
$edit['contact'] = $contact_value;
}
$this->drupalPostForm('admin/people/create', $edit, t('Create new account'));
$user = user_load_by_name($name);
$this->drupalLogout();
$this->drupalGet('user/' . $user->id() . '/contact');
$this->assertResponse($response);
}
/**
* Fills out a user's personal contact form and submits it.
*
* @param \Drupal\Core\Session\AccountInterface $account
* A user object of the user being contacted.
* @param array $message
* (optional) An array with the form fields being used. Defaults to an empty
* array.
*
* @return array
* An array with the form fields being used.
*/
protected function submitPersonalContact(AccountInterface $account, array $message = []) {
$message += [
'subject[0][value]' => $this->randomMachineName(16),
'message[0][value]' => $this->randomMachineName(64),
];
$this->drupalPostForm('user/' . $account->id() . '/contact', $message, t('Send message'));
return $message;
}
}
@@ -0,0 +1,553 @@
<?php
namespace Drupal\Tests\contact\Functional;
use Drupal\Component\Utility\Unicode;
use Drupal\contact\Entity\ContactForm;
use Drupal\Core\Mail\MailFormatHelper;
use Drupal\Core\Test\AssertMailTrait;
use Drupal\field_ui\Tests\FieldUiTestTrait;
use Drupal\Tests\BrowserTestBase;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\user\RoleInterface;
/**
* Tests site-wide contact form functionality.
*
* @see \Drupal\Tests\contact\Functional\ContactStorageTest
*
* @group contact
*/
class ContactSitewideTest extends BrowserTestBase {
use FieldUiTestTrait;
use AssertMailTrait;
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['text', 'contact', 'field_ui', 'contact_test', 'block', 'error_service_test', 'dblog'];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->drupalPlaceBlock('system_breadcrumb_block');
$this->drupalPlaceBlock('local_actions_block');
$this->drupalPlaceBlock('page_title_block');
}
/**
* Tests configuration options and the site-wide contact form.
*/
public function testSiteWideContact() {
// Create and log in administrative user.
$admin_user = $this->drupalCreateUser([
'access site-wide contact form',
'administer contact forms',
'administer users',
'administer account settings',
'administer contact_message display',
'administer contact_message fields',
'administer contact_message form display',
]);
$this->drupalLogin($admin_user);
// Check the presence of expected cache tags.
$this->drupalGet('contact');
$this->assertCacheTag('config:contact.settings');
$flood_limit = 3;
$this->config('contact.settings')
->set('flood.limit', $flood_limit)
->set('flood.interval', 600)
->save();
// Set settings.
$edit = [];
$edit['contact_default_status'] = TRUE;
$this->drupalPostForm('admin/config/people/accounts', $edit, t('Save configuration'));
$this->assertText(t('The configuration options have been saved.'));
$this->drupalGet('admin/structure/contact');
// Default form exists.
$this->assertLinkByHref('admin/structure/contact/manage/feedback/delete');
// User form could not be changed or deleted.
// Cannot use ::assertNoLinkByHref as it does partial url matching and with
// field_ui enabled admin/structure/contact/manage/personal/fields exists.
// @todo: See https://www.drupal.org/node/2031223 for the above.
$edit_link = $this->xpath('//a[@href=:href]', [
':href' => \Drupal::url('entity.contact_form.edit_form', ['contact_form' => 'personal'])
]);
$this->assertTrue(empty($edit_link), format_string('No link containing href %href found.',
['%href' => 'admin/structure/contact/manage/personal']
));
$this->assertNoLinkByHref('admin/structure/contact/manage/personal/delete');
$this->drupalGet('admin/structure/contact/manage/personal');
$this->assertResponse(403);
// Delete old forms to ensure that new forms are used.
$this->deleteContactForms();
$this->drupalGet('admin/structure/contact');
$this->assertText('Personal', 'Personal form was not deleted');
$this->assertNoLinkByHref('admin/structure/contact/manage/feedback');
// Ensure that the contact form won't be shown without forms.
user_role_grant_permissions(RoleInterface::ANONYMOUS_ID, ['access site-wide contact form']);
$this->drupalLogout();
$this->drupalGet('contact');
$this->assertResponse(404);
$this->drupalLogin($admin_user);
$this->drupalGet('contact');
$this->assertResponse(200);
$this->assertText(t('The contact form has not been configured.'));
// Test access personal form via site-wide contact page.
$this->drupalGet('contact/personal');
$this->assertResponse(403);
// Add forms.
// Test invalid recipients.
$invalid_recipients = ['invalid', 'invalid@', 'invalid@site.', '@site.', '@site.com'];
foreach ($invalid_recipients as $invalid_recipient) {
$this->addContactForm($this->randomMachineName(16), $this->randomMachineName(16), $invalid_recipient, '', FALSE);
$this->assertRaw(t('%recipient is an invalid email address.', ['%recipient' => $invalid_recipient]));
}
// Test validation of empty form and recipients fields.
$this->addContactForm('', '', '', '', TRUE);
$this->assertText(t('Label field is required.'));
$this->assertText(t('Machine-readable name field is required.'));
$this->assertText(t('Recipients field is required.'));
// Test validation of max_length machine name.
$recipients = ['simpletest&@example.com', 'simpletest2@example.com', 'simpletest3@example.com'];
$max_length = EntityTypeInterface::BUNDLE_MAX_LENGTH;
$max_length_exceeded = $max_length + 1;
$this->addContactForm($id = Unicode::strtolower($this->randomMachineName($max_length_exceeded)), $label = $this->randomMachineName($max_length_exceeded), implode(',', [$recipients[0]]), '', TRUE);
$this->assertText(format_string('Machine-readable name cannot be longer than @max characters but is currently @exceeded characters long.', ['@max' => $max_length, '@exceeded' => $max_length_exceeded]));
$this->addContactForm($id = Unicode::strtolower($this->randomMachineName($max_length)), $label = $this->randomMachineName($max_length), implode(',', [$recipients[0]]), '', TRUE);
$this->assertText(t('Contact form @label has been added.', ['@label' => $label]));
// Verify that the creation message contains a link to a contact form.
$view_link = $this->xpath('//div[@class="messages"]//a[contains(@href, :href)]', [':href' => 'contact/']);
$this->assert(isset($view_link), 'The message area contains a link to a contact form.');
// Create first valid form.
$this->addContactForm($id = Unicode::strtolower($this->randomMachineName(16)), $label = $this->randomMachineName(16), implode(',', [$recipients[0]]), '', TRUE);
$this->assertText(t('Contact form @label has been added.', ['@label' => $label]));
// Verify that the creation message contains a link to a contact form.
$view_link = $this->xpath('//div[@class="messages"]//a[contains(@href, :href)]', [':href' => 'contact/']);
$this->assert(isset($view_link), 'The message area contains a link to a contact form.');
// Check that the form was created in site default language.
$langcode = $this->config('contact.form.' . $id)->get('langcode');
$default_langcode = \Drupal::languageManager()->getDefaultLanguage()->getId();
$this->assertEqual($langcode, $default_langcode);
// Make sure the newly created form is included in the list of forms.
$this->assertNoUniqueText($label, 'New form included in forms list.');
// Ensure that the recipient email is escaped on the listing.
$this->drupalGet('admin/structure/contact');
$this->assertEscaped($recipients[0]);
// Test update contact form.
$this->updateContactForm($id, $label = $this->randomMachineName(16), $recipients_str = implode(',', [$recipients[0], $recipients[1]]), $reply = $this->randomMachineName(30), FALSE, 'Your message has been sent.', '/user');
$config = $this->config('contact.form.' . $id)->get();
$this->assertEqual($config['label'], $label);
$this->assertEqual($config['recipients'], [$recipients[0], $recipients[1]]);
$this->assertEqual($config['reply'], $reply);
$this->assertNotEqual($id, $this->config('contact.settings')->get('default_form'));
$this->assertText(t('Contact form @label has been updated.', ['@label' => $label]));
// Ensure the label is displayed on the contact page for this form.
$this->drupalGet('contact/' . $id);
$this->assertText($label);
// Reset the form back to be the default form.
$this->config('contact.settings')->set('default_form', $id)->save();
// Ensure that the contact form is shown without a form selection input.
user_role_grant_permissions(RoleInterface::ANONYMOUS_ID, ['access site-wide contact form']);
$this->drupalLogout();
$this->drupalGet('contact');
$this->assertText(t('Your email address'));
$this->assertNoText(t('Form'));
$this->drupalLogin($admin_user);
// Add more forms.
$this->addContactForm(Unicode::strtolower($this->randomMachineName(16)), $label = $this->randomMachineName(16), implode(',', [$recipients[0], $recipients[1]]), '', FALSE);
$this->assertText(t('Contact form @label has been added.', ['@label' => $label]));
$this->addContactForm($name = Unicode::strtolower($this->randomMachineName(16)), $label = $this->randomMachineName(16), implode(',', [$recipients[0], $recipients[1], $recipients[2]]), '', FALSE);
$this->assertText(t('Contact form @label has been added.', ['@label' => $label]));
// Try adding a form that already exists.
$this->addContactForm($name, $label, '', '', FALSE);
$this->assertNoText(t('Contact form @label has been added.', ['@label' => $label]));
$this->assertRaw(t('The machine-readable name is already in use. It must be unique.'));
$this->drupalLogout();
// Check to see that anonymous user cannot see contact page without permission.
user_role_revoke_permissions(RoleInterface::ANONYMOUS_ID, ['access site-wide contact form']);
$this->drupalGet('contact');
$this->assertResponse(403);
// Give anonymous user permission and see that page is viewable.
user_role_grant_permissions(RoleInterface::ANONYMOUS_ID, ['access site-wide contact form']);
$this->drupalGet('contact');
$this->assertResponse(200);
// Submit contact form with invalid values.
$this->submitContact('', $recipients[0], $this->randomMachineName(16), $id, $this->randomMachineName(64));
$this->assertText(t('Your name field is required.'));
$this->submitContact($this->randomMachineName(16), '', $this->randomMachineName(16), $id, $this->randomMachineName(64));
$this->assertText(t('Your email address field is required.'));
$this->submitContact($this->randomMachineName(16), $invalid_recipients[0], $this->randomMachineName(16), $id, $this->randomMachineName(64));
$this->assertRaw(t('The email address %mail is not valid.', ['%mail' => 'invalid']));
$this->submitContact($this->randomMachineName(16), $recipients[0], '', $id, $this->randomMachineName(64));
$this->assertText(t('Subject field is required.'));
$this->submitContact($this->randomMachineName(16), $recipients[0], $this->randomMachineName(16), $id, '');
$this->assertText(t('Message field is required.'));
// Test contact form with no default form selected.
$this->config('contact.settings')
->set('default_form', '')
->save();
$this->drupalGet('contact');
$this->assertResponse(404);
// Try to access contact form with non-existing form IDs.
$this->drupalGet('contact/0');
$this->assertResponse(404);
$this->drupalGet('contact/' . $this->randomMachineName());
$this->assertResponse(404);
// Submit contact form with correct values and check flood interval.
for ($i = 0; $i < $flood_limit; $i++) {
$this->submitContact($this->randomMachineName(16), $recipients[0], $this->randomMachineName(16), $id, $this->randomMachineName(64));
$this->assertText(t('Your message has been sent.'));
}
// Submit contact form one over limit.
$this->submitContact($this->randomMachineName(16), $recipients[0], $this->randomMachineName(16), $id, $this->randomMachineName(64));
$this->assertRaw(t('You cannot send more than %number messages in 10 min. Try again later.', ['%number' => $this->config('contact.settings')->get('flood.limit')]));
// Test listing controller.
$this->drupalLogin($admin_user);
$this->deleteContactForms();
$label = $this->randomMachineName(16);
$recipients = implode(',', [$recipients[0], $recipients[1], $recipients[2]]);
$contact_form = Unicode::strtolower($this->randomMachineName(16));
$this->addContactForm($contact_form, $label, $recipients, '', FALSE);
$this->drupalGet('admin/structure/contact');
$this->clickLink(t('Edit'));
$this->assertResponse(200);
$this->assertFieldByName('label', $label);
// Test field UI and field integration.
$this->drupalGet('admin/structure/contact');
$view_link = $this->xpath('//table/tbody/tr/td/a[contains(@href, :href) and text()=:text]', [
':href' => \Drupal::url('entity.contact_form.canonical', ['contact_form' => $contact_form]),
':text' => $label,
]
);
$this->assertTrue(!empty($view_link), 'Contact listing links to contact form.');
// Find out in which row the form we want to add a field to is.
foreach ($this->xpath('//table/tbody/tr') as $row) {
if ($row->findLink($label)) {
$row->clickLink('Manage fields');
break;
}
}
$this->assertResponse(200);
$this->clickLink(t('Add field'));
$this->assertResponse(200);
// Create a simple textfield.
$field_name = Unicode::strtolower($this->randomMachineName());
$field_label = $this->randomMachineName();
$this->fieldUIAddNewField(NULL, $field_name, $field_label, 'text');
$field_name = 'field_' . $field_name;
// Check preview field can be ordered.
$this->drupalGet('admin/structure/contact/manage/' . $contact_form . '/form-display');
$this->assertText(t('Preview'));
// Check that the field is displayed.
$this->drupalGet('contact/' . $contact_form);
$this->assertText($field_label);
// Submit the contact form and verify the content.
$edit = [
'subject[0][value]' => $this->randomMachineName(),
'message[0][value]' => $this->randomMachineName(),
$field_name . '[0][value]' => $this->randomMachineName(),
];
$this->drupalPostForm(NULL, $edit, t('Send message'));
$mails = $this->getMails();
$mail = array_pop($mails);
$this->assertEqual($mail['subject'], t('[@label] @subject', ['@label' => $label, '@subject' => $edit['subject[0][value]']]));
$this->assertTrue(strpos($mail['body'], $field_label));
$this->assertTrue(strpos($mail['body'], $edit[$field_name . '[0][value]']));
// Test messages and redirect.
/** @var \Drupal\contact\ContactFormInterface $form */
$form = ContactForm::load($contact_form);
$form->setMessage('Thanks for your submission.');
$form->setRedirectPath('/user/' . $admin_user->id());
$form->save();
// Check that the field is displayed.
$this->drupalGet('contact/' . $contact_form);
// Submit the contact form and verify the content.
$edit = [
'subject[0][value]' => $this->randomMachineName(),
'message[0][value]' => $this->randomMachineName(),
$field_name . '[0][value]' => $this->randomMachineName(),
];
$this->drupalPostForm(NULL, $edit, t('Send message'));
$this->assertText('Thanks for your submission.');
$this->assertUrl('user/' . $admin_user->id());
// Test Empty message.
/** @var \Drupal\contact\ContactFormInterface $form */
$form = ContactForm::load($contact_form);
$form->setMessage('');
$form->setRedirectPath('/user/' . $admin_user->id());
$form->save();
$this->drupalGet('admin/structure/contact/manage/' . $contact_form);
// Check that the field is displayed.
$this->drupalGet('contact/' . $contact_form);
// Submit the contact form and verify the content.
$edit = [
'subject[0][value]' => $this->randomMachineName(),
'message[0][value]' => $this->randomMachineName(),
$field_name . '[0][value]' => $this->randomMachineName(),
];
$this->drupalPostForm(NULL, $edit, t('Send message'));
$result = $this->xpath('//div[@role=:role]', [':role' => 'contentinfo']);
$this->assertEqual(count($result), 0, 'Messages not found.');
$this->assertUrl('user/' . $admin_user->id());
// Test preview and visibility of the message field and label. Submit the
// contact form and verify the content.
$edit = [
'subject[0][value]' => $this->randomMachineName(),
'message[0][value]' => $this->randomMachineName(),
$field_name . '[0][value]' => $this->randomMachineName(),
];
$this->drupalPostForm($form->toUrl('canonical'), $edit, t('Preview'));
// Message is now by default displayed twice, once for the form element and
// once for the viewed message.
$page_text = $this->getSession()->getPage()->getText();
$this->assertGreaterThan(1, substr_count($page_text, t('Message')));
$this->assertSession()->responseContains('class="field field--name-message field--type-string-long field--label-above');
$this->assertSession()->pageTextContains($edit['message[0][value]']);
// Hide the message field label.
$display_edit = [
'fields[message][label]' => 'hidden',
];
$this->drupalPostForm('admin/structure/contact/manage/' . $contact_form . '/display', $display_edit, t('Save'));
$this->drupalPostForm($form->toUrl('canonical'), $edit, t('Preview'));
// Message should only be displayed once now.
$page_text = $this->getSession()->getPage()->getText();
$this->assertEquals(1, substr_count($page_text, t('Message')));
$this->assertSession()->responseContains('class="field field--name-message field--type-string-long field--label-hidden field__item">');
$this->assertSession()->pageTextContains($edit['message[0][value]']);
}
/**
* Tests auto-reply on the site-wide contact form.
*/
public function testAutoReply() {
// Create and log in administrative user.
$admin_user = $this->drupalCreateUser([
'access site-wide contact form',
'administer contact forms',
'administer permissions',
'administer users',
'access site reports'
]);
$this->drupalLogin($admin_user);
// Set up three forms, 2 with an auto-reply and one without.
$foo_autoreply = $this->randomMachineName(40);
$bar_autoreply = $this->randomMachineName(40);
$this->addContactForm('foo', 'foo', 'foo@example.com', $foo_autoreply, FALSE);
$this->addContactForm('bar', 'bar', 'bar@example.com', $bar_autoreply, FALSE);
$this->addContactForm('no_autoreply', 'no_autoreply', 'bar@example.com', '', FALSE);
// Log the current user out in order to test the name and email fields.
$this->drupalLogout();
user_role_grant_permissions(RoleInterface::ANONYMOUS_ID, ['access site-wide contact form']);
// Test the auto-reply for form 'foo'.
$email = $this->randomMachineName(32) . '@example.com';
$subject = $this->randomMachineName(64);
$this->submitContact($this->randomMachineName(16), $email, $subject, 'foo', $this->randomString(128));
// We are testing the auto-reply, so there should be one email going to the sender.
$captured_emails = $this->getMails(['id' => 'contact_page_autoreply', 'to' => $email]);
$this->assertEqual(count($captured_emails), 1);
$this->assertEqual(trim($captured_emails[0]['body']), trim(MailFormatHelper::htmlToText($foo_autoreply)));
// Test the auto-reply for form 'bar'.
$email = $this->randomMachineName(32) . '@example.com';
$this->submitContact($this->randomMachineName(16), $email, $this->randomString(64), 'bar', $this->randomString(128));
// Auto-reply for form 'bar' should result in one auto-reply email to the sender.
$captured_emails = $this->getMails(['id' => 'contact_page_autoreply', 'to' => $email]);
$this->assertEqual(count($captured_emails), 1);
$this->assertEqual(trim($captured_emails[0]['body']), trim(MailFormatHelper::htmlToText($bar_autoreply)));
// Verify that no auto-reply is sent when the auto-reply field is left blank.
$email = $this->randomMachineName(32) . '@example.com';
$this->submitContact($this->randomMachineName(16), $email, $this->randomString(64), 'no_autoreply', $this->randomString(128));
$captured_emails = $this->getMails(['id' => 'contact_page_autoreply', 'to' => $email]);
$this->assertEqual(count($captured_emails), 0);
// Verify that the current error message doesn't show, that the auto-reply
// doesn't get sent and the correct silent error gets logged.
$email = '';
entity_get_form_display('contact_message', 'foo', 'default')
->removeComponent('mail')
->save();
$this->submitContact($this->randomMachineName(16), $email, $this->randomString(64), 'foo', $this->randomString(128));
$this->assertNoText('Unable to send email. Contact the site administrator if the problem persists.');
$captured_emails = $this->getMails(['id' => 'contact_page_autoreply', 'to' => $email]);
$this->assertEqual(count($captured_emails), 0);
$this->drupalLogin($admin_user);
$this->drupalGet('admin/reports/dblog');
$this->assertRaw('Error sending auto-reply, missing sender e-mail address in foo');
}
/**
* Adds a form.
*
* @param string $id
* The form machine name.
* @param string $label
* The form label.
* @param string $recipients
* The list of recipient email addresses.
* @param string $reply
* The auto-reply text that is sent to a user upon completing the contact
* form.
* @param bool $selected
* A Boolean indicating whether the form should be selected by default.
* @param string $message
* The message that will be displayed to a user upon completing the contact
* form.
* @param array $third_party_settings
* Array of third party settings to be added to the posted form data.
*/
public function addContactForm($id, $label, $recipients, $reply, $selected, $message = 'Your message has been sent.', $third_party_settings = []) {
$edit = [];
$edit['label'] = $label;
$edit['id'] = $id;
$edit['message'] = $message;
$edit['recipients'] = $recipients;
$edit['reply'] = $reply;
$edit['selected'] = ($selected ? TRUE : FALSE);
$edit += $third_party_settings;
$this->drupalPostForm('admin/structure/contact/add', $edit, t('Save'));
}
/**
* Updates a form.
*
* @param string $id
* The form machine name.
* @param string $label
* The form label.
* @param string $recipients
* The list of recipient email addresses.
* @param string $reply
* The auto-reply text that is sent to a user upon completing the contact
* form.
* @param bool $selected
* A Boolean indicating whether the form should be selected by default.
* @param string $message
* The message that will be displayed to a user upon completing the contact
* form.
* @param string $redirect
* The path where user will be redirect after this form has been submitted..
*/
public function updateContactForm($id, $label, $recipients, $reply, $selected, $message = 'Your message has been sent.', $redirect = '/') {
$edit = [];
$edit['label'] = $label;
$edit['recipients'] = $recipients;
$edit['reply'] = $reply;
$edit['selected'] = ($selected ? TRUE : FALSE);
$edit['message'] = $message;
$edit['redirect'] = $redirect;
$this->drupalPostForm("admin/structure/contact/manage/$id", $edit, t('Save'));
}
/**
* Submits the contact form.
*
* @param string $name
* The name of the sender.
* @param string $mail
* The email address of the sender.
* @param string $subject
* The subject of the message.
* @param string $id
* The form ID of the message.
* @param string $message
* The message body.
*/
public function submitContact($name, $mail, $subject, $id, $message) {
$edit = [];
$edit['name'] = $name;
$edit['mail'] = $mail;
$edit['subject[0][value]'] = $subject;
$edit['message[0][value]'] = $message;
if ($id == $this->config('contact.settings')->get('default_form')) {
$this->drupalPostForm('contact', $edit, t('Send message'));
}
else {
$this->drupalPostForm('contact/' . $id, $edit, t('Send message'));
}
}
/**
* Deletes all forms.
*/
public function deleteContactForms() {
$contact_forms = ContactForm::loadMultiple();;
foreach ($contact_forms as $id => $contact_form) {
if ($id == 'personal') {
// Personal form could not be deleted.
$this->drupalGet("admin/structure/contact/manage/$id/delete");
$this->assertResponse(403);
}
else {
$this->drupalPostForm("admin/structure/contact/manage/$id/delete", [], t('Delete'));
$this->assertRaw(t('The contact form %label has been deleted.', ['%label' => $contact_form->label()]));
$this->assertFalse(ContactForm::load($id), format_string('Form %contact_form not found', ['%contact_form' => $contact_form->label()]));
}
}
}
}
@@ -0,0 +1,77 @@
<?php
namespace Drupal\Tests\contact\Functional;
use Drupal\Component\Utility\Unicode;
use Drupal\contact\Entity\Message;
use Drupal\user\RoleInterface;
/**
* Tests storing contact messages.
*
* Note that the various test methods in ContactSitewideTest are also run by
* this test. This is by design to ensure that regular contact.module functions
* continue to work when a storage handler other than ContentEntityNullStorage
* is enabled for contact Message entities.
*
* @group contact
*/
class ContactStorageTest extends ContactSitewideTest {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = [
'block',
'text',
'contact',
'field_ui',
'contact_storage_test',
'contact_test',
];
/**
* Tests configuration options and the site-wide contact form.
*/
public function testContactStorage() {
// Create and log in administrative user.
$admin_user = $this->drupalCreateUser([
'access site-wide contact form',
'administer contact forms',
'administer users',
'administer account settings',
'administer contact_message fields',
]);
$this->drupalLogin($admin_user);
// Create first valid contact form.
$mail = 'simpletest@example.com';
$this->addContactForm($id = Unicode::strtolower($this->randomMachineName(16)), $label = $this->randomMachineName(16), implode(',', [$mail]), '', TRUE, 'Your message has been sent.', [
'send_a_pony' => 1,
]);
$this->assertText(t('Contact form @label has been added.', ['@label' => $label]));
// Ensure that anonymous can submit site-wide contact form.
user_role_grant_permissions(RoleInterface::ANONYMOUS_ID, ['access site-wide contact form']);
$this->drupalLogout();
$this->drupalGet('contact');
$this->assertText(t('Your email address'));
$this->assertNoText(t('Form'));
$this->submitContact($name = $this->randomMachineName(16), $mail, $subject = $this->randomMachineName(16), $id, $message = $this->randomMachineName(64));
$this->assertText(t('Your message has been sent.'));
$messages = Message::loadMultiple();
/** @var \Drupal\contact\Entity\Message $message */
$message = reset($messages);
$this->assertEqual($message->getContactForm()->id(), $id);
$this->assertTrue($message->getContactForm()->getThirdPartySetting('contact_storage_test', 'send_a_pony', FALSE));
$this->assertEqual($message->getSenderName(), $name);
$this->assertEqual($message->getSubject(), $subject);
$this->assertEqual($message->getSenderMail(), $mail);
$config = $this->config("contact.form.$id");
$this->assertEqual($config->get('id'), $id);
}
}
@@ -0,0 +1,65 @@
<?php
namespace Drupal\Tests\contact\Functional\Views;
use Drupal\field\Entity\FieldConfig;
use Drupal\Tests\views\Functional\ViewTestBase;
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\contact\Entity\ContactForm;
/**
* Tests which checks that no fieldapi fields are added on contact.
*
* @group contact
*/
class ContactFieldsTest extends ViewTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['field', 'text', 'contact'];
/**
* Contains the field storage definition for contact used for this test.
*
* @var \Drupal\field\Entity\FieldStorageConfig
*/
protected $fieldStorage;
protected function setUp($import_test_views = TRUE) {
parent::setUp($import_test_views);
$this->fieldStorage = FieldStorageConfig::create([
'field_name' => strtolower($this->randomMachineName()),
'entity_type' => 'contact_message',
'type' => 'text'
]);
$this->fieldStorage->save();
ContactForm::create([
'id' => 'contact_message',
'label' => 'Test contact form',
])->save();
FieldConfig::create([
'field_storage' => $this->fieldStorage,
'bundle' => 'contact_message',
])->save();
$this->container->get('views.views_data')->clear();
}
/**
* Tests the views data generation.
*/
public function testViewsData() {
// Test that the field is not exposed to views, since contact_message
// entities have no storage.
$table_name = 'contact_message__' . $this->fieldStorage->getName();
$data = $this->container->get('views.views_data')->get($table_name);
$this->assertFalse($data, 'The field is not exposed to Views.');
}
}
@@ -0,0 +1,108 @@
<?php
namespace Drupal\Tests\contact\Functional\Views;
use Drupal\Core\Cache\Cache;
use Drupal\Tests\views\Functional\ViewTestBase;
use Drupal\views\Tests\ViewTestData;
use Drupal\user\Entity\User;
/**
* Tests the contact link field.
*
* @group contact
* @see \Drupal\contact\Plugin\views\field\ContactLink.
*/
class ContactLinkTest extends ViewTestBase {
/**
* Stores the user data service used by the test.
*
* @var \Drupal\user\UserDataInterface
*/
public $userData;
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['contact_test_views'];
/**
* Views used by this test.
*
* @var array
*/
public static $testViews = ['test_contact_link'];
/**
* {@inheritdoc}
*/
protected function setUp($import_test_views = TRUE) {
parent::setUp($import_test_views);
ViewTestData::createTestViews(get_class($this), ['contact_test_views']);
$this->userData = $this->container->get('user.data');
}
/**
* Tests contact link.
*/
public function testContactLink() {
$accounts = [];
$accounts['root'] = User::load(1);
// Create an account with access to all contact pages.
$admin_account = $this->drupalCreateUser(['administer users']);
$accounts['admin'] = $admin_account;
// Create an account with no access to contact pages.
$no_contact_account = $this->drupalCreateUser();
$accounts['no_contact'] = $no_contact_account;
// Create an account with access to contact pages.
$contact_account = $this->drupalCreateUser(['access user contact forms']);
$accounts['contact'] = $contact_account;
$this->drupalLogin($admin_account);
$this->drupalGet('test-contact-link');
// The admin user has access to all contact links beside his own.
$this->assertContactLinks($accounts, ['root', 'no_contact', 'contact']);
$this->drupalLogin($no_contact_account);
$this->drupalGet('test-contact-link');
// Ensure that the user without the permission doesn't see any link.
$this->assertContactLinks($accounts, []);
$this->drupalLogin($contact_account);
$this->drupalGet('test-contact-link');
$this->assertContactLinks($accounts, ['root', 'admin', 'no_contact']);
// Disable contact link for no_contact.
$this->userData->set('contact', $no_contact_account->id(), 'enabled', FALSE);
// @todo Remove cache invalidation in https://www.drupal.org/node/2477903.
Cache::invalidateTags($no_contact_account->getCacheTagsToInvalidate());
$this->drupalGet('test-contact-link');
$this->assertContactLinks($accounts, ['root', 'admin']);
}
/**
* Asserts whether certain users contact links appear on the page.
*
* @param array $accounts
* All user objects used by the test.
* @param array $names
* Users which should have contact links.
*/
public function assertContactLinks(array $accounts, array $names) {
$result = $this->xpath('//div[contains(@class, "views-field-contact")]//a');
$this->assertEqual(count($result), count($names));
foreach ($names as $name) {
$account = $accounts[$name];
$result = $this->xpath('//div[contains(@class, "views-field-contact")]//a[contains(@href, :url)]', [':url' => $account->url('contact-form')]);
$this->assertTrue(count($result));
}
}
}
@@ -17,17 +17,17 @@ class MessageEntityTest extends EntityKernelTestBase {
*
* @var array
*/
public static $modules = array(
public static $modules = [
'system',
'contact',
'field',
'user',
'contact_test',
);
];
protected function setUp() {
parent::setUp();
$this->installConfig(array('contact', 'contact_test'));
$this->installConfig(['contact', 'contact_test']);
}
/**
@@ -35,7 +35,7 @@ class MessageEntityTest extends EntityKernelTestBase {
*/
public function testMessageMethods() {
$message_storage = $this->container->get('entity.manager')->getStorage('contact_message');
$message = $message_storage->create(array('contact_form' => 'feedback'));
$message = $message_storage->create(['contact_form' => 'feedback']);
// Check for empty values first.
$this->assertEqual($message->getMessage(), '');
@@ -18,7 +18,7 @@ class MigrateContactCategoryTest extends MigrateDrupal6TestBase {
*
* @var array
*/
public static $modules = array('contact');
public static $modules = ['contact'];
/**
* {@inheritdoc}
@@ -59,6 +59,26 @@ class MigrateContactCategoryTest extends MigrateDrupal6TestBase {
$this->assertEntity('website_feedback', 'Website feedback', ['admin@example.com'], '', 0);
$this->assertEntity('some_other_category', 'Some other category', ['test@example.com'], 'Thanks for contacting us, we will reply ASAP!', 1);
$this->assertEntity('a_category_much_longer_than_thir', 'A category much longer than thirty two characters', ['fortyninechars@example.com'], '', 2);
// Test there are no duplicated roles.
$contact_forms = [
'website_feedback1',
'some_other_category1',
'a_category_much_longer_than_thir1',
];
$this->assertEmpty(ContactForm::loadMultiple($contact_forms));
/*
* Remove the map row for the Website feedback contact form so that it
* can be migrated again.
*/
$id_map = $this->getMigration('contact_category')->getIdMap();
$id_map->delete(['cid' => '1']);
$this->executeMigration('contact_category');
// Test there is a duplicate Website feedback form.
$contact_form = ContactForm::load('website_feedback1');
$this->assertEntity('website_feedback1', 'Website feedback', ['admin@example.com'], '', 0);
}
}
@@ -2,7 +2,7 @@
namespace Drupal\Tests\contact\Kernel\Migrate\d6;
use Drupal\config\Tests\SchemaCheckTestTrait;
use Drupal\Tests\SchemaCheckTestTrait;
use Drupal\Tests\migrate_drupal\Kernel\d6\MigrateDrupal6TestBase;
/**
@@ -0,0 +1,57 @@
<?php
namespace Drupal\Tests\contact\Kernel\Plugin\migrate\source;
use Drupal\Tests\migrate\Kernel\MigrateSqlSourceTestBase;
/**
* Tests D6 contact category source plugin.
*
* @covers \Drupal\contact\Plugin\migrate\source\ContactCategory
* @group contact
*/
class ContactCategoryTest extends MigrateSqlSourceTestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['contact', 'migrate_drupal', 'user'];
/**
* {@inheritdoc}
*/
public function providerSource() {
$tests = [
[
'source_data' => [],
'expected_data' => [],
],
];
$tests[0]['expected_data'] = [
[
'cid' => 1,
'category' => 'contact category value 1',
'recipients' => ['admin@example.com', 'user@example.com'],
'reply' => 'auto reply value 1',
'weight' => 0,
'selected' => 0,
],
[
'cid' => 2,
'category' => 'contact category value 2',
'recipients' => ['admin@example.com', 'user@example.com'],
'reply' => 'auto reply value 2',
'weight' => 0,
'selected' => 0,
],
];
foreach ($tests[0]['expected_data'] as $k => $row) {
$row['recipients'] = implode(',', $row['recipients']);
$tests[0]['source_data']['contact'][$k] = $row;
}
return $tests;
}
}
@@ -0,0 +1,54 @@
<?php
namespace Drupal\Tests\contact\Kernel\Plugin\migrate\source\d6;
use Drupal\Tests\migrate\Kernel\MigrateSqlSourceTestBase;
/**
* Tests D6 contact settings source plugin.
*
* @covers \Drupal\contact\Plugin\migrate\source\ContactSettings
* @group contact
*/
class ContactSettingsTest extends MigrateSqlSourceTestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['contact', 'migrate_drupal', 'user'];
/**
* {@inheritdoc}
*/
public function providerSource() {
$tests = [];
$tests[0]['source_data']['variable'] = [
[
'name' => 'site_name',
'value' => serialize('Blorf!'),
],
];
$tests[0]['source_data']['contact'] = [
[
'cid' => '1',
'category' => 'Website feedback',
'recipients' => 'admin@example.com',
'reply' => '',
'weight' => '0',
'selected' => '1',
]
];
$tests[0]['expected_data'] = [
[
'default_category' => '1',
'site_name' => 'Blorf!',
],
];
$tests[0]['expected_count'] = NULL;
$tests[0]['configuration']['variables'] = ['site_name'];
return $tests;
}
}
@@ -3,6 +3,7 @@
namespace Drupal\Tests\contact\Unit;
use Drupal\contact\MailHandler;
use Drupal\contact\MailHandlerException;
use Drupal\contact\MessageInterface;
use Drupal\Core\Language\Language;
use Drupal\Core\Session\AccountInterface;
@@ -80,7 +81,7 @@ class MailHandlerTest extends UnitTestCase {
$string_translation = $this->getStringTranslationStub();
$this->contactMailHandler = new MailHandler($this->mailManager, $this->languageManager, $this->logger, $string_translation, $this->entityManager);
$language = new Language(array('id' => 'en'));
$language = new Language(['id' => 'en']);
$this->languageManager->expects($this->any())
->method('getDefaultLanguage')
@@ -94,9 +95,6 @@ class MailHandlerTest extends UnitTestCase {
/**
* Tests the children() method with an invalid key.
*
* @expectedException \Drupal\contact\MailHandlerException
* @expectedExceptionMessage Unable to determine message recipient
*
* @covers ::sendMailMessages
*/
public function testInvalidRecipient() {
@@ -121,6 +119,7 @@ class MailHandlerTest extends UnitTestCase {
$sender->expects($this->once())
->method('isAnonymous')
->willReturn(FALSE);
$this->setExpectedException(MailHandlerException::class, 'Unable to determine message recipient');
$this->contactMailHandler->sendMailMessages($message, $sender);
}
@@ -156,76 +155,76 @@ class MailHandlerTest extends UnitTestCase {
* Data provider for ::testSendMailMessages.
*/
public function getSendMailMessages() {
$data = array();
$recipients = array('admin@drupal.org', 'user@drupal.org');
$default_result = array(
$data = [];
$recipients = ['admin@drupal.org', 'user@drupal.org'];
$default_result = [
'module' => 'contact',
'key' => '',
'to' => implode(', ', $recipients),
'langcode' => 'en',
'params' => array(),
'params' => [],
'from' => 'anonymous@drupal.org',
);
$results = array();
];
$results = [];
$message = $this->getAnonymousMockMessage($recipients, '');
$sender = $this->getMockSender();
$result = array(
$result = [
'key' => 'page_mail',
'params' => array(
'params' => [
'contact_message' => $message,
'sender' => $sender,
'contact_form' => $message->getContactForm(),
),
);
],
];
$results[] = $result + $default_result;
$data[] = array($message, $sender, $results);
$data[] = [$message, $sender, $results];
$results = array();
$results = [];
$message = $this->getAnonymousMockMessage($recipients, 'reply');
$sender = $this->getMockSender();
$result = array(
$result = [
'key' => 'page_mail',
'params' => array(
'params' => [
'contact_message' => $message,
'sender' => $sender,
'contact_form' => $message->getContactForm(),
),
);
],
];
$results[] = $result + $default_result;
$result['key'] = 'page_autoreply';
$result['to'] = 'anonymous@drupal.org';
$result['from'] = NULL;
$results[] = $result + $default_result;
$data[] = array($message, $sender, $results);
$data[] = [$message, $sender, $results];
$results = array();
$results = [];
$message = $this->getAnonymousMockMessage($recipients, '', TRUE);
$sender = $this->getMockSender();
$result = array(
$result = [
'key' => 'page_mail',
'params' => array(
'params' => [
'contact_message' => $message,
'sender' => $sender,
'contact_form' => $message->getContactForm(),
),
);
],
];
$results[] = $result + $default_result;
$result['key'] = 'page_copy';
$result['to'] = 'anonymous@drupal.org';
$results[] = $result + $default_result;
$data[] = array($message, $sender, $results);
$data[] = [$message, $sender, $results];
$results = array();
$results = [];
$message = $this->getAnonymousMockMessage($recipients, 'reply', TRUE);
$sender = $this->getMockSender();
$result = array(
$result = [
'key' => 'page_mail',
'params' => array(
'params' => [
'contact_message' => $message,
'sender' => $sender,
'contact_form' => $message->getContactForm(),
),
);
],
];
$results[] = $result + $default_result;
$result['key'] = 'page_copy';
$result['to'] = 'anonymous@drupal.org';
@@ -233,48 +232,48 @@ class MailHandlerTest extends UnitTestCase {
$result['key'] = 'page_autoreply';
$result['from'] = NULL;
$results[] = $result + $default_result;
$data[] = array($message, $sender, $results);
$data[] = [$message, $sender, $results];
//For authenticated user.
$results = array();
$results = [];
$message = $this->getAuthenticatedMockMessage();
$sender = $this->getMockSender(FALSE, 'user@drupal.org');
$result = array(
$result = [
'module' => 'contact',
'key' => 'user_mail',
'to' => 'user2@drupal.org',
'langcode' => 'en',
'params' => array(
'params' => [
'contact_message' => $message,
'sender' => $sender,
'recipient' => $message->getPersonalRecipient(),
),
],
'from' => 'user@drupal.org',
);
];
$results[] = $result;
$data[] = array($message, $sender, $results);
$data[] = [$message, $sender, $results];
$results = array();
$results = [];
$message = $this->getAuthenticatedMockMessage(TRUE);
$sender = $this->getMockSender(FALSE, 'user@drupal.org');
$result = array(
$result = [
'module' => 'contact',
'key' => 'user_mail',
'to' => 'user2@drupal.org',
'langcode' => 'en',
'params' => array(
'params' => [
'contact_message' => $message,
'sender' => $sender,
'recipient' => $message->getPersonalRecipient(),
),
],
'from' => 'user@drupal.org',
);
];
$results[] = $result;
$result['key'] = 'user_copy';
$result['to'] = $result['from'];
$results[] = $result;
$data[] = array($message, $sender, $results);
$data[] = [$message, $sender, $results];
return $data;
}
@@ -299,7 +298,7 @@ class MailHandlerTest extends UnitTestCase {
->method('getEmail')
->willReturn($mail_address);
$sender->expects($this->any())
->method('getUsername')
->method('getDisplayName')
->willReturn('user');
// User ID 1 has special implications, use 3 instead.
$sender->expects($this->any())
@@ -369,7 +368,7 @@ class MailHandlerTest extends UnitTestCase {
->method('getEmail')
->willReturn('user2@drupal.org');
$recipient->expects($this->once())
->method('getUsername')
->method('getDisplayName')
->willReturn('user2');
$recipient->expects($this->once())
->method('getPreferredLangcode')