diff --git a/core/modules/openid/lib/Drupal/openid/Tests/OpenIDFunctionalTest.php b/core/modules/openid/lib/Drupal/openid/Tests/OpenIDFunctionalTest.php index 1715255..589db1a 100644 --- a/core/modules/openid/lib/Drupal/openid/Tests/OpenIDFunctionalTest.php +++ b/core/modules/openid/lib/Drupal/openid/Tests/OpenIDFunctionalTest.php @@ -264,13 +264,9 @@ function testBlockedUserLogin() { // Log in as an admin user and block the account. $admin_user = $this->drupalCreateUser(array('administer users')); $this->drupalLogin($admin_user); - $this->drupalGet('admin/people'); - $edit = array( - 'operation' => 'block', - 'accounts[' . $this->web_user->uid . ']' => TRUE, - ); - $this->drupalPost('admin/people', $edit, t('Update')); - $this->assertRaw('The update has been performed.', 'Account was blocked.'); + + $this->drupalPost("user/{$this->web_user->id()}/cancel", array(), t('Cancel account')); + $this->assertRaw(t('%user has been disabled.', array('%user' => $this->web_user->name) ), 'Account was blocked.'); $this->drupalLogout(); $this->submitLoginForm($identity); 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 fda3479..0d0972c 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 @@ -51,41 +51,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']); + } } /** @@ -112,4 +119,11 @@ public function views_form(&$form, &$form_state) { public function query() { } + /** + * Overrides \Drupal\views\Plugin\views\Plugin\field\FieldPluginBase::click_sortable(). + */ + 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..375d191 --- /dev/null +++ b/core/modules/user/config/views.view.user_admin_people.yml @@ -0,0 +1,277 @@ +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 + 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' + 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 + 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 +human_name: People +module: views +id: user_admin_people +tag: default +langcode: und 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 6264824..a41c770 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 @@ -51,7 +51,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 b50b0da..615663d 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 @@ -26,7 +26,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..e659078 --- /dev/null +++ b/core/modules/user/lib/Drupal/user/Plugin/views/field/UserBulkForm.php @@ -0,0 +1,84 @@ +invokeAll('user_operations')); + } + + /** + * Implements \Drupal\views\Plugin\views\field\FieldPluginBase::views_form_validate(). + */ + public function views_form_validate(&$form, &$form_state) { + $selected = array_filter($form_state['values'][$this->options['id']]); + if (count($selected) == 0) { + form_set_error('', t('No users selected.')); + } + } + + /** + * Implements \Drupal\system\Plugin\views\field\BulkFormBase::views_form_submit(). + */ + public function views_form_submit(&$form, &$form_state) { + if ($form_state['step'] == 'views_form_views_form') { + // Filter only selected checkboxes. + $selected = array_filter($form_state['values'][$this->options['id']]); + $accounts = array(); + foreach (array_intersect_key($this->view->result, $selected) as $result) { + if ($account = $this->get_entity($result)) { + $accounts[$account->id()] = $account; + } + } + // If there are no valid accounts selected, return. + if (empty($accounts)) { + drupal_set_message(t('No updates to perform.')); + return; + } + + $operations = \Drupal::moduleHandler()->invokeAll('user_operations', array($form_state['values']['action'])); + $operation = $operations[$form_state['values']['action']]; + // Filter out unchecked accounts. + if ($function = $operation['callback']) { + // Add in callback arguments if present. + if (isset($operation['callback arguments'])) { + $args = array_merge(array($accounts), $operation['callback arguments']); + } + else { + $args = array($accounts); + } + call_user_func_array($function, $args); + + if (isset($operation['redirect'])) { + $form_state['redirect'] = $operation['redirect']; + } + else { + drupal_set_message(t('The update has been performed.')); + } + } + } + } + +} diff --git a/core/modules/user/lib/Drupal/user/Tests/UserAdminTest.php b/core/modules/user/lib/Drupal/user/Tests/UserAdminTest.php index 3b83b52..ebd627d 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[DRUPAL_AUTHENTICATED_RID]); - $edit['role'] = key($roles); - $this->drupalPost('admin/people', $edit, t('Refine')); + $this->drupalGet('admin/people', array('query' => array('role' => key($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'] = 'block'; - $edit['accounts[' . $account->uid . ']'] = TRUE; - $this->drupalPost('admin/people', $edit, t('Update')); + $edit['action'] = 'block'; + $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'] = 'unblock'; - $editunblock['accounts[' . $account->uid . ']'] = TRUE; - $this->drupalPost('admin/people', $editunblock, t('Update')); + $editunblock['action'] = 'unblock'; + $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 70623bd..d08172d 100644 --- a/core/modules/user/lib/Drupal/user/Tests/UserCancelTest.php +++ b/core/modules/user/lib/Drupal/user/Tests/UserCancelTest.php @@ -66,6 +66,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( @@ -87,10 +88,10 @@ function testUserCancelUid1() { $this->admin_user = $this->drupalCreateUser(array('administer users')); $this->drupalLogin($this->admin_user); $edit = array( - 'operation' => 'cancel', - 'accounts[1]' => TRUE, + 'action' => 'cancel', + '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); @@ -390,6 +391,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(); @@ -407,14 +409,11 @@ function testMassUserCancelByAdmin() { // Cancel user accounts, including own one. $edit = array(); - $edit['operation'] = 'cancel'; - foreach ($users as $uid => $account) { - $edit['accounts[' . $uid . ']'] = TRUE; + $edit['action'] = 'cancel'; + 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/UserCreateTest.php b/core/modules/user/lib/Drupal/user/Tests/UserCreateTest.php index 565e40e..c1c80a6 100644 --- a/core/modules/user/lib/Drupal/user/Tests/UserCreateTest.php +++ b/core/modules/user/lib/Drupal/user/Tests/UserCreateTest.php @@ -14,6 +14,11 @@ */ class UserCreateTest extends WebTestBase { + /** + * Modules to enable. + */ + public static $modules = array('views'); + public static function getInfo() { return array( 'name' => 'User create', diff --git a/core/modules/user/lib/Drupal/user/Tests/UserTranslationUITest.php b/core/modules/user/lib/Drupal/user/Tests/UserTranslationUITest.php index 171a976..d12c898 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..51f9b4a --- /dev/null +++ b/core/modules/user/lib/Drupal/user/Tests/Views/BulkFormTest.php @@ -0,0 +1,106 @@ + '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')); + $this->assertText(t('No updates to perform.')); + } + +} 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..591e807 --- /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 +human_name: '' +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 c3931d8..82d3195 100644 --- a/core/modules/user/user.admin.inc +++ b/core/modules/user/user.admin.inc @@ -6,283 +6,11 @@ */ /** - * 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() + * Page callback: Generates user create administration form. */ -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 ? '
' . t('and where') . '
' : ''), - ); - $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() - */ -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)), - 'roles' => array('data' => t('Roles'), 'class' => array(RESPONSIVE_PRIORITY_LOW)), - 'member_for' => array('data' => t('Member for'), 'field' => 'u.created', 'sort' => 'desc', 'class' => array(RESPONSIVE_PRIORITY_LOW)), - 'access' => array('data' => t('Last access'), 'field' => 'u.access', 'class' => array(RESPONSIVE_PRIORITY_LOW)), - 'operations' => t('Operations'), - ); - - $query = db_select('users', 'u'); - $query->condition('u.uid', 0, '<>'); - user_build_filter_query($query); - - $count_query = clone $query; - $count_query->addExpression('COUNT(u.uid)'); - - $query = $query - ->extend('Drupal\Core\Database\Query\PagerSelectExtender') - ->extend('Drupal\Core\Database\Query\TableSortExtender'); - $query - ->fields('u', array('uid', 'name', 'status', 'created', 'access')) - ->limit(50) - ->orderByHeader($header) - ->setCountQuery($count_query); - $result = $query->execute(); - - $form['options'] = array( - '#type' => 'details', - '#title' => t('Update options'), - '#attributes' => array('class' => array('container-inline')), - ); - $options = array(); - foreach (module_invoke_all('user_operations') as $operation => $array) { - $options[$operation] = $array['label']; - } - $form['options']['operation'] = array( - '#type' => 'select', - '#title' => t('Operation'), - '#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)); - $accounts = array(); - foreach ($result as $account) { - $account = user_load($account->uid); - $users_roles = array(); - $roles_result = db_query('SELECT rid FROM {users_roles} WHERE uid = :uid', array(':uid' => $account->uid)); - foreach ($roles_result as $user_role) { - $users_roles[] = $roles[$user_role->rid]; - } - asort($users_roles); - - $options[$account->uid] = array( - 'username' => theme('username', array('account' => $account)), - 'status' => $status[$account->status], - 'roles' => theme('item_list', array('items' => $users_roles)), - 'member_for' => format_interval(REQUEST_TIME - $account->created), - 'access' => $account->access ? t('@time ago', array('@time' => format_interval(REQUEST_TIME - $account->access))) : t('never'), - ); - $links = array(); - $links['edit'] = array( - 'title' => t('Edit'), - 'href' => 'user/' . $account->uid . '/edit', - 'query' => $destination, - ); - if (module_invoke('translation_entity', 'translate_access', $account)) { - $links['translate'] = array( - 'title' => t('Translate'), - 'href' => 'user/' . $account->uid . '/translations', - 'query' => $destination, - ); - } - $options[$account->uid]['operations']['data'] = array( - '#type' => 'operations', - '#links' => $links, - ); - } - - $form['accounts'] = array( - '#type' => 'tableselect', - '#header' => $header, - '#options' => $options, - '#empty' => t('No people available.'), - ); - $form['pager'] = array('#markup' => theme('pager')); - - return $form; -} - -/** - * Submit the user administration update form. - */ -function user_admin_account_submit($form, &$form_state) { - $operations = module_invoke_all('user_operations', $form, $form_state); - $operation = $operations[$form_state['values']['operation']]; - // Filter out unchecked accounts. - $accounts = array_filter($form_state['values']['accounts']); - if ($function = $operation['callback']) { - // Add in callback arguments if present. - if (isset($operation['callback arguments'])) { - $args = array_merge(array($accounts), $operation['callback arguments']); - } - else { - $args = array($accounts); - } - call_user_func_array($function, $args); - - 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 bdf5862..016dd2d 100644 --- a/core/modules/user/user.module +++ b/core/modules/user/user.module @@ -929,25 +929,16 @@ function user_menu() { 'file' => 'user.pages.inc', ); - // User listing pages. $items['admin/people'] = array( 'title' => 'People', 'description' => 'Manage user accounts, roles, and permissions.', - 'page callback' => 'user_admin', - 'page arguments' => array('list'), + 'page callback' => 'system_admin_menu_block_page', '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', + 'file' => 'system.admin.inc', + 'file path' => drupal_get_path('module', 'system'), ); - // Permissions and role forms. $items['admin/people/permissions'] = array( 'title' => 'Permissions', @@ -983,10 +974,18 @@ 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( + 'title' => 'Add user', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('user_multiple_cancel_confirm'), + 'access arguments' => array('administer users'), + 'file' => 'user.admin.inc', + ); // Administration pages. $items['admin/config/people'] = array( @@ -1983,7 +1982,7 @@ function user_role_revoke_permissions($rid, array $permissions = array()) { /** * Implements hook_user_operations(). */ -function user_user_operations($form = array(), $form_state = array()) { +function user_user_operations($selected_operation = FALSE) { $operations = array( 'unblock' => array( 'label' => t('Unblock the selected users'), @@ -1995,6 +1994,8 @@ function user_user_operations($form = array(), $form_state = array()) { ), 'cancel' => array( 'label' => t('Cancel the selected user accounts'), + 'callback' => 'user_user_operations_cancel', + 'redirect' => 'admin/people/cancel', ), ); @@ -2028,13 +2029,13 @@ function user_user_operations($form = array(), $form_state = array()) { // If the form has been posted, we need to insert the proper data for // role editing if necessary. - if (!empty($form_state['submitted'])) { - $operation_rid = explode('-', $form_state['values']['operation']); + if ($selected_operation) { + $operation_rid = explode('-', $selected_operation); $operation = $operation_rid[0]; if ($operation == 'add_role' || $operation == 'remove_role') { $rid = $operation_rid[1]; if (user_access('administer permissions')) { - $operations[$form_state['values']['operation']] = array( + $operations[$selected_operation] = array( 'callback' => 'user_multiple_role_edit', 'callback arguments' => array($operation, $rid), ); @@ -2053,7 +2054,6 @@ function user_user_operations($form = array(), $form_state = array()) { * Callback function for admin mass unblocking users. */ function user_user_operations_unblock($accounts) { - $accounts = user_load_multiple($accounts); foreach ($accounts as $account) { // Skip unblocking user if they are already unblocked. if ($account !== FALSE && $account->status == 0) { @@ -2067,7 +2067,6 @@ function user_user_operations_unblock($accounts) { * Callback function for admin mass blocking users. */ function user_user_operations_block($accounts) { - $accounts = user_load_multiple($accounts); foreach ($accounts as $account) { // Skip blocking user if they are already blocked. if ($account !== FALSE && $account->status == 1) { @@ -2081,18 +2080,24 @@ function user_user_operations_block($accounts) { } /** + * Callback function for admin mass canceling users. + */ +function user_user_operations_cancel($accounts) { + global $user; + // Store the accounts to be canceled in a tempstore. + drupal_container()->get('user.tempstore')->get('user_user_operations_cancel')->set($user->uid, $accounts); +} + +/** * Callback function for admin mass adding/deleting a user role. */ function user_multiple_role_edit($accounts, $operation, $rid) { - $role_name = entity_load('user_role', $rid)->label(); - switch ($operation) { case 'add_role': - $accounts = user_load_multiple($accounts); foreach ($accounts as $account) { // Skip adding the role to the user if they already have it. if ($account !== FALSE && !isset($account->roles[$rid])) { - $roles = $account->roles + array($rid => $role_name); + $roles = $account->roles + array($rid => $rid); // For efficiency manually save the original account before applying // any changes. $account->original = clone $account; @@ -2102,11 +2107,10 @@ function user_multiple_role_edit($accounts, $operation, $rid) { } break; case 'remove_role': - $accounts = user_load_multiple($accounts); foreach ($accounts as $account) { // 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 => $role_name)); + $roles = array_diff($account->roles, array($rid => $rid)); // For efficiency manually save the original account before applying // any changes. $account->original = clone $account; @@ -2119,11 +2123,13 @@ function user_multiple_role_edit($accounts, $operation, $rid) { } function user_multiple_cancel_confirm($form, &$form_state) { - $edit = $form_state['input']; + global $user; + // Retrieve the accounts to be canceled from the temp store. + $accounts = drupal_container()->get('user.tempstore')->get('user_user_operations_cancel')->get($user->uid); $form['accounts'] = array('#prefix' => '', '#tree' => TRUE); - $accounts = user_load_multiple(array_keys(array_filter($edit['accounts']))); - foreach ($accounts as $uid => $account) { + foreach ($accounts as $account) { + $uid = $account->id(); // Prevent user 1 from being canceled. if ($uid <= 1) { continue; @@ -2186,6 +2192,8 @@ function user_multiple_cancel_confirm($form, &$form_state) { */ function user_multiple_cancel_confirm_submit($form, &$form_state) { global $user; + // Clear out the accounts from the temp store. + drupal_container()->get('user.tempstore')->get('user_user_operations_cancel')->delete($user->uid); if ($form_state['values']['confirm']) { foreach ($form_state['values']['accounts'] as $uid => $value) { diff --git a/core/modules/user/user.views.inc b/core/modules/user/user.views.inc index 11871c9..920bea1 100644 --- a/core/modules/user/user.views.inc +++ b/core/modules/user/user.views.inc @@ -316,6 +316,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/views.module b/core/modules/views/views.module index 99e000c..9c964e7 100644 --- a/core/modules/views/views.module +++ b/core/modules/views/views.module @@ -926,7 +926,6 @@ function views_get_handler($table, $field, $type, $override = NULL) { } // Finally, use the 'broken' handler. - debug(t("Missing handler: @table @field @type", array('@table' => $table, '@field' => $field, '@type' => $type))); return $manager->createInstance('broken'); }