-
Notifications
You must be signed in to change notification settings - Fork 16
Return partially populated OgRole entities when gathering default roles #237
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 6 commits
4c64f8f
5ad42f7
4fd7dc8
d3691d7
72b5136
8a2e506
e633578
495eb8e
82dba46
c7b916a
5dffcb4
0f2fef0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -174,11 +174,42 @@ public function setRoleType($role_type) { | |
| return $this->set('role_type', $role_type); | ||
| } | ||
|
|
||
| /** | ||
| * {@inheritdoc} | ||
| */ | ||
| public function getName() { | ||
| // If the name is not set yet, try to derive it from the ID. | ||
| if (empty($this->name) && !empty($this->id())) { | ||
| list(, , $name) = explode('-', $this->id()); | ||
| $this->setName($name); | ||
| } | ||
| return $this->name; | ||
| } | ||
|
|
||
| /** | ||
| * {@inheritdoc} | ||
| */ | ||
| public function setName($name) { | ||
| $this->name = $name; | ||
| return $this; | ||
| } | ||
|
|
||
| /** | ||
| * {@inheritdoc} | ||
| */ | ||
| public function save() { | ||
| if ($this->isNew()) { | ||
| // The ID of a new OgRole has to consist of the entity type ID, bundle ID | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Wouldn't it also be good to be able to have more generic roles? rather than always tied to a group? I am currently trying to use something similar. I have an admin type role, and this is available for on on all groups I create.
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Not sure I understand, but to clarify the logic in OG7 that should be ported to OG8: OgRoles could be either perBundle or perGroupId.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ok, thanks for clarifying @amitaibu. I think I am just using per bundle in that case... :)
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Basically, ignore me please ;)
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes the per-group ID roles are not in yet, these are going to be added in #209. I'm not worrying about them here, that's the responsibility of that ticket.
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @damiankloip if you want to provide a role that is available for all groups, you can provide an event subscriber that simply ignores the passed in group entity type and bundle and returns the role every time, eg: |
||
| // and role name, separated by dashes. | ||
| if ($this->isNew() && !empty($this->id())) { | ||
| list($entity_type_id, $bundle_id, $name) = explode('-', $this->id()); | ||
| if ($entity_type_id !== $this->getGroupType() || $bundle_id !== $this->getGroupBundle() || $name !== $this->getName()) { | ||
| 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() && empty($this->id())) { | ||
| if (empty($this->getGroupType())) { | ||
| throw new ConfigValueException('The group type can not be empty.'); | ||
| } | ||
|
|
@@ -187,6 +218,10 @@ public function save() { | |
| throw new ConfigValueException('The group bundle can not be empty.'); | ||
| } | ||
|
|
||
| if (empty($this->getName())) { | ||
| throw new ConfigValueException('The role name can not be empty.'); | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we seem to have a similar check in
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I had a look at this but it is not really possible I think. The These are different use cases, and calling |
||
| } | ||
|
|
||
| // 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() . '-'; | ||
|
|
@@ -195,7 +230,7 @@ public function save() { | |
| $prefix .= $this->getGroupId() . '-'; | ||
| } | ||
|
|
||
| $this->id = $prefix . $this->id(); | ||
| $this->id = $prefix . $this->getName(); | ||
| } | ||
|
|
||
| parent::save(); | ||
|
|
@@ -234,30 +269,26 @@ public function delete() { | |
| } | ||
|
|
||
| /** | ||
| * Returns default properties for the default OG roles. | ||
| * | ||
| * These are the two roles that are required by every group: the 'member' and | ||
| * 'non-member' roles. | ||
| * | ||
| * All other default roles are provided by DefaultRoleEvent. | ||
| * | ||
| * @return array | ||
| * An array of properties, keyed by OG role. | ||
| * | ||
| * @see \Drupal\og\Event\DefaultRoleEventInterface | ||
| * @see \Drupal\og\GroupManager::getDefaultRoles() | ||
| * {@inheritdoc} | ||
| */ | ||
| public static function getDefaultRoles() { | ||
| return [ | ||
| public static function getDefaultRoleProperties($default_role_name) { | ||
| if (!in_array($default_role_name, [self::ANONYMOUS, self::AUTHENTICATED])) { | ||
| throw new \InvalidArgumentException('Invalid role name.'); | ||
| } | ||
| $default_properties = [ | ||
| self::ANONYMOUS => [ | ||
| 'role_type' => OgRoleInterface::ROLE_TYPE_REQUIRED, | ||
| 'label' => 'Non-member', | ||
| 'name' => self::ANONYMOUS, | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. just to know, so you always use
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The main reason this is adopted in D8 is late static binding.
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. indeed, question is if
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. or am I messing concepts here? :)
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, basically.
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for the info! Fixed it. |
||
| ], | ||
| self::AUTHENTICATED => [ | ||
| 'role_type' => OgRoleInterface::ROLE_TYPE_REQUIRED, | ||
| 'label' => 'Member', | ||
| 'name' => self::AUTHENTICATED, | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed, thanks! |
||
| ], | ||
| ]; | ||
|
|
||
| return $default_properties[$default_role_name]; | ||
| } | ||
|
|
||
| /** | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,6 +2,7 @@ | |
|
|
||
| namespace Drupal\og\Event; | ||
|
|
||
| use Drupal\Core\Entity\EntityTypeManagerInterface; | ||
| use Drupal\og\OgRoleInterface; | ||
| use Symfony\Component\EventDispatcher\Event; | ||
|
|
||
|
|
@@ -21,6 +22,23 @@ class DefaultRoleEvent extends Event implements DefaultRoleEventInterface { | |
| */ | ||
| protected $roles = []; | ||
|
|
||
| /** | ||
| * The OG Role entity storage handler. | ||
| * | ||
| * @var \Drupal\Core\Entity\EntityStorageInterface | ||
| */ | ||
| protected $ogRoleStorage; | ||
|
|
||
| /** | ||
| * Constructs a DefaultRoleEvent object. | ||
| * | ||
| * @var \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager | ||
| * The entity type manager. | ||
| */ | ||
| public function __construct(EntityTypeManagerInterface $entity_type_manager) { | ||
| $this->ogRoleStorage = $entity_type_manager->getStorage('og_role'); | ||
| } | ||
|
|
||
| /** | ||
| * {@inheritdoc} | ||
| */ | ||
|
|
@@ -41,43 +59,46 @@ public function getRoles() { | |
| /** | ||
| * {@inheritdoc} | ||
| */ | ||
| public function addRole($name, array $properties) { | ||
| if (array_key_exists($name, $this->roles)) { | ||
| throw new \InvalidArgumentException("The '$name' role already exists."); | ||
| } | ||
| $this->validate($name, $properties); | ||
| public function addRole(array $properties) { | ||
| $this->validate($properties); | ||
|
|
||
| // Provide default value for the role type. | ||
| if (empty($properties['role_type'])) { | ||
| $properties['role_type'] = OgRoleInterface::ROLE_TYPE_STANDARD; | ||
| if (array_key_exists($properties['name'], $this->roles)) { | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this is still a bit weird that See comment I'll add in OgEventSubscriber::provideDefaultRoles ;) |
||
| throw new \InvalidArgumentException("The '{$properties['name']}' role already exists."); | ||
| } | ||
|
|
||
| $this->roles[$name] = $properties; | ||
| // Provide default values. | ||
| $properties += [ | ||
| 'role_type' => OgRoleInterface::ROLE_TYPE_STANDARD, | ||
| 'is_admin' => FALSE, | ||
| ]; | ||
|
|
||
| $this->roles[$properties['name']] = $this->ogRoleStorage->create($properties); | ||
| } | ||
|
|
||
| /** | ||
| * {@inheritdoc} | ||
| */ | ||
| public function addRoles(array $roles) { | ||
| foreach ($roles as $role => $properties) { | ||
| $this->addRole($role, $properties); | ||
| $this->addRole($properties); | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * {@inheritdoc} | ||
| */ | ||
| public function setRole($name, array $properties) { | ||
| $this->deleteRole($name); | ||
| $this->addRole($name, $properties); | ||
| public function setRole(array $properties) { | ||
| $this->validate($properties); | ||
| $this->deleteRole($properties['name']); | ||
| $this->addRole($properties); | ||
| } | ||
|
|
||
| /** | ||
| * {@inheritdoc} | ||
| */ | ||
| public function setRoles(array $roles) { | ||
| foreach ($roles as $name => $properties) { | ||
| $this->setRole($name, $properties); | ||
| $this->setRole($properties); | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -106,7 +127,11 @@ public function offsetGet($key) { | |
| * {@inheritdoc} | ||
| */ | ||
| public function offsetSet($key, $value) { | ||
| $this->setRole($key, $value); | ||
| $this->validate($value); | ||
| if ($value['name'] !== $key) { | ||
| throw new \InvalidArgumentException('The key and the "name" property of the role should be identical.'); | ||
| } | ||
| $this->setRole($value); | ||
| } | ||
|
|
||
| /** | ||
|
|
@@ -130,27 +155,36 @@ public function getIterator() { | |
| return new \ArrayIterator($this->roles); | ||
| } | ||
|
|
||
| /** | ||
| * {@inheritdoc} | ||
| */ | ||
| public function reset() { | ||
| $this->roles = []; | ||
| } | ||
|
|
||
| /** | ||
| * Validates a role that is about to be set or added. | ||
| * | ||
| * @param string $name | ||
| * The name of the role to add or set. | ||
| * @param array $properties | ||
| * The role properties to validate. | ||
| * | ||
| * @throws \InvalidArgumentException | ||
| * Thrown when the role name is empty, the 'label' property is missing, or | ||
| * the 'role_type' property is invalid. | ||
| */ | ||
| protected function validate($name, $properties) { | ||
| if (empty($name)) { | ||
| protected function validate($properties) { | ||
| if (empty($properties['name'])) { | ||
| throw new \InvalidArgumentException('Role name is required.'); | ||
| } | ||
|
|
||
| if (empty($properties['label'])) { | ||
| throw new \InvalidArgumentException('The label property is required.'); | ||
| } | ||
|
|
||
| if (!empty($properties['is_admin']) && !is_bool($properties['is_admin'])) { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Core roles don't usually do this. Relies on just casting it in isAdmin. Not sure if this is needed or not. Or it would cause more trouble with forms? Wouldn't the config schema help enforce this?
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I added it because this data is sourced from event subscribers, I thought it would help people making mistakes in their subscribers. I can remove it if you want.
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Removed it. |
||
| throw new \InvalidArgumentException('The is_admin property should be a boolean.'); | ||
| } | ||
|
|
||
| $valid_role_types = [ | ||
| OgRoleInterface::ROLE_TYPE_STANDARD, | ||
| OgRoleInterface::ROLE_TYPE_REQUIRED, | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -21,6 +21,10 @@ interface DefaultRoleEventInterface extends \ArrayAccess, \IteratorAggregate { | |
| * @param $name | ||
| * The name of the role to return. | ||
| * | ||
| * @return \Drupal\og\Entity\OgRole | ||
| * The OgRole entity. Note that we cannot specify OgRoleInterface here | ||
| * because of limitations in interface inheritance in PHP 5. | ||
| * | ||
| * @throws \InvalidArgumentException | ||
| * Thrown when the role with the given name does not exist. | ||
| */ | ||
|
|
@@ -37,20 +41,21 @@ public function getRoles(); | |
| /** | ||
| * Adds a default role. | ||
| * | ||
| * @param string $name | ||
| * The name of the role to add. | ||
| * @param array $properties | ||
| * An associative array of role properties, keyed by the following: | ||
| * - 'name': The machine name of the role. | ||
| * - 'label': The human readable label. | ||
| * - 'role_type': Either OgRoleInterface::ROLE_TYPE_STANDARD or | ||
| * OgRoleInterface::ROLE_TYPE_REQUIRED. Defaults to | ||
| * OgRoleInterface::ROLE_TYPE_STANDARD. | ||
| * - 'is_admin': Whether or not the role is an administration role. Defaults | ||
| * to FALSE. | ||
| * | ||
| * @throws \InvalidArgumentException | ||
| * Thrown when the role that is added already exists, when the role name is | ||
| * empty, or when the 'label' property is missing. | ||
| */ | ||
| public function addRole($name, array $properties); | ||
| public function addRole(array $properties); | ||
|
|
||
| /** | ||
| * Adds multiple default roles. | ||
|
|
@@ -63,17 +68,21 @@ public function addRoles(array $roles); | |
| /** | ||
| * Sets a default roles. | ||
| * | ||
| * @param string $name | ||
| * The name of the role to set. | ||
| * @param array $properties | ||
| * An associative array of role properties to set, keyed by the following: | ||
| * - 'name': The machine name of the role. | ||
| * - 'label': The human readable label. | ||
| * - 'role_type': Either OgRoleInterface::ROLE_TYPE_STANDARD or | ||
| * OgRoleInterface::ROLE_TYPE_REQUIRED. Defaults to | ||
| * OgRoleInterface::ROLE_TYPE_STANDARD. | ||
| * - 'is_admin': Whether or not the role is an administration role. Defaults | ||
| * to FALSE. | ||
| * | ||
| * @throws \InvalidArgumentException | ||
| * Thrown when the role name is empty, or when the 'label' property is | ||
| * missing. | ||
| */ | ||
| public function setRole($name, array $properties); | ||
| public function setRole(array $properties); | ||
|
|
||
| /** | ||
| * Sets multiple default roles. | ||
|
|
@@ -95,11 +104,18 @@ public function deleteRole($name); | |
| * Returns whether or not the given role exists. | ||
| * | ||
| * @param string $name | ||
| * The name of the role for which to verify the existance. | ||
| * The name of the role for which to verify the existence. | ||
| * | ||
| * @return bool | ||
| * TRUE if the role exists, FALSE otherwise. | ||
| */ | ||
| public function hasRole($name); | ||
|
|
||
| /** | ||
| * Resets the internal static cache. | ||
| * | ||
| * Call this before dispatching the event. | ||
| */ | ||
| public function reset(); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. huh? This seems like a weird thing to have to do if you're dispatching an event?
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it's strange too, but this is a so called "ContainerAware" event, which is a service that is lazy loaded when first requested and then persists in the container (much like a singleton). This has the side effect that if a second round is done the previous results are still there. In normal use it would probably be rare (but not impossible) to fire the event listener twice, but this happens all the time in tests. The I was looking for an issue about it on the Symfony but there are only 4 open issues mentioning the container aware event dispatcher. As an alternative to doing a manual reset, we can also cache the data locally per entity type and bundle.
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I opted to keep the
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The |
||
|
|
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -69,7 +69,11 @@ public function provideDefaultOgPermissions(PermissionEventInterface $event) { | |
| * The default role event. | ||
| */ | ||
| public function provideDefaultRoles(DefaultRoleEventInterface $event) { | ||
| $event->addRole(OgRoleInterface::ADMINISTRATOR, ['label' => 'Administrator']); | ||
| $event->addRole([ | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How about something like public function provideDefaultRoles(DefaultRoleEventInterface $event) {
$role = $this->ogRoleStorage->create(['name' => OgRoleInterface::ADMINISTRATOR);
// Letting OgRole do it's job ...
$role->setLabel('Administrator')
->setAdmin();
$event->addRole($role);
}Now our event's addRole doesn't need to know much about the role -- it's just passing it on. It's up to OgRole::save() to validate nothing is wrong.
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I mean if I understand correctly (and correct me if I don't 😉) - I think your goal is to prevent mistakes in the default definition, but it kind of causes double validations - one in OgRole and one in the event.
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. OK! I get your point. This seems like a good plan. I wanted to make it "easy" to implement a subscriber, but your idea makes sense. Is it acceptable for you to change this in a followup?
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Unless it is going to have a negative effect on your moral, I think it's better to have this change here. I'd prefer not to introduce a complexity where we know there's a simpler solution.
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No not at all :) I'll fix it tomorrow |
||
| 'name' => OgRoleInterface::ADMINISTRATOR, | ||
| 'label' => 'Administrator', | ||
| 'is_admin' => TRUE, | ||
| ]); | ||
| } | ||
|
|
||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems slightly limited. Maybe it would be better to explode, but pop off the last element and use that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There should always be three elements in the current implementation. But I think you're on to something, this is indeed a bug. I don't think there is anything stopping people to use dashes in their entity type IDs and bundle IDs, which means that counting the dashes is not going to cut it.
I'll change it to match exactly
"{$this->getGroupEntityTypeId()}-{$this->getGroupBundleId()}-$role_name", then we'll eliminate any dash-related problems.