og-8.x-1.x-dev/src/Entity/OgRole.php

src/Entity/OgRole.php
<?php

declare(strict_types=1);

namespace Drupal\og\Entity;

use Drupal\Core\Config\Action\Attribute\ActionMethod;
use Drupal\Core\Config\ConfigValueException;
use Drupal\Core\Config\Entity\ConfigEntityBase;
use Drupal\Core\Entity\Attribute\ConfigEntityType;
use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\og\Exception\OgRoleException;
use Drupal\og\GroupTypeManagerInterface;
use Drupal\og\OgAccessInterface;
use Drupal\og\OgRoleAccessControlHandler;
use Drupal\og\OgRoleInterface;
use Drupal\og\OgRoleStorage;
use Drupal\og_ui\Form\OgRoleDeleteForm;
use Drupal\og_ui\Form\OgRoleForm;

/**
 * Defines the OG user role entity class.
 *
 * @ConfigEntityType(
 *   id = "og_role",
 *   label = @Translation("OG role"),
 *   label_collection = @Translation("OG roles"),
 *   label_singular = @Translation("OG role"),
 *   label_plural = @Translation("OG roles"),
 *   label_count = @PluralTranslation(
 *     singular = "@count OG role",
 *     plural = "@count OG roles",
 *   ),
 *   handlers = {
 *     "storage" = "Drupal\og\OgRoleStorage",
 *     "access" = "Drupal\og\OgRoleAccessControlHandler",
 *     "list_builder" = "Drupal\og\Entity\OgRoleListBuilder",
 *     "form" = {
 *       "default" = "Drupal\og_ui\Form\OgRoleForm",
 *       "delete" = "Drupal\og_ui\Form\OgRoleDeleteForm",
 *     }
 *   },
 *   admin_permission = "administer organic groups",
 *   config_prefix = "og_role",
 *   static_cache = TRUE,
 *   entity_keys = {
 *     "id" = "id",
 *     "label" = "label",
 *     "weight" = "weight",
 *   },
 *   links = {
 *     "collection" = "/admin/config/group/roles/{entity_type_id}/{bundle_id}",
 *     "add-form" = "/admin/config/group/roles/{entity_type_id}/{bundle_id}/add",
 *     "edit-form" = "/admin/config/group/role/{og_role}/edit",
 *     "delete-form" = "/admin/config/group/role/{og_role}/delete",
 *   },
 *   config_export = {
 *     "id",
 *     "label",
 *     "weight",
 *     "is_admin",
 *     "group_type",
 *     "group_bundle",
 *     "group_id",
 *     "permissions",
 *     "role_type",
 *   }
 * )
 */
#[ConfigEntityType(
  id: 'og_role',
  label: new TranslatableMarkup('OG role'),
  label_collection: new TranslatableMarkup('OG roles'),
  label_singular: new TranslatableMarkup('OG role'),
  label_plural: new TranslatableMarkup('OG roles'),
  config_prefix: 'og_role',
  static_cache: TRUE,
  entity_keys: [
    'id' => 'id',
    'label' => 'label',
    'weight' => 'weight',
  ],
  handlers: [
    'storage' => OgRoleStorage::class,
    'access' => OgRoleAccessControlHandler::class,
    'list_builder' => OgRoleListBuilder::class,
    'form' => [
      'default' => OgRoleForm::class,
      'delete' => OgRoleDeleteForm::class,
    ],
  ],
  links: [
    'collection' => '/admin/config/group/roles/{entity_type_id}/{bundle_id}',
    'add-form' => '/admin/config/group/roles/{entity_type_id}/{bundle_id}/add',
    'edit-form' => '/admin/config/group/role/{og_role}/edit',
    'delete-form' => '/admin/config/group/role/{og_role}/delete',
  ],
  admin_permission: 'administer organic groups',
  label_count: [
    'singular' => '@count OG role',
    'plural' => '@count OG roles',
  ],
  config_export: [
    'id',
    'label',
    'weight',
    'is_admin',
    'group_type',
    'group_bundle',
    'group_id',
    'permissions',
    'role_type',
  ],
)]
class OgRole extends ConfigEntityBase implements OgRoleInterface {

