diff --git a/bundles/CoreBundle/config/services.yaml b/bundles/CoreBundle/config/services.yaml index c63f9db60fb..b7dd2ec4bc8 100644 --- a/bundles/CoreBundle/config/services.yaml +++ b/bundles/CoreBundle/config/services.yaml @@ -131,9 +131,9 @@ services: public: true class: Pimcore\Model\DataObject\QuantityValue\DefaultConverter - Pimcore\Bundle\CoreBundle\Request\ParamConverter\DataObjectParamConverter: + Pimcore\Bundle\CoreBundle\Request\ParamConverter\DataObjectParamResolver: tags: - - { name: request.param_converter, priority: -2, converter: data_object_converter } + - { name: controller.argument_value_resolver, priority: 101} Symfony\Component\Lock\PersistingStoreInterface: class: Symfony\Component\Lock\Store\DoctrineDbalStore diff --git a/bundles/CoreBundle/src/EventListener/ResponseHeaderListener.php b/bundles/CoreBundle/src/EventListener/ResponseHeaderListener.php index 8415e1c8053..fd4e21c15e1 100644 --- a/bundles/CoreBundle/src/EventListener/ResponseHeaderListener.php +++ b/bundles/CoreBundle/src/EventListener/ResponseHeaderListener.php @@ -17,6 +17,7 @@ namespace Pimcore\Bundle\CoreBundle\EventListener; +use Pimcore\Controller\Attribute\ResponseHeader; use Pimcore\Http\Request\Resolver\ResponseHeaderResolver; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\HttpKernel\Event\ResponseEvent; @@ -51,6 +52,13 @@ public function onKernelResponse(ResponseEvent $event): void $response = $event->getResponse(); foreach ($headers as $header) { + if (!$header instanceof ResponseHeader) { + trigger_deprecation( + 'pimcore/pimcore', + '10.6', + 'Usage of @ResponseHeader annotation is deprecated. please use #[ResponseHeader] attribute instead.' + ); + } $response->headers->set($header->getKey(), $header->getValues(), $header->getReplace()); } } diff --git a/bundles/CoreBundle/src/Request/ParamConverter/DataObjectParamConverter.php b/bundles/CoreBundle/src/Request/ParamConverter/DataObjectParamConverter.php deleted file mode 100644 index 41f504c514d..00000000000 --- a/bundles/CoreBundle/src/Request/ParamConverter/DataObjectParamConverter.php +++ /dev/null @@ -1,81 +0,0 @@ -getName(); - - if (!$request->attributes->has($param)) { - return false; - } - - $value = $request->attributes->get($param); - - if (!$value && $configuration->isOptional()) { - $request->attributes->set($param, null); - - return true; - } - - $class = $configuration->getClass(); - $options = $configuration->getOptions(); - - /** @var Concrete|null $object */ - $object = $class::getById($value); - if (!$object) { - throw new NotFoundHttpException(sprintf('Invalid data object ID given for parameter "%s".', $param)); - } elseif (!$object->isPublished() && !Tool::isElementRequestByAdmin($request, $object) && (!array_key_exists('unpublished', $options) || !$options['unpublished'])) { - throw new NotFoundHttpException(sprintf('Data object for parameter "%s" is not published.', $param)); - } - - $request->attributes->set($param, $object); - - return true; - } - - /** - * {@inheritdoc} - * - */ - public function supports(ParamConverter $configuration): bool - { - if (null === $configuration->getClass()) { - return false; - } - - return is_subclass_of($configuration->getClass(), AbstractObject::class); - } -} diff --git a/bundles/CoreBundle/src/Request/ParamConverter/DataObjectParamResolver.php b/bundles/CoreBundle/src/Request/ParamConverter/DataObjectParamResolver.php new file mode 100644 index 00000000000..e710e406d99 --- /dev/null +++ b/bundles/CoreBundle/src/Request/ParamConverter/DataObjectParamResolver.php @@ -0,0 +1,98 @@ +getAttributes(DataObjectParam::class, ArgumentMetadata::IS_INSTANCEOF); + + if (!isset($options[0])) { + $converters = $request->attributes->get('_converters'); + $converter = $converters[0] ?? false; + if ($converter instanceof ParamConverter) { + trigger_deprecation( + 'pimcore/pimcore', + '10.6', + 'Usage of @ParamConverter annotation is deprecated. please use #[DataObjectParam] argument attribute instead.' + ); + $options[0] = new DataObjectParam($converter->getClass(),$converter->getOptions()['unpublished'] ?? null, $converter->getOptions()); + } + } + + $class = $options[0]->class ?? $argument->getType(); + if (null === $class || !is_subclass_of($class, AbstractObject::class)) { + return []; + } + + $param = $argument->getName(); + if (!$request->attributes->has($param)) { + return []; + } + + $value = $request->attributes->get($param); + + if (!$value && $argument->isNullable()) { + $request->attributes->set($param, null); + return []; + } + + /** @var Concrete|null $object */ + $object = $class::getById($value); + if (!$object) { + throw new NotFoundHttpException(sprintf('Invalid data object ID given for parameter "%s".', $param)); + } elseif ( + !$object->isPublished() + && !Tool::isElementRequestByAdmin($request, $object) + && (!isset($options[0]) || !$options[0]->unpublished) + ) { + throw new NotFoundHttpException(sprintf('Data object for parameter "%s" is not published.', $param)); + } + + $request->attributes->set($param, $object); + + return [$object]; + } + + public function supports(Request $request, ArgumentMetadata $argument) + { + if (null === $argument->getType()) { + return false; + } + + return is_subclass_of($argument->getType(), AbstractObject::class); + } +} \ No newline at end of file diff --git a/doc/Development_Documentation/01_Getting_Started/06_Create_a_First_Project.md b/doc/Development_Documentation/01_Getting_Started/06_Create_a_First_Project.md index 37658d15e44..244e722b13b 100644 --- a/doc/Development_Documentation/01_Getting_Started/06_Create_a_First_Project.md +++ b/doc/Development_Documentation/01_Getting_Started/06_Create_a_First_Project.md @@ -295,14 +295,12 @@ Therefore create another action in the controller (ContentController) called `pr namespace App\Controller; use Pimcore\Controller\FrontendController; +use Symfony\Bridge\Twig\Attribute\Template; use Symfony\Component\HttpFoundation\Request; -use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template; class ContentController extends FrontendController { - /** - * @Template - */ + #[Template('content/default.html.twig')] public function defaultAction (Request $request) { return []; diff --git a/doc/Development_Documentation/02_MVC/00_Controller.md b/doc/Development_Documentation/02_MVC/00_Controller.md index ffeca005a3b..dbbc5eefe49 100644 --- a/doc/Development_Documentation/02_MVC/00_Controller.md +++ b/doc/Development_Documentation/02_MVC/00_Controller.md @@ -23,8 +23,10 @@ In controllers, for every action there exists a separate method ending with the The `DefaultController` comes with Pimcore. When you create an empty page in Pimcore it will call the `defaultAction` in the `DefaultController` which uses the view `/templates/default/default.html.twig`. -You can render templates just the [standard Symfony way](https://symfony.com/doc/current/templates.html#rendering-a-template-in-emails), by either using `$this->render('foo.html.twig')` or using the `@Template()` [annotation](https://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/annotations/view.html). - +You can render templates just the [standard Symfony way](https://symfony.com/doc/current/templates.html#rendering-a-template-in-emails), either by using: +- the render helper eg. `$this->render('foo.html.twig')` +- the `@Template()` [annotation](https://symfony.com/bundles/SensioFrameworkExtraBundle/current/annotations/view.html), altough this is deprecated and will not be supported in Pimcore 11. +- the `#Template[]` [attribute](https://symfony.com/doc/current/templates.html#rendering-a-template-in-controllers). ### Examples @@ -36,7 +38,7 @@ namespace App\Controller; use Pimcore\Controller\FrontendController; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; -use Pimcore\Controller\Configuration\ResponseHeader; +use Pimcore\Controller\Attribute\ResponseHeader; class DefaultController extends FrontendController { @@ -49,13 +51,15 @@ class DefaultController extends FrontendController } /** - * Example using the @Template annotation and auto-resolving the template using the controller/action name. - * The frontend controller also provides methods to add response headers via annotation without having + * Example using the #[Template] attribute to resolve the view. + * The frontend controller also provides methods to add response headers or via attributes without having * access to the final response object (as it is automatically created when rendering the view). * * @Template * @ResponseHeader("X-Foo", values={"123456", "98765"}) */ + #[Template('/default/header.html.twig')] + #[ResponseHeader(key: "X-Foo", values: ["123456", "98765"])] public function headerAction(Request $request) { // schedule a response header via code diff --git a/doc/Development_Documentation/02_MVC/02_Template/README.md b/doc/Development_Documentation/02_MVC/02_Template/README.md index 25c54edf2b8..61db5f76c99 100644 --- a/doc/Development_Documentation/02_MVC/02_Template/README.md +++ b/doc/Development_Documentation/02_MVC/02_Template/README.md @@ -11,7 +11,7 @@ Pimcore uses the Twig templating engine, you can use Twig exactly as documented * [Symfony Templating Documentation](https://symfony.com/doc/current/templating.html) * Check also our [Demo](https://github.com/pimcore/demo) as starting point -Just use annotations or render the view directly to use Twig: +Just use attributes or render the view directly to use Twig: ```php 'value1'])] + public function attributeAction() + { } public function directRenderAction() diff --git a/doc/Development_Documentation/02_MVC/04_Routing_and_URLs/02_Custom_Routes.md b/doc/Development_Documentation/02_MVC/04_Routing_and_URLs/02_Custom_Routes.md index 7a73ae176bc..8c2a64953e6 100644 --- a/doc/Development_Documentation/02_MVC/04_Routing_and_URLs/02_Custom_Routes.md +++ b/doc/Development_Documentation/02_MVC/04_Routing_and_URLs/02_Custom_Routes.md @@ -70,28 +70,26 @@ for converting data object IDs in the request parameters to actual objects. To use the param converter, simply type hint the argument (Symfony routing example): ```php - /** - * @Template - * @Route("/news/{news}") - */ - public function testAction(DataObject\News $news) { + use Symfony\Component\HttpFoundation\Request; + use Symfony\Component\Routing\Annotation\Route; + .... + #[Template('/news/test')] + #[Route('/news/{news}')] + public function detailAction(DataObject\News $news) { return [ 'news' => $news ]; } ``` -Param converters work with Pimcore Custom Routes as well as with Symfony Routes. -Of course, you can also configure the param converter using the `@ParamConverter`, for details please have a look at -the official documentation for [param converters](https://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/annotations/converters.html). +Param resolvers work with Pimcore Custom Routes as well as with Symfony Routes. -By taking advantage of the options that we can pass further on to the object, we can also work with unpublished objects. +By taking advantage of `#[DataObjectParam]` attribute, we can pass further options on to the object, e.g. working with unpublished objects. ````php -/** - -@route("/blog/{post}") -@ParamConverter( "post", options={ "unpublished" = true } ) -*/ +public function detailAction( + #[DataObjectParam(unpublished: true)] DataObject\News $news) { + ... +} ```` ## Building URLs based on Custom Routes diff --git a/doc/Development_Documentation/23_Installation_and_Upgrade/09_Upgrade_Notes/README.md b/doc/Development_Documentation/23_Installation_and_Upgrade/09_Upgrade_Notes/README.md index 6444f969ac9..17193019a1b 100644 --- a/doc/Development_Documentation/23_Installation_and_Upgrade/09_Upgrade_Notes/README.md +++ b/doc/Development_Documentation/23_Installation_and_Upgrade/09_Upgrade_Notes/README.md @@ -186,7 +186,11 @@ Please make sure to set your preferred storage location ***before*** migration. - [Commands] Calling `configureParallelization` on `Parallelization` trait is deprecated and will be removed in Pimcore 11. Please call `Parallelization::configureCommand` instead. - [Events] Event `pimcore.element.note.postAdd` has been deprecated. Use `pimcore.note.postAdd` instead. Note: The event type changed from `ElementEvent` to `ModelEvent`. - [Document] Deprecated loading documents via fixed namespace only. It will be removed in Pimcore 11. Use `pimcore:type_definitions instead` +- [Annotations] Using Annotations `@ResponseHeader` & `@ParamConverter`, `@Template` and +rest from [SensioFrameworkExtraBundle](https://symfony.com/bundles/SensioFrameworkExtraBundle/current/index.html#annotations-for-controllers) is deprecated and will not be supported on Pimcore 11. +Use `#[ResponseHeader]`,`#[DataObjectParam]` argument, `#[Template]` and other attributes instead. - [Navigation] Changed the navigation building process. It is easier to add main and submenus. For details please see [Adding Custom Main Navigation Items](https://pimcore.com/docs/pimcore/11.0/Development_Documentation/Extending_Pimcore/Bundle_Developers_Guide/Event_Listener_UI.html#page_Adding-Custom-Main-Navigation-Items) + ## 10.5.13 - [Web2Print] Print document twig expressions are now executed in a sandbox with restrictive security policies (just like Sending mails and Dataobject Text Layouts introduced in 10.5.9). diff --git a/lib/Controller/Attribute/ResponseHeader.php b/lib/Controller/Attribute/ResponseHeader.php new file mode 100644 index 00000000000..e4c8432b21e --- /dev/null +++ b/lib/Controller/Attribute/ResponseHeader.php @@ -0,0 +1,33 @@ +key = $key; + $this->values = $values; + $this->replace = $replace; } - parent::__construct($data); - if (empty($this->key)) { - throw new \InvalidArgumentException('The @ResponseHeaderAnnotation needs at least a key to be set'); + throw new \InvalidArgumentException('The ResponseHeader Annotation/Attribute needs at least a key to be set'); } } diff --git a/lib/Http/Request/Resolver/ResponseHeaderResolver.php b/lib/Http/Request/Resolver/ResponseHeaderResolver.php index dca5dbc95e3..4549494899a 100644 --- a/lib/Http/Request/Resolver/ResponseHeaderResolver.php +++ b/lib/Http/Request/Resolver/ResponseHeaderResolver.php @@ -17,7 +17,7 @@ namespace Pimcore\Http\Request\Resolver; -use Pimcore\Controller\Configuration\ResponseHeader; +use Pimcore\Controller\Attribute\ResponseHeader; use Symfony\Component\HttpFoundation\Request; /** diff --git a/lib/Request/Attribute/DataObjectParam.php b/lib/Request/Attribute/DataObjectParam.php new file mode 100644 index 00000000000..5d287531422 --- /dev/null +++ b/lib/Request/Attribute/DataObjectParam.php @@ -0,0 +1,31 @@ +