View source
<?php
declare (strict_types=1);
namespace Drupal\og\Entity;
use Drupal\Core\Cache\Cache;
use Drupal\Core\Entity\ContentEntityBase;
use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Field\BaseFieldDefinition;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\Core\Language\LanguageInterface;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\og\Og;
use Drupal\og\OgMembershipInterface;
use Drupal\og\OgRoleInterface;
use Drupal\user\EntityOwnerInterface;
use Drupal\user\UserInterface;
class OgMembership extends ContentEntityBase implements OgMembershipInterface {
public function getCreatedTime() : int {
return $this
->getFieldValue('created', 'value') ?: 0;
}
public function setCreatedTime(int $timestamp) : OgMembershipInterface {
$this
->set('created', $timestamp);
return $this;
}
public function getOwner() {
assert(!empty($this
->get('uid')->entity), new \LogicException(__METHOD__ . '() should only be called on loaded memberships, or on newly created memberships that already have the owner set.'));
return $this
->get('uid')->entity;
}
public function getOwnerId() {
$owner_id = $this
->getFieldValue('uid', 'target_id');
assert(!empty($owner_id), new \LogicException(__METHOD__ . '() should only be called on loaded memberships, or on newly created memberships that already have the owner set.'));
return $owner_id;
}
public function setOwner(UserInterface $account) {
$this
->set('uid', $account
->id());
return $this;
}
public function setOwnerId($uid) {
$this
->set('uid', $uid);
return $this;
}
public function setGroup(ContentEntityInterface $group) : OgMembershipInterface {
$this
->set('entity_type', $group
->getEntityTypeId());
$this
->set('entity_bundle', $group
->bundle());
$this
->set('entity_id', $group
->id());
return $this;
}
public function getGroupEntityType() : string {
$entity_type = $this
->getFieldValue('entity_type', 'value') ?: '';
assert(!empty($entity_type), new \LogicException(__METHOD__ . '() should only be called on loaded memberships, or on newly created memberships that already have the group type set.'));
return $entity_type;
}
public function getGroupBundle() : string {
$bundle = $this
->getFieldValue('entity_bundle', 'value') ?: '';
assert(!empty($bundle), new \LogicException(__METHOD__ . '() should only be called on loaded memberships, or on newly created memberships that already have the group bundle set.'));
return $bundle;
}
public function getGroupId() : string {
$entity_id = $this
->getFieldValue('entity_id', 'value') ?: '';
assert(!empty($entity_id), new \LogicException(__METHOD__ . '() should only be called on loaded memberships, or on newly created memberships that already have the group ID set.'));
return $entity_id;
}
protected function hasGroup() : bool {
$has_group = !empty($this
->getFieldValue('entity_type', 'value')) && !empty($this
->getFieldValue('entity_bundle', 'value')) && !empty($this
->getFieldValue('entity_id', 'value'));
return $has_group;
}
public function getGroup() : ?ContentEntityInterface {
$entity_type = $this
->getGroupEntityType();
$entity_id = $this
->getGroupId();
return \Drupal::entityTypeManager()
->getStorage($entity_type)
->load($entity_id);
}
public function setState(string $state) : OgMembershipInterface {
$this
->set('state', $state);
return $this;
}
public function getState() : string {
return $this
->getFieldValue('state', 'value') ?: '';
}
public function getType() : string {
return $this
->bundle();
}
public function addRole(OgRoleInterface $role) : OgMembershipInterface {
$roles = $this
->getRoles();
$roles[] = $role;
return $this
->setRoles($roles);
}
public function revokeRole(OgRoleInterface $role) : OgMembershipInterface {
return $this
->revokeRoleById($role
->id());
}
public function revokeRoleById(string $role_id) : OgMembershipInterface {
$roles = $this
->getRoles();
foreach ($roles as $key => $existing_role) {
if ($existing_role
->id() == $role_id) {
unset($roles[$key]);
break;
}
}
return $this
->setRoles($roles);
}
public function getRoles() : array {
$roles = [];
if ($this
->hasGroup()) {
$roles = [
OgRole::getRole($this
->getGroupEntityType(), $this
->getGroupBundle(), OgRoleInterface::AUTHENTICATED),
];
}
$roles = array_merge($roles, $this
->get('roles')
->referencedEntities());
return $roles;
}
public function setRoles(array $roles = []) : OgMembershipInterface {
$roles = array_filter($roles, function (OgRole $role) {
return !($role
->getName() == OgRoleInterface::AUTHENTICATED);
});
$role_ids = array_map(function (OgRole $role) {
return $role
->id();
}, $roles);
$this
->set('roles', array_unique($role_ids));
return $this;
}
public function getRolesIds() : array {
if (isset($this->fields['roles'][LanguageInterface::LANGCODE_DEFAULT])) {
$values = $this
->get('roles')
->getValue();
}
else {
$values = $this->values['roles'][LanguageInterface::LANGCODE_DEFAULT] ?? [];
}
$roles_ids = array_column($values, 'target_id');
if ($this
->hasGroup()) {
$roles_ids[] = "{$this->getGroupEntityType()}-{$this->getGroupBundle()}-" . OgRoleInterface::AUTHENTICATED;
}
return $roles_ids;
}
public function isRoleValid(OgRoleInterface $role) : bool {
$group = $this
->getGroup();
if (!$group) {
throw new \LogicException('Cannot determine whether a role is valid for a membership that doesn\'t have a group.');
}
if ($role
->getName() == OgRoleInterface::ANONYMOUS) {
return FALSE;
}
elseif ($role
->getGroupType() !== $group
->getEntityTypeId() || $role
->getGroupBundle() !== $group
->bundle()) {
return FALSE;
}
return TRUE;
}
public function hasRole(string $role_id) : bool {
return in_array($role_id, $this
->getRolesIds());
}
public function hasPermission(string $permission) : bool {
if ($this
->isBlocked()) {
return FALSE;
}
return (bool) array_filter($this
->getRoles(), function (OgRole $role) use ($permission) {
return $role
->hasPermission($permission);
});
}
public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
$fields = [];
$fields['id'] = BaseFieldDefinition::create('integer')
->setLabel(new TranslatableMarkup('ID'))
->setDescription(t("The group membership's unique ID."))
->setReadOnly(TRUE)
->setSetting('unsigned', TRUE);
$fields['uuid'] = BaseFieldDefinition::create('uuid')
->setLabel(new TranslatableMarkup('UUID'))
->setDescription(new TranslatableMarkup('The membership UUID.'))
->setReadOnly(TRUE);
$fields['type'] = BaseFieldDefinition::create('entity_reference')
->setLabel(new TranslatableMarkup('Type'))
->setDescription(new TranslatableMarkup('The bundle of the membership'))
->setSetting('target_type', 'og_membership_type');
$fields['uid'] = BaseFieldDefinition::create('entity_reference')
->setLabel(new TranslatableMarkup('Member User ID'))
->setDescription(new TranslatableMarkup('The user ID of the member.'))
->setSetting('target_type', 'user');
$fields['entity_type'] = BaseFieldDefinition::create('string')
->setLabel(new TranslatableMarkup('Group entity type'))
->setDescription(new TranslatableMarkup('The entity type of the group.'));
$fields['entity_bundle'] = BaseFieldDefinition::create('string')
->setLabel(new TranslatableMarkup('Group bundle ID'))
->setDescription(new TranslatableMarkup('The bundle ID of the group.'));
$fields['entity_id'] = BaseFieldDefinition::create('string')
->setLabel(new TranslatableMarkup('Group entity ID'))
->setDescription(new TranslatableMarkup('The entity ID of the group.'));
$fields['state'] = BaseFieldDefinition::create('string')
->setLabel(new TranslatableMarkup('State'))
->setDescription(new TranslatableMarkup('The user membership state: active, pending, or blocked.'))
->setDefaultValue(OgMembershipInterface::STATE_ACTIVE);
$fields['roles'] = BaseFieldDefinition::create('entity_reference')
->setLabel(new TranslatableMarkup('Roles'))
->setDescription(new TranslatableMarkup('The OG roles related to an OG membership entity.'))
->setCardinality(FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED)
->setDisplayOptions('view', [
'label' => 'hidden',
'type' => 'entity_reference_label',
'weight' => 0,
])
->setSetting('target_type', 'og_role');
$fields['created'] = BaseFieldDefinition::create('created')
->setLabel(new TranslatableMarkup('Create'))
->setDescription(new TranslatableMarkup('The Unix timestamp when the membership was created.'));
$fields['language'] = BaseFieldDefinition::create('language')
->setLabel(new TranslatableMarkup('Language'))
->setDescription(new TranslatableMarkup('The {languages}.language of this membership.'));
return $fields;
}
public function preSave(EntityStorageInterface $storage) {
if (!($uid = $this
->get('uid')->target_id)) {
throw new \LogicException('OG membership can not be created for an empty or anonymous user.');
}
if (!($entity_id = $this
->get('entity_id')->value)) {
throw new \LogicException('Membership cannot be set for an empty or an unsaved group.');
}
if (!($group = $this
->getGroup())) {
throw new \LogicException('A group entity is required for creating a membership.');
}
$entity_type_id = $group
->getEntityTypeId();
$bundle = $group
->bundle();
if (!Og::isGroup($entity_type_id, $bundle)) {
throw new \LogicException(sprintf('Entity type %s with ID %s is not an OG group.', $entity_type_id, $group
->id()));
}
foreach ($this
->getRoles() as $role) {
if ($role
->getName() == OgRoleInterface::ANONYMOUS) {
throw new \LogicException('Cannot save an OgMembership with reference to a non-member role.');
}
elseif ($role
->getName() == OgRoleInterface::AUTHENTICATED) {
$this
->revokeRole($role);
}
elseif (!$this
->isRoleValid($role)) {
throw new \LogicException(sprintf('The role with ID %s does not match the group type of the membership.', $role
->id()));
}
}
$query = \Drupal::entityQuery('og_membership');
$query
->condition('uid', $uid)
->condition('entity_id', $entity_id)
->condition('entity_type', $this
->get('entity_type')->value);
if (!$this
->isNew()) {
$query
->condition('id', $this
->id(), '<>');
}
$count = $query
->range(0, 1)
->count()
->execute();
if ($count) {
throw new \LogicException(sprintf('An OG membership already exists for user ID %d and group of entity type %s and ID %s', $uid, $entity_type_id, $group
->id()));
}
parent::preSave($storage);
}
public function save() {
$result = parent::save();
Og::reset();
return $result;
}
protected function invalidateTagsOnSave($update) {
parent::invalidateTagsOnSave($update);
$group = $this
->getGroup();
if (!empty($group)) {
$tags = Cache::buildTags(OgMembershipInterface::GROUP_MEMBERSHIP_LIST_CACHE_TAG_PREFIX, $group
->getCacheTagsToInvalidate());
Cache::invalidateTags($tags);
}
}
protected static function invalidateTagsOnDelete(EntityTypeInterface $entity_type, array $entities) {
parent::invalidateTagsOnDelete($entity_type, $entities);
$tags = [];
foreach ($entities as $entity) {
if ($group = $entity
->getGroup()) {
$tags = Cache::mergeTags(Cache::buildTags(OgMembershipInterface::GROUP_MEMBERSHIP_LIST_CACHE_TAG_PREFIX, $group
->getCacheTagsToInvalidate()), $tags);
}
}
Cache::invalidateTags($tags);
}
public static function create(array $values = []) {
$values += [
'type' => OgMembershipInterface::TYPE_DEFAULT,
];
return parent::create($values);
}
public function isActive() : bool {
return $this
->getState() === OgMembershipInterface::STATE_ACTIVE;
}
public function isPending() : bool {
return $this
->getState() === OgMembershipInterface::STATE_PENDING;
}
public function isBlocked() : bool {
return $this
->getState() === OgMembershipInterface::STATE_BLOCKED;
}
public function isOwner() : bool {
$group = $this
->getGroup();
return $group instanceof EntityOwnerInterface && $group
->getOwnerId() == $this
->getOwnerId();
}
public function getFieldValue($field_name, $property) {
if (!isset($this->fields[$field_name])) {
$field_values = NULL;
if (isset($this->values[$field_name][$this->activeLangcode])) {
$field_values = $this->values[$field_name][$this->activeLangcode];
}
elseif (isset($this->values[$field_name][LanguageInterface::LANGCODE_DEFAULT])) {
$field_values = $this->values[$field_name][LanguageInterface::LANGCODE_DEFAULT];
}
if ($field_values !== NULL) {
if (isset($field_values[0][$property]) && is_array($field_values[0])) {
return $field_values[0][$property];
}
elseif (isset($field_values[$property]) && is_array($field_values)) {
return $field_values[$property];
}
elseif (!is_array($field_values)) {
return $field_values;
}
}
}
return $this
->get($field_name)->{$property};
}
}