diff --git a/core/includes/update.inc b/core/includes/update.inc index 7ccda5a..3659caf 100644 --- a/core/includes/update.inc +++ b/core/includes/update.inc @@ -1542,3 +1542,34 @@ function update_add_cache_columns($table) { )); } } + +/** + * Replace permissions during update. + * + * This function can replace one permission to several or even delete an old + * one. + * + * @param array $replace + * An associative array. The keys are the old permissions the values are lists + * of new permissions. If the list is an empty array, the old permission is + * removed. + */ +function update_replace_permissions($replace) { + $prefix = 'user.role.'; + $cut = strlen($prefix); + $role_names = Drupal::service('config.storage')->listAll($prefix); + foreach ($role_names as $role_name) { + $rid = substr($role_name, $cut); + $config = Drupal::config("user.role.$rid"); + $permissions = $config->get('permissions') ?: array(); + foreach ($replace as $old_permission => $new_permissions) { + if (($index = array_search($old_permission, $permissions)) !== FALSE) { + unset($permissions[$index]); + $permissions = array_unique(array_merge($permissions, $new_permissions)); + } + } + $config + ->set('permissions', $permissions) + ->save(); + } +} diff --git a/core/modules/block/lib/Drupal/block/Tests/BlockInterfaceTest.php b/core/modules/block/lib/Drupal/block/Tests/BlockInterfaceTest.php index e71a146..cd070df 100644 --- a/core/modules/block/lib/Drupal/block/Tests/BlockInterfaceTest.php +++ b/core/modules/block/lib/Drupal/block/Tests/BlockInterfaceTest.php @@ -23,11 +23,6 @@ public static function getInfo() { ); } - protected function setUp() { - parent::setUp(); - $this->installSchema('user', 'role_permission'); - } - /** * Test configuration and subsequent form() and build() method calls. * diff --git a/core/modules/comment/comment.module b/core/modules/comment/comment.module index b0f71f1..a8c7896 100644 --- a/core/modules/comment/comment.module +++ b/core/modules/comment/comment.module @@ -1223,20 +1223,24 @@ function comment_node_update_index(EntityInterface $node, $langcode) { $index_comments = &drupal_static(__FUNCTION__); if ($index_comments === NULL) { - // Find and save roles that can 'access comments' or 'search content'. - $perms = array('access comments' => array(), 'search content' => array()); - $result = db_query("SELECT rid, permission FROM {role_permission} WHERE permission IN ('access comments', 'search content')"); - foreach ($result as $record) { - $perms[$record->permission][$record->rid] = $record->rid; - } - - // Prevent indexing of comments if there are any roles that can search but - // not view comments. + // Do not index in the following three cases: + // 1. 'Authenticated user' can search content but can't access comments. + // 2. 'Anonymous user' can search content but can't access comments. + // 3. Any role can search content but can't access comments and access + // comments is not granted by the 'authenticated user' role. In this case + // all users might have both permissions from various roles but it is also + // possible to set up a user to have only search content and so a user + // edit could change the security situation so it is not safe to index the + // comments. $index_comments = TRUE; - foreach ($perms['search content'] as $rid) { - if (!isset($perms['access comments'][$rid]) && (($rid == DRUPAL_AUTHENTICATED_RID || $rid == DRUPAL_ANONYMOUS_RID) || !isset($perms['access comments'][DRUPAL_AUTHENTICATED_RID]))) { - $index_comments = FALSE; - break; + $roles = Drupal::entityManager()->getStorageController('user_role')->load(); + $authenticated_can_access = $roles[DRUPAL_AUTHENTICATED_RID]->hasPermission('access comments'); + foreach ($roles as $rid => $role) { + if ($role->hasPermission('search content') && !$role->hasPermission('access comments')) { + if ($rid == DRUPAL_AUTHENTICATED_RID || $rid == DRUPAL_ANONYMOUS_RID || !$authenticated_can_access) { + $index_comments = FALSE; + break; + } } } } diff --git a/core/modules/field_ui/field_ui.install b/core/modules/field_ui/field_ui.install index c434bb3..7bc401f 100644 --- a/core/modules/field_ui/field_ui.install +++ b/core/modules/field_ui/field_ui.install @@ -9,39 +9,29 @@ * Upgrade Field UI permissions. */ function field_ui_update_8001() { - - $permissions = array( + $replace = array( 'administer comments' => array( + 'administer comments', 'administer comment fields', 'administer comment display', ), 'administer content types' => array( + 'administer content types', 'administer node fields', 'administer node display', ), 'administer users' => array( + 'administer users', 'administer user fields', 'administer user display', ), 'administer taxonomy' => array( + 'administer taxonomy', 'administer taxonomy_term fields', 'administer taxonomy_term display', ), ); - - // We can not call user_permission_get_modules() as that will start - // invoking hooks which we can't during update hooks. Directly query - // for the permissions and insert them into the database. - foreach ($permissions as $old_permission => $new_permissions) { - $results = db_query("SELECT rid FROM {role_permission} WHERE permission = :permission", array(':permission' => $old_permission)); - foreach ($results as $record) { - $query = db_insert('role_permission')->fields(array('rid', 'permission', 'module')); - foreach ($new_permissions as $new_permission) { - $query->values(array($record->rid, $new_permission, 'field_ui')); - } - $query->execute(); - } - } + update_replace_permissions($replace); } /** diff --git a/core/modules/filter/lib/Drupal/filter/Tests/FilterDefaultConfigTest.php b/core/modules/filter/lib/Drupal/filter/Tests/FilterDefaultConfigTest.php index ac078e8..4490b5a 100644 --- a/core/modules/filter/lib/Drupal/filter/Tests/FilterDefaultConfigTest.php +++ b/core/modules/filter/lib/Drupal/filter/Tests/FilterDefaultConfigTest.php @@ -30,7 +30,7 @@ function setUp() { // filter_permission() calls into url() to output a link in the description. $this->installSchema('system', 'url_alias'); - $this->installSchema('user', array('users_roles', 'role_permission')); + $this->installSchema('user', array('users_roles')); // Install filter_test module, which ships with custom default format. $this->installConfig(array('user', 'filter_test')); diff --git a/core/modules/node/lib/Drupal/node/Tests/NodeAccessTest.php b/core/modules/node/lib/Drupal/node/Tests/NodeAccessTest.php index 73b3a44..4a5e0f8 100644 --- a/core/modules/node/lib/Drupal/node/Tests/NodeAccessTest.php +++ b/core/modules/node/lib/Drupal/node/Tests/NodeAccessTest.php @@ -26,9 +26,7 @@ public static function getInfo() { function setUp() { parent::setUp(); // Clear permissions for authenticated users. - db_delete('role_permission') - ->condition('rid', DRUPAL_AUTHENTICATED_RID) - ->execute(); + $this->container->get('config.factory')->get('user.role.' . DRUPAL_AUTHENTICATED_RID)->set('permissions', array())->save(); } /** diff --git a/core/modules/node/node.install b/core/modules/node/node.install index a0a71ff..0c545b3 100644 --- a/core/modules/node/node.install +++ b/core/modules/node/node.install @@ -571,6 +571,15 @@ function _update_7000_node_get_types() { */ /** + * Implements hook_update_dependency(). + */ +function node_update_dependency() { + $dependencies['node'][8013] = array( + 'user' => 8002, + ); +} + +/** * Rename node type language variable names. * * @see http://drupal.org/node/540294 @@ -796,15 +805,11 @@ function node_update_8012() { * Renames global revision permissions to use the word 'all'. */ function node_update_8013() { - $actions = array('view', 'delete', 'revert'); - - foreach ($actions as $action) { - db_update('role_permission') - ->fields(array('permission' => $action . ' all revisions')) - ->condition('permission', $action . ' revisions') - ->condition('module', 'node') - ->execute(); - } + update_replace_permissions(array( + 'view revisions' => array('view all revisions'), + 'revert revisions' => array('revert all revisions'), + 'delete revisions' => array('delete all revisions'), + )); } /** diff --git a/core/modules/node/node.views_execution.inc b/core/modules/node/node.views_execution.inc index ead8702..e43690b 100644 --- a/core/modules/node/node.views_execution.inc +++ b/core/modules/node/node.views_execution.inc @@ -30,17 +30,11 @@ function node_views_analyze(ViewExecutable $view) { // check for no access control $access = $display->getOption('access'); if (empty($access['type']) || $access['type'] == 'none') { - $result = db_select('role_permission', 'p') - ->fields('p', array('rid', 'permission')) - ->condition('p.rid', array(DRUPAL_ANONYMOUS_RID, DRUPAL_AUTHENTICATED_RID), 'IN') - ->condition('p.permission', 'access content') - ->execute(); - - foreach ($result as $role) { - $role->safe = TRUE; - $roles[$role->rid] = $role; - } - if (!($roles[DRUPAL_ANONYMOUS_RID]->safe && $roles[DRUPAL_AUTHENTICATED_RID]->safe)) { + $anonymous_role = entity_load('user_role', DRUPAL_ANONYMOUS_RID); + $anonymous_has_access = $anonymous_role && $anonymous_role->hasPermission('access content'); + $authenticated_role = entity_load('user_role', DRUPAL_AUTHENTICATED_RID); + $authenticated_has_access = $authenticated_role && $authenticated_role->hasPermission('access content'); + if (!$anonymous_has_access || !$authenticated_has_access) { $ret[] = Analyzer::formatMessage(t('Some roles lack permission to access content, but display %display has no access control.', array('%display' => $display->display['display_title'])), 'warning'); } $filters = $display->getOption('filters'); diff --git a/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php b/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php index 8c46872..ffc9850 100644 --- a/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php +++ b/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php @@ -560,8 +560,7 @@ protected function drupalCreateRole(array $permissions, $rid = NULL, $name = NUL // Grant the specified permissions to the role, if any. if (!empty($permissions)) { user_role_grant_permissions($role->id(), $permissions); - - $assigned_permissions = db_query('SELECT permission FROM {role_permission} WHERE rid = :rid', array(':rid' => $role->id()))->fetchCol(); + $assigned_permissions = entity_load('user_role', $role->id())->permissions; $missing_permissions = array_diff($permissions, $assigned_permissions); if (!$missing_permissions) { $this->pass(t('Created permissions: @perms', array('@perms' => implode(', ', $permissions))), t('Role')); diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityAccessTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityAccessTest.php index 9733be6..d778211 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityAccessTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityAccessTest.php @@ -29,7 +29,7 @@ public static function getInfo() { function setUp() { parent::setUp(); - $this->installSchema('user', array('role_permission', 'users_roles')); + $this->installSchema('user', array('users_roles')); $this->installSchema('system', array('variable', 'url_alias')); $this->installSchema('language', 'language'); diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityBCDecoratorTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityBCDecoratorTest.php index 74c37be..0ba07bc 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityBCDecoratorTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityBCDecoratorTest.php @@ -31,7 +31,7 @@ public static function getInfo() { public function setUp() { parent::setUp(); - $this->installSchema('user', array('users_roles', 'users_data', 'role_permission')); + $this->installSchema('user', array('users_roles', 'users_data')); $this->installSchema('node', array('node', 'node_field_data', 'node_field_revision', 'node_type', 'node_access')); $this->installSchema('comment', array('comment', 'node_comment_statistics')); } diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityUnitTestBase.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityUnitTestBase.php index 5e73dbc..664b09c 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityUnitTestBase.php +++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityUnitTestBase.php @@ -36,8 +36,7 @@ public function setUp() { * (optional) The values used to create the entity. * @param array $permissions * (optional) Array of permission names to assign to user. The - * role_permission and users_roles tables must be installed before this can - * be used. + * users_roles tables must be installed before this can be used. * * @return \Drupal\user\Plugin\Core\Entity\User * The created user entity. diff --git a/core/modules/system/lib/Drupal/system/Tests/Module/UninstallTest.php b/core/modules/system/lib/Drupal/system/Tests/Module/UninstallTest.php index 3345d2f..78cecbf 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Module/UninstallTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Module/UninstallTest.php @@ -38,8 +38,7 @@ function testUserPermsUninstalled() { module_disable(array('module_test')); module_uninstall(array('module_test')); - // Are the perms defined by module_test removed from {role_permission}. - $count = db_query("SELECT COUNT(rid) FROM {role_permission} WHERE permission = :perm", array(':perm' => 'module_test perm'))->fetchField(); - $this->assertEqual(0, $count, 'Permissions were all removed.'); + // Are the perms defined by module_test removed? + $this->assertFalse(user_roles(FALSE, 'module_test perm'), 'Permissions were all removed.'); } } diff --git a/core/modules/system/lib/Drupal/system/Tests/Upgrade/FieldUIUpgradePathTest.php b/core/modules/system/lib/Drupal/system/Tests/Upgrade/FieldUIUpgradePathTest.php index 6f44942..4606d3b 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Upgrade/FieldUIUpgradePathTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Upgrade/FieldUIUpgradePathTest.php @@ -65,11 +65,11 @@ function testFieldUIPermissions() { $role_permissions = user_role_permissions(array($this->normal_role_id, $this->admin_role_id)); foreach ($permissions as $old_permission => $new_permissions) { - $this->assertFalse(isset($role_permissions[$this->normal_role_id][$old_permission]), format_string('%role_name does not have the old %permission permission', array('%role_name' => $this->normal_role_name, '%permission' => $old_permission))); - $this->assertTrue(isset($role_permissions[$this->admin_role_id][$old_permission]), format_string('%role_name still has the old %permission permission', array('%role_name' => $this->admin_role_name, '%permission' => $old_permission))); + $this->assertFalse(in_array($old_permission, $role_permissions[$this->normal_role_id]), format_string('%role_name does not have the old %permission permission', array('%role_name' => $this->normal_role_name, '%permission' => $old_permission))); + $this->assertTrue(in_array($old_permission, $role_permissions[$this->admin_role_id]), format_string('%role_name still has the old %permission permission', array('%role_name' => $this->admin_role_name, '%permission' => $old_permission))); foreach ($new_permissions as $new_permission) { - $this->assertFalse(isset($role_permissions[$this->normal_role_id][$new_permission]), format_string('%role_name does not have the new %permission permission', array('%role_name' => $this->normal_role_name, '%permission' => $new_permission))); - $this->assertTrue(isset($role_permissions[$this->admin_role_id][$new_permission]), format_string('%role_name has the new %permission permission', array('%role_name' => $this->admin_role_name, '%permission' => $new_permission))); + $this->assertFalse(in_array($new_permission, $role_permissions[$this->normal_role_id]), format_string('%role_name does not have the new %permission permission', array('%role_name' => $this->normal_role_name, '%permission' => $new_permission))); + $this->assertTrue(in_array($new_permission, $role_permissions[$this->admin_role_id]), format_string('%role_name has the new %permission permission', array('%role_name' => $this->admin_role_name, '%permission' => $new_permission))); } } } diff --git a/core/modules/system/lib/Drupal/system/Tests/Upgrade/LanguageUpgradePathTest.php b/core/modules/system/lib/Drupal/system/Tests/Upgrade/LanguageUpgradePathTest.php index ba62946..506ec4c 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Upgrade/LanguageUpgradePathTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Upgrade/LanguageUpgradePathTest.php @@ -191,6 +191,8 @@ public function testLanguageNoPluralsUpgrade() { * Tests upgrading translations permissions. */ public function testLanguagePermissionsUpgrade() { + // Insert a permission into the Drupal 7 database before running the + // upgrade. db_insert('role_permission')->fields(array( 'rid' => 2, 'permission' => 'translate content', @@ -198,12 +200,8 @@ public function testLanguagePermissionsUpgrade() { ))->execute(); $this->assertTrue($this->performUpgrade(), 'The upgrade was completed successfully.'); - - // Check that translate content role doesn't exist on database. - $old_permission_exists = db_query('SELECT * FROM {role_permission} WHERE permission LIKE ?', array('translate content'))->fetchObject(); - $this->assertFalse($old_permission_exists, 'No translate content role left on database.'); + $this->assertFalse(user_roles(FALSE, 'translate content'), 'No translate content role left in config.'); // Check that translate content has been renamed to translate all content. - $new_permission_exists = db_query('SELECT * FROM {role_permission} WHERE permission LIKE ?', array('translate all content'))->fetchObject(); - $this->assertTrue($new_permission_exists, 'Rename role translate content to translate all content was completed successfully.'); + $this->assertTrue(user_roles(FALSE, 'translate all content'), 'Rename role translate content to translate all content was completed successfully.'); } } diff --git a/core/modules/system/system.install b/core/modules/system/system.install index d3b67eb..f10d86b 100644 --- a/core/modules/system/system.install +++ b/core/modules/system/system.install @@ -1520,13 +1520,9 @@ function system_update_8020() { $blocked_ips_exists = db_query_range('SELECT 1 FROM {blocked_ips}', 0, 1)->fetchField(); if ($blocked_ips_exists) { // Rename the permission name. - db_update('role_permission') - ->fields(array( - 'permission' => 'ban IP addresses', - 'module' => 'ban', - )) - ->condition('permission', 'block IP addresses') - ->execute(); + update_replace_permissions(array( + 'block IP addresses' => array('ban IP addresses'), + )); // Rename {blocked_ips} table into {ban_ip}. db_rename_table('blocked_ips', 'ban_ip'); // Remove all references to the removed action callback. diff --git a/core/modules/text/lib/Drupal/text/Tests/TextSummaryTest.php b/core/modules/text/lib/Drupal/text/Tests/TextSummaryTest.php index aec92e0..b287a62 100644 --- a/core/modules/text/lib/Drupal/text/Tests/TextSummaryTest.php +++ b/core/modules/text/lib/Drupal/text/Tests/TextSummaryTest.php @@ -28,7 +28,6 @@ function setUp() { parent::setUp(); $this->installSchema('system', 'url_alias'); - $this->installSchema('user', 'role_permission'); $this->installConfig(array('text')); } diff --git a/core/modules/translation/translation.install b/core/modules/translation/translation.install index 61d014d..ad15ba0 100644 --- a/core/modules/translation/translation.install +++ b/core/modules/translation/translation.install @@ -10,10 +10,9 @@ * Rename the translate content permission. */ function translation_update_8000() { - db_update('role_permission') - ->fields(array('permission' => 'translate all content')) - ->condition('permission', 'translate content') - ->execute(); + update_replace_permissions(array( + 'translate content' => array('translate all content'), + )); } /** diff --git a/core/modules/user/config/schema/user.schema.yml b/core/modules/user/config/schema/user.schema.yml index 989abae..05155ff 100644 --- a/core/modules/user/config/schema/user.schema.yml +++ b/core/modules/user/config/schema/user.schema.yml @@ -131,6 +131,12 @@ user.role.*: weight: type: integer label: 'User role weight' + permissions: + type: sequence + label: 'Permissions' + sequence: + - type: string + label: 'Permission' langcode: type: string label: 'Default language' diff --git a/core/modules/user/config/views.view.user_admin_people.yml b/core/modules/user/config/views.view.user_admin_people.yml index efaca3c..90c17e1 100644 --- a/core/modules/user/config/views.view.user_admin_people.yml +++ b/core/modules/user/config/views.view.user_admin_people.yml @@ -742,7 +742,7 @@ display: plugin_id: user_roles permission: id: permission - table: role_permission + table: users_roles field: permission relationship: none group_type: group diff --git a/core/modules/user/lib/Drupal/user/Plugin/Core/Entity/Role.php b/core/modules/user/lib/Drupal/user/Plugin/Core/Entity/Role.php index 8d1bf0a..d35d7f1 100644 --- a/core/modules/user/lib/Drupal/user/Plugin/Core/Entity/Role.php +++ b/core/modules/user/lib/Drupal/user/Plugin/Core/Entity/Role.php @@ -68,6 +68,13 @@ class Role extends ConfigEntityBase implements RoleInterface { public $weight; /** + * The permissions belonging to this role. + * + * @var array + */ + public $permissions = array(); + + /** * {@inheritdoc} */ public function uri() { @@ -83,6 +90,38 @@ public function uri() { /** * {@inheritdoc} */ + public function getPermissions() { + return $this->permissions; + } + + /** + * {@inheritdoc} + */ + public function hasPermission($permission) { + return in_array($permission, $this->permissions); + } + + /** + * {@inheritdoc} + */ + public function grantPermission($permission) { + if (!$this->hasPermission($permission)) { + $this->permissions[] = $permission; + } + return $this; + } + + /** + * {@inheritdoc} + */ + public function revokePermission($permission) { + $this->permissions = array_diff($this->permissions, array($permission)); + return $this; + } + + /** + * {@inheritdoc} + */ public function preSave(EntityStorageControllerInterface $storage_controller) { if (!isset($this->weight) && ($roles = $storage_controller->load())) { // Set a role weight to make this new role last. diff --git a/core/modules/user/lib/Drupal/user/Plugin/views/field/Permissions.php b/core/modules/user/lib/Drupal/user/Plugin/views/field/Permissions.php index 34bedd8..6bee40c 100644 --- a/core/modules/user/lib/Drupal/user/Plugin/views/field/Permissions.php +++ b/core/modules/user/lib/Drupal/user/Plugin/views/field/Permissions.php @@ -8,7 +8,8 @@ namespace Drupal\user\Plugin\views\field; use Drupal\Component\Annotation\PluginID; -use Drupal\Core\Database\Connection; +use Drupal\Core\Entity\EntityManager; +use Drupal\Core\Extension\ModuleHandlerInterface; use Drupal\views\Plugin\views\display\DisplayPluginBase; use Drupal\views\ViewExecutable; use Drupal\views\Plugin\views\field\PrerenderList; @@ -24,11 +25,18 @@ class Permissions extends PrerenderList { /** - * Database Service Object. + * The role storage controller. * - * @var \Drupal\Core\Database\Connection + * @var \Drupal\user\RoleStorageControllerInterface */ - protected $database; + protected $roleStorageController; + + /** + * The module handler. + * + * @var \Drupal\Core\Extension\ModuleHandlerInterface + */ + protected $moduleHandler; /** * Constructs a Drupal\Component\Plugin\PluginBase object. @@ -39,20 +47,23 @@ class Permissions extends PrerenderList { * The plugin_id for the plugin instance. * @param array $plugin_definition * The plugin implementation definition. - * @param \Drupal\Core\Database\Connection $database - * Database Service Object. + * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler + * The module handler. + * @param \Drupal\Core\Entity\EntityManager $entity_manager + * The entity manager */ - public function __construct(array $configuration, $plugin_id, array $plugin_definition, Connection $database) { + public function __construct(array $configuration, $plugin_id, array $plugin_definition, ModuleHandlerInterface $module_handler, EntityManager $entity_manager) { parent::__construct($configuration, $plugin_id, $plugin_definition); - $this->database = $database; + $this->roleStorageController = $entity_manager->getStorageController('user_role'); + $this->moduleHandler = $module_handler; } /** * {@inheritdoc} */ public static function create(ContainerInterface $container, array $configuration, $plugin_id, array $plugin_definition) { - return new static($configuration, $plugin_id, $plugin_definition, $container->get('database')); + return new static($configuration, $plugin_id, $plugin_definition, $container->get('module_handler'), $container->get('plugin.manager.entity')); } /** @@ -73,27 +84,32 @@ public function preRender(&$values) { $uids = array(); $this->items = array(); + $permission_names = \Drupal::moduleHandler()->invokeAll('permission'); + + $rids = array(); foreach ($values as $result) { - $uids[] = $this->getValue($result); - } + $user_rids = $this->getEntity($result)->getRoles(); + $uid = $this->getValue($result); - if ($uids) { - // Get a list of all the modules implementing a hook_permission() and sort by - // display name. - $module_info = system_get_info('module'); - $modules = array(); - foreach (module_implements('permission') as $module) { - $modules[$module] = $module_info[$module]['name']; + foreach ($user_rids as $rid) { + $rids[$rid][] = $uid; } - asort($modules); - - $permissions = module_invoke_all('permission'); + } - $result = $this->database->query('SELECT u.uid, u.rid, rp.permission FROM {role_permission} rp INNER JOIN {users_roles} u ON u.rid = rp.rid WHERE u.uid IN (:uids) AND rp.module IN (:modules) ORDER BY rp.permission', - array(':uids' => $uids, ':modules' => array_keys($modules))); + if ($rids) { + $roles = $this->roleStorageController->load(array_keys($rids)); + foreach ($rids as $rid => $role_uids) { + foreach ($roles[$rid]->getPermissions() as $permission) { + foreach ($role_uids as $uid) { + $this->items[$uid][$permission]['permission'] = $permission_names[$permission]['title']; + } + } + } - foreach ($result as $perm) { - $this->items[$perm->uid][$perm->permission]['permission'] = $permissions[$perm->permission]['title']; + foreach ($uids as $uid) { + if (isset($this->items[$uid])) { + ksort($this->items[$uid]); + } } } } diff --git a/core/modules/user/lib/Drupal/user/Plugin/views/filter/Permissions.php b/core/modules/user/lib/Drupal/user/Plugin/views/filter/Permissions.php index 4d1aa28..6d763fa 100644 --- a/core/modules/user/lib/Drupal/user/Plugin/views/filter/Permissions.php +++ b/core/modules/user/lib/Drupal/user/Plugin/views/filter/Permissions.php @@ -8,7 +8,10 @@ namespace Drupal\user\Plugin\views\filter; use Drupal\Component\Annotation\PluginID; +use Drupal\Component\Utility\String; +use Drupal\Core\Extension\ModuleHandlerInterface; use Drupal\views\Plugin\views\filter\ManyToOne; +use Symfony\Component\DependencyInjection\ContainerInterface; /** * Filter handler for user roles. @@ -19,25 +22,83 @@ */ class Permissions extends ManyToOne { + /** + * The module handler. + * + * @var \Drupal\Core\Extension\ModuleHandlerInterface + */ + protected $moduleHandler; + + /** + * Constructs a Permissions object. + * + * @param array $configuration + * A configuration array containing information about the plugin instance. + * @param string $plugin_id + * The plugin_id for the plugin instance. + * @param array $plugin_definition + * The plugin implementation definition. + * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler + * The module handler. + */ + public function __construct(array $configuration, $plugin_id, array $plugin_definition, ModuleHandlerInterface $module_handler) { + parent::__construct($configuration, $plugin_id, $plugin_definition); + + $this->moduleHandler = $module_handler; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container, array $configuration, $plugin_id, array $plugin_definition) { + return new static($configuration, $plugin_id, $plugin_definition, $container->get('module_handler')); + } + public function getValueOptions() { - $module_info = system_get_info('module'); + if (!isset($this->value_options)) { + $module_info = system_get_info('module'); - // Get a list of all the modules implementing a hook_permission() and sort by - // display name. - $modules = array(); - foreach (module_implements('permission') as $module) { - $modules[$module] = $module_info[$module]['name']; - } - asort($modules); + // Get a list of all the modules implementing a hook_permission() and sort by + // display name. + $modules = array(); + foreach ($this->moduleHandler->getImplementations('permission') as $module) { + $modules[$module] = $module_info[$module]['name']; + } + asort($modules); - $this->value_options = array(); - foreach ($modules as $module => $display_name) { - if ($permissions = module_invoke($module, 'permission')) { - foreach ($permissions as $perm => $perm_item) { - $this->value_options[$display_name][$perm] = check_plain(strip_tags($perm_item['title'])); + $this->value_options = array(); + foreach ($modules as $module => $display_name) { + if ($permissions = $this->moduleHandler->invoke($module, 'permission')) { + foreach ($permissions as $perm => $perm_item) { + $this->value_options[$display_name][$perm] = String::checkPlain(strip_tags($perm_item['title'])); + } } } } + else { + return $this->value_options; + } + } + + /** + * {@inheritdoc} + * + * Replace the configured permission with a filter by all roles that have this + * permission. + */ + public function query() { + // @todo user_role_names() should maybe support multiple permissions. + $rids = array(); + // Get all roles, that have the configured permissions. + foreach ($this->value as $permission) { + $roles = user_role_names(FALSE, $permission); + $rids += array_keys($roles); + } + $rids = array_unique($rids); + $this->value = $rids; + + // $this->value contains the role IDs that have the configured permission. + parent::query(); } } diff --git a/core/modules/user/lib/Drupal/user/RoleInterface.php b/core/modules/user/lib/Drupal/user/RoleInterface.php index f503ecc..b6b749d 100644 --- a/core/modules/user/lib/Drupal/user/RoleInterface.php +++ b/core/modules/user/lib/Drupal/user/RoleInterface.php @@ -14,4 +14,45 @@ */ interface RoleInterface extends ConfigEntityInterface { + /** + * Returns a list of permissions assigned to the role. + * + * @return array + * The permissions assigned to the role. + */ + public function getPermissions(); + + /** + * Checks if the role has a permission. + * + * @param string $permission + * The permission to check for. + * + * @return bool + * TRUE if the role has the permission, FALSE if not. + */ + public function hasPermission($permission); + + /** + * Grant permissions to the role. + * + * @param string $permission + * The permission to grant. + * + * @return RoleInterface + * The called object for chaining. + */ + public function grantPermission($permission); + + /** + * Revokes a permissions from the user role. + * + * @param string $permission + * The permission to revoke. + * + * @return RoleInterface + * The called object for chaining. + */ + public function revokePermission($permission); + } diff --git a/core/modules/user/lib/Drupal/user/RoleStorageController.php b/core/modules/user/lib/Drupal/user/RoleStorageController.php index 4b6ca87..fff106e 100644 --- a/core/modules/user/lib/Drupal/user/RoleStorageController.php +++ b/core/modules/user/lib/Drupal/user/RoleStorageController.php @@ -19,20 +19,14 @@ class RoleStorageController extends ConfigStorageController implements RoleStora */ public function resetCache(array $ids = NULL) { parent::resetCache($ids); - // Clear the user access cache. drupal_static_reset('user_access'); - drupal_static_reset('user_role_permissions'); } /** * {@inheritdoc} */ public function deleteRoleReferences(array $rids) { - // Delete permission assignments. - db_delete('role_permission') - ->condition('rid', $rids) - ->execute(); // Remove the role from all users. db_delete('users_roles') ->condition('rid', $rids) diff --git a/core/modules/user/lib/Drupal/user/Tests/UserPermissionsTest.php b/core/modules/user/lib/Drupal/user/Tests/UserPermissionsTest.php index 3fc3b4b..3c8cd54 100644 --- a/core/modules/user/lib/Drupal/user/Tests/UserPermissionsTest.php +++ b/core/modules/user/lib/Drupal/user/Tests/UserPermissionsTest.php @@ -8,6 +8,7 @@ namespace Drupal\user\Tests; use Drupal\simpletest\WebTestBase; +use Drupal\user\RoleStorageController; class UserPermissionsTest extends WebTestBase { protected $admin_user; @@ -46,8 +47,8 @@ function testUserPermissionChanges() { $edit[$rid . '[administer nodes]'] = TRUE; $this->drupalPost('admin/people/permissions', $edit, t('Save permissions')); $this->assertText(t('The changes have been saved.'), 'Successful save message displayed.'); - drupal_static_reset('user_access'); - drupal_static_reset('user_role_permissions'); + $storage_controller = $this->container->get('plugin.manager.entity')->getStorageController('user_role'); + $storage_controller->resetCache(); $this->assertTrue(user_access('administer nodes', $account), 'User now has "administer nodes" permission.'); // Remove a permission. @@ -56,8 +57,7 @@ function testUserPermissionChanges() { $edit[$rid . '[access user profiles]'] = FALSE; $this->drupalPost('admin/people/permissions', $edit, t('Save permissions')); $this->assertText(t('The changes have been saved.'), 'Successful save message displayed.'); - drupal_static_reset('user_access'); - drupal_static_reset('user_role_permissions'); + $storage_controller->resetCache(); $this->assertFalse(user_access('access user profiles', $account), 'User no longer has "access user profiles" permission.'); } diff --git a/core/modules/user/lib/Drupal/user/Tests/Views/HandlerFieldPermissionTest.php b/core/modules/user/lib/Drupal/user/Tests/Views/HandlerFieldPermissionTest.php new file mode 100644 index 0000000..70e772a --- /dev/null +++ b/core/modules/user/lib/Drupal/user/Tests/Views/HandlerFieldPermissionTest.php @@ -0,0 +1,66 @@ + 'User: Permissions Field', + 'description' => 'Tests the permission field handler.', + 'group' => 'Views module integration', + ); + } + + /** + * Tests the permission field handler output. + */ + public function testFieldPermission() { + $this->setupPermissionTestData(); + + $view = views_get_view('test_field_permission'); + $this->executeView($view); + $view->initStyle(); + $view->render(); + $style_plugin = $view->style_plugin; + + $expected_permissions = array(); + $expected_permissions[$this->users[0]->id()] = array(); + $expected_permissions[$this->users[1]->id()] = array(); + $expected_permissions[$this->users[2]->id()][] = t('Administer permissions'); + // View user profiles comes first, because we sort by the permission + // machine name. + $expected_permissions[$this->users[3]->id()][] = t('Administer permissions'); + $expected_permissions[$this->users[3]->id()][] = t('Administer users'); + $expected_permissions[$this->users[3]->id()][] = t('View user profiles'); + + foreach ($view->result as $index => $row) { + $uid = $view->field['uid']->getValue($row); + $rendered_permission = $style_plugin->getField($index, 'permission'); + + $expected_output = implode(', ', $expected_permissions[$uid]); + $this->assertEqual($rendered_permission, $expected_output, 'The right permissions are rendered.'); + } + } + +} diff --git a/core/modules/user/lib/Drupal/user/Tests/Views/HandlerFilterPermissionTest.php b/core/modules/user/lib/Drupal/user/Tests/Views/HandlerFilterPermissionTest.php new file mode 100644 index 0000000..ed67b16 --- /dev/null +++ b/core/modules/user/lib/Drupal/user/Tests/Views/HandlerFilterPermissionTest.php @@ -0,0 +1,97 @@ + 'User: Permissions Filter', + 'description' => 'Tests the permission filter handler.', + 'group' => 'Views module integration', + ); + } + + /** + * Tests the permission filter handler. + * + * @todo Fix the different commented out tests by fixing the many to one + * handler handling with the NOT operator. + */ + public function testFilterPermission() { + $this->setupPermissionTestData(); + + $column_map = array('uid' => 'uid'); + $view = views_get_view('test_filter_permission'); + + // Filter by a non existing permission. + $view->initHandlers(); + $view->filter['permission']->value = array('non_existent_permission'); + $this->executeView($view); + $this->assertEqual(count($view->result), 4, 'A non existent permission is not filtered so everything is the result.'); + $expected[] = array('uid' => 1); + $expected[] = array('uid' => 2); + $expected[] = array('uid' => 3); + $expected[] = array('uid' => 4); + $this->assertIdenticalResultset($view, $expected, $column_map); + $view->destroy(); + + // Filter by a permission. + $view->initHandlers(); + $view->filter['permission']->value = array('administer permissions'); + $this->executeView($view); + $this->assertEqual(count($view->result), 2); + $expected = array(); + $expected[] = array('uid' => 3); + $expected[] = array('uid' => 4); + $this->assertIdenticalResultset($view, $expected, $column_map); + $view->destroy(); + + // Filter by another permission of a role with multiple permissions. + $view->initHandlers(); + $view->filter['permission']->value = array('administer users'); + $this->executeView($view); + $this->assertEqual(count($view->result), 1); + $expected = array(); + $expected[] = array('uid' => 4); + $this->assertIdenticalResultset($view, $expected, $column_map); + $view->destroy(); + + $view->initDisplay(); + $view->initHandlers(); + $view->filter['permission']->getValueOptions(); + + // Test the value options. + $value_options = $view->filter['permission']->getValueOptions(); + foreach (array('system' => 'System', 'user' => 'User') as $module => $title) { + $expected = array_map(function ($permission) { + return String::checkPlain(strip_tags($permission['title'])); + }, $this->container->get('module_handler')->invoke($module, 'permission')); + + $this->assertEqual($expected, $value_options[$title], 'Ensure the all permissions are available'); + } + } + +} diff --git a/core/modules/user/lib/Drupal/user/Tests/Views/UserUnitTestBase.php b/core/modules/user/lib/Drupal/user/Tests/Views/UserUnitTestBase.php new file mode 100644 index 0000000..9350338 --- /dev/null +++ b/core/modules/user/lib/Drupal/user/Tests/Views/UserUnitTestBase.php @@ -0,0 +1,96 @@ +installSchema('user', array('users', 'users_roles')); + $this->installSchema('system', 'sequences'); + + $entity_manager = $this->container->get('plugin.manager.entity'); + $this->roleStorageController = $entity_manager->getStorageController('user_role'); + $this->userStorageController = $entity_manager->getStorageController('user'); + } + + /** + * Set some test data for permission related tests. + */ + protected function setupPermissionTestData() { + // Setup a role without any permission. + $this->roleStorageController->create(array('id' => 'authenticated')) + ->save(); + $this->roleStorageController->create(array('id' => 'no_permission')) + ->save(); + // Setup a role with just one permission. + $this->roleStorageController->create(array('id' => 'one_permission')) + ->save(); + user_role_grant_permissions('one_permission', array('administer permissions')); + // Setup a role with multiple permissions. + $this->roleStorageController->create(array('id' => 'multiple_permissions')) + ->save(); + user_role_grant_permissions('multiple_permissions', array('administer permissions', 'administer users', 'access user profiles')); + + // Setup a user without an extra role. + $this->users[] = $account = $this->userStorageController->create(array()); + $account->save(); + // Setup a user with just the first role (so no permission beside the + // ones from the authenticated role). + $this->users[] = $account = $this->userStorageController->create(array('name' => 'first_role')); + $account->addRole('no_permission'); + $account->save(); + // Setup a user with just the second role (so one additional permission). + $this->users[] = $account = $this->userStorageController->create(array('name' => 'second_role')); + $account->addRole('one_permission'); + $account->save(); + // Setup a user with both the second and the third role. + $this->users[] = $account = $this->userStorageController->create(array('name' => 'second_third_role')); + $account->addRole('one_permission'); + $account->addRole('multiple_permissions'); + $account->save(); + } + +} diff --git a/core/modules/user/tests/modules/user_test_views/test_views/views.view.test_field_permission.yml b/core/modules/user/tests/modules/user_test_views/test_views/views.view.test_field_permission.yml new file mode 100644 index 0000000..94f587d --- /dev/null +++ b/core/modules/user/tests/modules/user_test_views/test_views/views.view.test_field_permission.yml @@ -0,0 +1,135 @@ +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: + access: + type: none + cache: + type: none + query: + type: views_query + exposed_form: + type: basic + pager: + type: full + style: + type: default + row: + type: fields + fields: + uid: + id: uid + table: users + field: uid + relationship: none + group_type: group + admin_label: '' + label: Uid + exclude: '0' + alter: + alter_text: '0' + text: '' + make_link: '0' + path: '' + absolute: '0' + external: '0' + replace_spaces: '0' + path_case: none + trim_whitespace: '0' + alt: '' + rel: '' + link_class: '' + prefix: '' + suffix: '' + target: '' + nl2br: '0' + max_length: '' + word_boundary: '1' + ellipsis: '1' + more_link: '0' + more_link_text: '' + more_link_path: '' + strip_tags: '0' + trim: '0' + preserve_tags: '' + html: '0' + element_type: '' + element_class: '' + element_label_type: '' + element_label_class: '' + element_label_colon: '1' + element_wrapper_type: '' + element_wrapper_class: '' + element_default_classes: '1' + empty: '' + hide_empty: '0' + empty_zero: '0' + hide_alter_empty: '1' + link_to_user: '0' + plugin_id: user + permission: + id: permission + table: users_roles + field: permission + relationship: none + group_type: group + admin_label: '' + label: Permission + exclude: '0' + alter: + alter_text: '0' + text: '' + make_link: '0' + path: '' + absolute: '0' + external: '0' + replace_spaces: '0' + path_case: none + trim_whitespace: '0' + alt: '' + rel: '' + link_class: '' + prefix: '' + suffix: '' + target: '' + nl2br: '0' + max_length: '' + word_boundary: '1' + ellipsis: '1' + more_link: '0' + more_link_text: '' + more_link_path: '' + strip_tags: '0' + trim: '0' + preserve_tags: '' + html: '0' + element_type: '' + element_class: '' + element_label_type: '' + element_label_class: '' + element_label_colon: '1' + element_wrapper_type: '' + element_wrapper_class: '' + element_default_classes: '1' + empty: '' + hide_empty: '0' + empty_zero: '0' + hide_alter_empty: '1' + type: separator + separator: ', ' + plugin_id: user_permissions + filters: { } + sorts: { } +human_name: test_field_permission +module: views +id: test_field_permission +tag: '' +langcode: en diff --git a/core/modules/user/tests/modules/user_test_views/test_views/views.view.test_filter_permission.yml b/core/modules/user/tests/modules/user_test_views/test_views/views.view.test_filter_permission.yml new file mode 100644 index 0000000..ef1fd1c --- /dev/null +++ b/core/modules/user/tests/modules/user_test_views/test_views/views.view.test_filter_permission.yml @@ -0,0 +1,123 @@ +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: + access: + type: none + cache: + type: none + query: + type: views_query + exposed_form: + type: basic + pager: + type: full + style: + type: default + row: + type: fields + fields: + uid: + id: uid + table: users + field: uid + relationship: none + group_type: group + admin_label: '' + label: '' + exclude: '0' + alter: + alter_text: '0' + text: '' + make_link: '0' + path: '' + absolute: '0' + external: '0' + replace_spaces: '0' + path_case: none + trim_whitespace: '0' + alt: '' + rel: '' + link_class: '' + prefix: '' + suffix: '' + target: '' + nl2br: '0' + max_length: '' + word_boundary: '1' + ellipsis: '1' + more_link: '0' + more_link_text: '' + more_link_path: '' + strip_tags: '0' + trim: '0' + preserve_tags: '' + html: '0' + element_type: '' + element_class: '' + element_label_type: '' + element_label_class: '' + element_label_colon: '0' + element_wrapper_type: '' + element_wrapper_class: '' + element_default_classes: '1' + empty: '' + hide_empty: '0' + empty_zero: '0' + hide_alter_empty: '1' + link_to_user: '0' + plugin_id: user + filters: + permission: + id: permission + table: users_roles + field: permission + relationship: none + group_type: group + admin_label: '' + operator: or + value: + 'access user profiles': 'access user profiles' + group: '1' + exposed: '0' + expose: + operator_id: '0' + label: '' + description: '' + use_operator: '0' + operator: '' + identifier: '' + required: '0' + remember: '0' + multiple: '0' + remember_roles: + authenticated: authenticated + reduce: '0' + is_grouped: '0' + group_info: + label: '' + description: '' + identifier: '' + optional: '1' + widget: select + multiple: '0' + remember: '0' + default_group: All + default_group_multiple: { } + group_items: { } + reduce_duplicates: '1' + plugin_id: user_permissions + sorts: { } +human_name: test_filter_permission +module: views +id: test_filter_permission +tag: '' +langcode: en diff --git a/core/modules/user/user.admin.inc b/core/modules/user/user.admin.inc index f9982e8..c406b10 100644 --- a/core/modules/user/user.admin.inc +++ b/core/modules/user/user.admin.inc @@ -105,7 +105,6 @@ function user_admin_account() { * @see theme_user_admin_permissions() */ function user_admin_permissions($form, $form_state, $rid = NULL) { - // Retrieve role names for columns. $role_names = user_role_names(); if (isset($rid)) { @@ -158,7 +157,7 @@ function user_admin_permissions($form, $form_state, $rid = NULL) { ); foreach ($role_names as $rid => $name) { // Builds arrays for checked boxes for each role - if (isset($role_permissions[$rid][$perm])) { + if (in_array($perm, $role_permissions[$rid])) { $status[$rid][] = $perm; } } diff --git a/core/modules/user/user.install b/core/modules/user/user.install index 293a45e..361a3e4 100644 --- a/core/modules/user/user.install +++ b/core/modules/user/user.install @@ -149,42 +149,6 @@ function user_schema() { ), ); - $schema['role_permission'] = array( - 'description' => 'Stores the permissions assigned to user roles.', - 'fields' => array( - 'rid' => array( - 'type' => 'varchar', - 'length' => 64, - 'not null' => TRUE, - 'description' => 'Foreign Key: {role}.rid.', - ), - 'permission' => array( - 'type' => 'varchar', - 'length' => 128, - 'not null' => TRUE, - 'default' => '', - 'description' => 'A single permission granted to the role identified by rid.', - ), - 'module' => array( - 'type' => 'varchar', - 'length' => DRUPAL_EXTENSION_NAME_MAX_LENGTH, - 'not null' => TRUE, - 'default' => '', - 'description' => "The module declaring the permission.", - ), - ), - 'primary key' => array('rid', 'permission'), - 'indexes' => array( - 'permission' => array('permission'), - ), - 'foreign keys' => array( - 'role' => array( - 'table' => 'role', - 'columns' => array('rid' => 'rid'), - ), - ), - ); - $schema['users_data'] = array( 'description' => 'Stores module data as key/value pairs per user.', 'fields' => array( @@ -386,6 +350,7 @@ function user_update_dependencies() { return $dependencies; } + /** * @addtogroup updates-7.x-to-8.x * @{ @@ -451,9 +416,6 @@ function user_update_8002() { ); db_change_field('role', 'rid', 'rid', $column); - $column['description'] = 'Foreign Key: {role}.rid.'; - db_change_field('role_permission', 'rid', 'rid', $column); - $column['description'] = 'Primary Key: {role}.rid for role.'; db_change_field('users_roles', 'rid', 'rid', $column); @@ -470,32 +432,16 @@ function user_update_8002() { db_drop_unique_key('role', 'name'); // Rename the built-in serial role IDs into the hardcoded machine names. - db_update('role') - ->fields(array('rid' => DRUPAL_ANONYMOUS_RID)) - ->condition('rid', 1) - ->execute(); - db_update('role') - ->fields(array('rid' => DRUPAL_AUTHENTICATED_RID)) - ->condition('rid', 2) - ->execute(); - - db_update('role_permission') - ->fields(array('rid' => DRUPAL_ANONYMOUS_RID)) - ->condition('rid', 1) - ->execute(); - db_update('role_permission') - ->fields(array('rid' => DRUPAL_AUTHENTICATED_RID)) - ->condition('rid', 2) - ->execute(); - - db_update('users_roles') - ->fields(array('rid' => DRUPAL_ANONYMOUS_RID)) - ->condition('rid', 1) - ->execute(); - db_update('users_roles') - ->fields(array('rid' => DRUPAL_AUTHENTICATED_RID)) - ->condition('rid', 2) - ->execute(); + foreach (array(1, 2) as $rid) { + db_update('role') + ->fields(array('rid' => _user_update_map_rid($rid))) + ->condition('rid', $rid) + ->execute(); + db_update('users_roles') + ->fields(array('rid' => _user_update_map_rid($rid))) + ->condition('rid', $rid) + ->execute(); + } } /** @@ -1088,5 +1034,46 @@ function user_update_8018() { } /** + * Maps a role id to the new string. + * + * @param int $rid + * A D7 numeric role ID. + * + * @return string + * A D8 string role ID. + */ +function _user_update_map_rid($rid) { + $rid_map = array( + 1 => DRUPAL_ANONYMOUS_RID, + 2 => DRUPAL_AUTHENTICATED_RID, + ); + return isset($rid_map[$rid]) ? $rid_map[$rid] : $rid; +} + +/** + * Migrate roles permissions into configuration. + * + * @ingroup config_upgrade + */ +function user_update_8019() { + $db_permissions = db_select('role_permission', 'p') + ->fields('p') + ->execute() + ->fetchAll(); + $new_permissions = array(); + foreach ($db_permissions as $permission) { + $rid = _user_update_map_rid($permission->rid); + if (!isset($new_permissions[$rid]) || !in_array($permission->permission, $new_permissions[$rid])) { + $new_permissions[$rid][] = $permission->permission; + } + } + foreach ($new_permissions as $rid => $permissions) { + config("user.role.$rid") + ->set('permissions', $permissions) + ->save(); + } +} + +/** * @} End of "addtogroup updates-7.x-to-8.x". */ diff --git a/core/modules/user/user.module b/core/modules/user/user.module index e7ee37a..a5e6e23 100644 --- a/core/modules/user/user.module +++ b/core/modules/user/user.module @@ -4,12 +4,10 @@ use Drupal\Core\Database\Query\SelectInterface; use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Session\AccountInterface; -use Drupal\comment\Plugin\Core\Entity\Comment; use Drupal\entity\Plugin\Core\Entity\EntityDisplay; use Drupal\file\Plugin\Core\Entity\File; use Drupal\user\Plugin\Core\Entity\User; use Drupal\user\UserInterface; -use Drupal\user\UserRole; use Drupal\user\RoleInterface; use Drupal\Core\Template\Attribute; use Symfony\Component\HttpFoundation\RedirectResponse; @@ -410,44 +408,44 @@ function user_password($length = 10) { /** * Determine the permissions for one or more roles. * - * @param $roles - * An array whose values are the role IDs of interest, such as $user->roles. + * @param array $roles + * An array of role IDs. * - * @return - * An array indexed by role ID. Each value is an array whose keys are the - * permission strings for the given role ID. + * @return array + * An array indexed by role ID. Each value is an array of permission strings + * for the given role. */ -function user_role_permissions($roles) { - $cache = &drupal_static(__FUNCTION__, array()); - - $role_permissions = $fetch = array(); - +function user_role_permissions(array $roles) { + if (defined('MAINTENANCE_MODE') && MAINTENANCE_MODE == 'update') { + return _user_role_permissions_update($roles); + } + $entities = entity_load_multiple('user_role', $roles); + $role_permissions = array(); foreach ($roles as $rid) { - if (isset($cache[$rid])) { - $role_permissions[$rid] = $cache[$rid]; - } - else { - // Add this rid to the list of those needing to be fetched. - $fetch[] = $rid; - // Prepare in case no permissions are returned. - $cache[$rid] = array(); - } + $role_permissions[$rid] = isset($entities[$rid]) ? $entities[$rid]->getPermissions() : array(); } + return $role_permissions; +} - if ($fetch) { - // Get from the database permissions that were not in the static variable. - // Only role IDs with at least one permission assigned will return rows. - $result = db_query("SELECT rid, permission FROM {role_permission} WHERE rid IN (:fetch)", array(':fetch' => $fetch)); - - foreach ($result as $row) { - $cache[$row->rid][$row->permission] = TRUE; - } - foreach ($fetch as $rid) { - // For every rid, we know we at least assigned an empty array. - $role_permissions[$rid] = $cache[$rid]; - } +/** + * Determine the permissions for one or more roles during update. + * + * A separate version is needed because during update the entity system can't + * be used and in non-update situations the entity system is preferred because + * of the hook system. + * + * @param array $roles + * An array of role IDs. + * + * @return array + * An array indexed by role ID. Each value is an array of permission strings + * for the given role. + */ +function _user_role_permissions_update($roles) { + $role_permissions = array(); + foreach ($roles as $rid) { + $role_permissions[$rid] = Drupal::config("user.role.$rid")->get('permissions') ?: array(); } - return $role_permissions; } @@ -490,13 +488,10 @@ function user_access($string, AccountInterface $account = NULL) { } $perm = &$drupal_static_fast['perm']; if (!isset($perm[$account->uid])) { - $role_permissions = user_role_permissions($account->getRoles()); - - $perms = array(); - foreach ($role_permissions as $one_role) { - $perms += $one_role; + $perm[$account->uid] = array(); + foreach (user_role_permissions($account->getRoles()) as $role_permissions) { + $perm[$account->uid] += array_fill_keys($role_permissions, TRUE); } - $perm[$account->uid] = $perms; } return isset($perm[$account->uid][$string]); @@ -1759,7 +1754,7 @@ function user_mail_tokens(&$replacements, $data, $options) { * value. */ function user_role_names($membersonly = FALSE, $permission = NULL) { - return array_map(function($item) { + return array_map(function ($item) { return $item->label(); }, user_roles($membersonly, $permission)); } @@ -1844,12 +1839,9 @@ function user_roles($membersonly = FALSE, $permission = NULL) { } if (!empty($permission)) { - $result = db_select('role_permission', 'p') - ->fields('p', array('rid')) - ->condition('p.rid', array_keys($roles)) - ->condition('p.permission', $permission) - ->execute()->fetchCol(); - $roles = array_intersect_key($roles, array_flip($result)); + $roles = array_filter($roles, function ($role) use ($permission) { + return $role->hasPermission($permission); + }); } if (empty($permission)) { @@ -1944,23 +1936,14 @@ function user_role_change_permissions($rid, array $permissions = array()) { * @see user_role_revoke_permissions() */ function user_role_grant_permissions($rid, array $permissions = array()) { - $modules = user_permission_get_modules(); // Grant new permissions for the role. - foreach ($permissions as $name) { - db_merge('role_permission') - ->key(array( - 'rid' => $rid, - 'permission' => $name, - )) - ->fields(array( - 'module' => $modules[$name], - )) - ->execute(); + $role = entity_load('user_role', $rid); + foreach ($permissions as $permission) { + $role->grantPermission($permission); } - + $role->save(); // Clear the user access cache. drupal_static_reset('user_access'); - drupal_static_reset('user_role_permissions'); } /** @@ -1976,14 +1959,13 @@ function user_role_grant_permissions($rid, array $permissions = array()) { */ function user_role_revoke_permissions($rid, array $permissions = array()) { // Revoke permissions for the role. - db_delete('role_permission') - ->condition('rid', $rid) - ->condition('permission', $permissions, 'IN') - ->execute(); - + $role = entity_load('user_role', $rid); + foreach ($permissions as $permission) { + $role->revokePermission($permission); + } + $role->save(); // Clear the user access cache. drupal_static_reset('user_access'); - drupal_static_reset('user_role_permissions'); } function user_multiple_cancel_confirm($form, &$form_state) { @@ -2143,18 +2125,20 @@ function user_build_filter_query(SelectInterface $query) { // the authenticated role. If so, then all users would be listed, and we can // skip adding it to the filter query. if ($key == 'permission') { - $account = entity_create('user', array( - 'uid' => 'user_filter', - 'roles' => array(DRUPAL_AUTHENTICATED_RID), - )); - if (user_access($value, $account)) { + $roles = user_roles(FALSE, $value); + if (isset($roles[DRUPAL_AUTHENTICATED_RID])) { continue; } - $users_roles_alias = $query->join('users_roles', 'ur', '%alias.uid = u.uid'); - $permission_alias = $query->join('role_permission', 'p', $users_roles_alias . '.rid = %alias.rid'); - $query->condition($permission_alias . '.permission', $value); + if (!empty($roles)) { + $value = array_keys($roles); + } + else { + // There's no role that has this permission set value to an impossible + // rid so that no users are matched. + $value = array(''); + } } - elseif ($key == 'role') { + if ($key == 'permission' || $key == 'role') { $users_roles_alias = $query->join('users_roles', 'ur', '%alias.uid = u.uid'); $query->condition($users_roles_alias . '.rid' , $value); } @@ -2427,9 +2411,6 @@ function user_modules_installed($modules) { * Implements hook_modules_uninstalled(). */ function user_modules_uninstalled($modules) { - db_delete('role_permission') - ->condition('module', $modules, 'IN') - ->execute(); // Remove any potentially orphan module data stored for users. drupal_container()->get('user.data')->delete($modules); } diff --git a/core/modules/user/user.views.inc b/core/modules/user/user.views.inc index 0cc88ce..83474b0 100644 --- a/core/modules/user/user.views.inc +++ b/core/modules/user/user.views.inc @@ -359,19 +359,7 @@ function user_views_data() { ), ); - // Define the base group of this table. Fields that don't have a group defined - // will go into this field by default. - $data['role_permission']['table']['group'] = t('User'); - // Explain how this table joins to others. - $data['role_permission']['table']['join'] = array( - 'users' => array( - 'left_table' => 'users_roles', - 'left_field' => 'rid', - 'field' => 'rid', - ), - ); - - $data['role_permission']['permission'] = array( + $data['users_roles']['permission'] = array( 'title' => t('Permission'), 'help' => t('The user permissions.'), 'field' => array( @@ -380,6 +368,7 @@ function user_views_data() { ), 'filter' => array( 'id' => 'user_permissions', + 'real field' => 'rid', ), ); diff --git a/core/modules/views/lib/Drupal/views/ManyToOneHelper.php b/core/modules/views/lib/Drupal/views/ManyToOneHelper.php index 24ef8ab..ccd3c3b 100644 --- a/core/modules/views/lib/Drupal/views/ManyToOneHelper.php +++ b/core/modules/views/lib/Drupal/views/ManyToOneHelper.php @@ -309,7 +309,7 @@ public function addFilter() { else { $placeholder = $this->placeholder(); if (count($this->handler->value) > 1) { - $this->query->addWhereExpression(0, "$field $operator($placeholder)", array($placeholder => $value)); + $this->handler->query->addWhereExpression(0, "$field $operator($placeholder)", array($placeholder => $value)); } else { $this->handler->query->addWhereExpression(0, "$field $operator $placeholder", array($placeholder => $value)); diff --git a/core/modules/views/lib/Drupal/views/Tests/Handler/FieldCounterTest.php b/core/modules/views/lib/Drupal/views/Tests/Handler/FieldCounterTest.php new file mode 100644 index 0000000..8773248 --- /dev/null +++ b/core/modules/views/lib/Drupal/views/Tests/Handler/FieldCounterTest.php @@ -0,0 +1,100 @@ + 'Field: Counter', + 'description' => 'Tests the Drupal\views\Plugin\views\field\Counter handler.', + 'group' => 'Views Handlers', + ); + } + + function testSimple() { + $view = views_get_view('test_view'); + $view->setDisplay(); + $view->displayHandlers->get('default')->overrideOption('fields', array( + 'counter' => array( + 'id' => 'counter', + 'table' => 'views', + 'field' => 'counter', + 'relationship' => 'none', + ), + 'name' => array( + 'id' => 'name', + 'table' => 'views_test_data', + 'field' => 'name', + 'relationship' => 'none', + ), + )); + $view->preview(); + + $counter = $view->style_plugin->getField(0, 'counter'); + $this->assertEqual($counter, 1, format_string('Make sure the expected number (@expected) patches with the rendered number (@counter)', array('@expected' => 1, '@counter' => $counter))); + $counter = $view->style_plugin->getField(1, 'counter'); + $this->assertEqual($counter, 2, format_string('Make sure the expected number (@expected) patches with the rendered number (@counter)', array('@expected' => 2, '@counter' => $counter))); + $counter = $view->style_plugin->getField(2, 'counter'); + $this->assertEqual($counter, 3, format_string('Make sure the expected number (@expected) patches with the rendered number (@counter)', array('@expected' => 3, '@counter' => $counter))); + $view->destroy(); + + $view->setDisplay(); + $rand_start = rand(5, 10); + $view->displayHandlers->get('default')->overrideOption('fields', array( + 'counter' => array( + 'id' => 'counter', + 'table' => 'views', + 'field' => 'counter', + 'relationship' => 'none', + 'counter_start' => $rand_start + ), + 'name' => array( + 'id' => 'name', + 'table' => 'views_test_data', + 'field' => 'name', + 'relationship' => 'none', + ), + )); + $view->preview(); + + $counter = $view->style_plugin->getField(0, 'counter'); + $expected_number = 0 + $rand_start; + $this->assertEqual($counter, $expected_number, format_string('Make sure the expected number (@expected) patches with the rendered number (@counter)', array('@expected' => $expected_number, '@counter' => $counter))); + $counter = $view->style_plugin->getField(1, 'counter'); + $expected_number = 1 + $rand_start; + $this->assertEqual($counter, $expected_number, format_string('Make sure the expected number (@expected) patches with the rendered number (@counter)', array('@expected' => $expected_number, '@counter' => $counter))); + $counter = $view->style_plugin->getField(2, 'counter'); + $expected_number = 2 + $rand_start; + $this->assertEqual($counter, $expected_number, format_string('Make sure the expected number (@expected) patches with the rendered number (@counter)', array('@expected' => $expected_number, '@counter' => $counter))); + } + + // @TODO: Write tests for pager. + function testPager() { + } + +} diff --git a/core/modules/views/lib/Drupal/views/Tests/Handler/FieldUnitTest.php b/core/modules/views/lib/Drupal/views/Tests/Handler/FieldUnitTest.php index 2c90c37..eff8929 100644 --- a/core/modules/views/lib/Drupal/views/Tests/Handler/FieldUnitTest.php +++ b/core/modules/views/lib/Drupal/views/Tests/Handler/FieldUnitTest.php @@ -38,11 +38,6 @@ public static function getInfo() { ); } - public function setUp() { - parent::setUp(); - $this->installSchema('user', 'role_permission'); - } - /** * Overrides Drupal\views\Tests\ViewTestBase::viewsData(). */ diff --git a/core/modules/views/lib/Drupal/views/Tests/Plugin/DisplayPageTest.php b/core/modules/views/lib/Drupal/views/Tests/Plugin/DisplayPageTest.php index 2ea84d4..049795c 100644 --- a/core/modules/views/lib/Drupal/views/Tests/Plugin/DisplayPageTest.php +++ b/core/modules/views/lib/Drupal/views/Tests/Plugin/DisplayPageTest.php @@ -60,7 +60,6 @@ protected function setUp() { $this->installSchema('system', 'router'); $this->installSchema('system', 'url_alias'); $this->installSchema('system', 'menu_router'); - $this->installSchema('user', 'role_permission'); } /** diff --git a/core/modules/views/lib/Drupal/views/Tests/Plugin/RelationshipJoinTestBase.php b/core/modules/views/lib/Drupal/views/Tests/Plugin/RelationshipJoinTestBase.php index 172678d..95a543f 100644 --- a/core/modules/views/lib/Drupal/views/Tests/Plugin/RelationshipJoinTestBase.php +++ b/core/modules/views/lib/Drupal/views/Tests/Plugin/RelationshipJoinTestBase.php @@ -26,7 +26,7 @@ * Overrides \Drupal\views\Tests\ViewUnitTestBase::setUpFixtures(). */ protected function setUpFixtures() { - $this->installSchema('user', array('users', 'users_roles', 'role_permission')); + $this->installSchema('user', array('users', 'users_roles')); $this->installConfig(array('user')); parent::setUpFixtures(); diff --git a/core/modules/views/lib/Drupal/views/Tests/ViewExecutableTest.php b/core/modules/views/lib/Drupal/views/Tests/ViewExecutableTest.php index b46840f..894363d 100644 --- a/core/modules/views/lib/Drupal/views/Tests/ViewExecutableTest.php +++ b/core/modules/views/lib/Drupal/views/Tests/ViewExecutableTest.php @@ -80,7 +80,7 @@ public static function getInfo() { } protected function setUpFixtures() { - $this->installSchema('user', array('users', 'role_permission')); + $this->installSchema('user', array('users')); $this->installSchema('node', array('node_type', 'node', 'node_field_data')); $this->installSchema('comment', array('comment', 'node_comment_statistics')); parent::setUpFixtures(); diff --git a/core/modules/views/lib/Drupal/views/Tests/ViewPageControllerTest.php b/core/modules/views/lib/Drupal/views/Tests/ViewPageControllerTest.php index d937aea..72da113 100644 --- a/core/modules/views/lib/Drupal/views/Tests/ViewPageControllerTest.php +++ b/core/modules/views/lib/Drupal/views/Tests/ViewPageControllerTest.php @@ -56,7 +56,6 @@ protected function setUp() { parent::setUp(); $this->installSchema('system', 'menu_router'); - $this->installSchema('user', 'role_permission'); $this->pageController = new ViewPageController($this->container->get('plugin.manager.entity')->getStorageController('view'), new ViewExecutableFactory()); }