local_translation-8.x-1.x-dev/modules/local_translation_content/src/Plugin/LocalTranslationAccessRulesPluginManager.php
modules/local_translation_content/src/Plugin/LocalTranslationAccessRulesPluginManager.php
<?php namespace Drupal\local_translation_content\Plugin; use Drupal\Core\Access\AccessResult; use Drupal\Core\Cache\CacheBackendInterface; use Drupal\Core\Entity\ContentEntityInterface; use Drupal\Core\Extension\ModuleHandlerInterface; use Drupal\Core\Language\LanguageInterface; use Drupal\Core\Plugin\DefaultPluginManager; /** * Class LocalTranslationAccessRulesPluginManager. * * @package Drupal\local_translation_content\Access */ class LocalTranslationAccessRulesPluginManager extends DefaultPluginManager { /** * Supported operations array. * * @var array */ protected $supportedOperations; /** * Route match. * * @var \Drupal\Core\Routing\RouteMatchInterface */ protected $routeMatch; /** * Current user. * * @var \Drupal\Core\Session\AccountProxyInterface */ protected $currentUser; /** * Flag about enabled permissions checking. * * @var bool */ protected $isEnabled; /** * Entity type manager. * * @var \Drupal\Core\Entity\EntityTypeManagerInterface */ protected $entityTypeManager; /** * Language manager. * * @var \Drupal\Core\Language\LanguageManagerInterface */ protected $languageManager; /** * Config factory. * * @var \Drupal\Core\Config\ConfigFactoryInterface */ protected $configFactory; /** * Local Translation user skills service. * * @var \Drupal\local_translation\Services\LocalTranslationUserSkills */ protected $userSkills; /** * {@inheritdoc} */ public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler) { parent::__construct( 'Plugin/LocalTranslationAccessRules', $namespaces, $module_handler, 'Drupal\local_translation_content\Plugin\LocalTranslationAccessRulesInterface', 'Drupal\local_translation_content\Annotation\LocalTranslationAccessRule' ); $this->supportedOperations = ['update', 'delete', 'create']; $this->alterInfo('local_translation_content_access_rules_info'); $this->setCacheBackend($cache_backend, 'local_translation_content_access_rules_info_plugins'); list($this->userSkills, $this->languageManager, $this->entityTypeManager, $this->configFactory, $this->routeMatch, $this->currentUser) = array_reverse(func_get_args()); $this->isEnabled = (bool) $this->configFactory ->get('local_translation.settings') ->get('enable_local_translation_content_permissions'); } /** * Check user access for the specified operation and language. * * @param string $operation * Operation name. * @param \Drupal\Core\Entity\ContentEntityInterface|null $entity * Entity to be operated on. * @param string|null $langcode * Language ID. * * @return \Drupal\Core\Access\AccessResult|\Drupal\Core\Access\AccessResultAllowed|\Drupal\Core\Access\AccessResultForbidden|\Drupal\Core\Access\AccessResultNeutral * Access result. * * @throws \Drupal\Component\Plugin\Exception\PluginException */ public function checkAccess($operation, $entity = NULL, $langcode = NULL) { // Workaround for admins. if ($this->isAdmin()) { return AccessResult::allowed() ->cachePerPermissions(); } // Skip any non-supported operations. if (!in_array($operation, $this->supportedOperations)) { return AccessResult::neutral() ->cachePerPermissions(); } // If the permissions checking is disabled - re-use the core's flow. if (!$this->isEnabled) { // If the entity exists - inherit the core's way to check // the translation permissions. if ($entity instanceof ContentEntityInterface) { // Get entity base info. $entity_type_id = $entity->getEntityTypeId(); $bundle = $entity->bundle(); // Get entity's access callback. $definition = $this->entityTypeManager->getDefinition($entity_type_id); $translation = $definition->get('translation'); $access_callback = $translation['content_translation']['access_callback']; $access = call_user_func($access_callback, $entity); if ($access->isAllowed()) { return $access; } // Check "translate any entity" permission. if ($this->currentUser->hasPermission('translate any entity')) { return AccessResult::allowed()->cachePerPermissions(); } // Check per entity permission. $permission = "translate {$entity_type_id}"; if ($definition->getPermissionGranularity() == 'bundle') { $permission = "translate {$bundle} {$entity_type_id}"; } $access = AccessResult::allowedIfHasPermission($this->currentUser, $permission); if ($access->isAllowed()) { return $access; } // Check for entity operation permission. return AccessResult::allowedIfHasPermission($this->currentUser, "$operation content translations"); } } $definitions = $this->getDefinitions(); // If there are no rules or no entity - skip checking. if (empty($definitions) || !$entity instanceof ContentEntityInterface) { // Workaround for "create" operation. if ($operation === 'create') { return $this->processCreateOperation(); } return AccessResult::neutral() ->cachePerPermissions(); } // Workaround for user edit form access. if ($entity->getEntityTypeId() === 'user' && !$this->currentUser->isAnonymous()) { if ($entity->id() === $this->currentUser->id() && $operation === 'update') { // Just return the same code as in User module. // @see \Drupal\user\UserAccessControlHandler::checkAccess(). return AccessResult::allowed()->cachePerUser(); } } // Additional workaround for the "create translation" operation. if ($operation === 'create' && $entity instanceof ContentEntityInterface) { return $this->processCreateTranslationOperation(); } // Try to find at least one rule that allowing user to access. foreach ($definitions as $id => $definition) { /** @var \Drupal\local_translation_content\Plugin\LocalTranslationAccessRulesInterface $instance */ $instance = $this->createInstance($id, $definition); if ($instance->isAllowed($operation, $entity, $langcode)) { return AccessResult::allowed() ->cachePerPermissions(); } } return AccessResult::forbidden() ->cachePerPermissions(); } /** * Process entity create translation operation access. * * @return \Drupal\Core\Access\AccessResult * Access result object. */ protected function processCreateTranslationOperation() { // Fetch source language object from the route params. $source = $this->routeMatch->getParameter('source'); // Prepare array with all "from" translation skills of the current user. $from = $this->userSkills->getSourceSkills(); // Fallback for the non-specified source language. // In this case we gonna use the entity's default translation language. if (!$source instanceof LanguageInterface) { $entity = $this->routeMatch->getParameter('node'); if ($entity instanceof ContentEntityInterface) { foreach ($from as $langcode) { if (!$entity->hasTranslation($langcode)) { continue; } $source = $this->languageManager->getLanguage($langcode); break; } if (!$source instanceof LanguageInterface) { $source = $entity->getUntranslated()->language(); } } } // Allow access if the source translation language is exists // in the user's "from" translation skills array. return AccessResult::allowedIf( $source instanceof LanguageInterface && !empty($from) && in_array($source->getId(), $from) )->cachePerPermissions(); } /** * Process entity create operation access. * * @return \Drupal\Core\Access\AccessResult * Access result object. */ protected function processCreateOperation() { $langcode = $this->languageManager->getCurrentLanguage()->getId(); foreach ($this->entityTypeManager->getDefinitions() as $entity_type) { if ($entity_type->getPermissionGranularity() === 'bundle') { $bundle_key = $entity_type->getBundleEntityType(); if ($bundle = $this->routeMatch->getParameter($bundle_key)) { return AccessResult::allowedIfHasPermission( $this->currentUser, "local_translation_content create {$bundle->id()} content" )->andIf( AccessResult::allowedIf($this->userSkills->userHasSkill($langcode)) )->cachePerPermissions(); } } } return AccessResult::neutral() ->cachePerPermissions(); } /** * Check whether the current user is admin or not. * * @return bool * TRUE - if current user is admin, FALSE otherwise. * * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException */ protected function isAdmin() { // Prevent any checking for anonymous users. if ($this->currentUser->isAnonymous()) { return FALSE; } // Load all admin roles. $admin_roles = $this->entityTypeManager ->getStorage('user_role') ->getQuery() ->condition('is_admin', TRUE, '=') ->execute(); // Get roles of current users. $roles = $this->currentUser->getRoles(TRUE); return !empty(array_intersect($roles, $admin_roles)); } }