Commit e5774f5b authored by alexpott's avatar alexpott

Issue #1851086 by tim.plunkett, damiankloip, dawehner, alimac: Replace admin/people with a View.

parent 4b815b31
......@@ -31,15 +31,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}
*/
......
......@@ -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 clickSortable() {
return FALSE;
}
}
......@@ -35,7 +35,7 @@ public function testActionUpgrade() {
$this->assertTrue($this->performUpgrade(), 'The upgrade was completed successfully.');
$this->drupalGet('admin/people');
$elements = $this->xpath('//select[@name="operation"]/option');
$elements = $this->xpath('//select[@name="action"]/option');
$this->assertTrue(!empty($elements), 'The user actions were upgraded.');
}
......
......@@ -1917,14 +1917,19 @@ function system_update_8045() {
}
/**
* Enable Views if the node listing is set as the frontpage.
* Install new default views.
*/
function system_update_8046() {
$front_page = config('system.site')->get('page.front');
if (!isset($front_page) || $front_page == 'node') {
$config_to_import = array(
'views.view.user_admin_people' => 'user',
'views.view.content' => 'node',
);
$front_page = config('system.site')->get('page.front') ?: 'node';
if ($front_page == 'node') {
// This imports the node frontpage view.
$module = 'node';
$config_name = 'views.view.frontpage';
$config_to_import['views.view.frontpage'] = 'node';
}
foreach ($config_to_import as $config_name => $module) {
$module_config_path = drupal_get_path('module', $module) . '/config';
$module_filestorage = new FileStorage($module_config_path);
$config_storage = drupal_container()->get('config.storage');
......
This diff is collapsed.
......@@ -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() {
......
......@@ -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');
......
<?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.'));
}
}
}
<?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()->getNGEntity();
$accounts[$account->label()] = $account;
}
// Create a blocked user.
$account = $this->drupalCreateUser()->getNGEntity();
$account->status = 0;
$account->save();
$accounts[$account->label()] = $account;
// Create a user at a certain timestamp.
$account = $this->drupalCreateUser()->getNGEntity();
$account->created = 1363219200;
$account->save();
$accounts[$account->label()] = $account;
$timestamp_user = $account->label();
$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()->getNGEntity();
$account->addRole($rid_1);
$account->addRole($rid_2);
$account->save();
$accounts[$account->label()] = $account;
$role_account_name = $account->label();
// Create an admin user and look at the listing.
$admin_user = $this->drupalCreateUser(array('administer users'))->getNGEntity();
$accounts[$admin_user->label()] = $admin_user;
$accounts['admin'] = entity_load('user', 1);
$this->drupalLogin($admin_user->getBCEntity());
$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->value, '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->value), 'Ensure the right member time is displayed.');
}
}
......@@ -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");
......
......@@ -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.');
......
......@@ -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(
......
<?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')));
// Test submitting the page with no selection.
$edit = array(
'action' => 'user_block_user_action',
);
$this->drupalPost('test-user-bulk-form', $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 = entity_load('user', $this->users[0]->id());
$roles = user_role_names(TRUE);
unset($roles[DRUPAL_AUTHENTICATED_RID]);
$role = key($roles);
$this->assertFalse($account->hasRole($role), 'The user currently does not have a custom role.');
$edit = array(
'user_bulk_form[1]' => TRUE,
'action' => 'user_add_role_action.' . $role,
);
$this->drupalPost(NULL, $edit, t('Apply'));
// Re-load the user and check their roles.
$account = entity_load('user', $account->id(), TRUE);
$this->assertTrue($account->hasRole($role), 'The user now has the custom role.');
$edit = array(
'user_bulk_form[1]' => TRUE,
'action' => 'user_remove_role_action.' . $role,
);
$this->drupalPost(NULL, $edit, t('Apply'));
// Re-load the user and check their roles.
$account = entity_load('user', $account->id(), TRUE);
$this->assertFalse($account->hasRole($role), 'The user no longer has the custom role.');
// Block a user using the bulk form.
$this->assertTrue($account->status->value, 'The user is not blocked.');
$this->assertRaw($account->label(), 'The user is found in the table.');
$edit = array(
'user_bulk_form[1]' => TRUE,
'action' => 'user_block_user_action',
);
$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->value, 'The user is blocked.');
$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' => 'user_block_user_action',
);
$this->drupalPost(NULL, $edit, t('Apply'));
$anonymous_account = user_load(0);
$this->assertFalse($anonymous_account->status, 0, 'Ensure the anonymous user got blocked.');
}
}
base_field: uid
base_table: users
core: 8.x
description: ''
status: '1'
display:
default:
display_plugin: default
id: default
display_title: Master
position: ''