From 3b31fe004519ff01a96789bc5d4b95ad80906e2f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Miko=C5=82aj=20Adamczyk?= <mikadamczyk@gmail.com>
Date: Fri, 19 Oct 2018 11:13:37 +0200
Subject: [PATCH] EZP-29734: As an Administrator I want to add Content Type
 limitation for Section/Assign policy

---
 src/bundle/Controller/SectionController.php   |  35 +++++-
 .../views/admin/section/list.html.twig        |   2 +-
 .../Event/Subscriber/SectionAssign.php        | 110 ++++++++++++++++++
 src/lib/Util/PermissionUtil.php               |  38 ++++++
 4 files changed, 183 insertions(+), 2 deletions(-)
 create mode 100644 src/lib/UniversalDiscovery/Event/Subscriber/SectionAssign.php
 create mode 100644 src/lib/Util/PermissionUtil.php

diff --git a/src/bundle/Controller/SectionController.php b/src/bundle/Controller/SectionController.php
index d10824b510..01274f50b6 100644
--- a/src/bundle/Controller/SectionController.php
+++ b/src/bundle/Controller/SectionController.php
@@ -15,9 +15,11 @@
 use eZ\Publish\API\Repository\Values\Content\Query;
 use eZ\Publish\API\Repository\Values\Content\Query\SortClause;
 use eZ\Publish\API\Repository\Values\Content\Section;
+use eZ\Publish\API\Repository\Values\User\Limitation\NewSectionLimitation;
 use eZ\Publish\Core\MVC\Symfony\Security\Authorization\Attribute;
 use eZ\Publish\Core\Pagination\Pagerfanta\ContentSearchAdapter;
 use eZ\Publish\API\Repository\SearchService;
+use eZ\Publish\API\Repository\PermissionResolver;
 use EzSystems\EzPlatformAdminUi\Form\Data\Section\SectionContentAssignData;
 use EzSystems\EzPlatformAdminUi\Form\Data\Section\SectionCreateData;
 use EzSystems\EzPlatformAdminUi\Form\Data\Section\SectionDeleteData;
@@ -29,6 +31,7 @@
 use EzSystems\EzPlatformAdminUi\Form\SubmitHandler;
 use EzSystems\EzPlatformAdminUi\Notification\NotificationHandlerInterface;
 use EzSystems\EzPlatformAdminUi\UI\Service\PathService;
+use EzSystems\EzPlatformAdminUi\Util\PermissionUtil;
 use EzSystems\EzPlatformAdminUiBundle\View\EzPagerfantaView;
 use EzSystems\EzPlatformAdminUiBundle\View\Template\EzPagerfantaTemplate;
 use Pagerfanta\Adapter\ArrayAdapter;
@@ -74,6 +77,12 @@ class SectionController extends Controller
     /** @var \EzSystems\EzPlatformAdminUi\UI\Service\PathService */
     private $pathService;
 
+    /** @var \eZ\Publish\API\Repository\PermissionResolver */
+    private $permissionResolver;
+
+    /** @var \EzSystems\EzPlatformAdminUi\Util\PermissionUtil */
+    private $permissionUtil;
+
     /** @var int */
     private $defaultPaginationLimit;
 
@@ -89,6 +98,8 @@ class SectionController extends Controller
      * @param \eZ\Publish\API\Repository\ContentTypeService $contentTypeService
      * @param \eZ\Publish\API\Repository\LocationService $locationService
      * @param \EzSystems\EzPlatformAdminUi\UI\Service\PathService $pathService
