Skip to content

Commit

Permalink
Add bundle permissions for content entity (#4138)
Browse files Browse the repository at this point in the history
* 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.
  • Loading branch information
clemens-tolboom authored and enzolutions committed Sep 6, 2019
1 parent e03be58 commit 3c83f02
Show file tree
Hide file tree
Showing 6 changed files with 207 additions and 1 deletion.
20 changes: 19 additions & 1 deletion src/Command/Generate/EntityContentCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,16 @@ protected function configure()
null,
InputOption::VALUE_NONE,
$this->trans('commands.generate.entity.content.options.has-owner')
)->setAliases(['geco']);
);

$this->addOption(
'has-bundle-permissions',
null,
InputOption::VALUE_NONE,
$this->trans('commands.generate.entity.content.options.has-bundle-permissions')
);

$this->setAliases(['geco']);
}

/**
Expand Down Expand Up @@ -156,6 +165,13 @@ protected function interact(InputInterface $input, OutputInterface $output)
true
);
$input->setOption('has-owner', $has_owner);

// --has-bundle-permissions
$has_bundle_permissions = $this->getIo()->confirm(
$this->trans('commands.generate.entity.content.questions.has-bundle-permissions'),
true
);
$input->setOption('has-bundle-permissions', $has_bundle_permissions);
}

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

$generator = $this->generator;

Expand All @@ -193,6 +210,7 @@ protected function execute(InputInterface $input, OutputInterface $output)
'revisionable' => $revisionable,
'has_forms' => $has_forms,
'has_owner' => $has_owner,
'has_bundle_permissions' => $has_bundle_permissions,
]);

if ($has_bundles) {
Expand Down
9 changes: 9 additions & 0 deletions src/Generator/EntityContentGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ public function generate(array $parameters)
$is_translatable = $parameters['is_translatable'];
$revisionable = $parameters['revisionable'];
$has_forms = $parameters['has_forms'];
$has_bundle_permissions = $parameters['has_bundle_permissions'];

$moduleInstance = $this->extensionManager->getModule($module);
$moduleDir = $moduleInstance->getPath();
Expand All @@ -82,6 +83,14 @@ public function generate(array $parameters)
FILE_APPEND
);

if ($has_bundle_permissions) {
$this->renderFile(
'module/src/entity-content-bundle-permissions.php.twig',
$moduleSourcePath . 'Permissions.php',
$parameters
);
}

$this->renderFile(
'module/src/accesscontrolhandler-entity-content.php.twig',
$moduleSourcePath . 'AccessControlHandler.php',
Expand Down
5 changes: 5 additions & 0 deletions templates/module/permissions-entity-content.yml.twig
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,8 @@ delete all {{ label|lower }} revisions:
title: 'Delete all revisions'
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>.'
{% endif %}

{% if has_bundle_permissions %}
permission_callbacks:
- \Drupal\{{ module }}\{{ entity_class}}Permissions::generatePermissions
{% endif %}
3 changes: 3 additions & 0 deletions templates/module/src/Entity/entity-content.php.twig
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,9 @@ use Drupal\user\UserInterface;
* revision_data_table = "{{ entity_name }}_field_revision",
{% endif %}
* translatable = {{ is_translatable ? 'TRUE' : 'FALSE' }},
{% if has_bundle_permissions %}
* permission_granularity = "bundle",
{% endif %}
* admin_permission = "administer {{ label|lower }} entities",
* entity_keys = {
* "id" = "id",
Expand Down
79 changes: 79 additions & 0 deletions templates/module/src/accesscontrolhandler-entity-content.php.twig
Original file line number Diff line number Diff line change
Expand Up @@ -28,17 +28,49 @@ class {{ entity_class }}AccessControlHandler extends EntityAccessControlHandler
*/
protected function checkAccess(EntityInterface $entity, $operation, AccountInterface $account) {
/** @var \Drupal\{{ module }}\Entity\{{ entity_class }}Interface $entity */

switch ($operation) {

case 'view':

if (!$entity->isPublished()) {
{% if has_bundle_permissions %}
$permission = $this->checkOwn($entity, 'view unpublished', $account);
if (!empty($permission)) {
return AccessResult::allowed();
}

{% endif %}
return AccessResult::allowedIfHasPermission($account, 'view unpublished {{ label|lower }} entities');
}

{% if has_bundle_permissions %}
$permission = $this->checkOwn($entity, $operation, $account);
if (!empty($permission)) {
return AccessResult::allowed();
}
{% endif %}

return AccessResult::allowedIfHasPermission($account, 'view published {{ label|lower }} entities');

case 'update':

{% if has_bundle_permissions %}
$permission = $this->checkOwn($entity, $operation, $account);
if (!empty($permission)) {
return AccessResult::allowed();
}
{% endif %}
return AccessResult::allowedIfHasPermission($account, 'edit {{ label|lower }} entities');

case 'delete':

{% if has_bundle_permissions %}
$permission = $this->checkOwn($entity, $operation, $account);
if (!empty($permission)) {
return AccessResult::allowed();
}
{% endif %}
return AccessResult::allowedIfHasPermission($account, 'delete {{ label|lower }} entities');
}

Expand All @@ -52,4 +84,51 @@ class {{ entity_class }}AccessControlHandler extends EntityAccessControlHandler
protected function checkCreateAccess(AccountInterface $account, array $context, $entity_bundle = NULL) {
return AccessResult::allowedIfHasPermission($account, 'add {{ label|lower }} entities');
}

{% if has_bundle_permissions %}
/**
* Test for given 'own' permission.
*
* @param \Drupal\Core\Entity\EntityInterface $entity
* @param $operation
* @param \Drupal\Core\Session\AccountInterface $account
*
* @return string|null
* The permission string indicating it's allowed.
*/
protected function checkOwn(EntityInterface $entity, $operation, AccountInterface $account) {
$status = $entity->isPublished();
$uid = $entity->getOwnerId();

$is_own = $account->isAuthenticated() && $account->id() == $uid;
if (!$is_own) {
return;
}

$bundle = $entity->bundle();

$ops = [
'create' => '%bundle add own %bundle entities',
'view unpublished' => '%bundle view own unpublished %bundle entities',
'view' => '%bundle view own entities',
'update' => '%bundle edit own entities',
'delete' => '%bundle delete own entities',
];
$permission = strtr($ops[$operation], ['%bundle' => $bundle]);

if ($operation === 'view unpublished') {
if (!$status && $account->hasPermission($permission)) {
return $permission;
}
else {
return NULL;
}
}
if ($account->hasPermission($permission)) {
return $permission;
}

return NULL;
}
{% endif %}
{% endblock %}
92 changes: 92 additions & 0 deletions templates/module/src/entity-content-bundle-permissions.php.twig
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@

{% extends "base/class.php.twig" %}

{% block file_path %}
\Drupal\{{ module }}\Entity\{{ entity_class }}.
{% endblock %}

{% block namespace_class %}
namespace Drupal\{{ module }};
{% endblock %}

{% block use_class %}
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\{{ module }}\Entity\{{ entity_class }}Type;

{% endblock %}

{% block class_declaration %}
/**
* Provides dynamic permissions for {{ label }} of different types.
*
* @ingroup {{ module }}
*
*/
class {{ entity_class }}Permissions{% endblock %}

{% block class_methods %}
use StringTranslationTrait;

/**
* Returns an array of node type permissions.
*
* @return array
* The {{ entity_class }} by bundle permissions.
* @see \Drupal\user\PermissionHandlerInterface::getPermissions()
*/
public function generatePermissions() {
$perms = [];

foreach ({{ entity_class }}Type::loadMultiple() as $type) {
$perms += $this->buildPermissions($type);
}

return $perms;
}

/**
* Returns a list of node permissions for a given node type.
*
* @param \Drupal\{{ module }}\Entity\{{ entity_class }}Type $type
* The {{ entity_class }} type.
*
* @return array
* An associative array of permission names and descriptions.
*/
protected function buildPermissions({{ entity_class}}Type $type) {
$type_id = $type->id();
$type_params = ['%type_name' => $type->label()];

return [
"$type_id create entities" => [
'title' => $this->t('Create new %type_name entities', $type_params),
],
"$type_id edit own entities" => [
'title' => $this->t('Edit own %type_name entities', $type_params),
],
"$type_id edit any entities" => [
'title' => $this->t('Edit any %type_name entities', $type_params),
],
"$type_id delete own entities" => [
'title' => $this->t('Delete own %type_name entities', $type_params),
],
"$type_id delete any entities" => [
'title' => $this->t('Delete any %type_name entities', $type_params),
],
{% if revisionable %}
"$type_id view revisions" => [
'title' => $this->t('View %type_name revisions', $type_params),
'description' => t('To view a revision, you also need permission to view the entity item.'),
],
"$type_id revert revisions" => [
'title' => $this->t('Revert %type_name revisions', $type_params),
'description' => t('To revert a revision, you also need permission to edit the entity item.'),
],
"$type_id delete revisions" => [
'title' => $this->t('Delete %type_name revisions', $type_params),
'description' => $this->t('To delete a revision, you also need permission to delete the entity item.'),
],
{% endif %}
];
}
{% endblock %}

0 comments on commit 3c83f02

Please sign in to comment.