Skip to content

Commit 3c83f02

Browse files
clemens-tolboomenzolutions
authored andcommitted
Add bundle permissions for content entity (#4138)
* Add command option has-bundle-permissions and use on entity. * Add (Entity}Permissions.php file. * Add permission_callback to permissions.yml file. * Added fix from #4139 as it hurts. * Add own permissions checks.
1 parent e03be58 commit 3c83f02

6 files changed

+207
-1
lines changed

src/Command/Generate/EntityContentCommand.php

+19-1
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,16 @@ protected function configure()
109109
null,
110110
InputOption::VALUE_NONE,
111111
$this->trans('commands.generate.entity.content.options.has-owner')
112-
)->setAliases(['geco']);
112+
);
113+
114+
$this->addOption(
115+
'has-bundle-permissions',
116+
null,
117+
InputOption::VALUE_NONE,
118+
$this->trans('commands.generate.entity.content.options.has-bundle-permissions')
119+
);
120+
121+
$this->setAliases(['geco']);
113122
}
114123

115124
/**
@@ -156,6 +165,13 @@ protected function interact(InputInterface $input, OutputInterface $output)
156165
true
157166
);
158167
$input->setOption('has-owner', $has_owner);
168+
169+
// --has-bundle-permissions
170+
$has_bundle_permissions = $this->getIo()->confirm(
171+
$this->trans('commands.generate.entity.content.questions.has-bundle-permissions'),
172+
true
173+
);
174+
$input->setOption('has-bundle-permissions', $has_bundle_permissions);
159175
}
160176

161177
/**
@@ -175,6 +191,7 @@ protected function execute(InputInterface $input, OutputInterface $output)
175191
$revisionable = $input->getOption('revisionable');
176192
$has_forms = $input->getOption('has-forms');
177193
$has_owner = $input->getOption('has-owner');
194+
$has_bundle_permissions = $input->getOption('has-bundle-permissions');
178195

179196
$generator = $this->generator;
180197

@@ -193,6 +210,7 @@ protected function execute(InputInterface $input, OutputInterface $output)
193210
'revisionable' => $revisionable,
194211
'has_forms' => $has_forms,
195212
'has_owner' => $has_owner,
213+
'has_bundle_permissions' => $has_bundle_permissions,
196214
]);
197215

198216
if ($has_bundles) {

src/Generator/EntityContentGenerator.php

+9
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ public function generate(array $parameters)
6565
$is_translatable = $parameters['is_translatable'];
6666
$revisionable = $parameters['revisionable'];
6767
$has_forms = $parameters['has_forms'];
68+
$has_bundle_permissions = $parameters['has_bundle_permissions'];
6869

6970
$moduleInstance = $this->extensionManager->getModule($module);
7071
$moduleDir = $moduleInstance->getPath();
@@ -82,6 +83,14 @@ public function generate(array $parameters)
8283
FILE_APPEND
8384
);
8485

86+
if ($has_bundle_permissions) {
87+
$this->renderFile(
88+
'module/src/entity-content-bundle-permissions.php.twig',
89+
$moduleSourcePath . 'Permissions.php',
90+
$parameters
91+
);
92+
}
93+
8594
$this->renderFile(
8695
'module/src/accesscontrolhandler-entity-content.php.twig',
8796
$moduleSourcePath . 'AccessControlHandler.php',

templates/module/permissions-entity-content.yml.twig

+5
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,8 @@ delete all {{ label|lower }} revisions:
3030
title: 'Delete all revisions'
3131
description: 'Role requires permission to <em>view {{ label }} revisions</em> and <em>delete rights</em> for {{ label|lower }} entities in question or <em>administer {{ label|lower }} entities</em>.'
3232
{% endif %}
33+
34+
{% if has_bundle_permissions %}
35+
permission_callbacks:
36+
- \Drupal\{{ module }}\{{ entity_class}}Permissions::generatePermissions
37+
{% endif %}

templates/module/src/Entity/entity-content.php.twig

+3
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,9 @@ use Drupal\user\UserInterface;
7272
* revision_data_table = "{{ entity_name }}_field_revision",
7373
{% endif %}
7474
* translatable = {{ is_translatable ? 'TRUE' : 'FALSE' }},
75+
{% if has_bundle_permissions %}
76+
* permission_granularity = "bundle",
77+
{% endif %}
7578
* admin_permission = "administer {{ label|lower }} entities",
7679
* entity_keys = {
7780
* "id" = "id",

templates/module/src/accesscontrolhandler-entity-content.php.twig

+79
Original file line numberDiff line numberDiff line change
@@ -28,17 +28,49 @@ class {{ entity_class }}AccessControlHandler extends EntityAccessControlHandler
2828
*/
2929
protected function checkAccess(EntityInterface $entity, $operation, AccountInterface $account) {
3030
/** @var \Drupal\{{ module }}\Entity\{{ entity_class }}Interface $entity */
31+
3132
switch ($operation) {
33+
3234
case 'view':
35+
3336
if (!$entity->isPublished()) {
37+
{% if has_bundle_permissions %}
38+
$permission = $this->checkOwn($entity, 'view unpublished', $account);
39+
if (!empty($permission)) {
40+
return AccessResult::allowed();
41+
}
42+
43+
{% endif %}
3444
return AccessResult::allowedIfHasPermission($account, 'view unpublished {{ label|lower }} entities');
3545
}
46+
47+
{% if has_bundle_permissions %}
48+
$permission = $this->checkOwn($entity, $operation, $account);
49+
if (!empty($permission)) {
50+
return AccessResult::allowed();
51+
}
52+
{% endif %}
53+
3654
return AccessResult::allowedIfHasPermission($account, 'view published {{ label|lower }} entities');
3755

3856
case 'update':
57+
58+
{% if has_bundle_permissions %}
59+
$permission = $this->checkOwn($entity, $operation, $account);
60+
if (!empty($permission)) {
61+
return AccessResult::allowed();
62+
}
63+
{% endif %}
3964
return AccessResult::allowedIfHasPermission($account, 'edit {{ label|lower }} entities');
4065

4166
case 'delete':
67+
68+
{% if has_bundle_permissions %}
69+
$permission = $this->checkOwn($entity, $operation, $account);
70+
if (!empty($permission)) {
71+
return AccessResult::allowed();
72+
}
73+
{% endif %}
4274
return AccessResult::allowedIfHasPermission($account, 'delete {{ label|lower }} entities');
4375
}
4476

@@ -52,4 +84,51 @@ class {{ entity_class }}AccessControlHandler extends EntityAccessControlHandler
5284
protected function checkCreateAccess(AccountInterface $account, array $context, $entity_bundle = NULL) {
5385
return AccessResult::allowedIfHasPermission($account, 'add {{ label|lower }} entities');
5486
}
87+
88+
{% if has_bundle_permissions %}
89+
/**
90+
* Test for given 'own' permission.
91+
*
92+
* @param \Drupal\Core\Entity\EntityInterface $entity
93+
* @param $operation
94+
* @param \Drupal\Core\Session\AccountInterface $account
95+
*
96+
* @return string|null
97+
* The permission string indicating it's allowed.
98+
*/
99+
protected function checkOwn(EntityInterface $entity, $operation, AccountInterface $account) {
100+
$status = $entity->isPublished();
101+
$uid = $entity->getOwnerId();
102+
103+
$is_own = $account->isAuthenticated() && $account->id() == $uid;
104+
if (!$is_own) {
105+
return;
106+
}
107+
108+
$bundle = $entity->bundle();
109+
110+
$ops = [
111+
'create' => '%bundle add own %bundle entities',
112+
'view unpublished' => '%bundle view own unpublished %bundle entities',
113+
'view' => '%bundle view own entities',
114+
'update' => '%bundle edit own entities',
115+
'delete' => '%bundle delete own entities',
116+
];
117+
$permission = strtr($ops[$operation], ['%bundle' => $bundle]);
118+
119+
if ($operation === 'view unpublished') {
120+
if (!$status && $account->hasPermission($permission)) {
121+
return $permission;
122+
}
123+
else {
124+
return NULL;
125+
}
126+
}
127+
if ($account->hasPermission($permission)) {
128+
return $permission;
129+
}
130+
131+
return NULL;
132+
}
133+
{% endif %}
55134
{% endblock %}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
2+
{% extends "base/class.php.twig" %}
3+
4+
{% block file_path %}
5+
\Drupal\{{ module }}\Entity\{{ entity_class }}.
6+
{% endblock %}
7+
8+
{% block namespace_class %}
9+
namespace Drupal\{{ module }};
10+
{% endblock %}
11+
12+
{% block use_class %}
13+
use Drupal\Core\StringTranslation\StringTranslationTrait;
14+
use Drupal\{{ module }}\Entity\{{ entity_class }}Type;
15+
16+
{% endblock %}
17+
18+
{% block class_declaration %}
19+
/**
20+
* Provides dynamic permissions for {{ label }} of different types.
21+
*
22+
* @ingroup {{ module }}
23+
*
24+
*/
25+
class {{ entity_class }}Permissions{% endblock %}
26+
27+
{% block class_methods %}
28+
use StringTranslationTrait;
29+
30+
/**
31+
* Returns an array of node type permissions.
32+
*
33+
* @return array
34+
* The {{ entity_class }} by bundle permissions.
35+
* @see \Drupal\user\PermissionHandlerInterface::getPermissions()
36+
*/
37+
public function generatePermissions() {
38+
$perms = [];
39+
40+
foreach ({{ entity_class }}Type::loadMultiple() as $type) {
41+
$perms += $this->buildPermissions($type);
42+
}
43+
44+
return $perms;
45+
}
46+
47+
/**
48+
* Returns a list of node permissions for a given node type.
49+
*
50+
* @param \Drupal\{{ module }}\Entity\{{ entity_class }}Type $type
51+
* The {{ entity_class }} type.
52+
*
53+
* @return array
54+
* An associative array of permission names and descriptions.
55+
*/
56+
protected function buildPermissions({{ entity_class}}Type $type) {
57+
$type_id = $type->id();
58+
$type_params = ['%type_name' => $type->label()];
59+
60+
return [
61+
"$type_id create entities" => [
62+
'title' => $this->t('Create new %type_name entities', $type_params),
63+
],
64+
"$type_id edit own entities" => [
65+
'title' => $this->t('Edit own %type_name entities', $type_params),
66+
],
67+
"$type_id edit any entities" => [
68+
'title' => $this->t('Edit any %type_name entities', $type_params),
69+
],
70+
"$type_id delete own entities" => [
71+
'title' => $this->t('Delete own %type_name entities', $type_params),
72+
],
73+
"$type_id delete any entities" => [
74+
'title' => $this->t('Delete any %type_name entities', $type_params),
75+
],
76+
{% if revisionable %}
77+
"$type_id view revisions" => [
78+
'title' => $this->t('View %type_name revisions', $type_params),
79+
'description' => t('To view a revision, you also need permission to view the entity item.'),
80+
],
81+
"$type_id revert revisions" => [
82+
'title' => $this->t('Revert %type_name revisions', $type_params),
83+
'description' => t('To revert a revision, you also need permission to edit the entity item.'),
84+
],
85+
"$type_id delete revisions" => [
86+
'title' => $this->t('Delete %type_name revisions', $type_params),
87+
'description' => $this->t('To delete a revision, you also need permission to delete the entity item.'),
88+
],
89+
{% endif %}
90+
];
91+
}
92+
{% endblock %}

0 commit comments

Comments
 (0)