permission_group-1.0.x-dev/src/Entity/PermissionGroup.php
src/Entity/PermissionGroup.php
<?php
namespace Drupal\permission_group\Entity;
use Drupal\Core\Config\Entity\ConfigEntityBase;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\user\Entity\Role;
/**
* Defines the permission group entity type.
*
* @ConfigEntityType(
* id = "permission_group",
* label = @Translation("Permission Group"),
* label_collection = @Translation("Permission Groups"),
* label_singular = @Translation("permission group"),
* label_plural = @Translation("permission groups"),
* label_count = @PluralTranslation(
* singular = "@count permission group",
* plural = "@count permission groups",
* ),
* handlers = {
* "list_builder" = "Drupal\permission_group\PermissionGroupListBuilder",
* "form" = {
* "add" = "Drupal\permission_group\Form\PermissionGroupForm",
* "edit" = "Drupal\permission_group\Form\PermissionGroupForm",
* "delete" = "Drupal\permission_group\Form\PermissionGroupDeleteForm"
* }
* },
* config_prefix = "group",
* admin_permission = "administer permissions",
* links = {
* "collection" = "/admin/structure/group",
* "add-form" = "/admin/structure/group/add",
* "edit-form" = "/admin/structure/group/{permission_group}",
* "edit-permissions-form" =
* "/admin/people/permission_groups/permissions/{permission_group}",
* "delete-form" = "/admin/structure/group/{permission_group}/delete"
* },
* entity_keys = {
* "id" = "id",
* "label" = "label",
* "uuid" = "uuid"
* },
* config_export = {
* "id",
* "label",
* "description",
* "permissions",
* "permission_groups"
* }
* )
*/
class PermissionGroup extends ConfigEntityBase {
/**
* The permission group ID.
*
* @var string
*/
protected $id;
/**
* The permission group label. Maps to the permission title.
*
* @var string
*/
protected $label = '';
/**
* The permission group status.
*
* @var bool
*/
protected $status;
/**
* The group description.
*
* @var string
*/
protected $description = '';
/**
* The permissions belonging to this permission group.
*
* @var array
*/
protected $permissions = [];
/**
* Linked permission groups.
*
* @var array
*/
protected $permission_groups = [];
/**
* Returns a list of permissions assigned to the group.
*
* @param bool $resolve_groups
* This will resolve permission groups using recursion so a group can
* contain other groups.
*
* @return string[]
* The permissions assigned to the group.
*/
public function permissions($resolve_groups = FALSE) : array {
static $resolved_groups = [];
if (!$resolve_groups) {
return $this->permissions;
}
// Determine if this is the top level call.
$top_level_call = empty($resolved_groups);
$permissions = $this->permissions;
foreach ($this->permission_groups as $group_id) {
if (!in_array($group_id, $resolved_groups, TRUE) && ($group = PermissionGroup::load($group_id))) {
array_push($resolved_groups, $group_id);
$permissions = array_merge($permissions, $group->permissions(TRUE));
}
}
if ($top_level_call) {
// Reset the static.
$resolved_groups = [];
// Only need to manipulate the array once.
$permissions = array_unique($permissions);
sort($permissions);
}
return $permissions;
}
/**
* Gets the description.
*
* @return string
* The description.
*/
public function description() : string {
return $this->description;
}
/**
* Sets the list of permissions assigned to the group.
*
* @param string[] $permissions
* A list of permissions to assign to the group.
*
* @return $this
*/
public function setPermissions(array $permissions) : PermissionGroup {
$this->permissions = $permissions;
return $this;
}
/**
* Returns a list of linked permission groups..
*
* @return string[]
* The permission group IDs linked to the group.
*/
public function getPermissionGroups() : array {
return $this->permission_groups;
}
/**
* Sets the list of linked permission groups.
*
* @param string[] $permission_groups
* A list of permission group IDs.
*
* @return $this
*/
public function setPermissionGroups(array $permission_groups) : PermissionGroup {
$this->permission_groups = $permission_groups;
return $this;
}
/**
* {@inheritdoc}
*/
public function postSave(EntityStorageInterface $storage, $update = TRUE) {
parent::postSave($storage, $update);
$original_permissions = isset($this->original) ? $this->original->permissions(TRUE) : [];
if ($original_permissions !== $this->permissions(TRUE)) {
/** @var \Drupal\user\Entity\Role $role */
foreach (Role::loadMultiple() as $role) {
if ($this->isGroupInList($role->getThirdPartySetting('permission_group', 'groups', []))) {
// Clean the slate of any permissions that are assigned originally by
// this group so the permissions can be correctly rebuilt in
// permission_group_user_role_presave().
foreach ($original_permissions as $original_permission) {
$role->revokePermission($original_permission);
}
$role->save();
}
}
}
}
/**
* Determines if a group is in a list of groups or their linked groups.
*
* @param string[] $groups
* The list of groups to check.
*
* @return bool
* TRUE is the group is in the list of groups.
*/
private function isGroupInList(array $groups) {
if (in_array($this->id(), $groups, TRUE)) {
return TRUE;
}
if (!empty($groups)) {
foreach (PermissionGroup::loadMultiple($groups) as $permissionGroup) {
if ($this->isGroupInList($permissionGroup->getPermissionGroups())) {
return TRUE;
}
}
}
return FALSE;
}
/**
* {@inheritdoc}
*/
public static function preDelete(EntityStorageInterface $storage, array $entities) {
/** @var \Drupal\permission_group\Entity\PermissionGroup[] $entities */
$ids_to_delete = array_keys($entities);
// Empty the permissions so roles will be correctly updated in
// PermissionGroup::postSave() and permission_group_user_role_presave().
foreach ($entities as $permission_group) {
$permission_group->setPermissions([])->save();
}
// Fix any roles that have a direct usage of permission groups that will be
// deleted.
foreach (Role::loadMultiple() as $role) {
$linked_groups = $role->getThirdPartySetting('permission_group', 'groups', []);
$new_linked_groups = array_diff($linked_groups, $ids_to_delete);
if ($linked_groups !== $new_linked_groups) {
$role
->setThirdPartySetting('permission_group', 'groups', $new_linked_groups)
->save();
}
}
// Fix any permission groups that have a direct usage of the permission
// groups that will be deleted.
foreach (PermissionGroup::loadMultiple() as $permission_group) {
if (!in_array($permission_group->id(), $ids_to_delete, TRUE)) {
$linked_groups = $permission_group->getPermissionGroups();
$new_linked_groups = array_diff($linked_groups, $ids_to_delete);
if ($linked_groups !== $new_linked_groups) {
$permission_group
->setPermissionGroups($new_linked_groups)
->save();
}
}
}
parent::preDelete($storage, $entities);
}
/**
* Determines whether this group has any restricted access permissions.
*
* @param array|null $permission_handler_result
* For form building it is often useful to generate the current permission
* list outside of calling this method.
*
* @return bool
* TRUE if the permission group contains a restricted access permission.
*/
public function restrictAccess(array $permission_handler_result = NULL) : bool {
if (!$permission_handler_result) {
$permission_handler_result = \Drupal::service('user.permissions')->getPermissions();
}
$restricted_access_permissions = array_keys(array_filter($permission_handler_result, function ($permission) {
return isset($permission['restrict access']) && $permission['restrict access'];
}));
return (bool) array_intersect($this->permissions(TRUE), $restricted_access_permissions);
}
/**
* Updates the permission groups on a role, if necessary.
*
* @param \Drupal\user\Entity\Role $role
* The role to update, if necessary.
* @param array $group_ids
* The permission group IDs to link to the role.
*/
public static function roleSave(Role $role, array $group_ids) {
// Only save a role when something has changed.
if ($group_ids === $role->getThirdPartySetting('permission_group', 'groups', [])) {
return;
}
if (count($group_ids) > 0) {
$role->setThirdPartySetting('permission_group', 'groups', $group_ids);
}
else {
$role->unsetThirdPartySetting('permission_group', 'groups');
}
$role->save();
}
/**
* {@inheritdoc}
*/
public function calculateDependencies() {
parent::calculateDependencies();
// Load all permission definitions.
$permission_definitions = \Drupal::service('user.permissions')->getPermissions();
$valid_permissions = array_intersect($this->permissions, array_keys($permission_definitions));
$invalid_permissions = array_diff($this->permissions, $valid_permissions);
if (!empty($invalid_permissions) && !$this->get('skip_missing_permission_deprecation')) {
@trigger_error('Adding non-existent permissions to a permission group is deprecated in drupal:9.3.0 and triggers a runtime exception before drupal:10.0.0. The incorrect permissions are "' . implode('", "', $invalid_permissions) . '". Permissions should be defined in a permissions.yml file or a permission callback. See https://www.drupal.org/node/3193348', E_USER_DEPRECATED);
}
foreach ($valid_permissions as $permission) {
// Depend on the module that is providing this permissions.
$this->addDependency('module', $permission_definitions[$permission]['provider']);
// Depend on any other dependencies defined by permissions granted to
// this role.
if (!empty($permission_definitions[$permission]['dependencies'])) {
$this->addDependencies($permission_definitions[$permission]['dependencies']);
}
}
return $this;
}
/**
* {@inheritdoc}
*/
public function onDependencyRemoval(array $dependencies) {
$changed = parent::onDependencyRemoval($dependencies);
// Load all permission definitions.
$permission_definitions = \Drupal::service('user.permissions')->getPermissions();
// Convert config and content entity dependencies to a list of names to make
// it easier to check.
foreach (['content', 'config'] as $type) {
$dependencies[$type] = array_keys($dependencies[$type]);
}
// Remove any permissions from the permission group that are dependent on
// anything being deleted or uninstalled.
foreach ($this->permissions as $key => $permission) {
if (!isset($permission_definitions[$permission])) {
// If the permission is not defined then there's nothing we can do.
continue;
}
if (in_array($permission_definitions[$permission]['provider'], $dependencies['module'], TRUE)) {
unset($this->permissions[$key]);
$changed = TRUE;
// Process the next permission.
continue;
}
if (isset($permission_definitions[$permission]['dependencies'])) {
foreach ($permission_definitions[$permission]['dependencies'] as $type => $list) {
if (array_intersect($list, $dependencies[$type])) {
unset($this->permissions[$key]);
$changed = TRUE;
// Process the next permission.
continue 2;
}
}
}
}
return $changed;
}
}
