Initial Drupal 11 with DDEV setup
This commit is contained in:
@ -0,0 +1,10 @@
|
||||
langcode: en
|
||||
status: true
|
||||
dependencies: { }
|
||||
id: personal
|
||||
label: 'Personal contact form'
|
||||
recipients: { }
|
||||
reply: ''
|
||||
weight: 0
|
||||
message: 'Your message has been sent.'
|
||||
redirect: ''
|
||||
@ -0,0 +1,5 @@
|
||||
default_form: feedback
|
||||
flood:
|
||||
limit: 5
|
||||
interval: 3600
|
||||
user_default_enabled: true
|
||||
71
web/core/modules/contact/config/schema/contact.schema.yml
Normal file
71
web/core/modules/contact/config/schema/contact.schema.yml
Normal file
@ -0,0 +1,71 @@
|
||||
# Schema for the configuration files of the Contact module.
|
||||
|
||||
contact.form.*:
|
||||
type: config_entity
|
||||
label: 'Contact form'
|
||||
mapping:
|
||||
id:
|
||||
type: machine_name
|
||||
label: 'ID'
|
||||
constraints:
|
||||
Length:
|
||||
# Contact form IDs are specifically limited to 32 characters.
|
||||
# @see \Drupal\contact\ContactFormEditForm::form()
|
||||
max: 32
|
||||
label:
|
||||
type: required_label
|
||||
label: 'Label'
|
||||
recipients:
|
||||
type: sequence
|
||||
label: 'Recipients'
|
||||
sequence:
|
||||
type: email
|
||||
label: 'Email address'
|
||||
reply:
|
||||
type: text
|
||||
label: 'Auto-reply'
|
||||
weight:
|
||||
type: weight
|
||||
label: 'Weight'
|
||||
message:
|
||||
type: text
|
||||
label: 'Message displayed to user on submission'
|
||||
redirect:
|
||||
type: path
|
||||
label: 'Redirect path on submission'
|
||||
|
||||
contact.settings:
|
||||
type: config_object
|
||||
label: 'Contact settings'
|
||||
constraints:
|
||||
FullyValidatable: ~
|
||||
mapping:
|
||||
default_form:
|
||||
type: string
|
||||
label: 'Default form identifier'
|
||||
# It is possible to not configure a default form.
|
||||
# @see \Drupal\contact\ContactFormEditForm::save()
|
||||
nullable: true
|
||||
constraints:
|
||||
ConfigExists:
|
||||
prefix: contact.form.
|
||||
flood:
|
||||
# @see \Drupal\Core\Flood\FloodInterface::isAllowed()
|
||||
type: mapping
|
||||
label: 'Flood control'
|
||||
mapping:
|
||||
limit:
|
||||
type: integer
|
||||
label: 'Limit (messages per interval)'
|
||||
constraints:
|
||||
Range:
|
||||
min: 1
|
||||
interval:
|
||||
type: integer
|
||||
label: 'Interval (seconds)'
|
||||
constraints:
|
||||
Range:
|
||||
min: 1
|
||||
user_default_enabled:
|
||||
type: boolean
|
||||
label: 'Personal contact form enabled by default'
|
||||
@ -0,0 +1,5 @@
|
||||
# Schema for the views plugins of the Contact module.
|
||||
|
||||
views.field.contact_link:
|
||||
type: views.field.user_link
|
||||
label: 'Link to user contact page'
|
||||
6
web/core/modules/contact/contact.info.yml
Normal file
6
web/core/modules/contact/contact.info.yml
Normal file
@ -0,0 +1,6 @@
|
||||
name: Contact
|
||||
type: module
|
||||
description: 'Provides site-wide contact forms and forms to contact individual users.'
|
||||
package: Core
|
||||
version: VERSION
|
||||
configure: entity.contact_form.collection
|
||||
6
web/core/modules/contact/contact.links.action.yml
Normal file
6
web/core/modules/contact/contact.links.action.yml
Normal file
@ -0,0 +1,6 @@
|
||||
contact.form_add:
|
||||
route_name: contact.form_add
|
||||
title: 'Add contact form'
|
||||
weight: 1
|
||||
appears_on:
|
||||
- entity.contact_form.collection
|
||||
10
web/core/modules/contact/contact.links.menu.yml
Normal file
10
web/core/modules/contact/contact.links.menu.yml
Normal file
@ -0,0 +1,10 @@
|
||||
entity.contact_form.collection:
|
||||
title: 'Contact forms'
|
||||
parent: system.admin_structure
|
||||
description: 'Create and manage contact forms.'
|
||||
route_name: entity.contact_form.collection
|
||||
contact.site_page:
|
||||
title: Contact
|
||||
route_name: contact.site_page
|
||||
menu_name: footer
|
||||
enabled: 0
|
||||
17
web/core/modules/contact/contact.links.task.yml
Normal file
17
web/core/modules/contact/contact.links.task.yml
Normal file
@ -0,0 +1,17 @@
|
||||
entity.contact_form.canonical:
|
||||
title: 'View'
|
||||
route_name: entity.contact_form.canonical
|
||||
weight: 0
|
||||
base_route: entity.contact_form.edit_form
|
||||
|
||||
entity.contact_form.edit_form:
|
||||
title: 'Edit'
|
||||
route_name: entity.contact_form.edit_form
|
||||
weight: 1
|
||||
base_route: entity.contact_form.edit_form
|
||||
|
||||
entity.user.contact_form:
|
||||
title: 'Contact'
|
||||
route_name: entity.user.contact_form
|
||||
weight: 2
|
||||
base_route: entity.user.canonical
|
||||
30
web/core/modules/contact/contact.module
Normal file
30
web/core/modules/contact/contact.module
Normal file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
*/
|
||||
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
|
||||
/**
|
||||
* Form submission handler for \Drupal\user\ProfileForm.
|
||||
*
|
||||
* Saves the contact page setting.
|
||||
*/
|
||||
function contact_user_profile_form_submit($form, FormStateInterface $form_state): void {
|
||||
$account = $form_state->getFormObject()->getEntity();
|
||||
if ($account->id() && $form_state->hasValue('contact')) {
|
||||
\Drupal::service('user.data')->set('contact', $account->id(), 'enabled', (int) $form_state->getValue('contact'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Form submission handler for \Drupal\user\AccountSettingsForm.
|
||||
*
|
||||
* @see contact_form_user_admin_settings_alter()
|
||||
*/
|
||||
function contact_form_user_admin_settings_submit($form, FormStateInterface $form_state): void {
|
||||
\Drupal::configFactory()->getEditable('contact.settings')
|
||||
->set('user_default_enabled', $form_state->getValue('contact_default_status'))
|
||||
->save();
|
||||
}
|
||||
6
web/core/modules/contact/contact.permissions.yml
Normal file
6
web/core/modules/contact/contact.permissions.yml
Normal file
@ -0,0 +1,6 @@
|
||||
administer contact forms:
|
||||
title: 'Administer contact forms and contact form settings'
|
||||
access site-wide contact form:
|
||||
title: 'Use the site-wide contact form'
|
||||
access user contact forms:
|
||||
title: 'Use users'' personal contact forms'
|
||||
16
web/core/modules/contact/contact.post_update.php
Normal file
16
web/core/modules/contact/contact.post_update.php
Normal file
@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Post update functions for Contact.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_removed_post_updates().
|
||||
*/
|
||||
function contact_removed_post_updates(): array {
|
||||
return [
|
||||
'contact_post_update_add_message_redirect_field_to_contact_form' => '9.0.0',
|
||||
'contact_post_update_set_empty_default_form_to_null' => '11.0.0',
|
||||
];
|
||||
}
|
||||
57
web/core/modules/contact/contact.routing.yml
Normal file
57
web/core/modules/contact/contact.routing.yml
Normal file
@ -0,0 +1,57 @@
|
||||
entity.contact_form.delete_form:
|
||||
path: '/admin/structure/contact/manage/{contact_form}/delete'
|
||||
defaults:
|
||||
_entity_form: 'contact_form.delete'
|
||||
_title: 'Delete'
|
||||
requirements:
|
||||
_entity_access: 'contact_form.delete'
|
||||
|
||||
entity.contact_form.collection:
|
||||
path: '/admin/structure/contact'
|
||||
defaults:
|
||||
_entity_list: 'contact_form'
|
||||
_title: 'Contact forms'
|
||||
requirements:
|
||||
_permission: 'administer contact forms'
|
||||
|
||||
contact.form_add:
|
||||
path: '/admin/structure/contact/add'
|
||||
defaults:
|
||||
_entity_form: 'contact_form.add'
|
||||
_title: 'Add contact form'
|
||||
requirements:
|
||||
_permission: 'administer contact forms'
|
||||
|
||||
entity.contact_form.edit_form:
|
||||
path: '/admin/structure/contact/manage/{contact_form}'
|
||||
defaults:
|
||||
_entity_form: 'contact_form.edit'
|
||||
_title: 'Edit contact form'
|
||||
requirements:
|
||||
_entity_access: 'contact_form.update'
|
||||
|
||||
contact.site_page:
|
||||
path: '/contact'
|
||||
defaults:
|
||||
_title: 'Contact'
|
||||
_controller: '\Drupal\contact\Controller\ContactController::contactSitePage'
|
||||
contact_form: NULL
|
||||
requirements:
|
||||
_permission: 'access site-wide contact form'
|
||||
|
||||
entity.contact_form.canonical:
|
||||
path: '/contact/{contact_form}'
|
||||
defaults:
|
||||
_title: 'Contact'
|
||||
_controller: '\Drupal\contact\Controller\ContactController::contactSitePage'
|
||||
requirements:
|
||||
_entity_access: 'contact_form.view'
|
||||
|
||||
entity.user.contact_form:
|
||||
path: '/user/{user}/contact'
|
||||
defaults:
|
||||
_title: 'Contact'
|
||||
_controller: '\Drupal\contact\Controller\ContactController::contactPersonalPage'
|
||||
requirements:
|
||||
_access_contact_personal_tab: 'TRUE'
|
||||
user: \d+
|
||||
19
web/core/modules/contact/contact.services.yml
Normal file
19
web/core/modules/contact/contact.services.yml
Normal file
@ -0,0 +1,19 @@
|
||||
parameters:
|
||||
contact.skip_procedural_hook_scan: true
|
||||
|
||||
services:
|
||||
_defaults:
|
||||
autoconfigure: true
|
||||
access_check.contact_personal:
|
||||
class: Drupal\contact\Access\ContactPageAccess
|
||||
tags:
|
||||
- { name: access_check, applies_to: _access_contact_personal_tab }
|
||||
arguments: ['@config.factory', '@user.data']
|
||||
contact.mail_handler:
|
||||
class: Drupal\contact\MailHandler
|
||||
arguments: ['@plugin.manager.mail', '@language_manager', '@logger.channel.contact', '@string_translation', '@entity_type.manager']
|
||||
Drupal\contact\MailHandlerInterface: '@contact.mail_handler'
|
||||
logger.channel.contact:
|
||||
class: Drupal\Core\Logger\LoggerChannel
|
||||
factory: ['@logger.factory', 'get']
|
||||
arguments: ['contact']
|
||||
@ -0,0 +1,17 @@
|
||||
---
|
||||
label: 'Managing the fields of contact forms'
|
||||
related:
|
||||
- contact.overview
|
||||
---
|
||||
{% set contact_link_text %}{% trans %}Contact forms{% endtrans %}{% endset %}
|
||||
{% set contact_link = render_var(help_route_link(contact_link_text, 'entity.contact_form.collection')) %}
|
||||
<h2>{% trans %}Goal{% endtrans %}</h2>
|
||||
<p>{% trans %}Add, remove, or rearrange the fields on personal and site-wide contact forms.{% endtrans %}</p>
|
||||
<h2>{% trans %}What are the fields on contact forms?{% endtrans %}</h2>
|
||||
<p>{% trans %}Both personal and site-wide contact forms will always have <em>Subject</em> and <em>Message</em> fields. You can add additional fields for users to fill out if desired. Note that if you want to display other content on a form page, such as text or images, you can use a content block.{% endtrans %}</p>
|
||||
<h2>{% trans %}Steps{% endtrans %}</h2>
|
||||
<ol>
|
||||
<li>{% trans %}In the <em>Manage</em> administrative menu, navigate to <em>Structure</em> > <em>{{ contact_link }}</em>.{% endtrans %}</li>
|
||||
<li>{% trans %}Click <em>Manage fields</em> for the form you want to change the fields of, and add or remove one or more fields on the form.{% endtrans %}</li>
|
||||
<li>{% trans %}Click <em>Manage form display</em> to change the order or configuration of the fields on the form.{% endtrans %}</li>
|
||||
</ol>
|
||||
@ -0,0 +1,23 @@
|
||||
---
|
||||
label: 'Configuring personal contact forms'
|
||||
related:
|
||||
- contact.overview
|
||||
- contact.adding_fields
|
||||
---
|
||||
{% set config_link_text %}{% trans %}Account settings{% endtrans %}{% endset %}
|
||||
{% set config_link = render_var(help_route_link(config_link_text, 'entity.user.admin_form')) %}
|
||||
{% set permission_link_text %}{% trans %}Permissions{% endtrans %}{% endset %}
|
||||
{% set permission_link = render_var(help_route_link(permission_link_text, 'user.admin_permissions')) %}
|
||||
{% set adding_fields_topic = render_var(help_topic_link('contact.adding_fields')) %}
|
||||
<h2>{% trans %}Goal{% endtrans %}</h2>
|
||||
<p>{% trans %}Configure personal contact forms for registered users on the website.{% endtrans %}</p>
|
||||
<h2>{% trans %}Steps{% endtrans %}</h2>
|
||||
<ol>
|
||||
<li>{% trans %}In the <em>Manage</em> administrative menu, navigate to <em>Configuration</em> > <em>People</em> > <em>{{ config_link }}</em>.{% endtrans %}</li>
|
||||
<li>{% trans %}In the <em>Contact settings</em> section, check/uncheck the box to enable/disable the contact form for new user accounts.{% endtrans %}</li>
|
||||
<li>{% trans %}Click <em>Save configuration</em>.{% endtrans %}</li>
|
||||
<li>{% trans %}In the <em>Manage</em> administrative menu, navigate to <em>People</em> > <em>{{ permission_link }}</em>.{% endtrans %}</li>
|
||||
<li>{% trans %}Verify that permissions are correct for your site's roles, including the generic <em>Anonymous user</em> and <em>Authenticated user</em>. In order to use personal contact forms, users need both <em>View user information</em> (in the <em>User</em> section, which enables them to view user profiles) and <em>Use users' personal contact forms</em> (in the <em>Contact</em> section, which enables them to use contact forms if they can view user profiles).{% endtrans %}</li>
|
||||
<li>{% trans %}Click <em>Save permissions</em>.{% endtrans %}</li>
|
||||
<li>{% trans %}The contact form will always have <em>Subject</em> and <em>Message</em> fields. If you want to add more fields, follow the steps in {{ adding_fields_topic }}.{% endtrans %}</li>
|
||||
</ol>
|
||||
@ -0,0 +1,20 @@
|
||||
---
|
||||
label: 'Creating a new contact form'
|
||||
related:
|
||||
- contact.overview
|
||||
- contact.adding_fields
|
||||
- contact.setting_default
|
||||
---
|
||||
{% set contact_link_text %}{% trans %}Contact forms{% endtrans %}{% endset %}
|
||||
{% set contact_link = render_var(help_route_link(contact_link_text, 'entity.contact_form.collection')) %}
|
||||
{% set adding_fields_topic = render_var(help_topic_link('contact.adding_fields')) %}
|
||||
<h2>{% trans %}Goal{% endtrans %}</h2>
|
||||
<p>{% trans %}Create a new site-wide contact form.{% endtrans %}</p>
|
||||
<h2>{% trans %}Steps{% endtrans %}</h2>
|
||||
<ol>
|
||||
<li>{% trans %}In the <em>Manage</em> administrative menu, navigate to <em>Structure</em> > <em>{{ contact_link }}</em>.{% endtrans %}</li>
|
||||
<li>{% trans %}Click <em>Add contact form</em>.{% endtrans %}</li>
|
||||
<li>{% trans %}Fill in the <em>Label</em> (title) for the form, <em>Recipients</em>, and optionally the other settings.{% endtrans %}</li>
|
||||
<li>{% trans %}Click <em>Save</em>. You should see your new contact form in the table, along with a link to view it.{% endtrans %}</li>
|
||||
<li>{% trans %}The contact form will always have <em>Subject</em> and <em>Message</em> fields. If you want to add more fields, follow the steps in {{ adding_fields_topic }}.{% endtrans %}</li>
|
||||
</ol>
|
||||
@ -0,0 +1,10 @@
|
||||
---
|
||||
label: 'Managing contact forms'
|
||||
top_level: true
|
||||
---
|
||||
<h2>{% trans %}What are contact forms?{% endtrans %}</h2>
|
||||
<p>{% trans %}There are two different types of contact forms provided by the core Contact module: personal contact forms, which allow users to contact other users on the site, and site-wide contact forms, which allow users to contact site managers or administrators. A site can have more than one site-wide contact form; each has its own fields to fill out, recipients, and URL; you can also change the fields that are shown on personal contact forms.{% endtrans %}</p>
|
||||
<h2>{% trans %}Using the personal contact form{% endtrans %}</h2>
|
||||
<p>{% trans %}Site visitors can email registered users on your site by using the personal contact form, without knowing or learning the email address of the recipient. When a user with the correct permissions is viewing another user's profile, the viewer will see a <em>Contact</em> tab or link, which leads to the personal contact form if the user whose profile is being viewed has their personal contact form enabled (this is a user account setting).{% endtrans %}</p>
|
||||
<h2>{% trans %}Contact form management tasks{% endtrans %}</h2>
|
||||
<p>{% trans %}See the related topics below for specific tasks.{% endtrans %}</p>
|
||||
@ -0,0 +1,17 @@
|
||||
---
|
||||
label: 'Setting a default contact form'
|
||||
related:
|
||||
- contact.overview
|
||||
---
|
||||
{% set contact_link_text %}{% trans %}Contact forms{% endtrans %}{% endset %}
|
||||
{% set contact_link = render_var(help_route_link(contact_link_text, 'entity.contact_form.collection')) %}
|
||||
{% set contact_page_link_text %}{% trans %}the form displayed on the default contact page{% endtrans %}{% endset %}
|
||||
{% set contact_page_link = render_var(help_route_link(contact_page_link_text, 'contact.site_page')) %}
|
||||
<h2>{% trans %}Goal{% endtrans %}</h2>
|
||||
<p>{% trans %}Set a site-wide contact form to be the default contact form ({{ contact_page_link }}; for example, <em>/contact</em>).{% endtrans %}</p>
|
||||
<h2>{% trans %}Steps{% endtrans %}</h2>
|
||||
<ol>
|
||||
<li>{% trans %}In the <em>Manage</em> administrative menu, navigate to <em>Structure</em> > <em>{{ contact_link }}</em>.{% endtrans %}</li>
|
||||
<li>{% trans %}Click <em>Edit</em> for the site-wide form you want to be the default.{% endtrans %}</li>
|
||||
<li>{% trans %}Check <em>Make this the default form</em> and click <em>Save</em>.{% endtrans %}</li>
|
||||
</ol>
|
||||
24
web/core/modules/contact/migrations/contact_category.yml
Normal file
24
web/core/modules/contact/migrations/contact_category.yml
Normal file
@ -0,0 +1,24 @@
|
||||
id: contact_category
|
||||
label: Contact category configuration
|
||||
migration_tags:
|
||||
- Drupal 6
|
||||
- Drupal 7
|
||||
- Configuration
|
||||
source:
|
||||
plugin: contact_category
|
||||
process:
|
||||
id:
|
||||
-
|
||||
plugin: machine_name
|
||||
source: category
|
||||
-
|
||||
plugin: make_unique_entity_field
|
||||
entity_type: contact_form
|
||||
field: id
|
||||
length: 30
|
||||
label: category
|
||||
recipients: recipients
|
||||
reply: reply
|
||||
weight: weight
|
||||
destination:
|
||||
plugin: entity:contact_form
|
||||
28
web/core/modules/contact/migrations/d6_contact_settings.yml
Normal file
28
web/core/modules/contact/migrations/d6_contact_settings.yml
Normal file
@ -0,0 +1,28 @@
|
||||
id: d6_contact_settings
|
||||
label: Contact configuration
|
||||
migration_tags:
|
||||
- Drupal 6
|
||||
- Configuration
|
||||
source:
|
||||
plugin: contact_settings
|
||||
variables:
|
||||
- contact_default_status
|
||||
- contact_hourly_threshold
|
||||
process:
|
||||
user_default_enabled: contact_default_status
|
||||
'flood/limit': contact_hourly_threshold
|
||||
'flood/interval':
|
||||
plugin: default_value
|
||||
# It was defaulted to 3600 in D6.
|
||||
# @see https://api.drupal.org/api/drupal/includes%21common.inc/function/flood_is_allowed/6.x
|
||||
default_value: 3600
|
||||
default_form:
|
||||
plugin: migration_lookup
|
||||
migration: contact_category
|
||||
source: default_category
|
||||
destination:
|
||||
plugin: config
|
||||
config_name: contact.settings
|
||||
migration_dependencies:
|
||||
required:
|
||||
- contact_category
|
||||
34
web/core/modules/contact/migrations/d7_contact_settings.yml
Normal file
34
web/core/modules/contact/migrations/d7_contact_settings.yml
Normal file
@ -0,0 +1,34 @@
|
||||
id: d7_contact_settings
|
||||
label: Contact configuration
|
||||
migration_tags:
|
||||
- Drupal 7
|
||||
- Configuration
|
||||
source:
|
||||
plugin: contact_settings
|
||||
variables:
|
||||
- contact_default_status
|
||||
- contact_threshold_limit
|
||||
process:
|
||||
user_default_enabled: contact_default_status
|
||||
'flood/limit': contact_threshold_limit
|
||||
'flood/interval':
|
||||
plugin: default_value
|
||||
# It was defaulted to 3600 in D7.
|
||||
# @see https://api.drupal.org/api/drupal/includes%21common.inc/function/flood_is_allowed/7.x
|
||||
default_value: 3600
|
||||
default_form:
|
||||
plugin: migration_lookup
|
||||
migration: contact_category
|
||||
source: default_category
|
||||
destination:
|
||||
plugin: config
|
||||
config_name: contact.settings
|
||||
migration_dependencies:
|
||||
required:
|
||||
- contact_category
|
||||
dependencies:
|
||||
config:
|
||||
- migrate.migration.contact_category
|
||||
module:
|
||||
- contact
|
||||
- migrate_drupal
|
||||
@ -0,0 +1,5 @@
|
||||
finished:
|
||||
6:
|
||||
contact: contact
|
||||
7:
|
||||
contact: contact
|
||||
99
web/core/modules/contact/src/Access/ContactPageAccess.php
Normal file
99
web/core/modules/contact/src/Access/ContactPageAccess.php
Normal file
@ -0,0 +1,99 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\contact\Access;
|
||||
|
||||
use Drupal\Core\Access\AccessResult;
|
||||
use Drupal\Core\Config\ConfigFactoryInterface;
|
||||
use Drupal\Core\Routing\Access\AccessInterface;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Drupal\user\UserDataInterface;
|
||||
use Drupal\user\UserInterface;
|
||||
|
||||
/**
|
||||
* Access check for contact_personal_page route.
|
||||
*/
|
||||
class ContactPageAccess implements AccessInterface {
|
||||
|
||||
/**
|
||||
* The contact settings config object.
|
||||
*
|
||||
* @var \Drupal\Core\Config\ConfigFactoryInterface
|
||||
*/
|
||||
protected $configFactory;
|
||||
|
||||
/**
|
||||
* The user data service.
|
||||
*
|
||||
* @var \Drupal\user\UserDataInterface
|
||||
*/
|
||||
protected $userData;
|
||||
|
||||
/**
|
||||
* Constructs a ContactPageAccess instance.
|
||||
*
|
||||
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
|
||||
* The config factory.
|
||||
* @param \Drupal\user\UserDataInterface $user_data
|
||||
* The user data service.
|
||||
*/
|
||||
public function __construct(ConfigFactoryInterface $config_factory, UserDataInterface $user_data) {
|
||||
$this->configFactory = $config_factory;
|
||||
$this->userData = $user_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks access to the given user's contact page.
|
||||
*
|
||||
* @param \Drupal\user\UserInterface $user
|
||||
* The user being contacted.
|
||||
* @param \Drupal\Core\Session\AccountInterface $account
|
||||
* The currently logged in account.
|
||||
*
|
||||
* @return \Drupal\Core\Access\AccessResultInterface
|
||||
* The access result.
|
||||
*/
|
||||
public function access(UserInterface $user, AccountInterface $account) {
|
||||
$contact_account = $user;
|
||||
|
||||
// Anonymous users cannot have contact forms.
|
||||
if ($contact_account->isAnonymous()) {
|
||||
return AccessResult::forbidden();
|
||||
}
|
||||
|
||||
// Users may not contact themselves by default, hence this requires user
|
||||
// granularity for caching.
|
||||
$access = AccessResult::neutral()->cachePerUser();
|
||||
if ($account->id() == $contact_account->id()) {
|
||||
return $access;
|
||||
}
|
||||
|
||||
// User administrators should always have access to personal contact forms.
|
||||
$permission_access = AccessResult::allowedIfHasPermission($account, 'administer users');
|
||||
if ($permission_access->isAllowed()) {
|
||||
return $access->orIf($permission_access);
|
||||
}
|
||||
|
||||
// If requested user has been blocked, do not allow users to contact them.
|
||||
$access->addCacheableDependency($contact_account);
|
||||
if ($contact_account->isBlocked()) {
|
||||
return $access;
|
||||
}
|
||||
|
||||
// Forbid access if the requested user has disabled their contact form.
|
||||
$account_data = $this->userData->get('contact', $contact_account->id(), 'enabled');
|
||||
if (isset($account_data) && !$account_data) {
|
||||
return $access;
|
||||
}
|
||||
|
||||
// If the requested user did not save a preference yet, deny access if the
|
||||
// configured default is disabled.
|
||||
$contact_settings = $this->configFactory->get('contact.settings');
|
||||
$access->addCacheableDependency($contact_settings);
|
||||
if (!isset($account_data) && !$contact_settings->get('user_default_enabled')) {
|
||||
return $access;
|
||||
}
|
||||
|
||||
return $access->orIf(AccessResult::allowedIfHasPermission($account, 'access user contact forms'));
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\contact;
|
||||
|
||||
use Drupal\Core\Access\AccessResult;
|
||||
use Drupal\Core\Entity\EntityAccessControlHandler;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
|
||||
/**
|
||||
* Defines the access control handler for the contact form entity type.
|
||||
*
|
||||
* @see \Drupal\contact\Entity\ContactForm.
|
||||
*/
|
||||
class ContactFormAccessControlHandler extends EntityAccessControlHandler {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function checkAccess(EntityInterface $entity, $operation, AccountInterface $account) {
|
||||
if ($operation == 'view') {
|
||||
// Do not allow access personal form via site-wide route.
|
||||
return AccessResult::allowedIfHasPermission($account, 'access site-wide contact form')->andIf(AccessResult::allowedIf($entity->id() !== 'personal'));
|
||||
}
|
||||
elseif ($operation == 'delete' || $operation == 'update') {
|
||||
// Do not allow the 'personal' form to be deleted, as it's used for
|
||||
// the personal contact form.
|
||||
return AccessResult::allowedIfHasPermission($account, 'administer contact forms')->andIf(AccessResult::allowedIf($entity->id() !== 'personal'));
|
||||
}
|
||||
|
||||
return parent::checkAccess($entity, $operation, $account);
|
||||
}
|
||||
|
||||
}
|
||||
193
web/core/modules/contact/src/ContactFormEditForm.php
Normal file
193
web/core/modules/contact/src/ContactFormEditForm.php
Normal file
@ -0,0 +1,193 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\contact;
|
||||
|
||||
use Drupal\Component\Utility\EmailValidatorInterface;
|
||||
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Drupal\Core\Entity\EntityForm;
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Drupal\Core\Form\ConfigFormBaseTrait;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Path\PathValidatorInterface;
|
||||
use Drupal\Core\Render\Element\PathElement;
|
||||
|
||||
/**
|
||||
* Base form for contact form edit forms.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class ContactFormEditForm extends EntityForm implements ContainerInjectionInterface {
|
||||
use ConfigFormBaseTrait;
|
||||
|
||||
/**
|
||||
* The email validator.
|
||||
*
|
||||
* @var \Drupal\Component\Utility\EmailValidatorInterface
|
||||
*/
|
||||
protected $emailValidator;
|
||||
|
||||
/**
|
||||
* The path validator.
|
||||
*
|
||||
* @var \Drupal\Core\Path\PathValidatorInterface
|
||||
*/
|
||||
protected $pathValidator;
|
||||
|
||||
/**
|
||||
* Constructs a new ContactFormEditForm.
|
||||
*
|
||||
* @param \Drupal\Component\Utility\EmailValidatorInterface $email_validator
|
||||
* The email validator.
|
||||
* @param \Drupal\Core\Path\PathValidatorInterface $path_validator
|
||||
* The path validator service.
|
||||
*/
|
||||
public function __construct(EmailValidatorInterface $email_validator, PathValidatorInterface $path_validator) {
|
||||
$this->emailValidator = $email_validator;
|
||||
$this->pathValidator = $path_validator;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('email.validator'),
|
||||
$container->get('path.validator')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getEditableConfigNames() {
|
||||
return ['contact.settings'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function form(array $form, FormStateInterface $form_state) {
|
||||
$form = parent::form($form, $form_state);
|
||||
|
||||
$contact_form = $this->entity;
|
||||
$default_form = $this->config('contact.settings')->get('default_form');
|
||||
|
||||
$form['label'] = [
|
||||
'#type' => 'textfield',
|
||||
'#title' => $this->t('Label'),
|
||||
'#maxlength' => 255,
|
||||
'#default_value' => $contact_form->label(),
|
||||
'#description' => $this->t("Example: 'website feedback' or 'product information'."),
|
||||
'#required' => TRUE,
|
||||
];
|
||||
$form['id'] = [
|
||||
'#type' => 'machine_name',
|
||||
'#default_value' => $contact_form->id(),
|
||||
'#maxlength' => EntityTypeInterface::BUNDLE_MAX_LENGTH,
|
||||
'#machine_name' => [
|
||||
'exists' => '\Drupal\contact\Entity\ContactForm::load',
|
||||
],
|
||||
'#disabled' => !$contact_form->isNew(),
|
||||
];
|
||||
$form['recipients'] = [
|
||||
'#type' => 'textarea',
|
||||
'#title' => $this->t('Recipients'),
|
||||
'#default_value' => implode(', ', $contact_form->getRecipients()),
|
||||
'#description' => $this->t("Example: 'webmaster@example.com' or 'sales@example.com,support@example.com' . To specify multiple recipients, separate each email address with a comma."),
|
||||
'#required' => TRUE,
|
||||
];
|
||||
$form['message'] = [
|
||||
'#type' => 'textarea',
|
||||
'#title' => $this->t('Message'),
|
||||
'#default_value' => $contact_form->getMessage(),
|
||||
'#description' => $this->t('The message to display to the user after submission of this form. Leave blank for no message.'),
|
||||
];
|
||||
$form['redirect'] = [
|
||||
'#type' => 'path',
|
||||
'#title' => $this->t('Redirect path'),
|
||||
'#convert_path' => PathElement::CONVERT_NONE,
|
||||
'#default_value' => $contact_form->getRedirectPath(),
|
||||
'#description' => $this->t('Path to redirect the user to after submission of this form. For example, type "/about" to redirect to that page. Use a relative path with a slash in front.'),
|
||||
];
|
||||
$form['reply'] = [
|
||||
'#type' => 'textarea',
|
||||
'#title' => $this->t('Auto-reply'),
|
||||
'#default_value' => $contact_form->getReply(),
|
||||
'#description' => $this->t('Optional auto-reply. Leave empty if you do not want to send the user an auto-reply message.'),
|
||||
];
|
||||
$form['weight'] = [
|
||||
'#type' => 'weight',
|
||||
'#title' => $this->t('Weight'),
|
||||
'#default_value' => $contact_form->getWeight(),
|
||||
'#description' => $this->t('When listing forms, those with lighter (smaller) weights get listed before forms with heavier (larger) weights. Forms with equal weights are sorted alphabetically.'),
|
||||
];
|
||||
$form['selected'] = [
|
||||
'#type' => 'checkbox',
|
||||
'#title' => $this->t('Make this the default form'),
|
||||
'#default_value' => $default_form === $contact_form->id(),
|
||||
];
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validateForm(array &$form, FormStateInterface $form_state) {
|
||||
parent::validateForm($form, $form_state);
|
||||
|
||||
// Validate and each email recipient.
|
||||
$recipients = explode(',', $form_state->getValue('recipients'));
|
||||
|
||||
foreach ($recipients as &$recipient) {
|
||||
$recipient = trim($recipient);
|
||||
if (!$this->emailValidator->isValid($recipient)) {
|
||||
$form_state->setErrorByName('recipients', $this->t('%recipient is an invalid email address.', ['%recipient' => $recipient]));
|
||||
}
|
||||
}
|
||||
$form_state->setValue('recipients', $recipients);
|
||||
$redirect_url = $form_state->getValue('redirect');
|
||||
if ($redirect_url && $this->pathValidator->isValid($redirect_url)) {
|
||||
if (mb_substr($redirect_url, 0, 1) !== '/') {
|
||||
$form_state->setErrorByName('redirect', $this->t('The path should start with /.'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function save(array $form, FormStateInterface $form_state) {
|
||||
$contact_form = $this->entity;
|
||||
$status = $contact_form->save();
|
||||
$contact_settings = $this->config('contact.settings');
|
||||
|
||||
$edit_link = $this->entity->toLink($this->t('Edit'))->toString();
|
||||
$view_link = $contact_form->toLink($contact_form->label(), 'canonical')->toString();
|
||||
if ($status == SAVED_UPDATED) {
|
||||
$this->messenger()->addStatus($this->t('Contact form %label has been updated.', ['%label' => $view_link]));
|
||||
$this->logger('contact')->notice('Contact form %label has been updated.', ['%label' => $contact_form->label(), 'link' => $edit_link]);
|
||||
}
|
||||
else {
|
||||
$this->messenger()->addStatus($this->t('Contact form %label has been added.', ['%label' => $view_link]));
|
||||
$this->logger('contact')->notice('Contact form %label has been added.', ['%label' => $contact_form->label(), 'link' => $edit_link]);
|
||||
}
|
||||
|
||||
// Update the default form.
|
||||
if ($form_state->getValue('selected')) {
|
||||
$contact_settings
|
||||
->set('default_form', $contact_form->id())
|
||||
->save();
|
||||
}
|
||||
// If it was the default form, empty out the setting.
|
||||
elseif ($contact_settings->get('default_form') == $contact_form->id()) {
|
||||
$contact_settings
|
||||
->set('default_form', NULL)
|
||||
->save();
|
||||
}
|
||||
|
||||
$form_state->setRedirectUrl($contact_form->toUrl('collection'));
|
||||
}
|
||||
|
||||
}
|
||||
112
web/core/modules/contact/src/ContactFormInterface.php
Normal file
112
web/core/modules/contact/src/ContactFormInterface.php
Normal file
@ -0,0 +1,112 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\contact;
|
||||
|
||||
use Drupal\Core\Config\Entity\ConfigEntityInterface;
|
||||
|
||||
/**
|
||||
* Provides an interface defining a contact form entity.
|
||||
*/
|
||||
interface ContactFormInterface extends ConfigEntityInterface {
|
||||
|
||||
/**
|
||||
* Returns the message to be displayed to user.
|
||||
*
|
||||
* @return string
|
||||
* A user message.
|
||||
*/
|
||||
public function getMessage();
|
||||
|
||||
/**
|
||||
* Returns list of recipient email addresses.
|
||||
*
|
||||
* @return array
|
||||
* List of recipient email addresses.
|
||||
*/
|
||||
public function getRecipients();
|
||||
|
||||
/**
|
||||
* Returns the path for redirect.
|
||||
*
|
||||
* @return string
|
||||
* The redirect path.
|
||||
*/
|
||||
public function getRedirectPath();
|
||||
|
||||
/**
|
||||
* Returns the \Drupal\Core\Url object for redirect path.
|
||||
*
|
||||
* Empty redirect property results a \Drupal\Core\Url object of front page.
|
||||
*
|
||||
* @return \Drupal\Core\Url
|
||||
* The redirect \Drupal\Core\Url object.
|
||||
*/
|
||||
public function getRedirectUrl();
|
||||
|
||||
/**
|
||||
* Returns an auto-reply message to send to the message author.
|
||||
*
|
||||
* @return string
|
||||
* An auto-reply message
|
||||
*/
|
||||
public function getReply();
|
||||
|
||||
/**
|
||||
* Returns the weight of this category (used for sorting).
|
||||
*
|
||||
* @return int
|
||||
* The weight of this category.
|
||||
*/
|
||||
public function getWeight();
|
||||
|
||||
/**
|
||||
* Sets the message to be displayed to the user.
|
||||
*
|
||||
* @param string $message
|
||||
* The message to display after form is submitted.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setMessage($message);
|
||||
|
||||
/**
|
||||
* Sets list of recipient email addresses.
|
||||
*
|
||||
* @param array $recipients
|
||||
* The desired list of email addresses of this category.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setRecipients($recipients);
|
||||
|
||||
/**
|
||||
* Sets the redirect path.
|
||||
*
|
||||
* @param string $redirect
|
||||
* The desired path.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setRedirectPath($redirect);
|
||||
|
||||
/**
|
||||
* Sets an auto-reply message to send to the message author.
|
||||
*
|
||||
* @param string $reply
|
||||
* The desired reply.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setReply($reply);
|
||||
|
||||
/**
|
||||
* Sets the weight.
|
||||
*
|
||||
* @param int $weight
|
||||
* The desired weight.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setWeight($weight);
|
||||
|
||||
}
|
||||
48
web/core/modules/contact/src/ContactFormListBuilder.php
Normal file
48
web/core/modules/contact/src/ContactFormListBuilder.php
Normal file
@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\contact;
|
||||
|
||||
use Drupal\Core\Config\Entity\ConfigEntityListBuilder;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
|
||||
/**
|
||||
* Defines a class to build a listing of contact form entities.
|
||||
*
|
||||
* @see \Drupal\contact\Entity\ContactForm
|
||||
*/
|
||||
class ContactFormListBuilder extends ConfigEntityListBuilder {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildHeader() {
|
||||
$header['form'] = $this->t('Form');
|
||||
$header['recipients'] = $this->t('Recipients');
|
||||
$header['selected'] = $this->t('Selected');
|
||||
return $header + parent::buildHeader();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildRow(EntityInterface $entity) {
|
||||
// Special case the personal form.
|
||||
if ($entity->id() == 'personal') {
|
||||
$row['form'] = $entity->label();
|
||||
$row['recipients'] = $this->t('Selected user');
|
||||
$row['selected'] = $this->t('No');
|
||||
}
|
||||
else {
|
||||
$row['form'] = $entity->toLink(NULL, 'canonical')->toString();
|
||||
$row['recipients']['data'] = [
|
||||
'#theme' => 'item_list',
|
||||
'#items' => $entity->getRecipients(),
|
||||
'#context' => ['list_style' => 'comma-list'],
|
||||
];
|
||||
$default_form = \Drupal::config('contact.settings')->get('default_form');
|
||||
$row['selected'] = ($default_form == $entity->id() ? $this->t('Yes') : $this->t('No'));
|
||||
}
|
||||
return $row + parent::buildRow($entity);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\contact;
|
||||
|
||||
use Drupal\Core\Access\AccessResult;
|
||||
use Drupal\Core\Entity\EntityAccessControlHandler;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
|
||||
/**
|
||||
* Defines the access control handler for the message form entity type.
|
||||
*
|
||||
* @see \Drupal\contact\Entity\Message.
|
||||
*/
|
||||
class ContactMessageAccessControlHandler extends EntityAccessControlHandler {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function checkCreateAccess(AccountInterface $account, array $context, $entity_bundle = NULL) {
|
||||
return AccessResult::allowedIfHasPermission($account, 'access site-wide contact form');
|
||||
}
|
||||
|
||||
}
|
||||
124
web/core/modules/contact/src/Controller/ContactController.php
Normal file
124
web/core/modules/contact/src/Controller/ContactController.php
Normal file
@ -0,0 +1,124 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\contact\Controller;
|
||||
|
||||
use Drupal\Core\Controller\ControllerBase;
|
||||
use Drupal\contact\ContactFormInterface;
|
||||
use Drupal\Core\Render\RendererInterface;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\user\UserInterface;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
|
||||
/**
|
||||
* Controller routines for contact routes.
|
||||
*/
|
||||
class ContactController extends ControllerBase {
|
||||
|
||||
/**
|
||||
* The renderer.
|
||||
*
|
||||
* @var \Drupal\Core\Render\RendererInterface
|
||||
*/
|
||||
protected $renderer;
|
||||
|
||||
/**
|
||||
* Constructs a ContactController object.
|
||||
*
|
||||
* @param \Drupal\Core\Render\RendererInterface $renderer
|
||||
* The renderer.
|
||||
*/
|
||||
public function __construct(RendererInterface $renderer) {
|
||||
$this->renderer = $renderer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Presents the site-wide contact form.
|
||||
*
|
||||
* @param \Drupal\contact\ContactFormInterface $contact_form
|
||||
* The contact form to use.
|
||||
*
|
||||
* @return array
|
||||
* The form as render array as expected by
|
||||
* \Drupal\Core\Render\RendererInterface::render().
|
||||
*
|
||||
* @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
|
||||
* Exception is thrown when user tries to access non existing default
|
||||
* contact form.
|
||||
*/
|
||||
public function contactSitePage(?ContactFormInterface $contact_form = NULL) {
|
||||
$config = $this->config('contact.settings');
|
||||
|
||||
// Use the default form if no form has been passed.
|
||||
if (empty($contact_form)) {
|
||||
$default_form = $config->get('default_form');
|
||||
// Load the default form, if configured.
|
||||
if (!is_null($default_form)) {
|
||||
$contact_form = $this->entityTypeManager()
|
||||
->getStorage('contact_form')
|
||||
->load($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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$message = $this->entityTypeManager()
|
||||
->getStorage('contact_message')
|
||||
->create([
|
||||
'contact_form' => $contact_form->id(),
|
||||
]);
|
||||
|
||||
$form = $this->entityFormBuilder()->getForm($message);
|
||||
$form['#title'] = $contact_form->label();
|
||||
$form['#cache']['contexts'][] = 'user.permissions';
|
||||
$this->renderer->addCacheableDependency($form, $config);
|
||||
|
||||
// The form might not have the correct cacheability metadata, so make it
|
||||
// uncacheable by default.
|
||||
// @todo Remove this in https://www.drupal.org/node/3395506.
|
||||
$form['#cache']['max-age'] = 0;
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Form constructor for the personal contact form.
|
||||
*
|
||||
* @param \Drupal\user\UserInterface $user
|
||||
* The account for which a personal contact form should be generated.
|
||||
*
|
||||
* @return array
|
||||
* The personal contact form as render array as expected by
|
||||
* \Drupal\Core\Render\RendererInterface::render().
|
||||
*
|
||||
* @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
|
||||
* Exception is thrown when user tries to access a contact form for a
|
||||
* user who does not have an email address configured.
|
||||
*/
|
||||
public function contactPersonalPage(UserInterface $user) {
|
||||
// Do not continue if the user does not have an email address configured.
|
||||
if (!$user->getEmail()) {
|
||||
throw new NotFoundHttpException();
|
||||
}
|
||||
|
||||
$message = $this->entityTypeManager()->getStorage('contact_message')->create([
|
||||
'contact_form' => 'personal',
|
||||
'recipient' => $user->id(),
|
||||
]);
|
||||
|
||||
$form = $this->entityFormBuilder()->getForm($message);
|
||||
$form['#title'] = $this->t('Contact @username', ['@username' => $user->getDisplayName()]);
|
||||
$form['#cache']['contexts'][] = 'user.permissions';
|
||||
return $form;
|
||||
}
|
||||
|
||||
}
|
||||
208
web/core/modules/contact/src/Entity/ContactForm.php
Normal file
208
web/core/modules/contact/src/Entity/ContactForm.php
Normal file
@ -0,0 +1,208 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\contact\Entity;
|
||||
|
||||
use Drupal\contact\ContactFormAccessControlHandler;
|
||||
use Drupal\contact\ContactFormEditForm;
|
||||
use Drupal\contact\ContactFormInterface;
|
||||
use Drupal\contact\ContactFormListBuilder;
|
||||
use Drupal\Core\Config\Action\Attribute\ActionMethod;
|
||||
use Drupal\Core\Config\Entity\ConfigEntityBundleBase;
|
||||
use Drupal\Core\Entity\Attribute\ConfigEntityType;
|
||||
use Drupal\Core\Entity\EntityDeleteForm;
|
||||
use Drupal\Core\StringTranslation\TranslatableMarkup;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\user\Entity\EntityPermissionsRouteProvider;
|
||||
|
||||
/**
|
||||
* Defines the contact form entity.
|
||||
*/
|
||||
#[ConfigEntityType(
|
||||
id: 'contact_form',
|
||||
label: new TranslatableMarkup('Contact form'),
|
||||
label_collection: new TranslatableMarkup('Contact forms'),
|
||||
label_singular: new TranslatableMarkup('contact form'),
|
||||
label_plural: new TranslatableMarkup('contact forms'),
|
||||
config_prefix: 'form',
|
||||
entity_keys: [
|
||||
'id' => 'id',
|
||||
'label' => 'label',
|
||||
],
|
||||
handlers: [
|
||||
'access' => ContactFormAccessControlHandler::class,
|
||||
'list_builder' => ContactFormListBuilder::class,
|
||||
'form' => [
|
||||
'add' => ContactFormEditForm::class,
|
||||
'edit' => ContactFormEditForm::class,
|
||||
'delete' => EntityDeleteForm::class,
|
||||
],
|
||||
'route_provider' => ['permissions' => EntityPermissionsRouteProvider::class],
|
||||
],
|
||||
links: [
|
||||
'delete-form' => '/admin/structure/contact/manage/{contact_form}/delete',
|
||||
'edit-form' => '/admin/structure/contact/manage/{contact_form}',
|
||||
'entity-permissions-form' => '/admin/structure/contact/manage/{contact_form}/permissions',
|
||||
'collection' => '/admin/structure/contact',
|
||||
'canonical' => '/contact/{contact_form}',
|
||||
],
|
||||
admin_permission: 'administer contact forms',
|
||||
bundle_of: 'contact_message',
|
||||
label_count: [
|
||||
'singular' => '@count contact form',
|
||||
'plural' => '@count contact forms',
|
||||
],
|
||||
config_export: [
|
||||
'id',
|
||||
'label',
|
||||
'recipients',
|
||||
'reply',
|
||||
'weight',
|
||||
'message',
|
||||
'redirect',
|
||||
],
|
||||
)]
|
||||
class ContactForm extends ConfigEntityBundleBase implements ContactFormInterface {
|
||||
|
||||
/**
|
||||
* The form ID.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $id;
|
||||
|
||||
/**
|
||||
* The human-readable label of the category.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $label;
|
||||
|
||||
/**
|
||||
* The message displayed to user on form submission.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $message;
|
||||
|
||||
/**
|
||||
* List of recipient email addresses.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $recipients = [];
|
||||
|
||||
/**
|
||||
* The path to redirect to on form submission.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $redirect;
|
||||
|
||||
/**
|
||||
* An auto-reply message.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $reply = '';
|
||||
|
||||
/**
|
||||
* The weight of the category.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $weight = 0;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getMessage() {
|
||||
return $this->message;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
#[ActionMethod(adminLabel: new TranslatableMarkup('Set contact form message'), pluralize: FALSE)]
|
||||
public function setMessage($message) {
|
||||
$this->message = $message;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getRecipients() {
|
||||
return $this->recipients;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
#[ActionMethod(adminLabel: new TranslatableMarkup('Set recipients'), pluralize: FALSE)]
|
||||
public function setRecipients($recipients) {
|
||||
$this->recipients = $recipients;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getRedirectPath() {
|
||||
return $this->redirect;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getRedirectUrl() {
|
||||
if ($this->redirect) {
|
||||
$url = Url::fromUserInput($this->redirect);
|
||||
}
|
||||
else {
|
||||
$url = Url::fromRoute('<front>');
|
||||
}
|
||||
return $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
#[ActionMethod(adminLabel: new TranslatableMarkup('Set redirect path'), pluralize: FALSE)]
|
||||
public function setRedirectPath($redirect) {
|
||||
$this->redirect = $redirect;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getReply() {
|
||||
return $this->reply;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
#[ActionMethod(adminLabel: new TranslatableMarkup('Set auto-reply message'), pluralize: FALSE)]
|
||||
public function setReply($reply) {
|
||||
$this->reply = $reply;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getWeight() {
|
||||
return $this->weight;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
#[ActionMethod(adminLabel: new TranslatableMarkup('Set weight'), pluralize: FALSE)]
|
||||
public function setWeight($weight) {
|
||||
$this->weight = $weight;
|
||||
return $this;
|
||||
}
|
||||
|
||||
}
|
||||
204
web/core/modules/contact/src/Entity/Message.php
Normal file
204
web/core/modules/contact/src/Entity/Message.php
Normal file
@ -0,0 +1,204 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\contact\Entity;
|
||||
|
||||
use Drupal\contact\ContactMessageAccessControlHandler;
|
||||
use Drupal\contact\MessageForm;
|
||||
use Drupal\contact\MessageViewBuilder;
|
||||
use Drupal\Core\Entity\Attribute\ContentEntityType;
|
||||
use Drupal\Core\Entity\ContentEntityNullStorage;
|
||||
use Drupal\Core\StringTranslation\TranslatableMarkup;
|
||||
use Drupal\Core\Entity\ContentEntityBase;
|
||||
use Drupal\contact\MessageInterface;
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Drupal\Core\Field\BaseFieldDefinition;
|
||||
|
||||
/**
|
||||
* Defines the contact message entity.
|
||||
*/
|
||||
#[ContentEntityType(
|
||||
id: 'contact_message',
|
||||
label: new TranslatableMarkup('Contact message'),
|
||||
label_collection: new TranslatableMarkup('Contact messages'),
|
||||
label_singular: new TranslatableMarkup('contact message'),
|
||||
label_plural: new TranslatableMarkup('contact messages'),
|
||||
entity_keys: [
|
||||
'bundle' => 'contact_form',
|
||||
'uuid' => 'uuid',
|
||||
'langcode' => 'langcode',
|
||||
],
|
||||
handlers: [
|
||||
'access' => ContactMessageAccessControlHandler::class,
|
||||
'storage' => ContentEntityNullStorage::class,
|
||||
'view_builder' => MessageViewBuilder::class,
|
||||
'form' => ['default' => MessageForm::class],
|
||||
],
|
||||
admin_permission: 'administer contact forms',
|
||||
bundle_entity_type: 'contact_form',
|
||||
bundle_label: new TranslatableMarkup('Contact form'),
|
||||
label_count: [
|
||||
'singular' => '@count contact message',
|
||||
'plural' => '@count contact messages',
|
||||
],
|
||||
field_ui_base_route: 'entity.contact_form.edit_form'
|
||||
)]
|
||||
class Message extends ContentEntityBase implements MessageInterface {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isPersonal() {
|
||||
return $this->bundle() == 'personal';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getContactForm() {
|
||||
return $this->get('contact_form')->entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getSenderName() {
|
||||
return $this->get('name')->value;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setSenderName($sender_name) {
|
||||
$this->set('name', $sender_name);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getSenderMail() {
|
||||
return $this->get('mail')->value;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setSenderMail($sender_mail) {
|
||||
$this->set('mail', $sender_mail);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getSubject() {
|
||||
return $this->get('subject')->value;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setSubject($subject) {
|
||||
$this->set('subject', $subject);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getMessage() {
|
||||
return $this->get('message')->value;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setMessage($message) {
|
||||
$this->set('message', $message);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function copySender() {
|
||||
return (bool) $this->get('copy')->value;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setCopySender($inform) {
|
||||
$this->set('copy', (bool) $inform);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getPersonalRecipient() {
|
||||
if ($this->isPersonal()) {
|
||||
return $this->get('recipient')->entity;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
|
||||
/** @var \Drupal\Core\Field\BaseFieldDefinition[] $fields */
|
||||
$fields = parent::baseFieldDefinitions($entity_type);
|
||||
|
||||
$fields['contact_form']->setLabel(t('Form ID'))
|
||||
->setDescription(t('The ID of the associated form.'));
|
||||
|
||||
$fields['uuid']->setDescription(t('The message UUID.'));
|
||||
|
||||
$fields['langcode']->setDescription(t('The message language code.'));
|
||||
|
||||
$fields['name'] = BaseFieldDefinition::create('string')
|
||||
->setLabel(t("The sender's name"))
|
||||
->setDescription(t('The name of the person that is sending the contact message.'));
|
||||
|
||||
$fields['mail'] = BaseFieldDefinition::create('email')
|
||||
->setLabel(t("The sender's email"))
|
||||
->setDescription(t('The email of the person that is sending the contact message.'));
|
||||
|
||||
// The subject of the contact message.
|
||||
$fields['subject'] = BaseFieldDefinition::create('string')
|
||||
->setLabel(t('Subject'))
|
||||
->setRequired(TRUE)
|
||||
->setSetting('max_length', 100)
|
||||
->setDisplayOptions('form', [
|
||||
'type' => 'string_textfield',
|
||||
'weight' => -10,
|
||||
])
|
||||
->setDisplayConfigurable('form', TRUE);
|
||||
|
||||
// The text of the contact message.
|
||||
$fields['message'] = BaseFieldDefinition::create('string_long')
|
||||
->setLabel(t('Message'))
|
||||
->setRequired(TRUE)
|
||||
->setDisplayOptions('form', [
|
||||
'type' => 'string_textarea',
|
||||
'weight' => 0,
|
||||
'settings' => [
|
||||
'rows' => 12,
|
||||
],
|
||||
])
|
||||
->setDisplayConfigurable('form', TRUE)
|
||||
->setDisplayOptions('view', [
|
||||
'type' => 'string',
|
||||
'weight' => 0,
|
||||
'label' => 'above',
|
||||
])
|
||||
->setDisplayConfigurable('view', TRUE);
|
||||
|
||||
$fields['copy'] = BaseFieldDefinition::create('boolean')
|
||||
->setLabel(t('Copy'))
|
||||
->setDescription(t('Whether to send a copy of the message to the sender.'));
|
||||
|
||||
$fields['recipient'] = BaseFieldDefinition::create('entity_reference')
|
||||
->setLabel(t('Recipient ID'))
|
||||
->setDescription(t('The ID of the recipient user for personal contact messages.'))
|
||||
->setSetting('target_type', 'user');
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
}
|
||||
76
web/core/modules/contact/src/Hook/ContactFormHooks.php
Normal file
76
web/core/modules/contact/src/Hook/ContactFormHooks.php
Normal file
@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\contact\Hook;
|
||||
|
||||
use Drupal\Core\Config\ConfigFactoryInterface;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Hook\Attribute\Hook;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Drupal\Core\StringTranslation\StringTranslationTrait;
|
||||
use Drupal\user\UserDataInterface;
|
||||
|
||||
/**
|
||||
* Form hook implementations for Contact module.
|
||||
*/
|
||||
class ContactFormHooks {
|
||||
|
||||
use StringTranslationTrait;
|
||||
|
||||
public function __construct(
|
||||
protected readonly AccountInterface $currentUser,
|
||||
protected readonly UserDataInterface $userData,
|
||||
protected readonly configFactoryInterface $configFactory,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Implements hook_form_FORM_ID_alter() for \Drupal\user\ProfileForm.
|
||||
*
|
||||
* Add the enable personal contact form to an individual user's account page.
|
||||
*
|
||||
* @see \Drupal\user\ProfileForm::form()
|
||||
*/
|
||||
#[Hook('form_user_form_alter')]
|
||||
public function formUserFormAlter(&$form, FormStateInterface $form_state) : void {
|
||||
$form['contact'] = [
|
||||
'#type' => 'details',
|
||||
'#title' => $this->t('Contact settings'),
|
||||
'#open' => TRUE,
|
||||
'#weight' => 5,
|
||||
];
|
||||
$account = $form_state->getFormObject()->getEntity();
|
||||
if (!$this->currentUser->isAnonymous() && $account->id()) {
|
||||
$account_data = $this->userData->get('contact', $account->id(), 'enabled');
|
||||
}
|
||||
$form['contact']['contact'] = [
|
||||
'#type' => 'checkbox',
|
||||
'#title' => $this->t('Personal contact form'),
|
||||
'#default_value' => $account_data ?? $this->configFactory->getEditable('contact.settings')->get('user_default_enabled'),
|
||||
'#description' => $this->t('Allow other users to contact you via a personal contact form which keeps your email address hidden. Note that some privileged users such as site administrators are still able to contact you even if you choose to disable this feature.'),
|
||||
];
|
||||
$form['actions']['submit']['#submit'][] = 'contact_user_profile_form_submit';
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_form_FORM_ID_alter() for \Drupal\user\AccountSettingsForm.
|
||||
*
|
||||
* Adds the default personal contact setting on the user settings page.
|
||||
*/
|
||||
#[Hook('form_user_admin_settings_alter')]
|
||||
public function formUserAdminSettingsAlter(&$form, FormStateInterface $form_state) : void {
|
||||
$form['contact'] = [
|
||||
'#type' => 'details',
|
||||
'#title' => $this->t('Contact settings'),
|
||||
'#open' => TRUE,
|
||||
'#weight' => 0,
|
||||
];
|
||||
$form['contact']['contact_default_status'] = [
|
||||
'#type' => 'checkbox',
|
||||
'#title' => $this->t('Enable the personal contact form by default for new users'),
|
||||
'#description' => $this->t('Changing this setting will not affect existing users.'),
|
||||
'#default_value' => $this->configFactory->getEditable('contact.settings')->get('user_default_enabled'),
|
||||
];
|
||||
// Add submit handler to save contact configuration.
|
||||
$form['#submit'][] = 'contact_form_user_admin_settings_submit';
|
||||
}
|
||||
|
||||
}
|
||||
182
web/core/modules/contact/src/Hook/ContactHooks.php
Normal file
182
web/core/modules/contact/src/Hook/ContactHooks.php
Normal file
@ -0,0 +1,182 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\contact\Hook;
|
||||
|
||||
use Drupal\contact\Plugin\rest\resource\ContactMessageResource;
|
||||
use Drupal\Core\StringTranslation\StringTranslationTrait;
|
||||
use Drupal\user\Entity\User;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\Core\Routing\RouteMatchInterface;
|
||||
use Drupal\Core\Hook\Attribute\Hook;
|
||||
|
||||
/**
|
||||
* Hook implementations for contact.
|
||||
*/
|
||||
class ContactHooks {
|
||||
|
||||
use StringTranslationTrait;
|
||||
|
||||
/**
|
||||
* Implements hook_help().
|
||||
*/
|
||||
#[Hook('help')]
|
||||
public function help($route_name, RouteMatchInterface $route_match): ?string {
|
||||
switch ($route_name) {
|
||||
case 'help.page.contact':
|
||||
$menu_page = \Drupal::moduleHandler()->moduleExists('menu_ui') ? Url::fromRoute('entity.menu.collection')->toString() : '#';
|
||||
$block_page = \Drupal::moduleHandler()->moduleExists('block') ? Url::fromRoute('block.admin_display')->toString() : '#';
|
||||
$contact_page = Url::fromRoute('entity.contact_form.collection')->toString();
|
||||
$output = '';
|
||||
$output .= '<h2>' . $this->t('About') . '</h2>';
|
||||
$output .= '<p>' . $this->t('The Contact module allows visitors to contact registered users on your site, using the personal contact form, and also allows you to set up site-wide contact forms. For more information, see the <a href=":contact">online documentation for the Contact module</a>.', [':contact' => 'https://www.drupal.org/documentation/modules/contact']) . '</p>';
|
||||
$output .= '<h2>' . $this->t('Uses') . '</h2>';
|
||||
$output .= '<dl>';
|
||||
$output .= '<dt>' . $this->t('Using the personal contact form') . '</dt>';
|
||||
$output .= '<dd>' . $this->t("Site visitors can email registered users on your site by using the personal contact form, without knowing or learning the email address of the recipient. When a site visitor is viewing a user profile, the viewer will see a <em>Contact</em> tab or link, which leads to the personal contact form. The personal contact link is not shown when you are viewing your own profile, and users must have both <em>View user information</em> (to see user profiles) and <em>Use users' personal contact forms</em> permission to see the link. The user whose profile is being viewed must also have their personal contact form enabled (this is a user account setting); viewers with <em>Administer users</em> permission can bypass this setting.") . '</dd>';
|
||||
$output .= '<dt>' . $this->t('Configuring contact forms') . '</dt>';
|
||||
$output .= '<dd>' . $this->t('On the <a href=":contact_admin">Contact forms page</a>, you can configure the fields and display of the personal contact form, and you can set up one or more site-wide contact forms. Each site-wide contact form has a machine name, a label, and one or more defined recipients; when a site visitor submits the form, the field values are sent to those recipients.', [':contact_admin' => $contact_page]) . '</dd>';
|
||||
$output .= '<dt>' . $this->t('Linking to contact forms') . '</dt>';
|
||||
$output .= '<dd>' . $this->t('One site-wide contact form can be designated as the default contact form. If you choose to designate a default form, the <em>Contact</em> menu link in the <em>Footer</em> menu will link to it. You can modify this link from the <a href=":menu-settings">Menus page</a> if you have the Menu UI module installed. You can also create links to other contact forms; the URL for each form you have set up has format <em>contact/machine_name_of_form</em>.', [':menu-settings' => $menu_page]) . '</p>';
|
||||
$output .= '<dt>' . $this->t('Adding content to contact forms') . '</dt>';
|
||||
$output .= '<dd>' . $this->t('From the <a href=":contact_admin">Contact forms page</a>, you can configure the fields to be shown on contact forms, including their labels and help text. If you would like other content (such as text or images) to appear on a contact form, use a block. You can create and edit blocks on the <a href=":blocks">Block layout page</a>, if the Block module is installed.', [':blocks' => $block_page, ':contact_admin' => $contact_page]) . '</dd>';
|
||||
$output .= '</dl>';
|
||||
return $output;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_entity_type_alter().
|
||||
*/
|
||||
#[Hook('entity_type_alter')]
|
||||
public function entityTypeAlter(array &$entity_types) : void {
|
||||
/** @var \Drupal\Core\Entity\EntityTypeInterface[] $entity_types */
|
||||
$entity_types['user']->setLinkTemplate('contact-form', '/user/{user}/contact');
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_entity_extra_field_info().
|
||||
*/
|
||||
#[Hook('entity_extra_field_info')]
|
||||
public function entityExtraFieldInfo(): array {
|
||||
$fields = [];
|
||||
foreach (array_keys(\Drupal::service('entity_type.bundle.info')->getBundleInfo('contact_message')) as $bundle) {
|
||||
$fields['contact_message'][$bundle]['form']['name'] = ['label' => $this->t('Sender name'), 'description' => $this->t('Text'), 'weight' => -50];
|
||||
$fields['contact_message'][$bundle]['form']['mail'] = ['label' => $this->t('Sender email'), 'description' => $this->t('Email'), 'weight' => -40];
|
||||
if ($bundle == 'personal') {
|
||||
$fields['contact_message'][$bundle]['form']['recipient'] = ['label' => $this->t('Recipient username'), 'description' => $this->t('User'), 'weight' => -30];
|
||||
}
|
||||
$fields['contact_message'][$bundle]['form']['preview'] = [
|
||||
'label' => $this->t('Preview sender message'),
|
||||
'description' => $this->t('Preview'),
|
||||
'weight' => 40,
|
||||
];
|
||||
$fields['contact_message'][$bundle]['form']['copy'] = [
|
||||
'label' => $this->t('Send copy to sender'),
|
||||
'description' => $this->t('Option'),
|
||||
'weight' => 50,
|
||||
];
|
||||
}
|
||||
$fields['user']['user']['form']['contact'] = [
|
||||
'label' => $this->t('Contact settings'),
|
||||
'description' => $this->t('Contact module form element.'),
|
||||
'weight' => 5,
|
||||
];
|
||||
return $fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_menu_local_tasks_alter().
|
||||
*
|
||||
* Hides the 'Contact' tab on the user profile if the user does not have an
|
||||
* email address configured.
|
||||
*/
|
||||
#[Hook('menu_local_tasks_alter')]
|
||||
public function menuLocalTasksAlter(&$data, $route_name): void {
|
||||
if ($route_name == 'entity.user.canonical' && isset($data['tabs'][0])) {
|
||||
foreach ($data['tabs'][0] as $href => $tab_data) {
|
||||
if ($href == 'entity.user.contact_form') {
|
||||
$link_params = $tab_data['#link']['url']->getRouteParameters();
|
||||
$account = User::load($link_params['user']);
|
||||
if (!$account->getEmail()) {
|
||||
unset($data['tabs'][0]['entity.user.contact_form']);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_mail().
|
||||
*/
|
||||
#[Hook('mail')]
|
||||
public function mail($key, &$message, $params): void {
|
||||
$contact_message = $params['contact_message'];
|
||||
/** @var \Drupal\user\UserInterface $sender */
|
||||
$sender = $params['sender'];
|
||||
$language = \Drupal::languageManager()->getLanguage($message['langcode']);
|
||||
$variables = [
|
||||
'@site-name' => \Drupal::config('system.site')->get('name'),
|
||||
'@subject' => $contact_message->getSubject(),
|
||||
'@form' => !empty($params['contact_form']) ? $params['contact_form']->label() : '',
|
||||
'@form-url' => Url::fromRoute('<current>', [], [
|
||||
'absolute' => TRUE,
|
||||
'language' => $language,
|
||||
])->toString(),
|
||||
'@sender-name' => $sender->getDisplayName(),
|
||||
];
|
||||
if ($sender->isAuthenticated()) {
|
||||
$variables['@sender-url'] = $sender->toUrl('canonical', ['absolute' => TRUE, 'language' => $language])->toString();
|
||||
}
|
||||
else {
|
||||
$variables['@sender-url'] = $params['sender']->getEmail() ?? '';
|
||||
}
|
||||
$options = ['langcode' => $language->getId()];
|
||||
switch ($key) {
|
||||
case 'page_mail':
|
||||
case 'page_copy':
|
||||
$message['subject'] .= $this->t('[@form] @subject', $variables, $options);
|
||||
$message['body'][] = $this->t("@sender-name (@sender-url) sent a message using the contact form at @form-url.", $variables, $options);
|
||||
$build = \Drupal::entityTypeManager()->getViewBuilder('contact_message')->view($contact_message, 'mail');
|
||||
$message['body'][] = \Drupal::service('renderer')->renderInIsolation($build);
|
||||
break;
|
||||
|
||||
case 'page_autoreply':
|
||||
$message['subject'] .= $this->t('[@form] @subject', $variables, $options);
|
||||
$message['body'][] = $params['contact_form']->getReply();
|
||||
break;
|
||||
|
||||
case 'user_mail':
|
||||
case 'user_copy':
|
||||
$variables += [
|
||||
'@recipient-name' => $params['recipient']->getDisplayName(),
|
||||
'@recipient-edit-url' => $params['recipient']->toUrl('edit-form', [
|
||||
'absolute' => TRUE,
|
||||
'language' => $language,
|
||||
])->toString(),
|
||||
];
|
||||
$message['subject'] .= $this->t('[@site-name] @subject', $variables, $options);
|
||||
$message['body'][] = $this->t('Hello @recipient-name,', $variables, $options);
|
||||
$message['body'][] = $this->t("@sender-name (@sender-url) has sent you a message via your contact form at @site-name.", $variables, $options);
|
||||
// Only include the opt-out line in the original email and not in the
|
||||
// copy to the sender. Also exclude this if the email was sent from a
|
||||
// user administrator because they can always send emails even if the
|
||||
// contacted user has disabled their contact form.
|
||||
if ($key === 'user_mail' && !$params['sender']->hasPermission('administer users')) {
|
||||
$message['body'][] = $this->t("If you don't want to receive such messages, you can change your settings at @recipient-edit-url.", $variables, $options);
|
||||
}
|
||||
$build = \Drupal::entityTypeManager()->getViewBuilder('contact_message')->view($contact_message, 'mail');
|
||||
$message['body'][] = \Drupal::service('renderer')->renderInIsolation($build);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_rest_resource_alter().
|
||||
*/
|
||||
#[Hook('rest_resource_alter')]
|
||||
public function restResourceAlter(&$definitions): void {
|
||||
$definitions['entity:contact_message']['class'] = ContactMessageResource::class;
|
||||
}
|
||||
|
||||
}
|
||||
29
web/core/modules/contact/src/Hook/ContactViewsHooks.php
Normal file
29
web/core/modules/contact/src/Hook/ContactViewsHooks.php
Normal file
@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\contact\Hook;
|
||||
|
||||
use Drupal\Core\Hook\Attribute\Hook;
|
||||
use Drupal\Core\StringTranslation\StringTranslationTrait;
|
||||
|
||||
/**
|
||||
* Hook implementations for contact.
|
||||
*/
|
||||
class ContactViewsHooks {
|
||||
|
||||
use StringTranslationTrait;
|
||||
|
||||
/**
|
||||
* Implements hook_views_data_alter().
|
||||
*/
|
||||
#[Hook('views_data_alter')]
|
||||
public function viewsDataAlter(&$data): void {
|
||||
$data['users']['contact'] = [
|
||||
'field' => [
|
||||
'title' => $this->t('Contact link'),
|
||||
'help' => $this->t('Provide a simple link to the user contact page.'),
|
||||
'id' => 'contact_link',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
151
web/core/modules/contact/src/MailHandler.php
Normal file
151
web/core/modules/contact/src/MailHandler.php
Normal file
@ -0,0 +1,151 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\contact;
|
||||
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Drupal\Core\Language\LanguageManagerInterface;
|
||||
use Drupal\Core\Mail\MailManagerInterface;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Drupal\Core\StringTranslation\StringTranslationTrait;
|
||||
use Drupal\Core\StringTranslation\TranslationInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
/**
|
||||
* Provides a class for handling assembly and dispatch of contact mail messages.
|
||||
*/
|
||||
class MailHandler implements MailHandlerInterface {
|
||||
|
||||
use StringTranslationTrait;
|
||||
|
||||
/**
|
||||
* Language manager service.
|
||||
*
|
||||
* @var \Drupal\Core\Language\LanguageManagerInterface
|
||||
*/
|
||||
protected $languageManager;
|
||||
|
||||
/**
|
||||
* Logger service.
|
||||
*
|
||||
* @var \Drupal\Core\Logger\LoggerChannelInterface
|
||||
*/
|
||||
protected $logger;
|
||||
|
||||
/**
|
||||
* Mail manager service.
|
||||
*
|
||||
* @var \Drupal\Core\Mail\MailManagerInterface
|
||||
*/
|
||||
protected $mailManager;
|
||||
|
||||
/**
|
||||
* The user entity storage handler.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityStorageInterface
|
||||
*/
|
||||
protected $userStorage;
|
||||
|
||||
/**
|
||||
* Constructs a new \Drupal\contact\MailHandler object.
|
||||
*
|
||||
* @param \Drupal\Core\Mail\MailManagerInterface $mail_manager
|
||||
* Mail manager service.
|
||||
* @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
|
||||
* Language manager service.
|
||||
* @param \Psr\Log\LoggerInterface $logger
|
||||
* A logger instance.
|
||||
* @param \Drupal\Core\StringTranslation\TranslationInterface $string_translation
|
||||
* String translation service.
|
||||
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
|
||||
* The entity type manager.
|
||||
*/
|
||||
public function __construct(MailManagerInterface $mail_manager, LanguageManagerInterface $language_manager, LoggerInterface $logger, TranslationInterface $string_translation, EntityTypeManagerInterface $entity_type_manager) {
|
||||
$this->languageManager = $language_manager;
|
||||
$this->mailManager = $mail_manager;
|
||||
$this->logger = $logger;
|
||||
$this->stringTranslation = $string_translation;
|
||||
$this->userStorage = $entity_type_manager->getStorage('user');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function sendMailMessages(MessageInterface $message, AccountInterface $sender) {
|
||||
// Clone the sender, as we make changes to mail and name properties.
|
||||
$sender_cloned = clone $this->userStorage->load($sender->id());
|
||||
$params = [];
|
||||
$current_langcode = $this->languageManager->getCurrentLanguage()->getId();
|
||||
$recipient_langcode = $this->languageManager->getDefaultLanguage()->getId();
|
||||
$contact_form = $message->getContactForm();
|
||||
|
||||
if ($sender_cloned->isAnonymous()) {
|
||||
// At this point, $sender contains an anonymous user, so we need to take
|
||||
// over the submitted form values.
|
||||
$sender_cloned->name = $message->getSenderName();
|
||||
$sender_cloned->mail = $message->getSenderMail();
|
||||
|
||||
// For the email message, clarify that the sender name is not verified; it
|
||||
// could potentially clash with a username on this site.
|
||||
$sender_cloned->name = $this->t('@name (not verified)', ['@name' => $message->getSenderName()]);
|
||||
}
|
||||
|
||||
// Build email parameters.
|
||||
$params['contact_message'] = $message;
|
||||
$params['sender'] = $sender_cloned;
|
||||
|
||||
if (!$message->isPersonal()) {
|
||||
// Send to the form recipient(s), using the site's default language.
|
||||
$params['contact_form'] = $contact_form;
|
||||
|
||||
$to = implode(', ', $contact_form->getRecipients());
|
||||
}
|
||||
elseif ($recipient = $message->getPersonalRecipient()) {
|
||||
// Send to the user in the user's preferred language.
|
||||
$to = $recipient->getEmail();
|
||||
$recipient_langcode = $recipient->getPreferredLangcode();
|
||||
$params['recipient'] = $recipient;
|
||||
}
|
||||
else {
|
||||
throw new MailHandlerException('Unable to determine message recipient');
|
||||
}
|
||||
|
||||
// Send email to the recipient(s).
|
||||
$key_prefix = $message->isPersonal() ? 'user' : 'page';
|
||||
$this->mailManager->mail('contact', $key_prefix . '_mail', $to, $recipient_langcode, $params, $sender_cloned->getEmail());
|
||||
|
||||
// If requested, send a copy to the user, using the current language.
|
||||
if ($message->copySender()) {
|
||||
$this->mailManager->mail('contact', $key_prefix . '_copy', $sender_cloned->getEmail(), $current_langcode, $params, $sender_cloned->getEmail());
|
||||
}
|
||||
|
||||
// If configured, send an auto-reply, using the current language.
|
||||
if (!$message->isPersonal() && $contact_form->getReply()) {
|
||||
// User contact forms do not support an auto-reply message, so this
|
||||
// message always originates from the site.
|
||||
if (!$sender_cloned->getEmail()) {
|
||||
$this->logger->error('Error sending auto-reply, missing sender email address in %contact_form', [
|
||||
'%contact_form' => $contact_form->label(),
|
||||
]);
|
||||
}
|
||||
else {
|
||||
$this->mailManager->mail('contact', 'page_autoreply', $sender_cloned->getEmail(), $current_langcode, $params);
|
||||
}
|
||||
}
|
||||
|
||||
if (!$message->isPersonal()) {
|
||||
$this->logger->info('%sender-name (@sender-from) sent an email regarding %contact_form.', [
|
||||
'%sender-name' => $sender_cloned->getAccountName(),
|
||||
'@sender-from' => $sender_cloned->getEmail() ?? '',
|
||||
'%contact_form' => $contact_form->label(),
|
||||
]);
|
||||
}
|
||||
else {
|
||||
$this->logger->info('%sender-name (@sender-from) sent %recipient-name an email.', [
|
||||
'%sender-name' => $sender_cloned->getAccountName(),
|
||||
'@sender-from' => $sender_cloned->getEmail(),
|
||||
'%recipient-name' => $message->getPersonalRecipient()->getAccountName(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
8
web/core/modules/contact/src/MailHandlerException.php
Normal file
8
web/core/modules/contact/src/MailHandlerException.php
Normal file
@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\contact;
|
||||
|
||||
/**
|
||||
* Exception thrown by MailHandler when unable to determine message recipient.
|
||||
*/
|
||||
class MailHandlerException extends \RuntimeException {}
|
||||
30
web/core/modules/contact/src/MailHandlerInterface.php
Normal file
30
web/core/modules/contact/src/MailHandlerInterface.php
Normal file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\contact;
|
||||
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
|
||||
/**
|
||||
* Provides an interface for assembly and dispatch of contact mail messages.
|
||||
*/
|
||||
interface MailHandlerInterface {
|
||||
|
||||
/**
|
||||
* Sends mail messages as appropriate for a given Message form submission.
|
||||
*
|
||||
* Can potentially send up to three messages as follows:
|
||||
* - To the configured recipient;
|
||||
* - Auto-reply to the sender; and
|
||||
* - Carbon copy to the sender.
|
||||
*
|
||||
* @param \Drupal\contact\MessageInterface $message
|
||||
* Submitted message entity.
|
||||
* @param \Drupal\Core\Session\AccountInterface $sender
|
||||
* User that submitted the message entity form.
|
||||
*
|
||||
* @throws \Drupal\contact\MailHandlerException
|
||||
* When unable to determine message recipient.
|
||||
*/
|
||||
public function sendMailMessages(MessageInterface $message, AccountInterface $sender);
|
||||
|
||||
}
|
||||
237
web/core/modules/contact/src/MessageForm.php
Normal file
237
web/core/modules/contact/src/MessageForm.php
Normal file
@ -0,0 +1,237 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\contact;
|
||||
|
||||
use Drupal\Component\Datetime\TimeInterface;
|
||||
use Drupal\Core\Datetime\DateFormatterInterface;
|
||||
use Drupal\Core\Entity\ContentEntityForm;
|
||||
use Drupal\Core\Entity\EntityRepositoryInterface;
|
||||
use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
|
||||
use Drupal\Core\Flood\FloodInterface;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Language\LanguageManagerInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Form controller for contact message forms.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class MessageForm extends ContentEntityForm {
|
||||
|
||||
/**
|
||||
* The message being used by this form.
|
||||
*
|
||||
* @var \Drupal\contact\MessageInterface
|
||||
*/
|
||||
protected $entity;
|
||||
|
||||
/**
|
||||
* The flood control mechanism.
|
||||
*
|
||||
* @var \Drupal\Core\Flood\FloodInterface
|
||||
*/
|
||||
protected $flood;
|
||||
|
||||
/**
|
||||
* The language manager service.
|
||||
*
|
||||
* @var \Drupal\Core\Language\LanguageManagerInterface
|
||||
*/
|
||||
protected $languageManager;
|
||||
|
||||
/**
|
||||
* The contact mail handler service.
|
||||
*
|
||||
* @var \Drupal\contact\MailHandlerInterface
|
||||
*/
|
||||
protected $mailHandler;
|
||||
|
||||
/**
|
||||
* The date formatter service.
|
||||
*
|
||||
* @var \Drupal\Core\Datetime\DateFormatterInterface
|
||||
*/
|
||||
protected $dateFormatter;
|
||||
|
||||
/**
|
||||
* Constructs a MessageForm object.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityRepositoryInterface $entity_repository
|
||||
* The entity repository.
|
||||
* @param \Drupal\Core\Flood\FloodInterface $flood
|
||||
* The flood control mechanism.
|
||||
* @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
|
||||
* The language manager service.
|
||||
* @param \Drupal\contact\MailHandlerInterface $mail_handler
|
||||
* The contact mail handler service.
|
||||
* @param \Drupal\Core\Datetime\DateFormatterInterface $date_formatter
|
||||
* The date service.
|
||||
* @param \Drupal\Core\Entity\EntityTypeBundleInfoInterface $entity_type_bundle_info
|
||||
* The entity type bundle service.
|
||||
* @param \Drupal\Component\Datetime\TimeInterface $time
|
||||
* The time service.
|
||||
*/
|
||||
public function __construct(EntityRepositoryInterface $entity_repository, FloodInterface $flood, LanguageManagerInterface $language_manager, MailHandlerInterface $mail_handler, DateFormatterInterface $date_formatter, ?EntityTypeBundleInfoInterface $entity_type_bundle_info = NULL, ?TimeInterface $time = NULL) {
|
||||
parent::__construct($entity_repository, $entity_type_bundle_info, $time);
|
||||
$this->flood = $flood;
|
||||
$this->languageManager = $language_manager;
|
||||
$this->mailHandler = $mail_handler;
|
||||
$this->dateFormatter = $date_formatter;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('entity.repository'),
|
||||
$container->get('flood'),
|
||||
$container->get('language_manager'),
|
||||
$container->get('contact.mail_handler'),
|
||||
$container->get('date.formatter'),
|
||||
$container->get('entity_type.bundle.info'),
|
||||
$container->get('datetime.time')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function form(array $form, FormStateInterface $form_state) {
|
||||
$user = $this->currentUser();
|
||||
$message = $this->entity;
|
||||
$form = parent::form($form, $form_state);
|
||||
$form['#attributes']['class'][] = 'contact-form';
|
||||
|
||||
if (!empty($message->preview)) {
|
||||
$form['preview'] = [
|
||||
'#theme_wrappers' => ['container__preview'],
|
||||
'#attributes' => ['class' => ['preview']],
|
||||
];
|
||||
$form['preview']['message'] = $this->entityTypeManager->getViewBuilder('contact_message')->view($message, 'full');
|
||||
}
|
||||
|
||||
$form['name'] = [
|
||||
'#type' => 'textfield',
|
||||
'#title' => $this->t('Your name'),
|
||||
'#maxlength' => 255,
|
||||
'#required' => TRUE,
|
||||
];
|
||||
$form['mail'] = [
|
||||
'#type' => 'email',
|
||||
'#title' => $this->t('Your email address'),
|
||||
'#required' => TRUE,
|
||||
];
|
||||
// Do not allow authenticated users to alter the name or email values to
|
||||
// prevent the impersonation of other users.
|
||||
if ($user->isAuthenticated()) {
|
||||
$form['name']['#type'] = 'item';
|
||||
$form['name']['#value'] = $user->getDisplayName();
|
||||
$form['name']['#required'] = FALSE;
|
||||
$form['name']['#plain_text'] = $user->getDisplayName();
|
||||
|
||||
$form['mail']['#type'] = 'item';
|
||||
$form['mail']['#value'] = $user->getEmail();
|
||||
$form['mail']['#required'] = FALSE;
|
||||
$form['mail']['#plain_text'] = $user->getEmail();
|
||||
}
|
||||
|
||||
// The user contact form has a preset recipient.
|
||||
if ($message->isPersonal()) {
|
||||
$form['recipient'] = [
|
||||
'#type' => 'item',
|
||||
'#title' => $this->t('To'),
|
||||
'#value' => $message->getPersonalRecipient()->id(),
|
||||
'name' => [
|
||||
'#theme' => 'username',
|
||||
'#account' => $message->getPersonalRecipient(),
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
$form['copy'] = [
|
||||
'#type' => 'checkbox',
|
||||
'#title' => $this->t('Send yourself a copy'),
|
||||
// Do not allow anonymous users to send themselves a copy, because it can
|
||||
// be abused to spam people.
|
||||
'#access' => $user->isAuthenticated(),
|
||||
];
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function actions(array $form, FormStateInterface $form_state) {
|
||||
$elements = parent::actions($form, $form_state);
|
||||
$elements['submit']['#value'] = $this->t('Send message');
|
||||
$elements['preview'] = [
|
||||
'#type' => 'submit',
|
||||
'#value' => $this->t('Preview'),
|
||||
'#submit' => ['::submitForm', '::preview'],
|
||||
'#access' => !empty($form_state->getStorage()['form_display']->getComponent('preview')),
|
||||
];
|
||||
return $elements;
|
||||
}
|
||||
|
||||
/**
|
||||
* Form submission handler for the 'preview' action.
|
||||
*/
|
||||
public function preview(array $form, FormStateInterface $form_state) {
|
||||
$message = $this->entity;
|
||||
$message->preview = TRUE;
|
||||
$form_state->setRebuild();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validateForm(array &$form, FormStateInterface $form_state) {
|
||||
$message = parent::validateForm($form, $form_state);
|
||||
|
||||
// Check if flood control has been activated for sending emails.
|
||||
if (!$this->currentUser()->hasPermission('administer contact forms') && (!$message->isPersonal() || !$this->currentUser()->hasPermission('administer users'))) {
|
||||
$limit = $this->config('contact.settings')->get('flood.limit');
|
||||
$interval = $this->config('contact.settings')->get('flood.interval');
|
||||
|
||||
if (!$this->flood->isAllowed('contact', $limit, $interval)) {
|
||||
$form_state->setErrorByName('', $this->t('You cannot send more than %limit messages in @interval. Try again later.', [
|
||||
'%limit' => $limit,
|
||||
'@interval' => $this->dateFormatter->formatInterval($interval),
|
||||
]));
|
||||
}
|
||||
}
|
||||
|
||||
return $message;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function save(array $form, FormStateInterface $form_state) {
|
||||
$message = $this->entity;
|
||||
$user = $this->currentUser();
|
||||
// Save the message. In core this is a no-op but should contrib wish to
|
||||
// implement message storage, this will make the task of swapping in a real
|
||||
// storage controller straight-forward.
|
||||
$message->save();
|
||||
$this->mailHandler->sendMailMessages($message, $user);
|
||||
$contact_form = $message->getContactForm();
|
||||
|
||||
$this->flood->register('contact', $this->config('contact.settings')->get('flood.interval'));
|
||||
if ($submission_message = $contact_form->getMessage()) {
|
||||
$this->messenger()->addStatus($submission_message);
|
||||
}
|
||||
|
||||
// To avoid false error messages caused by flood control, redirect away from
|
||||
// the contact form; either to the contacted user account or the front page.
|
||||
if ($message->isPersonal() && $user->hasPermission('access user profiles')) {
|
||||
$form_state->setRedirectUrl($message->getPersonalRecipient()->toUrl());
|
||||
}
|
||||
else {
|
||||
$form_state->setRedirectUrl($contact_form->getRedirectUrl());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
116
web/core/modules/contact/src/MessageInterface.php
Normal file
116
web/core/modules/contact/src/MessageInterface.php
Normal file
@ -0,0 +1,116 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\contact;
|
||||
|
||||
use Drupal\Core\Entity\ContentEntityInterface;
|
||||
|
||||
/**
|
||||
* Provides an interface defining a contact message entity.
|
||||
*/
|
||||
interface MessageInterface extends ContentEntityInterface {
|
||||
|
||||
/**
|
||||
* Returns the form this contact message belongs to.
|
||||
*
|
||||
* @return \Drupal\contact\ContactFormInterface
|
||||
* The contact form entity.
|
||||
*/
|
||||
public function getContactForm();
|
||||
|
||||
/**
|
||||
* Returns the name of the sender.
|
||||
*
|
||||
* @return string
|
||||
* The name of the message sender.
|
||||
*/
|
||||
public function getSenderName();
|
||||
|
||||
/**
|
||||
* Sets the name of the message sender.
|
||||
*
|
||||
* @param string $sender_name
|
||||
* The name of the message sender.
|
||||
*/
|
||||
public function setSenderName($sender_name);
|
||||
|
||||
/**
|
||||
* Returns the email address of the sender.
|
||||
*
|
||||
* @return string
|
||||
* The email address of the message sender.
|
||||
*/
|
||||
public function getSenderMail();
|
||||
|
||||
/**
|
||||
* Sets the email address of the sender.
|
||||
*
|
||||
* @param string $sender_mail
|
||||
* The email address of the message sender.
|
||||
*/
|
||||
public function setSenderMail($sender_mail);
|
||||
|
||||
/**
|
||||
* Returns the message subject.
|
||||
*
|
||||
* @return string
|
||||
* The message subject.
|
||||
*/
|
||||
public function getSubject();
|
||||
|
||||
/**
|
||||
* Sets the subject for the email.
|
||||
*
|
||||
* @param string $subject
|
||||
* The message subject.
|
||||
*/
|
||||
public function setSubject($subject);
|
||||
|
||||
/**
|
||||
* Returns the message body.
|
||||
*
|
||||
* @return string
|
||||
* The message body.
|
||||
*/
|
||||
public function getMessage();
|
||||
|
||||
/**
|
||||
* Sets the email message to send.
|
||||
*
|
||||
* @param string $message
|
||||
* The message body.
|
||||
*/
|
||||
public function setMessage($message);
|
||||
|
||||
/**
|
||||
* Returns TRUE if a copy should be sent to the sender.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if a copy should be sent, FALSE if not.
|
||||
*/
|
||||
public function copySender();
|
||||
|
||||
/**
|
||||
* Sets if the sender should receive a copy of this email or not.
|
||||
*
|
||||
* @param bool $inform
|
||||
* TRUE if a copy should be sent, FALSE if not.
|
||||
*/
|
||||
public function setCopySender($inform);
|
||||
|
||||
/**
|
||||
* Returns TRUE if this is the personal contact form.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if the message bundle is personal.
|
||||
*/
|
||||
public function isPersonal();
|
||||
|
||||
/**
|
||||
* Returns the user this message is being sent to.
|
||||
*
|
||||
* @return \Drupal\user\UserInterface
|
||||
* The user entity of the recipient, NULL if this is not a personal message.
|
||||
*/
|
||||
public function getPersonalRecipient();
|
||||
|
||||
}
|
||||
46
web/core/modules/contact/src/MessageViewBuilder.php
Normal file
46
web/core/modules/contact/src/MessageViewBuilder.php
Normal file
@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\contact;
|
||||
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Entity\EntityViewBuilder;
|
||||
use Drupal\Core\Render\Element;
|
||||
|
||||
/**
|
||||
* Render controller for contact messages.
|
||||
*/
|
||||
class MessageViewBuilder 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.
|
||||
unset($build['#theme']);
|
||||
return $build;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function view(EntityInterface $entity, $view_mode = 'full', $langcode = NULL) {
|
||||
$build = parent::view($entity, $view_mode, $langcode);
|
||||
|
||||
if ($view_mode == 'mail') {
|
||||
// Convert field labels into headings.
|
||||
// @todo Improve \Drupal\Core\Mail\MailFormatHelper::htmlToText() to
|
||||
// convert DIVs correctly.
|
||||
foreach (Element::children($build) as $key) {
|
||||
if (isset($build[$key]['#label_display']) && $build[$key]['#label_display'] == 'above') {
|
||||
$build[$key] += ['#prefix' => ''];
|
||||
$build[$key]['#prefix'] = $build[$key]['#title'] . ":\n";
|
||||
$build[$key]['#label_display'] = 'hidden';
|
||||
}
|
||||
}
|
||||
}
|
||||
return $build;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\contact\Plugin\migrate\source;
|
||||
|
||||
use Drupal\migrate\Row;
|
||||
use Drupal\migrate_drupal\Plugin\migrate\source\DrupalSqlBase;
|
||||
|
||||
/**
|
||||
* Drupal 6/7 contact category source from database.
|
||||
*
|
||||
* For available configuration keys, refer to the parent classes.
|
||||
*
|
||||
* @see \Drupal\migrate\Plugin\migrate\source\SqlBase
|
||||
* @see \Drupal\migrate\Plugin\migrate\source\SourcePluginBase
|
||||
*
|
||||
* @MigrateSource(
|
||||
* id = "contact_category",
|
||||
* source_module = "contact"
|
||||
* )
|
||||
*/
|
||||
class ContactCategory extends DrupalSqlBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function query() {
|
||||
$query = $this->select('contact', 'c')
|
||||
->fields('c', [
|
||||
'cid',
|
||||
'category',
|
||||
'recipients',
|
||||
'reply',
|
||||
'weight',
|
||||
'selected',
|
||||
]
|
||||
);
|
||||
$query->orderBy('c.cid');
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function prepareRow(Row $row) {
|
||||
$row->setSourceProperty('recipients', explode(',', $row->getSourceProperty('recipients')));
|
||||
return parent::prepareRow($row);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function fields() {
|
||||
return [
|
||||
'cid' => $this->t('Primary Key: Unique category ID.'),
|
||||
'category' => $this->t('Category name.'),
|
||||
'recipients' => $this->t('Comma-separated list of recipient email addresses.'),
|
||||
'reply' => $this->t('Text of the auto-reply message.'),
|
||||
'weight' => $this->t("The category's weight."),
|
||||
'selected' => $this->t('Flag to indicate whether or not category is selected by default. (1 = Yes, 0 = No)'),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getIds() {
|
||||
$ids['cid']['type'] = 'integer';
|
||||
return $ids;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\contact\Plugin\migrate\source;
|
||||
|
||||
use Drupal\migrate_drupal\Plugin\migrate\source\Variable;
|
||||
|
||||
/**
|
||||
* Drupal 6/7 contact settings source from database.
|
||||
*
|
||||
* For available configuration keys, refer to the parent classes.
|
||||
*
|
||||
* @see \Drupal\migrate\Plugin\migrate\source\SqlBase
|
||||
* @see \Drupal\migrate\Plugin\migrate\source\SourcePluginBase
|
||||
*
|
||||
* @MigrateSource(
|
||||
* id = "contact_settings",
|
||||
* source_module = "contact"
|
||||
* )
|
||||
*/
|
||||
class ContactSettings extends Variable {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function initializeIterator() {
|
||||
$default_category = $this->select('contact', 'c')
|
||||
->fields('c', ['cid'])
|
||||
->condition('c.selected', 1)
|
||||
->execute()
|
||||
->fetchField();
|
||||
return new \ArrayIterator([$this->values() + ['default_category' => $default_category]]);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\contact\Plugin\rest\resource;
|
||||
|
||||
use Drupal\rest\Plugin\rest\resource\EntityResource;
|
||||
|
||||
/**
|
||||
* Customizes the entity REST Resource plugin for Contact's Message entities.
|
||||
*
|
||||
* Message entities are not stored, so they cannot be:
|
||||
* - retrieved (GET)
|
||||
* - modified (PATCH)
|
||||
* - deleted (DELETE)
|
||||
* Messages can only be sent/created (POST).
|
||||
*/
|
||||
class ContactMessageResource extends EntityResource {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function availableMethods() {
|
||||
return ['POST'];
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\contact\Plugin\views\field;
|
||||
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\views\Attribute\ViewsField;
|
||||
use Drupal\views\Plugin\views\field\LinkBase;
|
||||
use Drupal\views\ResultRow;
|
||||
|
||||
/**
|
||||
* Defines a field that links to the user contact page, if access is permitted.
|
||||
*
|
||||
* @ingroup views_field_handlers
|
||||
*/
|
||||
#[ViewsField("contact_link")]
|
||||
class ContactLink extends LinkBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildOptionsForm(&$form, FormStateInterface $form_state) {
|
||||
parent::buildOptionsForm($form, $form_state);
|
||||
$form['text']['#title'] = $this->t('Link label');
|
||||
$form['text']['#required'] = TRUE;
|
||||
$form['text']['#default_value'] = empty($this->options['text']) ? $this->getDefaultLabel() : $this->options['text'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getUrlInfo(ResultRow $row) {
|
||||
$entity = $this->getEntity($row);
|
||||
if (!$entity) {
|
||||
return NULL;
|
||||
}
|
||||
return Url::fromRoute('entity.user.contact_form', ['user' => $entity->id()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function renderLink(ResultRow $row) {
|
||||
$entity = $this->getEntity($row);
|
||||
if (!$entity) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$this->options['alter']['make_link'] = TRUE;
|
||||
$this->options['alter']['url'] = $this->getUrlInfo($row);
|
||||
|
||||
$title = $this->t('Contact %user', ['%user' => $entity->label()]);
|
||||
$this->options['alter']['attributes'] = ['title' => $title];
|
||||
|
||||
if (!empty($this->options['text'])) {
|
||||
return $this->options['text'];
|
||||
}
|
||||
else {
|
||||
return $title;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getDefaultLabel() {
|
||||
return $this->t('contact');
|
||||
}
|
||||
|
||||
}
|
||||
36
web/core/modules/contact/tests/drupal-7.contact.database.php
Normal file
36
web/core/modules/contact/tests/drupal-7.contact.database.php
Normal file
@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Database additions for Drupal\contact\Tests\ContactUpgradePathTest.
|
||||
*
|
||||
* This dump only contains data for the contact module. The
|
||||
* drupal-7.filled.bare.php file is imported before this dump, so the two form
|
||||
* the database structure expected in tests altogether.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
$connection = \Drupal::database();
|
||||
// Update the default category to that it is not selected.
|
||||
$connection->update('contact')
|
||||
->fields(['selected' => '0'])
|
||||
->condition('cid', '1')
|
||||
->execute();
|
||||
|
||||
// Add a custom contact category.
|
||||
$connection->insert('contact')->fields([
|
||||
'category',
|
||||
'recipients',
|
||||
'reply',
|
||||
'weight',
|
||||
'selected',
|
||||
])
|
||||
->values([
|
||||
'category' => 'Upgrade test',
|
||||
'recipients' => 'test1@example.com,test2@example.com',
|
||||
'reply' => 'Test reply',
|
||||
'weight' => 1,
|
||||
'selected' => 1,
|
||||
])
|
||||
->execute();
|
||||
@ -0,0 +1,9 @@
|
||||
# Schema for configuration files of the Contact Storage Test module.
|
||||
|
||||
contact.form.*.third_party.contact_storage_test:
|
||||
type: mapping
|
||||
label: 'Per-contact form storage settings'
|
||||
mapping:
|
||||
send_a_pony:
|
||||
type: boolean
|
||||
label: 'Send a Pony'
|
||||
@ -0,0 +1,8 @@
|
||||
name: 'Contact test storage'
|
||||
type: module
|
||||
description: 'Tests that contact messages can be stored.'
|
||||
package: Testing
|
||||
version: VERSION
|
||||
dependencies:
|
||||
- drupal:contact
|
||||
- drupal:user
|
||||
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains install and update hooks.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Drupal\Core\Entity\Sql\SqlContentEntityStorage;
|
||||
|
||||
/**
|
||||
* Implements hook_install().
|
||||
*/
|
||||
function contact_storage_test_install(): void {
|
||||
$entity_definition_update_manager = \Drupal::entityDefinitionUpdateManager();
|
||||
|
||||
$original = $entity_definition_update_manager->getEntityType('contact_message');
|
||||
$entity_definition_update_manager->uninstallEntityType($original);
|
||||
|
||||
// Update the entity type definition and make it use the default SQL storage.
|
||||
// @see contact_storage_test_entity_type_alter()
|
||||
$entity_type = clone $original;
|
||||
$entity_type->setStorageClass(SqlContentEntityStorage::class);
|
||||
$keys = $entity_type->getKeys();
|
||||
$keys['id'] = 'id';
|
||||
$entity_type->set('entity_keys', $keys);
|
||||
$entity_type->set('base_table', 'contact_message');
|
||||
$entity_definition_update_manager->installEntityType($entity_type);
|
||||
}
|
||||
@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\contact_storage_test\Hook;
|
||||
|
||||
use Drupal\contact\ContactFormInterface;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Field\BaseFieldDefinition;
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Drupal\Core\Hook\Attribute\Hook;
|
||||
|
||||
/**
|
||||
* Hook implementations for contact_storage_test.
|
||||
*/
|
||||
class ContactStorageTestHooks {
|
||||
|
||||
/**
|
||||
* Implements hook_entity_base_field_info().
|
||||
*/
|
||||
#[Hook('entity_base_field_info')]
|
||||
public function entityBaseFieldInfo(EntityTypeInterface $entity_type): array {
|
||||
$fields = [];
|
||||
if ($entity_type->id() == 'contact_message') {
|
||||
$fields['id'] = BaseFieldDefinition::create('integer')->setLabel('Message ID')->setDescription('The message ID.')->setReadOnly(TRUE)->setSetting('unsigned', TRUE);
|
||||
}
|
||||
return $fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_entity_type_alter().
|
||||
*/
|
||||
#[Hook('entity_type_alter')]
|
||||
public function entityTypeAlter(array &$entity_types) : void {
|
||||
/** @var \Drupal\Core\Entity\EntityTypeInterface[] $entity_types */
|
||||
// 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';
|
||||
$entity_types['contact_message']->set('entity_keys', $keys);
|
||||
$entity_types['contact_message']->set('base_table', 'contact_message');
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_form_FORM_ID_alter() for contact_form_form().
|
||||
*/
|
||||
#[Hook('form_contact_form_form_alter')]
|
||||
public function formContactFormFormAlter(&$form, FormStateInterface $form_state) : void {
|
||||
/** @var \Drupal\contact\ContactFormInterface $contact_form */
|
||||
$contact_form = $form_state->getFormObject()->getEntity();
|
||||
$form['send_a_pony'] = [
|
||||
'#type' => 'checkbox',
|
||||
'#title' => 'Send submitters a voucher for a free pony.',
|
||||
'#description' => 'Enable to send an additional email with a free pony voucher to anyone who submits the form.',
|
||||
'#default_value' => $contact_form->getThirdPartySetting('contact_storage_test', 'send_a_pony', FALSE),
|
||||
];
|
||||
$form['#entity_builders'][] = [$this, 'contactFormBuilder'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Entity builder for the contact form edit form with third party options.
|
||||
*
|
||||
* @see contact_storage_test_form_contact_form_edit_form_alter()
|
||||
*/
|
||||
public function contactFormBuilder($entity_type, ContactFormInterface $contact_form, &$form, FormStateInterface $form_state): void {
|
||||
$contact_form->setThirdPartySetting('contact_storage_test', 'send_a_pony', $form_state->getValue('send_a_pony'));
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,9 @@
|
||||
langcode: en
|
||||
status: true
|
||||
id: feedback
|
||||
label: 'Website feedback'
|
||||
recipients: { }
|
||||
reply: ''
|
||||
weight: 0
|
||||
message: 'Your message has been sent.'
|
||||
redirect: ''
|
||||
@ -0,0 +1,7 @@
|
||||
name: 'Contact test module'
|
||||
type: module
|
||||
description: 'Contains test contact form.'
|
||||
package: Testing
|
||||
version: VERSION
|
||||
dependencies:
|
||||
- drupal:contact
|
||||
@ -0,0 +1,9 @@
|
||||
name: 'Contact test views'
|
||||
type: module
|
||||
description: 'Provides default views for views contact tests.'
|
||||
package: Testing
|
||||
version: VERSION
|
||||
dependencies:
|
||||
- drupal:contact
|
||||
- drupal:views
|
||||
- drupal:user
|
||||
@ -0,0 +1,148 @@
|
||||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
module:
|
||||
- contact
|
||||
- user
|
||||
id: test_contact_link
|
||||
label: test_contact_link
|
||||
module: views
|
||||
description: ''
|
||||
tag: ''
|
||||
base_table: users_field_data
|
||||
base_field: uid
|
||||
display:
|
||||
default:
|
||||
display_plugin: default
|
||||
id: default
|
||||
display_title: Default
|
||||
position: 1
|
||||
display_options:
|
||||
access:
|
||||
type: none
|
||||
cache:
|
||||
type: tag
|
||||
options: { }
|
||||
query:
|
||||
type: views_query
|
||||
options:
|
||||
disable_sql_rewrite: false
|
||||
distinct: false
|
||||
replica: false
|
||||
query_comment: ''
|
||||
query_tags: { }
|
||||
exposed_form:
|
||||
type: basic
|
||||
options:
|
||||
submit_button: Apply
|
||||
reset_button: false
|
||||
reset_button_label: Reset
|
||||
exposed_sorts_label: 'Sort by'
|
||||
expose_sort_order: true
|
||||
sort_asc_label: Asc
|
||||
sort_desc_label: Desc
|
||||
pager:
|
||||
type: full
|
||||
options:
|
||||
items_per_page: 10
|
||||
offset: 0
|
||||
id: 0
|
||||
total_pages: null
|
||||
expose:
|
||||
items_per_page: false
|
||||
items_per_page_label: 'Items per page'
|
||||
items_per_page_options: '5, 10, 20, 40, 60'
|
||||
items_per_page_options_all: false
|
||||
items_per_page_options_all_label: '- All -'
|
||||
offset: false
|
||||
offset_label: Offset
|
||||
tags:
|
||||
previous: '‹ Previous'
|
||||
next: 'Next ›'
|
||||
first: '« First'
|
||||
last: 'Last »'
|
||||
quantity: 9
|
||||
style:
|
||||
type: default
|
||||
row:
|
||||
type: fields
|
||||
fields:
|
||||
name:
|
||||
id: name
|
||||
table: users_field_data
|
||||
field: name
|
||||
label: ''
|
||||
alter:
|
||||
alter_text: false
|
||||
make_link: false
|
||||
absolute: false
|
||||
trim: false
|
||||
word_boundary: false
|
||||
ellipsis: false
|
||||
strip_tags: false
|
||||
html: false
|
||||
hide_empty: false
|
||||
empty_zero: false
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
exclude: 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_alter_empty: true
|
||||
plugin_id: field
|
||||
type: user_name
|
||||
entity_type: user
|
||||
entity_field: name
|
||||
contact:
|
||||
id: contact
|
||||
table: users
|
||||
field: contact
|
||||
plugin_id: contact_link
|
||||
exclude: false
|
||||
entity_type: user
|
||||
filters:
|
||||
status:
|
||||
value: '1'
|
||||
table: users_field_data
|
||||
field: status
|
||||
id: status
|
||||
expose:
|
||||
operator: '0'
|
||||
group: 1
|
||||
plugin_id: boolean
|
||||
entity_type: user
|
||||
entity_field: status
|
||||
sorts: { }
|
||||
title: test_contact_link
|
||||
header: { }
|
||||
footer: { }
|
||||
empty: { }
|
||||
relationships: { }
|
||||
arguments: { }
|
||||
display_extenders: { }
|
||||
cache_metadata:
|
||||
contexts:
|
||||
- 'languages:language_content'
|
||||
- 'languages:language_interface'
|
||||
max-age: 0
|
||||
page_1:
|
||||
display_plugin: page
|
||||
id: page_1
|
||||
display_title: Page
|
||||
position: 1
|
||||
display_options:
|
||||
display_extenders: { }
|
||||
path: test-contact-link
|
||||
cache_metadata:
|
||||
contexts:
|
||||
- 'languages:language_content'
|
||||
- 'languages:language_interface'
|
||||
max-age: 0
|
||||
@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\Tests\contact\Functional;
|
||||
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
use Drupal\Tests\language\Traits\LanguageTestTrait;
|
||||
|
||||
/**
|
||||
* Tests contact messages with language module.
|
||||
*
|
||||
* This is to ensure that a contact form by default does not show the language
|
||||
* select, but it does so when it's enabled from the content language settings
|
||||
* page.
|
||||
*
|
||||
* @group contact
|
||||
*/
|
||||
class ContactLanguageTest extends BrowserTestBase {
|
||||
|
||||
use LanguageTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $modules = [
|
||||
'contact',
|
||||
'language',
|
||||
'contact_test',
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $defaultTheme = 'stark';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
// Create and log in administrative user.
|
||||
$admin_user = $this->drupalCreateUser([
|
||||
'access site-wide contact form',
|
||||
'administer languages',
|
||||
]);
|
||||
$this->drupalLogin($admin_user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests configuration options with language enabled.
|
||||
*/
|
||||
public function testContactLanguage(): void {
|
||||
// Ensure that contact form by default does not show the language select.
|
||||
$this->drupalGet('contact');
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
$this->assertSession()->fieldNotExists('edit-langcode-0-value');
|
||||
|
||||
// Enable translations for feedback contact messages.
|
||||
static::enableBundleTranslation('contact_message', 'feedback');
|
||||
|
||||
// Ensure that contact form now shows the language select.
|
||||
$this->drupalGet('contact');
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
$this->assertSession()->fieldExists('edit-langcode-0-value');
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,395 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\Tests\contact\Functional;
|
||||
|
||||
use Drupal\Component\Utility\Html;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Drupal\Core\Test\AssertMailTrait;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
use Drupal\Tests\system\Functional\Cache\AssertPageCacheContextsAndTagsTrait;
|
||||
use Drupal\user\RoleInterface;
|
||||
|
||||
/**
|
||||
* Tests personal contact form functionality.
|
||||
*
|
||||
* @group contact
|
||||
*/
|
||||
class ContactPersonalTest extends BrowserTestBase {
|
||||
|
||||
use AssertMailTrait;
|
||||
use AssertPageCacheContextsAndTagsTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $modules = ['contact', 'dblog', 'mail_html_test'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $defaultTheme = 'stark';
|
||||
|
||||
/**
|
||||
* 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 function setUp(): void {
|
||||
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(): void {
|
||||
// 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->assertSession()->assertEscaped($mail);
|
||||
$message = $this->submitPersonalContact($this->contactUser);
|
||||
$mails = $this->getMails();
|
||||
$this->assertCount(1, $mails);
|
||||
$mail = $mails[0];
|
||||
$this->assertEquals($this->contactUser->getEmail(), $mail['to']);
|
||||
$this->assertEquals($this->config('system.site')->get('mail'), $mail['from']);
|
||||
$this->assertEquals($this->webUser->getEmail(), $mail['reply-to']);
|
||||
$this->assertEquals('user_mail', $mail['key']);
|
||||
$subject = '[' . $this->config('system.site')->get('name') . '] ' . $message['subject[0][value]'];
|
||||
$this->assertEquals($subject, $mail['subject'], 'Subject is in sent message.');
|
||||
$this->assertStringContainsString('Hello ' . $this->contactUser->getDisplayName(), $mail['body'], 'Recipient name is in sent message.');
|
||||
$this->assertStringContainsString($this->webUser->getDisplayName(), $mail['body'], 'Sender name is in sent message.');
|
||||
$this->assertStringContainsString($message['message[0][value]'], $mail['body'], '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');
|
||||
$this->assertSession()->responseContains($this->webUser->getAccountName() . " (" . HTML::escape($this->webUser->getEmail()) . ") sent " . $this->contactUser->getAccountName() . " an email.");
|
||||
// Ensure an unescaped version of the email does not exist anywhere.
|
||||
$this->assertSession()->responseNotContains($this->webUser->getEmail());
|
||||
|
||||
// Test HTML mails.
|
||||
$mail_config = $this->config('system.mail');
|
||||
$mail_config->set('interface.default', 'test_html_mail_collector');
|
||||
$mail_config->save();
|
||||
|
||||
$this->drupalLogin($this->webUser);
|
||||
$message['message[0][value]'] = 'This <i>is</i> a more <b>specific</b> <sup>test</sup>, the emails are formatted now.';
|
||||
$message = $this->submitPersonalContact($this->contactUser, $message);
|
||||
|
||||
// Assert mail content.
|
||||
$this->assertMailString('body', 'Hello ' . $this->contactUser->getDisplayName(), 1);
|
||||
$this->assertMailString('body', $this->webUser->getDisplayName(), 1);
|
||||
$this->assertMailString('body', Html::escape($message['message[0][value]']), 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests access to the personal contact form.
|
||||
*/
|
||||
public function testPersonalContactAccess(): void {
|
||||
// Test allowed access to admin user's contact form.
|
||||
$this->drupalLogin($this->webUser);
|
||||
$this->drupalGet('user/' . $this->adminUser->id() . '/contact');
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
// Check the page title is properly displayed.
|
||||
$this->assertSession()->pageTextContains('Contact ' . $this->adminUser->getDisplayName());
|
||||
|
||||
// Test denied access to admin user's own contact form.
|
||||
$this->drupalLogout();
|
||||
$this->drupalLogin($this->adminUser);
|
||||
$this->drupalGet('user/' . $this->adminUser->id() . '/contact');
|
||||
$this->assertSession()->statusCodeEquals(403);
|
||||
|
||||
// Test allowed access to user with contact form enabled.
|
||||
$this->drupalLogin($this->webUser);
|
||||
$this->drupalGet('user/' . $this->contactUser->id() . '/contact');
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
|
||||
// Test that there is no access to personal contact forms for users
|
||||
// without an email address configured.
|
||||
$original_email = $this->contactUser->getEmail();
|
||||
$this->contactUser->setEmail(FALSE)->save();
|
||||
$this->drupalGet('user/' . $this->contactUser->id() . '/contact');
|
||||
$this->assertSession()->statusCodeEquals(404);
|
||||
|
||||
// Test that the 'contact tab' does not appear on the user profiles
|
||||
// for users without an email address configured.
|
||||
$this->drupalGet('user/' . $this->contactUser->id());
|
||||
$contact_link = '/user/' . $this->contactUser->id() . '/contact';
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
$this->assertSession()->linkByHrefNotExists($contact_link, 'The "contact" tab is hidden on profiles for users with no email address');
|
||||
|
||||
// Restore original email address.
|
||||
$this->contactUser->setEmail($original_email)->save();
|
||||
|
||||
// Test denied access to the user's own contact form.
|
||||
$this->drupalGet('user/' . $this->webUser->id() . '/contact');
|
||||
$this->assertSession()->statusCodeEquals(403);
|
||||
|
||||
// Test always denied access to the anonymous user contact form.
|
||||
$this->drupalGet('user/0/contact');
|
||||
$this->assertSession()->statusCodeEquals(403);
|
||||
|
||||
// Test that anonymous users can access the contact form.
|
||||
$this->drupalLogout();
|
||||
user_role_grant_permissions(RoleInterface::ANONYMOUS_ID, ['access user contact forms']);
|
||||
$this->drupalGet('user/' . $this->contactUser->id() . '/contact');
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
|
||||
// Test that anonymous users can access admin user's contact form.
|
||||
$this->drupalGet('user/' . $this->adminUser->id() . '/contact');
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
$this->assertCacheContext('user');
|
||||
|
||||
// Revoke the personal contact permission for the anonymous user.
|
||||
user_role_revoke_permissions(RoleInterface::ANONYMOUS_ID, ['access user contact forms']);
|
||||
$this->drupalGet('user/' . $this->contactUser->id() . '/contact');
|
||||
$this->assertSession()->statusCodeEquals(403);
|
||||
$this->assertCacheContext('user');
|
||||
$this->drupalGet('user/' . $this->adminUser->id() . '/contact');
|
||||
$this->assertSession()->statusCodeEquals(403);
|
||||
|
||||
// Disable the personal contact form.
|
||||
$this->drupalLogin($this->adminUser);
|
||||
$edit = ['contact_default_status' => FALSE];
|
||||
$this->drupalGet('admin/config/people/accounts');
|
||||
$this->submitForm($edit, 'Save configuration');
|
||||
$this->assertSession()->pageTextContains('The configuration options have been saved.');
|
||||
$this->drupalLogout();
|
||||
|
||||
// Re-create our contacted user with personal contact forms disabled by
|
||||
// default.
|
||||
$this->contactUser = $this->drupalCreateUser();
|
||||
|
||||
// Test denied access to a user with contact form disabled.
|
||||
$this->drupalLogin($this->webUser);
|
||||
$this->drupalGet('user/' . $this->contactUser->id() . '/contact');
|
||||
$this->assertSession()->statusCodeEquals(403);
|
||||
|
||||
// Test allowed access for admin user to a user with contact form disabled.
|
||||
$this->drupalLogin($this->adminUser);
|
||||
$this->drupalGet('user/' . $this->contactUser->id() . '/contact');
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
|
||||
// Re-create our contacted user as a blocked user.
|
||||
$this->contactUser = $this->drupalCreateUser();
|
||||
$this->contactUser->block();
|
||||
$this->contactUser->save();
|
||||
|
||||
// Test that blocked users can still be contacted by admin.
|
||||
$this->drupalGet('user/' . $this->contactUser->id() . '/contact');
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
|
||||
// Test that blocked users cannot be contacted by non-admins.
|
||||
$this->drupalLogin($this->webUser);
|
||||
$this->drupalGet('user/' . $this->contactUser->id() . '/contact');
|
||||
$this->assertSession()->statusCodeEquals(403);
|
||||
|
||||
// Test enabling and disabling the contact page through the user profile
|
||||
// form.
|
||||
$this->drupalGet('user/' . $this->webUser->id() . '/edit');
|
||||
$this->assertSession()->checkboxNotChecked('edit-contact--2');
|
||||
$this->assertNull(\Drupal::service('user.data')->get('contact', $this->webUser->id(), 'enabled'), 'Personal contact form disabled');
|
||||
$this->submitForm(['contact' => TRUE], 'Save');
|
||||
$this->assertSession()->checkboxChecked('edit-contact--2');
|
||||
$this->assertNotEmpty(\Drupal::service('user.data')->get('contact', $this->webUser->id(), 'enabled'), 'Personal contact form enabled');
|
||||
|
||||
// Test with disabled global default contact form in combination with a user
|
||||
// that has the contact form enabled.
|
||||
$this->config('contact.settings')->set('user_default_enabled', FALSE)->save();
|
||||
$this->contactUser = $this->drupalCreateUser();
|
||||
\Drupal::service('user.data')->set('contact', $this->contactUser->id(), 'enabled', 1);
|
||||
|
||||
$this->drupalGet('user/' . $this->contactUser->id() . '/contact');
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the personal contact form flood protection.
|
||||
*/
|
||||
public function testPersonalContactFlood(): void {
|
||||
$flood_limit = 3;
|
||||
$this->config('contact.settings')->set('flood.limit', $flood_limit)->save();
|
||||
|
||||
$this->drupalLogin($this->webUser);
|
||||
|
||||
// Submit contact form with correct values and check flood interval.
|
||||
for ($i = 0; $i < $flood_limit; $i++) {
|
||||
$this->submitPersonalContact($this->contactUser);
|
||||
$this->assertSession()->pageTextContains('Your message has been sent.');
|
||||
}
|
||||
|
||||
// Submit contact form one over limit.
|
||||
$this->submitPersonalContact($this->contactUser);
|
||||
// Normal user should be denied access to flooded contact form.
|
||||
$interval = \Drupal::service('date.formatter')->formatInterval($this->config('contact.settings')->get('flood.interval'));
|
||||
$this->assertSession()->pageTextContains("You cannot send more than 3 messages in {$interval}. Try again later.");
|
||||
|
||||
// Test that the admin user can still access the contact form even though
|
||||
// the flood limit was reached.
|
||||
$this->drupalLogin($this->adminUser);
|
||||
$this->assertSession()->pageTextNotContains('Try again later.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the personal contact form based access when an admin adds users.
|
||||
*/
|
||||
public function testAdminContact(): void {
|
||||
user_role_grant_permissions(RoleInterface::ANONYMOUS_ID, ['access user contact forms']);
|
||||
$this->checkContactAccess(200);
|
||||
$this->checkContactAccess(403, FALSE);
|
||||
$config = $this->config('contact.settings');
|
||||
$config->set('user_default_enabled', FALSE);
|
||||
$config->save();
|
||||
$this->checkContactAccess(403);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a user and then checks contact form access.
|
||||
*
|
||||
* @param int $response
|
||||
* The expected response code.
|
||||
* @param bool $contact_value
|
||||
* (optional) The value the contact field should be set too.
|
||||
*/
|
||||
protected function checkContactAccess($response, $contact_value = NULL): void {
|
||||
$this->drupalLogin($this->adminUser);
|
||||
$this->drupalGet('admin/people/create');
|
||||
if ($this->config('contact.settings')->get('user_default_enabled', TRUE)) {
|
||||
$this->assertSession()->checkboxChecked('edit-contact--2');
|
||||
}
|
||||
else {
|
||||
$this->assertSession()->checkboxNotChecked('edit-contact--2');
|
||||
}
|
||||
$name = $this->randomMachineName();
|
||||
$edit = [
|
||||
'name' => $name,
|
||||
'mail' => $this->randomMachineName() . '@example.com',
|
||||
'pass[pass1]' => $pass = $this->randomString(),
|
||||
'pass[pass2]' => $pass,
|
||||
'notify' => FALSE,
|
||||
];
|
||||
if (isset($contact_value)) {
|
||||
$edit['contact'] = $contact_value;
|
||||
}
|
||||
$this->drupalGet('admin/people/create');
|
||||
$this->submitForm($edit, 'Create new account');
|
||||
$user = user_load_by_name($name);
|
||||
$this->drupalLogout();
|
||||
|
||||
$this->drupalGet('user/' . $user->id() . '/contact');
|
||||
$this->assertSession()->statusCodeEquals($response);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fills out a user's personal contact form and submits it.
|
||||
*
|
||||
* @param \Drupal\Core\Session\AccountInterface $account
|
||||
* A user object of the user being contacted.
|
||||
* @param array $message
|
||||
* (optional) An array with the form fields being used. Defaults to an empty
|
||||
* array.
|
||||
* @param bool $user_copy
|
||||
* (optional) A boolean to determine whether to send a user copy email.
|
||||
* Defaults to FALSE.
|
||||
*
|
||||
* @return array
|
||||
* An array with the form fields being used.
|
||||
*/
|
||||
protected function submitPersonalContact(AccountInterface $account, array $message = [], bool $user_copy = FALSE): array {
|
||||
$message += [
|
||||
'subject[0][value]' => $this->randomMachineName(16) . '< " =+ >',
|
||||
'message[0][value]' => $this->randomMachineName(64) . '< " =+ >',
|
||||
'copy' => $user_copy,
|
||||
];
|
||||
$this->drupalGet('user/' . $account->id() . '/contact');
|
||||
$this->submitForm($message, 'Send message');
|
||||
return $message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the opt-out message is included correctly in contact emails.
|
||||
*/
|
||||
public function testPersonalContactForm(): void {
|
||||
$opt_out_message = "If you don't want to receive such messages, you can change your settings at";
|
||||
|
||||
// Send an email from an admin (should not contain the opt-out message).
|
||||
$this->drupalLogin($this->adminUser);
|
||||
$this->submitPersonalContact($this->contactUser);
|
||||
$this->drupalLogout();
|
||||
|
||||
$this->assertStringNotContainsString($opt_out_message, $this->getMails()[0]['body'], 'Opt-out message excluded in email.');
|
||||
|
||||
// Send an email from a non-admin (should contain the opt-out message).
|
||||
$this->drupalLogin($this->webUser);
|
||||
$this->submitPersonalContact($this->contactUser);
|
||||
|
||||
$this->assertMailString('body', $opt_out_message, 1, 'Opt-out message included in email.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the opt-out message is not included in user copy emails.
|
||||
*/
|
||||
public function testPersonalContactFormUserCopy(): void {
|
||||
$opt_out_message = "If you don't want to receive such messages, you can change your settings at";
|
||||
|
||||
// Send an email from an admin.
|
||||
$this->drupalLogin($this->adminUser);
|
||||
$this->submitPersonalContact($this->contactUser, [], TRUE);
|
||||
$this->drupalLogout();
|
||||
|
||||
// Send an email from a non-admin.
|
||||
$this->drupalLogin($this->webUser);
|
||||
$this->submitPersonalContact($this->contactUser, [], TRUE);
|
||||
|
||||
$user_copy_emails = $this->getMails(['id' => 'contact_user_copy']);
|
||||
|
||||
// Tests that the opt-out message is not included in admin user copy emails.
|
||||
$this->assertStringNotContainsString($opt_out_message, $user_copy_emails[0]['body'], 'Opt-out message not included in admin user copy email.');
|
||||
// Tests that the opt-out message is not included in non-admin user copy
|
||||
// emails.
|
||||
$this->assertStringNotContainsString($opt_out_message, $user_copy_emails[1]['body'], 'Opt-out message not included in non-admin user copy email.');
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,627 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\Tests\contact\Functional;
|
||||
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\contact\Entity\ContactForm;
|
||||
use Drupal\Core\Mail\MailFormatHelper;
|
||||
use Drupal\Core\Test\AssertMailTrait;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Drupal\Tests\field_ui\Traits\FieldUiTestTrait;
|
||||
use Drupal\user\RoleInterface;
|
||||
|
||||
/**
|
||||
* Tests site-wide contact form functionality.
|
||||
*
|
||||
* @see \Drupal\Tests\contact\Functional\ContactStorageTest
|
||||
*
|
||||
* @group contact
|
||||
*/
|
||||
class ContactSitewideTest extends BrowserTestBase {
|
||||
|
||||
use FieldUiTestTrait;
|
||||
use AssertMailTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $modules = [
|
||||
'text',
|
||||
'contact',
|
||||
'field_ui',
|
||||
'contact_test',
|
||||
'block',
|
||||
'error_service_test',
|
||||
'dblog',
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $defaultTheme = 'stark';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
$this->drupalPlaceBlock('system_breadcrumb_block');
|
||||
$this->drupalPlaceBlock('local_tasks_block');
|
||||
$this->drupalPlaceBlock('local_actions_block');
|
||||
$this->drupalPlaceBlock('page_title_block');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests configuration options and the site-wide contact form.
|
||||
*/
|
||||
public function testSiteWideContact(): void {
|
||||
// Tests name and email fields for authenticated and anonymous users.
|
||||
$this->drupalLogin($this->drupalCreateUser([
|
||||
'access site-wide contact form',
|
||||
]));
|
||||
$this->drupalGet('contact');
|
||||
|
||||
// Ensure that there is no textfield for name.
|
||||
$this->assertSession()->fieldNotExists('name');
|
||||
|
||||
// Ensure that there is no textfield for email.
|
||||
$this->assertSession()->fieldNotExists('mail');
|
||||
|
||||
// Logout and retrieve the page as an anonymous user
|
||||
$this->drupalLogout();
|
||||
user_role_grant_permissions('anonymous', ['access site-wide contact form']);
|
||||
$this->drupalGet('contact');
|
||||
|
||||
// Ensure that there is textfield for name.
|
||||
$this->assertSession()->fieldExists('name');
|
||||
|
||||
// Ensure that there is textfield for email.
|
||||
$this->assertSession()->fieldExists('mail');
|
||||
|
||||
// Create and log in administrative user.
|
||||
$admin_user = $this->drupalCreateUser([
|
||||
'access site-wide contact form',
|
||||
'administer contact forms',
|
||||
'administer users',
|
||||
'administer account settings',
|
||||
'administer contact_message display',
|
||||
'administer contact_message fields',
|
||||
'administer contact_message form display',
|
||||
]);
|
||||
$this->drupalLogin($admin_user);
|
||||
|
||||
// Check the presence of expected cache tags.
|
||||
$this->drupalGet('contact');
|
||||
$this->assertSession()->responseHeaderContains('X-Drupal-Cache-Tags', 'config:contact.settings');
|
||||
|
||||
$flood_limit = 3;
|
||||
$this->config('contact.settings')
|
||||
->set('flood.limit', $flood_limit)
|
||||
->set('flood.interval', 600)
|
||||
->save();
|
||||
|
||||
// Set settings.
|
||||
$edit = [];
|
||||
$edit['contact_default_status'] = TRUE;
|
||||
$this->drupalGet('admin/config/people/accounts');
|
||||
$this->submitForm($edit, 'Save configuration');
|
||||
$this->assertSession()->pageTextContains('The configuration options have been saved.');
|
||||
|
||||
$this->drupalGet('admin/structure/contact');
|
||||
// Default form exists.
|
||||
$this->assertSession()->linkByHrefExists('admin/structure/contact/manage/feedback/delete');
|
||||
// User form could not be changed or deleted.
|
||||
// Cannot use ::assertNoLinkByHref as it does partial URL matching and with
|
||||
// field_ui enabled admin/structure/contact/manage/personal/fields exists.
|
||||
// @todo See https://www.drupal.org/node/2031223 for the above.
|
||||
$url = Url::fromRoute('entity.contact_form.edit_form', ['contact_form' => 'personal'])->toString();
|
||||
$this->assertSession()->elementNotExists('xpath', "//a[@href='{$url}']");
|
||||
$this->assertSession()->linkByHrefNotExists('admin/structure/contact/manage/personal/delete');
|
||||
|
||||
$this->drupalGet('admin/structure/contact/manage/personal');
|
||||
$this->assertSession()->statusCodeEquals(403);
|
||||
|
||||
// Delete old forms to ensure that new forms are used.
|
||||
$this->deleteContactForms();
|
||||
$this->drupalGet('admin/structure/contact');
|
||||
$this->assertSession()->pageTextContains('Personal');
|
||||
$this->assertSession()->linkByHrefNotExists('admin/structure/contact/manage/feedback');
|
||||
|
||||
// Ensure that the contact form won't be shown without forms.
|
||||
user_role_grant_permissions(RoleInterface::ANONYMOUS_ID, ['access site-wide contact form']);
|
||||
$this->drupalLogout();
|
||||
$this->drupalGet('contact');
|
||||
$this->assertSession()->statusCodeEquals(404);
|
||||
|
||||
$this->drupalLogin($admin_user);
|
||||
$this->drupalGet('contact');
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
$this->assertSession()->pageTextContains('The contact form has not been configured.');
|
||||
// Test access personal form via site-wide contact page.
|
||||
$this->drupalGet('contact/personal');
|
||||
$this->assertSession()->statusCodeEquals(403);
|
||||
|
||||
// Add forms.
|
||||
// Test invalid recipients.
|
||||
$invalid_recipients = ['invalid', 'invalid@', 'invalid@site.', '@site.', '@site.com'];
|
||||
foreach ($invalid_recipients as $invalid_recipient) {
|
||||
$this->addContactForm($this->randomMachineName(16), $this->randomMachineName(16), $invalid_recipient, '', FALSE);
|
||||
$this->assertSession()->pageTextContains($invalid_recipient . ' is an invalid email address.');
|
||||
}
|
||||
|
||||
// Test validation of empty form and recipients fields.
|
||||
$this->addContactForm('', '', '', '', TRUE);
|
||||
$this->assertSession()->pageTextContains('Label field is required.');
|
||||
$this->assertSession()->pageTextContains('Machine-readable name field is required.');
|
||||
$this->assertSession()->pageTextContains('Recipients field is required.');
|
||||
|
||||
// Test validation of max_length machine name.
|
||||
$recipients = ['simpletest&@example.com', 'simpletest2@example.com', 'simpletest3@example.com'];
|
||||
$max_length = EntityTypeInterface::BUNDLE_MAX_LENGTH;
|
||||
$max_length_exceeded = $max_length + 1;
|
||||
$this->addContactForm($id = $this->randomMachineName($max_length_exceeded), $label = $this->randomMachineName($max_length_exceeded), implode(',', [$recipients[0]]), '', TRUE);
|
||||
$this->assertSession()->pageTextContains('Machine-readable name cannot be longer than ' . $max_length . ' characters but is currently ' . $max_length_exceeded . ' characters long.');
|
||||
$this->addContactForm($id = $this->randomMachineName($max_length), $label = $this->randomMachineName($max_length), implode(',', [$recipients[0]]), '', TRUE);
|
||||
$this->assertSession()->pageTextContains('Contact form ' . $label . ' has been added.');
|
||||
|
||||
// Verify that the creation message contains a link to a contact form.
|
||||
$this->assertSession()->elementExists('xpath', '//div[@data-drupal-messages]//a[contains(@href, "contact/")]');
|
||||
|
||||
// Create first valid form.
|
||||
$this->addContactForm($id = $this->randomMachineName(16), $label = $this->randomMachineName(16), implode(',', [$recipients[0]]), '', TRUE);
|
||||
$this->assertSession()->pageTextContains('Contact form ' . $label . ' has been added.');
|
||||
|
||||
// Verify that the creation message contains a link to a contact form.
|
||||
$this->assertSession()->elementExists('xpath', '//div[@data-drupal-messages]//a[contains(@href, "contact/")]');
|
||||
|
||||
// Check that the form was created in site default language.
|
||||
$langcode = $this->config('contact.form.' . $id)->get('langcode');
|
||||
$default_langcode = \Drupal::languageManager()->getDefaultLanguage()->getId();
|
||||
$this->assertEquals($default_langcode, $langcode);
|
||||
|
||||
// Make sure the newly created form is included in the list of forms.
|
||||
$this->assertSession()->pageTextMatchesCount(2, '/' . $label . '/');
|
||||
|
||||
// Ensure that the recipient email is escaped on the listing.
|
||||
$this->drupalGet('admin/structure/contact');
|
||||
$this->assertSession()->assertEscaped($recipients[0]);
|
||||
|
||||
// Test update contact form.
|
||||
$this->updateContactForm($id, $label = $this->randomMachineName(16), implode(',', [$recipients[0], $recipients[1]]), $reply = $this->randomMachineName(30), FALSE, 'Your message has been sent.', '/user');
|
||||
$config = $this->config('contact.form.' . $id)->get();
|
||||
$this->assertEquals($label, $config['label']);
|
||||
$this->assertEquals([$recipients[0], $recipients[1]], $config['recipients']);
|
||||
$this->assertEquals($reply, $config['reply']);
|
||||
$this->assertNotEquals($this->config('contact.settings')->get('default_form'), $id);
|
||||
$this->assertSession()->pageTextContains('Contact form ' . $label . ' has been updated.');
|
||||
// Ensure the label is displayed on the contact page for this form.
|
||||
$this->drupalGet('contact/' . $id);
|
||||
$this->assertSession()->pageTextContains($label);
|
||||
|
||||
// Reset the form back to be the default form.
|
||||
$this->config('contact.settings')->set('default_form', $id)->save();
|
||||
|
||||
// Ensure that the contact form is shown without a form selection input.
|
||||
user_role_grant_permissions(RoleInterface::ANONYMOUS_ID, ['access site-wide contact form']);
|
||||
$this->drupalLogout();
|
||||
$this->drupalGet('contact');
|
||||
$this->assertSession()->pageTextContains('Your email address');
|
||||
$this->assertSession()->pageTextNotContains('Form');
|
||||
$this->drupalLogin($admin_user);
|
||||
|
||||
// Add more forms.
|
||||
$this->addContactForm($this->randomMachineName(16), $label = $this->randomMachineName(16), implode(',', [$recipients[0], $recipients[1]]), '', FALSE);
|
||||
$this->assertSession()->pageTextContains('Contact form ' . $label . ' has been added.');
|
||||
|
||||
$this->addContactForm($name = $this->randomMachineName(16), $label = $this->randomMachineName(16), implode(',', [$recipients[0], $recipients[1], $recipients[2]]), '', FALSE);
|
||||
$this->assertSession()->pageTextContains('Contact form ' . $label . ' has been added.');
|
||||
|
||||
// Try adding a form that already exists.
|
||||
$this->addContactForm($name, $label, '', '', FALSE);
|
||||
$this->assertSession()->pageTextNotContains("Contact form $label has been added.");
|
||||
$this->assertSession()->pageTextContains('The machine-readable name is already in use. It must be unique.');
|
||||
|
||||
$this->drupalLogout();
|
||||
|
||||
// Check to see that anonymous user cannot see contact page without
|
||||
// permission.
|
||||
user_role_revoke_permissions(RoleInterface::ANONYMOUS_ID, ['access site-wide contact form']);
|
||||
$this->drupalGet('contact');
|
||||
$this->assertSession()->statusCodeEquals(403);
|
||||
|
||||
// Give anonymous user permission and see that page is viewable.
|
||||
user_role_grant_permissions(RoleInterface::ANONYMOUS_ID, ['access site-wide contact form']);
|
||||
$this->drupalGet('contact');
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
|
||||
// Submit contact form with invalid values.
|
||||
$this->submitContact('', $recipients[0], $this->randomMachineName(16), $id, $this->randomMachineName(64));
|
||||
$this->assertSession()->pageTextContains('Your name field is required.');
|
||||
|
||||
$this->submitContact($this->randomMachineName(16), '', $this->randomMachineName(16), $id, $this->randomMachineName(64));
|
||||
$this->assertSession()->pageTextContains('Your email address field is required.');
|
||||
|
||||
$this->submitContact($this->randomMachineName(16), $invalid_recipients[0], $this->randomMachineName(16), $id, $this->randomMachineName(64));
|
||||
$this->assertSession()->pageTextContains('The email address invalid is not valid.');
|
||||
|
||||
$this->submitContact($this->randomMachineName(16), $recipients[0], '', $id, $this->randomMachineName(64));
|
||||
$this->assertSession()->pageTextContains('Subject field is required.');
|
||||
|
||||
$this->submitContact($this->randomMachineName(16), $recipients[0], $this->randomMachineName(16), $id, '');
|
||||
$this->assertSession()->pageTextContains('Message field is required.');
|
||||
|
||||
// Test contact form with no default form selected.
|
||||
$this->config('contact.settings')
|
||||
->set('default_form', NULL)
|
||||
->save();
|
||||
$this->drupalGet('contact');
|
||||
$this->assertSession()->statusCodeEquals(404);
|
||||
|
||||
// Try to access contact form with non-existing form IDs.
|
||||
$this->drupalGet('contact/0');
|
||||
$this->assertSession()->statusCodeEquals(404);
|
||||
$this->drupalGet('contact/' . $this->randomMachineName());
|
||||
$this->assertSession()->statusCodeEquals(404);
|
||||
|
||||
// Submit contact form with correct values and check flood interval.
|
||||
for ($i = 0; $i < $flood_limit; $i++) {
|
||||
$this->submitContact($this->randomMachineName(16), $recipients[0], $this->randomMachineName(16), $id, $this->randomMachineName(64));
|
||||
$this->assertSession()->pageTextContains('Your message has been sent.');
|
||||
}
|
||||
// Submit contact form one over limit.
|
||||
$this->submitContact($this->randomMachineName(16), $recipients[0], $this->randomMachineName(16), $id, $this->randomMachineName(64));
|
||||
$this->assertSession()->pageTextContains('You cannot send more than ' . $this->config('contact.settings')->get('flood.limit') . ' messages in 10 min. Try again later.');
|
||||
|
||||
// Test listing controller.
|
||||
$this->drupalLogin($admin_user);
|
||||
|
||||
$this->deleteContactForms();
|
||||
|
||||
$label = $this->randomMachineName(16);
|
||||
$recipients = implode(',', [$recipients[0], $recipients[1], $recipients[2]]);
|
||||
$contact_form = $this->randomMachineName(16);
|
||||
$this->addContactForm($contact_form, $label, $recipients, '', FALSE);
|
||||
$this->drupalGet('admin/structure/contact');
|
||||
$this->clickLink('Edit');
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
$this->assertSession()->fieldValueEquals('label', $label);
|
||||
|
||||
// Verify contact "View" tab exists.
|
||||
$this->assertSession()->linkExists('View');
|
||||
|
||||
// Test field UI and field integration.
|
||||
$this->drupalGet('admin/structure/contact');
|
||||
|
||||
// Test contact listing links to contact form.
|
||||
$this->assertSession()->elementExists('xpath', $this->assertSession()->buildXPathQuery('//table/tbody/tr/td/a[contains(@href, :href) and text()=:text]', [
|
||||
':href' => Url::fromRoute('entity.contact_form.canonical', ['contact_form' => $contact_form])->toString(),
|
||||
':text' => $label,
|
||||
]));
|
||||
|
||||
// Find out in which row the form we want to add a field to is.
|
||||
foreach ($this->xpath('//table/tbody/tr') as $row) {
|
||||
if ($row->findLink($label)) {
|
||||
$row->clickLink('Manage fields');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
$this->clickLink('Create a new field');
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
|
||||
// Create a simple textfield.
|
||||
$field_name = $this->randomMachineName();
|
||||
$field_label = $this->randomMachineName();
|
||||
$this->fieldUIAddNewField(NULL, $field_name, $field_label, 'text');
|
||||
$field_name = 'field_' . $field_name;
|
||||
|
||||
// Check preview field can be ordered.
|
||||
$this->drupalGet('admin/structure/contact/manage/' . $contact_form . '/form-display');
|
||||
$this->assertSession()->pageTextContains('Preview');
|
||||
|
||||
// Check that the field is displayed.
|
||||
$this->drupalGet('contact/' . $contact_form);
|
||||
$this->assertSession()->pageTextContains($field_label);
|
||||
|
||||
// Submit the contact form and verify the content.
|
||||
$edit = [
|
||||
'subject[0][value]' => $this->randomMachineName(),
|
||||
'message[0][value]' => $this->randomMachineName(),
|
||||
$field_name . '[0][value]' => $this->randomMachineName(),
|
||||
];
|
||||
$this->submitForm($edit, 'Send message');
|
||||
$mails = $this->getMails();
|
||||
$mail = array_pop($mails);
|
||||
$this->assertEquals(sprintf('[%s] %s', $label, $edit['subject[0][value]']), $mail['subject']);
|
||||
$this->assertStringContainsString($field_label, $mail['body']);
|
||||
$this->assertStringContainsString($edit[$field_name . '[0][value]'], $mail['body']);
|
||||
|
||||
// Test messages and redirect.
|
||||
/** @var \Drupal\contact\ContactFormInterface $form */
|
||||
$form = ContactForm::load($contact_form);
|
||||
$form->setMessage('Thanks for your submission.');
|
||||
$form->setRedirectPath('/user/' . $admin_user->id());
|
||||
$form->save();
|
||||
// Check that the field is displayed.
|
||||
$this->drupalGet('contact/' . $contact_form);
|
||||
|
||||
// Submit the contact form and verify the content.
|
||||
$edit = [
|
||||
'subject[0][value]' => $this->randomMachineName(),
|
||||
'message[0][value]' => $this->randomMachineName(),
|
||||
$field_name . '[0][value]' => $this->randomMachineName(),
|
||||
];
|
||||
$this->submitForm($edit, 'Send message');
|
||||
$this->assertSession()->pageTextContains('Thanks for your submission.');
|
||||
$this->assertSession()->addressEquals('user/' . $admin_user->id());
|
||||
|
||||
// Test Empty message.
|
||||
/** @var \Drupal\contact\ContactFormInterface $form */
|
||||
$form = ContactForm::load($contact_form);
|
||||
$form->setMessage('');
|
||||
$form->setRedirectPath('/user/' . $admin_user->id());
|
||||
$form->save();
|
||||
$this->drupalGet('admin/structure/contact/manage/' . $contact_form);
|
||||
// Check that the field is displayed.
|
||||
$this->drupalGet('contact/' . $contact_form);
|
||||
|
||||
// Submit the contact form and verify the content.
|
||||
$edit = [
|
||||
'subject[0][value]' => $this->randomMachineName(),
|
||||
'message[0][value]' => $this->randomMachineName(),
|
||||
$field_name . '[0][value]' => $this->randomMachineName(),
|
||||
];
|
||||
$this->submitForm($edit, 'Send message');
|
||||
// Verify that messages are not found.
|
||||
$this->assertSession()->elementNotExists('xpath', '//div[@data-drupal-messages]');
|
||||
$this->assertSession()->addressEquals('user/' . $admin_user->id());
|
||||
|
||||
// Test preview and visibility of the message field and label. Submit the
|
||||
// contact form and verify the content.
|
||||
$edit = [
|
||||
'subject[0][value]' => $this->randomMachineName(),
|
||||
'message[0][value]' => $this->randomMachineName(),
|
||||
$field_name . '[0][value]' => $this->randomMachineName(),
|
||||
];
|
||||
$this->drupalGet($form->toUrl('canonical'));
|
||||
$this->submitForm($edit, 'Preview');
|
||||
|
||||
// Message is now by default displayed twice, once for the form element and
|
||||
// once for the viewed message.
|
||||
$message = $edit['message[0][value]'];
|
||||
$this->assertSession()->pageTextMatchesCount(2, '/Message/');
|
||||
$this->assertSession()->pageTextMatchesCount(2, '/' . $message . '/');
|
||||
// Check for label and message in form element.
|
||||
$this->assertSession()->elementTextEquals('css', 'label[for="edit-message-0-value"]', 'Message');
|
||||
$this->assertSession()->fieldValueEquals('edit-message-0-value', $message);
|
||||
// Check for label and message in preview.
|
||||
$this->assertSession()->elementTextContains('css', '#edit-preview', 'Message');
|
||||
$this->assertSession()->elementTextContains('css', '#edit-preview', $message);
|
||||
|
||||
// Hide the message field label.
|
||||
$display_edit = [
|
||||
'fields[message][label]' => 'hidden',
|
||||
];
|
||||
$this->drupalGet('admin/structure/contact/manage/' . $contact_form . '/display');
|
||||
$this->submitForm($display_edit, 'Save');
|
||||
|
||||
$this->drupalGet($form->toUrl('canonical'));
|
||||
$this->submitForm($edit, 'Preview');
|
||||
// 'Message' should only be displayed once now with the actual message
|
||||
// displayed twice.
|
||||
$this->assertSession()->pageTextContainsOnce('Message');
|
||||
$this->assertSession()->pageTextMatchesCount(2, '/' . $message . '/');
|
||||
// Check for label and message in form element.
|
||||
$this->assertSession()->elementTextEquals('css', 'label[for="edit-message-0-value"]', 'Message');
|
||||
$this->assertSession()->fieldValueEquals('edit-message-0-value', $message);
|
||||
// Check for message in preview but no label.
|
||||
$this->assertSession()->elementTextNotContains('css', '#edit-preview', 'Message');
|
||||
$this->assertSession()->elementTextContains('css', '#edit-preview', $message);
|
||||
|
||||
// Set the preview field to 'hidden' in the view mode and check that the
|
||||
// field is hidden.
|
||||
$edit = [
|
||||
'fields[preview][region]' => 'hidden',
|
||||
];
|
||||
$this->drupalGet('admin/structure/contact/manage/' . $contact_form . '/form-display');
|
||||
$this->submitForm($edit, 'Save');
|
||||
$this->assertSession()->fieldExists('fields[preview][region]');
|
||||
|
||||
// Check that the field preview is not displayed in the form.
|
||||
$this->drupalGet('contact/' . $contact_form);
|
||||
$this->assertSession()->responseNotContains('Preview');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests auto-reply on the site-wide contact form.
|
||||
*/
|
||||
public function testAutoReply(): void {
|
||||
// Create and log in administrative user.
|
||||
$admin_user = $this->drupalCreateUser([
|
||||
'access site-wide contact form',
|
||||
'administer contact forms',
|
||||
'administer permissions',
|
||||
'administer users',
|
||||
'access site reports',
|
||||
]);
|
||||
$this->drupalLogin($admin_user);
|
||||
|
||||
// Set up three forms, 2 with an auto-reply and one without.
|
||||
$foo_autoreply = $this->randomMachineName(40);
|
||||
$bar_autoreply = $this->randomMachineName(40);
|
||||
$this->addContactForm('foo', 'foo', 'foo@example.com', $foo_autoreply, FALSE);
|
||||
$this->addContactForm('bar', 'bar', 'bar@example.com', $bar_autoreply, FALSE);
|
||||
$this->addContactForm('no_autoreply', 'no_autoreply', 'bar@example.com', '', FALSE);
|
||||
|
||||
// Log the current user out in order to test the name and email fields.
|
||||
$this->drupalLogout();
|
||||
user_role_grant_permissions(RoleInterface::ANONYMOUS_ID, ['access site-wide contact form']);
|
||||
|
||||
// Test the auto-reply for form 'foo'.
|
||||
$email = $this->randomMachineName(32) . '@example.com';
|
||||
$subject = $this->randomMachineName(64);
|
||||
$this->submitContact($this->randomMachineName(16), $email, $subject, 'foo', $this->randomString(128));
|
||||
|
||||
// We are testing the auto-reply, so there should be one email going to the
|
||||
// sender.
|
||||
$captured_emails = $this->getMails(['id' => 'contact_page_autoreply', 'to' => $email]);
|
||||
$this->assertCount(1, $captured_emails);
|
||||
$this->assertEquals(trim(MailFormatHelper::htmlToText($foo_autoreply)), trim($captured_emails[0]['body']));
|
||||
|
||||
// Test the auto-reply for form 'bar'.
|
||||
$email = $this->randomMachineName(32) . '@example.com';
|
||||
$this->submitContact($this->randomMachineName(16), $email, $this->randomString(64), 'bar', $this->randomString(128));
|
||||
|
||||
// Auto-reply for form 'bar' should result in one auto-reply email to the
|
||||
// sender.
|
||||
$captured_emails = $this->getMails(['id' => 'contact_page_autoreply', 'to' => $email]);
|
||||
$this->assertCount(1, $captured_emails);
|
||||
$this->assertEquals(trim(MailFormatHelper::htmlToText($bar_autoreply)), trim($captured_emails[0]['body']));
|
||||
|
||||
// Verify that no auto-reply is sent when the auto-reply field is left
|
||||
// blank.
|
||||
$email = $this->randomMachineName(32) . '@example.com';
|
||||
$this->submitContact($this->randomMachineName(16), $email, $this->randomString(64), 'no_autoreply', $this->randomString(128));
|
||||
$captured_emails = $this->getMails(['id' => 'contact_page_autoreply', 'to' => $email]);
|
||||
$this->assertCount(0, $captured_emails);
|
||||
|
||||
// Verify that the current error message doesn't show, that the auto-reply
|
||||
// doesn't get sent and the correct silent error gets logged.
|
||||
$email = '';
|
||||
\Drupal::service('entity_display.repository')
|
||||
->getFormDisplay('contact_message', 'foo')
|
||||
->removeComponent('mail')
|
||||
->save();
|
||||
$this->submitContact($this->randomMachineName(16), $email, $this->randomString(64), 'foo', $this->randomString(128));
|
||||
$this->assertSession()->pageTextNotContains('Unable to send email. Contact the site administrator if the problem persists.');
|
||||
$captured_emails = $this->getMails(['id' => 'contact_page_autoreply', 'to' => $email]);
|
||||
$this->assertCount(0, $captured_emails);
|
||||
$this->drupalLogin($admin_user);
|
||||
$this->drupalGet('admin/reports/dblog');
|
||||
$this->assertSession()->responseContains('Error sending auto-reply, missing sender email address in foo');
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a form.
|
||||
*
|
||||
* @param string $id
|
||||
* The form machine name.
|
||||
* @param string $label
|
||||
* The form label.
|
||||
* @param string $recipients
|
||||
* The list of recipient email addresses.
|
||||
* @param string $reply
|
||||
* The auto-reply text that is sent to a user upon completing the contact
|
||||
* form.
|
||||
* @param bool $selected
|
||||
* A Boolean indicating whether the form should be selected by default.
|
||||
* @param string $message
|
||||
* The message that will be displayed to a user upon completing the contact
|
||||
* form.
|
||||
* @param array $third_party_settings
|
||||
* Array of third party settings to be added to the posted form data.
|
||||
*/
|
||||
public function addContactForm($id, $label, $recipients, $reply, $selected, $message = 'Your message has been sent.', $third_party_settings = []): void {
|
||||
$edit = [];
|
||||
$edit['label'] = $label;
|
||||
$edit['id'] = $id;
|
||||
$edit['message'] = $message;
|
||||
$edit['recipients'] = $recipients;
|
||||
$edit['reply'] = $reply;
|
||||
$edit['selected'] = ($selected ? TRUE : FALSE);
|
||||
$edit += $third_party_settings;
|
||||
$this->drupalGet('admin/structure/contact/add');
|
||||
$this->submitForm($edit, 'Save');
|
||||
|
||||
// Ensure the statically cached bundle info is aware of the contact form
|
||||
// that was just created in the UI.
|
||||
$this->container->get('entity_type.bundle.info')->clearCachedBundles();
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates a form.
|
||||
*
|
||||
* @param string $id
|
||||
* The form machine name.
|
||||
* @param string $label
|
||||
* The form label.
|
||||
* @param string $recipients
|
||||
* The list of recipient email addresses.
|
||||
* @param string $reply
|
||||
* The auto-reply text that is sent to a user upon completing the contact
|
||||
* form.
|
||||
* @param bool $selected
|
||||
* A Boolean indicating whether the form should be selected by default.
|
||||
* @param string $message
|
||||
* The message that will be displayed to a user upon completing the contact
|
||||
* form.
|
||||
* @param string $redirect
|
||||
* The path where user will be redirect after this form has been submitted..
|
||||
*/
|
||||
public function updateContactForm($id, $label, $recipients, $reply, $selected, $message = 'Your message has been sent.', $redirect = '/'): void {
|
||||
$edit = [];
|
||||
$edit['label'] = $label;
|
||||
$edit['recipients'] = $recipients;
|
||||
$edit['reply'] = $reply;
|
||||
$edit['selected'] = ($selected ? TRUE : FALSE);
|
||||
$edit['message'] = $message;
|
||||
$edit['redirect'] = $redirect;
|
||||
$this->drupalGet("admin/structure/contact/manage/{$id}");
|
||||
$this->submitForm($edit, '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): void {
|
||||
$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->drupalGet('contact');
|
||||
$this->submitForm($edit, 'Send message');
|
||||
}
|
||||
else {
|
||||
$this->drupalGet('contact/' . $id);
|
||||
$this->submitForm($edit, 'Send message');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes all forms.
|
||||
*/
|
||||
public function deleteContactForms(): void {
|
||||
$contact_forms = ContactForm::loadMultiple();
|
||||
foreach ($contact_forms as $id => $contact_form) {
|
||||
if ($id == 'personal') {
|
||||
// Personal form could not be deleted.
|
||||
$this->drupalGet("admin/structure/contact/manage/$id/delete");
|
||||
$this->assertSession()->statusCodeEquals(403);
|
||||
}
|
||||
else {
|
||||
$this->drupalGet("admin/structure/contact/manage/{$id}/delete");
|
||||
$this->submitForm([], 'Delete');
|
||||
$this->assertSession()->pageTextContains("The contact form {$contact_form->label()} has been deleted.");
|
||||
$this->assertNull(ContactForm::load($id), "Form {$contact_form->label()} not found");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,81 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\Tests\contact\Functional;
|
||||
|
||||
use Drupal\contact\Entity\Message;
|
||||
use Drupal\user\RoleInterface;
|
||||
|
||||
/**
|
||||
* Tests storing contact messages.
|
||||
*
|
||||
* Note that the various test methods in ContactSitewideTest are also run by
|
||||
* this test. This is by design to ensure that regular contact.module functions
|
||||
* continue to work when a storage handler other than ContentEntityNullStorage
|
||||
* is enabled for contact Message entities.
|
||||
*
|
||||
* @group contact
|
||||
*/
|
||||
class ContactStorageTest extends ContactSitewideTest {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $modules = [
|
||||
'block',
|
||||
'text',
|
||||
'contact',
|
||||
'field_ui',
|
||||
'contact_storage_test',
|
||||
'contact_test',
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $defaultTheme = 'stark';
|
||||
|
||||
/**
|
||||
* Tests configuration options and the site-wide contact form.
|
||||
*/
|
||||
public function testContactStorage(): void {
|
||||
// Create and log in administrative user.
|
||||
$admin_user = $this->drupalCreateUser([
|
||||
'access site-wide contact form',
|
||||
'administer contact forms',
|
||||
'administer users',
|
||||
'administer account settings',
|
||||
'administer contact_message fields',
|
||||
]);
|
||||
$this->drupalLogin($admin_user);
|
||||
// Create first valid contact form.
|
||||
$mail = 'simpletest@example.com';
|
||||
$this->addContactForm($id = $this->randomMachineName(16), $label = $this->randomMachineName(16), implode(',', [$mail]), '', TRUE, 'Your message has been sent.', [
|
||||
'send_a_pony' => 1,
|
||||
]);
|
||||
$this->assertSession()->pageTextContains('Contact form ' . $label . ' has been added.');
|
||||
|
||||
// Ensure that anonymous can submit site-wide contact form.
|
||||
user_role_grant_permissions(RoleInterface::ANONYMOUS_ID, ['access site-wide contact form']);
|
||||
$this->drupalLogout();
|
||||
$this->drupalGet('contact');
|
||||
$this->assertSession()->pageTextContains('Your email address');
|
||||
$this->assertSession()->pageTextNotContains('Form');
|
||||
$this->submitContact($name = $this->randomMachineName(16), $mail, $subject = $this->randomMachineName(16), $id, $message = $this->randomMachineName(64));
|
||||
$this->assertSession()->pageTextContains('Your message has been sent.');
|
||||
|
||||
$messages = Message::loadMultiple();
|
||||
/** @var \Drupal\contact\Entity\Message $message */
|
||||
$message = reset($messages);
|
||||
$this->assertEquals($id, $message->getContactForm()->id());
|
||||
$this->assertTrue($message->getContactForm()->getThirdPartySetting('contact_storage_test', 'send_a_pony', FALSE));
|
||||
$this->assertEquals($name, $message->getSenderName());
|
||||
$this->assertEquals($subject, $message->getSubject());
|
||||
$this->assertEquals($mail, $message->getSenderMail());
|
||||
|
||||
$config = $this->config("contact.form.$id");
|
||||
$this->assertEquals($id, $config->get('id'));
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\Tests\contact\Functional;
|
||||
|
||||
use Drupal\Tests\system\Functional\Module\GenericModuleTestBase;
|
||||
|
||||
/**
|
||||
* Generic module test for contact.
|
||||
*
|
||||
* @group contact
|
||||
*/
|
||||
class GenericTest extends GenericModuleTestBase {}
|
||||
@ -0,0 +1,129 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\Tests\contact\Functional\Jsonapi;
|
||||
|
||||
use Drupal\jsonapi\JsonApiSpec;
|
||||
use Drupal\contact\Entity\ContactForm;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\Tests\jsonapi\Functional\ConfigEntityResourceTestBase;
|
||||
|
||||
/**
|
||||
* JSON:API integration test for the "ContactForm" config entity type.
|
||||
*
|
||||
* @group contact
|
||||
*/
|
||||
class ContactFormTest extends ConfigEntityResourceTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $modules = ['contact'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $entityTypeId = 'contact_form';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $resourceTypeName = 'contact_form--contact_form';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @var \Drupal\contact\ContactFormInterface
|
||||
*/
|
||||
protected $entity;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $defaultTheme = 'stark';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUpAuthorization($method): void {
|
||||
$this->grantPermissionsToTestedRole(['access site-wide contact form']);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function createEntity() {
|
||||
$contact_form = ContactForm::create([
|
||||
'id' => 'llama',
|
||||
'label' => 'Llama',
|
||||
'message' => 'Let us know what you think about llamas',
|
||||
'reply' => 'Llamas are indeed awesome!',
|
||||
'recipients' => [
|
||||
'llama@example.com',
|
||||
'contact@example.com',
|
||||
],
|
||||
]);
|
||||
$contact_form->save();
|
||||
|
||||
return $contact_form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getExpectedDocument(): array {
|
||||
$self_url = Url::fromUri('base:/jsonapi/contact_form/contact_form/' . $this->entity->uuid())->setAbsolute()->toString(TRUE)->getGeneratedUrl();
|
||||
return [
|
||||
'jsonapi' => [
|
||||
'meta' => [
|
||||
'links' => [
|
||||
'self' => ['href' => JsonApiSpec::SUPPORTED_SPECIFICATION_PERMALINK],
|
||||
],
|
||||
],
|
||||
'version' => JsonApiSpec::SUPPORTED_SPECIFICATION_VERSION,
|
||||
],
|
||||
'links' => [
|
||||
'self' => ['href' => $self_url],
|
||||
],
|
||||
'data' => [
|
||||
'id' => $this->entity->uuid(),
|
||||
'type' => 'contact_form--contact_form',
|
||||
'links' => [
|
||||
'self' => ['href' => $self_url],
|
||||
],
|
||||
'attributes' => [
|
||||
'dependencies' => [],
|
||||
'label' => 'Llama',
|
||||
'langcode' => 'en',
|
||||
'message' => 'Let us know what you think about llamas',
|
||||
'recipients' => [
|
||||
'llama@example.com',
|
||||
'contact@example.com',
|
||||
],
|
||||
'redirect' => NULL,
|
||||
'reply' => 'Llamas are indeed awesome!',
|
||||
'status' => TRUE,
|
||||
'weight' => 0,
|
||||
'drupal_internal__id' => 'llama',
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getPostDocument(): array {
|
||||
// @todo Update in https://www.drupal.org/node/2300677.
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getExpectedUnauthorizedAccessMessage($method): string {
|
||||
return "The 'access site-wide contact form' permission is required.";
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,203 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\Tests\contact\Functional\Jsonapi;
|
||||
|
||||
use Drupal\Component\Utility\NestedArray;
|
||||
use Drupal\contact\Entity\ContactForm;
|
||||
use Drupal\contact\Entity\Message;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\Tests\jsonapi\Functional\ResourceTestBase;
|
||||
use GuzzleHttp\RequestOptions;
|
||||
use Symfony\Component\Routing\Exception\RouteNotFoundException;
|
||||
|
||||
/**
|
||||
* JSON:API integration test for the "Message" content entity type.
|
||||
*
|
||||
* @group contact
|
||||
*/
|
||||
class MessageTest extends ResourceTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $modules = ['contact'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $defaultTheme = 'stark';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $entityTypeId = 'contact_message';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $resourceTypeName = 'contact_message--camelids';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @var \Drupal\contact\MessageInterface
|
||||
*/
|
||||
protected $entity;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $labelFieldName = 'subject';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUpAuthorization($method): void {
|
||||
$this->grantPermissionsToTestedRole(['access site-wide contact form']);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function createEntity() {
|
||||
if (!ContactForm::load('camelids')) {
|
||||
// Create a "Camelids" contact form.
|
||||
ContactForm::create([
|
||||
'id' => 'camelids',
|
||||
'label' => 'Llama',
|
||||
'message' => 'Let us know what you think about llamas',
|
||||
'reply' => 'Llamas are indeed awesome!',
|
||||
'recipients' => [
|
||||
'llama@example.com',
|
||||
'contact@example.com',
|
||||
],
|
||||
])->save();
|
||||
}
|
||||
|
||||
$message = Message::create([
|
||||
'contact_form' => 'camelids',
|
||||
'subject' => 'Llama Gabilondo',
|
||||
'message' => 'Llamas are awesome!',
|
||||
]);
|
||||
$message->save();
|
||||
|
||||
return $message;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getExpectedDocument() {
|
||||
throw new \Exception('Not yet supported.');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getPostDocument(): array {
|
||||
return [
|
||||
'data' => [
|
||||
'type' => 'contact_message--camelids',
|
||||
'attributes' => [
|
||||
'subject' => 'Drama llama',
|
||||
'message' => 'http://www.urbandictionary.com/define.php?term=drama%20llama',
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getExpectedUnauthorizedAccessMessage($method) {
|
||||
if ($method === 'POST') {
|
||||
return "The 'access site-wide contact form' permission is required.";
|
||||
}
|
||||
return parent::getExpectedUnauthorizedAccessMessage($method);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function testGetIndividual(): void {
|
||||
// Contact Message entities are not stored, so they cannot be retrieved.
|
||||
$this->expectException(RouteNotFoundException::class);
|
||||
$this->expectExceptionMessage('Route "jsonapi.contact_message--camelids.individual" does not exist.');
|
||||
|
||||
Url::fromRoute('jsonapi.contact_message--camelids.individual')->toString(TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doTestPatchIndividual(): void {
|
||||
// Contact Message entities are not stored, so they cannot be modified.
|
||||
$this->expectException(RouteNotFoundException::class);
|
||||
$this->expectExceptionMessage('Route "jsonapi.contact_message--camelids.individual" does not exist.');
|
||||
|
||||
Url::fromRoute('jsonapi.contact_message--camelids.individual')->toString(TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doTestDeleteIndividual(): void {
|
||||
// Contact Message entities are not stored, so they cannot be deleted.
|
||||
$this->expectException(RouteNotFoundException::class);
|
||||
$this->expectExceptionMessage('Route "jsonapi.contact_message--camelids.individual" does not exist.');
|
||||
|
||||
Url::fromRoute('jsonapi.contact_message--camelids.individual')->toString(TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function testRelated(): void {
|
||||
// Contact Message entities are not stored, so they cannot be retrieved.
|
||||
$this->expectException(RouteNotFoundException::class);
|
||||
$this->expectExceptionMessage('Route "jsonapi.contact_message--camelids.related" does not exist.');
|
||||
|
||||
Url::fromRoute('jsonapi.contact_message--camelids.related')->toString(TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function testRelationships(): void {
|
||||
// Contact Message entities are not stored, so they cannot be retrieved.
|
||||
$this->expectException(RouteNotFoundException::class);
|
||||
$this->expectExceptionMessage('Route "jsonapi.contact_message--camelids.relationship.get" does not exist.');
|
||||
|
||||
Url::fromRoute('jsonapi.contact_message--camelids.relationship.get')->toString(TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function testCollection(): void {
|
||||
$collection_url = Url::fromRoute('jsonapi.contact_message--camelids.collection.post')->setAbsolute(TRUE);
|
||||
$request_options = [];
|
||||
$request_options[RequestOptions::HEADERS]['Accept'] = 'application/vnd.api+json';
|
||||
$request_options = NestedArray::mergeDeep($request_options, $this->getAuthenticationRequestOptions());
|
||||
|
||||
// 405 because Message entities are not stored, so they cannot be retrieved,
|
||||
// yet the same URL can be used to POST them.
|
||||
$response = $this->request('GET', $collection_url, $request_options);
|
||||
$this->assertSame(405, $response->getStatusCode());
|
||||
$this->assertSame(['POST'], $response->getHeader('Allow'));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function testRevisions(): void {
|
||||
// Contact Message entities are not stored, so they cannot be retrieved.
|
||||
$this->expectException(RouteNotFoundException::class);
|
||||
$this->expectExceptionMessage('Route "jsonapi.contact_message--camelids.individual" does not exist.');
|
||||
|
||||
Url::fromRoute('jsonapi.contact_message--camelids.individual')->toString(TRUE);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\Tests\contact\Functional\Rest;
|
||||
|
||||
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class ContactFormJsonAnonTest extends ContactFormResourceTestBase {
|
||||
|
||||
use AnonResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $defaultTheme = 'stark';
|
||||
|
||||
}
|
||||
@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\Tests\contact\Functional\Rest;
|
||||
|
||||
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class ContactFormJsonBasicAuthTest extends ContactFormResourceTestBase {
|
||||
|
||||
use BasicAuthResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $modules = ['basic_auth'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $defaultTheme = 'stark';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $auth = 'basic_auth';
|
||||
|
||||
}
|
||||
@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\Tests\contact\Functional\Rest;
|
||||
|
||||
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class ContactFormJsonCookieTest extends ContactFormResourceTestBase {
|
||||
|
||||
use CookieResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $auth = 'cookie';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $defaultTheme = 'stark';
|
||||
|
||||
}
|
||||
@ -0,0 +1,103 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\Tests\contact\Functional\Rest;
|
||||
|
||||
use Drupal\contact\Entity\ContactForm;
|
||||
use Drupal\Tests\rest\Functional\EntityResource\ConfigEntityResourceTestBase;
|
||||
|
||||
/**
|
||||
* Resource test base for the ContactForm entity.
|
||||
*/
|
||||
abstract class ContactFormResourceTestBase extends ConfigEntityResourceTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $modules = ['contact'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $entityTypeId = 'contact_form';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $patchProtectedFieldNames = [];
|
||||
|
||||
/**
|
||||
* @var \Drupal\contact\Entity\ContactForm
|
||||
*/
|
||||
protected $entity;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUpAuthorization($method) {
|
||||
switch ($method) {
|
||||
case 'GET':
|
||||
$this->grantPermissionsToTestedRole(['access site-wide contact form']);
|
||||
default:
|
||||
$this->grantPermissionsToTestedRole(['administer contact forms']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function createEntity() {
|
||||
$contact_form = ContactForm::create([
|
||||
'id' => 'llama',
|
||||
'label' => 'Llama',
|
||||
'message' => 'Let us know what you think about llamas',
|
||||
'reply' => 'Llamas are indeed awesome!',
|
||||
'recipients' => [
|
||||
'llama@example.com',
|
||||
'contact@example.com',
|
||||
],
|
||||
]);
|
||||
$contact_form->save();
|
||||
|
||||
return $contact_form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getExpectedNormalizedEntity() {
|
||||
return [
|
||||
'dependencies' => [],
|
||||
'id' => 'llama',
|
||||
'label' => 'Llama',
|
||||
'langcode' => 'en',
|
||||
'message' => 'Let us know what you think about llamas',
|
||||
'recipients' => [
|
||||
'llama@example.com',
|
||||
'contact@example.com',
|
||||
],
|
||||
'redirect' => NULL,
|
||||
'reply' => 'Llamas are indeed awesome!',
|
||||
'status' => TRUE,
|
||||
'uuid' => $this->entity->uuid(),
|
||||
'weight' => 0,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getNormalizedPostEntity() {
|
||||
// @todo Update in https://www.drupal.org/node/2300677.
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getExpectedUnauthorizedAccessMessage($method) {
|
||||
return "The 'access site-wide contact form' permission is required.";
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\Tests\contact\Functional\Rest;
|
||||
|
||||
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
|
||||
use Drupal\Tests\rest\Functional\EntityResource\XmlEntityNormalizationQuirksTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class ContactFormXmlAnonTest extends ContactFormResourceTestBase {
|
||||
|
||||
use AnonResourceTestTrait;
|
||||
use XmlEntityNormalizationQuirksTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'xml';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'text/xml; charset=UTF-8';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $defaultTheme = 'stark';
|
||||
|
||||
}
|
||||
@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\Tests\contact\Functional\Rest;
|
||||
|
||||
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
|
||||
use Drupal\Tests\rest\Functional\EntityResource\XmlEntityNormalizationQuirksTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class ContactFormXmlBasicAuthTest extends ContactFormResourceTestBase {
|
||||
|
||||
use BasicAuthResourceTestTrait;
|
||||
use XmlEntityNormalizationQuirksTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $modules = ['basic_auth'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $defaultTheme = 'stark';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'xml';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'text/xml; charset=UTF-8';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $auth = 'basic_auth';
|
||||
|
||||
}
|
||||
@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\Tests\contact\Functional\Rest;
|
||||
|
||||
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
|
||||
use Drupal\Tests\rest\Functional\EntityResource\XmlEntityNormalizationQuirksTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class ContactFormXmlCookieTest extends ContactFormResourceTestBase {
|
||||
|
||||
use CookieResourceTestTrait;
|
||||
use XmlEntityNormalizationQuirksTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'xml';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'text/xml; charset=UTF-8';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $auth = 'cookie';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $defaultTheme = 'stark';
|
||||
|
||||
}
|
||||
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\Tests\contact\Functional\Rest;
|
||||
|
||||
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class MessageJsonAnonTest extends MessageResourceTestBase {
|
||||
|
||||
use AnonResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $defaultTheme = 'stark';
|
||||
|
||||
}
|
||||
@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\Tests\contact\Functional\Rest;
|
||||
|
||||
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class MessageJsonBasicAuthTest extends MessageResourceTestBase {
|
||||
|
||||
use BasicAuthResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $modules = ['basic_auth'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $defaultTheme = 'stark';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $auth = 'basic_auth';
|
||||
|
||||
}
|
||||
@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\Tests\contact\Functional\Rest;
|
||||
|
||||
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class MessageJsonCookieTest extends MessageResourceTestBase {
|
||||
|
||||
use CookieResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $auth = 'cookie';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $defaultTheme = 'stark';
|
||||
|
||||
}
|
||||
@ -0,0 +1,151 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\Tests\contact\Functional\Rest;
|
||||
|
||||
use Drupal\contact\Entity\ContactForm;
|
||||
use Drupal\contact\Entity\Message;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase;
|
||||
use Symfony\Component\Routing\Exception\RouteNotFoundException;
|
||||
|
||||
/**
|
||||
* Resource test base for the ContactMessage entity.
|
||||
*/
|
||||
abstract class MessageResourceTestBase extends EntityResourceTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $modules = ['contact'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $entityTypeId = 'contact_message';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $labelFieldName = 'subject';
|
||||
|
||||
/**
|
||||
* The Message entity.
|
||||
*
|
||||
* @var \Drupal\contact\MessageInterface
|
||||
*/
|
||||
protected $entity;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUpAuthorization($method) {
|
||||
$this->grantPermissionsToTestedRole(['access site-wide contact form']);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function createEntity() {
|
||||
if (!ContactForm::load('camelids')) {
|
||||
// Create a "Camelids" contact form.
|
||||
ContactForm::create([
|
||||
'id' => 'camelids',
|
||||
'label' => 'Llama',
|
||||
'message' => 'Let us know what you think about llamas',
|
||||
'reply' => 'Llamas are indeed awesome!',
|
||||
'recipients' => [
|
||||
'llama@example.com',
|
||||
'contact@example.com',
|
||||
],
|
||||
])->save();
|
||||
}
|
||||
|
||||
$message = Message::create([
|
||||
'contact_form' => 'camelids',
|
||||
'subject' => 'Llama Gabilondo',
|
||||
'message' => 'Llamas are awesome!',
|
||||
]);
|
||||
$message->save();
|
||||
|
||||
return $message;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getNormalizedPostEntity() {
|
||||
return [
|
||||
'subject' => [
|
||||
[
|
||||
'value' => 'Drama llama',
|
||||
],
|
||||
],
|
||||
'contact_form' => [
|
||||
[
|
||||
'target_id' => 'camelids',
|
||||
],
|
||||
],
|
||||
'message' => [
|
||||
[
|
||||
'value' => 'http://www.urbandictionary.com/define.php?term=drama%20llama',
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getExpectedNormalizedEntity() {
|
||||
throw new \Exception('Not yet supported.');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getExpectedUnauthorizedAccessMessage($method) {
|
||||
if ($method === 'POST') {
|
||||
return "The 'access site-wide contact form' permission is required.";
|
||||
}
|
||||
return parent::getExpectedUnauthorizedAccessMessage($method);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function testGet(): void {
|
||||
// Contact Message entities are not stored, so they cannot be retrieved.
|
||||
$this->expectException(RouteNotFoundException::class);
|
||||
$this->expectExceptionMessage('Route "rest.entity.contact_message.GET" does not exist.');
|
||||
|
||||
$this->provisionEntityResource();
|
||||
Url::fromRoute('rest.entity.contact_message.GET')->toString(TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function testPatch(): void {
|
||||
// Contact Message entities are not stored, so they cannot be modified.
|
||||
$this->expectException(RouteNotFoundException::class);
|
||||
$this->expectExceptionMessage('Route "rest.entity.contact_message.PATCH" does not exist.');
|
||||
|
||||
$this->provisionEntityResource();
|
||||
Url::fromRoute('rest.entity.contact_message.PATCH')->toString(TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function testDelete(): void {
|
||||
// Contact Message entities are not stored, so they cannot be deleted.
|
||||
$this->expectException(RouteNotFoundException::class);
|
||||
$this->expectExceptionMessage('Route "rest.entity.contact_message.DELETE" does not exist.');
|
||||
|
||||
$this->provisionEntityResource();
|
||||
Url::fromRoute('rest.entity.contact_message.DELETE')->toString(TRUE);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\Tests\contact\Functional\Rest;
|
||||
|
||||
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
|
||||
use Drupal\Tests\rest\Functional\EntityResource\XmlEntityNormalizationQuirksTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class MessageXmlAnonTest extends MessageResourceTestBase {
|
||||
|
||||
use AnonResourceTestTrait;
|
||||
use XmlEntityNormalizationQuirksTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'xml';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'text/xml; charset=UTF-8';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $defaultTheme = 'stark';
|
||||
|
||||
}
|
||||
@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\Tests\contact\Functional\Rest;
|
||||
|
||||
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
|
||||
use Drupal\Tests\rest\Functional\EntityResource\XmlEntityNormalizationQuirksTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class MessageXmlBasicAuthTest extends MessageResourceTestBase {
|
||||
|
||||
use BasicAuthResourceTestTrait;
|
||||
use XmlEntityNormalizationQuirksTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $modules = ['basic_auth'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'xml';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'text/xml; charset=UTF-8';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $defaultTheme = 'stark';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $auth = 'basic_auth';
|
||||
|
||||
}
|
||||
@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\Tests\contact\Functional\Rest;
|
||||
|
||||
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
|
||||
use Drupal\Tests\rest\Functional\EntityResource\XmlEntityNormalizationQuirksTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class MessageXmlCookieTest extends MessageResourceTestBase {
|
||||
|
||||
use CookieResourceTestTrait;
|
||||
use XmlEntityNormalizationQuirksTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'xml';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'text/xml; charset=UTF-8';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $defaultTheme = 'stark';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $auth = 'cookie';
|
||||
|
||||
}
|
||||
@ -0,0 +1,109 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\Tests\contact\Functional\Views;
|
||||
|
||||
use Drupal\Core\Cache\Cache;
|
||||
use Drupal\Tests\views\Functional\ViewTestBase;
|
||||
use Drupal\user\Entity\User;
|
||||
|
||||
/**
|
||||
* Tests the contact link field.
|
||||
*
|
||||
* @group contact
|
||||
* @see \Drupal\contact\Plugin\views\field\ContactLink.
|
||||
*/
|
||||
class ContactLinkTest extends ViewTestBase {
|
||||
|
||||
/**
|
||||
* Stores the user data service used by the test.
|
||||
*
|
||||
* @var \Drupal\user\UserDataInterface
|
||||
*/
|
||||
public $userData;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $modules = ['contact_test_views'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $defaultTheme = 'stark';
|
||||
|
||||
/**
|
||||
* Views used by this test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $testViews = ['test_contact_link'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp($import_test_views = TRUE, $modules = ['contact_test_views']): void {
|
||||
parent::setUp($import_test_views, $modules);
|
||||
|
||||
$this->userData = $this->container->get('user.data');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests contact link.
|
||||
*/
|
||||
public function testContactLink(): void {
|
||||
$accounts = [];
|
||||
$accounts['root'] = User::load(1);
|
||||
// Create an account with access to all contact pages.
|
||||
$admin_account = $this->drupalCreateUser(['administer users']);
|
||||
$accounts['admin'] = $admin_account;
|
||||
// Create an account with no access to contact pages.
|
||||
$no_contact_account = $this->drupalCreateUser();
|
||||
$accounts['no_contact'] = $no_contact_account;
|
||||
|
||||
// Create an account with access to contact pages.
|
||||
$contact_account = $this->drupalCreateUser(['access user contact forms']);
|
||||
$accounts['contact'] = $contact_account;
|
||||
|
||||
$this->drupalLogin($admin_account);
|
||||
$this->drupalGet('test-contact-link');
|
||||
// The admin user has access to all contact links beside their own.
|
||||
$this->assertContactLinks($accounts, ['root', 'no_contact', 'contact']);
|
||||
|
||||
$this->drupalLogin($no_contact_account);
|
||||
$this->drupalGet('test-contact-link');
|
||||
// Ensure that the user without the permission doesn't see any link.
|
||||
$this->assertContactLinks($accounts, []);
|
||||
|
||||
$this->drupalLogin($contact_account);
|
||||
$this->drupalGet('test-contact-link');
|
||||
$this->assertContactLinks($accounts, ['root', 'admin', 'no_contact']);
|
||||
|
||||
// Disable contact link for no_contact.
|
||||
$this->userData->set('contact', $no_contact_account->id(), 'enabled', FALSE);
|
||||
// @todo Remove cache invalidation in https://www.drupal.org/node/2477903.
|
||||
Cache::invalidateTags($no_contact_account->getCacheTagsToInvalidate());
|
||||
$this->drupalGet('test-contact-link');
|
||||
$this->assertContactLinks($accounts, ['root', 'admin']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts whether certain users contact links appear on the page.
|
||||
*
|
||||
* @param array $accounts
|
||||
* All user objects used by the test.
|
||||
* @param array $names
|
||||
* Users which should have contact links.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public function assertContactLinks(array $accounts, array $names): void {
|
||||
$this->assertSession()->elementsCount('xpath', '//div[contains(@class, "views-field-contact")]//a', count($names));
|
||||
foreach ($names as $name) {
|
||||
$account_url = $accounts[$name]->toUrl('contact-form')->toString();
|
||||
$this->assertSession()->elementExists('xpath', "//div[contains(@class, 'views-field-contact')]//a[contains(@href, '$account_url')]");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,82 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\Tests\contact\Kernel;
|
||||
|
||||
use Drupal\contact\Entity\ContactForm;
|
||||
use Drupal\Core\Config\Action\ConfigActionManager;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
|
||||
/**
|
||||
* @group contact
|
||||
*/
|
||||
class ConfigActionsTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $modules = ['contact', 'system', 'user'];
|
||||
|
||||
/**
|
||||
* The configuration action manager.
|
||||
*
|
||||
* @var \Drupal\Core\Config\Action\ConfigActionManager
|
||||
*/
|
||||
private readonly ConfigActionManager $configActionManager;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
$this->installConfig('contact');
|
||||
$this->configActionManager = $this->container->get('plugin.manager.config_action');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the application of configuration actions on a contact form.
|
||||
*/
|
||||
public function testConfigActions(): void {
|
||||
$form = ContactForm::load('personal');
|
||||
$this->assertSame('Your message has been sent.', $form->getMessage());
|
||||
$this->assertEmpty($form->getRecipients());
|
||||
$this->assertSame('/', $form->getRedirectUrl()->toString());
|
||||
$this->assertEmpty($form->getReply());
|
||||
$this->assertSame(0, $form->getWeight());
|
||||
|
||||
$this->configActionManager->applyAction(
|
||||
'entity_method:contact.form:setMessage',
|
||||
$form->getConfigDependencyName(),
|
||||
'Fly, little message!',
|
||||
);
|
||||
$this->configActionManager->applyAction(
|
||||
'entity_method:contact.form:setRecipients',
|
||||
$form->getConfigDependencyName(),
|
||||
['ben@deep.space', 'jake@deep.space'],
|
||||
);
|
||||
$this->configActionManager->applyAction(
|
||||
'entity_method:contact.form:setRedirectPath',
|
||||
$form->getConfigDependencyName(),
|
||||
'/admin/appearance',
|
||||
);
|
||||
$this->configActionManager->applyAction(
|
||||
'entity_method:contact.form:setReply',
|
||||
$form->getConfigDependencyName(),
|
||||
"From hell's heart, I reply to thee.",
|
||||
);
|
||||
$this->configActionManager->applyAction(
|
||||
'entity_method:contact.form:setWeight',
|
||||
$form->getConfigDependencyName(),
|
||||
-10,
|
||||
);
|
||||
|
||||
$form = ContactForm::load($form->id());
|
||||
$this->assertSame('Fly, little message!', $form->getMessage());
|
||||
$this->assertSame(['ben@deep.space', 'jake@deep.space'], $form->getRecipients());
|
||||
$this->assertSame('/admin/appearance', $form->getRedirectUrl()->toString());
|
||||
$this->assertSame("From hell's heart, I reply to thee.", $form->getReply());
|
||||
$this->assertSame(-10, $form->getWeight());
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\Tests\contact\Kernel;
|
||||
|
||||
use Drupal\contact\ContactFormInterface;
|
||||
use Drupal\contact\Entity\ContactForm;
|
||||
use Drupal\KernelTests\Core\Config\ConfigEntityValidationTestBase;
|
||||
|
||||
/**
|
||||
* Tests validation of contact_form entities.
|
||||
*
|
||||
* @group contact
|
||||
*/
|
||||
class ContactFormValidationTest extends ConfigEntityValidationTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $modules = ['contact', 'user'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$this->entity = ContactForm::create([
|
||||
'id' => 'test',
|
||||
'label' => 'Test',
|
||||
]);
|
||||
$this->entity->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests validation of message.
|
||||
*/
|
||||
public function testMessageValidation(): void {
|
||||
assert($this->entity instanceof ContactFormInterface);
|
||||
// Messages should be able to span multiple lines.
|
||||
$this->entity->setMessage("Multi\nLine");
|
||||
$this->assertValidationErrors([]);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,74 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\Tests\contact\Kernel;
|
||||
|
||||
use Drupal\KernelTests\Core\Entity\EntityKernelTestBase;
|
||||
|
||||
/**
|
||||
* Tests the message entity class.
|
||||
*
|
||||
* @group contact
|
||||
* @see \Drupal\contact\Entity\Message
|
||||
*/
|
||||
class MessageEntityTest extends EntityKernelTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $modules = [
|
||||
'system',
|
||||
'contact',
|
||||
'field',
|
||||
'user',
|
||||
'contact_test',
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
$this->installConfig(['contact', 'contact_test']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests some of the methods.
|
||||
*/
|
||||
public function testMessageMethods(): void {
|
||||
$message_storage = $this->container->get('entity_type.manager')->getStorage('contact_message');
|
||||
$message = $message_storage->create(['contact_form' => 'feedback']);
|
||||
|
||||
// Check for empty values first.
|
||||
$this->assertEquals('', $message->getMessage());
|
||||
$this->assertEquals('', $message->getSenderName());
|
||||
$this->assertEquals('', $message->getSenderMail());
|
||||
$this->assertFalse($message->copySender());
|
||||
|
||||
// Check for default values.
|
||||
$this->assertEquals('feedback', $message->getContactForm()->id());
|
||||
$this->assertFalse($message->isPersonal());
|
||||
|
||||
// Set some values and check for them afterwards.
|
||||
$message->setMessage('welcome_message');
|
||||
$message->setSenderName('sender_name');
|
||||
$message->setSenderMail('sender_mail');
|
||||
$message->setCopySender(TRUE);
|
||||
|
||||
$this->assertEquals('welcome_message', $message->getMessage());
|
||||
$this->assertEquals('sender_name', $message->getSenderName());
|
||||
$this->assertEquals('sender_mail', $message->getSenderMail());
|
||||
$this->assertTrue($message->copySender());
|
||||
|
||||
$no_access_user = $this->createUser([], NULL, FALSE, ['uid' => 2]);
|
||||
$access_user = $this->createUser(['access site-wide contact form'], NULL, FALSE, ['uid' => 3]);
|
||||
$admin = $this->createUser(['administer contact forms'], NULL, FALSE, ['uid' => 4]);
|
||||
|
||||
$this->assertFalse(\Drupal::entityTypeManager()->getAccessControlHandler('contact_message')->createAccess(NULL, $no_access_user));
|
||||
$this->assertTrue(\Drupal::entityTypeManager()->getAccessControlHandler('contact_message')->createAccess(NULL, $access_user));
|
||||
$this->assertTrue($message->access('update', $admin));
|
||||
$this->assertFalse($message->access('update', $access_user));
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,85 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\Tests\contact\Kernel\Migrate;
|
||||
|
||||
use Drupal\contact\Entity\ContactForm;
|
||||
use Drupal\contact\ContactFormInterface;
|
||||
use Drupal\Tests\migrate_drupal\Kernel\d6\MigrateDrupal6TestBase;
|
||||
|
||||
/**
|
||||
* Migrate contact categories to contact.form.*.yml.
|
||||
*
|
||||
* @group contact_category
|
||||
*/
|
||||
class MigrateContactCategoryTest extends MigrateDrupal6TestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $modules = ['contact'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
$this->executeMigration('contact_category');
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs various assertions on a single contact form entity.
|
||||
*
|
||||
* @param string $id
|
||||
* The contact form ID.
|
||||
* @param string $expected_label
|
||||
* The expected label.
|
||||
* @param string[] $expected_recipients
|
||||
* The recipient email addresses the form should have.
|
||||
* @param string $expected_reply
|
||||
* The expected reply message.
|
||||
* @param int $expected_weight
|
||||
* The contact form's expected weight.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
protected function assertEntity(string $id, string $expected_label, array $expected_recipients, string $expected_reply, int $expected_weight): void {
|
||||
/** @var \Drupal\contact\ContactFormInterface $entity */
|
||||
$entity = ContactForm::load($id);
|
||||
$this->assertInstanceOf(ContactFormInterface::class, $entity);
|
||||
$this->assertSame($expected_label, $entity->label());
|
||||
$this->assertSame($expected_recipients, $entity->getRecipients());
|
||||
$this->assertSame($expected_reply, $entity->getReply());
|
||||
$this->assertSame($expected_weight, $entity->getWeight());
|
||||
}
|
||||
|
||||
/**
|
||||
* The Drupal 6 and 7 contact categories to Drupal 8 migration.
|
||||
*/
|
||||
public function testContactCategory(): void {
|
||||
$this->assertEntity('website_feedback', 'Website feedback', ['admin@example.com'], '', 0);
|
||||
$this->assertEntity('some_other_category', 'Some other category', ['test@example.com'], 'Thanks for contacting us, we will reply ASAP!', 1);
|
||||
$this->assertEntity('a_category_much_longer_than_th', 'A category much longer than thirty two characters', ['fortyninechars@example.com'], '', 2);
|
||||
|
||||
// Test there are no duplicated roles.
|
||||
$contact_forms = [
|
||||
'website_feedback1',
|
||||
'some_other_category1',
|
||||
'a_category_much_longer_than_thir1',
|
||||
];
|
||||
$this->assertEmpty(ContactForm::loadMultiple($contact_forms));
|
||||
|
||||
/*
|
||||
* Remove the map row for the Website feedback contact form so that it
|
||||
* can be migrated again.
|
||||
*/
|
||||
$id_map = $this->getMigration('contact_category')->getIdMap();
|
||||
$id_map->delete(['cid' => '1']);
|
||||
$this->executeMigration('contact_category');
|
||||
|
||||
// Test there is a duplicate Website feedback form.
|
||||
$this->assertEntity('website_feedback1', 'Website feedback', ['admin@example.com'], '', 0);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\Tests\contact\Kernel\Migrate\d6;
|
||||
|
||||
use Drupal\Tests\SchemaCheckTestTrait;
|
||||
use Drupal\Tests\migrate_drupal\Kernel\d6\MigrateDrupal6TestBase;
|
||||
|
||||
/**
|
||||
* Upgrade variables to contact.settings.yml.
|
||||
*
|
||||
* @group migrate_drupal_6
|
||||
*/
|
||||
class MigrateContactSettingsTest extends MigrateDrupal6TestBase {
|
||||
|
||||
use SchemaCheckTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $modules = ['contact'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
$this->executeMigrations(['contact_category', 'd6_contact_settings']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests migration of contact variables to contact.settings.yml.
|
||||
*/
|
||||
public function testContactSettings(): void {
|
||||
$config = $this->config('contact.settings');
|
||||
$this->assertTrue($config->get('user_default_enabled'));
|
||||
$this->assertSame(3, $config->get('flood.limit'));
|
||||
$this->assertSame('some_other_category', $config->get('default_form'));
|
||||
$this->assertConfigSchema(\Drupal::service('config.typed'), 'contact.settings', $config->get());
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\Tests\contact\Kernel\Migrate\d7;
|
||||
|
||||
use Drupal\Tests\migrate_drupal\Kernel\d7\MigrateDrupal7TestBase;
|
||||
|
||||
/**
|
||||
* Tests migration of Contact settings to configuration.
|
||||
*
|
||||
* @group migrate_drupal_7
|
||||
*/
|
||||
class MigrateContactSettingsTest extends MigrateDrupal7TestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $modules = ['contact'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
$this->executeMigration('contact_category');
|
||||
$this->executeMigration('d7_contact_settings');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests migration of Contact's variables to configuration.
|
||||
*/
|
||||
public function testContactSettings(): void {
|
||||
$config = $this->config('contact.settings');
|
||||
$this->assertTrue($config->get('user_default_enabled'));
|
||||
$this->assertSame(33, $config->get('flood.limit'));
|
||||
$this->assertEquals('website_testing', $config->get('default_form'));
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\Tests\contact\Kernel\Plugin\migrate\source;
|
||||
|
||||
use Drupal\Tests\migrate\Kernel\MigrateSqlSourceTestBase;
|
||||
|
||||
/**
|
||||
* Tests D6 contact category source plugin.
|
||||
*
|
||||
* @covers \Drupal\contact\Plugin\migrate\source\ContactCategory
|
||||
* @group contact
|
||||
*/
|
||||
class ContactCategoryTest extends MigrateSqlSourceTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $modules = ['contact', 'migrate_drupal', 'user'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function providerSource() {
|
||||
$tests = [
|
||||
[
|
||||
'source_data' => [],
|
||||
'expected_data' => [],
|
||||
],
|
||||
];
|
||||
|
||||
$tests[0]['expected_data'] = [
|
||||
[
|
||||
'cid' => 1,
|
||||
'category' => 'contact category value 1',
|
||||
'recipients' => ['admin@example.com', 'user@example.com'],
|
||||
'reply' => 'auto reply value 1',
|
||||
'weight' => 0,
|
||||
'selected' => 0,
|
||||
],
|
||||
[
|
||||
'cid' => 2,
|
||||
'category' => 'contact category value 2',
|
||||
'recipients' => ['admin@example.com', 'user@example.com'],
|
||||
'reply' => 'auto reply value 2',
|
||||
'weight' => 0,
|
||||
'selected' => 0,
|
||||
],
|
||||
];
|
||||
|
||||
foreach ($tests[0]['expected_data'] as $k => $row) {
|
||||
$row['recipients'] = implode(',', $row['recipients']);
|
||||
$tests[0]['source_data']['contact'][$k] = $row;
|
||||
}
|
||||
return $tests;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\Tests\contact\Kernel\Plugin\migrate\source\d6;
|
||||
|
||||
use Drupal\Tests\migrate\Kernel\MigrateSqlSourceTestBase;
|
||||
|
||||
/**
|
||||
* Tests D6 contact settings source plugin.
|
||||
*
|
||||
* @covers \Drupal\contact\Plugin\migrate\source\ContactSettings
|
||||
* @group contact
|
||||
*/
|
||||
class ContactSettingsTest extends MigrateSqlSourceTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $modules = ['contact', 'migrate_drupal', 'user'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function providerSource() {
|
||||
$tests = [];
|
||||
|
||||
$tests[0]['source_data']['variable'] = [
|
||||
[
|
||||
'name' => 'site_name',
|
||||
'value' => serialize('foo!'),
|
||||
],
|
||||
];
|
||||
$tests[0]['source_data']['contact'] = [
|
||||
[
|
||||
'cid' => '1',
|
||||
'category' => 'Website feedback',
|
||||
'recipients' => 'admin@example.com',
|
||||
'reply' => '',
|
||||
'weight' => '0',
|
||||
'selected' => '1',
|
||||
],
|
||||
];
|
||||
$tests[0]['expected_data'] = [
|
||||
[
|
||||
'default_category' => '1',
|
||||
'site_name' => 'foo!',
|
||||
],
|
||||
];
|
||||
$tests[0]['expected_count'] = NULL;
|
||||
$tests[0]['configuration']['variables'] = ['site_name'];
|
||||
|
||||
return $tests;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\Tests\contact\Kernel\Views;
|
||||
|
||||
use Drupal\contact\Entity\ContactForm;
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Tests that no storage is created for the contact_message entity.
|
||||
*
|
||||
* @group contact
|
||||
*/
|
||||
class ContactFieldsTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $modules = [
|
||||
'contact',
|
||||
'field',
|
||||
'system',
|
||||
'text',
|
||||
'user',
|
||||
'views',
|
||||
];
|
||||
|
||||
/**
|
||||
* Tests the views data generation.
|
||||
*/
|
||||
public function testViewsData(): void {
|
||||
$this->installConfig(['contact']);
|
||||
FieldStorageConfig::create([
|
||||
'type' => 'text',
|
||||
'entity_type' => 'contact_message',
|
||||
'field_name' => $field_name = $this->randomMachineName(),
|
||||
])->save();
|
||||
|
||||
ContactForm::create([
|
||||
'id' => 'contact_message',
|
||||
'label' => 'Test contact form',
|
||||
])->save();
|
||||
|
||||
FieldConfig::create([
|
||||
'entity_type' => 'contact_message',
|
||||
'bundle' => 'contact_message',
|
||||
'field_name' => $field_name,
|
||||
])->save();
|
||||
|
||||
// Test that the field is not exposed to views, since contact_message
|
||||
// entities have no storage.
|
||||
$table_name = 'contact_message__' . $field_name;
|
||||
$data = $this->container->get('views.views_data')->get($table_name);
|
||||
$this->assertEmpty($data);
|
||||
}
|
||||
|
||||
}
|
||||
52
web/core/modules/contact/tests/src/Unit/ContactLinkTest.php
Normal file
52
web/core/modules/contact/tests/src/Unit/ContactLinkTest.php
Normal file
@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\Tests\contact\Unit;
|
||||
|
||||
use Drupal\contact\Plugin\views\field\ContactLink;
|
||||
use Drupal\Core\Access\AccessManagerInterface;
|
||||
use Drupal\Core\Entity\EntityRepositoryInterface;
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Drupal\Core\Language\LanguageManagerInterface;
|
||||
use Drupal\Core\StringTranslation\TranslationInterface;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
use Drupal\Tests\views\Traits\ViewsLoggerTestTrait;
|
||||
use Drupal\views\Plugin\views\display\DisplayPluginBase;
|
||||
use Drupal\views\ResultRow;
|
||||
use Drupal\views\ViewExecutable;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\contact\Plugin\views\field\ContactLink
|
||||
* @group contact
|
||||
*/
|
||||
class ContactLinkTest extends UnitTestCase {
|
||||
|
||||
use ViewsLoggerTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
$this->setUpMockLoggerWithMissingEntity();
|
||||
$container = \Drupal::getContainer();
|
||||
$container->set('string_translation', $this->createMock(TranslationInterface::class));
|
||||
\Drupal::setContainer($container);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the render method when getEntity returns NULL.
|
||||
*
|
||||
* @covers ::render
|
||||
*/
|
||||
public function testRenderNullEntity(): void {
|
||||
$row = new ResultRow();
|
||||
$field = new ContactLink(['entity_type' => 'foo', 'entity field' => 'bar'], '', [], $this->createMock(AccessManagerInterface::class), $this->createMock(EntityTypeManagerInterface::class), $this->createMock(EntityRepositoryInterface::class), $this->createMock(LanguageManagerInterface::class));
|
||||
$view = $this->createMock(ViewExecutable::class);
|
||||
$display = $this->createMock(DisplayPluginBase::class);
|
||||
$field->init($view, $display);
|
||||
$this->assertEmpty($field->render($row));
|
||||
}
|
||||
|
||||
}
|
||||
28
web/core/modules/contact/tests/src/Unit/ContactTest.php
Normal file
28
web/core/modules/contact/tests/src/Unit/ContactTest.php
Normal file
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\Tests\contact\Unit;
|
||||
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
use Drupal\contact\Hook\ContactHooks;
|
||||
|
||||
/**
|
||||
* Tests for Contact hooks.
|
||||
*
|
||||
* @group contact
|
||||
*/
|
||||
class ContactTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* Test contact_menu_local_tasks_alter doesn't throw warnings.
|
||||
*/
|
||||
public function testLocalTasksAlter(): void {
|
||||
require_once $this->root . '/core/modules/contact/contact.module';
|
||||
$data = [];
|
||||
$contactMenuLocalTasksAlter = new ContactHooks();
|
||||
$contactMenuLocalTasksAlter->menuLocalTasksAlter($data, 'entity.user.canonical');
|
||||
$this->assertTrue(TRUE, 'No warning thrown');
|
||||
}
|
||||
|
||||
}
|
||||
346
web/core/modules/contact/tests/src/Unit/MailHandlerTest.php
Normal file
346
web/core/modules/contact/tests/src/Unit/MailHandlerTest.php
Normal file
@ -0,0 +1,346 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\Tests\contact\Unit;
|
||||
|
||||
use Drupal\contact\MailHandler;
|
||||
use Drupal\contact\MailHandlerException;
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Drupal\Core\Language\Language;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
use Drupal\user\Entity\User;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\contact\MailHandler
|
||||
* @group contact
|
||||
*/
|
||||
class MailHandlerTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* Language manager service.
|
||||
*
|
||||
* @var \Drupal\Core\Language\LanguageManagerInterface|\PHPUnit\Framework\MockObject\MockObject
|
||||
*/
|
||||
protected $languageManager;
|
||||
|
||||
/**
|
||||
* Logger service.
|
||||
*
|
||||
* @var \Psr\Log\LoggerInterface|\PHPUnit\Framework\MockObject\MockObject
|
||||
*/
|
||||
protected $logger;
|
||||
|
||||
/**
|
||||
* Mail manager service.
|
||||
*
|
||||
* @var \Drupal\Core\Mail\MailManagerInterface|\PHPUnit\Framework\MockObject\MockObject
|
||||
*/
|
||||
protected $mailManager;
|
||||
|
||||
/**
|
||||
* Contact mail messages service.
|
||||
*
|
||||
* @var \Drupal\contact\MailHandlerInterface|\PHPUnit\Framework\MockObject\MockObject
|
||||
*/
|
||||
protected $contactMailHandler;
|
||||
|
||||
/**
|
||||
* The contact form entity.
|
||||
*
|
||||
* @var \Drupal\contact\ContactFormInterface|\PHPUnit\Framework\MockObject\MockObject
|
||||
*/
|
||||
protected $contactForm;
|
||||
|
||||
/**
|
||||
* The entity type manager.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityTypeManagerInterface|\PHPUnit\Framework\MockObject\MockObject
|
||||
*/
|
||||
protected $entityTypeManager;
|
||||
|
||||
/**
|
||||
* The user storage handler.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityStorageInterface|\PHPUnit\Framework\MockObject\MockObject
|
||||
*/
|
||||
protected $userStorage;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
$this->mailManager = $this->createMock('\Drupal\Core\Mail\MailManagerInterface');
|
||||
$this->languageManager = $this->createMock('\Drupal\Core\Language\LanguageManagerInterface');
|
||||
$this->logger = $this->createMock('\Psr\Log\LoggerInterface');
|
||||
$this->entityTypeManager = $this->createMock(EntityTypeManagerInterface::class);
|
||||
$this->userStorage = $this->createMock('\Drupal\Core\Entity\EntityStorageInterface');
|
||||
$this->entityTypeManager->expects($this->any())
|
||||
->method('getStorage')
|
||||
->with('user')
|
||||
->willReturn($this->userStorage);
|
||||
|
||||
$string_translation = $this->getStringTranslationStub();
|
||||
$this->contactMailHandler = new MailHandler($this->mailManager, $this->languageManager, $this->logger, $string_translation, $this->entityTypeManager);
|
||||
$language = new Language(['id' => 'en']);
|
||||
|
||||
$this->languageManager->expects($this->any())
|
||||
->method('getDefaultLanguage')
|
||||
->willReturn($language);
|
||||
|
||||
$this->languageManager->expects($this->any())
|
||||
->method('getCurrentLanguage')
|
||||
->willReturn($language);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the children() method with an invalid key.
|
||||
*
|
||||
* @covers ::sendMailMessages
|
||||
*/
|
||||
public function testInvalidRecipient(): void {
|
||||
$message = $this->createMock('\Drupal\contact\MessageInterface');
|
||||
$message->expects($this->once())
|
||||
->method('isPersonal')
|
||||
->willReturn(TRUE);
|
||||
$message->expects($this->once())
|
||||
->method('getPersonalRecipient')
|
||||
->willReturn(NULL);
|
||||
$message->expects($this->once())
|
||||
->method('getContactForm')
|
||||
->willReturn($this->createMock('\Drupal\contact\ContactFormInterface'));
|
||||
$sender = $this->createMock('\Drupal\Core\Session\AccountInterface');
|
||||
$this->userStorage->expects($this->any())
|
||||
->method('load')
|
||||
->willReturn($sender);
|
||||
// User IDs 1 and 0 have special implications, use 3 instead.
|
||||
$sender->expects($this->any())
|
||||
->method('id')
|
||||
->willReturn(3);
|
||||
$sender->expects($this->once())
|
||||
->method('isAnonymous')
|
||||
->willReturn(FALSE);
|
||||
$this->expectException(MailHandlerException::class);
|
||||
$this->expectExceptionMessage('Unable to determine message recipient');
|
||||
$this->contactMailHandler->sendMailMessages($message, $sender);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the sendMailMessages method.
|
||||
*
|
||||
* @dataProvider getSendMailMessages
|
||||
*
|
||||
* @covers ::sendMailMessages
|
||||
*/
|
||||
public function testSendMailMessages(bool $anonymous, ?bool $auto_reply, bool $copy_sender, array $results): void {
|
||||
if ($anonymous) {
|
||||
$message = $this->getAnonymousMockMessage(explode(', ', $results[0]['to']), $auto_reply, $copy_sender);
|
||||
$sender = $this->getMockSender();
|
||||
}
|
||||
else {
|
||||
$message = $this->getAuthenticatedMockMessage($copy_sender);
|
||||
$sender = $this->getMockSender(FALSE, 'user@drupal.org');
|
||||
}
|
||||
|
||||
$expected_params['contact_message'] = $message;
|
||||
$expected_params['sender'] = $sender;
|
||||
if ($anonymous) {
|
||||
$expected_params['contact_form'] = $message->getContactForm();
|
||||
}
|
||||
else {
|
||||
$expected_params['recipient'] = $message->getPersonalRecipient();
|
||||
}
|
||||
|
||||
$this->logger->expects($this->once())
|
||||
->method('info');
|
||||
$this->mailManager->expects($this->any())
|
||||
->method('mail')
|
||||
->willReturnCallback(
|
||||
function ($module, $key, $to, $langcode, $params, $from) use (&$results, $expected_params) {
|
||||
$result = array_shift($results);
|
||||
$this->assertEquals($module, $result['module']);
|
||||
$this->assertEquals($key, $result['key']);
|
||||
$this->assertEquals($to, $result['to']);
|
||||
$this->assertEquals($langcode, $result['langcode']);
|
||||
$this->assertEquals($params, $expected_params);
|
||||
$this->assertEquals($from, $result['from']);
|
||||
});
|
||||
$this->userStorage->expects($this->any())
|
||||
->method('load')
|
||||
->willReturn(clone $sender);
|
||||
$this->contactMailHandler->sendMailMessages($message, $sender);
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for ::testSendMailMessages.
|
||||
*/
|
||||
public static function getSendMailMessages() {
|
||||
$default_result = [
|
||||
'module' => 'contact',
|
||||
'key' => 'page_mail',
|
||||
'to' => 'admin@drupal.org, user@drupal.org',
|
||||
'langcode' => 'en',
|
||||
'params' => [],
|
||||
'from' => 'anonymous@drupal.org',
|
||||
];
|
||||
|
||||
$autoreply_result = [
|
||||
'key' => 'page_autoreply',
|
||||
'to' => 'anonymous@drupal.org',
|
||||
'from' => NULL,
|
||||
] + $default_result;
|
||||
|
||||
$copy_result = [
|
||||
'key' => 'page_copy',
|
||||
'to' => 'anonymous@drupal.org',
|
||||
] + $default_result;
|
||||
|
||||
yield 'anonymous, no auto reply, no copy sender' => [TRUE, FALSE, FALSE, [$default_result]];
|
||||
yield 'anonymous, auto reply, no copy sender' => [TRUE, TRUE, FALSE, [$default_result, $autoreply_result]];
|
||||
yield 'anonymous, no auto reply, copy sender' => [TRUE, FALSE, TRUE, [$default_result, $copy_result]];
|
||||
yield 'anonymous, auto reply, copy sender' => [TRUE, TRUE, TRUE, [$default_result, $copy_result, $autoreply_result]];
|
||||
|
||||
// For authenticated user.
|
||||
$default_result = [
|
||||
'module' => 'contact',
|
||||
'key' => 'user_mail',
|
||||
'to' => 'user2@drupal.org',
|
||||
'langcode' => 'en',
|
||||
'from' => 'user@drupal.org',
|
||||
];
|
||||
|
||||
$copy_result = [
|
||||
'key' => 'user_copy',
|
||||
'to' => 'user@drupal.org',
|
||||
] + $default_result;
|
||||
|
||||
yield 'authenticated, no copy sender' => [FALSE, NULL, FALSE, [$default_result]];
|
||||
yield 'authenticated, copy sender' => [FALSE, NULL, TRUE, [$default_result, $copy_result]];
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a mock sender on given scenario.
|
||||
*
|
||||
* @param bool $anonymous
|
||||
* TRUE if the sender is anonymous.
|
||||
* @param string $mail_address
|
||||
* The mail address of the user.
|
||||
*
|
||||
* @return \Drupal\Core\Session\AccountInterface|\PHPUnit\Framework\MockObject\MockObject
|
||||
* Mock sender for testing.
|
||||
*/
|
||||
protected function getMockSender($anonymous = TRUE, $mail_address = 'anonymous@drupal.org') {
|
||||
$sender = $this->createMock(User::class);
|
||||
$sender->expects($this->once())
|
||||
->method('isAnonymous')
|
||||
->willReturn($anonymous);
|
||||
$sender->expects($this->any())
|
||||
->method('getEmail')
|
||||
->willReturn($mail_address);
|
||||
$sender->expects($this->any())
|
||||
->method('getDisplayName')
|
||||
->willReturn('user');
|
||||
// User ID 1 has special implications, use 3 instead.
|
||||
$sender->expects($this->any())
|
||||
->method('id')
|
||||
->willReturn($anonymous ? 0 : 3);
|
||||
if ($anonymous) {
|
||||
// Anonymous user values set in params include updated values for name and
|
||||
// mail.
|
||||
$sender->name = 'Anonymous (not verified)';
|
||||
$sender->mail = 'anonymous@drupal.org';
|
||||
}
|
||||
return $sender;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a mock message from anonymous user.
|
||||
*
|
||||
* @param array $recipients
|
||||
* An array of recipient email addresses.
|
||||
* @param bool $auto_reply
|
||||
* TRUE if auto reply is enable.
|
||||
* @param bool $copy_sender
|
||||
* TRUE if a copy should be sent, FALSE if not.
|
||||
*
|
||||
* @return \Drupal\contact\MessageInterface|\PHPUnit\Framework\MockObject\MockObject
|
||||
* Mock message for testing.
|
||||
*/
|
||||
protected function getAnonymousMockMessage($recipients, $auto_reply, $copy_sender = FALSE) {
|
||||
$message = $this->createMock('\Drupal\contact\MessageInterface');
|
||||
$message->expects($this->any())
|
||||
->method('getSenderName')
|
||||
->willReturn('Anonymous');
|
||||
$message->expects($this->once())
|
||||
->method('getSenderMail')
|
||||
->willReturn('anonymous@drupal.org');
|
||||
$message->expects($this->any())
|
||||
->method('isPersonal')
|
||||
->willReturn(FALSE);
|
||||
$message->expects($this->once())
|
||||
->method('copySender')
|
||||
->willReturn($copy_sender);
|
||||
$message->expects($this->any())
|
||||
->method('getContactForm')
|
||||
->willReturn($this->getMockContactForm($recipients, $auto_reply ? 'reply' : ''));
|
||||
return $message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a mock message from authenticated user.
|
||||
*
|
||||
* @param bool $copy_sender
|
||||
* TRUE if a copy should be sent, FALSE if not.
|
||||
*
|
||||
* @return \Drupal\contact\MessageInterface|\PHPUnit\Framework\MockObject\MockObject
|
||||
* Mock message for testing.
|
||||
*/
|
||||
protected function getAuthenticatedMockMessage($copy_sender = FALSE) {
|
||||
$message = $this->createMock('\Drupal\contact\MessageInterface');
|
||||
$message->expects($this->any())
|
||||
->method('isPersonal')
|
||||
->willReturn(TRUE);
|
||||
$message->expects($this->once())
|
||||
->method('copySender')
|
||||
->willReturn($copy_sender);
|
||||
$recipient = $this->createMock('\Drupal\user\UserInterface');
|
||||
$recipient->expects($this->once())
|
||||
->method('getEmail')
|
||||
->willReturn('user2@drupal.org');
|
||||
$recipient->expects($this->any())
|
||||
->method('getDisplayName')
|
||||
->willReturn('user2');
|
||||
$recipient->expects($this->once())
|
||||
->method('getPreferredLangcode')
|
||||
->willReturn('en');
|
||||
$message->expects($this->any())
|
||||
->method('getPersonalRecipient')
|
||||
->willReturn($recipient);
|
||||
return $message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a mock message on given scenario.
|
||||
*
|
||||
* @param array $recipients
|
||||
* An array of recipient email addresses.
|
||||
* @param string $auto_reply
|
||||
* An auto-reply message to send to the message author.
|
||||
*
|
||||
* @return \Drupal\contact\ContactFormInterface|\PHPUnit\Framework\MockObject\MockObject
|
||||
* Mock message for testing.
|
||||
*/
|
||||
protected function getMockContactForm($recipients, $auto_reply) {
|
||||
$contact_form = $this->createMock('\Drupal\contact\ContactFormInterface');
|
||||
$contact_form->expects($this->once())
|
||||
->method('getRecipients')
|
||||
->willReturn($recipients);
|
||||
$contact_form->expects($this->once())
|
||||
->method('getReply')
|
||||
->willReturn($auto_reply);
|
||||
|
||||
return $contact_form;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user