+     * @param \eZ\Publish\API\Repository\PermissionResolver $permissionResolver
+     * @param \EzSystems\EzPlatformAdminUi\Util\PermissionUtil $permissionUtil
      * @param int $defaultPaginationLimit
      */
     public function __construct(
@@ -103,6 +114,8 @@ public function __construct(
         ContentTypeService $contentTypeService,
         LocationService $locationService,
         PathService $pathService,
+        PermissionResolver $permissionResolver,
+        PermissionUtil $permissionUtil,
         int $defaultPaginationLimit
     ) {
         $this->notificationHandler = $notificationHandler;
@@ -117,6 +130,8 @@ public function __construct(
         $this->locationService = $locationService;
         $this->pathService = $pathService;
         $this->defaultPaginationLimit = $defaultPaginationLimit;
+        $this->permissionResolver = $permissionResolver;
+        $this->permissionUtil = $permissionUtil;
     }
 
     public function performAccessCheck(): void
@@ -130,6 +145,7 @@ public function performAccessCheck(): void
      *
      * @return \Symfony\Component\HttpFoundation\Response
      *
+     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException
      * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException
      */
     public function listAction(Request $request): Response
@@ -205,6 +221,7 @@ public function viewAction(Section $section): Response
      *
      * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException
      * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException
+     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException
      */
     public function viewSectionContentAction(Section $section, int $page = 1, int $limit = 10): Response
     {
@@ -490,9 +507,25 @@ private function getSectionsNumbers(array $sections): array
      * @param \eZ\Publish\API\Repository\Values\Content\Section $section
      *
      * @return bool
+     *
+     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException
      */
     private function canUserAssignSectionToAnyContent(Section $section): bool
     {
-        return $this->isGranted(new Attribute('section', 'assign', ['valueObject' => new ContentInfo(), 'targets' => $section]));
+        $hasAccess = $this->permissionResolver->hasAccess('section', 'assign');
+
+        if (is_bool($hasAccess)) {
+            return $hasAccess;
+        }
+
+        foreach ($this->permissionUtil->flattenArrayOfLimitations($hasAccess) as $limitation) {
+            if ($limitation instanceof NewSectionLimitation) {
+                // If one of user limitation is NewSectionLimitation we check if user can assign Content to $section
+                return in_array($section->id, $limitation->limitationValues);
+            }
+        }
+
+        // If a user has other limitation than NewSectionLimitation, then a decision will be taken later, based on selected Content.
+        return true;
     }
 }
diff --git a/src/bundle/Resources/views/admin/section/list.html.twig b/src/bundle/Resources/views/admin/section/list.html.twig
index a7c6a6e4e4..3da8210811 100644
--- a/src/bundle/Resources/views/admin/section/list.html.twig
+++ b/src/bundle/Resources/views/admin/section/list.html.twig
@@ -93,7 +93,7 @@
                                 href="#"
                                 data-section-id="{{ section.id }}"
                                 data-form-action="{{ path("ezplatform.section.assign_content", {"sectionId": section.id}) }}"
-                                data-udw-config="{{ ez_udw_config('multiple', {}) }}"
+                                data-udw-config="{{ ez_udw_config('multiple', {'type': 'section_assign'}) }}"
                                 class="btn btn-icon mx-3">
                                 <svg class="ez-icon ez-icon-relations ez-icon--secondary btn--open-udw">
                                     <use xlink:href="{{ asset('bundles/ezplatformadminui/img/ez-icons.svg') }}#assign-section"></use>
diff --git a/src/lib/UniversalDiscovery/Event/Subscriber/SectionAssign.php b/src/lib/UniversalDiscovery/Event/Subscriber/SectionAssign.php
new file mode 100644
index 0000000000..b89646573b
--- /dev/null
+++ b/src/lib/UniversalDiscovery/Event/Subscriber/SectionAssign.php
@@ -0,0 +1,110 @@
+<?php
+
+/**
+ * @copyright Copyright (C) eZ Systems AS. All rights reserved.
+ * @license For full copyright and license information view LICENSE file distributed with this source code.
+ */
+declare(strict_types=1);
+
+namespace EzSystems\EzPlatformAdminUi\UniversalDiscovery\Event\Subscriber;
+
+use eZ\Publish\API\Repository\ContentTypeService;
+use eZ\Publish\API\Repository\Exceptions\NotFoundException;
+use eZ\Publish\API\Repository\PermissionResolver;
+use eZ\Publish\API\Repository\Values\User\Limitation\ContentTypeLimitation;
+use EzSystems\EzPlatformAdminUi\UniversalDiscovery\Event\ConfigResolveEvent;
+use EzSystems\EzPlatformAdminUi\Util\PermissionUtil;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+
+class SectionAssign implements EventSubscriberInterface
+{
+    /** @var array */
+    private $allowedContentTypesIdentifiers;
+
+    /** @var \EzSystems\EzPlatformAdminUi\Util\PermissionUtil */
+    private $permissionUtil;
+
+    /** @var \eZ\Publish\API\Repository\ContentTypeService */
+    private $contentTypeService;
+
+    /**
+     * @param \eZ\Publish\API\Repository\PermissionResolver $permissionResolver
+     * @param \EzSystems\EzPlatformAdminUi\Util\PermissionUtil $permissionUtil
+     * @param \eZ\Publish\API\Repository\ContentTypeService $contentTypeService
+     *
+     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException
+     */
+    public function __construct(
+        PermissionResolver $permissionResolver,
+        PermissionUtil $permissionUtil,
+        ContentTypeService $contentTypeService
+    ) {
+        $this->permissionUtil = $permissionUtil;
+        $this->contentTypeService = $contentTypeService;
+        $hasAccess = $permissionResolver->hasAccess('section', 'assign');
+        $this->allowedContentTypesIdentifiers = is_array($hasAccess) ? $this->checkAllowedContentTypes($hasAccess) : [];
+    }
+
+    /**
+     * @return array
+     */
+    public static function getSubscribedEvents(): array
+    {
+        return [
+            ConfigResolveEvent::NAME => ['onUdwConfigResolve'],
+        ];
+    }
+
+    /**
+     * @param \EzSystems\EzPlatformAdminUi\UniversalDiscovery\Event\ConfigResolveEvent $event
+     */
+    public function onUdwConfigResolve(ConfigResolveEvent $event): void
+    {
+        $configName = $event->getConfigName();
+        if ('multiple' !== $configName) {
+            return;
+        }
+
+        $context = $event->getContext();
+        if (
+            !isset($context['type'])
+            || 'section_assign' !== $context['type']
+        ) {
+            return;
+        }
+
+        if (!empty($this->allowedContentTypesIdentifiers)) {
+            $config = $event->getConfig();
+            $config['content_on_the_fly']['allowed_content_types'] = $this->allowedContentTypesIdentifiers;
+            $event->setConfig($config);
+        }
+    }
+
+    /**
+     * @param array $hasAccess
+     *
+     * @return array
+     */
+    private function checkAllowedContentTypes(array $hasAccess): array
+    {
+        $allowedContentTypesIdentifiers = [];
+        $allowedContentTypesIds = [];
+
+        foreach ($this->permissionUtil->flattenArrayOfLimitations($hasAccess) as $limitation) {
+            if ($limitation instanceof ContentTypeLimitation) {
+                $allowedContentTypesIds[] = $limitation->limitationValues;
+            }
+        }
+
+        $allowedContentTypesIds = array_unique(array_merge(...$allowedContentTypesIds));
+        foreach ($allowedContentTypesIds as $allowedContentTypeId) {
+            try {
+                $identifier = $this->contentTypeService->loadContentType($allowedContentTypeId)->identifier;
+                $allowedContentTypesIdentifiers[] = $identifier;
+            } catch (NotFoundException $e) {
+            }
+        }
+
+        return $allowedContentTypesIdentifiers;
+    }
+}
diff --git a/src/lib/Util/PermissionUtil.php b/src/lib/Util/PermissionUtil.php
new file mode 100644
index 0000000000..3bbe8b036b
--- /dev/null
+++ b/src/lib/Util/PermissionUtil.php
@@ -0,0 +1,38 @@
+<?php
+
+/**
+ * @copyright Copyright (C) eZ Systems AS. All rights reserved.
+ * @license For full copyright and license information view LICENSE file distributed with this source code.
+ */
+declare(strict_types=1);
+
+namespace EzSystems\EzPlatformAdminUi\Util;
+
+class PermissionUtil
+{
+    /**
+     * @param array $hasAccess
+     *
+     * @return array
+     */
+    public function flattenArrayOfLimitations(array $hasAccess): array
+    {
+        $limitations = [];
+        foreach ($hasAccess as $permissionSet) {
+            if ($permissionSet['limitation'] !== null) {
+                $limitations[] = $permissionSet['limitation'];
+            }
+            /** @var \eZ\Publish\API\Repository\Values\User\Policy $policy */
+            foreach ($permissionSet['policies'] as $policy) {
+                $policyLimitations = $policy->getLimitations();
+                if (!empty($policyLimitations)) {
+                    foreach ($policyLimitations as $policyLimitation) {
+                        $limitations[] = $policyLimitation;
+                    }
+                }
+            }
+        }
+
+        return $limitations;
+    }
+}