  /**
   * The machine name of this role.
   */
  protected string $id;

  /**
   * The human-readable label of this role.
   */
  protected ?string $label;

  /**
   * The role name.
   */
  protected string $name;

  /**
   * Whether the parent entity we depend on is being removed.
   */
  protected bool $parentEntityIsBeingRemoved = FALSE;

  /**
   * The weight of this role in administrative listings.
   */
  protected ?int $weight = 0;

  /**
   * The permissions belonging to this role.
   *
   * @var string[]
   */
  protected array $permissions = [];

  /**
   * An indicator whether the role has all permissions.
   */
  protected ?bool $is_admin = FALSE;

  /**
   * Constructs an OgRole object.
   *
   * @param array $values
   *   An array of values to set, keyed by property name.
   */
  public function __construct(array $values) {
    parent::__construct($values, 'og_role');
  }

  /**
   * {@inheritdoc}
   */
  public function getPermissions(): array {
    if ($this->isAdmin()) {
      return [];
    }
    return $this->permissions;
  }

  /**
   * {@inheritdoc}
   */
  public function getWeight() {
    return $this->get('weight');
  }

  /**
   * {@inheritdoc}
   */
  public function setWeight($weight): static {
    $this->set('weight', $weight);
    return $this;
  }

  /**
   * {@inheritdoc}
   */
  public function hasPermission($permission): bool {
    if ($this->isAdmin()) {
      return TRUE;
    }
    return in_array($permission, $this->permissions);
  }

  /**
   * {@inheritdoc}
   */
  #[ActionMethod(adminLabel: new TranslatableMarkup('Add permission to role'))]
  public function grantPermission($permission): static {
    if ($this->isAdmin()) {
      return $this;
    }
    if (!$this->hasPermission($permission)) {
      $this->permissions[] = $permission;
    }
    return $this;
  }

  /**
   * {@inheritdoc}
   */
  public function revokePermission($permission): static {
    if ($this->isAdmin()) {
      return $this;
    }
    $this->permissions = array_diff($this->permissions, [$permission]);
    return $this;
  }

  /**
   * {@inheritdoc}
   */
  public function isAdmin(): bool {
    return (bool) $this->is_admin;
  }

  /**
   * {@inheritdoc}
   */
  public function setIsAdmin($is_admin): static {
    $this->is_admin = $is_admin;
    return $this;
  }

  /**
   * {@inheritdoc}
   */
  public function setId($id): static {
    $this->set('id', $id);
    return $this;
  }

  /**
   * {@inheritdoc}
   */
  public function getLabel(): string {
    return $this->get('label');
  }

  /**
   * {@inheritdoc}
   */
  public function setLabel(string $label): static {
    $this->set('label', $label);
    return $this;
  }

  /**
   * {@inheritdoc}
   */
  public function getGroupType(): ?string {
    return $this->get('group_type');
  }

  /**
   * {@inheritdoc}
   */
  public function setGroupType(string $group_type): static {
    $this->set('group_type', $group_type);
    return $this;
  }

  /**
   * {@inheritdoc}
   */
  public function getGroupBundle(): string {
    return $this->get('group_bundle');
  }

  /**
   * {@inheritdoc}
   */
  public function setGroupBundle($group_bundle): static {
    $this->set('group_bundle', $group_bundle);
    return $this;
  }

  /**
   * {@inheritdoc}
   */
  public function getRoleType(): string {
    return $this->get('role_type') ?: OgRoleInterface::ROLE_TYPE_STANDARD;
  }

  /**
   * {@inheritdoc}
   */
  public function setRoleType(string $role_type): static {
    if (!in_array($role_type, [
      self::ROLE_TYPE_REQUIRED,
      self::ROLE_TYPE_STANDARD,
    ])) {
      throw new \InvalidArgumentException("'$role_type' is not a valid role type.");
    }
    return $this->set('role_type', $role_type);
  }

