diff --git a/core/modules/contact/contact.module b/core/modules/contact/contact.module
index 4cdea75..6a61e05 100644
--- a/core/modules/contact/contact.module
+++ b/core/modules/contact/contact.module
@@ -86,8 +86,6 @@ function contact_menu() {
);
$items['contact/%contact_category'] = array(
'title' => 'Contact category form',
- 'title callback' => 'entity_page_label',
- 'title arguments' => array(1),
'route_name' => 'contact.site_page_category',
'type' => MENU_VISIBLE_IN_BREADCRUMB,
);
@@ -101,18 +99,6 @@ function contact_menu() {
}
/**
- * Access callback: Checks access for a user's personal contact form.
- *
- * @param $account
- * The user object of the user whose contact form is being requested.
- *
- * @see contact_menu()
- */
-function _contact_personal_tab_access(UserInterface $account) {
- return \Drupal::service('access_manager')->checkNamedRoute('contact.personal_page', array('user' => $account->id()));
-}
-
-/**
* Implements hook_entity_bundle_info().
*/
function contact_entity_bundle_info() {
diff --git a/core/modules/contact/contact.pages.inc b/core/modules/contact/contact.pages.inc
deleted file mode 100644
index 491bb30..0000000
--- a/core/modules/contact/contact.pages.inc
+++ /dev/null
@@ -1,110 +0,0 @@
-get('default_category');
- if (isset($categories[$default_category])) {
- $category = $categories[$default_category];
- }
- // If there are no categories, do not display the form.
- else {
- if (user_access('administer contact forms')) {
- drupal_set_message(t('The contact form has not been configured. Add one or more categories to the form.', array('@add' => url('admin/structure/contact/add'))), 'error');
- return array();
- }
- else {
- throw new NotFoundHttpException();
- }
- }
- }
- else if ($category->id() == 'personal') {
- throw new NotFoundHttpException();
- }
- $message = entity_create('contact_message', array(
- 'category' => $category->id(),
- ));
- return \Drupal::entityManager()->getForm($message);
-}
-
-/**
- * Page callback: Form constructor for the personal contact form.
- *
- * @param $recipient
- * The account for which a personal contact form should be generated.
- *
- * @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException
- *
- * @see contact_menu()
- * @see contact_personal_form_submit()
- *
- * @ingroup forms
- *
- * @deprecated Use \Drupal\contact\Controller\ContactController::contactPersonalPage()
- */
-function contact_personal_page($recipient) {
- global $user;
-
- // Check if flood control has been activated for sending e-mails.
- if (!user_access('administer contact forms') && !user_access('administer users')) {
- contact_flood_control();
- }
-
- drupal_set_title(t('Contact @username', array('@username' => user_format_name($recipient))), PASS_THROUGH);
-
- $message = entity_create('contact_message', array(
- 'recipient' => $recipient,
- 'category' => 'personal',
- ));
- return \Drupal::entityManager()->getForm($message);
-}
-
-/**
- * Throws an exception if the current user is not allowed to submit a contact form.
- *
- * @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException
- *
- * @see contact_site_page()
- * @see contact_personal_page()
- */
-function contact_flood_control() {
- $config = \Drupal::config('contact.settings');
- $limit = $config->get('flood.limit');
- $interval = $config->get('flood.interval');
- if (!\Drupal::service('flood')->isAllowed('contact', $limit, $interval)) {
- drupal_set_message(t("You cannot send more than %limit messages in @interval. Try again later.", array(
- '%limit' => $limit,
- '@interval' => format_interval($interval),
- )), 'error');
- throw new AccessDeniedHttpException();
- }
-}
diff --git a/core/modules/contact/contact.routing.yml b/core/modules/contact/contact.routing.yml
index c616c25..fea384a 100644
--- a/core/modules/contact/contact.routing.yml
+++ b/core/modules/contact/contact.routing.yml
@@ -1,10 +1,10 @@
contact.category_delete:
- path: 'admin/structure/contact/manage/{contact_category}/delete'
+ path: '/admin/structure/contact/manage/{contact_category}/delete'
defaults:
- _entity_form: contact_category.delete
+ _entity_form: 'contact_category.delete'
_title: 'Delete'
requirements:
- _entity_access: contact_category.delete
+ _entity_access: 'contact_category.delete'
contact.category_list:
path: '/admin/structure/contact'
@@ -16,16 +16,16 @@ contact.category_list:
contact.category_add:
path: '/admin/structure/contact/add'
defaults:
- _entity_form: contact_category.add
+ _entity_form: 'contact_category.add'
requirements:
_permission: 'administer contact forms'
contact.category_edit:
path: '/admin/structure/contact/manage/{contact_category}'
defaults:
- _entity_form: contact_category.edit
+ _entity_form: 'contact_category.edit'
requirements:
- _entity_access: contact_category.update
+ _entity_access: 'contact_category.update'
contact.site_page:
path: '/contact'
@@ -42,7 +42,7 @@ contact.site_page_category:
_title: 'Contact category form'
_content: '\Drupal\contact\Controller\ContactController::contactSitePage'
requirements:
- _permission: 'access site-wide contact form'
+ _entity_access: 'contact_category.page'
contact.personal_page:
path: '/user/{user}/contact'
diff --git a/core/modules/contact/lib/Drupal/contact/CategoryAccessController.php b/core/modules/contact/lib/Drupal/contact/CategoryAccessController.php
index ba0bab7..45b9b94 100644
--- a/core/modules/contact/lib/Drupal/contact/CategoryAccessController.php
+++ b/core/modules/contact/lib/Drupal/contact/CategoryAccessController.php
@@ -10,6 +10,7 @@
use Drupal\Core\Entity\EntityAccessController;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Session\AccountInterface;
+
/**
* Defines an access controller for the contact category entity.
*
@@ -21,12 +22,18 @@ class CategoryAccessController extends EntityAccessController {
* {@inheritdoc}
*/
public function checkAccess(EntityInterface $entity, $operation, $langcode, AccountInterface $account) {
+ $this->prepareUser($account);
if ($operation == 'delete' || $operation == 'update') {
- // Do not allow delete 'personal' category used for personal contact form.
- return user_access('administer contact forms', $account) && $entity->id() !== 'personal';
+ // Do not allow the 'personal' category to be deleted, as it's used for
+ // the personal contact form.
+ return $account->hasPermission('administer contact forms') && $entity->id() !== 'personal';
+ }
+ elseif ($operation == 'page') {
+ // Do not allow access personal category via site-wide route.
+ return $account->hasPermission('access site-wide contact form') && $entity->id() !== 'personal';
}
else {
- return user_access('administer contact forms', $account);
+ return $account->hasPermission('administer contact forms');
}
}
diff --git a/core/modules/contact/lib/Drupal/contact/Controller/ContactController.php b/core/modules/contact/lib/Drupal/contact/Controller/ContactController.php
index cb6c62d..ff5f71d 100644
--- a/core/modules/contact/lib/Drupal/contact/Controller/ContactController.php
+++ b/core/modules/contact/lib/Drupal/contact/Controller/ContactController.php
@@ -7,28 +7,135 @@
namespace Drupal\contact\Controller;
+use Drupal\Core\Controller\ControllerBase;
+use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
+use Drupal\Core\Flood\FloodInterface;
use Drupal\contact\CategoryInterface;
use Drupal\user\UserInterface;
+use Drupal\Component\Utility\String;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
+use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/**
* Controller routines for contact routes.
*/
-class ContactController {
+class ContactController extends ControllerBase implements ContainerInjectionInterface {
/**
- * @todo Remove contact_site_page().
+ * The flood service.
+ *
+ * @var \Drupal\Core\Flood\FloodInterface
+ */
+ protected $flood;
+
+ /**
+ * Constructs a ContactController object.
+ *
+ * @param \Drupal\Core\Flood\FloodInterface $flood
+ * The flood service.
+ */
+ public function __construct(FloodInterface $flood) {
+ $this->flood = $flood;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public static function create(ContainerInterface $container) {
+ return new static(
+ $container->get('flood')
+ );
+ }
+
+ /**
+ * Presents the site-wide contact form.
+ *
+ * @param \Drupal\contact\CategoryInterface $contact_category
+ * The contact category to use.
+ *
+ * @return array
+ * The form as render array as expected by drupal_render().
+ *
+ * @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
+ * Exception is thrown when user tries to access non existing default
+ * contact category form.
*/
public function contactSitePage(CategoryInterface $contact_category = NULL) {
- module_load_include('pages.inc', 'contact');
- return contact_site_page($contact_category);
+ // Check if flood control has been activated for sending e-mails.
+ if (!$this->currentUser()->hasPermission('administer contact forms')) {
+ $this->contactFloodControl();
+ }
+
+ // Use the default category if no category has been passed.
+ if (empty($contact_category)) {
+ $contact_category = $this->entityManager()
+ ->getStorageController('contact_category')
+ ->load($this->config('contact.settings')->get('default_category'));
+ // If there are no categories, do not display the form.
+ if (empty($contact_category)) {
+ if ($this->currentUser()->hasPermission('administer contact forms')) {
+ drupal_set_message($this->t('The contact form has not been configured. Add one or more categories to the form.', array(
+ '@add' => $this->urlGenerator()->generateFromRoute('contact.category_add'))), 'error');
+ return array();
+ }
+ else {
+ throw new NotFoundHttpException();
+ }
+ }
+ }
+
+ $message = $this->entityManager()
+ ->getStorageController('contact_message')
+ ->create(array(
+ 'category' => $contact_category->id(),
+ ));
+
+ $form = $this->entityManager()->getForm($message);
+ $form['#title'] = String::checkPlain($contact_category->label());
+ return $form;
}
/**
- * @todo Remove contact_personal_page().
+ * 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_render().
*/
public function contactPersonalPage(UserInterface $user) {
- module_load_include('pages.inc', 'contact');
- return contact_personal_page($user);
+ // Check if flood control has been activated for sending e-mails.
+ if (!$this->currentUser()->hasPermission('administer contact forms') && !$this->currentUser()->hasPermission('administer users')) {
+ $this->contactFloodControl();
+ }
+
+ $message = $this->entityManager()->getStorageController('contact_message')->create(array(
+ 'category' => 'personal',
+ 'recipient' => $user->id(),
+ ));
+
+ $form = $this->entityManager()->getForm($message);
+ $form['#title'] = $this->t('Contact @username', array('@username' => $user->getUsername()));
+ return $form;
+ }
+
+ /**
+ * Throws an exception if the current user triggers flood control.
+ *
+ * @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException
+ */
+ protected function contactFloodControl() {
+ $limit = $this->config('contact.settings')->get('flood.limit');
+ $interval = $this->config('contact.settings')->get('flood.interval');
+ if (!$this->flood->isAllowed('contact', $limit, $interval)) {
+ drupal_set_message($this->t('You cannot send more than %limit messages in @interval. Try again later.', array(
+ '%limit' => $limit,
+ '@interval' => format_interval($interval),
+ )), 'error');
+ throw new AccessDeniedHttpException();
+ }
}
}
diff --git a/core/modules/contact/lib/Drupal/contact/Plugin/views/field/ContactLink.php b/core/modules/contact/lib/Drupal/contact/Plugin/views/field/ContactLink.php
index e1943d5..97f4dc1 100644
--- a/core/modules/contact/lib/Drupal/contact/Plugin/views/field/ContactLink.php
+++ b/core/modules/contact/lib/Drupal/contact/Plugin/views/field/ContactLink.php
@@ -7,10 +7,14 @@
namespace Drupal\contact\Plugin\views\field;
-use Drupal\Core\Entity\EntityInterface;
use Drupal\Component\Annotation\PluginID;
+use Drupal\Core\Access\StaticAccessCheckInterface;
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Routing\RouteProviderInterface;
use Drupal\user\Plugin\views\field\Link;
use Drupal\views\ResultRow;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+use Symfony\Component\HttpFoundation\Request;
/**
* Defines a field that links to the user contact page, if access is permitted.
@@ -22,6 +26,53 @@
class ContactLink extends Link {
/**
+ * The access manager for contact.personal_page route.
+ *
+ * @var \Drupal\Core\Access\StaticAccessCheckInterface
+ */
+ protected $accessChecker;
+
+ /**
+ * The router provider interface.
+ *
+ * @var \Drupal\Core\Routing\RouteProviderInterface
+ */
+ protected $routeProvider;
+
+ /**
+ * Constructs a Drupal\Component\Plugin\PluginBase object.
+ *
+ * @param array $configuration
+ * A configuration array containing information about the plugin instance.
+ * @param string $plugin_id
+ * The plugin_id for the plugin instance.
+ * @param array $plugin_definition
+ * The plugin implementation definition.
+ * @param \Drupal\Core\Access\StaticAccessCheckInterface $access_checker
+ * The access manager for contact.personal_page route.
+ * @param \Drupal\Core\Routing\RouteProviderInterface $route_provider
+ * The router provider interface.
+ */
+ public function __construct(array $configuration, $plugin_id, array $plugin_definition, StaticAccessCheckInterface $access_checker, RouteProviderInterface $route_provider) {
+ parent::__construct($configuration, $plugin_id, $plugin_definition);
+ $this->accessChecker = $access_checker;
+ $this->routeProvider = $route_provider;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public static function create(ContainerInterface $container, array $configuration, $plugin_id, array $plugin_definition) {
+ return new static(
+ $configuration,
+ $plugin_id,
+ $plugin_definition,
+ $container->get('access_check.contact_personal'),
+ $container->get('router.route_provider')
+ );
+ }
+
+ /**
* {@inheritdoc}
*/
public function buildOptionsForm(&$form, &$form_state) {
@@ -54,7 +105,10 @@ protected function renderLink(EntityInterface $entity, ResultRow $values) {
$uid = $entity->id();
$path = "user/$uid/contact";
- if (!_contact_personal_tab_access($entity)) {
+ $request = Request::create($path);
+ $request->attributes->set('user', $entity);
+ $route = $this->routeProvider->getRouteByName('contact.personal_page');
+ if ($this->accessChecker->access($route, $request) !== StaticAccessCheckInterface::ALLOW) {
return;
}
diff --git a/core/modules/contact/lib/Drupal/contact/Tests/ContactPersonalTest.php b/core/modules/contact/lib/Drupal/contact/Tests/ContactPersonalTest.php
index e974ec6..2c95d3b 100644
--- a/core/modules/contact/lib/Drupal/contact/Tests/ContactPersonalTest.php
+++ b/core/modules/contact/lib/Drupal/contact/Tests/ContactPersonalTest.php
@@ -94,6 +94,8 @@ function testPersonalContactAccess() {
$this->drupalLogin($this->web_user);
$this->drupalGet('user/' . $this->admin_user->id() . '/contact');
$this->assertResponse(200);
+ // Check the page title is properly displayed.
+ $this->assertText(t('Contact @username', array('@username' => $this->admin_user->getUsername())));
// Test denied access to admin user's own contact form.
$this->drupalLogout();
diff --git a/core/modules/contact/lib/Drupal/contact/Tests/ContactSitewideTest.php b/core/modules/contact/lib/Drupal/contact/Tests/ContactSitewideTest.php
index 8a9e210..b8ae287 100644
--- a/core/modules/contact/lib/Drupal/contact/Tests/ContactSitewideTest.php
+++ b/core/modules/contact/lib/Drupal/contact/Tests/ContactSitewideTest.php
@@ -90,6 +90,9 @@ function testSiteWideContact() {
$this->drupalGet('contact');
$this->assertResponse(200);
$this->assertText(t('The contact form has not been configured.'));
+ // Test access personal category via site-wide contact page.
+ $this->drupalGet('contact/personal');
+ $this->assertResponse(403);
// Add categories.
// Test invalid recipients.
@@ -126,6 +129,9 @@ function testSiteWideContact() {
$this->assertEqual($config['reply'], $reply);
$this->assertNotEqual($id, \Drupal::config('contact.settings')->get('default_category'));
$this->assertRaw(t('Category %label has been updated.', array('%label' => $label)));
+ // Ensure the label is displayed on the contact page for this category.
+ $this->drupalGet('contact/' . $id);
+ $this->assertText($label);
// Reset the category back to be the default category.
\Drupal::config('contact.settings')->set('default_category', $id)->save();