Commit f03337ad authored by Dries's avatar Dries

Issue #1825044 by sun: turn contact form submissions into full-blown Contact...

Issue #1825044 by sun: turn contact form submissions into full-blown Contact Message entities, without storage.
parent 3250f84a
......@@ -117,7 +117,14 @@ public function __construct($entityType) {
$this->entityInfo = entity_get_info($entityType);
$this->entityCache = array();
$this->hookLoadArguments = array();
$this->idKey = $this->entityInfo['entity_keys']['id'];
// Check if the entity type supports IDs.
if (isset($this->entityInfo['entity_keys']['id'])) {
$this->idKey = $this->entityInfo['entity_keys']['id'];
}
else {
$this->idKey = FALSE;
}
// Check if the entity type supports UUIDs.
if (!empty($this->entityInfo['entity_keys']['uuid'])) {
......
......@@ -184,6 +184,9 @@ public function validate(array $form, array &$form_state) {
* A reference to a keyed array containing the current state of the form.
*/
public function submit(array $form, array &$form_state) {
// Remove button and internal Form API values from submitted values.
form_state_values_clean($form_state);
$this->submitEntityLanguage($form, $form_state);
$entity = $this->buildEntity($form, $form_state);
$this->setEntity($entity, $form_state);
......
......@@ -74,6 +74,8 @@ function contact_menu() {
);
$items['admin/structure/contact/manage/%contact_category'] = array(
'title' => 'Edit contact category',
'title callback' => 'entity_page_label',
'title arguments' => array(4),
'page callback' => 'contact_category_edit',
'page arguments' => array(4),
'access arguments' => array('administer contact forms'),
......@@ -90,21 +92,32 @@ function contact_menu() {
'page arguments' => array('contact_category_delete_form', 4),
'access arguments' => array('administer contact forms'),
'type' => MENU_LOCAL_TASK,
'weight' => 10,
'file' => 'contact.admin.inc',
);
$items['contact'] = array(
'title' => 'Contact',
'page callback' => 'drupal_get_form',
'page arguments' => array('contact_site_form'),
'page callback' => 'contact_site_page',
'access arguments' => array('access site-wide contact form'),
'menu_name' => 'footer',
'type' => MENU_SUGGESTED_ITEM,
'file' => 'contact.pages.inc',
);
$items['contact/%contact_category'] = array(
'title' => 'Contact category form',
'title callback' => 'entity_page_label',
'title arguments' => array(1),
'page callback' => 'contact_site_page',
'page arguments' => array(1),
'access arguments' => array('access site-wide contact form'),
'type' => MENU_VISIBLE_IN_BREADCRUMB,
'file' => 'contact.pages.inc',
);
$items['user/%user/contact'] = array(
'title' => 'Contact',
'page callback' => 'drupal_get_form',
'page arguments' => array('contact_personal_form', 1),
'page callback' => 'contact_personal_page',
'page arguments' => array(1),
'type' => MENU_LOCAL_TASK,
'access callback' => '_contact_personal_tab_access',
'access arguments' => array(1),
......@@ -210,6 +223,72 @@ function contact_config_import_delete($name, $new_config, $old_config) {
return TRUE;
}
/**
* Implements hook_entity_info().
*/
function contact_entity_info(&$types) {
foreach (config_get_storage_names_with_prefix('contact.category.') as $config_name) {
$config = config($config_name);
$types['contact_message']['bundles'][$config->get('id')] = array(
'label' => $config->get('label'),
'admin' => array(
'path' => 'admin/structure/contact/manage/%contact_category',
'real path' => 'admin/structure/contact/manage/' . $config->get('id'),
'bundle argument' => 4,
'access arguments' => array('administer contact forms'),
),
);
}
}
/**
* Implements hook_field_extra_fields().
*/
function contact_field_extra_fields() {
$fields = array();
$entity_info = entity_get_info('contact_message');
foreach (array_keys($entity_info['bundles']) as $bundle) {
$fields['contact_message'][$bundle]['form']['name'] = array(
'label' => t('Sender name'),
'description' => t('Text'),
'weight' => -50,
);
$fields['contact_message'][$bundle]['form']['mail'] = array(
'label' => t('Sender e-mail'),
'description' => t('E-mail'),
'weight' => -40,
);
// @todo Recipient only makes sense if user contact form is a bundle/category.
$fields['contact_message'][$bundle]['form']['recipient'] = array(
'label' => t('Recipient user name'),
'description' => t('User'),
'weight' => -30,
);
$fields['contact_message'][$bundle]['form']['subject'] = array(
'label' => t('Subject'),
'description' => t('Text'),
'weight' => -10,
);
$fields['contact_message'][$bundle]['form']['message'] = array(
'label' => t('Message'),
'description' => t('Long text'),
'weight' => 0,
);
$fields['contact_message'][$bundle]['form']['copy'] = array(
'label' => t('Send copy to sender'),
'description' => t('Option'),
'weight' => 50,
);
$fields['contact_message'][$bundle]['display']['message'] = array(
'label' => t('Message'),
'description' => t('The main contact message'),
'weight' => 0,
);
}
return $fields;
}
/**
* Loads a contact category.
*
......@@ -242,27 +321,39 @@ function contact_category_uri(Category $category) {
* Implements hook_mail().
*/
function contact_mail($key, &$message, $params) {
$contact_message = $params['contact_message'];
$sender = $params['sender'];
$language = language_load($message['langcode']);
$variables = array(
'!site-name' => config('system.site')->get('name'),
'!subject' => $params['subject'],
'!category' => isset($params['category']) ? $params['category']->label() : '',
'!subject' => $contact_message->subject,
'!category' => isset($params['contact_category']) ? $params['contact_category']->label() : NULL,
'!form-url' => url(current_path(), array('absolute' => TRUE, 'language' => $language)),
'!sender-name' => user_format_name($params['sender']),
'!sender-url' => $params['sender']->uid ? url('user/' . $params['sender']->uid, array('absolute' => TRUE, 'language' => $language)) : $params['sender']->mail,
'!sender-name' => user_format_name($sender),
);
if (!empty($sender->uid)) {
$sender_uri = $sender->uri();
$variables['!sender-url'] = url($sender_uri['path'], array('absolute' => TRUE, 'language' => $language) + $sender_uri['options']);
}
else {
$variables['!sender-url'] = $params['sender']->mail;
}
$options = array('langcode' => $language->langcode);
switch ($key) {
case 'page_mail':
case 'page_copy':
$message['subject'] .= t('[!category] !subject', $variables, array('langcode' => $language->langcode));
$message['body'][] = t("!sender-name (!sender-url) sent a message using the contact form at !form-url.", $variables, array('langcode' => $language->langcode));
$message['body'][] = $params['message'];
$message['subject'] .= t('[!category] !subject', $variables, $options);
$message['body'][] = t("!sender-name (!sender-url) sent a message using the contact form at !form-url.", $variables, $options);
$build = entity_view($contact_message, 'mail', $language->langcode);
$message['body'][] = drupal_render($build);
break;
case 'page_autoreply':
$message['subject'] .= t('[!category] !subject', $variables, array('langcode' => $language->langcode));
$message['body'][] = $params['category']->reply;
$message['subject'] .= t('[!category] !subject', $variables, $options);
$message['body'][] = $params['contact_category']->reply;
break;
case 'user_mail':
......@@ -271,12 +362,12 @@ function contact_mail($key, &$message, $params) {
'!recipient-name' => user_format_name($params['recipient']),
'!recipient-edit-url' => url('user/' . $params['recipient']->uid . '/edit', array('absolute' => TRUE, 'language' => $language)),
);
$message['subject'] .= t('[!site-name] !subject', $variables, array('langcode' => $language->langcode));
$message['body'][] = t('Hello !recipient-name,', $variables, array('langcode' => $language->langcode));
$message['body'][] = t("!sender-name (!sender-url) has sent you a message via your contact form at !site-name.", $variables, array('langcode' => $language->langcode));
$message['body'][] = t("If you don't want to receive such e-mails, you can change your settings at !recipient-edit-url.", $variables, array('langcode' => $language->langcode));
$message['body'][] = t('Message:', array(), array('langcode' => $language->langcode));
$message['body'][] = $params['message'];
$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);
$message['body'][] = t("If you don't want to receive such e-mails, you can change your settings at !recipient-edit-url.", $variables, $options);
$message['body'][] = t('Message:', array(), $options);
$message['body'][] = $contact_message->message;
break;
}
}
......
This diff is collapsed.
......@@ -14,6 +14,29 @@
*/
class CategoryListController extends ConfigEntityListController {
/**
* Overrides Drupal\Core\Entity\EntityListController::getOperations().
*/
public function getOperations(EntityInterface $entity) {
$operations = parent::getOperations($entity);
if (module_exists('field_ui')) {
$uri = $entity->uri();
$operations['manage-fields'] = array(
'title' => t('Manage fields'),
'href' => $uri['path'] . '/fields',
'options' => $uri['options'],
'weight' => 11,
);
$operations['manage-display'] = array(
'title' => t('Manage display'),
'href' => $uri['path'] . '/display',
'options' => $uri['options'],
'weight' => 12,
);
}
return $operations;
}
/**
* Overrides Drupal\Core\Entity\EntityListController::buildHeader().
*/
......
<?php
/**
* @file
* Contains Drupal\contact\CategoryStorageController.
*/
namespace Drupal\contact;
use Drupal\Core\Config\Entity\ConfigStorageController;
use Drupal\Core\Entity\EntityInterface;
/**
* Controller class for contact categories.
*/
class CategoryStorageController extends ConfigStorageController {
/**
* Overrides \Drupal\Core\Config\Entity\ConfigStorageController::postSave().
*/
protected function postSave(EntityInterface $entity, $update) {
parent::postSave($entity, $update);
if (!$update) {
field_attach_create_bundle('contact_message', $entity->id());
}
elseif ($entity->original->id() != $entity->id()) {
field_attach_rename_bundle('contact_message', $entity->original->id(), $entity->id());
}
}
/**
* Overrides \Drupal\Core\Config\Entity\ConfigStorageController::postDelete().
*/
protected function postDelete($entities) {
parent::postDelete($entities);
foreach ($entities as $entity) {
field_attach_delete_bundle('contact_message', $entity->id());
}
}
}
<?php
/**
* @file
* Definition of Drupal\contact\MessageFormController.
*/
namespace Drupal\contact;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityFormController;
use Drupal\user\Plugin\Core\Entity\User;
/**
* Form controller for contact message forms.
*/
class MessageFormController extends EntityFormController {
/**
* Overrides Drupal\Core\Entity\EntityFormController::form().
*/
public function form(array $form, array &$form_state, EntityInterface $message) {
global $user;
$form = parent::form($form, $form_state, $message);
$form['#attributes']['class'][] = 'contact-form';
if (!empty($message->preview)) {
$form['preview'] = array(
'#theme_wrappers' => array('container__preview'),
'#attributes' => array('class' => array('preview')),
);
$form['preview']['message'] = entity_view($message, 'full');
}
$form['name'] = array(
'#type' => 'textfield',
'#title' => t('Your name'),
'#maxlength' => 255,
'#required' => TRUE,
);
$form['mail'] = array(
'#type' => 'email',
'#title' => t('Your e-mail address'),
'#required' => TRUE,
);
if (!$user->uid) {
$form['#attached']['library'][] = array('system', 'jquery.cookie');
$form['#attributes']['class'][] = 'user-info-from-cookie';
}
// Do not allow authenticated users to alter the name or e-mail values to
// prevent the impersonation of other users.
else {
$form['name']['#type'] = 'item';
$form['name']['#value'] = $user->name;
$form['name']['#required'] = FALSE;
$form['name']['#markup'] = check_plain(user_format_name($user));
$form['mail']['#type'] = 'item';
$form['mail']['#value'] = $user->mail;
$form['mail']['#required'] = FALSE;
$form['mail']['#markup'] = check_plain($user->mail);
}
// The user contact form only has a recipient, not a category.
// @todo Convert user contact form into a locked contact category.
if ($message->recipient instanceof User) {
$form['recipient'] = array(
'#type' => 'item',
'#title' => t('To'),
'#value' => $message->recipient,
'name' => array(
'#theme' => 'username',
'#account' => $message->recipient,
),
);
}
else {
$form['category'] = array(
'#type' => 'value',
'#value' => $message->category,
);
}
$form['subject'] = array(
'#type' => 'textfield',
'#title' => t('Subject'),
'#maxlength' => 100,
'#required' => TRUE,
);
$form['message'] = array(
'#type' => 'textarea',
'#title' => t('Message'),
'#required' => TRUE,
'#rows' => 12,
);
$form['copy'] = array(
'#type' => 'checkbox',
'#title' => t('Send yourself a copy.'),
// Do not allow anonymous users to send themselves a copy, because it can
// be abused to spam people.
'#access' => !empty($user->uid),
);
return $form;
}
/**
* Overrides Drupal\Core\Entity\EntityFormController::actions().
*/
public function actions(array $form, array &$form_state) {
$elements = parent::actions($form, $form_state);
$elements['submit']['#value'] = t('Send message');
$elements['delete']['#access'] = FALSE;
$elements['preview'] = array(
'#value' => t('Preview'),
'#validate' => array(
array($this, 'validate'),
),
'#submit' => array(
array($this, 'submit'),
array($this, 'preview'),
),
);
return $elements;
}
/**
* Form submission handler for the 'preview' action.
*/
public function preview(array $form, array &$form_state) {
$message = $this->getEntity($form_state);
$message->preview = TRUE;
$form_state['rebuild'] = TRUE;
}
/**
* Overrides Drupal\Core\Entity\EntityFormController::save().
*/
public function save(array $form, array &$form_state) {
global $user;
$language_interface = language(LANGUAGE_TYPE_INTERFACE);
$message = $this->getEntity($form_state);
$sender = clone user_load($user->uid);
if (!$user->uid) {
// At this point, $sender contains drupal_anonymous_user(), so we need to
// take over the submitted form values.
$sender->name = $message->name;
$sender->mail = $message->mail;
// Save the anonymous user information to a cookie for reuse.
user_cookie_save(array('name' => $message->name, 'mail' => $message->mail));
// For the e-mail message, clarify that the sender name is not verified; it
// could potentially clash with a username on this site.
$sender->name = t('!name (not verified)', array('!name' => $message->name));
}
// Build e-mail parameters.
$params['contact_message'] = $message;
$params['sender'] = $sender;
if ($message->category) {
// Send to the category recipient(s), using the site's default language.
$category = entity_load('contact_category', $message->category);
$params['contact_category'] = $category;
$to = implode(', ', $category->recipients);
$recipient_langcode = language_default()->langcode;
}
elseif ($message->recipient instanceof User) {
// Send to the user in the user's preferred language.
$to = $message->recipient->mail;
$recipient_langcode = user_preferred_langcode($message->recipient);
}
else {
throw new \RuntimeException(t('Unable to determine message recipient.'));
}
// Send e-mail to the recipient(s).
drupal_mail('contact', 'page_mail', $to, $recipient_langcode, $params, $sender->mail);
// If requested, send a copy to the user, using the current language.
if ($message->copy) {
drupal_mail('contact', 'page_copy', $sender->mail, $language_interface->langcode, $params, $sender->mail);
}
// If configured, send an auto-reply, using the current language.
if ($message->category && $category->reply) {
// User contact forms do not support an auto-reply message, so this
// message always originates from the site.
drupal_mail('contact', 'page_autoreply', $sender->mail, $language_interface->langcode, $params);
}
drupal_container()->get('flood')->register('contact', config('contact.settings')->get('flood.interval'));
if ($message->category) {
watchdog('contact', '%sender-name (@sender-from) sent an e-mail regarding %category.', array(
'%sender-name' => $sender->name,
'@sender-from' => $sender->mail,
'%category' => $category->label(),
));
}
else {
watchdog('contact', '%sender-name (@sender-from) sent %recipient-name an e-mail.', array(
'%sender-name' => $sender->name,
'@sender-from' => $sender->mail,
'%recipient-name' => $message->recipient->name,
));
}
drupal_set_message(t('Your message has been sent.'));
// 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.
if ($message->recipient instanceof User && user_access('access user profiles')) {
$uri = $message->recipient->uri();
$form_state['redirect'] = array($uri['path'], $uri['options']);
}
else {
$form_state['redirect'] = '';
}
}
}
<?php
/**
* @file
* Contains Drupal\contact\MessageRenderController.
*/
namespace Drupal\contact;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityRenderController;
/**
* Render controller for contact messages.
*/
class MessageRenderController extends EntityRenderController {
/**
* Overrides Drupal\Core\Entity\EntityRenderController::buildContent().
*/
public function buildContent(array $entities = array(), $view_mode = 'full', $langcode = NULL) {
parent::buildContent($entities, $view_mode, $langcode);
foreach ($entities as $entity) {
// Add the message extra field, if enabled.
$bundle = $entity->bundle();
$entity_view_mode = $entity->content['#view_mode'];
$fields = field_extra_fields_get_display($this->entityType, $bundle, $entity_view_mode);
if (!empty($entity->message) && !empty($fields['message']['visible'])) {
$entity->content['message'] = array(
'#type' => 'item',
'#title' => t('Message'),
'#markup' => check_plain($entity->message),
'#weight' => $fields['message']['weight'],
);
}
}
}
/**
* Overrides Drupal\Core\Entity\EntityRenderController::view().
*/
public function view(EntityInterface $entity, $view_mode = 'full', $langcode = NULL) {
$build = parent::view($entity, $view_mode, $langcode);
if ($view_mode == 'mail') {
// Convert field labels into headings.
// @todo Improve drupal_html_to_text() to 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]['#title'] . ":\n";
$build[$key]['#label_display'] = 'hidden';
}
}
$build = array(
'#markup' => drupal_html_to_text(drupal_render($build)),
);
}
return $build;
}
}
......@@ -16,9 +16,9 @@
*
* @Plugin(
* id = "contact_category",
* label = @Translation("Category"),
* label = @Translation("Contact category"),
* module = "contact",
* controller_class = "Drupal\Core\Config\Entity\ConfigStorageController",
* controller_class = "Drupal\contact\CategoryStorageController",
* list_controller_class = "Drupal\contact\CategoryListController",
* form_controller_class = {
* "default" = "Drupal\contact\CategoryFormController"
......
<?php
/**
* @file
* Contains Drupal\contact\Plugin\Core\Entity\Message.
*/
namespace Drupal\contact\Plugin\Core\Entity;
use Drupal\Core\Annotation\Plugin;
use Drupal\Core\Annotation\Translation;
use Drupal\Core\Entity\Entity;
/**
* Defines the contact message entity.
*
* @Plugin(
* id = "contact_message",
* label = @Translation("Contact message"),
* module = "contact",