  /**
   * {@inheritdoc}
   */
  public function isLocked(): bool {
    return $this->get('role_type') !== OgRoleInterface::ROLE_TYPE_STANDARD;
  }

  /**
   * {@inheritdoc}
   */
  public function getName(): ?string {
    // If the name is not set yet, try to derive it from the ID.
    if (empty($this->name) && $this->id() && $this->getGroupType() && $this->getGroupBundle()) {
      // Check if the ID matches the pattern '{entity type}-{bundle}-{name}'.
      $pattern = preg_quote("{$this->getGroupType()}-{$this->getGroupBundle()}-");
      preg_match("/$pattern(.+)/", $this->id(), $matches);
      if (!empty($matches[1])) {
        $this->setName($matches[1]);
      }
    }
    return $this->get('name');
  }

  /**
   * {@inheritdoc}
   */
  public function setName(string $name): static {
    $this->name = $name;
    return $this;
  }

  /**
   * {@inheritdoc}
   */
  public static function postLoad(EntityStorageInterface $storage, array &$entities): void {
    parent::postLoad($storage, $entities);
    // Sort the queried roles by their weight.
    // See \Drupal\Core\Config\Entity\ConfigEntityBase::sort().
    uasort($entities, [static::class, 'sort']);
  }

  /**
   * {@inheritdoc}
   */
  public function preSave(EntityStorageInterface $storage): void {
    parent::preSave($storage);

    if (!isset($this->weight) && ($roles = $storage->loadMultiple())) {
      // Set a role weight to make this new role last.
      $max = array_reduce($roles, function ($max, $role) {
        return max($max, $role->weight);
      });
      $this->weight = $max + 1;
    }

    if (!$this->isSyncing() && $this->hasTrustedData()) {
      // Permissions are always ordered alphabetically to avoid conflicts in the
      // exported configuration. If the save is not trusted then the
      // configuration will be sorted by StorableConfigBase.
      sort($this->permissions);
    }
  }

  /**
   * {@inheritdoc}
   */
  public static function loadByGroupAndName(ContentEntityInterface $group, string $name): ?static {
    $role_id = "{$group->getEntityTypeId()}-{$group->bundle()}-$name";
    return self::load($role_id);
  }

  /**
   * {@inheritdoc}
   */
  public static function loadByGroupType(string $group_entity_type_id, string $group_bundle_id): array {
    $properties = [
      'group_type' => $group_entity_type_id,
      'group_bundle' => $group_bundle_id,
    ];
    return \Drupal::entityTypeManager()->getStorage('og_role')->loadByProperties($properties);
  }

  /**
   * {@inheritdoc}
   */
  public function save() {
    // The ID of a new OgRole has to consist of the entity type ID, bundle ID
    // and role name, separated by dashes.
    if ($this->isNew() && $this->id()) {
      $pattern = preg_quote("{$this->getGroupType()}-{$this->getGroupBundle()}-{$this->getName()}");
      if (!preg_match("/$pattern/", $this->id())) {
        throw new ConfigValueException('The ID should consist of the group entity type ID, group bundle ID and role name, separated by dashes.');
      }
    }

    // If a new OgRole is saved and the ID is not set, construct the ID from
    // the entity type ID, bundle ID and role name.
    if ($this->isNew() && !$this->id()) {
      if (!$this->getGroupType()) {
        throw new ConfigValueException('The group type can not be empty.');
      }

      if (!$this->getGroupBundle()) {
        throw new ConfigValueException('The group bundle can not be empty.');
      }

      if (!$this->getName()) {
        throw new ConfigValueException('The role name can not be empty.');
      }

      // When assigning a role to group we need to add a prefix to the ID in
      // order to prevent duplicate IDs.
      $prefix = $this->getGroupType() . '-' . $this->getGroupBundle() . '-';

      $this->setId($prefix . $this->getName());
    }

    return parent::save();
  }

