diff --git a/core/lib/Drupal/Core/Access/AccessManager.php b/core/lib/Drupal/Core/Access/AccessManager.php
index 62ab2d6..ab34fc5 100644
--- a/core/lib/Drupal/Core/Access/AccessManager.php
+++ b/core/lib/Drupal/Core/Access/AccessManager.php
@@ -2,7 +2,7 @@
 
 /**
  * @file
- * Contains Drupal\Core\Access\AccessManager.
+ * Contains \Drupal\Core\Access\AccessManager.
  */
 
 namespace Drupal\Core\Access;
diff --git a/core/modules/contact/contact.module b/core/modules/contact/contact.module
index 66a80ec..8b24b6c 100644
--- a/core/modules/contact/contact.module
+++ b/core/modules/contact/contact.module
@@ -80,8 +80,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,
   );
@@ -95,18 +93,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()), \Drupal::currentUser());
-}
-
-/**
  * 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 061bd10..0000000
--- a/core/modules/contact/contact.pages.inc
+++ /dev/null
@@ -1,113 +0,0 @@
-<?php
-
-/**
- * @file
- * Page callbacks for the Contact module.
- */
-
-use Drupal\contact\Entity\Category;
-use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
-use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
-use Drupal\Component\Utility\String;
-
-/**
- * Page callback: Presents the site-wide contact form.
- *
- * @param \Drupal\contact\Entity\Category $category
- *   (optional) The contact category to use.
- *
- * @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException
- * @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
- *
- * @see contact_menu()
- * @see contact_site_form_submit()
- * @ingroup forms
- *
- * @deprecated Use \Drupal\contact\Controller\ContactController::contactSitePage()
- */
-function contact_site_page(Category $category = NULL) {
-  // Check if flood control has been activated for sending e-mails.
-  if (!user_access('administer contact forms')) {
-    contact_flood_control();
-  }
-
-  if (!isset($category)) {
-    $categories = entity_load_multiple('contact_category');
-    $default_category = \Drupal::config('contact.settings')->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. <a href="@add">Add one or more categories</a> 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(),
-  ));
-  $form = \Drupal::entityManager()->getForm($message);
-  $form['#title'] = String::checkPlain($category->label());
-  return $form;
-}
-
-/**
- * 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();
-  }
-
-  $message = entity_create('contact_message', array(
-    'recipient' => $recipient,
-    'category' => 'personal',
-  ));
-  $form = \Drupal::entityManager()->getForm($message);
-  $form['#title'] = t('Contact @username', array('@username' => $recipient->getUsername()));
-  return $form;
-}
-
-/**
- * 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 f12d67e..10ba2db 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'
   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,7 +16,7 @@ contact.category_list:
 contact.category_add:
   path: '/admin/structure/contact/add'
   defaults:
-    _entity_form: contact_category.add
+    _entity_form: 'contact_category.add'
     _title: 'Add category'
   requirements:
     _permission: 'administer contact forms'
@@ -24,9 +24,9 @@ contact.category_add:
 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'
@@ -43,7 +43,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.view'
 
 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..59ee2c7 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,17 @@ class CategoryAccessController extends EntityAccessController {
    * {@inheritdoc}
    */
   public function checkAccess(EntityInterface $entity, $operation, $langcode, AccountInterface $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';
+    if ($operation == 'view') {
+      // Do not allow access personal category via site-wide route.
+      return $account->hasPermission('access site-wide contact form') && $entity->id() !== 'personal';
+    }
+    elseif ($operation == 'delete' || $operation == 'update') {
+      // 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';
     }
     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. <a href="@add">Add one or more categories</a> 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 d1eb806..86d6f63 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,9 +7,11 @@
 
 namespace Drupal\contact\Plugin\views\field;
 
+use Drupal\Core\Access\AccessManager;
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\user\Plugin\views\field\Link;
 use Drupal\views\ResultRow;
+use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
  * Defines a field that links to the user contact page, if access is permitted.
@@ -21,6 +23,64 @@
 class ContactLink extends Link {
 
   /**
+   * The access manager.
+   *
+   * @var \Drupal\Core\Access\AccessManager
+   */
+  protected $accessManager;
+
+  /**
+   * Current user object.
+   *
+   * @var \Drupal\Core\Session\AccountInterface
+   */
+  protected $currentUser;
+
+  /**
+   * Gets the current active user.
+   *
+   * @todo: https://drupal.org/node/2105123 put this method in
+   *   \Drupal\Core\Plugin\PluginBase instead.
+   *
+   * @return \Drupal\Core\Session\AccountInterface
+   */
+  protected function currentUser() {
+    if (!$this->currentUser) {
+      $this->currentUser = \Drupal::currentUser();
+    }
+    return $this->currentUser;
+  }
+
+  /**
+   * Constructs a ContactLink 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\AccessManager $access_manager
+   *   The access manager.
+   */
+  public function __construct(array $configuration, $plugin_id, array $plugin_definition, AccessManager $access_manager) {
+    parent::__construct($configuration, $plugin_id, $plugin_definition);
+    $this->accessManager = $access_manager;
+  }
+
+  /**
+   * {@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_manager')
+    );
+  }
+
+  /**
    * {@inheritdoc}
    */
   public function buildOptionsForm(&$form, &$form_state) {
@@ -50,15 +110,12 @@ protected function renderLink(EntityInterface $entity, ResultRow $values) {
 
     // Check access when we pull up the user account so we know
     // if the user has made the contact page available.
-    $uid = $entity->id();
-
-    $path = "user/$uid/contact";
-    if (!_contact_personal_tab_access($entity)) {
+    if (!$this->accessManager->checkNamedRoute('contact.personal_page', array('user' => $entity->id()), $this->currentUser())) {
       return;
     }
 
     $this->options['alter']['make_link'] = TRUE;
-    $this->options['alter']['path'] = $path;
+    $this->options['alter']['path'] = "user/{$entity->id()}/contact";
 
     $title = t('Contact %user', array('%user' => $entity->name->value));
     $this->options['alter']['attributes'] = array('title' => $title);
diff --git a/core/modules/contact/lib/Drupal/contact/Tests/ContactSitewideTest.php b/core/modules/contact/lib/Drupal/contact/Tests/ContactSitewideTest.php
index 1c69c2e..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.
