Skip to content

Commit c58018b

Browse files
authored
Fix custom limitation example (#2915)
* Adjusted code samples for Notifications * Extracted code sample * Adjusted custom limitation example * Revert "Adjusted code samples for Notifications" This reverts commit a0555c7. * Put Notification errors into the baseline * PHP & JS CS Fixes * Run Rector on CustomController * Added AuthenticatedRememberedCheckTrait * Fixed link * Tested the Controller
1 parent ed1886b commit c58018b

File tree

7 files changed

+126
-75
lines changed

7 files changed

+126
-75
lines changed
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace App\Controller;
4+
5+
use App\Security\Limitation\CustomLimitationValue;
6+
use Ibexa\Contracts\AdminUi\Controller\Controller;
7+
use Ibexa\Contracts\AdminUi\Permission\PermissionCheckerInterface;
8+
use Ibexa\Contracts\Core\Repository\PermissionResolver;
9+
use Ibexa\Contracts\User\Controller\AuthenticatedRememberedCheckTrait;
10+
use Ibexa\Contracts\User\Controller\RestrictedControllerInterface;
11+
use Ibexa\Core\MVC\Symfony\Security\Authorization\Attribute;
12+
use Symfony\Component\HttpFoundation\Request;
13+
use Symfony\Component\HttpFoundation\Response;
14+
15+
class CustomController extends Controller implements RestrictedControllerInterface
16+
{
17+
use AuthenticatedRememberedCheckTrait {
18+
AuthenticatedRememberedCheckTrait::performAccessCheck as public traitPerformAccessCheck;
19+
}
20+
21+
public function __construct(
22+
// ...,
23+
private readonly PermissionResolver $permissionResolver,
24+
private readonly PermissionCheckerInterface $permissionChecker
25+
) {
26+
}
27+
28+
// Controller actions...
29+
public function customAction(Request $request): Response
30+
{
31+
// ...
32+
if ($this->getCustomLimitationValue()) {
33+
// Action only for user having the custom limitation checked
34+
}
35+
36+
return new Response('<html><body>...</body></html>');
37+
}
38+
39+
private function getCustomLimitationValue(): bool
40+
{
41+
$hasAccess = $this->permissionResolver->hasAccess('custom_module', 'custom_function_2');
42+
43+
if (is_bool($hasAccess)) {
44+
return $hasAccess;
45+
}
46+
47+
$customLimitationValues = $this->permissionChecker->getRestrictions($hasAccess, CustomLimitationValue::class);
48+
49+
return $customLimitationValues['value'] ?? false;
50+
}
51+
52+
public function performAccessCheck(): void
53+
{
54+
$this->traitPerformAccessCheck();
55+
$this->denyAccessUnlessGranted(new Attribute('custom_module', 'custom_function_2'));
56+
}
57+
}

code_samples/back_office/limitation/src/Security/Limitation/Mapper/CustomLimitationValueMapper.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,9 @@
99

1010
class CustomLimitationValueMapper implements LimitationValueMapperInterface
1111
{
12-
public function mapLimitationValue(Limitation $limitation): bool
12+
/** return array<bool> */
13+
public function mapLimitationValue(Limitation $limitation): array
1314
{
14-
return $limitation->limitationValues['value'];
15+
return [$limitation->limitationValues['value']];
1516
}
1617
}
Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
{# templates/themes/standard/limitation/custom_limitation_value.html.twig #}
2-
{% block ez_limitation_customlimitation_value %}
3-
<span style="color: {{ values ? 'green' : 'red' }};">{{ values ? 'Yes' : 'No' }}</span>
2+
{% block ibexa_limitation_customlimitation_value %}
3+
{% set isSet = values | first %}
4+
<span style="color: {{ isSet ? 'green' : 'red' }};">{{ isSet ? 'Yes' : 'No' }}</span>
45
{% endblock %}

code_samples/back_office/notifications/src/Notification/MyRenderer.php

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,6 @@ public function generateUrl(Notification $notification): ?string
3535
return null;
3636
}
3737

38-
}
39-
4038
public function getTypeLabel(): string
4139
{
4240
return /** @Desc("Workflow stage changed") */

composer.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,8 @@
7474
"ibexa/discounts": "~5.0.x-dev",
7575
"ibexa/discounts-codes": "~5.0.x-dev",
7676
"ibexa/core-search": "~5.0.x-dev",
77-
"ibexa/product-catalog-symbol-attribute": "~5.0.x-dev"
77+
"ibexa/product-catalog-symbol-attribute": "~5.0.x-dev",
78+
"ibexa/messenger": "~5.0.x-dev"
7879
},
7980
"scripts": {
8081
"fix-cs": "php-cs-fixer fix --config=.php-cs-fixer.php -v --show-progress=dots",

docs/permissions/custom_policies.md

Lines changed: 7 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ services:
174174

175175
#### Form mapper
176176

177-
To provide support for editing custom policies in the back office, you need to implement [`Ibexa\AdminUi\Limitation\LimitationFormMapperInterface`](https://github.com/ibexa/admin-ui/blob/4.5/src/lib/Limitation/LimitationFormMapperInterface.php).
177+
To provide support for editing custom policies in the back office, you need to implement [`Ibexa\AdminUi\Limitation\LimitationFormMapperInterface`](https://github.com/ibexa/admin-ui/blob/5.0/src/lib/Limitation/LimitationFormMapperInterface.php).
178178

179179
- `mapLimitationForm` adds the limitation field as a child to a provided Symfony form.
180180
- `getFormTemplate` returns the path to the template to use for rendering the limitation form. Here it use [`form_label`]([[= symfony_doc =]]/form/form_customization.html#reference-forms-twig-label) and [`form_widget`]([[= symfony_doc =]]/form/form_customization.html#reference-forms-twig-widget) to do so.
@@ -205,7 +205,7 @@ Some abstract limitation type form mapper classes are provided to help implement
205205

206206
#### Value mapper
207207

208-
By default, without a value mapper, the limitation value is rendered by using the block `ez_limitation_value_fallback` of the template [`vendor/ibexa/admin-ui/src/bundle/Resources/views/themes/admin/limitation/limitation_values.html.twig`](https://github.com/ibexa/admin-ui/blob/4.5/src/bundle/Resources/views/themes/admin/limitation/limitation_values.html.twig#L1-L6).
208+
By default, without a value mapper, the limitation value is rendered by using the block `ibexa_limitation_value_fallback` of the template [`vendor/ibexa/admin-ui/src/bundle/Resources/views/themes/admin/limitation/limitation_values.html.twig`](https://github.com/ibexa/admin-ui/blob/v[[= latest_tag_5_0 =]]/src/bundle/Resources/views/themes/admin/limitation/limitation_values.html.twig).
209209

210210
To customize the rendering, a value mapper eventually transforms the limitation value and sends it to a custom template.
211211

@@ -223,17 +223,17 @@ Then register the service with the `ibexa.admin_ui.limitation.mapper.value` tag
223223
[[= include_file('code_samples/back_office/limitation/config/append_to_services.yaml', 9, 12) =]]
224224
```
225225

226-
When a value mapper exists for a limitation, the rendering uses a Twig block named `ez_limitation_<lower_case_identifier>_value` where `<lower_case_identifier>` is the limitation identifier in lower case.
227-
In this example, block name is `ez_limitation_customlimitation_value` as the identifier is `CustomLimitation`.
226+
When a value mapper exists for a limitation, the rendering uses a Twig block named `ibexa_limitation_<lower_case_identifier>_value` where `<lower_case_identifier>` is the limitation identifier in lower case.
227+
In this example, block name is `ibexa_limitation_customlimitation_value` as the identifier is `CustomLimitation`.
228228

229-
This template receives a `values` variable which is the return of the `mapLimitationValue` function from the corresponding value mapper.
229+
This template receives a `values` variable which is the return value of the `mapLimitationValue` function from the corresponding value mapper.
230230

231231
``` html+twig
232232
[[= include_file('code_samples/back_office/limitation/templates/themes/standard/limitation/custom_limitation_value.html.twig') =]]
233233
```
234234

235235
To have your block found, you have to register its template.
236-
Add the template to the configuration under `ezplatform.system.<SCOPE>.limitation_value_templates`:
236+
Add the template to the configuration under `ibexa.system.<SCOPE>.limitation_value_templates`:
237237

238238
``` yaml
239239
[[= include_file('code_samples/back_office/limitation/config/packages/ibexa_security.yaml') =]]
@@ -251,54 +251,5 @@ For example, `translations/ibexa_content_forms_policies.en.yaml`:
251251
Check if current user has this custom limitation set to true from a custom controller:
252252

253253
```php
254-
<?php declare(strict_types=1);
255-
256-
namespace App\Controller;
257-
258-
use Ibexa\Contracts\AdminUi\Controller\Controller;
259-
use Ibexa\Contracts\AdminUi\Permission\PermissionCheckerInterface;
260-
use Ibexa\Contracts\Core\Repository\PermissionResolver;
261-
use Symfony\Component\HttpFoundation\Request;
262-
use Symfony\Component\HttpFoundation\Response;
263-
264-
class CustomController extends Controller
265-
{
266-
// ...
267-
/** @var PermissionResolver */
268-
private $permissionResolver;
269-
270-
/** @var PermissionCheckerInterface */
271-
private $permissionChecker;
272-
273-
public function __construct(
274-
// ...,
275-
PermissionResolver $permissionResolver,
276-
PermissionCheckerInterface $permissionChecker
277-
)
278-
{
279-
// ...
280-
$this->permissionResolver = $permissionResolver;
281-
$this->permissionChecker = $permissionChecker;
282-
}
283-
284-
// Controller actions...
285-
public function customAction(Request $request): Response {
286-
// ...
287-
if ($this->getCustomLimitationValue()) {
288-
// Action only for user having the custom limitation checked
289-
}
290-
}
291-
292-
private function getCustomLimitationValue(): bool {
293-
$customLimitationValues = $this->permissionChecker->getRestrictions($this->permissionResolver->hasAccess('custom_module', 'custom_function_2'), CustomLimitationValue::class);
294-
295-
return $customLimitationValues['value'] ?? false;
296-
}
297-
298-
public function performAccessCheck()
299-
{
300-
parent::performAccessCheck();
301-
$this->denyAccessUnlessGranted(new Attribute('custom_module', 'custom_function_2'));
302-
}
303-
}
254+
[[= include_file('code_samples/back_office/limitation/src/Controller/CustomController.php') =]]
304255
```

phpstan-baseline.neon

Lines changed: 54 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -240,18 +240,6 @@ parameters:
240240
count: 1
241241
path: code_samples/back_office/images/src/PlaceholderProvider.php
242242

243-
-
244-
message: '#^Method App\\Security\\Limitation\\Mapper\\CustomLimitationFormMapper\:\:mapLimitationForm\(\) has parameter \$form with generic interface Symfony\\Component\\Form\\FormInterface but does not specify its types\: TData$#'
245-
identifier: missingType.generics
246-
count: 1
247-
path: code_samples/back_office/limitation/src/Security/Limitation/Mapper/CustomLimitationFormMapper.php
248-
249-
-
250-
message: '#^Return type \(bool\) of method App\\Security\\Limitation\\Mapper\\CustomLimitationValueMapper\:\:mapLimitationValue\(\) should be compatible with return type \(array\<mixed\>\) of method Ibexa\\AdminUi\\Limitation\\LimitationValueMapperInterface\:\:mapLimitationValue\(\)$#'
251-
identifier: method.childReturnType
252-
count: 1
253-
path: code_samples/back_office/limitation/src/Security/Limitation/Mapper/CustomLimitationValueMapper.php
254-
255243
-
256244
message: '#^Method App\\Security\\MyPolicyProvider\:\:getFiles\(\) return type has no value type specified in iterable type array\.$#'
257245
identifier: missingType.iterableValue
@@ -270,6 +258,24 @@ parameters:
270258
count: 1
271259
path: code_samples/back_office/menu/menu_item/src/EventSubscriber/MyMenuSubscriber.php
272260

261+
-
262+
message: '#^Access to an undefined property App\\Notification\\ListRenderer\:\:\$translator\.$#'
263+
identifier: property.notFound
264+
count: 1
265+
path: code_samples/back_office/notifications/src/Notification/ListRenderer.php
266+
267+
-
268+
message: '#^Access to an undefined property App\\Notification\\MyRenderer\:\:\$translator\.$#'
269+
identifier: property.notFound
270+
count: 1
271+
path: code_samples/back_office/notifications/src/Notification/MyRenderer.php
272+
273+
-
274+
message: '#^Undefined variable\: \$templateToExtend$#'
275+
identifier: variable.undefined
276+
count: 1
277+
path: code_samples/back_office/notifications/src/Notification/MyRenderer.php
278+
273279
-
274280
message: '#^Cannot call method getValue\(\) on Ibexa\\Contracts\\FieldTypePage\\FieldType\\LandingPage\\Model\\Attribute\|null\.$#'
275281
identifier: method.nonObject
@@ -540,6 +546,42 @@ parameters:
540546
count: 1
541547
path: code_samples/front/shop/storefront/src/EventSubscriber/BreadcrumbsMenuSubscriber.php
542548

549+
-
550+
message: '#^Call to method addCriterion\(\) on an unknown class Ibexa\\Contracts\\Core\\Repository\\Values\\NotificationQuery\.$#'
551+
identifier: class.notFound
552+
count: 3
553+
path: code_samples/notifications/Src/Query/search.php
554+
555+
-
556+
message: '#^Instantiated class DateCreated not found\.$#'
557+
identifier: class.notFound
558+
count: 1
559+
path: code_samples/notifications/Src/Query/search.php
560+
561+
-
562+
message: '#^Instantiated class Ibexa\\Contracts\\Core\\Repository\\Values\\NotificationQuery not found\.$#'
563+
identifier: class.notFound
564+
count: 1
565+
path: code_samples/notifications/Src/Query/search.php
566+
567+
-
568+
message: '#^Instantiated class Status not found\.$#'
569+
identifier: class.notFound
570+
count: 1
571+
path: code_samples/notifications/Src/Query/search.php
572+
573+
-
574+
message: '#^Instantiated class Type not found\.$#'
575+
identifier: class.notFound
576+
count: 1
577+
path: code_samples/notifications/Src/Query/search.php
578+
579+
-
580+
message: '#^Variable \$this might not be defined\.$#'
581+
identifier: variable.undefined
582+
count: 1
583+
path: code_samples/notifications/Src/Query/search.php
584+
543585
-
544586
message: '#^Method App\\Block\\Attribute\\MyStringAttributeMapper\:\:map\(\) has parameter \$constraints with no value type specified in iterable type array\.$#'
545587
identifier: missingType.iterableValue

0 commit comments

Comments
 (0)