  /**
   * {@inheritdoc}
   */
  public function set($property_name, $value) {
    // Prevent the ID, role type, group ID, group entity type or bundle from
    // being changed once they are set. These properties are required and
    // shouldn't be tampered with.
    $is_locked_property = in_array($property_name, [
      'id',
      'role_type',
      'group_id',
      'group_type',
      'group_bundle',
    ]);

    if (!$is_locked_property || $this->isNew()) {
      return parent::set($property_name, $value);
    }

    if ($this->get($property_name) == $value) {
      // Locked property hasn't changed, so we can return early.
      return $this;
    }

    throw new OgRoleException("The $property_name cannot be changed.");
  }

  /**
   * {@inheritdoc}
   */
  public function delete(): void {
    // The default roles are required. Prevent them from being deleted for as
    // long as the group still exists, unless the group itself is in the process
    // of being removed.
    if (!$this->parentEntityIsBeingRemoved && $this->isRequired() && $this->groupTypeManager()->isGroup($this->getGroupType(), $this->getGroupBundle())) {
      throw new OgRoleException('The default roles "non-member" and "member" cannot be deleted.');
    }

    parent::delete();
  }

  /**
   * {@inheritdoc}
   */
  public function isRequired(): bool {
    return static::getRoleTypeByName($this->getName()) === OgRoleInterface::ROLE_TYPE_REQUIRED;
  }

  /**
   * Maps role names to role types.
   *
   * The 'anonymous' and 'authenticated' roles should not be changed or deleted.
   * All others are standard roles.
   *
   * @param string $role_name
   *   The role name for which to return the type.
   *
   * @return string
   *   The role type, either OgRoleInterface::ROLE_TYPE_REQUIRED or
   *   OgRoleInterface::ROLE_TYPE_STANDARD.
   */
  public static function getRoleTypeByName(string $role_name): string {
    return in_array($role_name, [
      OgRoleInterface::ANONYMOUS,
      OgRoleInterface::AUTHENTICATED,
    ]) ? OgRoleInterface::ROLE_TYPE_REQUIRED : OgRoleInterface::ROLE_TYPE_STANDARD;
  }

  /**
   * {@inheritdoc}
   */
  public static function getRole(string $entity_type_id, string $bundle, string $role_name): ?static {
    return self::load($entity_type_id . '-' . $bundle . '-' . $role_name);
  }

  /**
   * Gets the group manager.
   */
  protected function groupTypeManager(): GroupTypeManagerInterface {
    // Returning the group manager by calling the global factory method might
    // seem less than ideal, but Entity classes are not designed to work with
    // proper dependency injection. The ::create() method only accepts a $values
    // array, which is not compatible with ContainerInjectionInterface.
    // See for example Entity::uuidGenerator() in the base Entity class, it
    // also uses this pattern.
    return \Drupal::service('og.group_type_manager');
  }

  /**
   * Gets the OG access service.
   */
  protected function ogAccess(): OgAccessInterface {
    return \Drupal::service('og.access');
  }

  /**
   * {@inheritdoc}
   */
  public function calculateDependencies() {
    parent::calculateDependencies();

    // OG doesn't need to validate the existence of each role-assigned
    // permission.
    // @see https://www.drupal.org/node/3193348
    // Create a dependency on the group bundle.
    $bundle_config_dependency = \Drupal::entityTypeManager()->getDefinition($this->getGroupType())->getBundleConfigDependency($this->getGroupBundle());
    $this->addDependency($bundle_config_dependency['type'], $bundle_config_dependency['name']);

    return $this;
  }

  /**
   * {@inheritdoc}
   */
  public function onDependencyRemoval(array $dependencies): bool {
    // The parent entity we depend on is being removed. Set a flag so we can
    // allow removal of required roles.
    $this->parentEntityIsBeingRemoved = TRUE;

    $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 role 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;
  }

}

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc