contact_storage.module in Contact Storage 8
Contains main module logic.
File
contact_storage.moduleView source
<?php
/**
* @file
* Contains main module logic.
*/
use Drupal\contact\MessageForm;
use Drupal\contact_storage\Form\ContactFormDisableForm;
use Drupal\contact_storage\Form\ContactFormEnableForm;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Field\BaseFieldDefinition;
use Drupal\contact\ContactFormInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Language\LanguageInterface;
use Drupal\contact\Entity\ContactForm;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Url;
use Drupal\views\Views;
/**
* Implements hook_help().
*/
function contact_storage_help($route_name, RouteMatchInterface $route_match) {
switch ($route_name) {
case 'help.page.contact_storage':
$output = '<h3>' . t('About') . '</h3>';
$output .= '<p>' . t('The <a href="https://www.drupal.org/project/contact_storage">Contact Storage</a> module allows registered <em>users</em> on your site to see messages sent by <em>visitors</em> when using personal or site-wide forms. The Contact Storage module provides the ability to store the messages so registered users can later view them thru the site.') . '</p>';
$output .= '<p>' . t('The combination of <a href="contact">Contact</a> and Contact Storage modules allows for a general means to create, read, update and delete user generated data. For more information, see the <a href=":contact_storage">online documentation for the Contact Storage module</a>.', [
':contact_storage' => 'https://www.drupal.org/node/2718407',
]) . '</p>';
$output .= '<h3>' . t('Uses') . '</h3>';
$output .= '<dl>';
$output .= '<dt>' . t('Using the Contact messages page') . '</dt>';
$output .= '<dd>' . t('Registered users can access stored messages on the <a href=":url">Contact messages</a> page. This page can be manually accessed from the Administration menu path "Home >> Administration >> Structure >> Contact forms". The Contact Storage module places the "List" tab on the "Contact forms" page. The "Contact messages" page is displayed when the List tab is selected. View, edit and delete operations can be performed on individual messages from this listing of contact messages.', [
':url' => Url::fromRoute('entity.contact_message.collection')
->toString(),
]) . '</dd>';
$output .= '<dt>' . t('Configuring contact message pages') . '</dt>';
$output .= '<dd>' . t('The listing of Contact messages is a <a href=":url">Views</a> page. This page can be manually accessed from the Administration menu path "Home >> Administration >> Structure >> Views" like any other view. It has a VIEW NAME of "Contact messages". Registered users can perform Edit, Duplicate, Disable and Delete Operations from the Views listing page.', [
':url' => Url::fromRoute('entity.view.collection')
->toString(),
]) . '</dd>';
return $output;
}
}
/**
* Implements hook_form_FORM_ID_alter() for contact_form_form().
*/
function contact_storage_form_contact_form_form_alter(&$form, FormStateInterface $form_state) {
/** @var \Drupal\contact\ContactFormInterface $contact_form */
$form_object = $form_state
->getFormObject();
if (!in_array($form_object
->getOperation(), [
'edit',
'add',
], TRUE)) {
// Only alter the edit and add forms.
return;
}
$contact_form = $form_object
->getEntity();
$form['contact_storage_submit_text'] = [
'#type' => 'textfield',
'#title' => t('Submit button text'),
'#description' => t("Override the submit button's default <em>Send message</em> text."),
'#default_value' => $contact_form
->getThirdPartySetting('contact_storage', 'submit_text', t('Send message')),
];
$form['contact_storage_url_alias'] = [
'#type' => 'textfield',
'#title' => t('Add URL alias'),
'#description' => t('Optionally add an URL alias to the contact form.'),
];
if (!$contact_form
->isNew()) {
$aliases = \Drupal::entityTypeManager()
->getStorage('path_alias')
->loadByProperties([
'path' => '/' . $contact_form
->toUrl()
->getInternalPath(),
]);
if ($aliases) {
/** @var \Drupal\path_alias\PathAliasInterface $alias */
$alias = reset($aliases);
$form_state
->set('path_alias_id', $alias
->id());
$form['contact_storage_url_alias']['#default_value'] = $alias
->getAlias();
}
}
$form['contact_storage_disabled_form_message'] = [
'#type' => 'textfield',
'#title' => t('Default disabled contact form message'),
'#description' => t('Default message to display if the contact form is disabled.'),
'#default_value' => $contact_form
->getThirdPartySetting('contact_storage', 'disabled_form_message', t('This contact form has been disabled.')),
];
$form['contact_storage_preview'] = [
'#type' => 'checkbox',
'#title' => t('Allow preview'),
'#description' => t('Show the preview button?'),
'#default_value' => $contact_form
->getThirdPartySetting('contact_storage', 'show_preview', TRUE),
];
$form['contact_storage_maximum_submissions_user'] = [
'#type' => 'textfield',
'#title' => t('Maximum submissions'),
'#description' => t('The maximum number of times, per user, the form can be submitted (0 for unlimited).'),
'#default_value' => $contact_form
->getThirdPartySetting('contact_storage', 'maximum_submissions_user', 0),
];
// Overrides the 'reply' field provided by Core with a formattable field. If
// html e-mails are disabled, enforce plain text format.
$form['reply']['#type'] = 'text_format';
if (!\Drupal::config('contact_storage.settings')
->get('send_html')) {
$form['reply']['#allowed_formats'] = [
'plain_text',
];
}
else {
$form['reply']['#format'] = $contact_form
->getThirdPartySetting('contact_storage', 'page_autoreply_format', 'plain_text');
// Explicitly set the allowed formats so that the fallback format is not
// removed. That allows to prevent any formatting and is the default option,
// without setting it, this option would go away after selecting something
// else.
$formats = filter_formats(\Drupal::currentUser());
$form['reply']['#allowed_formats'] = array_keys($formats);
}
$form['#entity_builders'][] = 'contact_storage_contact_form_form_builder';
$form['#validate'][] = 'contact_storage_contact_form_form_validate';
$form['actions']['submit']['#submit'][] = 'contact_storage_contact_form_form_submit';
}
/**
* Entity builder for the contact form edit form with third party options.
*
* @see contact_storage_test_form_contact_form_edit_form_alter()
*/
function contact_storage_contact_form_form_builder($entity_type, ContactFormInterface $contact_form, &$form, FormStateInterface $form_state) {
$contact_form
->setThirdPartySetting('contact_storage', 'submit_text', $form_state
->getValue('contact_storage_submit_text'));
$contact_form
->setThirdPartySetting('contact_storage', 'show_preview', $form_state
->getValue('contact_storage_preview'));
$contact_form
->setThirdPartySetting('contact_storage', 'disabled_form_message', $form_state
->getValue('contact_storage_disabled_form_message'));
$contact_form
->setThirdPartySetting('contact_storage', 'maximum_submissions_user', $form_state
->getValue('contact_storage_maximum_submissions_user'));
// Auto-reply value is handled by Core; 'reply' is expected to be a string.
$reply = $form_state
->getValue('reply');
if (isset($reply['value'])) {
$form_state
->setValue('reply', $reply['value']);
$contact_form
->setThirdPartySetting('contact_storage', 'page_autoreply_format', $reply['format']);
}
}
/**
* Contact form's form validation handler.
*
* @param array $form
* An associative array containing the structure of the form.
*
* @param \Drupal\Core\Form\FormStateInterface $formState
* The current state of the form.
*/
function contact_storage_contact_form_form_validate(&$form, FormStateInterface &$formState) {
// Validates the url alias. It has to start with a slash.
if (!empty($formState
->getValue('contact_storage_url_alias'))) {
if (strpos($formState
->getValue('contact_storage_url_alias'), '/') !== 0) {
$formState
->setError($form['contact_storage_url_alias'], 'The alias path has to start with a slash.');
}
}
}
/**
* Contact form's form submission handler.
*
* @param array $form
* An associative array containing the structure of the form.
*
* @param \Drupal\Core\Form\FormStateInterface $formState
* The current state of the form.
*/
function contact_storage_contact_form_form_submit(&$form, FormStateInterface &$formState) {
$alias_storage = \Drupal::entityTypeManager()
->getStorage('path_alias');
$entity = $formState
->getFormObject()
->getEntity();
if ($old_alias = $formState
->get('path_alias_id')) {
$old_alias = $alias_storage
->load($old_alias);
}
$new_alias = $formState
->getValue('contact_storage_url_alias');
// If there isn't an alias, set a new one.
if (!$old_alias) {
if ($new_alias) {
$alias_storage
->create([
'path' => '/' . $entity
->toUrl()
->getInternalPath(),
'alias' => $formState
->getValue('contact_storage_url_alias'),
])
->save();
}
}
else {
// Delete old alias if user erased it.
if ($old_alias && !$new_alias) {
$old_alias
->delete();
}
elseif ($new_alias) {
$old_alias
->setAlias($formState
->getValue('contact_storage_url_alias'))
->save();
}
}
}
/**
* Implements hook_form_FORM_ID_alter() for contact_form_form().
*/
function contact_storage_form_contact_message_form_alter(&$form, &$form_state, $form_id) {
/** @var \Drupal\Core\Entity\ContentEntityForm $form_object */
$form_object = $form_state
->getFormObject();
/* @var \Drupal\contact\MessageInterface $contact_message */
$contact_message = $form_object
->getEntity();
$contact_form = ContactForm::load($contact_message
->bundle());
/** @var \Drupal\Core\Entity\Display\EntityFormDisplayInterface $form_mode */
if ($form_object instanceof MessageForm) {
if ($submit_text = $contact_form
->getThirdPartySetting('contact_storage', 'submit_text', FALSE)) {
$form['actions']['submit']['#value'] = $submit_text;
}
if (!$contact_form
->getThirdPartySetting('contact_storage', 'show_preview', TRUE)) {
$form['actions']['preview']['#access'] = FALSE;
}
}
// Check if the current user has reached the form's maximum submission limit.
$maximum_submissions_user = $contact_form
->getThirdPartySetting('contact_storage', 'maximum_submissions_user', 0);
if ($maximum_submissions_user !== 0 && contact_storage_maximum_submissions_user($contact_form) >= $maximum_submissions_user) {
// Sets the error message.
$form['maximum_submissions_error'] = [
'#type' => 'container',
'#markup' => t('You have reached the maximum submission limit of @limit for this form.', [
'@limit' => $maximum_submissions_user,
]),
'#attributes' => [
'class' => [
'messages',
'messages--error',
],
],
'#weight' => -100,
];
// Remove the submit and preview buttons.
$form['actions']['submit']['#access'] = FALSE;
$form['actions']['preview']['#access'] = FALSE;
}
}
/**
* Implements hook_entity_base_field_info().
*/
function contact_storage_entity_base_field_info(EntityTypeInterface $entity_type) {
if ($entity_type
->id() == 'contact_message') {
$fields = [];
$fields['id'] = BaseFieldDefinition::create('integer')
->setLabel(t('Message ID'))
->setDescription(t('The message ID.'))
->setReadOnly(TRUE)
->setSetting('unsigned', TRUE);
$fields['created'] = BaseFieldDefinition::create('created')
->setLabel(t('Created'))
->setDescription(t('The time that the message was created.'))
->setTranslatable(TRUE)
->setReadOnly(TRUE);
$fields['uid'] = BaseFieldDefinition::create('entity_reference')
->setLabel(t('User ID'))
->setDescription(t('The user ID.'))
->setSetting('target_type', 'contact_form')
->setDefaultValueCallback('contact_storage_contact_message_default_uid');
$fields['ip_address'] = BaseFieldDefinition::create('string')
->setLabel(t('IP address'))
->setDescription(t('The IP address of the submitter.'))
->setDefaultValueCallback('contact_storage_contact_message_default_ip_address');
return $fields;
}
}
/**
* Default value callback for the contact message uid field.
*
* @return int
* The user ID.
*/
function contact_storage_contact_message_default_uid() {
return \Drupal::currentUser()
->id();
}
/**
* Default value callback for the contact message ip_address field.
*
* @return int
* The client IP address.
*/
function contact_storage_contact_message_default_ip_address() {
return \Drupal::request()
->getClientIp();
}
/**
* Implements hook_entity_base_field_info_alter().
*/
function contact_storage_entity_base_field_info_alter(&$fields, EntityTypeInterface $entity_type) {
if ($entity_type
->id() == 'contact_message') {
// Start at min 3 because message default weight is 0.
$i = -3;
foreach ([
'name',
'mail',
'subject',
] as $field_name) {
$fields[$field_name]
->setDisplayConfigurable('view', TRUE);
$fields[$field_name]
->setDisplayOptions('view', [
'weight' => $i,
]);
$i++;
}
// Add a validation constraint to prevent form submission if the limit is
// reached.
$fields['message']
->addConstraint('ConstactStorageMaximumSubmissions', []);
}
}
/**
* Implements hook_entity_operation_alter().
*/
function contact_storage_entity_operation_alter(array &$operations, EntityInterface $entity) {
if ($entity
->getEntityTypeId() == 'contact_message' && $entity
->access('view')) {
$operations['view'] = [
'title' => t('View'),
'weight' => 0,
'url' => $entity
->toUrl('canonical'),
];
}
if ($entity
->getEntityTypeId() == 'contact_form' && $entity
->access('update')) {
$operations['clone'] = [
'title' => t('Clone'),
'weight' => 10,
'url' => $entity
->toUrl('clone-form'),
];
// Provide a link to view messages submitted form the form, if the view
// exists and if the user has access rights to it.
$view = Views::getView('contact_messages');
if ($view && $view
->access('page_1')) {
$view
->setDisplay('page_1');
$operations['view_messages'] = [
'title' => t('View messages'),
'url' => $view
->getUrl()
->setOption('query', [
'form' => $entity
->id(),
]),
];
}
}
}
/**
* Implements hook_entity_type_alter().
*/
function contact_storage_entity_type_alter(array &$entity_types) {
/* @var $entity_types \Drupal\Core\Entity\EntityTypeInterface[] */
// Set the controller class for nodes to an alternate implementation of the
// Drupal\Core\Entity\EntityStorageInterface interface.
$entity_types['contact_message']
->setStorageClass('\\Drupal\\Core\\Entity\\Sql\\SqlContentEntityStorage');
$keys = $entity_types['contact_message']
->getKeys();
$keys['id'] = 'id';
$keys['label'] = 'subject';
$entity_types['contact_message']
->set('entity_keys', $keys);
$entity_types['contact_message']
->set('base_table', 'contact_message');
// Add edit and delete forms.
$entity_types['contact_message']
->setFormClass('edit', '\\Drupal\\contact_storage\\MessageEditForm');
$entity_types['contact_message']
->setFormClass('delete', '\\Drupal\\Core\\Entity\\ContentEntityDeleteForm');
$entity_types['contact_message']
->setFormClass('delete-multiple-confirm', 'Drupal\\Core\\Entity\\Form\\DeleteMultipleForm');
// Add clone form for messages.
$entity_types['contact_form']
->setFormClass('clone', '\\Drupal\\contact_storage\\Form\\ContactFormCloneForm');
// Allow edit/delete links in list builder.
$entity_types['contact_message']
->setLinkTemplate('collection', '/admin/structure/contact/messages');
$entity_types['contact_message']
->setLinkTemplate('canonical', '/admin/structure/contact/messages/{contact_message}');
$entity_types['contact_message']
->setLinkTemplate('edit-form', '/admin/structure/contact/messages/{contact_message}/edit');
$entity_types['contact_message']
->setLinkTemplate('delete-form', '/admin/structure/contact/messages/{contact_message}/delete');
$entity_types['contact_message']
->setLinkTemplate('delete-multiple-form', '/admin/structure/contact/messages/delete');
// Add clone link for forms.
$entity_types['contact_form']
->setLinkTemplate('clone-form', '/admin/structure/contact/manage/{contact_form}/clone');
// Define the entity route provider.
$route_providers = $entity_types['contact_message']
->getRouteProviderClasses();
$route_providers['html'] = '\\Drupal\\contact_storage\\ContactRouteProvider';
$entity_types['contact_message']
->setHandlerClass('route_provider', $route_providers);
$entity_types['contact_form']
->setHandlerClass('route_provider', $route_providers);
// @todo Replace with access control handler when not enough.
$entity_types['contact_message']
->set('admin_permission', 'administer contact forms');
// Integrate with Views.
$entity_types['contact_message']
->setHandlerClass('views_data', '\\Drupal\\contact_storage\\MessageViewsData');
$entity_types['contact_message']
->setListBuilderClass('\\Drupal\\Core\\Entity\\EntityListBuilder');
$entity_types['contact_form']
->setViewBuilderClass('\\Drupal\\contact_storage\\ContactFormViewBuilder');
// If the body of the message should be sent as HTML.
if (\Drupal::config('contact_storage.settings')
->get('send_html')) {
$entity_types['contact_message']
->setViewBuilderClass('Drupal\\contact_storage\\ContactMessageViewBuilder');
}
$keys = $entity_types['contact_form']
->getKeys();
$keys['status'] = 'status';
$entity_types['contact_form']
->set('entity_keys', $keys);
// Handler classes and templates for the Enable and Disable options.
$entity_types['contact_form']
->setFormClass('disable', ContactFormDisableForm::class)
->setFormClass('enable', ContactFormEnableForm::class)
->setLinkTemplate('enable', '/admin/structure/contact/view/{contact_form}/enable')
->setLinkTemplate('disable', '/admin/structure/contact/view/{contact_form}/disable');
}
/**
* Implements hook_entity_extra_field_info().
*/
function contact_storage_entity_extra_field_info() {
$fields = [];
foreach (array_keys(\Drupal::service('entity_type.bundle.info')
->getBundleInfo('contact_message')) as $bundle) {
$fields['contact_message'][$bundle]['form']['preview'] = [
'label' => t('Preview'),
'description' => t('Rendered preview'),
'weight' => 50,
];
}
return $fields;
}
/**
* Implements hook_mail_alter().
*/
function contact_storage_mail_alter(&$message) {
// Check that the message isn't a copy sent to the sender (page_copy).
if ($message['key'] == 'page_mail' && isset($message['params']['contact_message'])) {
$contact_message = $message['params']['contact_message'];
foreach ($contact_message
->getFields() as $field) {
if ($field
->getFieldDefinition()
->getType() === 'contact_storage_options_email') {
// Add recipients to the message from the Option email field.
foreach ($field as $delta => $item) {
$label = $item->value;
// Obtain the email to add to the message, using the label.
$email = $item
->getFieldDefinition()
->getSetting('allowed_values')[$label]['emails'];
$message['to'] .= ',' . $email;
}
}
}
}
if ($message['module'] === 'contact' && isset($message['params']['contact_message'])) {
if ($message['key'] == 'page_autoreply') {
$contact_form = $message['params']['contact_form'];
// Filters the auto-reply message using the chosen format and sets it.
if ($reply = $contact_form
->getReply()) {
$filtered_reply = check_markup($contact_form
->getReply(), $contact_form
->getThirdPartySetting('contact_storage', 'page_autoreply_format', 'plain_text'));
$message['body'] = [
$filtered_reply,
];
}
}
// Enforce that we are sending mails as html, if enabled, and tell
// Swiftmailer to generate a plain text version.
if (\Drupal::config('contact_storage.settings')
->get('send_html')) {
$message['headers']['Content-Type'] = 'text/html';
$message['params']['convert'] = TRUE;
}
}
}
/**
* Implements hook_theme().
*/
function contact_storage_theme() {
return [
'swiftmailer__contact' => [
'variables' => [
'message' => [],
],
],
'contact_storage_disabled_form' => [
'template' => 'contact-storage-disabled-form',
'variables' => [
'contact_form' => NULL,
'disabled_form_message' => '',
],
],
];
}
/**
* Prepares variables for contact mail templates.
*
* @param array $variables
* An associative array containing:
* - message: An associative array containing the message array.
* - body: The processed body.
*/
function template_preprocess_swiftmailer__contact(&$variables) {
$variables['subject'] = $variables['message']['subject'];
$variables['body'] = $variables['message']['body'];
}
/**
* Implements hook_field_formatter_info_alter().
*/
function contact_storage_field_formatter_info_alter(&$info) {
// Let our options_email field type re-use the default list formatter.
$info['list_default']['field_types'][] = 'contact_storage_options_email';
}
/**
* Implements hook_field_widget_info_alter().
*/
function contact_storage_field_widget_info_alter(&$info) {
// Let our options_email field type re-use the default options widget.
$info['options_select']['field_types'][] = 'contact_storage_options_email';
}
/**
* Returns the number of times the current user has submitted the specified
* form.
*
* @param Drupal\contact\ContactFormInterface $contact_form
* The contact_form entity.
*
* @return int
* The number of times the current user has submitted the specified form.
*/
function contact_storage_maximum_submissions_user(ContactFormInterface $contact_form) {
$account = \Drupal::currentUser();
if ($account
->isAnonymous()) {
// Anonymous user, limit per submission with the same IP address.
$ip_address = \Drupal::request()
->getClientIp();
$query = \Drupal::entityQuery('contact_message')
->condition('contact_form', $contact_form
->id())
->condition('ip_address', $ip_address)
->condition('uid', $account
->id());
return count($query
->execute());
}
else {
// Limit per submission with the same uid.
$query = \Drupal::entityQuery('contact_message')
->condition('contact_form', $contact_form
->id())
->condition('uid', $account
->id());
return count($query
->execute());
}
}
/**
* Implements hook_ENTITY_TYPE_delete() for 'contact_form'.
*/
function contact_storage_contact_form_delete(EntityInterface $entity) {
$alias_storage = \Drupal::entityTypeManager()
->getStorage('path_alias');
// Delete all aliases with this contact form as a source.
$aliases = $alias_storage
->loadByProperties([
'path' => '/' . $entity
->toUrl()
->getInternalPath(),
]);
$alias_storage
->delete($aliases);
}
/**
* Implements hook_action_info_alter().
*/
function contact_storage_action_info_alter(array &$definitions) {
// For backwards compatibility, treat 'message_delete_action' as an alias of
// the 'entity:delete_action:contact_message' plugin provided by core.
$definitions['message_delete_action'] = $definitions['entity:delete_action:contact_message'];
}