Commit 6f121b3c by Manzar Hussain

contact storage

parent 615a419f
INTRODUCTION
------------
The Contact Block module provides you with contact forms in a block. It extends
Drupal core's Contact module which provides the forms.
* Visit the module's project page:
https://drupal.org/project/contact_block
REQUIREMENTS
------------
This module requires the following modules:
* Contact module (Drupal core)
INSTALLATION
------------
Install as you would normally install a contributed Drupal module. See:
https://www.drupal.org/documentation/install/modules-themes/modules-8
for further information.
CONFIGURATION
-------------
Create a contact form
- Home > Administration > Structure > Contact forms
- Edit (or delete) the default contact forms. Use Manage fields to add, update
or remove fields of the form.
- Optionally, create a contact form.
Add a Contact block to a block region.
- Home > Administration > Structure > Block layout
- Click 'Place block' of the region you want to place a contact block in.
- Search for 'Contact block' in the listed blocks and click 'Place block'.
- Select the contact form you want to show in this block.
- Save the block.
- Optionally, create another contact block.
The personal contact form is built to be used on a pages that 'know' about the
user. The user 'To' address is determined by using the user ID in the URL. No
personal contact form is displayed if the user ID is not in the URL.
For developers: The personal contact form is only loaded if the path contains
the 'user' placeholder. For example in /user/{user}.
The contact forms of Contact module remain functional at their URL. Use custom
code or an other module to deny access to these pages.
MAINTAINERS
-----------
Current maintainers:
* Erik Stielstra (Sutharsan) https://www.drupal.org/user/73854
This project has been sponsored by:
* Wizzlern, The Drupal trainers
{
"name": "drupal/contact_block",
"description": "The Contact Block module provides contact forms in a block.",
"type": "drupal-module",
"keywords": ["Drupal", "contact_block"],
"homepage": "https://www.drupal.org/project/contact_block",
"support": {
"issues": "https://www.drupal.org/project/issues/contact_block"
},
"license": "GPL-2.0+",
"minimum-stability": "dev",
"require": {
"drupal/core": "^8 || ^9"
}
}
block.settings.contact_block:
type: block_settings
label: 'Contact block'
mapping:
contact_form:
type: string
label: 'Contact form'
name: 'Contact Block'
type: module
description: 'Provides blocks for Contact form module.'
core: 8.x
core_version_requirement: ^8 || ^9
dependencies:
- drupal:block
- drupal:contact
# Information added by Drupal.org packaging script on 2020-04-18
version: '8.x-1.5'
project: 'contact_block'
datestamp: 1587219508
contact_block.contact_form_edit_form:
title: 'Edit contact form'
group: contact_block
route_name: 'entity.contact_form.edit_form'
contact_block.contact_form_manage_fields:
title: 'Manage fields'
group: contact_block
route_name: 'entity.contact_message.field_ui_fields'
<?php
namespace Drupal\contact_block\Plugin\Block;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Block\BlockBase;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Entity\EntityFormBuilderInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Render\RendererInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Routing\CurrentRouteMatch;
use Drupal\contact\Access\ContactPageAccess;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Provides a 'ContactBlock' block.
*
* @Block(
* id = "contact_block",
* admin_label = @Translation("Contact block"),
* )
*/
class ContactBlock extends BlockBase implements ContainerFactoryPluginInterface {
/**
* The EntityTypeManager.
*
* @var \Drupal\Core\Entity\EntityTypeManager
*/
protected $entityTypeManager;
/**
* The ConfigFactory.
*
* @var \Drupal\Core\Config\ConfigFactory
*/
protected $configFactory;
/**
* The EntityFormBuilder.
*
* @var \Drupal\Core\Entity\EntityFormBuilder
*/
protected $entityFormBuilder;
/**
* The Renderer.
*
* @var \Drupal\Core\Render\Renderer
*/
protected $renderer;
/**
* The contact form configuration entity.
*
* @var \Drupal\contact\Entity\ContactForm
*/
protected $contactForm;
/**
* The current route match.
*
* @var \Drupal\Core\Routing\CurrentRouteMatch
*/
protected $routeMatch;
/**
* The access check of personal contact.
*
* @var \Drupal\contact\Access\ContactPageAccess
*/
protected $checkContactPageAccess;
/**
* Constructor for ContactBlock block class.
*
* @param array $configuration
* A configuration array containing information about the plugin instance.
* @param string $plugin_id
* The plugin_id for the plugin instance.
* @param string $plugin_definition
* The plugin implementation definition.
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
* The entity manager.
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
* The config factory.
* @param \Drupal\Core\Entity\EntityFormBuilderInterface $entity_form_builder
* The entity form builder.
* @param \Drupal\Core\Render\RendererInterface $renderer
* The renderer.
* @param \Drupal\Core\Routing\CurrentRouteMatch $route_match
* The route match service.
* @param \Drupal\contact\Access\ContactPageAccess $check_contact_page_access
* Check the access of personal contact.
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entity_type_manager, ConfigFactoryInterface $config_factory, EntityFormBuilderInterface $entity_form_builder, RendererInterface $renderer, CurrentRouteMatch $route_match, ContactPageAccess $check_contact_page_access) {
$this->entityTypeManager = $entity_type_manager;
$this->configFactory = $config_factory;
$this->entityFormBuilder = $entity_form_builder;
$this->renderer = $renderer;
$this->routeMatch = $route_match;
$this->checkContactPageAccess = $check_contact_page_access;
parent::__construct($configuration, $plugin_id, $plugin_definition);
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('entity_type.manager'),
$container->get('config.factory'),
$container->get('entity.form_builder'),
$container->get('renderer'),
$container->get('current_route_match'),
$container->get('access_check.contact_personal')
);
}
/**
* {@inheritdoc}
*/
protected function blockAccess(AccountInterface $account) {
$contact_form = $this->getContactForm();
$contact_message = $this->createContactMessage();
// Deny access when the configured contact form has been deleted.
if (empty($contact_form)) {
return AccessResult::forbidden();
}
if ($contact_message->isPersonal()) {
/** @var \Drupal\user\Entity\User $user */
$user = $this->routeMatch->getParameter('user');
// Deny access to the contact form if we are not on a user related page
// or we have no access to that page.
if (empty($user)) {
return AccessResult::forbidden();
}
// Use the regular personal contact access service to check.
return $this->checkContactPageAccess->access($user, $account);
}
// Access to other contact forms is equal to the permission of the
// entity.contact_form.canonical route.
return $contact_form->access('view', $account, TRUE);
}
/**
* {@inheritdoc}
*/
public function defaultConfiguration() {
$default_form = $this->configFactory->get('contact.settings')->get('default_form');
return [
'label' => $this->t('Contact block'),
'contact_form' => $default_form,
];
}
/**
* {@inheritdoc}
*/
public function blockForm($form, FormStateInterface $form_state) {
$options = $this->entityTypeManager
->getStorage('contact_form')
->loadMultiple();
foreach ($options as $key => $option) {
$options[$key] = $option->label();
}
$form['contact_form'] = [
'#type' => 'select',
'#title' => $this->t('Contact form'),
'#options' => $options,
'#default_value' => $this->configuration['contact_form'],
'#required' => TRUE,
];
return $form;
}
/**
* {@inheritdoc}
*/
public function blockSubmit($form, FormStateInterface $form_state) {
$this->configuration['contact_form'] = $form_state->getValue('contact_form');
}
/**
* {@inheritdoc}
*/
public function build() {
$form = [];
/** @var \Drupal\contact\Entity\ContactForm $contact_form */
$contact_form = $this->getContactForm();
if ($contact_form) {
$contact_message = $this->createContactMessage();
// The personal contact form has a fixed recipient: the user who's
// contact page we visit. We use the 'user' property from the URL
// to determine this user. For example: user/{user}.
if ($contact_message->isPersonal()) {
$user = $this->routeMatch->getParameter('user');
$contact_message->set('recipient', $user);
}
$form = $this->entityFormBuilder->getForm($contact_message);
$form['#cache']['contexts'][] = 'user.permissions';
$this->renderer->addCacheableDependency($form, $contact_form);
$form['#contextual_links']['contact_block'] = [
'route_parameters' => ['contact_form' => $contact_form->id()],
];
}
return $form;
}
/**
* {@inheritdoc}
*/
public function calculateDependencies() {
$dependencies = array_merge_recursive(parent::calculateDependencies(), ['config' => []]);
// Add the contact form as a dependency.
if ($contact_form = $this->getContactForm()) {
$dependencies['config'][] = $contact_form->getConfigDependencyName();
}
return $dependencies;
}
/**
* Loads the contact form entity.
*
* @return \Drupal\contact\Entity\ContactForm|null
* The contact form configuration entity. NULL if the entity does not exist.
*
* @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
* @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
*/
protected function getContactForm() {
if (!isset($this->contactForm)) {
if (isset($this->configuration['contact_form'])) {
$this->contactForm = $this->entityTypeManager
->getStorage('contact_form')
->load($this->configuration['contact_form']);
}
}
return $this->contactForm;
}
/**
* Creates the contact message entity without saving it.
*
* @return \Drupal\contact\Entity\Message|null
* The contact message entity. NULL if the entity does not exist.
*
* @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
* @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
*/
protected function createContactMessage() {
$contact_message = NULL;
$contact_form = $this->getContactForm();
if ($contact_form) {
$contact_message = $this->entityTypeManager
->getStorage('contact_message')
->create(['contact_form' => $contact_form->id()]);
}
return $contact_message;
}
}
PROJECT
-------------
https://www.drupal.org/project/contact_storage
INSTALLATION
-------------
1. Download and extract the module to your sites/all/modules/contrib folder.
2. Enable the module on the Drupal Modules page (admin/modules) or using :
$ drush en
If you want to be able to send messages in HTML format, the Swiftmailer module
is required. To install it, follow the same instructions as above, using the
following module :
https://www.drupal.org/project/swiftmailer
The module administration page is available at : /admin/structure/contact
The module settings page is available at : /admin/structure/contact/settings
Adding a new form can be done at : /admin/structure/contact/add
A listing of sent messages is available at : /admin/structure/contact/messages
INSTRUCTIONS TO ENABLE HTML
-------------
In order to be able to send messages in HTML format, once Swiftmailer module
has been installed and enabled, follow these steps :
1. Enable Mail System and select Swiftmailer as your default mail system.
In "Configuration" -> "Mail System", choose "Swiftmailer" under "Formatter"
and "Sender".
2. HTML should not be enforced and provided e-mail format should be respected.
In "Configuration" -> "Swift Mailer", in the "Messages" tab, select "Plain
Text" under "Message Format" and check the "Respect provided e-mail
format." option.
3. Enable sending messages in HTML format within Contact Storage.
In "Structure" -> "Contact forms", in the "Contact settings" tab, check the
"Send HTML" option.
4. Customize theming.
The Contact Storage module provides a default template,
"swiftmailer--contact.html.twig", in /templates directory. This template
can be changed to conform to your needs.
OVERVIEW
-------------
Contact Storage module will provide storage for Contact messages which are
fully-fledged entities in Drupal 8. This plus core contact module aim to
provide functionality equivalent to the base-features of Webform or Entity
Form. The goal is to firm up this functionality in contrib with view to move
into core in 8.1.x or later.
FEATURES
-------------
Message storage
Edit messages
Admin listing
Views integration
REQUIREMENTS
-------------
Core Contact Module and Swiftmailer module, if sending messages in HTML format
is desired.
CREDITS
-------------
Collaboration between the following developers :
larowlan
jibran
andypost
berdir
Supporting organizations:
PreviousNext (Development time)
MD Systems (Development time)
{
"name": "drupal/contact_storage",
"description": "Provides storage and edit capability for contact messages.",
"type": "drupal-module",
"require": {
"drupal/core": "^8.7.7 || ^9",
"drupal/token": "^1.6"
}
}
langcode: en
status: true
dependencies:
enforced:
module:
- contact_storage
module:
- contact_storage
id: contact_message_delete_action
label: 'Delete contact message'
type: contact_message
plugin: entity:delete_action:contact_message
configuration: { }
contact.form.*.third_party.contact_storage:
type: mapping
label: 'Contact form redirection'
mapping:
redirect_uri:
type: string
label: 'Redirect URI'
submit_text:
type: label
label: 'Submit Text'
show_preview:
type: boolean
label: 'Show preview button'
disabled_form_message:
type: string
label: 'Disabled contact form message'
maximum_submissions_user:
type: integer
label: 'Maximum submission limit per user'
page_autoreply_format:
type: string
label: 'Autoreply format'
field.storage_settings.contact_storage_options_email:
type: mapping
label: 'Options email item settings'
mapping:
allowed_values:
type: sequence
label: 'Allowed values list'
sequence:
type: mapping
label: 'Allowed value with label'
mapping:
value:
type: string
key:
type: string
emails:
type: string
allowed_values_function:
type: string
label: 'Allowed values function'
field.field_settings.contact_storage_options_email:
label: 'Options email item settings'
type: mapping
field.value.contact_storage_options_email:
type: mapping
label: 'Default value'
mapping:
value:
type: string
label: 'Value'
contact_storage.settings:
type: config_object
label: 'Contact Storage settings'
mapping:
send_html:
type: boolean
label: 'Whether the mail should be sent as HTML'
action.configuration.entity:delete_action:contact_message:
type: action_configuration_default
label: 'Delete contact message configuration'
views.field.contact_form:
type: views_field
label: 'Contact form'
views.field.message_bulk_form:
type: views_field_bulk_form
label: 'Contact Message bulk form'
name: 'Contact storage'
type: module
description: 'Provides storage and edit capability for contact messages.'
core_version_requirement: '^8.8 || ^9'
dependencies:
- drupal:contact
- drupal:views
- drupal:options
- drupal:filter
- token:token
configure: entity.contact_message.collection
# Information added by Drupal.org packaging script on 2020-06-10
version: '8.x-1.1'
project: 'contact_storage'
datestamp: 1591811493
<?php
/**
* @file
* Contains install and update hooks.
*/
use Drupal\Core\Entity\Sql\SqlContentEntityStorage;
use Drupal\Core\Url;
use Drupal\Core\Config\InstallStorage;
use Drupal\Core\Config\FileStorage;
use Drupal\Core\Field\BaseFieldDefinition;
/**
* Implements hook_install().
*/
function contact_storage_install() {
$entity_definition_update_manager = \Drupal::entityDefinitionUpdateManager();
$original_contact_message = $entity_definition_update_manager->getEntityType('contact_message');
$original_contact_form = \Drupal::entityTypeManager()->getDefinition('contact_form');
$entity_type_contact_message = clone $original_contact_message;
$entity_definition_update_manager->uninstallEntityType($original_contact_message);
// Update the entity type definition and make it use the default SQL storage.
// @see contact_storage_entity_type_alter()
$entity_types = [
'contact_message' => $entity_type_contact_message,
'contact_form' => $original_contact_form,
];
contact_storage_entity_type_alter($entity_types);
$entity_definition_update_manager->installEntityType($entity_types['contact_message']);
_contact_storage_ensure_fields();
}
/**
* Make sure the fields are added.
*/
function contact_storage_update_8001() {
_contact_storage_ensure_fields();
}
/**
* Ensure fields are added.
*/
function _contact_storage_ensure_fields() {
/** @var \Drupal\Core\Entity\EntityFieldManagerInterface $field_manager */
$field_manager = \Drupal::service('entity_field.manager');
$entity_definition_update_manager = \Drupal::entityDefinitionUpdateManager();
foreach (['id', 'created', 'uid', 'ip_address'] as $field_name) {
$field_definition = $field_manager->getFieldStorageDefinitions('contact_message')[$field_name];
$entity_definition_update_manager->installFieldStorageDefinition($field_name, 'contact_message', 'contact_storage', $field_definition);
}
}
/**
* Save the bulk delete action to config.
*/
function contact_storage_update_8002() {
$entity_type_manager = \Drupal::entityTypeManager();
$module_handler = \Drupal::moduleHandler();
// Save the bulk delete action to config.
$config_install_path = $module_handler->getModule('contact_storage')->getPath() . '/' . InstallStorage::CONFIG_INSTALL_DIRECTORY;
// Create action if it doesn't exist.
$action_storage = $entity_type_manager->getStorage('action');
$action = $action_storage->load('message_delete_action');
if (!$action) {
$storage = new FileStorage($config_install_path);
$entity_type_manager
->getStorage('action')
->create($storage->read('system.action.message_delete_action'))
->save();
}
}
/**
* Defines fields for the user id and ip address, for the contact messages.
*/
function contact_storage_update_8003() {
$storage_definition = 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');
\Drupal::entityDefinitionUpdateManager()
->installFieldStorageDefinition('uid', 'contact_message', 'contact_storage', $storage_definition);
$storage_definition = BaseFieldDefinition::create('string')
->setLabel(t('IP address'))
->setDescription(t('The IP address of the submitter.'))
->setDefaultValueCallback('contact_storage_contact_message_default_ip_address');
\Drupal::entityDefinitionUpdateManager()
->installFieldStorageDefinition('ip_address', 'contact_message', 'contact_storage', $storage_definition);
}
/**
* Updates the redirect paths to the core property redirect path.
*/
function contact_storage_update_8200() {
$config_factory = \Drupal::configFactory();
// Iterate on all text formats config entities.
foreach ($config_factory->listAll('contact.form.') as $name) {
if ($redirect_page = $config_factory->get($name)->get('third_party_settings.contact_storage.redirect_uri')) {
$config = $config_factory->getEditable($name);
$config->clear('third_party_settings.contact_storage.redirect_uri');
try {
$url = '/' . Url::fromUri($redirect_page)->getInternalPath();
}
catch (Exception $e) {
continue;
}
if (!$config->get('redirect')) {
$config->set('redirect', $url);
}
$config->save();
}
}
}
/**
* Enables the options module as it is now a dependency.
*/
function contact_storage_update_8201() {
\Drupal::service('module_installer')->install(['options']);
}
/**
* Fix the last installed definition for the 'contact_message' entity type.
*/
function contact_storage_update_8202() {
$entity_type = \Drupal::entityDefinitionUpdateManager()->getEntityType('contact_message');
$keys = $entity_type->getKeys();
if (empty($keys['langcode'])) {
$keys['langcode'] = 'langcode';
$entity_type->set('entity_keys', $keys);
\Drupal::entityDefinitionUpdateManager()->updateEntityType($entity_type);
}
}
entity.contact_message.forms:
title: 'Forms'
route_name: entity.contact_form.collection
base_route: entity.contact_form.collection
entity.contact_message.collection:
title: 'List'
route_name: entity.contact_message.collection
base_route: entity.contact_form.collection
entity.contact_form.clone_form:
route_name: entity.contact_form.clone_form
base_route: entity.contact_form.edit_form
title: Clone
weight: 20
contact_storage.settings:
title: 'Contact settings'
route_name: contact_storage.settings
base_route: entity.contact_form.collection
<?php
/**
* @file
* Post update functions for Contact Storage.
*/
use Drupal\system\Entity\Action;
/**
* Renames the "message_delete" action to avoid Message module conflicts.
*/
function contact_storage_post_update_rename_message_delete_action() {
$action = Action::load('message_delete_action');
if ($action) {
$action->set('id', 'contact_message_delete_action')
->setPlugin('entity:delete_action:contact_message');
$action->save();
}
}
contact_storage.settings:
path: '/admin/structure/contact/settings'
defaults:
_form: '\Drupal\contact_storage\Form\ContactStorageSettingsForm'
_title: 'Contact settings'
requirements:
_permission: 'administer contact forms'
entity.contact_form.disable:
path: '/admin/structure/contact/manage/{contact_form}/disable'
defaults:
_entity_form: 'contact_form.disable'
_title: 'Disable contact form'
requirements:
_entity_access: 'contact_form.disable'
entity.contact_form.enable:
path: '/admin/structure/contact/manage/{contact_form}/enable'
defaults:
_entity_form: 'contact_form.enable'
_title: 'Enable contact form'
requirements:
_entity_access: 'contact_form.enable'
entity.contact.multiple_delete_confirm:
path: '/admin/structure/contact/messages/delete'
defaults:
_form: 'Drupal\Core\Entity\Form\DeleteMultipleForm'
entity_type_id: 'contact_message'
requirements:
_permission: 'administer contact forms'
services:
contact_storage.settings_form_save:
class: \Drupal\contact_storage\EventSubscriber\ContactStorageSettingsFormSave
tags:
- { name: event_subscriber }
contact_storage.route_subscriber:
class: Drupal\contact_storage\Routing\RouteSubscriber
tags:
- { name: event_subscriber }
<?php
namespace Drupal\contact_storage;
use Drupal\Core\Config\Config;
use Drupal\Core\Entity\EntityFormBuilderInterface;
use Drupal\Core\Entity\EntityHandlerInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Entity\EntityViewBuilderInterface;
use Drupal\Core\Field\FieldItemInterface;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Render\RendererInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Provides a contact form view builder.
*
* @see \Drupal\contact\Entity\ContactForm
*/
class ContactFormViewBuilder implements EntityViewBuilderInterface, EntityHandlerInterface {
/**
* The entity form builder.
*
* @var \Drupal\Core\Entity\EntityFormBuilderInterface
*/
protected $entityFormBuilder;
/**
* The renderer.
*
* @var \Drupal\Core\Render\RendererInterface
*/
protected $renderer;
/**
* The contact settings config object.
*
* @var \Drupal\Core\Config\Config
*/
protected $config;
/**
* The contact message storage.
*
* @var \Drupal\Core\Entity\EntityStorageInterface
*/
protected $contactMessageStorage;
/**
* Constructs a new contact form view builder.
*
* @param \Drupal\Core\Entity\EntityFormBuilderInterface $entity_form_builder
* The entity form builder service.
* @param \Drupal\Core\Render\RendererInterface $renderer
* The renderer service.
* @param \Drupal\Core\Config\Config $config
* The contact settings config object.
* @param \Drupal\Core\Entity\EntityStorageInterface $contact_message_storage
* The contact message storage.
*/
public function __construct(EntityFormBuilderInterface $entity_form_builder, RendererInterface $renderer, Config $config, EntityStorageInterface $contact_message_storage) {
$this->entityFormBuilder = $entity_form_builder;
$this->renderer = $renderer;
$this->config = $config;
$this->contactMessageStorage = $contact_message_storage;
}
/**
* {@inheritdoc}
*/
public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) {
return new static(
$container->get('entity.form_builder'),
$container->get('renderer'),
$container->get('config.factory')->get('contact.settings'),
$container->get('entity_type.manager')->getStorage('contact_message')
);
}
/**
* {@inheritdoc}
*/
public function view(EntityInterface $entity, $view_mode = 'full', $langcode = NULL) {
if ($entity->status()) {
$message = $this->contactMessageStorage->create([
'contact_form' => $entity->id(),
]);
$form = $this->entityFormBuilder->getForm($message);
$form['#title'] = $entity->label();
$form['#cache']['contexts'][] = 'user.permissions';
$this->renderer->addCacheableDependency($form, $this->config);
}
else {
// Form disabled, display a custom message using a template.
$form['disabled_form_error'] = [
'#theme' => 'contact_storage_disabled_form',
'#contact_form' => $entity,
'#redirect_uri' => $entity->getThirdPartySetting('contact_storage', 'redirect_uri', ''),
'#disabled_form_message' => $entity->getThirdPartySetting('contact_storage', 'disabled_form_message', t('This contact form has been disabled.')),
];
}
// Add required cacheability metadata from the contact form entity, so that
// changing it invalidates the cache.
$this->renderer->addCacheableDependency($form, $entity);
return $form;
}
/**
* {@inheritdoc}
*/
public function viewMultiple(array $entities = [], $view_mode = 'full', $langcode = NULL) {
$build = [];
foreach ($entities as $key => $entity) {
$build[$key] = $this->view($entity, $view_mode, $langcode);
}
return $build;
}
/**
* {@inheritdoc}
*/
public function resetCache(array $entities = NULL) {
// Intentionally empty.
}
/**
* {@inheritdoc}
*/
public function getCacheTags() {
// Intentionally empty.
}
/**
* {@inheritdoc}
*/
public function buildComponents(array &$build, array $entities, array $displays, $view_mode) {
throw new \LogicException();
}
/**
* {@inheritdoc}
*/
public function viewField(FieldItemListInterface $items, $display_options = []) {
throw new \LogicException();
}
/**
* {@inheritdoc}
*/
public function viewFieldItem(FieldItemInterface $item, $display_options = []) {
throw new \LogicException();
}
}
<?php
namespace Drupal\contact_storage;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityViewBuilder;
/**
* Customized contact message view that does not do HTML to plain conversion.
*
* Also relies on standard field formatters to build the message. Does not
* extend from MessageViewBuilder to avoid running that code.
*/
class ContactMessageViewBuilder extends EntityViewBuilder {
/**
* {@inheritdoc}
*/
protected function getBuildDefaults(EntityInterface $entity, $view_mode) {
$build = parent::getBuildDefaults($entity, $view_mode);
// The message fields are individually rendered into email templates, so
// the entity has no template itself.
// @todo Remove this when providing a template in
// https://www.drupal.org/node/2722501.
unset($build['#theme']);
return $build;
}
}
<?php
namespace Drupal\contact_storage;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Entity\Routing\DefaultHtmlRouteProvider;
use Symfony\Component\Routing\Route;
/**
* Provides routes for contact messages and contact forms.
*/
class ContactRouteProvider extends DefaultHtmlRouteProvider {
/**
* {@inheritdoc}
*/
public function getRoutes(EntityTypeInterface $entity_type) {
$route_collection = parent::getRoutes($entity_type);
if ($entity_type->hasLinkTemplate('collection')) {
$route = (new Route($entity_type->getLinkTemplate('collection')))
->addDefaults([
'_entity_list' => 'contact_message',
'_title' => 'Contact messages',
])
->addRequirements([
'_permission' => 'administer contact forms',
]);
$route_collection->add('entity.' . $entity_type->id() . '.collection', $route);
}
if ($entity_type->hasLinkTemplate('clone-form')) {
$route = (new Route($entity_type->getLinkTemplate('clone-form')))
->addDefaults([
'_entity_form' => 'contact_form.clone',
'_title' => 'Clone form',
])
->addRequirements([
'_entity_access' => 'contact_form.clone',
]);
$route_collection->add('entity.' . $entity_type->id() . '.clone_form', $route);
}
return $route_collection;
}
}
<?php
namespace Drupal\contact_storage\Controller;
use Drupal\contact\ContactFormInterface;
use Drupal\contact\Controller\ContactController;
use Drupal\Core\Url;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/**
* Controller routines for contact storage routes.
*/
class ContactStorageController extends ContactController {
/**
* {@inheritdoc}
*/
public function contactSitePage(ContactFormInterface $contact_form = NULL) {
// This is an override of ContactController::contactSitePage() that uses
// the entity view builder. This is necessary to show the close message for
// disabled forms.
$config = $this->config('contact.settings');
// Use the default form if no form has been passed.
$manager = $this->entityTypeManager();
if (empty($contact_form)) {
$contact_form = $manager
->getStorage('contact_form')
->load($config->get('default_form'));
// If there are no forms, do not display the form.
if (empty($contact_form)) {
if ($this->currentUser()->hasPermission('administer contact forms')) {
$this->messenger()->addError($this->t('The contact form has not been configured. <a href=":add">Add one or more forms</a> .', [
':add' => Url::fromRoute('contact.form_add')->toString(),
]));
return [];
}
else {
throw new NotFoundHttpException();
}
}
}
$view_builder = $manager->getViewBuilder('contact_form');
return $view_builder->view($contact_form, 'full', $contact_form->language());
}
/**
* Route title callback.
*
* @param \Drupal\contact\ContactFormInterface $contact_form
* The contact form.
*
* @return string
* The title of the contact form.
*/
public function contactFormTitle(ContactFormInterface $contact_form) {
return $contact_form->label();
}
/**
* Edit route title callback.
*
* @param \Drupal\contact\ContactFormInterface $contact_form
* The contact form.
*
* @return string
* The title of the contact form.
*/
public function contactEditFormTitle(ContactFormInterface $contact_form) {
return $this->t('Edit @label', ['@label' => $contact_form->label()]);
}
}
<?php
namespace Drupal\contact_storage\EventSubscriber;
use Drupal\Core\Config\ConfigCrudEvent;
use Drupal\Core\Config\ConfigEvents;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
/**
* Invalidates the entity type definition cache when the settings are changed.
*/
class ContactStorageSettingsFormSave implements EventSubscriberInterface {
/**
* Invalidates the entity type definition cache whenever settings are changed.
*
* @param \Drupal\Core\Config\ConfigCrudEvent $event
* The Event to process.
*/
public function onSave(ConfigCrudEvent $event) {
if ($event->getConfig()->getName() === 'contact_storage.settings') {
\Drupal::entityTypeManager()->clearCachedDefinitions();
}
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents() {
$events[ConfigEvents::SAVE][] = ['onSave'];
return $events;
}
}
<?php
namespace Drupal\contact_storage\Form;
use Drupal\contact\ContactFormEditForm;
use Drupal\Core\Entity\Entity\EntityFormDisplay;
use Drupal\Core\Entity\Entity\EntityViewDisplay;
use Drupal\Core\Entity\EntityFieldManagerInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Field\BaseFieldDefinition;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Path\PathValidatorInterface;
use Drupal\field\Entity\FieldConfig;
use Egulias\EmailValidator\EmailValidator;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Defines a class for cloning a contact form.
*/
class ContactFormCloneForm extends ContactFormEditForm {
/**
* Entity Field manager service.
*
* @var \Drupal\Core\Entity\EntityFieldManagerInterface
*/
protected $fieldManager;
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('email.validator'),
$container->get('path.validator'),
$container->get('entity_field.manager')
);
}
/**
* Constructs a new ContactFormCloneForm object.
*
* @param \Egulias\EmailValidator\EmailValidator $email_validator
* Email validator.
* @param \Drupal\Core\Path\PathValidatorInterface $path_validator
* The path validator.
* @param \Drupal\Core\Entity\EntityFieldManagerInterface $field_manager
* Entity field manager.
*/
public function __construct(EmailValidator $email_validator, PathValidatorInterface $path_validator, EntityFieldManagerInterface $field_manager) {
parent::__construct($email_validator, $path_validator);
$this->fieldManager = $field_manager;
}
/**
* {@inheritdoc}
*/
public function form(array $form, FormStateInterface $form_state) {
// Add #process and #after_build callbacks.
$form['#process'][] = '::processForm';
$form['#after_build'][] = '::afterBuild';
$form['label'] = [
'#type' => 'textfield',
'#title' => $this->t('Label'),
'#maxlength' => 255,
'#default_value' => '',
'#description' => $this->t("Example: 'website feedback' or 'product information'."),
'#required' => TRUE,
];
$form['id'] = [
'#type' => 'machine_name',
'#default_value' => '',
'#maxlength' => EntityTypeInterface::BUNDLE_MAX_LENGTH,
'#machine_name' => [
'exists' => '\Drupal\contact\Entity\ContactForm::load',
],
];
return $form;
}
/**
* {@inheritdoc}
*/
public function validateForm(array &$form, FormStateInterface $form_state) {
}
/**
* {@inheritdoc}
*/
public function save(array $form, FormStateInterface $form_state) {
/** @var \Drupal\contact\ContactFormInterface $contact_form */
$contact_form = $this->entity;
// Get the original ID.
$original_id = $contact_form->getOriginalId();
$new_id = $contact_form->id();
// Create the new form.
$contact_form = $contact_form->createDuplicate();
$contact_form->set('id', $new_id);
$contact_form->save();
// Clone configurable fields.
foreach ($this->fieldManager->getFieldDefinitions('contact_message', $original_id) as $field) {
if ($field instanceof BaseFieldDefinition) {
continue;
}
if ($this->moduleHandler->moduleExists('field')) {
if ($config = $field->getConfig($original_id)) {
$new_config = FieldConfig::create([
'bundle' => $contact_form->id(),
'uuid' => NULL,
] + $config->toArray());
$new_config->save();
}
}
}
// Clone the entity form display.
$display = EntityFormDisplay::load('contact_message.' . $original_id . '.default');
EntityFormDisplay::create([
'bundle' => $contact_form->id(),
'uuid' => NULL,
] + $display->toArray())->save();
// Clone the entity view display.
$display = EntityViewDisplay::load('contact_message.' . $original_id . '.default');
EntityViewDisplay::create([
'bundle' => $contact_form->id(),
'uuid' => NULL,
] + $display->toArray())->save();
// Redirect and show messge.
$form_state->setRedirect('entity.contact_form.edit_form', ['contact_form' => $contact_form->id()]);
$edit_link = $this->entity->toLink($this->t('Edit'))->toString();
$this->messenger()->addStatus($this->t('Contact form %label has been added.', ['%label' => $contact_form->label()]));
$this->logger('contact')->notice('Contact form %label has been added.', ['%label' => $contact_form->label(), 'link' => $edit_link]);
}
/**
* {@inheritdoc}
*/
protected function actions(array $form, FormStateInterface $form_state) {
$actions = parent::actions($form, $form_state);
$actions['submit']['#value'] = $this->t('Clone');
return $actions;
}
}
<?php
namespace Drupal\contact_storage\Form;
use Drupal\Core\Entity\EntityConfirmFormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Url;
/**
* Provides the contact form disable form.
*/
class ContactFormDisableForm extends EntityConfirmFormBase {
/**
* {@inheritdoc}
*/
public function getQuestion() {
return $this->t('Are you sure you want to disable the contact form %form?', ['%form' => $this->entity->label()]);
}
/**
* {@inheritdoc}
*/
public function getCancelUrl() {
return new Url('entity.contact_form.collection');
}
/**
* {@inheritdoc}
*/
public function getConfirmText() {
return $this->t('Disable');
}
/**
* {@inheritdoc}
*/
public function getDescription() {
return $this->t('Disabled contact forms are not displayed. This action can be undone from the contact forms administration page.');
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state) {
$form = parent::buildForm($form, $form_state);
$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. It will be saved when clicking "Disable".'),
'#default_value' => $this->getEntity()->getThirdPartySetting('contact_storage', 'disabled_form_message', $this->t('This contact form has been disabled.')),
];
return $form;
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
// Save the default disabled form message.
$this->entity->setThirdPartySetting('contact_storage', 'disabled_form_message', $form_state->getValue('contact_storage_disabled_form_message'));
$this->entity->disable()->save();
$this->messenger()->addStatus($this->t('Disabled contact form %form.', ['%form' => $this->entity->label()]));
$form_state->setRedirectUrl($this->getCancelUrl());
}
}
<?php
namespace Drupal\contact_storage\Form;
use Drupal\Core\Entity\EntityConfirmFormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Url;
/**
* Provides the contact form enable form.
*/
class ContactFormEnableForm extends EntityConfirmFormBase {
/**
* {@inheritdoc}
*/
public function getQuestion() {
return $this->t('Are you sure you want to enable the contact form %form?', ['%form' => $this->entity->label()]);
}
/**
* {@inheritdoc}
*/
public function getCancelUrl() {
return new Url('entity.contact_form.collection');
}
/**
* {@inheritdoc}
*/
public function getConfirmText() {
return $this->t('Enable');
}
/**
* {@inheritdoc}
*/
public function getDescription() {
return $this->t('Enabling the contact form will make it visible. This action can be undone from the contact forms administration page.');
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
$this->entity->enable()->save();
$this->messenger()->addStatus($this->t('Enabled contact form %form.', ['%form' => $this->entity->label()]));
$form_state->setRedirectUrl($this->getCancelUrl());
}
}
<?php
namespace Drupal\contact_storage\Form;
use Drupal\Core\Form\ConfigFormBase;
use Drupal\Core\Form\FormStateInterface;
/**
* Defines a class for contact_storage's settings form.
*/
class ContactStorageSettingsForm extends ConfigFormBase {
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'contact_storage_settings';
}
/**
* {@inheritdoc}
*/
protected function getEditableConfigNames() {
return [
'contact_storage.settings',
];
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state) {
$config = $this->config('contact_storage.settings');
// Global setting.
$form['send_html'] = [
'#type' => 'checkbox',
'#title' => t('Send HTML'),
'#description' => t('Whether the mails should be sent as HTML. A module like <a href="https://www.drupal.org/project/swiftmailer">Swiftmailer</a> is needed for this feature. This has only been tested with the Swiftmailer module, other modules might not work out of the box and will not use the provided default template.'),
'#default_value' => $config->get('send_html'),
];
return parent::buildForm($form, $form_state);
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
parent::submitForm($form, $form_state);
$this->config('contact_storage.settings')
->set('send_html', $form_state->getValue('send_html'))
->save();
}
}
<?php
namespace Drupal\contact_storage;
use Drupal\Core\Entity\ContentEntityForm;
use Drupal\Core\Form\FormStateInterface;
/**
* Form controller for contact message edit forms.
*/
class MessageEditForm extends ContentEntityForm {
/**
* {@inheritdoc}
*/
public function form(array $form, FormStateInterface $form_state) {
/** @var \Drupal\contact\MessageInterface $message */
$message = $this->entity;
$form = parent::form($form, $form_state);
$form['name'] = [
'#type' => 'textfield',
'#title' => $this->t('Author name'),
'#maxlength' => 255,
'#default_value' => $message->getSenderName(),
];
$form['mail'] = [
'#type' => 'email',
'#title' => $this->t('Sender email address'),
'#default_value' => $message->getSenderMail(),
];
return $form;
}
/**
* {@inheritdoc}
*/
public function save(array $form, FormStateInterface $form_state) {
$this->entity->save();
$this->logger('contact')->notice('The contact message %subject has been updated.', [
'%subject' => $this->entity->getSubject(),
'link' => $this->getEntity()->toLink($this->t('Edit'), 'edit-form')->toString(),
]);
}
}
<?php
namespace Drupal\contact_storage;
use Drupal\views\EntityViewsData;
/**
* Provides data to integrate messages with Views.
*/
class MessageViewsData extends EntityViewsData {
/**
* {@inheritdoc}
*/
public function getViewsData() {
$data = parent::getViewsData();
$data['contact_message']['contact_form_label'] = [
'title' => $this->t('Form'),
'help' => $this->t('The label of the associated form.'),
'real field' => 'contact_form',
'field' => [
'id' => 'contact_form',
],
];
$data['contact_message']['message_bulk_form'] = [
'title' => $this->t('Message operations bulk form'),
'help' => $this->t('Add a form element that lets you run operations on multiple messages.'),
'field' => [
'id' => 'message_bulk_form',
],
];
return $data;
}
}
<?php
namespace Drupal\contact_storage\Plugin\Field\FieldType;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\options\Plugin\Field\FieldType\ListItemBase;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\Core\TypedData\DataDefinition;
use Drupal\Core\Session\AccountInterface;
/**
* Plugin to add the Option email item custom field type.
*
* @FieldType(
* id = "contact_storage_options_email",
* label = @Translation("Options email"),
* description = @Translation("Stores e-mail recipients for the provided options."),
* default_widget = "options_select",
* default_formatter = "list_default"
* )
*/
class OptionsEmailItem extends ListItemBase {
/**
* {@inheritdoc}
*/
public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
$properties['value'] = DataDefinition::create('string')
->setLabel(new TranslatableMarkup('Text value'))
->addConstraint('Length', ['max' => 255])
->setRequired(TRUE);
return $properties;
}
/**
* {@inheritdoc}
*/
public static function schema(FieldStorageDefinitionInterface $field_definition) {
return [
'columns' => [
'value' => [
'type' => 'varchar',
'length' => 255,
],
],
'indexes' => [
'value' => ['value'],
],
];
}
/**
* {@inheritdoc}
*/
protected function allowedValuesDescription() {
$description = '<p>' . $this->t('The possible values this field can contain. Enter one value per line, in the format key|label|emails.');
$description .= '<br/>' . $this->t('"key" is the message that is added to the body of the message.');
$description .= '<br/>' . $this->t('"label" is the value displayed in the dropdown menu on the contact form.');
$description .= '<br/>' . $this->t('"emails" are the email addresses to add to the recipients list (each separated by a comma).');
$description .= '</p>';
$description .= '<p>' . $this->t('Allowed HTML tags in labels: @tags', ['@tags' => $this->displayAllowedTags()]) . '</p>';
return $description;
}
/**
* {@inheritdoc}
*/
protected static function extractAllowedValues($string, $has_data) {
$values = [];
// Explode the content of the text area per line.
$list = explode("\n", $string);
$list = array_map('trim', $list);
$list = array_filter($list, 'strlen');
foreach ($list as $text) {
// Explode each line around the pipe symbol.
$elements = explode('|', $text);
// Expects 3 elements (value, label and emails).
if (count($elements) == 3) {
// Sanitize the email address.
if (\Drupal::service('email.validator')->isValid($elements[2])) {
$values[$elements[0]] = [
'value' => $elements[1],
'emails' => $elements[2],
];
continue;
}
}
// Failed at some point. Returns NULL to display an error.
return;
}
if (empty($values)) {
return;
}
return $values;
}
/**
* {@inheritdoc}
*/
protected static function simplifyAllowedValues(array $structured_values) {
$values = [];
foreach ($structured_values as $value) {
$values[$value['key']] = [
'value' => $value['value'],
'emails' => $value['emails'],
];
}
return $values;
}
/**
* {@inheritdoc}
*/
protected static function structureAllowedValues(array $values) {
$structured_values = [];
foreach ($values as $key => $value) {
$structured_values[] = [
'key' => $key,
'value' => $value['value'],
'emails' => $value['emails'],
];
}
return $structured_values;
}
/**
* {@inheritdoc}
*/
public function getSettableOptions(AccountInterface $account = NULL) {
$allowed_options_keys = [];
$allowed_options = $this->getOptionsAllowedValues();
// Each option is currently an array containing the value and emails, keyed
// with the key defined by the user. Remove the array to keep only the key.
foreach ($allowed_options as $key => $option) {
$allowed_options_keys[$key] = $key;
}
return $allowed_options_keys;
}
/**
* Returns the array of allowed values for the Options email field.
*
* @return array
* An array of allowed values entered by the user, for the Options email
* field.
*/
protected function getOptionsAllowedValues() {
return options_allowed_values($this->getFieldDefinition()->getFieldStorageDefinition(), $this->getEntity());
}
/**
* {@inheritdoc}
*/
protected function allowedValuesString($values) {
$lines = [];
foreach ($values as $key => $value) {
$lines[] = $key . '|' . $value['value'] . '|' . $value['emails'];
}
return implode("\n", $lines);
}
}
<?php
namespace Drupal\contact_storage\Plugin\Mail;
use Drupal\Core\Mail\MailFormatHelper;
use Drupal\Core\Mail\MailInterface;
/**
* A mail sending implementation that captures sent messages to a variable.
*
* This class is for running tests or for development and does not convert HTML
* to plaintext.
*
* @Mail(
* id = "test_contact_storage_html_mail",
* label = @Translation("HTML test mailer"),
* )
*/
class HTMLTestingMailSystem implements MailInterface {
/**
* Implements MailSystemInterface::format().
*/
public function format(array $message) {
// Join the body array into one string.
$message['body'] = implode("\n\n", $message['body']);
// Wrap the mail body for sending.
$message['body'] = MailFormatHelper::wrapMail($message['body']);
return $message;
}
/**
* Implements MailSystemInterface::mail().
*/
public function mail(array $message) {
$captured_emails = \Drupal::state()->get('system.test_mail_collector') ?: [];
$captured_emails[] = $message;
\Drupal::state()->set('system.test_mail_collector', $captured_emails);
return TRUE;
}
}
<?php
namespace Drupal\contact_storage\Plugin\Validation\Constraint;
use Symfony\Component\Validator\Constraint;
/**
* Verify that the form has not been submitted more times that the limit.
*
* @Constraint(
* id = "ConstactStorageMaximumSubmissions",
* label = @Translation("Maximum submission limit", context = "Validation"),
* )
*/
class ConstactStorageMaximumSubmissionsConstraint extends Constraint {
/**
* Message shown when the maximum submission limit has been reached.
*
* @var string
*/
public $limitReached = 'You have reached the maximum submission limit of @limit for this form.';
}
<?php
namespace Drupal\contact_storage\Plugin\Validation\Constraint;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
/**
* Validates the maximum submission limit constraint.
*/
class ConstactStorageMaximumSubmissionsConstraintValidator extends ConstraintValidator {
/**
* {@inheritdoc}
*/
public function validate($entity, Constraint $constraint) {
// Check if the current user has reached the form's maximum submission limit.
$contact_form = $entity->getParent()->get('contact_form')->referencedEntities()[0];
$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) {
// Limit reached; can't submit the form.
$this->context->addViolation($constraint->limitReached, ['@limit' => $maximum_submissions_user]);
}
}
}
<?php
namespace Drupal\contact_storage\Plugin\views\field;
use Drupal\Core\Config\Entity\ConfigEntityStorageInterface;
use Drupal\views\Plugin\views\field\FieldPluginBase;
use Drupal\views\ResultRow;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Field handler to provide the label of a contact form.
*
* @ingroup views_field_handlers
*
* @ViewsField("contact_form")
*/
class ContactForm extends FieldPluginBase {
/**
* The storage controller for contact forms.
*
* @var \Drupal\Core\Config\Entity\ConfigEntityStorageInterface
*/
protected $formStorage;
/**
* Constructs a ContactForm Views field object.
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, ConfigEntityStorageInterface $form_storage) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->formStorage = $form_storage;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static($configuration, $plugin_id, $plugin_definition, $container->get('entity_type.manager')->getStorage('contact_form'));
}
/**
* Render form label.
*/
protected function renderName($form_id, $values) {
if ($form_id !== NULL && $form_id !== '') {
$type = $this->formStorage->load($form_id);
return $type ? $this->sanitizeValue($type->label()) : '';
}
return $this->sanitizeValue($form_id);
}
/**
* {@inheritdoc}
*/
public function render(ResultRow $values) {
$value = $this->getValue($values);
return $this->renderName($value, $values);
}
}
<?php
namespace Drupal\contact_storage\Plugin\views\field;
use Drupal\views\Plugin\views\field\BulkForm;
/**
* Defines a contact message operations bulk form element.
*
* @ViewsField("message_bulk_form")
*/
class MessageBulkForm extends BulkForm {
/**
* {@inheritdoc}
*/
protected function emptySelectedMessage() {
return $this->t('No message selected.');
}
}
<?php
namespace Drupal\contact_storage\Routing;
use Drupal\Core\Routing\RouteSubscriberBase;
use Symfony\Component\Routing\RouteCollection;
use \Drupal\contact_storage\Controller\ContactStorageController;
/**
* Listens to the dynamic route events.
*/
class RouteSubscriber extends RouteSubscriberBase {
/**
* {@inheritdoc}
*/
public function alterRoutes(RouteCollection $collection) {
// Change the contact_form controller.
if ($route = $collection->get('entity.contact_form.canonical')) {
$route->setDefault('_controller', ContactStorageController::class . '::contactSitePage');
$route->setDefault('_title_callback', ContactStorageController::class . '::contactFormTitle');
}
if ($route = $collection->get('entity.contact_form.edit_form')) {
$route->setDefault('_title_callback', ContactStorageController::class . '::contactEditFormTitle');
}
}
}
{#
/**
* @file
* The template file for a disabled contact form.
*
* Template used to display a message when a contact form is disabled. The
* provided example displays a standard error message showing the message
* defined when creating or disabling the form, for the Bartik theme.
*
* Available variables:
* - contact_message: The contact message entity. Some useful methods available
* are :
* - contact_form.getRecipients will return the list of recipients.
* - contact_form.label will return the label of the contact form.
* See \Drupal\contact\Entity\ContactForm for a full list of public
* properties and methods for the contact form object. Other available
* variables :
* - redirect_uri : The redirect URI of the contact form, an empty string if
* not defined.
* - disabled_form_message : The disabled form message, configured when
* creating or disabling the form. Bu default "This contact form has been
* disabled.".
*/
#}
<div class="messages messages--error">
{{ disabled_form_message }}
</div>
{#
/**
* @file
* The template file for contact_storage e-mails.
*
* The table / HTML structure is taken from swiftmailer's default template file
* for e-mails, only the CSS has been added.
*/
#}
<style type="text/css">
table tr td {
font-family: Arial;
font-size: 12px;
}
.form-item, .field--label-inline, .field--label-above {
margin-top: 10px;
}
.label, label {
font-weight: bold;
}
label {
display: block;
}
.field--label-inline .label {
float:left; /*LTR*/
margin-right: 0.5em; /*LTR*/
}
.field--label-inline .label::after {
content: ':';
}
.clearfix {
clear: both;
}
</style>
<div>
<table width="800px" cellpadding="0" cellspacing="0">
<tr>
<td>
<div style="padding: 0px 0px 0px 0px;">
{{ body }}
</div>
</td>
</tr>
</table>
</div>
name: 'Contact test views'
type: module
description: 'Provides default views for views contact tests.'
package: Testing
core_version_requirement: '^8.8 || ^9'
dependencies:
- contact_storage:contact_storage
- drupal:language
# Information added by Drupal.org packaging script on 2020-06-10
version: '8.x-1.1'
project: 'contact_storage'
datestamp: 1591811493
langcode: en
status: true
dependencies:
module:
- contact
- contact_storage
id: test_contact_message_bulk_form
label: ''
module: views
description: ''
tag: ''
base_table: contact_message
base_field: id
core: 8.x
display:
default:
display_plugin: default
id: default
display_title: Master
position: null
display_options:
style:
type: table
row:
type: fields
fields:
message_bulk_form:
id: message_bulk_form
table: contact_message
field: message_bulk_form
relationship: none
group_type: group
admin_label: ''
label: 'Message operations bulk form'
exclude: false
alter:
alter_text: false
text: ''
make_link: false
path: ''
absolute: false
external: false
replace_spaces: false
path_case: none
trim_whitespace: false
alt: ''
rel: ''
link_class: ''
prefix: ''
suffix: ''
target: ''
nl2br: false
max_length: 0
word_boundary: true
ellipsis: true
more_link: false
more_link_text: ''
more_link_path: ''
strip_tags: false
trim: false
preserve_tags: ''
html: false
element_type: ''
element_class: ''
element_label_type: ''
element_label_class: ''
element_label_colon: true
element_wrapper_type: ''
element_wrapper_class: ''
element_default_classes: true
empty: ''
hide_empty: false
empty_zero: false
hide_alter_empty: true
action_title: 'With selection'
include_exclude: exclude
selected_actions: { }
entity_type: contact_message
plugin_id: message_bulk_form
subject:
table: contact_message
field: subject
id: subject
entity_type: null
entity_field: subject
plugin_id: field
relationship: none
group_type: group
admin_label: ''
label: Subject
exclude: false
alter:
alter_text: false
text: ''
make_link: false
path: ''
absolute: false
external: false
replace_spaces: false
path_case: none
trim_whitespace: false
alt: ''
rel: ''
link_class: ''
prefix: ''
suffix: ''
target: ''
nl2br: false
max_length: 0
word_boundary: true
ellipsis: true
more_link: false
more_link_text: ''
more_link_path: ''
strip_tags: false
trim: false
preserve_tags: ''
html: false
element_type: ''
element_class: ''
element_label_type: ''
element_label_class: ''
element_label_colon: true
element_wrapper_type: ''
element_wrapper_class: ''
element_default_classes: true
empty: ''
hide_empty: false
empty_zero: false
hide_alter_empty: true
click_sort_column: value
type: string
settings: { }
group_column: value
group_columns: { }
group_rows: true
delta_limit: 0
delta_offset: 0
delta_reversed: false
delta_first_last: false
multi_type: separator
separator: ', '
field_api_classes: false
filters: { }
sorts:
id:
id: id
table: contact_message
field: id
relationship: none
group_type: group
admin_label: ''
order: ASC
exposed: false
expose:
label: ''
entity_type: contact_message
entity_field: id
plugin_id: standard
title: ''
header: { }
footer: { }
empty: { }
relationships: { }
arguments: { }
display_extenders: { }
cache_metadata:
max-age: 0
contexts:
- 'languages:language_content'
- 'languages:language_interface'
- url.query_args
tags: { }
page_1:
display_plugin: page
id: page_1
display_title: Page
position: 1
display_options:
display_extenders: { }
path: test-contact-message-bulk-form
cache_metadata:
max-age: 0
contexts:
- 'languages:language_content'
- 'languages:language_interface'
- url.query_args
tags: { }
<?php
namespace Drupal\Tests\contact_storage\Functional;
use Drupal\Core\Session\AccountInterface;
use Drupal\views\Tests\ViewTestData;
/**
* Tests a contact message bulk form.
*
* @group contact_storage
* @see \Drupal\contact_storage\Plugin\views\field\MessageBulkForm
*/
class BulkFormTest extends ContactStorageTestBase {
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* Modules to be enabled.
*
* @var array
*/
public static $modules = [
'contact_storage',
'contact_test_views',
'language',
];
/**
* Views used by this test.
*
* @var array
*/
public static $testViews = ['test_contact_message_bulk_form'];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::Setup();
// Create and login administrative user.
$admin_user = $this->drupalCreateUser([
'administer contact forms',
]);
$this->drupalLogin($admin_user);
// Create first valid contact form.
$mail = 'simpletest@example.com';
$this->addContactForm('test_id', 'test_label', $mail, TRUE);
$this->assertText('Contact form test_label has been added.');
$this->drupalLogout();
// Ensure that anonymous can submit site-wide contact form.
user_role_grant_permissions(AccountInterface::ANONYMOUS_ROLE, ['access site-wide contact form']);
$this->drupalGet('contact');
$this->assertText('Your email address');
// Submit contact form few times.
for ($i = 1; $i <= 5; $i++) {
$this->submitContact($this->randomMachineName(), $mail, $this->randomMachineName(), 'test_id', $this->randomMachineName());
$this->assertText('Your message has been sent.');
}
}
/**
* Test multiple deletion.
*/
public function testBulkDeletion() {
$this->drupalGet('contact');
ViewTestData::createTestViews(get_class($this), ['contact_test_views']);
// Check the operations are accessible to the administer permission user.
$this->drupalLogin($this->drupalCreateUser(['administer contact forms']));
$this->drupalGet('test-contact-message-bulk-form');
$elements = $this->xpath('//select[@id="edit-action"]//option');
$this->assertIdentical(count($elements), 1, 'All contact message operations are found.');
$this->drupalPostForm('test-contact-message-bulk-form', [], t('Apply to selected items'));
$this->assertText('No message selected.');
}
}
<?php
namespace Drupal\Tests\contact_storage\Functional;
use Drupal\Component\Render\FormattableMarkup;
use Drupal\Component\Render\PlainTextOutput;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Test\AssertMailTrait;
use Drupal\Tests\BrowserTestBase;
/**
* Tests personal contact form functionality.
*
* @group contact
*/
class ContactStoragePersonalTest extends BrowserTestBase {
use AssertMailTrait {
getMails as drupalGetMails;
}
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['contact', 'contact_storage', '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;
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
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->getAccountName() . '&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->drupalGetMails();
$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->getDisplayName(),
];
$this->assertRaw(new FormattableMarkup('@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());
}
/**
* 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;
}
}
<?php
namespace Drupal\Tests\contact_storage\Functional;
use Drupal\Tests\BrowserTestBase;
/**
* Defines a base-class for contact-storage tests.
*/
abstract class ContactStorageTestBase extends BrowserTestBase {
/**
* 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 bool $selected
* A Boolean indicating whether the form should be selected by default.
* @param array $third_party_settings
* Array of third party settings to be added to the posted form data.
* @param string $message
* The message that will be displayed to a user upon completing the contact
* form.
*/
public function addContactForm($id, $label, $recipients, $selected, $third_party_settings = [], $message = 'Your message has been sent.') {
$this->drupalGet('admin/structure/contact/add');
$edit = [];
$edit['label'] = $label;
$edit['id'] = $id;
// 8.2.x added the message field, which is by default empty. Conditionally
// submit it if the field can be found.
$xpath = '//textarea[@name=:value]|//input[@name=:value]|//select[@name=:value]';
if ($this->xpath($this->buildXPathQuery($xpath, [':value' => 'message']))) {
$edit['message'] = $message;
}
$edit['recipients'] = $recipients;
$edit['selected'] = ($selected ? TRUE : FALSE);
$edit += $third_party_settings;
$this->drupalPostForm(NULL, $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'));
}
}
}
<?php
namespace Drupal\Tests\contact_storage\Functional;
/**
* Tests adding contact form as entity reference and viewing them through UI.
*
* @group contact_storage
*/
class ContactViewBuilderTest extends ContactStorageTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = [
'user',
'node',
'contact',
'field_ui',
'contact_test',
'contact_storage',
];
/**
* An administrative user with permission to administer contact forms.
*
* @var \Drupal\user\UserInterface
*/
protected $adminUser;
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
// Create Article node type.
$this->drupalCreateContentType([
'type' => 'article',
'name' => 'Article',
'display_submitted' => FALSE,
]);
}
/**
* Tests contact view builder functionality.
*/
public function testContactViewBuilder() {
// Create test admin user.
$this->adminUser = $this->drupalCreateUser([
'administer content types',
'access site-wide contact form',
'administer contact forms',
'administer users',
'administer account settings',
'administer contact_message fields',
]);
// Login as admin user.
$this->drupalLogin($this->adminUser);
// Create first valid contact form.
$mail = 'simpletest@example.com';
$this->addContactForm('test_id', 'test_label', $mail, TRUE);
$this->assertText('Contact form test_label has been added.');
$field_name = 'contact';
$entity_type = 'node';
$bundle_name = 'article';
// Add a Entity Reference Contact Field to Article content type.
$field_storage = \Drupal::entityTypeManager()
->getStorage('field_storage_config')
->create([
'field_name' => $field_name,
'entity_type' => $entity_type,
'type' => 'entity_reference',
'settings' => ['target_type' => 'contact_form'],
]);
$field_storage->save();
$field = \Drupal::entityTypeManager()
->getStorage('field_config')
->create([
'field_storage' => $field_storage,
'bundle' => $bundle_name,
'settings' => [
'handler' => 'default',
],
]);
$field->save();
// Configure the contact reference field form Entity form display.
$this->container->get('entity_display.repository')->getFormDisplay($entity_type, $bundle_name)
->setComponent($field_name, [
'type' => 'options_select',
'settings' => [
'weight' => 20,
],
])
->save();
// Configure the contact reference field form Entity view display.
$this->container->get('entity_display.repository')->getViewDisplay('node', 'article')
->setComponent($field_name, [
'label' => 'above',
'type' => 'entity_reference_entity_view',
'weight' => 20,
])
->save();
// Display Article creation form.
$this->drupalGet('node/add/article');
$title_key = 'title[0][value]';
$body_key = 'body[0][value]';
$contact_key = 'contact';
// Create article node.
$edit = [];
$edit[$title_key] = $this->randomMachineName(8);
$edit[$body_key] = $this->randomMachineName(16);
$edit[$contact_key] = 'test_id';
$this->drupalPostForm('node/add/article', $edit, t('Save'));
// Check that the node exists in the database.
$node = $this->drupalGetNodeByTitle($edit[$title_key]);
$this->drupalGet('node/' . $node->id());
// Some fields should be present.
$this->assertText('Your email address');
$this->assertText('Subject');
$this->assertText('Message');
$this->assertFieldByName('subject[0][value]');
$this->assertFieldByName('message[0][value]');
}
}
<?php
namespace Drupal\Tests\contact_storage\Kernel;
use Drupal\KernelTests\KernelTestBase;
/**
* Tests contact_storage ID field.
*
* @group contact_storage
*/
class ContactStorageFieldTest extends KernelTestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['contact', 'user', 'system'];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->installEntitySchema('contact_message');
}
/**
* Covers contact_storage_install().
*/
public function testContactIdFieldIsCreated() {
$this->container->get('module_installer')->install(['contact_storage']);
// There should be no updates as contact_storage_install() should have
// applied the new field.
$this->assertTrue(empty($this->container->get('entity.definition_update_manager')->needsUpdates()['contact_message']));
$this->assertTrue(!empty($this->container->get('entity_field.manager')->getFieldStorageDefinitions('contact_message')['id']));
}
}
......@@ -10,4 +10,12 @@
use Drupal\Core\Form\FormStateInterface;
use Drupal\media\MediaForm;
use Drupal\views\Form\ViewsForm;
use Drupal\views\ViewExecutable;
\ No newline at end of file
use Drupal\views\ViewExecutable;
function envigo_preprocess_html(&$variables) {
// Add node ID to the body class.
$node = \Drupal::routeMatch()->getParameter('node');
if (is_object($node)) {
$variables['attributes']['class'][] = 'node-' . $node->id();
}
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment