diff --git a/core/modules/node/lib/Drupal/node/Plugin/views/field/NodeBulkForm.php b/core/modules/node/lib/Drupal/node/Plugin/views/field/NodeBulkForm.php
index da633d7..68441f6 100644
--- a/core/modules/node/lib/Drupal/node/Plugin/views/field/NodeBulkForm.php
+++ b/core/modules/node/lib/Drupal/node/Plugin/views/field/NodeBulkForm.php
@@ -34,15 +34,6 @@ public function __construct(array $configuration, $plugin_id, array $plugin_defi
   /**
    * {@inheritdoc}
    */
-  protected function getBulkOptions() {
-    return array_map(function ($action) {
-      return $action->label();
-    }, $this->actions);
-  }
-
-  /**
-   * {@inheritdoc}
-   */
   public function views_form_validate(&$form, &$form_state) {
     $selected = array_filter($form_state['values'][$this->options['id']]);
     if (empty($selected)) {
diff --git a/core/modules/system/lib/Drupal/system/Plugin/views/field/BulkFormBase.php b/core/modules/system/lib/Drupal/system/Plugin/views/field/BulkFormBase.php
index 4bc26dd..f50d874 100644
--- a/core/modules/system/lib/Drupal/system/Plugin/views/field/BulkFormBase.php
+++ b/core/modules/system/lib/Drupal/system/Plugin/views/field/BulkFormBase.php
@@ -84,41 +84,48 @@ public function views_form(&$form, &$form_state) {
     // Add the tableselect javascript.
     $form['#attached']['library'][] = array('system', 'drupal.tableselect');
 
-    // Render checkboxes for all rows.
-    $form[$this->options['id']]['#tree'] = TRUE;
-    foreach ($this->view->result as $row_index => $row) {
-      $form[$this->options['id']][$row_index] = array(
-        '#type' => 'checkbox',
-        // We are not able to determine a main "title" for each row, so we can
-        // only output a generic label.
-        '#title' => t('Update this item'),
-        '#title_display' => 'invisible',
-        '#default_value' => !empty($form_state['values'][$this->options['id']][$row_index]) ? 1 : NULL,
+    // Only add the bulk form options and buttons if there are results.
+    if (!empty($this->view->result)) {
+      // Render checkboxes for all rows.
+      $form[$this->options['id']]['#tree'] = TRUE;
+      foreach ($this->view->result as $row_index => $row) {
+        $form[$this->options['id']][$row_index] = array(
+          '#type' => 'checkbox',
+          // We are not able to determine a main "title" for each row, so we can
+          // only output a generic label.
+          '#title' => t('Update this item'),
+          '#title_display' => 'invisible',
+          '#default_value' => !empty($form_state['values'][$this->options['id']][$row_index]) ? 1 : NULL,
+        );
+      }
+
+      // Replace the form submit button label.
+      $form['actions']['submit']['#value'] = t('Apply');
+
+      // Ensure a consistent container for filters/operations in the view header.
+      $form['header'] = array(
+        '#type' => 'container',
+        '#weight' => -100,
       );
-    }
 
-    // Replace the form submit button label.
-    $form['actions']['submit']['#value'] = t('Apply');
-
-    // Ensure a consistent container for filters/operations in the view header.
-    $form['header'] = array(
-      '#type' => 'container',
-      '#weight' => -100,
-    );
-
-    // Build the bulk operations action widget for the header.
-    // Allow themes to apply .container-inline on this separate container.
-    $form['header'][$this->options['id']] = array(
-      '#type' => 'container',
-    );
-    $form['header'][$this->options['id']]['action'] = array(
-      '#type' => 'select',
-      '#title' => t('With selection'),
-      '#options' => $this->getBulkOptions(),
-    );
-
-    // Duplicate the form actions into the action container in the header.
-    $form['header'][$this->options['id']]['actions'] = $form['actions'];
+      // Build the bulk operations action widget for the header.
+      // Allow themes to apply .container-inline on this separate container.
+      $form['header'][$this->options['id']] = array(
+        '#type' => 'container',
+      );
+      $form['header'][$this->options['id']]['action'] = array(
+        '#type' => 'select',
+        '#title' => t('With selection'),
+        '#options' => $this->getBulkOptions(),
+      );
+
+      // Duplicate the form actions into the action container in the header.
+      $form['header'][$this->options['id']]['actions'] = $form['actions'];
+    }
+    else {
+      // Remove the default actions build array.
+      unset($form['actions']);
+    }
   }
 
   /**
@@ -127,7 +134,11 @@ public function views_form(&$form, &$form_state) {
    * @return array
    *   An associative array of operations, suitable for a select element.
    */
-  abstract protected function getBulkOptions();
+  protected function getBulkOptions() {
+    return array_map(function ($action) {
+      return $action->label();
+    }, $this->actions);
+  }
 
   /**
    * Submit handler for the bulk form.
@@ -163,4 +174,11 @@ public function views_form_submit(&$form, &$form_state) {
   public function query() {
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function click_sortable() {
+    return FALSE;
+  }
+
 }
diff --git a/core/modules/user/config/views.view.user_admin_people.yml b/core/modules/user/config/views.view.user_admin_people.yml
new file mode 100644
index 0000000..72eaee1
--- /dev/null
+++ b/core/modules/user/config/views.view.user_admin_people.yml
@@ -0,0 +1,281 @@
+base_field: uid
+base_table: users
+core: 8.x
+description: 'Find and manage people interacting with your site.'
+status: '1'
+display:
+  page_1:
+    display_plugin: page
+    id: page_1
+    display_title: Page
+    position: ''
+    display_options:
+      path: admin/people/list
+      show_admin_links: '0'
+      menu:
+        type: 'default tab'
+        title: List
+        description: 'Find and manage people interacting with your site.'
+        name: admin
+        weight: '-10'
+        context: '0'
+      tab_options:
+        type: normal
+        title: People
+        description: 'Manage user accounts, roles, and permissions.'
+        name: admin
+        weight: '0'
+      defaults:
+        show_admin_links: '0'
+  default:
+    display_plugin: default
+    id: default
+    display_title: Master
+    position: ''
+    display_options:
+      access:
+        type: perm
+        options:
+          perm: 'administer users'
+      cache:
+        type: none
+      query:
+        type: views_query
+      exposed_form:
+        type: basic
+        options:
+          submit_button: Filter
+          reset_button: '1'
+          reset_button_label: Reset
+      pager:
+        type: full
+        options:
+          items_per_page: '50'
+      style:
+        type: table
+        options:
+          columns:
+            user_bulk_form: user_bulk_form
+            name: name
+            status: status
+            rid: rid
+            created: created
+            access: access
+            edit_node: edit_node
+            translation_link: translation_link
+            dropbutton: dropbutton
+          info:
+            user_bulk_form:
+              align: ''
+              separator: ''
+              empty_column: '0'
+              responsive: ''
+            name:
+              sortable: '1'
+              default_sort_order: asc
+              align: ''
+              separator: ''
+              empty_column: '0'
+              responsive: ''
+            status:
+              sortable: '1'
+              default_sort_order: asc
+              align: ''
+              separator: ''
+              empty_column: '0'
+              responsive: priority-low
+            rid:
+              sortable: '0'
+              default_sort_order: asc
+              align: ''
+              separator: ''
+              empty_column: '0'
+              responsive: priority-low
+            created:
+              sortable: '1'
+              default_sort_order: desc
+              align: ''
+              separator: ''
+              empty_column: '0'
+              responsive: priority-low
+            access:
+              sortable: '1'
+              default_sort_order: desc
+              align: ''
+              separator: ''
+              empty_column: '0'
+              responsive: priority-low
+            edit_node:
+              align: ''
+              separator: ''
+              empty_column: '0'
+              responsive: priority-low
+            translation_link:
+              align: ''
+              separator: ''
+              empty_column: '0'
+              responsive: ''
+            dropbutton:
+              sortable: '0'
+              default_sort_order: asc
+              align: ''
+              separator: ''
+              empty_column: '0'
+              responsive: ''
+          default: created
+          empty_table: '1'
+      row:
+        type: fields
+      fields:
+        user_bulk_form:
+          id: user_bulk_form
+          table: users
+          field: user_bulk_form
+          label: 'Bulk update'
+          plugin_id: user_bulk_form
+        name:
+          id: name
+          table: users
+          field: name
+          label: Username
+          link_to_user: '1'
+          format_username: '1'
+          plugin_id: user_name
+        status:
+          id: status
+          table: users
+          field: status
+          label: Status
+          type: active-blocked
+          plugin_id: boolean
+        rid:
+          id: rid
+          table: users_roles
+          field: rid
+          label: Roles
+          type: ul
+          plugin_id: user_roles
+        created:
+          id: created
+          table: users
+          field: created
+          label: 'Member for'
+          date_format: 'raw time ago'
+          plugin_id: date
+        access:
+          id: access
+          table: users
+          field: access
+          label: 'Last access'
+          date_format: 'time ago'
+          plugin_id: date
+        edit_node:
+          id: edit_node
+          table: users
+          field: edit_node
+          exclude: '1'
+          text: Edit
+          plugin_id: user_link_edit
+        translation_link:
+          id: translation_link
+          table: users
+          field: translation_link
+          label: 'Translation link'
+          exclude: '1'
+          alter:
+            alter_text: '0'
+          element_class: ''
+          element_default_classes: '1'
+          empty: ''
+          hide_empty: '0'
+          empty_zero: '0'
+          hide_alter_empty: '1'
+          text: Translate
+          optional: '1'
+          plugin_id: translation_entity_link
+        dropbutton:
+          id: dropbutton
+          table: views
+          field: dropbutton
+          label: Operations
+          fields:
+            edit_node: edit_node
+            translation_link: translation_link
+          destination: '1'
+          plugin_id: dropbutton
+      filters:
+        rid:
+          id: rid
+          table: users_roles
+          field: rid
+          operator: or
+          value: {  }
+          group: '1'
+          exposed: '1'
+          expose:
+            operator_id: rid_op
+            label: Role
+            operator: rid_op
+            identifier: role
+          plugin_id: user_roles
+        permission:
+          id: permission
+          table: role_permission
+          field: permission
+          operator: or
+          value: {  }
+          group: '1'
+          exposed: '1'
+          expose:
+            operator_id: permission_op
+            label: Permission
+            operator: permission_op
+            identifier: permission
+          plugin_id: user_permissions
+        status:
+          id: status
+          table: users
+          field: status
+          operator: '='
+          value: All
+          group: '1'
+          exposed: '1'
+          expose:
+            operator_id: ''
+            label: Active
+            operator: status_op
+            identifier: status
+          plugin_id: boolean
+        uid_raw:
+          id: uid_raw
+          table: users
+          field: uid_raw
+          operator: '!='
+          value:
+            min: ''
+            max: ''
+            value: '0'
+          group: '1'
+          exposed: '0'
+          plugin_id: numeric
+      sorts:
+        created:
+          id: created
+          table: users
+          field: created
+          order: DESC
+          plugin_id: date
+      title: People
+      empty:
+        area_text_custom:
+          id: area_text_custom
+          table: views
+          field: area_text_custom
+          empty: '1'
+          content: 'No people available.'
+          plugin_id: text_custom
+label: People
+module: views
+id: user_admin_people
+tag: default
+langcode: und
diff --git a/core/modules/user/lib/Drupal/user/Plugin/Action/AddRoleUser.php b/core/modules/user/lib/Drupal/user/Plugin/Action/AddRoleUser.php
index f1d1f37..0288ce4 100644
--- a/core/modules/user/lib/Drupal/user/Plugin/Action/AddRoleUser.php
+++ b/core/modules/user/lib/Drupal/user/Plugin/Action/AddRoleUser.php
@@ -28,12 +28,9 @@ class AddRoleUser extends ChangeUserRoleBase {
   public function execute($account = NULL) {
     $rid = $this->configuration['rid'];
     // Skip adding the role to the user if they already have it.
-    if ($account !== FALSE && !isset($account->roles[$rid])) {
-      $roles = $account->roles + array($rid => $rid);
-      // For efficiency manually save the original account before applying
-      // any changes.
+    if (array_search($rid, $account->getBCEntity()->roles) === FALSE) {
       $account->original = clone $account;
-      $account->roles = $roles;
+      $account->roles[] = $rid;
       $account->save();
     }
   }
diff --git a/core/modules/user/lib/Drupal/user/Plugin/Action/RemoveRoleUser.php b/core/modules/user/lib/Drupal/user/Plugin/Action/RemoveRoleUser.php
index a2b2616..2c200e6 100644
--- a/core/modules/user/lib/Drupal/user/Plugin/Action/RemoveRoleUser.php
+++ b/core/modules/user/lib/Drupal/user/Plugin/Action/RemoveRoleUser.php
@@ -28,12 +28,10 @@ class RemoveRoleUser extends ChangeUserRoleBase {
   public function execute($account = NULL) {
     $rid = $this->configuration['rid'];
     // Skip removing the role from the user if they already don't have it.
-    if ($account !== FALSE && isset($account->roles[$rid])) {
-      $roles = array_diff($account->roles, array($rid => $rid));
-      // For efficiency manually save the original account before applying
-      // any changes.
+    $key = array_search($rid, $account->getBCEntity()->roles);
+    if ($key !== FALSE) {
       $account->original = clone $account;
-      $account->roles = $roles;
+      unset($account->roles[$key]);
       $account->save();
     }
   }
diff --git a/core/modules/user/lib/Drupal/user/Plugin/views/field/Link.php b/core/modules/user/lib/Drupal/user/Plugin/views/field/Link.php
index 20f68e8..4ef99cb 100644
--- a/core/modules/user/lib/Drupal/user/Plugin/views/field/Link.php
+++ b/core/modules/user/lib/Drupal/user/Plugin/views/field/Link.php
@@ -48,7 +48,7 @@ public function buildOptionsForm(&$form, &$form_state) {
 
   // An example of field level access control.
   public function access() {
-    return user_access('access user profiles');
+    return user_access('administer users') || user_access('access user profiles');
   }
 
   public function query() {
diff --git a/core/modules/user/lib/Drupal/user/Plugin/views/field/LinkEdit.php b/core/modules/user/lib/Drupal/user/Plugin/views/field/LinkEdit.php
index e7f63be..8954610 100644
--- a/core/modules/user/lib/Drupal/user/Plugin/views/field/LinkEdit.php
+++ b/core/modules/user/lib/Drupal/user/Plugin/views/field/LinkEdit.php
@@ -23,7 +23,7 @@ class LinkEdit extends Link {
    * Overrides \Drupal\user\Plugin\views\field\Link::render_link().
    */
   public function render_link(EntityInterface $entity, \stdClass $values) {
-    if ($entity && $entity->access('edit')) {
+    if ($entity && $entity->access('update')) {
       $this->options['alter']['make_link'] = TRUE;
 
       $text = !empty($this->options['text']) ? $this->options['text'] : t('Edit');
diff --git a/core/modules/user/lib/Drupal/user/Plugin/views/field/UserBulkForm.php b/core/modules/user/lib/Drupal/user/Plugin/views/field/UserBulkForm.php
new file mode 100644
index 0000000..a302a50
--- /dev/null
+++ b/core/modules/user/lib/Drupal/user/Plugin/views/field/UserBulkForm.php
@@ -0,0 +1,64 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\user\Plugin\views\field\UserBulkForm.
+ */
+
+namespace Drupal\user\Plugin\views\field;
+
+use Drupal\Component\Annotation\PluginID;
+use Drupal\Core\Entity\EntityManager;
+use Drupal\system\Plugin\views\field\BulkFormBase;
+use Drupal\user\UserInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Defines a user operations bulk form element.
+ *
+ * @PluginID("user_bulk_form")
+ */
+class UserBulkForm extends BulkFormBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function __construct(array $configuration, $plugin_id, array $plugin_definition, EntityManager $manager) {
+    parent::__construct($configuration, $plugin_id, $plugin_definition, $manager);
+
+    // Filter the actions to only include those for the 'user' entity type.
+    $this->actions = array_filter($this->actions, function ($action) {
+      return $action->getType() == 'user';
+    });
+
+  }
+
+  /**
+   * {@inheritdoc}
+   *
+   * Provide a more useful title to improve the accessibility.
+   */
+  public function views_form(&$form, &$form_state) {
+    parent::views_form($form, $form_state);
+
+    if (!empty($this->view->result)) {
+      foreach ($this->view->result as $row_index => $result) {
+        $account = $result->_entity;
+        if ($account instanceof UserInterface) {
+          $form[$this->options['id']][$row_index]['#title'] = t('Update the user %name', array('%name' => $account->label()));
+        }
+      }
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function views_form_validate(&$form, &$form_state) {
+    $selected = array_filter($form_state['values'][$this->options['id']]);
+    if (empty($selected)) {
+      form_set_error('', t('No users selected.'));
+    }
+  }
+
+}
diff --git a/core/modules/user/lib/Drupal/user/Tests/UserAdminListingTest.php b/core/modules/user/lib/Drupal/user/Tests/UserAdminListingTest.php
new file mode 100644
index 0000000..e1e20e6
--- /dev/null
+++ b/core/modules/user/lib/Drupal/user/Tests/UserAdminListingTest.php
@@ -0,0 +1,103 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\user\Tests\UserAdminListingTest.
+ */
+
+namespace Drupal\user\Tests;
+
+use Drupal\simpletest\WebTestBase;
+
+/**
+ * Defines a test for the fallback user admin listing.
+ *
+ * @see user_admin_account()
+ */
+class UserAdminListingTest extends WebTestBase {
+
+  public static function getInfo() {
+    return array(
+      'name' => 'User people listing',
+      'description' => 'Test the user admin listing if views is not enabled.',
+      'group' => 'User'
+    );
+  }
+
+  /**
+   * Tests the listing.
+   */
+  public function testUserListing() {
+    $this->drupalGet('admin/people');
+    $this->assertResponse(403, 'Anonymous user does not have access to the user admin listing.');
+
+    // Create a bunch of users.
+    $accounts = array();
+    for ($i = 0; $i < 3; $i++) {
+      $account = $this->drupalCreateUser();
+      $accounts[$account->name] = $account;
+    }
+    // Create a blocked user.
+    $account = $this->drupalCreateUser();
+    $account->status = 0;
+    $account->save();
+    $accounts[$account->name] = $account;
+
+    // Create a user at a certain timestamp.
+    $account = $this->drupalCreateUser();
+    $account->created = 1363219200;
+    $account->save();
+    $accounts[$account->name] = $account;
+    $timestamp_user = $account->name;
+
+    $rid_1 = $this->drupalCreateRole(array(), 'custom_role_1', 'custom_role_1');
+    $rid_2 = $this->drupalCreateRole(array(), 'custom_role_2', 'custom_role_2');
+
+    $account = $this->drupalCreateUser();
+    $account->roles[$rid_1] = $rid_1;
+    $account->roles[$rid_2] = $rid_2;
+    $account->save();
+    $accounts[$account->name] = $account;
+    $role_account_name = $account->name;
+
+    // Create an admin user and look at the listing.
+    $admin_user = $this->drupalCreateUser(array('administer users'));
+    $accounts[$admin_user->name] = $admin_user;
+
+    $accounts['admin'] = user_load('1');
+
+    $this->drupalLogin($admin_user);
+
+    $this->drupalGet('admin/people');
+    $this->assertResponse(200, 'The admin user has access to the user admin listing.');
+
+    $result = $this->xpath('//table[contains(@class, "responsive-enabled")]/tbody/tr');
+    $result_accounts = array();
+    foreach ($result as $account) {
+      $name = (string) $account->td[0]->span;
+      $roles = array();
+      if (isset($account->td[2]->div->ul)) {
+        foreach ($account->td[2]->div->ul->li as $element) {
+          $roles[] = (string) $element;
+        }
+      }
+      $result_accounts[$name] = array(
+        'name' => $name,
+        'status' => (string) $account->td[1],
+        'roles' => $roles,
+        'member_for' => (string) $account->td[3],
+      );
+    }
+
+    $this->assertFalse(array_diff(array_keys($result_accounts), array_keys($accounts)), 'Ensure all accounts are listed.');
+    foreach ($result_accounts as $name => $values) {
+      $this->assertEqual($values['status'] == t('active'), $accounts[$name]->status, 'Ensure the status is displayed properly.');
+    }
+
+    $expected_roles = array('custom_role_1', 'custom_role_2');
+    $this->assertEqual($result_accounts[$role_account_name]['roles'], $expected_roles, 'Ensure roles are listed properly.');
+
+    $this->assertEqual($result_accounts[$timestamp_user]['member_for'], format_interval(REQUEST_TIME - $accounts[$timestamp_user]->created), 'Ensure the right member time is displayed.');
+  }
+
+}
diff --git a/core/modules/user/lib/Drupal/user/Tests/UserAdminTest.php b/core/modules/user/lib/Drupal/user/Tests/UserAdminTest.php
index ca17c50..234391d 100644
--- a/core/modules/user/lib/Drupal/user/Tests/UserAdminTest.php
+++ b/core/modules/user/lib/Drupal/user/Tests/UserAdminTest.php
@@ -16,7 +16,7 @@ class UserAdminTest extends WebTestBase {
    *
    * @var array
    */
-  public static $modules = array('taxonomy');
+  public static $modules = array('taxonomy', 'views');
 
   public static function getInfo() {
     return array(
@@ -49,9 +49,7 @@ function testUserAdmin() {
     $this->assertRaw($link, 'Found user A edit link on admin users page');
 
     // Filter the users by permission 'administer taxonomy'.
-    $edit = array();
-    $edit['permission'] = 'administer taxonomy';
-    $this->drupalPost('admin/people', $edit, t('Filter'));
+    $this->drupalGet('admin/people', array('query' => array('permission' => 'administer taxonomy')));
 
     // Check if the correct users show up.
     $this->assertNoText($user_a->name, 'User A not on filtered by perm admin users page');
@@ -61,8 +59,7 @@ function testUserAdmin() {
     // Filter the users by role. Grab the system-generated role name for User C.
     $roles = $user_c->roles;
     unset($roles[array_search(DRUPAL_AUTHENTICATED_RID, $roles)]);
-    $edit['role'] = reset($roles);
-    $this->drupalPost('admin/people', $edit, t('Refine'));
+    $this->drupalGet('admin/people', array('query' => array('role' => reset($roles))));
 
     // Check if the correct users show up when filtered by role.
     $this->assertNoText($user_a->name, 'User A not on filtered by role on admin users page');
@@ -73,17 +70,17 @@ function testUserAdmin() {
     $account = user_load($user_c->uid);
     $this->assertEqual($account->status, 1, 'User C not blocked');
     $edit = array();
-    $edit['operation'] = 'user_block_user_action';
-    $edit['accounts[' . $account->uid . ']'] = TRUE;
-    $this->drupalPost('admin/people', $edit, t('Update'));
+    $edit['action'] = 'user_block_user_action';
+    $edit['user_bulk_form[1]'] = TRUE;
+    $this->drupalPost('admin/people', $edit, t('Apply'));
     $account = user_load($user_c->uid, TRUE);
     $this->assertEqual($account->status, 0, 'User C blocked');
 
     // Test unblocking of a user from /admin/people page and sending of activation mail
     $editunblock = array();
-    $editunblock['operation'] = 'user_unblock_user_action';
-    $editunblock['accounts[' . $account->uid . ']'] = TRUE;
-    $this->drupalPost('admin/people', $editunblock, t('Update'));
+    $editunblock['action'] = 'user_unblock_user_action';
+    $editunblock['user_bulk_form[1]'] = TRUE;
+    $this->drupalPost('admin/people', $editunblock, t('Apply'));
     $account = user_load($user_c->uid, TRUE);
     $this->assertEqual($account->status, 1, 'User C unblocked');
     $this->assertMail("to", $account->mail, "Activation mail sent to user C");
diff --git a/core/modules/user/lib/Drupal/user/Tests/UserCancelTest.php b/core/modules/user/lib/Drupal/user/Tests/UserCancelTest.php
index 3861468..15e9912 100644
--- a/core/modules/user/lib/Drupal/user/Tests/UserCancelTest.php
+++ b/core/modules/user/lib/Drupal/user/Tests/UserCancelTest.php
@@ -67,6 +67,7 @@ function testUserCancelWithoutPermission() {
    * administer the site.
    */
   function testUserCancelUid1() {
+    module_enable(array('views'));
     // Update uid 1's name and password to we know it.
     $password = user_password();
     $account = array(
@@ -88,10 +89,10 @@ function testUserCancelUid1() {
     $this->admin_user = $this->drupalCreateUser(array('administer users'));
     $this->drupalLogin($this->admin_user);
     $edit = array(
-      'operation' => 'user_cancel_user_action',
-      'accounts[1]' => TRUE,
+      'action' => 'user_cancel_user_action',
+      'user_bulk_form[0]' => TRUE,
     );
-    $this->drupalPost('admin/people', $edit, t('Update'));
+    $this->drupalPost('admin/people', $edit, t('Apply'));
 
     // Verify that uid 1's account was not cancelled.
     $user1 = user_load(1, TRUE);
@@ -391,6 +392,7 @@ function testUserWithoutEmailCancelByAdmin() {
    * Create an administrative user and mass-delete other users.
    */
   function testMassUserCancelByAdmin() {
+    module_enable(array('views'));
     config('user.settings')->set('cancel_method', 'user_cancel_reassign')->save();
     // Enable account cancellation notification.
     config('user.settings')->set('notify.status_canceled', TRUE)->save();
@@ -408,14 +410,11 @@ function testMassUserCancelByAdmin() {
 
     // Cancel user accounts, including own one.
     $edit = array();
-    $edit['operation'] = 'user_cancel_user_action';
-    foreach ($users as $uid => $account) {
-      $edit['accounts[' . $uid . ']'] = TRUE;
+    $edit['action'] = 'user_cancel_user_action';
+    for ($i = 0; $i <= 4; $i++) {
+      $edit['user_bulk_form[' . $i . ']'] = TRUE;
     }
-    $edit['accounts[' . $admin_user->uid . ']'] = TRUE;
-    // Also try to cancel uid 1.
-    $edit['accounts[1]'] = TRUE;
-    $this->drupalPost('admin/people', $edit, t('Update'));
+    $this->drupalPost('admin/people', $edit, t('Apply'));
     $this->assertText(t('Are you sure you want to cancel these user accounts?'), 'Confirmation form to cancel accounts displayed.');
     $this->assertText(t('When cancelling these accounts'), 'Allows to select account cancellation method.');
     $this->assertText(t('Require e-mail confirmation to cancel account.'), 'Allows to send confirmation mail.');
diff --git a/core/modules/user/lib/Drupal/user/Tests/UserTranslationUITest.php b/core/modules/user/lib/Drupal/user/Tests/UserTranslationUITest.php
index fe3fa0f..d92754e 100644
--- a/core/modules/user/lib/Drupal/user/Tests/UserTranslationUITest.php
+++ b/core/modules/user/lib/Drupal/user/Tests/UserTranslationUITest.php
@@ -24,7 +24,7 @@ class UserTranslationUITest extends EntityTranslationUITest {
    *
    * @var array
    */
-  public static $modules = array('language', 'translation_entity', 'user');
+  public static $modules = array('language', 'translation_entity', 'user', 'views');
 
   public static function getInfo() {
     return array(
diff --git a/core/modules/user/lib/Drupal/user/Tests/Views/BulkFormTest.php b/core/modules/user/lib/Drupal/user/Tests/Views/BulkFormTest.php
new file mode 100644
index 0000000..d51a6b6
--- /dev/null
+++ b/core/modules/user/lib/Drupal/user/Tests/Views/BulkFormTest.php
@@ -0,0 +1,108 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\user\Tests\Views\BulkFormTest.
+ */
+
+namespace Drupal\user\Tests\Views;
+
+/**
+ * Tests the views bulk form test.
+ *
+ * @see \Drupal\user\Plugin\views\field\BulkForm
+ */
+class BulkFormTest extends UserTestBase {
+
+  /**
+   * Views used by this test.
+   *
+   * @var array
+   */
+  public static $testViews = array('test_user_bulk_form');
+
+  public static function getInfo() {
+    return array(
+      'name' => 'User: Bulk form',
+      'description' => 'Tests a user bulk form.',
+      'group' => 'Views module integration',
+    );
+  }
+
+  /**
+   * Tests the user bulk form.
+   */
+  public function testBulkForm() {
+    $this->drupalLogin($this->drupalCreateUser(array('administer permissions')));
+
+    $this->drupalGet('test-user-bulk-form');
+    $elements = $this->xpath('//select[@id="edit-action"]//option');
+    $this->assertIdentical(count($elements), count($this->container->get('module_handler')->invokeAll('user_operations')), 'All user operations are found.');
+
+    // Test submitting the page with no selection.
+    $edit = array(
+      'action' => 'block',
+    );
+    $this->drupalPost(NULL, $edit, t('Apply'));
+    // @todo Validation errors are only shown on page refresh.
+    $this->drupalGet('test-user-bulk-form');
+    $this->assertText(t('No users selected.'));
+
+    // Assign a role to a user.
+    $account = $this->users[0];
+    $roles = user_role_names(TRUE);
+    unset($roles[DRUPAL_AUTHENTICATED_RID]);
+    $role = key($roles);
+
+    $this->assertTrue(!isset($account->roles[$role]), 'The user currently does not have a custom role.');
+    $edit = array(
+      'user_bulk_form[1]' => TRUE,
+      'action' => 'add_role-' . $role,
+    );
+    $this->drupalPost(NULL, $edit, t('Apply'));
+    // Re-load the user and check their roles.
+    $account = entity_load('user', $account->id(), TRUE);
+    $this->assertTrue(isset($account->roles[$role]), 'The user now has the custom role.');
+
+    $edit = array(
+      'user_bulk_form[1]' => TRUE,
+      'action' => 'remove_role-' . $role,
+    );
+    $this->drupalPost(NULL, $edit, t('Apply'));
+    // Re-load the user and check their roles.
+    $account = entity_load('user', $account->id(), TRUE);
+    $this->assertTrue(!isset($account->roles[$role]), 'The user no longer has the custom role.');
+
+    // Block a user using the bulk form.
+    $this->assertTrue($account->status);
+    $this->assertRaw($account->label(), 'The user is found in the table.');
+    $edit = array(
+      'user_bulk_form[1]' => TRUE,
+      'action' => 'block',
+    );
+    $this->drupalPost(NULL, $edit, t('Apply'));
+    // Re-load the user and check their status.
+    $account = entity_load('user', $account->id(), TRUE);
+    $this->assertFalse($account->status);
+    $this->assertNoRaw($account->label(), 'The user is not found in the table.');
+
+    // Remove the user status filter from the view.
+    $view = views_get_view('test_user_bulk_form');
+    $view->removeItem('default', 'filter', 'status');
+    $view->storage->save();
+
+    // Ensure the anonymous user is found.
+    $this->drupalGet('test-user-bulk-form');
+    $this->assertText(config('user.settings')->get('anonymous'));
+
+    // Attempt to block the anonymous user.
+    $edit = array(
+      'user_bulk_form[0]' => TRUE,
+      'action' => 'block',
+    );
+    $this->drupalPost(NULL, $edit, t('Apply'));
+    $anonymous_account = user_load(0);
+    $this->assertFalse($anonymous_account->status, 0, 'Ensure the anonymous user got blocked.');
+  }
+
+}
diff --git a/core/modules/user/tests/modules/user_test_views/test_views/views.view.test_user_bulk_form.yml b/core/modules/user/tests/modules/user_test_views/test_views/views.view.test_user_bulk_form.yml
new file mode 100644
index 0000000..ca11a38
--- /dev/null
+++ b/core/modules/user/tests/modules/user_test_views/test_views/views.view.test_user_bulk_form.yml
@@ -0,0 +1,53 @@
+base_field: uid
+base_table: users
+core: 8.x
+description: ''
+status: '1'
+display:
+  default:
+    display_plugin: default
+    id: default
+    display_title: Master
+    position: ''
+    display_options:
+      style:
+        type: table
+      row:
+        type: fields
+      fields:
+        user_bulk_form:
+          id: user_bulk_form
+          table: users
+          field: user_bulk_form
+          plugin_id: user_bulk_form
+        name:
+          id: name
+          table: users
+          field: name
+          plugin_id: user_name
+      sorts:
+        uid:
+          id: uid
+          table: users
+          field: uid
+          order: ASC
+          plugin_id: user
+      filters:
+        status:
+          id: status
+          table: users
+          field: status
+          operator: '='
+          value: '1'
+          plugin_id: boolean
+  page_1:
+    display_plugin: page
+    id: page_1
+    display_title: Page
+    position: ''
+    display_options:
+      path: test-user-bulk-form
+label: ''
+module: views
+id: test_user_bulk_form
+tag: ''
diff --git a/core/modules/user/user.admin.inc b/core/modules/user/user.admin.inc
index 7c04815..842b1cf 100644
--- a/core/modules/user/user.admin.inc
+++ b/core/modules/user/user.admin.inc
@@ -6,158 +6,9 @@
  */
 
 /**
- * Page callback: Generates the appropriate user administration form.
- *
- * This function generates the user registration, multiple user cancellation,
- * or filtered user list admin form, depending on the argument and the POST
- * form values.
- *
- * @param string $callback_arg
- *   (optional) Indicates which form to build. Defaults to '', which will
- *   trigger the user filter form. If the POST value 'op' is present, this
- *   function uses that value as the callback argument.
- *
- * @return string
- *   A renderable form array for the respective request.
- */
-function user_admin($callback_arg = '') {
-  $op = isset($_POST['op']) ? $_POST['op'] : $callback_arg;
-
-  switch ($op) {
-    case t('Create new account'):
-    case 'create':
-      $account = entity_create('user', array());
-      $build['user_register'] = entity_get_form($account, 'register');
-      break;
-    default:
-      if (!empty($_POST['accounts']) && isset($_POST['operation']) && ($_POST['operation'] == 'cancel')) {
-        $build['user_multiple_cancel_confirm'] = drupal_get_form('user_multiple_cancel_confirm');
-      }
-      else {
-        $build['user_filter_form'] = drupal_get_form('user_filter_form');
-        $build['user_admin_account'] = drupal_get_form('user_admin_account');
-      }
-  }
-  return $build;
-}
-
-/**
- * Form builder; Return form for user administration filters.
- *
- * @ingroup forms
- * @see user_filter_form_submit()
- */
-function user_filter_form() {
-  $session = isset($_SESSION['user_overview_filter']) ? $_SESSION['user_overview_filter'] : array();
-  $filters = user_filters();
-
-  $i = 0;
-  $form['filters'] = array(
-    '#type' => 'details',
-    '#title' => t('Show only users where'),
-    '#theme' => 'exposed_filters__user',
-  );
-  foreach ($session as $filter) {
-    list($type, $value) = $filter;
-    if ($type == 'permission') {
-      // Merge arrays of module permissions into one.
-      // Slice past the first element '[any]' whose value is not an array.
-      $options = call_user_func_array('array_merge', array_slice($filters[$type]['options'], 1));
-      $value = $options[$value];
-    }
-    else {
-      $value = $filters[$type]['options'][$value];
-    }
-    $t_args = array('%property' => $filters[$type]['title'], '%value' => $value);
-    if ($i++) {
-      $form['filters']['current'][] = array('#markup' => t('and where %property is %value', $t_args));
-    }
-    else {
-      $form['filters']['current'][] = array('#markup' => t('%property is %value', $t_args));
-    }
-  }
-
-  $form['filters']['status'] = array(
-    '#type' => 'container',
-    '#attributes' => array('class' => array('clearfix')),
-    '#prefix' => ($i ? '<div class="additional-filters">' . t('and where') . '</div>' : ''),
-  );
-  $form['filters']['status']['filters'] = array(
-    '#type' => 'container',
-    '#attributes' => array('class' => array('filters')),
-  );
-  foreach ($filters as $key => $filter) {
-    $form['filters']['status']['filters'][$key] = array(
-      '#type' => 'select',
-      '#options' => $filter['options'],
-      '#title' => $filter['title'],
-      '#default_value' => '[any]',
-    );
-  }
-
-  $form['filters']['status']['actions'] = array(
-    '#type' => 'actions',
-    '#attributes' => array('class' => array('container-inline')),
-  );
-  $form['filters']['status']['actions']['submit'] = array(
-    '#type' => 'submit',
-    '#value' => (count($session) ? t('Refine') : t('Filter')),
-  );
-  if (count($session)) {
-    $form['filters']['status']['actions']['undo'] = array(
-      '#type' => 'submit',
-      '#value' => t('Undo'),
-    );
-    $form['filters']['status']['actions']['reset'] = array(
-      '#type' => 'submit',
-      '#value' => t('Reset'),
-    );
-  }
-
-  drupal_add_library('system', 'drupal.form');
-
-  return $form;
-}
-
-/**
- * Process result from user administration filter form.
- */
-function user_filter_form_submit($form, &$form_state) {
-  $op = $form_state['values']['op'];
-  $filters = user_filters();
-  switch ($op) {
-    case t('Filter'):
-    case t('Refine'):
-      // Apply every filter that has a choice selected other than 'any'.
-      foreach ($filters as $filter => $options) {
-        if (isset($form_state['values'][$filter]) && $form_state['values'][$filter] != '[any]') {
-          $_SESSION['user_overview_filter'][] = array($filter, $form_state['values'][$filter]);
-        }
-      }
-      break;
-    case t('Undo'):
-      array_pop($_SESSION['user_overview_filter']);
-      break;
-    case t('Reset'):
-      $_SESSION['user_overview_filter'] = array();
-      break;
-    case t('Update'):
-      return;
-  }
-
-  $form_state['redirect'] = 'admin/people';
-  return;
-}
-
-/**
- * Form builder; User administration page.
- *
- * @ingroup forms
- * @see user_admin_account_validate()
- * @see user_admin_account_submit()
+ * Page callback: User administration page.
  */
 function user_admin_account() {
-
   $header = array(
     'username' => array('data' => t('Username'), 'field' => 'u.name'),
     'status' => array('data' => t('Status'), 'field' => 'u.status', 'class' => array(RESPONSIVE_PRIORITY_LOW)),
@@ -184,29 +35,6 @@ function user_admin_account() {
     ->setCountQuery($count_query);
   $result = $query->execute();
 
-  $form['options'] = array(
-    '#type' => 'details',
-    '#title' => t('Update options'),
-    '#attributes' => array('class' => array('container-inline')),
-  );
-  $options = array();
-  $actions = entity_load_multiple_by_properties('action', array('type' => 'user'));
-  foreach ($actions as $id => $action) {
-    $options[$id] = $action->label();
-  }
-  $form['options']['operation'] = array(
-    '#type' => 'select',
-    '#title' => t('Action'),
-    '#title_display' => 'invisible',
-    '#options' => $options,
-    '#default_value' => 'unblock',
-  );
-  $options = array();
-  $form['options']['submit'] = array(
-    '#type' => 'submit',
-    '#value' => t('Update'),
-  );
-
   $destination = drupal_get_destination();
   $status = array(t('blocked'), t('active'));
   $roles = array_map('check_plain', user_role_names(TRUE));
@@ -250,9 +78,9 @@ function user_admin_account() {
   }
 
   $form['accounts'] = array(
-    '#type' => 'tableselect',
+    '#theme' => 'table',
     '#header' => $header,
-    '#options' => $options,
+    '#rows' => $options,
     '#empty' => t('No people available.'),
   );
   $form['pager'] = array('#markup' => theme('pager'));
@@ -261,25 +89,11 @@ function user_admin_account() {
 }
 
 /**
- * Submit the user administration update form.
+ * Page callback: Generates user create administration form.
  */
-function user_admin_account_submit($form, &$form_state) {
-  if ($action = entity_load('action', $form_state['values']['operation'])) {
-    $accounts = entity_load_multiple('user', array_filter($form_state['values']['accounts']));
-    $action->execute($accounts);
-    $operation_definition = $action->getPluginDefinition();
-    if (!empty($operation_definition['confirm_form_path'])) {
-      $form_state['redirect'] = $operation_definition['confirm_form_path'];
-    }
-    drupal_set_message(t('The update has been performed.'));
-  }
-}
-
-function user_admin_account_validate($form, &$form_state) {
-  $form_state['values']['accounts'] = array_filter($form_state['values']['accounts']);
-  if (count($form_state['values']['accounts']) == 0) {
-    form_set_error('', t('No users selected.'));
-  }
+function user_admin_create() {
+  $account = entity_create('user', array());
+  return entity_get_form($account, 'register');
 }
 
 /**
diff --git a/core/modules/user/user.module b/core/modules/user/user.module
index 1c7633d..1c2a9d4 100644
--- a/core/modules/user/user.module
+++ b/core/modules/user/user.module
@@ -930,21 +930,12 @@ function user_menu() {
   $items['admin/people'] = array(
     'title' => 'People',
     'description' => 'Manage user accounts, roles, and permissions.',
-    'page callback' => 'user_admin',
-    'page arguments' => array('list'),
+    'page callback' => 'user_admin_account',
     'access arguments' => array('administer users'),
     'position' => 'left',
     'weight' => -4,
     'file' => 'user.admin.inc',
   );
-  $items['admin/people/people'] = array(
-    'title' => 'List',
-    'description' => 'Find and manage people interacting with your site.',
-    'access arguments' => array('administer users'),
-    'type' => MENU_DEFAULT_LOCAL_TASK,
-    'file' => 'user.admin.inc',
-  );
-
   // Permissions and role forms.
   $items['admin/people/permissions'] = array(
     'title' => 'Permissions',
@@ -984,8 +975,9 @@ function user_menu() {
 
   $items['admin/people/create'] = array(
     'title' => 'Add user',
-    'page arguments' => array('create'),
+    'page callback' => 'user_admin_create',
     'access arguments' => array('administer users'),
+    'file' => 'user.admin.inc',
     'type' => MENU_LOCAL_ACTION,
   );
   $items['admin/people/cancel'] = array(
@@ -994,6 +986,7 @@ function user_menu() {
     'page arguments' => array('user_multiple_cancel_confirm'),
     'access arguments' => array('administer users'),
     'file' => 'user.admin.inc',
+    'type' => MENU_CALLBACK,
   );
 
   // Administration pages.
@@ -2064,8 +2057,6 @@ function user_role_revoke_permissions($rid, array $permissions = array()) {
 }
 
 function user_multiple_cancel_confirm($form, &$form_state) {
-  $edit = $form_state['input'];
-
   // Retrieve the accounts to be canceled from the temp store.
   $accounts = Drupal::service('user.tempstore')->get('user_user_operations_cancel')->get($GLOBALS['user']->uid);
   $form['accounts'] = array('#prefix' => '<ul>', '#suffix' => '</ul>', '#tree' => TRUE);
diff --git a/core/modules/user/user.views.inc b/core/modules/user/user.views.inc
index 173d615..0cc88ce 100644
--- a/core/modules/user/user.views.inc
+++ b/core/modules/user/user.views.inc
@@ -318,6 +318,14 @@ function user_views_data() {
     ),
   );
 
+  $data['users']['user_bulk_form'] = array(
+    'title' => t('Bulk update'),
+    'help' => t('Add a form element that lets you run operations on multiple users.'),
+    'field' => array(
+      'id' => 'user_bulk_form',
+    ),
+  );
+
   // Define the base group of this table. Fields that don't have a group defined
   // will go into this field by default.
   $data['users_roles']['table']['group']  = t('User');
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/query/Sql.php b/core/modules/views/lib/Drupal/views/Plugin/views/query/Sql.php
index b23fe4a..b22353b 100644
--- a/core/modules/views/lib/Drupal/views/Plugin/views/query/Sql.php
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/query/Sql.php
@@ -1562,7 +1562,7 @@ function loadEntities(&$results) {
 
       foreach ($results as $index => $result) {
         // Store the entity id if it was found.
-        if (!empty($result->$id_alias)) {
+        if (isset($result->{$id_alias}) && $result->{$id_alias} != '') {
           $ids_by_table[$table_alias][$index] = $result->$id_alias;
         }
       }
