From 046cb0f72641e3ab1dfd565e86bdf4496381d356 Mon Sep 17 00:00:00 2001 From: "M. Vondano" Date: Sat, 26 Mar 2022 13:17:43 +0100 Subject: [PATCH 01/28] prefer legacy element when using fragment with same name --- .../src/EventListener/GlobalsMapListener.php | 2 +- .../EventListener/GlobalsMapListenerTest.php | 41 +++++++++++++------ 2 files changed, 30 insertions(+), 13 deletions(-) diff --git a/core-bundle/src/EventListener/GlobalsMapListener.php b/core-bundle/src/EventListener/GlobalsMapListener.php index 58bcdc9bf40..8d50b0ec7ca 100644 --- a/core-bundle/src/EventListener/GlobalsMapListener.php +++ b/core-bundle/src/EventListener/GlobalsMapListener.php @@ -28,7 +28,7 @@ public function onInitializeSystem(): void { foreach ($this->globals as $key => $value) { if (\is_array($value) && isset($GLOBALS[$key]) && \is_array($GLOBALS[$key])) { - $GLOBALS[$key] = array_replace_recursive($GLOBALS[$key], $value); + $GLOBALS[$key] = array_replace_recursive($value, $GLOBALS[$key]); } else { $GLOBALS[$key] = $value; } diff --git a/core-bundle/tests/EventListener/GlobalsMapListenerTest.php b/core-bundle/tests/EventListener/GlobalsMapListenerTest.php index 301bcf8dd88..bb19f3d8cfc 100644 --- a/core-bundle/tests/EventListener/GlobalsMapListenerTest.php +++ b/core-bundle/tests/EventListener/GlobalsMapListenerTest.php @@ -19,26 +19,43 @@ class GlobalsMapListenerTest extends TestCase { /** * @dataProvider getValuesData - * - * @runInSeparateProcess - * @preserveGlobalState disabled */ - public function testMergesTheValuesIntoTheGlobalsArray(string $key, array|string|null $existing, array|string $new): void + public function testMergesTheValuesIntoTheGlobalsArray(array $existing, array $new, array $expected): void { - $GLOBALS[$key] = $existing; + $GLOBALS['TL_CTE'] = $existing; - $listener = new GlobalsMapListener([$key => $new]); + $listener = new GlobalsMapListener(['TL_CTE' => $new]); $listener->onInitializeSystem(); - $this->assertSame($new, $GLOBALS[$key]); + $this->assertSame($expected, $GLOBALS['TL_CTE']); + + unset($GLOBALS['TL_CTE']); } public function getValuesData(): \Generator { - yield ['foo', null, 'bar']; - yield ['foo', 'bar', 'baz']; - - yield ['TL_CTE', null, ['foo' => 'bar']]; - yield ['TL_CTE', ['foo' => 'bar'], ['foo' => 'baz']]; + yield 'add single' => [ + [], + ['text' => 'HeadlineFragment'], + ['text' => 'HeadlineFragment'], + ]; + + yield 'add group' => [ + [], + ['texts' => ['headline' => 'HeadlineFragment']], + ['texts' => ['headline' => 'HeadlineFragment']], + ]; + + yield 'add to existing group' => [ + ['texts' => ['text' => 'LegacyText']], + ['texts' => ['headline' => 'HeadlineFragment']], + ['texts' => ['headline' => 'HeadlineFragment', 'text' => 'LegacyText']], + ]; + + yield 'prefer existing entries' => [ + ['texts' => ['headline' => 'LegacyHeadline']], + ['texts' => ['headline' => 'HeadlineFragment']], + ['texts' => ['headline' => 'LegacyHeadline']], + ]; } } From bb86d9d134b417a3a9d7384d0e7b40907f8f1557 Mon Sep 17 00:00:00 2001 From: "M. Vondano" Date: Sat, 26 Mar 2022 13:22:52 +0100 Subject: [PATCH 02/28] always set the template in the RegisterFragmentsPass --- .../Compiler/RegisterFragmentsPass.php | 6 +-- .../Compiler/RegisterFragmentsPassTest.php | 54 +++++++++++++++++++ 2 files changed, 57 insertions(+), 3 deletions(-) diff --git a/core-bundle/src/DependencyInjection/Compiler/RegisterFragmentsPass.php b/core-bundle/src/DependencyInjection/Compiler/RegisterFragmentsPass.php index 5b88a0bd59e..5bf922f8dad 100644 --- a/core-bundle/src/DependencyInjection/Compiler/RegisterFragmentsPass.php +++ b/core-bundle/src/DependencyInjection/Compiler/RegisterFragmentsPass.php @@ -92,9 +92,9 @@ protected function registerFragments(ContainerBuilder $container, string $tag): $config = $this->getFragmentConfig($container, new Reference($serviceId), $attributes); - if (!empty($attributes['template'])) { - $templates[$attributes['type']] = $attributes['template']; - } + $attributes['template'] ??= substr($tag, 7).'/'.$attributes['type']; + + $templates[$attributes['type']] = $attributes['template']; if (is_a($definition->getClass(), FragmentPreHandlerInterface::class, true)) { $preHandlers[$identifier] = new Reference($serviceId); diff --git a/core-bundle/tests/DependencyInjection/Compiler/RegisterFragmentsPassTest.php b/core-bundle/tests/DependencyInjection/Compiler/RegisterFragmentsPassTest.php index e1b6213db7c..29af7252a09 100644 --- a/core-bundle/tests/DependencyInjection/Compiler/RegisterFragmentsPassTest.php +++ b/core-bundle/tests/DependencyInjection/Compiler/RegisterFragmentsPassTest.php @@ -296,6 +296,60 @@ public function testFailsToRegisterGlobalsMapListenerIfCategoryIsMissing(): void $pass->process($container); } + /** + * @dataProvider provideTemplateNames + */ + public function testSetsTemplatesInTempaltesOptionsListener(string|null $template, array $expectedCustomTemplates): void + { + $contentController = new Definition('App\Controller\TextController'); + $contentController->addTag('contao.content_element', array_filter(['template' => $template])); + + $container = $this->getContainerWithFragmentServices(); + $container->setDefinition('app.fragments.content_controller', $contentController); + $container->setDefinition('contao.listener.element_template_options', $templateOptionsListener = new Definition()); + + (new ResolveClassPass())->process($container); + + $pass = new RegisterFragmentsPass( + ContentElementReference::TAG_NAME, + templateOptionsListener: 'contao.listener.element_template_options' + ); + + $pass->process($container); + + $this->assertCount(1, $calls = $templateOptionsListener->getMethodCalls()); + $this->assertSame('setCustomTemplates', $calls[0][0]); + $this->assertSame([$expectedCustomTemplates], $calls[0][1]); + } + + public function provideTemplateNames(): \Generator + { + yield 'legacy template' => [ + 'ce_text', + ['text' => 'ce_text'], + ]; + + yield 'legacy template, alternative name' => [ + 'ce_foo', + ['text' => 'ce_foo'], + ]; + + yield 'template inferred from type' => [ + null, + ['text' => 'content_element/text'], + ]; + + yield 'modern template' => [ + 'content_element/text', + ['text' => 'content_element/text'], + ]; + + yield 'modern template, alternative name' => [ + 'content_element/foobar', + ['text' => 'content_element/foobar'], + ]; + } + public function testDoesNothingIfThereIsNoFragmentRegistry(): void { $container = $this->createMock(ContainerBuilder::class); From d481dcd79c9214e1e19e2643b9736385e9d62433 Mon Sep 17 00:00:00 2001 From: "M. Vondano" Date: Sun, 3 Apr 2022 16:37:57 +0200 Subject: [PATCH 03/28] create a new FragmentTemplate --- core-bundle/src/Twig/FragmentTemplate.php | 719 ++++++++++++++++++ .../tests/Twig/FragmentTemplateTest.php | 117 +++ 2 files changed, 836 insertions(+) create mode 100644 core-bundle/src/Twig/FragmentTemplate.php create mode 100644 core-bundle/tests/Twig/FragmentTemplateTest.php diff --git a/core-bundle/src/Twig/FragmentTemplate.php b/core-bundle/src/Twig/FragmentTemplate.php new file mode 100644 index 00000000000..7271a3d58e1 --- /dev/null +++ b/core-bundle/src/Twig/FragmentTemplate.php @@ -0,0 +1,719 @@ + + */ + private array $context = []; + + /** + * @param \Closure(self, Response|null):Response $onGetResponse + * + * @internal + * @noinspection MagicMethodsValidityInspection + */ + public function __construct(private string $templateName, private \Closure $onGetResponse) + { + // Do not call parent constructor + } + + /** + * @param string $key + * @param mixed $value + */ + public function __set(/* string */ $key, $value): void + { + $this->set($key, $value); + } + + /** + * @param string $key + * + * @return mixed + */ + public function __get(/* string */ $key) + { + return $this->get($key); + } + + /** + * @param string $key + */ + public function __isset(/* string */ $key): bool + { + return $this->has($key); + } + + public function __call($strKey, $arrParams): never + { + self::throwOnAccess(); + } + + /** + * @param mixed $value + */ + public function set(string $key, $value): void + { + $this->context[$key] = $value; + } + + /** + * @return mixed + */ + public function get(string $key) + { + return $this->context[$key] ?? throw new \RuntimeException(sprintf('Key "%s" does not exist.', $key)); + } + + public function has(string $key): bool + { + return isset($this->context[$key]); + } + + /** + * @param array $data + */ + public function setData(/* array */ $data): void + { + $this->context = $data; + } + + /** + * @return array + */ + public function getData(): array + { + return $this->context; + } + + /** + * @param string $name + */ + public function setName(/* string */ $name): void + { + $this->templateName = $name; + } + + public function getName(): string + { + return $this->templateName; + } + + /** + * Renders the template and returns a new Response, that has the rendered + * output set as content, as well as the appropriate headers that allows + * our SubrequestCacheSubscriber to merge it with others of the same page. + * + * For modern fragments, the behavior is identical to calling render() on + * the AbstractFragmentController. Like with render(), you can pass a + * prebuilt Response if you want to have full control - no headers will be + * set then. + */ + public function getResponse(Response|null $preBuiltResponse = null): Response + { + return ($this->onGetResponse)($this, $preBuiltResponse); + } + + // We need to extend from the legacy Template class to keep existing type + // hints working. In the future, when people migrated their usages, we will + // drop the base class and the following overrides, that are only there to + // prevent usage of the base class functionalities. + + /** + * @internal + */ + public static function getContainer(): never + { + self::throwOnAccess(); + } + + /** + * @internal + */ + public static function setContainer(ContainerInterface $container): never + { + self::throwOnAccess(); + } + + /** + * @internal + */ + public function inherit(): never + { + self::throwOnAccess(); + } + + /** + * @internal + */ + public static function getTemplate($strTemplate): never + { + self::throwOnAccess(); + } + + /** + * @internal + */ + public static function getTemplateGroup($strPrefix, array $arrAdditionalMapper = [], $strDefaultTemplate = ''): never + { + self::throwOnAccess(); + } + + /** + * @internal + */ + public static function getFrontendModule($intId, $strColumn = 'main'): never + { + self::throwOnAccess(); + } + + /** + * @internal + */ + public static function getArticle($varId, $blnMultiMode = false, $blnIsInsertTag = false, $strColumn = 'main'): never + { + self::throwOnAccess(); + } + + /** + * @internal + */ + public static function getContentElement($intId, $strColumn = 'main'): never + { + self::throwOnAccess(); + } + + /** + * @internal + */ + public static function getForm($varId, $strColumn = 'main', $blnModule = false): never + { + self::throwOnAccess(); + } + + /** + * @internal + */ + public static function getPageStatusIcon($objPage): never + { + self::throwOnAccess(); + } + + /** + * @internal + */ + public static function isVisibleElement(Model $objElement): never + { + self::throwOnAccess(); + } + + /** + * @internal + */ + public static function replaceInsertTags($strBuffer, $blnCache = true): never + { + self::throwOnAccess(); + } + + /** + * @internal + */ + public static function replaceDynamicScriptTags($strBuffer): never + { + self::throwOnAccess(); + } + + /** + * @internal + */ + public static function generateMargin($arrValues, $strType = 'margin'): never + { + self::throwOnAccess(); + } + + /** + * @internal + */ + public static function reload(): never + { + self::throwOnAccess(); + } + + /** + * @internal + */ + public static function redirect($strLocation, $intStatus = 303): never + { + self::throwOnAccess(); + } + + /** + * @internal + */ + public static function generateFrontendUrl(array $arrRow, $strParams = null, $strForceLang = null, $blnFixDomain = false): never + { + self::throwOnAccess(); + } + + /** + * @internal + */ + public static function convertRelativeUrls($strContent, $strBase = '', $blnHrefOnly = false): never + { + self::throwOnAccess(); + } + + /** + * @internal + */ + public static function sendFileToBrowser($strFile, $inline = false): never + { + self::throwOnAccess(); + } + + /** + * @internal + */ + public static function loadDataContainer($strTable, $blnNoCache = false): never + { + self::throwOnAccess(); + } + + /** + * @internal + */ + public static function resetControllerCache(): never + { + self::throwOnAccess(); + } + + /** + * @internal + */ + public static function addImageToTemplate($template, array $rowData, $maxWidth = null, $lightboxGroupIdentifier = null, FilesModel $filesModel = null): never + { + self::throwOnAccess(); + } + + /** + * @internal + */ + public static function addEnclosuresToTemplate($objTemplate, $arrItem, $strKey = 'enclosure'): void + { + self::throwOnAccess(); + } + + /** + * @internal + */ + public static function setStaticUrls(): void + { + self::throwOnAccess(); + } + + /** + * @internal + */ + public static function addStaticUrlTo($script, ContaoContext $context = null): void + { + self::throwOnAccess(); + } + + /** + * @internal + */ + public static function addAssetsUrlTo($script): void + { + self::throwOnAccess(); + } + + /** + * @internal + */ + public static function addFilesUrlTo($script): void + { + self::throwOnAccess(); + } + + /** + * @internal + */ + public static function restoreBasicEntities($strBuffer): void + { + self::throwOnAccess(); + } + + /** + * @internal + */ + public static function generateImage($src, $alt = '', $attributes = ''): void + { + self::throwOnAccess(); + } + + /** + * @internal + */ + public static function optionSelected($strOption, $varValues): void + { + self::throwOnAccess(); + } + + /** + * @internal + */ + public static function optionChecked($strOption, $varValues): void + { + self::throwOnAccess(); + } + + /** + * @internal + */ + public function parse(): never + { + self::throwOnAccess(); + } + + /** + * @internal + */ + public function output(): void + { + self::throwOnAccess(); + } + + /** + * @internal + */ + public static function addToUrl($strRequest, $blnIgnoreParams = false, $arrUnset = []): never + { + self::throwOnAccess(); + } + + /** + * @internal + */ + public static function importStatic($strClass, $strKey = null, $blnForce = false): never + { + self::throwOnAccess(); + } + + /** + * @internal + */ + public static function log($strText, $strFunction, $strCategory): never + { + self::throwOnAccess(); + } + + /** + * @internal + */ + public static function getReferer($blnEncodeAmpersands = false, $strTable = null): never + { + self::throwOnAccess(); + } + + /** + * @internal + */ + public static function loadLanguageFile($strName, $strLanguage = null, $blnNoCache = false): never + { + self::throwOnAccess(); + } + + /** + * @internal + */ + public static function isInstalledLanguage($strLanguage): never + { + self::throwOnAccess(); + } + + /** + * @internal + */ + public static function getCountries(): never + { + self::throwOnAccess(); + } + + /** + * @internal + */ + public static function getLanguages($blnInstalledOnly = false): never + { + self::throwOnAccess(); + } + + /** + * @internal + */ + public static function getTimeZones(): never + { + self::throwOnAccess(); + } + + /** + * @internal + */ + public static function urlEncode($strPath): never + { + self::throwOnAccess(); + } + + /** + * @internal + */ + public static function setCookie($strName, $varValue, $intExpires, $strPath = null, $strDomain = null, $blnSecure = null, $blnHttpOnly = false): never + { + self::throwOnAccess(); + } + + /** + * @internal + */ + public static function getReadableSize($intSize, $intDecimals = 1): never + { + self::throwOnAccess(); + } + + /** + * @internal + */ + public static function getFormattedNumber($varNumber, $intDecimals = 2): never + { + self::throwOnAccess(); + } + + /** + * @internal + */ + public static function anonymizeIp($strIp): never + { + self::throwOnAccess(); + } + + /** + * @internal + */ + public function setFormat($strFormat): never + { + self::throwOnAccess(); + } + + /** + * @internal + */ + public function getFormat(): never + { + self::throwOnAccess(); + } + + /** + * @internal + */ + public function dumpTemplateVars(): never + { + self::throwOnAccess(); + } + + /** + * @internal + */ + public function route($strName, $arrParams = []): never + { + self::throwOnAccess(); + } + + /** + * @internal + */ + public function previewRoute($strName, $arrParams = []): never + { + self::throwOnAccess(); + } + + /** + * @internal + */ + public function trans($strId, array $arrParams = [], $strDomain = 'contao_default'): never + { + self::throwOnAccess(); + } + + /** + * @internal + */ + public function rawPlainText(string $value, bool $removeInsertTags = false): never + { + self::throwOnAccess(); + } + + /** + * @internal + */ + public function rawHtmlToPlainText(string $value, bool $removeInsertTags = false): never + { + self::throwOnAccess(); + } + + /** + * @internal + */ + public function addSchemaOrg(array $jsonLd): never + { + self::throwOnAccess(); + } + + /** + * @internal + */ + public function figure($from, $size, $configuration = [], $template = 'image'): never + { + self::throwOnAccess(); + } + + /** + * @internal + */ + public function asset($path, $packageName = null): never + { + self::throwOnAccess(); + } + + /** + * @internal + */ + public function param($strKey): never + { + self::throwOnAccess(); + } + + /** + * @internal + */ + public function minifyHtml($strHtml): never + { + self::throwOnAccess(); + } + + /** + * @internal + */ + public static function generateStyleTag($href, $media = null, $mtime = false): never + { + self::throwOnAccess(); + } + + /** + * @internal + */ + public static function generateInlineStyle($script): never + { + self::throwOnAccess(); + } + + /** + * @internal + */ + public static function generateScriptTag($src, $async = false, $mtime = false, $hash = null, $crossorigin = null, $referrerpolicy = null): never + { + self::throwOnAccess(); + } + + /** + * @internal + */ + public static function generateInlineScript($script): never + { + self::throwOnAccess(); + } + + /** + * @internal + */ + public static function generateFeedTag($href, $format, $title): never + { + self::throwOnAccess(); + } + + /** + * @internal + */ + public function setDebug(bool $debug = null): never + { + self::throwOnAccess(); + } + + /** + * @internal + */ + public function extend($name): never + { + self::throwOnAccess(); + } + + /** + * @internal + */ + public function parent(): never + { + self::throwOnAccess(); + } + + /** + * @internal + */ + public function block($name): never + { + self::throwOnAccess(); + } + + /** + * @internal + */ + public function endblock(): never + { + self::throwOnAccess(); + } + + /** + * @internal + */ + public function insert($name, array $data = null): never + { + self::throwOnAccess(); + } + + private static function throwOnAccess(): never + { + $function = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2)[1]['function']; + + throw new \LogicException(sprintf('Calling the "%s()" function on a FragmentTemplate is not allowed. Set template data instead and optionally output it with getResponse().', $function)); + } +} diff --git a/core-bundle/tests/Twig/FragmentTemplateTest.php b/core-bundle/tests/Twig/FragmentTemplateTest.php new file mode 100644 index 00000000000..63c90b3bc3f --- /dev/null +++ b/core-bundle/tests/Twig/FragmentTemplateTest.php @@ -0,0 +1,117 @@ +getFragmentTemplate(); + + $this->assertSame('content_element/text', $template->getName()); + $template->setName('content_element/foobar'); + $this->assertSame('content_element/foobar', $template->getName()); + + $template->setData(['foobar' => 'foobar']); + $template->set('foo', 'f'); + $template->set('bar', 42); + $template->baz = true; + + $this->assertSame( + ['foobar' => 'foobar', 'foo' => 'f', 'bar' => 42, 'baz' => true], + $template->getData() + ); + + $this->assertSame('f', $template->get('foo')); + $this->assertSame('f', $template->foo); + + $this->assertTrue($template->has('bar')); + $this->assertTrue(isset($template->bar)); + + $this->assertFalse($template->has('x')); + $this->assertFalse(isset($template->x)); + } + + public function testDelegatesGetResponseCall(): void + { + $returnedResponse = new Response(); + $preBuiltResponse = new Response(); + + $callback = function (FragmentTemplate $reference, Response|null $response) use ($returnedResponse, $preBuiltResponse): Response { + $this->assertSame('content_element/text', $reference->getName()); + $this->assertSame($preBuiltResponse, $response); + + return $returnedResponse; + }; + + $template = $this->getFragmentTemplate($callback); + $this->assertSame($returnedResponse, $template->getResponse($preBuiltResponse)); + } + + /** + * @dataProvider provideIllegalParentMethods + */ + public function testDisallowsAccessOfParentMethods(string $method, array $args): void + { + $template = $this->getFragmentTemplate(); + + $this->expectException(\LogicException::class); + $this->expectExceptionMessage(sprintf('Calling the "%s()" function on a FragmentTemplate is not allowed. Set template data instead and optionally output it with getResponse().', $method)); + + $template->$method(...$args); + } + + public function provideIllegalParentMethods(): \Generator + { + $excluded = ['__construct', '__set', '__get', '__isset', 'setData', 'getData', 'setName', 'getName', 'getResponse']; + + $parent = (new \ReflectionClass(FragmentTemplate::class))->getParentClass(); + + foreach ($parent->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) { + if (\in_array($name = $method->getName(), $excluded, true)) { + continue; + } + + $args = array_map( + function (\ReflectionParameter $parameter) { + if (!($type = $parameter->getType()) instanceof \ReflectionType) { + return null; + } + + /** @phpstan-ignore-next-line because PHPStan doesn't think getName() exists */ + return match ($name = $type->getName()) { + 'bool' => false, + 'string' => '', + 'array' => [], + /** @phpstan-ignore-next-line because mocked type cannot be inferred */ + default => $this->createMock($name) + }; + }, + $method->getParameters() + ); + + yield "accessing $name()" => [$name, $args]; + } + } + + private function getFragmentTemplate(\Closure $callback = null): FragmentTemplate + { + $callback ??= (static fn () => new Response()); + + return new FragmentTemplate('content_element/text', $callback); + } +} From 1b44558b8b83165d09a6eed5d33afde3f69c38f8 Mon Sep 17 00:00:00 2001 From: "M. Vondano" Date: Sun, 3 Apr 2022 16:46:11 +0200 Subject: [PATCH 04/28] allow creating enhanced contexts from plain arrays --- .../src/Twig/Interop/ContextFactory.php | 22 +++++++++++----- .../tests/Twig/Interop/ContextFactoryTest.php | 26 ++++++++++++++++--- 2 files changed, 37 insertions(+), 11 deletions(-) diff --git a/core-bundle/src/Twig/Interop/ContextFactory.php b/core-bundle/src/Twig/Interop/ContextFactory.php index 9e64015e924..e26d8d36458 100644 --- a/core-bundle/src/Twig/Interop/ContextFactory.php +++ b/core-bundle/src/Twig/Interop/ContextFactory.php @@ -24,10 +24,22 @@ final class ContextFactory */ public function fromContaoTemplate(Template $template): array { - $context = $template->getData(); + $context = $this->fromData($template->getData()); + if (!isset($context['Template'])) { + $context['Template'] = $template; + } + + return $context; + } + + /** + * Replaces all occurrences of closures by callable wrappers. + */ + public function fromData(array $data): array + { array_walk_recursive( - $context, + $data, function (&$value, $key): void { if ($value instanceof \Closure) { $value = $this->getCallableWrapper($value, (string) $key); @@ -35,11 +47,7 @@ function (&$value, $key): void { } ); - if (!isset($context['Template'])) { - $context['Template'] = $template; - } - - return $context; + return $data; } /** diff --git a/core-bundle/tests/Twig/Interop/ContextFactoryTest.php b/core-bundle/tests/Twig/Interop/ContextFactoryTest.php index 45162472d33..309d192ceab 100644 --- a/core-bundle/tests/Twig/Interop/ContextFactoryTest.php +++ b/core-bundle/tests/Twig/Interop/ContextFactoryTest.php @@ -75,14 +75,32 @@ public function testCreateContextFromTemplate(): void OUTPUT; - $output = (new Environment(new ArrayLoader(['test.html.twig' => $content])))->render( - 'test.html.twig', - (new ContextFactory())->fromContaoTemplate($template) - ); + $context = (new ContextFactory())->fromContaoTemplate($template); + + $this->assertSame($template, $context['Template']); + + $output = (new Environment(new ArrayLoader(['test.html.twig' => $content])))->render('test.html.twig', $context); $this->assertSame($expectedOutput, $output); } + public function testCreatesContextFromData(): void + { + $data = [ + 'foo' => 'a', + 'bar' => static fn () => 'b', + 'baz' => [ + 'foobar' => static fn () => 'c', + ], + ]; + + $context = (new ContextFactory())->fromData($data); + + $this->assertSame('a', $context['foo']); + $this->assertSame('b', $context['bar']()); + $this->assertSame('c', (string) $context['baz']['foobar']); + } + /** * @group legacy */ From b3d33e91a4fc09affc9171eadafef0f7bd23c344 Mon Sep 17 00:00:00 2001 From: "M. Vondano" Date: Sun, 3 Apr 2022 16:50:04 +0200 Subject: [PATCH 05/28] rework rendering fragments in the AbstractFragmentController --- .../Controller/AbstractFragmentController.php | 106 ++++++++++++++---- .../src/Resources/config/controller.yml | 5 + core-bundle/src/Resources/config/services.yml | 1 + 3 files changed, 91 insertions(+), 21 deletions(-) diff --git a/core-bundle/src/Controller/AbstractFragmentController.php b/core-bundle/src/Controller/AbstractFragmentController.php index b9a82a71cc9..e140e14f447 100644 --- a/core-bundle/src/Controller/AbstractFragmentController.php +++ b/core-bundle/src/Controller/AbstractFragmentController.php @@ -15,7 +15,9 @@ use Contao\CoreBundle\EventListener\SubrequestCacheSubscriber; use Contao\CoreBundle\Fragment\FragmentOptionsAwareInterface; use Contao\CoreBundle\Routing\ScopeMatcher; -use Contao\FragmentTemplate; +use Contao\CoreBundle\Twig\FragmentTemplate; +use Contao\CoreBundle\Twig\Interop\ContextFactory; +use Contao\CoreBundle\Twig\Loader\ContaoFilesystemLoader; use Contao\FrontendTemplate; use Contao\Model; use Contao\PageModel; @@ -28,6 +30,7 @@ abstract class AbstractFragmentController extends AbstractController implements FragmentOptionsAwareInterface { protected array $options = []; + private string|null $view = null; public function setFragmentOptions(array $options): void { @@ -43,6 +46,8 @@ public static function getSubscribedServices(): array $services['request_stack'] = RequestStack::class; $services['contao.routing.scope_matcher'] = ScopeMatcher::class; + $services['contao.twig.filesystem_loader'] = ContaoFilesystemLoader::class; + $services['contao.twig.interop.context_factory'] = ContextFactory::class; return $services; } @@ -59,38 +64,68 @@ protected function getPageModel(): PageModel|null } /** - * Creates a template by name or from the "customTpl" field of the model. + * Creates a FragmentTemplate container object by template name or from the + * "customTpl" field of the model and registers the effective template as + * default view when using render(). + * + * Calling getResponse() method on the returned object will internally call + * render() with the set parameters and return the response. + * + * Note: The $fallbackTemplateName argument will be removed in Contao 6; + * always set a template via the fragment options, instead. */ - protected function createTemplate(Model $model, string $templateName): Template + protected function createTemplate(Model $model, string|null $fallbackTemplateName = null): FragmentTemplate { - if (isset($this->options['template'])) { - $templateName = $this->options['template']; + $templateName = $this->getTemplateName($model, $fallbackTemplateName); + + if ($isLegacyTemplate = $this->isLegacyTemplate($templateName)) { + // TODO: enable deprecation once existing fragments have been adjusted + // trigger_deprecation('contao/core-bundle', '5.0', 'Creating fragments with legacy templates is deprecated and will not work anymore in Contao 6.'); } - $request = $this->container->get('request_stack')->getCurrentRequest(); + // Allow calling render() without a view + $this->view = !$isLegacyTemplate ? "@Contao/$templateName.html.twig" : null; + + $onGetResponse = function (FragmentTemplate $template, Response|null $preBuiltResponse) use ($templateName, $isLegacyTemplate): Response { + if ($isLegacyTemplate) { + // Render using the legacy framework + $legacyTemplate = $this->container->get('contao.framework')->createInstance(FrontendTemplate::class, [$templateName]); + $legacyTemplate->setData($template->getData()); + + $response = $legacyTemplate->getResponse(); - if ($model->customTpl) { - // Use the custom template unless it is a back end request - if (null === $request || !$this->container->get('contao.routing.scope_matcher')->isBackendRequest($request)) { - $templateName = $model->customTpl; + if (null !== $preBuiltResponse) { + return $preBuiltResponse->setContent($response->getContent()); + } + + $this->markResponseForInternalCaching($response); + + return $response; } - } - $templateClass = FragmentTemplate::class; + // Directly render with Twig + $context = $this->container->get('contao.twig.interop.context_factory')->fromData($template->getData()); - // Current request is the main request (e.g. ESI fragment), so we have to replace - // insert tags etc. on the template output - if ($request === $this->container->get('request_stack')->getMainRequest()) { - $templateClass = FrontendTemplate::class; - } + return $this->render($template->getName(), $context, $preBuiltResponse); + }; + + $template = new FragmentTemplate($templateName, $onGetResponse); - /** @var Template $template */ - $template = $this->container->get('contao.framework')->createInstance($templateClass, [$templateName]); - $template->setData($model->row()); + if ($isLegacyTemplate) { + $template->setData((array) $model->row()); + } return $template; } + /** + * @internal + */ + protected function isLegacyTemplate(string $templateName): bool + { + return !str_contains($templateName, '/'); + } + protected function addHeadlineToTemplate(Template $template, array|string|null $headline): void { $data = StringUtil::deserialize($headline); @@ -139,8 +174,19 @@ protected function getType(): string return Container::underscore($className); } - protected function render(string $view, array $parameters = [], Response $response = null): Response + /** + * Renders a template. If $view is set to null, the default template of + * this fragment will rendered. + * + * By default, the returned response will have the appropriate headers set, + * that allow our SubrequestCacheSubscriber to merge it with others of the + * same page. Pass a prebuilt Response if you want to have full control - + * no headers will be set then. + */ + protected function render(string|null $view = null, array $parameters = [], Response $response = null): Response { + $view ??= $this->view ?? throw new \InvalidArgumentException('Cannot derive template name, please make sure createTemplate() was called before or specify the template explicitly.'); + if (null === $response) { $response = new Response(); @@ -158,4 +204,22 @@ protected function markResponseForInternalCaching(Response $response): void $response->headers->set(SubrequestCacheSubscriber::MERGE_CACHE_HEADER, '1'); $response->headers->remove('Cache-Control'); } + + private function getTemplateName(Model $model, string|null $fallbackTemplateName): string + { + // If set, use the custom template unless it is a back end request + if ($model->customTpl && !$this->isBackendScope()) { + return $model->customTpl; + } + + $definedTemplateName = $this->options['template'] ?? null; + + // Always use the defined name for legacy templates and for modern + // templates that exist (= those that do not need to have a fallback) + if (null !== $definedTemplateName && ($this->isLegacyTemplate($definedTemplateName) || $this->container->get('contao.twig.filesystem_loader')->exists("@Contao/$definedTemplateName.html.twig"))) { + return $definedTemplateName; + } + + return $fallbackTemplateName ?? throw new \InvalidArgumentException('No template was set in the fragment options.'); + } } diff --git a/core-bundle/src/Resources/config/controller.yml b/core-bundle/src/Resources/config/controller.yml index 56b7e7ae065..41a2d47167f 100644 --- a/core-bundle/src/Resources/config/controller.yml +++ b/core-bundle/src/Resources/config/controller.yml @@ -11,6 +11,11 @@ services: tags: - { name: container.service_subscriber, id: contao.csrf.token_manager } + Contao\CoreBundle\Controller\AbstractFragmentController: + tags: + - { name: container.service_subscriber, id: contao.twig.filesystem_loader } + - { name: container.service_subscriber, id: contao.twig.interop.context_factory } + # We explicitly allow autowiring and FQCN service IDs in controllers Contao\CoreBundle\Controller\BackendController: ~ diff --git a/core-bundle/src/Resources/config/services.yml b/core-bundle/src/Resources/config/services.yml index f3516ae2853..77b95998815 100644 --- a/core-bundle/src/Resources/config/services.yml +++ b/core-bundle/src/Resources/config/services.yml @@ -1039,6 +1039,7 @@ services: Contao\CoreBundle\Slug\Slug: '@contao.slug' Contao\CoreBundle\String\HtmlDecoder: '@contao.string.html_decoder' Contao\CoreBundle\String\SimpleTokenParser: '@contao.string.simple_token_parser' + Contao\CoreBundle\Twig\Interop\ContextFactory: '@contao.twig.interop.context_factory' # Backwards compatibility contao.cache.clear_internal: From 6a1504afd9b62fb85f602574062d52f13011233f Mon Sep 17 00:00:00 2001 From: "M. Vondano" Date: Sun, 3 Apr 2022 16:52:17 +0200 Subject: [PATCH 06/28] rework default template context for modern fragments --- .../Controller/AbstractFragmentController.php | 50 +++++++++++++++ .../AbstractContentElementController.php | 61 ++++++++++++++++--- .../AbstractFrontendModuleController.php | 60 +++++++++++++++--- 3 files changed, 156 insertions(+), 15 deletions(-) diff --git a/core-bundle/src/Controller/AbstractFragmentController.php b/core-bundle/src/Controller/AbstractFragmentController.php index e140e14f447..6e4069f2603 100644 --- a/core-bundle/src/Controller/AbstractFragmentController.php +++ b/core-bundle/src/Controller/AbstractFragmentController.php @@ -12,6 +12,8 @@ namespace Contao\CoreBundle\Controller; +use Contao\CoreBundle\Controller\ContentElement\AbstractContentElementController; +use Contao\CoreBundle\Controller\FrontendModule\AbstractFrontendModuleController; use Contao\CoreBundle\EventListener\SubrequestCacheSubscriber; use Contao\CoreBundle\Fragment\FragmentOptionsAwareInterface; use Contao\CoreBundle\Routing\ScopeMatcher; @@ -126,15 +128,29 @@ protected function isLegacyTemplate(string $templateName): bool return !str_contains($templateName, '/'); } + /** + * @internal the addHeadlineToTemplate() method is considered internal in + * Contao 5 and won't be accessible anymore in Contao 6. Headline data is + * always added to the context of modern fragment templates. + */ protected function addHeadlineToTemplate(Template $template, array|string|null $headline): void { + $this->triggerDeprecationIfCallingFromCustomClass(__METHOD__); + $data = StringUtil::deserialize($headline); $template->headline = \is_array($data) ? $data['value'] : $data; $template->hl = \is_array($data) ? $data['unit'] : 'h1'; } + /** + * @internal the addCssAttributesToTemplate() method is considered internal + * in Contao 5 and won't be accessible anymore in Contao 6. Attributes data + * is always added to the context of modern fragment templates. + */ protected function addCssAttributesToTemplate(Template $template, string $templateName, array|string|null $cssID, array $classes = null): void { + $this->triggerDeprecationIfCallingFromCustomClass(__METHOD__); + $data = StringUtil::deserialize($cssID, true); $template->class = trim($templateName.' '.($data[1] ?? '')); $template->cssID = !empty($data[0]) ? ' id="'.$data[0].'"' : ''; @@ -144,20 +160,38 @@ protected function addCssAttributesToTemplate(Template $template, string $templa } } + /** + * @internal the addPropertiesToTemplate() method is considered internal in + * Contao 5 and won't be accessible anymore in Contao 6. Custom properties + * are always added to the context of modern fragment templates. + */ protected function addPropertiesToTemplate(Template $template, array $properties): void { + $this->triggerDeprecationIfCallingFromCustomClass(__METHOD__); + foreach ($properties as $k => $v) { $template->{$k} = $v; } } + /** + * @internal the addSectionToTemplate() method is considered internal in + * Contao 5 and won't be accessible anymore in Contao 6. Section data is + * always added to the context of modern fragment templates. + */ protected function addSectionToTemplate(Template $template, string $section): void { + $this->triggerDeprecationIfCallingFromCustomClass(__METHOD__); + $template->inColumn = $section; } /** * Returns the type from the class name. + * + * @internal the getType() method is considered internal in Contao 5 and + * won't be accessible anymore in Contao 6. Retrieve the type from the + * fragment options instead. */ protected function getType(): string { @@ -196,6 +230,13 @@ protected function render(string|null $view = null, array $parameters = [], Resp return parent::render($view, $parameters, $response); } + protected function isBackendScope(): bool + { + $request = $this->container->get('request_stack')->getCurrentRequest(); + + return null !== $request && $this->container->get('contao.routing.scope_matcher')->isBackendRequest($request); + } + /** * Marks the response to affect the caching of the current page but removes any default cache header. */ @@ -205,6 +246,15 @@ protected function markResponseForInternalCaching(Response $response): void $response->headers->remove('Cache-Control'); } + private function triggerDeprecationIfCallingFromCustomClass(string $method): void + { + $caller = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 3)[2]['class']; + + if (!\in_array($caller, [AbstractContentElementController::class, AbstractFrontendModuleController::class], true)) { + trigger_deprecation('contao/core-bundle', '5.0', 'The "%s" method is considered internal and won\'t be accessible anymore in Contao 6.', $method); + } + } + private function getTemplateName(Model $model, string|null $fallbackTemplateName): string { // If set, use the custom template unless it is a back end request diff --git a/core-bundle/src/Controller/ContentElement/AbstractContentElementController.php b/core-bundle/src/Controller/ContentElement/AbstractContentElementController.php index cf618bdadee..47e26e23c0e 100644 --- a/core-bundle/src/Controller/ContentElement/AbstractContentElementController.php +++ b/core-bundle/src/Controller/ContentElement/AbstractContentElementController.php @@ -14,7 +14,9 @@ use Contao\ContentModel; use Contao\CoreBundle\Controller\AbstractFragmentController; -use Contao\Template; +use Contao\CoreBundle\String\HtmlAttributes; +use Contao\CoreBundle\Twig\FragmentTemplate; +use Contao\StringUtil; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; @@ -22,13 +24,17 @@ abstract class AbstractContentElementController extends AbstractFragmentControll { public function __invoke(Request $request, ContentModel $model, string $section, array $classes = null): Response { - $type = $this->getType(); - $template = $this->createTemplate($model, 'ce_'.$type); + $template = $this->createTemplate($model, 'ce_'.$this->getType()); + + $this->addDefaultDataToTemplate( + $template, + (array) $model->row(), + $section, + $classes ?? [], + $request->attributes->get('templateProperties', []), + $this->isBackendScope($request) + ); - $this->addHeadlineToTemplate($template, $model->headline); - $this->addCssAttributesToTemplate($template, 'ce_'.$type, $model->cssID, $classes); - $this->addPropertiesToTemplate($template, $request->attributes->get('templateProperties', [])); - $this->addSectionToTemplate($template, $section); $this->tagResponse($model); return $this->getResponse($template, $model, $request); @@ -54,5 +60,44 @@ protected function addSharedMaxAgeToResponse(Response $response, ContentModel $m $response->setSharedMaxAge(min($min)); } - abstract protected function getResponse(Template $template, ContentModel $model, Request $request): Response; + /** + * Add default content element data to the template context. + * + * @param array $modelData + * @param array $classes + * @param array $properties + */ + protected function addDefaultDataToTemplate(FragmentTemplate $template, array $modelData = [], string $section = 'main', array $classes = [], array $properties = [], bool $asOverview = false): void + { + if ($this->isLegacyTemplate($template->getName())) { + // Legacy fragments + $this->addHeadlineToTemplate($template, $modelData['headline'] ?? null); + $this->addCssAttributesToTemplate($template, $template->getName(), $modelData['cssID'] ?? null, $classes); + $this->addPropertiesToTemplate($template, $properties); + $this->addSectionToTemplate($template, $section); + + return; + } + + $headlineData = StringUtil::deserialize($modelData['headline'] ?? [] ?: '', true); + $attributesData = StringUtil::deserialize($modelData['cssID'] ?? [] ?: '', true); + + $template->setData([ + 'type' => $this->getType(), + 'template' => $template->getName(), + 'as_overview' => $asOverview, + 'data' => $modelData, + 'section' => $section, + 'properties' => $properties, + 'attributes' => (new HtmlAttributes()) + ->setIfExists('id', $attributesData[0] ?? null) + ->addClass($attributesData[1] ?? '', ...$classes), + 'headline' => [ + 'value' => $headlineData['value'] ?? '', + 'unit' => $headlineData['unit'] ?? 'h1', + ], + ]); + } + + abstract protected function getResponse(FragmentTemplate $template, ContentModel $model, Request $request): Response; } diff --git a/core-bundle/src/Controller/FrontendModule/AbstractFrontendModuleController.php b/core-bundle/src/Controller/FrontendModule/AbstractFrontendModuleController.php index 268429f669d..d873b819684 100644 --- a/core-bundle/src/Controller/FrontendModule/AbstractFrontendModuleController.php +++ b/core-bundle/src/Controller/FrontendModule/AbstractFrontendModuleController.php @@ -14,7 +14,11 @@ use Contao\BackendTemplate; use Contao\CoreBundle\Controller\AbstractFragmentController; +use Contao\CoreBundle\String\HtmlAttributes; +use Contao\CoreBundle\Twig\FragmentTemplate; +use Contao\Model; use Contao\ModuleModel; +use Contao\StringUtil; use Contao\Template; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; @@ -28,13 +32,16 @@ public function __invoke(Request $request, ModuleModel $model, string $section, return $this->getBackendWildcard($model); } - $type = $this->getType(); - $template = $this->createTemplate($model, 'mod_'.$type); + $template = $this->createTemplate($model, 'mod_'.$this->getType()); + + $this->addDefaultDataToTemplate( + $template, + (array) $model->row(), + $section, + $classes ?? [], + $request->attributes->get('templateProperties', []), + ); - $this->addHeadlineToTemplate($template, $model->headline); - $this->addCssAttributesToTemplate($template, 'mod_'.$type, $model->cssID, $classes); - $this->addPropertiesToTemplate($template, $request->attributes->get('templateProperties', [])); - $this->addSectionToTemplate($template, $section); $this->tagResponse($model); return $this->getResponse($template, $model, $request); @@ -67,5 +74,44 @@ protected function getBackendWildcard(ModuleModel $module): Response return new Response($template->parse()); } - abstract protected function getResponse(Template $template, ModuleModel $model, Request $request): Response; + /** + * Add default frontend module data to the template context. + * + * @param array $modelData + * @param array $classes + * @param array $properties + */ + protected function addDefaultDataToTemplate(FragmentTemplate $template, array $modelData = [], string $section = 'main', array $classes = [], array $properties = [], bool $asOverview = false): void + { + if ($this->isLegacyTemplate($template->getName())) { + // Legacy fragments + $this->addHeadlineToTemplate($template, $modelData['headline'] ?? null); + $this->addCssAttributesToTemplate($template, $template->getName(), $modelData['cssID'] ?? null, $classes); + $this->addPropertiesToTemplate($template, $properties); + $this->addSectionToTemplate($template, $section); + + return; + } + + $headlineData = StringUtil::deserialize($modelData['headline'] ?? [] ?: '', true); + $attributesData = StringUtil::deserialize($modelData['cssID'] ?? [] ?: '', true); + + $template->setData([ + 'type' => $this->getType(), + 'template' => $template->getName(), + 'as_overview' => $asOverview, + 'data' => $modelData, + 'section' => $section, + 'properties' => $properties, + 'attributes' => (new HtmlAttributes()) + ->setIfExists('id', $attributesData[0] ?? null) + ->addClass($attributesData[1] ?? '', ...$classes), + 'headline' => [ + 'value' => $headlineData['value'] ?? '', + 'unit' => $headlineData['unit'] ?? 'h1', + ], + ]); + } + + abstract protected function getResponse(FragmentTemplate $template, ModuleModel $model, Request $request): Response; } From 7ceab8a4db69c9459f4cfe7fa233e58e325a1263 Mon Sep 17 00:00:00 2001 From: "M. Vondano" Date: Sun, 3 Apr 2022 16:53:29 +0200 Subject: [PATCH 07/28] deprecate the old template class, so that its usage in the old getResponse definition gets obvious --- core-bundle/src/Resources/contao/library/Contao/Template.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core-bundle/src/Resources/contao/library/Contao/Template.php b/core-bundle/src/Resources/contao/library/Contao/Template.php index 40811536264..04408355a1e 100644 --- a/core-bundle/src/Resources/contao/library/Contao/Template.php +++ b/core-bundle/src/Resources/contao/library/Contao/Template.php @@ -62,6 +62,8 @@ * @property boolean $trustedDevicesEnabled * @property array $trustedDevices * @property string $currentDevice + * + * @deprecated this class will be removed in Contao 6, use Twig templates instead */ abstract class Template extends Controller { From d5aa39377f77c5fba0de612d984267139f240400 Mon Sep 17 00:00:00 2001 From: "M. Vondano" Date: Sun, 3 Apr 2022 19:09:03 +0200 Subject: [PATCH 08/28] rework back end wildcard --- .../Controller/AbstractFragmentController.php | 5 +-- .../AbstractFrontendModuleController.php | 31 +++++++------------ .../src/Resources/config/controller.yml | 4 +++ .../_new/back_end/module_wildcard.html.twig | 12 +++++++ 4 files changed, 31 insertions(+), 21 deletions(-) create mode 100644 core-bundle/src/Resources/contao/templates/_new/back_end/module_wildcard.html.twig diff --git a/core-bundle/src/Controller/AbstractFragmentController.php b/core-bundle/src/Controller/AbstractFragmentController.php index 6e4069f2603..38fde6ac206 100644 --- a/core-bundle/src/Controller/AbstractFragmentController.php +++ b/core-bundle/src/Controller/AbstractFragmentController.php @@ -26,6 +26,7 @@ use Contao\StringUtil; use Contao\Template; use Symfony\Component\DependencyInjection\Container; +use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\HttpFoundation\Response; @@ -230,9 +231,9 @@ protected function render(string|null $view = null, array $parameters = [], Resp return parent::render($view, $parameters, $response); } - protected function isBackendScope(): bool + protected function isBackendScope(Request $request = null): bool { - $request = $this->container->get('request_stack')->getCurrentRequest(); + $request ??= $this->container->get('request_stack')->getCurrentRequest(); return null !== $request && $this->container->get('contao.routing.scope_matcher')->isBackendRequest($request); } diff --git a/core-bundle/src/Controller/FrontendModule/AbstractFrontendModuleController.php b/core-bundle/src/Controller/FrontendModule/AbstractFrontendModuleController.php index d873b819684..aea5297920a 100644 --- a/core-bundle/src/Controller/FrontendModule/AbstractFrontendModuleController.php +++ b/core-bundle/src/Controller/FrontendModule/AbstractFrontendModuleController.php @@ -12,23 +12,21 @@ namespace Contao\CoreBundle\Controller\FrontendModule; -use Contao\BackendTemplate; use Contao\CoreBundle\Controller\AbstractFragmentController; +use Contao\CoreBundle\Csrf\ContaoCsrfTokenManager; use Contao\CoreBundle\String\HtmlAttributes; use Contao\CoreBundle\Twig\FragmentTemplate; -use Contao\Model; use Contao\ModuleModel; use Contao\StringUtil; use Contao\Template; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; -use Symfony\Contracts\Translation\TranslatorInterface; abstract class AbstractFrontendModuleController extends AbstractFragmentController { public function __invoke(Request $request, ModuleModel $model, string $section, array $classes = null): Response { - if ($this->container->get('contao.routing.scope_matcher')->isBackendRequest($request)) { + if ($this->isBackendScope($request)) { return $this->getBackendWildcard($model); } @@ -51,27 +49,22 @@ public static function getSubscribedServices(): array { $services = parent::getSubscribedServices(); - $services['translator'] = TranslatorInterface::class; + $services['contao.csrf.token_manager'] = ContaoCsrfTokenManager::class; return $services; } protected function getBackendWildcard(ModuleModel $module): Response { - $href = $this->container->get('router')->generate( - 'contao_backend', - ['do' => 'themes', 'table' => 'tl_module', 'act' => 'edit', 'id' => $module->id] - ); - - $name = $this->container->get('translator')->trans('FMD.'.$this->getType().'.0', [], 'contao_modules'); - - $template = new BackendTemplate('be_wildcard'); - $template->wildcard = '### '.strtoupper($name).' ###'; - $template->id = $module->id; - $template->link = $module->name; - $template->href = $href; - - return new Response($template->parse()); + $context = [ + 'id' => (int) $module->id, + 'name' => $module->name, + 'type' => $module->type, + 'title' => StringUtil::deserialize($module->headline, true)['value'] ?? null, + 'request_token' => $this->container->get('contao.csrf.token_manager')->getDefaultTokenValue(), + ]; + + return $this->render('@Contao/back_end/module_wildcard.html.twig', $context); } /** diff --git a/core-bundle/src/Resources/config/controller.yml b/core-bundle/src/Resources/config/controller.yml index 41a2d47167f..ca2ce723812 100644 --- a/core-bundle/src/Resources/config/controller.yml +++ b/core-bundle/src/Resources/config/controller.yml @@ -16,6 +16,10 @@ services: - { name: container.service_subscriber, id: contao.twig.filesystem_loader } - { name: container.service_subscriber, id: contao.twig.interop.context_factory } + Contao\CoreBundle\Controller\FrontendModule\AbstractFrontendModuleController: + tags: + - { name: container.service_subscriber, id: contao.csrf.token_manager } + # We explicitly allow autowiring and FQCN service IDs in controllers Contao\CoreBundle\Controller\BackendController: ~ diff --git a/core-bundle/src/Resources/contao/templates/_new/back_end/module_wildcard.html.twig b/core-bundle/src/Resources/contao/templates/_new/back_end/module_wildcard.html.twig new file mode 100644 index 00000000000..f68a5c73736 --- /dev/null +++ b/core-bundle/src/Resources/contao/templates/_new/back_end/module_wildcard.html.twig @@ -0,0 +1,12 @@ +{% trans_default_domain 'contao_modules' %} + +{% if title %} +

{{ title }}

+{% endif %} + +
+ ### {{ ('FMD.' ~ type ~ '.0')|trans }} ### +
+ {% set href = path('contao_backend', {do: 'themes', table: 'tl_module', act: 'edit', id, rt: request_token}) %} + {{ name }} (ID: {{ id }}) +
From 52284e89bc1c129ad6c123c05ed2309a268ed02c Mon Sep 17 00:00:00 2001 From: "M. Vondano" Date: Sun, 3 Apr 2022 19:09:17 +0200 Subject: [PATCH 09/28] adjust and add tests --- .../ContentElementControllerTest.php | 214 ++++++++--------- .../ContentElement/TemplateControllerTest.php | 2 + .../FrontendModuleControllerTest.php | 219 +++++++++++++----- ...RootPageDependentModulesControllerTest.php | 50 ---- 4 files changed, 267 insertions(+), 218 deletions(-) diff --git a/core-bundle/tests/Controller/ContentElement/ContentElementControllerTest.php b/core-bundle/tests/Controller/ContentElement/ContentElementControllerTest.php index 3bfa084c0b6..1f868dd59d6 100644 --- a/core-bundle/tests/Controller/ContentElement/ContentElementControllerTest.php +++ b/core-bundle/tests/Controller/ContentElement/ContentElementControllerTest.php @@ -17,11 +17,10 @@ use Contao\CoreBundle\Cache\EntityCacheTags; use Contao\CoreBundle\Fixtures\Controller\ContentElement\TestController; use Contao\CoreBundle\Fixtures\Controller\ContentElement\TestSharedMaxAgeController; +use Contao\CoreBundle\Routing\ScopeMatcher; use Contao\CoreBundle\Tests\TestCase; -use Contao\FragmentTemplate; -use Contao\FrontendTemplate; +use Contao\CoreBundle\Twig\Loader\ContaoFilesystemLoader; use Contao\System; -use PHPUnit\Framework\MockObject\MockObject; use Symfony\Bridge\PhpUnit\ClockMock; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\HttpFoundation\Request; @@ -37,6 +36,7 @@ protected function setUp(): void $this->container = $this->getContainerWithContaoConfiguration(); $this->container->set('contao.cache.entity_tags', $this->createMock(EntityCacheTags::class)); + $this->container->set('contao.twig.filesystem_loader', $this->createMock(ContaoFilesystemLoader::class)); System::setContainer($this->container); } @@ -52,40 +52,41 @@ protected function tearDown(): void public function testCreatesTheTemplateFromTheClassName(): void { - $controller = new TestController(); - $controller->setContainer($this->mockContainerWithFrameworkTemplate('ce_test')); + $controller = $this->getTestController(); + + $response = $controller(new Request(), $this->getContentModel(), 'main'); + $template = json_decode($response->getContent(), true); - $controller(new Request(), $this->mockContentModel(), 'main'); + $this->assertSame('ce_test', $template['templateName']); } public function testCreatesTheTemplateFromTheTypeFragmentOptions(): void { - $controller = new TestController(); - $controller->setContainer($this->mockContainerWithFrameworkTemplate('ce_foo')); - $controller->setFragmentOptions(['type' => 'foo']); + $controller = $this->getTestController(['type' => 'foo']); - $controller(new Request(), $this->mockContentModel(), 'main'); + $response = $controller(new Request(), $this->getContentModel(), 'main'); + $template = json_decode($response->getContent(), true); + + $this->assertSame('ce_foo', $template['templateName']); } public function testCreatesTheTemplateFromTheTemplateFragmentOption(): void { - $controller = new TestController(); - $controller->setContainer($this->mockContainerWithFrameworkTemplate('ce_bar')); - $controller->setFragmentOptions(['template' => 'ce_bar']); + $controller = $this->getTestController(['template' => 'ce_bar']); - $controller(new Request(), $this->mockContentModel(), 'main'); + $response = $controller(new Request(), $this->getContentModel(), 'main'); + $template = json_decode($response->getContent(), true); + + $this->assertSame('ce_bar', $template['templateName']); } public function testCreatesTheTemplateFromACustomTpl(): void { - $model = $this->mockContentModel(); - $model->customTpl = 'ce_bar'; + $this->container->set('request_stack', new RequestStack()); - $container = $this->mockContainerWithFrameworkTemplate('ce_bar'); - $container->set('request_stack', new RequestStack()); + $controller = $this->getTestController(['template' => 'ce_bar']); - $controller = new TestController(); - $controller->setContainer($container); + $model = $this->getContentModel(['customTpl' => 'ce_bar']); $response = $controller(new Request(), $model, 'main'); $template = json_decode($response->getContent(), true); @@ -95,20 +96,16 @@ public function testCreatesTheTemplateFromACustomTpl(): void public function testDoesNotCreateTheTemplateFromACustomTplInTheBackend(): void { - $model = $this->mockContentModel(); - $model->customTpl = 'ce_bar'; - $request = new Request([], [], ['_scope' => 'backend']); - $requestStack = new RequestStack(); $requestStack->push($request); - $container = $this->mockContainerWithFrameworkTemplate('ce_test'); - $container->set('request_stack', $requestStack); - $container->set('contao.routing.scope_matcher', $this->mockScopeMatcher()); + $this->container->set('request_stack', $requestStack); + $this->container->set('contao.routing.scope_matcher', $this->mockScopeMatcher()); + + $controller = $this->getTestController(); - $controller = new TestController(); - $controller->setContainer($container); + $model = $this->getContentModel(['customTpl' => 'ce_bar']); $response = $controller($request, $model, 'main'); $template = json_decode($response->getContent(), true); @@ -118,10 +115,9 @@ public function testDoesNotCreateTheTemplateFromACustomTplInTheBackend(): void public function testSetsTheClassFromTheType(): void { - $controller = new TestController(); - $controller->setContainer($this->mockContainerWithFrameworkTemplate('ce_test')); + $controller = $this->getTestController(); - $response = $controller(new Request(), $this->mockContentModel(), 'main'); + $response = $controller(new Request(), $this->getContentModel(), 'main'); $template = json_decode($response->getContent(), true); $this->assertSame('', $template['cssID']); @@ -130,11 +126,9 @@ public function testSetsTheClassFromTheType(): void public function testSetsTheHeadlineFromTheModel(): void { - $model = $this->mockContentModel(); - $model->headline = serialize(['unit' => 'h6', 'value' => 'foobar']); + $controller = $this->getTestController(); - $controller = new TestController(); - $controller->setContainer($this->mockContainerWithFrameworkTemplate('ce_test')); + $model = $this->getContentModel(['headline' => serialize(['unit' => 'h6', 'value' => 'foobar'])]); $response = $controller(new Request(), $model, 'main'); $template = json_decode($response->getContent(), true); @@ -145,11 +139,9 @@ public function testSetsTheHeadlineFromTheModel(): void public function testSetsTheCssIdAndClassFromTheModel(): void { - $model = $this->mockContentModel(); - $model->cssID = serialize(['foo', 'bar']); + $controller = $this->getTestController(); - $controller = new TestController(); - $controller->setContainer($this->mockContainerWithFrameworkTemplate('ce_test')); + $model = $this->getContentModel(['cssID' => serialize(['foo', 'bar'])]); $response = $controller(new Request(), $model, 'main'); $template = json_decode($response->getContent(), true); @@ -160,10 +152,9 @@ public function testSetsTheCssIdAndClassFromTheModel(): void public function testSetsTheLayoutSection(): void { - $controller = new TestController(); - $controller->setContainer($this->mockContainerWithFrameworkTemplate('ce_test')); + $controller = $this->getTestController(); - $response = $controller(new Request(), $this->mockContentModel(), 'left'); + $response = $controller(new Request(), $this->getContentModel(), 'left'); $template = json_decode($response->getContent(), true); $this->assertSame('left', $template['inColumn']); @@ -171,19 +162,71 @@ public function testSetsTheLayoutSection(): void public function testSetsTheClasses(): void { - $controller = new TestController(); - $controller->setContainer($this->mockContainerWithFrameworkTemplate('ce_test')); + $controller = $this->getTestController(); - $response = $controller(new Request(), $this->mockContentModel(), 'main', ['first', 'last']); + $response = $controller(new Request(), $this->getContentModel(), 'main', ['first', 'last']); $template = json_decode($response->getContent(), true); $this->assertSame('ce_test first last', $template['class']); } + /** + * @dataProvider provideScope + */ + public function testSetsTemplateContextForModernFragments(bool $backendScope): void + { + $filesystemLoader = $this->createMock(ContaoFilesystemLoader::class); + $filesystemLoader + ->expects($this->once()) + ->method('exists') + ->with('@Contao/content_element/text.html.twig') + ->willReturn(true) + ; + + $requestStack = $this->createMock(RequestStack::class); + $requestStack + ->method('getCurrentRequest') + ->willReturn($this->createMock(Request::class)) + ; + + $scopeMatcher = $this->createMock(ScopeMatcher::class); + $scopeMatcher + ->method('isBackendRequest') + ->willReturn($backendScope) + ; + + $this->container->set('request_stack', $requestStack); + $this->container->set('contao.twig.filesystem_loader', $filesystemLoader); + $this->container->set('contao.routing.scope_matcher', $scopeMatcher); + + $controller = $this->getTestController(['type' => 'text', 'template' => 'content_element/text']); + + $model = $this->getContentModel([ + 'headline' => serialize(['value' => 'foo', 'unit' => 'h3']), + 'cssID' => serialize(['foo-id', 'foo-class']), + ]); + + $response = $controller(new Request(), $model, 'main'); + $template = json_decode($response->getContent(), true); + + $this->assertSame('text', $template['type']); + $this->assertSame('content_element/text', $template['template']); + $this->assertSame($backendScope, $template['as_overview']); + $this->assertSame('main', $template['section']); + $this->assertSame(['id' => 'foo-id', 'class' => 'foo-class'], $template['attributes']); + $this->assertSame(['value' => 'foo', 'unit' => 'h3'], $template['headline']); + $this->assertSame($model->row(), $template['data']); + } + + public function provideScope(): \Generator + { + yield 'frontend' => [false]; + yield 'backend' => [true]; + } + public function testAddsTheCacheTags(): void { - $model = $this->mockContentModel(); - $model->id = 42; + $model = $this->getContentModel(['id' => 42]); $entityCacheTags = $this->createMock(EntityCacheTags::class); $entityCacheTags @@ -192,11 +235,9 @@ public function testAddsTheCacheTags(): void ->with($model) ; - $container = $this->mockContainerWithFrameworkTemplate('ce_test'); - $container->set('contao.cache.entity_tags', $entityCacheTags); + $this->container->set('contao.cache.entity_tags', $entityCacheTags); - $controller = new TestController(); - $controller->setContainer($container); + $controller = $this->getTestController(); $controller(new Request(), $model, 'main'); } @@ -209,13 +250,10 @@ public function testSetsTheSharedMaxAgeIfTheElementHasAStartDate(): void $start = strtotime('+2 weeks', $time); $expires = $start - $time; - $model = $this->mockContentModel(); - $model->start = (string) $start; - - $container = $this->mockContainerWithFrameworkTemplate('ce_test_shared_max_age'); + $model = $this->getContentModel(['start' => (string) $start]); $controller = new TestSharedMaxAgeController(); - $controller->setContainer($container); + $controller->setContainer($this->container); $response = $controller(new Request(), $model, 'main'); @@ -232,13 +270,10 @@ public function testSetsTheSharedMaxAgeIfTheElementHasAStopDate(): void $stop = strtotime('+2 weeks', $time); $expires = $stop - $time; - $model = $this->mockContentModel(); - $model->stop = (string) $stop; - - $container = $this->mockContainerWithFrameworkTemplate('ce_test_shared_max_age'); + $model = $this->getContentModel(['stop' => (string) $stop]); $controller = new TestSharedMaxAgeController(); - $controller->setContainer($container); + $controller->setContainer($this->container); $response = $controller(new Request(), $model, 'main'); @@ -249,61 +284,30 @@ public function testSetsTheSharedMaxAgeIfTheElementHasAStopDate(): void public function testDoesNotSetTheSharedMaxAgeIfTheElementHasNeitherAStartNorAStopDate(): void { - $container = $this->mockContainerWithFrameworkTemplate('ce_test_shared_max_age'); - $controller = new TestSharedMaxAgeController(); - $controller->setContainer($container); + $controller->setContainer($this->container); - $response = $controller(new Request(), $this->mockContentModel(), 'main'); + $response = $controller(new Request(), $this->getContentModel(), 'main'); $this->assertNull($response->getMaxAge()); } - public function testUsesFragmentTemplateForSubrequests(): void + private function getContentModel(array $data = []): ContentModel { - $framework = $this->mockContaoFramework(); - $framework - ->expects($this->once()) - ->method('createInstance') - ->with(FragmentTemplate::class, ['ce_test']) - ->willReturn(new FragmentTemplate('ce_test')) - ; - - $this->container->set('contao.framework', $framework); - $this->container->set('contao.routing.scope_matcher', $this->mockScopeMatcher()); - - $currentRequest = new Request([], [], ['_scope' => 'frontend']); - - $requestStack = $this->container->get('request_stack'); - $requestStack->push(new Request()); // Main request - $requestStack->push($currentRequest); // Sub request + /** @var ContentModel $model */ + $model = (new \ReflectionClass(ContentModel::class))->newInstanceWithoutConstructor(); + $model->setRow($data); - $controller = new TestController(); - $controller->setContainer($this->container); - - $controller($currentRequest, $this->mockContentModel(), 'main'); + return $model; } - private function mockContainerWithFrameworkTemplate(string $templateName): ContainerBuilder + private function getTestController(array $fragmentOptions = []): TestController { - $framework = $this->mockContaoFramework(); - $framework - ->expects($this->once()) - ->method('createInstance') - ->with(FrontendTemplate::class, [$templateName]) - ->willReturn(new FrontendTemplate($templateName)) - ; - - $this->container->set('contao.framework', $framework); + $controller = new TestController(); - return $this->container; - } + $controller->setContainer($this->container); + $controller->setFragmentOptions($fragmentOptions); - /** - * @return ContentModel&MockObject - */ - private function mockContentModel(): ContentModel - { - return $this->mockClassWithProperties(ContentModel::class); + return $controller; } } diff --git a/core-bundle/tests/Controller/ContentElement/TemplateControllerTest.php b/core-bundle/tests/Controller/ContentElement/TemplateControllerTest.php index 4035bb3101b..d9261914b51 100644 --- a/core-bundle/tests/Controller/ContentElement/TemplateControllerTest.php +++ b/core-bundle/tests/Controller/ContentElement/TemplateControllerTest.php @@ -15,6 +15,7 @@ use Contao\ContentModel; use Contao\CoreBundle\Cache\EntityCacheTags; use Contao\CoreBundle\Controller\ContentElement\TemplateController; +use Contao\CoreBundle\Routing\ScopeMatcher; use Contao\CoreBundle\Tests\TestCase; use Contao\FrontendTemplate; use Symfony\Component\DependencyInjection\Container; @@ -107,6 +108,7 @@ private function mockContainer(array $expectedData, string $expectedTemplate): C $container->set('contao.framework', $framework); $container->set('request_stack', $this->createMock(RequestStack::class)); $container->set('contao.cache.entity_tags', $this->createMock(EntityCacheTags::class)); + $container->set('contao.routing.scope_matcher', $this->createMock(ScopeMatcher::class)); return $container; } diff --git a/core-bundle/tests/Controller/FrontendModule/FrontendModuleControllerTest.php b/core-bundle/tests/Controller/FrontendModule/FrontendModuleControllerTest.php index 22ad0e4c41f..5f692142d46 100644 --- a/core-bundle/tests/Controller/FrontendModule/FrontendModuleControllerTest.php +++ b/core-bundle/tests/Controller/FrontendModule/FrontendModuleControllerTest.php @@ -14,16 +14,18 @@ use Contao\Config; use Contao\CoreBundle\Cache\EntityCacheTags; +use Contao\CoreBundle\Csrf\ContaoCsrfTokenManager; use Contao\CoreBundle\Fixtures\Controller\FrontendModule\TestController; +use Contao\CoreBundle\Routing\ScopeMatcher; use Contao\CoreBundle\Tests\TestCase; -use Contao\FragmentTemplate; -use Contao\FrontendTemplate; +use Contao\CoreBundle\Twig\FragmentTemplate; +use Contao\CoreBundle\Twig\Loader\ContaoFilesystemLoader; use Contao\ModuleModel; use Contao\System; -use PHPUnit\Framework\MockObject\MockObject; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\RequestStack; +use Twig\Environment; class FrontendModuleControllerTest extends TestCase { @@ -50,40 +52,41 @@ protected function tearDown(): void public function testCreatesTheTemplateFromTheClassName(): void { - $controller = new TestController(); - $controller->setContainer($this->mockContainerWithFrameworkTemplate('mod_test')); + $controller = $this->getTestController(); + + $response = $controller(new Request([], [], ['_scope' => 'frontend']), $this->getModuleModel(), 'main'); + $template = json_decode($response->getContent(), true); - $controller(new Request([], [], ['_scope' => 'frontend']), $this->getModuleModel(), 'main'); + $this->assertSame('mod_test', $template['templateName']); } public function testCreatesTheTemplateFromTheTypeFragmentOption(): void { - $controller = new TestController(); - $controller->setContainer($this->mockContainerWithFrameworkTemplate('mod_foo')); - $controller->setFragmentOptions(['type' => 'foo']); + $controller = $this->getTestController(['type' => 'foo']); + + $response = $controller(new Request(), $this->getModuleModel(), 'main'); + $template = json_decode($response->getContent(), true); - $controller(new Request(), $this->getModuleModel(), 'main'); + $this->assertSame('mod_foo', $template['templateName']); } public function testCreatesTheTemplateFromTheTemplateFragmentOption(): void { - $controller = new TestController(); - $controller->setContainer($this->mockContainerWithFrameworkTemplate('mod_bar')); - $controller->setFragmentOptions(['template' => 'mod_bar']); + $controller = $this->getTestController(['template' => 'mod_bar']); + + $response = $controller(new Request(), $this->getModuleModel(), 'main'); + $template = json_decode($response->getContent(), true); - $controller(new Request(), $this->getModuleModel(), 'main'); + $this->assertSame('mod_bar', $template['templateName']); } public function testCreatesTheTemplateFromACustomTpl(): void { - $model = $this->getModuleModel(); - $model->customTpl = 'mod_bar'; + $this->container->set('request_stack', new RequestStack()); - $container = $this->mockContainerWithFrameworkTemplate('mod_bar'); - $container->set('request_stack', new RequestStack()); + $controller = $this->getTestController(); - $controller = new TestController(); - $controller->setContainer($container); + $model = $this->getModuleModel(['customTpl' => 'mod_bar']); $response = $controller(new Request(), $model, 'main'); $template = json_decode($response->getContent(), true); @@ -93,8 +96,7 @@ public function testCreatesTheTemplateFromACustomTpl(): void public function testSetsTheClassFromTheType(): void { - $controller = new TestController(); - $controller->setContainer($this->mockContainerWithFrameworkTemplate('mod_test')); + $controller = $this->getTestController(); $response = $controller(new Request(), $this->getModuleModel(), 'main'); $template = json_decode($response->getContent(), true); @@ -105,11 +107,9 @@ public function testSetsTheClassFromTheType(): void public function testSetsTheHeadlineFromTheModel(): void { - $model = $this->getModuleModel(); - $model->headline = serialize(['unit' => 'h6', 'value' => 'foobar']); + $controller = $this->getTestController(); - $controller = new TestController(); - $controller->setContainer($this->mockContainerWithFrameworkTemplate('mod_test')); + $model = $this->getModuleModel(['headline' => serialize(['unit' => 'h6', 'value' => 'foobar'])]); $response = $controller(new Request(), $model, 'main'); $template = json_decode($response->getContent(), true); @@ -120,11 +120,9 @@ public function testSetsTheHeadlineFromTheModel(): void public function testSetsTheCssIdAndClassFromTheModel(): void { - $model = $this->getModuleModel(); - $model->cssID = serialize(['foo', 'bar']); + $controller = $this->getTestController(); - $controller = new TestController(); - $controller->setContainer($this->mockContainerWithFrameworkTemplate('mod_test')); + $model = $this->getModuleModel(['cssID' => serialize(['foo', 'bar'])]); $response = $controller(new Request(), $model, 'main'); $template = json_decode($response->getContent(), true); @@ -135,8 +133,7 @@ public function testSetsTheCssIdAndClassFromTheModel(): void public function testSetsTheLayoutSection(): void { - $controller = new TestController(); - $controller->setContainer($this->mockContainerWithFrameworkTemplate('mod_test')); + $controller = $this->getTestController(); $response = $controller(new Request(), $this->getModuleModel(), 'left'); $template = json_decode($response->getContent(), true); @@ -144,6 +141,111 @@ public function testSetsTheLayoutSection(): void $this->assertSame('left', $template['inColumn']); } + public function testSetsTemplateContextForModernFragments(): void + { + $filesystemLoader = $this->createMock(ContaoFilesystemLoader::class); + $filesystemLoader + ->expects($this->once()) + ->method('exists') + ->with('@Contao/frontend_module/html.html.twig') + ->willReturn(true) + ; + + $requestStack = $this->createMock(RequestStack::class); + $requestStack + ->method('getCurrentRequest') + ->willReturn($this->createMock(Request::class)) + ; + + $scopeMatcher = $this->createMock(ScopeMatcher::class); + $scopeMatcher + ->method('isBackendRequest') + ->willReturn(false) + ; + + $this->container->set('request_stack', $requestStack); + $this->container->set('contao.twig.filesystem_loader', $filesystemLoader); + $this->container->set('contao.routing.scope_matcher', $scopeMatcher); + + $controller = $this->getTestController(['type' => 'html', 'template' => 'frontend_module/html']); + + $model = $this->getModuleModel([ + 'headline' => serialize(['value' => 'foo', 'unit' => 'h3']), + 'cssID' => serialize(['foo-id', 'foo-class']), + ]); + + $response = $controller(new Request(), $model, 'main'); + $template = json_decode($response->getContent(), true); + + $this->assertSame('html', $template['type']); + $this->assertSame('frontend_module/html', $template['template']); + $this->assertFalse($template['as_overview']); + $this->assertSame('main', $template['section']); + $this->assertSame('main', $template['section']); + $this->assertSame(['id' => 'foo-id', 'class' => 'foo-class'], $template['attributes']); + $this->assertSame(['value' => 'foo', 'unit' => 'h3'], $template['headline']); + $this->assertSame($model->row(), $template['data']); + } + + public function testReturnsWildCardInBackendScope(): void + { + $requestStack = $this->createMock(RequestStack::class); + $requestStack + ->method('getCurrentRequest') + ->willReturn($this->createMock(Request::class)) + ; + + $scopeMatcher = $this->createMock(ScopeMatcher::class); + $scopeMatcher + ->method('isBackendRequest') + ->willReturn(true) + ; + + $tokenManager = $this->createMock(ContaoCsrfTokenManager::class); + $tokenManager + ->method('getDefaultTokenValue') + ->willReturn('') + ; + + $twig = $this->createMock(Environment::class); + $twig + ->method('render') + ->with( + '@Contao/back_end/module_wildcard.html.twig', + [ + 'id' => 42, + 'name' => 'foo', + 'title' => 'foo headline', + 'request_token' => '', + ], + ) + ->willReturn('') + ; + + $this->container->set('request_stack', $requestStack); + $this->container->set('contao.routing.scope_matcher', $scopeMatcher); + $this->container->set('contao.csrf.token_manager', $tokenManager); + $this->container->set('twig', $twig); + + $controller = $this->getTestController(); + + $model = $this->getModuleModel([ + 'id' => 42, + 'name' => 'foo', + 'headline' => serialize(['value' => 'foo headline', 'unit' => 'h3']), + ]); + + $response = $controller(new Request(), $model, 'main'); + + $this->assertSame('', $response->getContent()); + } + + public function provideScope(): \Generator + { + yield 'frontend' => [false]; + yield 'backend' => [true]; + } + public function testAddsTheCacheTags(): void { $model = $this->getModuleModel(); @@ -165,52 +267,43 @@ public function testAddsTheCacheTags(): void $controller(new Request(), $model, 'main'); } - public function testUsesFragmentTemplateForSubrequests(): void + private function mockContainerWithFrameworkTemplate(string $templateName): ContainerBuilder { $framework = $this->mockContaoFramework(); $framework - ->expects($this->once()) ->method('createInstance') - ->with(FragmentTemplate::class, ['mod_test']) - ->willReturn(new FragmentTemplate('mod_test')) + ->willReturnCallback( + function (string $class, array $params) use ($templateName): FragmentTemplate { + $this->assertSame(FragmentTemplate::class, $class); + $this->assertSame($templateName, $params[0]); + + return new FragmentTemplate(...$params); + } + ) ; $this->container->set('contao.framework', $framework); $this->container->set('contao.routing.scope_matcher', $this->mockScopeMatcher()); - $currentRequest = new Request([], [], ['_scope' => 'frontend']); - - $requestStack = $this->container->get('request_stack'); - $requestStack->push(new Request()); // Main request - $requestStack->push($currentRequest); // Sub request - - $controller = new TestController(); - $controller->setContainer($this->container); - - $controller($currentRequest, $this->getModuleModel(), 'main'); + return $this->container; } - private function mockContainerWithFrameworkTemplate(string $templateName): ContainerBuilder + private function getModuleModel(array $data = []): ModuleModel { - $framework = $this->mockContaoFramework(); - $framework - ->expects($this->once()) - ->method('createInstance') - ->with(FrontendTemplate::class, [$templateName]) - ->willReturn(new FrontendTemplate($templateName)) - ; + /** @var ModuleModel $model */ + $model = (new \ReflectionClass(ModuleModel::class))->newInstanceWithoutConstructor(); + $model->setRow($data); - $this->container->set('contao.framework', $framework); - $this->container->set('contao.routing.scope_matcher', $this->mockScopeMatcher()); - - return $this->container; + return $model; } - /** - * @return ModuleModel&MockObject - */ - private function getModuleModel(): ModuleModel + private function getTestController(array $fragmentOptions = []): TestController { - return $this->mockClassWithProperties(ModuleModel::class); + $controller = new TestController(); + + $controller->setContainer($this->container); + $controller->setFragmentOptions($fragmentOptions); + + return $controller; } } diff --git a/core-bundle/tests/Controller/FrontendModule/RootPageDependentModulesControllerTest.php b/core-bundle/tests/Controller/FrontendModule/RootPageDependentModulesControllerTest.php index d2e1f8fc724..8a2d69b8448 100644 --- a/core-bundle/tests/Controller/FrontendModule/RootPageDependentModulesControllerTest.php +++ b/core-bundle/tests/Controller/FrontendModule/RootPageDependentModulesControllerTest.php @@ -16,7 +16,6 @@ use Contao\Controller; use Contao\CoreBundle\Cache\EntityCacheTags; use Contao\CoreBundle\Controller\FrontendModule\RootPageDependentModulesController; -use Contao\CoreBundle\Routing\ScopeMatcher; use Contao\CoreBundle\Tests\TestCase; use Contao\ModuleModel; use Contao\PageModel; @@ -27,8 +26,6 @@ use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\RequestStack; -use Symfony\Component\Routing\RouterInterface; -use Symfony\Contracts\Translation\TranslatorInterface; class RootPageDependentModulesControllerTest extends TestCase { @@ -53,53 +50,6 @@ protected function tearDown(): void parent::tearDown(); } - public function testReturnsWildCardWhenBackendRequest(): void - { - $scopeMatcher = $this->createMock(ScopeMatcher::class); - $scopeMatcher - ->expects($this->once()) - ->method('isBackendRequest') - ->willReturn(true) - ; - - $router = $this->createMock(RouterInterface::class); - $router - ->expects($this->once()) - ->method('generate') - ; - - $translator = $this->createMock(TranslatorInterface::class); - $translator - ->expects($this->once()) - ->method('trans') - ->willReturn('Root Page Dependent Modules') - ; - - $container = $this->mockContainer(); - $container->set('contao.routing.scope_matcher', $scopeMatcher); - $container->set('router', $router); - $container->set('translator', $translator); - $container->setParameter('kernel.project_dir', \dirname(__DIR__, 3)); - - TemplateLoader::addFile('be_wildcard', 'src/Resources/contao/templates/backend'); - - $controller = new RootPageDependentModulesController(); - $controller->setContainer($container); - - $response = $controller(new Request([], [], ['_scope' => 'backend']), $this->getModuleModel(), 'main'); - - $expectedOutput = - <<<'OUTPUT' -
- ### ROOT PAGE DEPENDENT MODULES ### -
- OUTPUT; - - $minifiedExpectedOutput = preg_replace(['/\s\s|\n/', '/\sassertSame($minifiedExpectedOutput, preg_replace(['/\s\s|\n/', '/\sgetContent())); - } - public function testReturnsEmptyResponse(): void { $controller = new RootPageDependentModulesController(); From bea6eef42581ae406cfc796ad2e6ae0f0bae6701 Mon Sep 17 00:00:00 2001 From: "M. Vondano" Date: Sun, 3 Apr 2022 16:54:03 +0200 Subject: [PATCH 10/28] fix quoting in the TemplateLoader --- .../src/Resources/contao/library/Contao/TemplateLoader.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core-bundle/src/Resources/contao/library/Contao/TemplateLoader.php b/core-bundle/src/Resources/contao/library/Contao/TemplateLoader.php index 2e117b454b4..23c3981e3b5 100644 --- a/core-bundle/src/Resources/contao/library/Contao/TemplateLoader.php +++ b/core-bundle/src/Resources/contao/library/Contao/TemplateLoader.php @@ -75,6 +75,8 @@ public static function getFiles() */ public static function getPrefixedFiles($prefix) { + $prefix = preg_quote($prefix, '/'); + if (substr($prefix, -1) != '_') { $prefix .= '($|_)'; From 31f10846df5f877f12e216487081d731da8f1305 Mon Sep 17 00:00:00 2001 From: "M. Vondano" Date: Sun, 3 Apr 2022 16:55:25 +0200 Subject: [PATCH 11/28] fix a discouraged usage in the TwoFactorController --- .../src/Controller/FrontendModule/TwoFactorController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core-bundle/src/Controller/FrontendModule/TwoFactorController.php b/core-bundle/src/Controller/FrontendModule/TwoFactorController.php index 8b6f380a086..c41e68561a4 100644 --- a/core-bundle/src/Controller/FrontendModule/TwoFactorController.php +++ b/core-bundle/src/Controller/FrontendModule/TwoFactorController.php @@ -125,7 +125,7 @@ protected function getResponse(Template $template, ModuleModel $model, Request $ $template->href = $this->pageModel->getAbsoluteUrl().'?2fa=enable'; $template->trustedDevices = $this->container->get('contao.security.two_factor.trusted_device_manager')->getTrustedDevices($user); - return new Response($template->parse()); + return $template->getResponse(); } private function enableTwoFactor(Template $template, Request $request, FrontendUser $user, string $return): Response|null From 3e3cb3144ab87868f2ac0220bb94d79af3403f06 Mon Sep 17 00:00:00 2001 From: "M. Vondano" Date: Mon, 4 Apr 2022 21:53:40 +0200 Subject: [PATCH 12/28] rename headline attributes --- .../ContentElement/AbstractContentElementController.php | 4 ++-- .../FrontendModule/AbstractFrontendModuleController.php | 4 ++-- .../ContentElement/ContentElementControllerTest.php | 2 +- .../FrontendModule/FrontendModuleControllerTest.php | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/core-bundle/src/Controller/ContentElement/AbstractContentElementController.php b/core-bundle/src/Controller/ContentElement/AbstractContentElementController.php index 47e26e23c0e..a8782b8a5f3 100644 --- a/core-bundle/src/Controller/ContentElement/AbstractContentElementController.php +++ b/core-bundle/src/Controller/ContentElement/AbstractContentElementController.php @@ -93,8 +93,8 @@ protected function addDefaultDataToTemplate(FragmentTemplate $template, array $m ->setIfExists('id', $attributesData[0] ?? null) ->addClass($attributesData[1] ?? '', ...$classes), 'headline' => [ - 'value' => $headlineData['value'] ?? '', - 'unit' => $headlineData['unit'] ?? 'h1', + 'text' => $headlineData['value'] ?? '', + 'tagName' => $headlineData['unit'] ?? 'h1', ], ]); } diff --git a/core-bundle/src/Controller/FrontendModule/AbstractFrontendModuleController.php b/core-bundle/src/Controller/FrontendModule/AbstractFrontendModuleController.php index aea5297920a..67a261ae3e1 100644 --- a/core-bundle/src/Controller/FrontendModule/AbstractFrontendModuleController.php +++ b/core-bundle/src/Controller/FrontendModule/AbstractFrontendModuleController.php @@ -100,8 +100,8 @@ protected function addDefaultDataToTemplate(FragmentTemplate $template, array $m ->setIfExists('id', $attributesData[0] ?? null) ->addClass($attributesData[1] ?? '', ...$classes), 'headline' => [ - 'value' => $headlineData['value'] ?? '', - 'unit' => $headlineData['unit'] ?? 'h1', + 'text' => $headlineData['value'] ?? '', + 'tagName' => $headlineData['unit'] ?? 'h1', ], ]); } diff --git a/core-bundle/tests/Controller/ContentElement/ContentElementControllerTest.php b/core-bundle/tests/Controller/ContentElement/ContentElementControllerTest.php index 1f868dd59d6..848fed7d4bf 100644 --- a/core-bundle/tests/Controller/ContentElement/ContentElementControllerTest.php +++ b/core-bundle/tests/Controller/ContentElement/ContentElementControllerTest.php @@ -214,7 +214,7 @@ public function testSetsTemplateContextForModernFragments(bool $backendScope): v $this->assertSame($backendScope, $template['as_overview']); $this->assertSame('main', $template['section']); $this->assertSame(['id' => 'foo-id', 'class' => 'foo-class'], $template['attributes']); - $this->assertSame(['value' => 'foo', 'unit' => 'h3'], $template['headline']); + $this->assertSame(['text' => 'foo', 'tagName' => 'h3'], $template['headline']); $this->assertSame($model->row(), $template['data']); } diff --git a/core-bundle/tests/Controller/FrontendModule/FrontendModuleControllerTest.php b/core-bundle/tests/Controller/FrontendModule/FrontendModuleControllerTest.php index 5f692142d46..e3695e43e49 100644 --- a/core-bundle/tests/Controller/FrontendModule/FrontendModuleControllerTest.php +++ b/core-bundle/tests/Controller/FrontendModule/FrontendModuleControllerTest.php @@ -183,7 +183,7 @@ public function testSetsTemplateContextForModernFragments(): void $this->assertSame('main', $template['section']); $this->assertSame('main', $template['section']); $this->assertSame(['id' => 'foo-id', 'class' => 'foo-class'], $template['attributes']); - $this->assertSame(['value' => 'foo', 'unit' => 'h3'], $template['headline']); + $this->assertSame(['text' => 'foo', 'tagName' => 'h3'], $template['headline']); $this->assertSame($model->row(), $template['data']); } From d9498d0b13bc0d3a808d7881d02c2519e940305f Mon Sep 17 00:00:00 2001 From: "M. Vondano" Date: Mon, 4 Apr 2022 21:53:55 +0200 Subject: [PATCH 13/28] fix testReturnsWildCardInBackendScope test --- .../Controller/FrontendModule/FrontendModuleControllerTest.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core-bundle/tests/Controller/FrontendModule/FrontendModuleControllerTest.php b/core-bundle/tests/Controller/FrontendModule/FrontendModuleControllerTest.php index e3695e43e49..c4fcefa7aef 100644 --- a/core-bundle/tests/Controller/FrontendModule/FrontendModuleControllerTest.php +++ b/core-bundle/tests/Controller/FrontendModule/FrontendModuleControllerTest.php @@ -217,6 +217,7 @@ public function testReturnsWildCardInBackendScope(): void 'name' => 'foo', 'title' => 'foo headline', 'request_token' => '', + 'type' => 'foobar', ], ) ->willReturn('') @@ -231,6 +232,7 @@ public function testReturnsWildCardInBackendScope(): void $model = $this->getModuleModel([ 'id' => 42, + 'type' => 'foobar', 'name' => 'foo', 'headline' => serialize(['value' => 'foo headline', 'unit' => 'h3']), ]); From d12a7d019fa6e9ea8d7a8043c26f3985e53fb5fd Mon Sep 17 00:00:00 2001 From: "M. Vondano" Date: Mon, 4 Apr 2022 21:56:33 +0200 Subject: [PATCH 14/28] CS --- .../FrontendModule/AbstractFrontendModuleController.php | 1 - 1 file changed, 1 deletion(-) diff --git a/core-bundle/src/Controller/FrontendModule/AbstractFrontendModuleController.php b/core-bundle/src/Controller/FrontendModule/AbstractFrontendModuleController.php index 67a261ae3e1..ba4061f061b 100644 --- a/core-bundle/src/Controller/FrontendModule/AbstractFrontendModuleController.php +++ b/core-bundle/src/Controller/FrontendModule/AbstractFrontendModuleController.php @@ -18,7 +18,6 @@ use Contao\CoreBundle\Twig\FragmentTemplate; use Contao\ModuleModel; use Contao\StringUtil; -use Contao\Template; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; From 6187b5426c18f5cc85ebbf9325b7c7fffb8f45d4 Mon Sep 17 00:00:00 2001 From: "M. Vondano" Date: Tue, 5 Apr 2022 12:17:45 +0200 Subject: [PATCH 15/28] add clarifying comment --- core-bundle/src/EventListener/GlobalsMapListener.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/core-bundle/src/EventListener/GlobalsMapListener.php b/core-bundle/src/EventListener/GlobalsMapListener.php index 8d50b0ec7ca..ce2fcb5ffe8 100644 --- a/core-bundle/src/EventListener/GlobalsMapListener.php +++ b/core-bundle/src/EventListener/GlobalsMapListener.php @@ -23,6 +23,11 @@ public function __construct(private array $globals) /** * Maps fragments to the globals array. + * + * Note: Existing elements in $GLOBALS[$key] won't be overwritten. This + * allows opting out of our new content elements/modules as fragments by + * creating a $GLOBALS definition that points to the respective legacy + * class. */ public function onInitializeSystem(): void { From 31e4082515b72ed0ca9d78da9ac3f006e9ebac61 Mon Sep 17 00:00:00 2001 From: Leo Feyer Date: Tue, 5 Apr 2022 18:42:04 +0200 Subject: [PATCH 16/28] CS --- core-bundle/src/Controller/AbstractFragmentController.php | 4 ++-- core-bundle/src/Twig/FragmentTemplate.php | 5 +++-- core-bundle/tests/Twig/FragmentTemplateTest.php | 3 +-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/core-bundle/src/Controller/AbstractFragmentController.php b/core-bundle/src/Controller/AbstractFragmentController.php index 38fde6ac206..9c324ad89fb 100644 --- a/core-bundle/src/Controller/AbstractFragmentController.php +++ b/core-bundle/src/Controller/AbstractFragmentController.php @@ -71,7 +71,7 @@ protected function getPageModel(): PageModel|null * "customTpl" field of the model and registers the effective template as * default view when using render(). * - * Calling getResponse() method on the returned object will internally call + * Calling getResponse() on the returned object will internally call * render() with the set parameters and return the response. * * Note: The $fallbackTemplateName argument will be removed in Contao 6; @@ -211,7 +211,7 @@ protected function getType(): string /** * Renders a template. If $view is set to null, the default template of - * this fragment will rendered. + * this fragment will be rendered. * * By default, the returned response will have the appropriate headers set, * that allow our SubrequestCacheSubscriber to merge it with others of the diff --git a/core-bundle/src/Twig/FragmentTemplate.php b/core-bundle/src/Twig/FragmentTemplate.php index 7271a3d58e1..9562ee7d8ce 100644 --- a/core-bundle/src/Twig/FragmentTemplate.php +++ b/core-bundle/src/Twig/FragmentTemplate.php @@ -21,8 +21,10 @@ /** * This class is a simple container object for template data. + * + * @todo Remove the base class in Contao 6 */ -final class FragmentTemplate /* TODO: remove base class in Contao 6 */ extends Template +final class FragmentTemplate extends Template { /** * @var array @@ -33,7 +35,6 @@ final class FragmentTemplate /* TODO: remove base class in Contao 6 */ extends T * @param \Closure(self, Response|null):Response $onGetResponse * * @internal - * @noinspection MagicMethodsValidityInspection */ public function __construct(private string $templateName, private \Closure $onGetResponse) { diff --git a/core-bundle/tests/Twig/FragmentTemplateTest.php b/core-bundle/tests/Twig/FragmentTemplateTest.php index 63c90b3bc3f..82a7637e9f6 100644 --- a/core-bundle/tests/Twig/FragmentTemplateTest.php +++ b/core-bundle/tests/Twig/FragmentTemplateTest.php @@ -21,8 +21,8 @@ class FragmentTemplateTest extends TestCase public function testSetAndGetValues(): void { $template = $this->getFragmentTemplate(); - $this->assertSame('content_element/text', $template->getName()); + $template->setName('content_element/foobar'); $this->assertSame('content_element/foobar', $template->getName()); @@ -78,7 +78,6 @@ public function testDisallowsAccessOfParentMethods(string $method, array $args): public function provideIllegalParentMethods(): \Generator { $excluded = ['__construct', '__set', '__get', '__isset', 'setData', 'getData', 'setName', 'getName', 'getResponse']; - $parent = (new \ReflectionClass(FragmentTemplate::class))->getParentClass(); foreach ($parent->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) { From 1fe0bf9f90bd630946d6b59fd15f1d43f34b0a07 Mon Sep 17 00:00:00 2001 From: "M. Vondano" Date: Wed, 6 Apr 2022 11:14:39 +0200 Subject: [PATCH 17/28] enable deprecation warning --- core-bundle/src/Controller/AbstractFragmentController.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/core-bundle/src/Controller/AbstractFragmentController.php b/core-bundle/src/Controller/AbstractFragmentController.php index 9c324ad89fb..342581db2b9 100644 --- a/core-bundle/src/Controller/AbstractFragmentController.php +++ b/core-bundle/src/Controller/AbstractFragmentController.php @@ -82,8 +82,7 @@ protected function createTemplate(Model $model, string|null $fallbackTemplateNam $templateName = $this->getTemplateName($model, $fallbackTemplateName); if ($isLegacyTemplate = $this->isLegacyTemplate($templateName)) { - // TODO: enable deprecation once existing fragments have been adjusted - // trigger_deprecation('contao/core-bundle', '5.0', 'Creating fragments with legacy templates is deprecated and will not work anymore in Contao 6.'); + trigger_deprecation('contao/core-bundle', '5.0', 'Creating fragments with legacy templates is deprecated and will not work anymore in Contao 6.'); } // Allow calling render() without a view From 9ad787224c495e21f1a3f7fb7f0f80bcc4abd20b Mon Sep 17 00:00:00 2001 From: "M. Vondano" Date: Wed, 6 Apr 2022 11:33:34 +0200 Subject: [PATCH 18/28] rename 'back end' to 'backend' --- .../FrontendModule/AbstractFrontendModuleController.php | 2 +- .../_new/{back_end => backend}/module_wildcard.html.twig | 0 .../Controller/FrontendModule/FrontendModuleControllerTest.php | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) rename core-bundle/src/Resources/contao/templates/_new/{back_end => backend}/module_wildcard.html.twig (100%) diff --git a/core-bundle/src/Controller/FrontendModule/AbstractFrontendModuleController.php b/core-bundle/src/Controller/FrontendModule/AbstractFrontendModuleController.php index ba4061f061b..99d8403cbe4 100644 --- a/core-bundle/src/Controller/FrontendModule/AbstractFrontendModuleController.php +++ b/core-bundle/src/Controller/FrontendModule/AbstractFrontendModuleController.php @@ -63,7 +63,7 @@ protected function getBackendWildcard(ModuleModel $module): Response 'request_token' => $this->container->get('contao.csrf.token_manager')->getDefaultTokenValue(), ]; - return $this->render('@Contao/back_end/module_wildcard.html.twig', $context); + return $this->render('@Contao/backend/module_wildcard.html.twig', $context); } /** diff --git a/core-bundle/src/Resources/contao/templates/_new/back_end/module_wildcard.html.twig b/core-bundle/src/Resources/contao/templates/_new/backend/module_wildcard.html.twig similarity index 100% rename from core-bundle/src/Resources/contao/templates/_new/back_end/module_wildcard.html.twig rename to core-bundle/src/Resources/contao/templates/_new/backend/module_wildcard.html.twig diff --git a/core-bundle/tests/Controller/FrontendModule/FrontendModuleControllerTest.php b/core-bundle/tests/Controller/FrontendModule/FrontendModuleControllerTest.php index c4fcefa7aef..e0601f36dd6 100644 --- a/core-bundle/tests/Controller/FrontendModule/FrontendModuleControllerTest.php +++ b/core-bundle/tests/Controller/FrontendModule/FrontendModuleControllerTest.php @@ -211,7 +211,7 @@ public function testReturnsWildCardInBackendScope(): void $twig ->method('render') ->with( - '@Contao/back_end/module_wildcard.html.twig', + '@Contao/backend/module_wildcard.html.twig', [ 'id' => 42, 'name' => 'foo', From 23fecb1232849b0c82267e12fa3cd2c2f1cd78b2 Mon Sep 17 00:00:00 2001 From: "M. Vondano" Date: Wed, 6 Apr 2022 11:42:26 +0200 Subject: [PATCH 19/28] expect self deprecations --- .../ContentElementControllerTest.php | 73 +++++++++++++++++++ .../ContentElement/MarkdownControllerTest.php | 18 +++++ .../ContentElement/TemplateControllerTest.php | 18 +++++ .../FrontendModuleControllerTest.php | 48 ++++++++++++ .../TwoFactorControllerTest.php | 53 ++++++++++++++ 5 files changed, 210 insertions(+) diff --git a/core-bundle/tests/Controller/ContentElement/ContentElementControllerTest.php b/core-bundle/tests/Controller/ContentElement/ContentElementControllerTest.php index 848fed7d4bf..5c6b9512785 100644 --- a/core-bundle/tests/Controller/ContentElement/ContentElementControllerTest.php +++ b/core-bundle/tests/Controller/ContentElement/ContentElementControllerTest.php @@ -22,12 +22,15 @@ use Contao\CoreBundle\Twig\Loader\ContaoFilesystemLoader; use Contao\System; use Symfony\Bridge\PhpUnit\ClockMock; +use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\RequestStack; class ContentElementControllerTest extends TestCase { + use ExpectDeprecationTrait; + private ContainerBuilder $container; protected function setUp(): void @@ -50,8 +53,13 @@ protected function tearDown(): void parent::tearDown(); } + /** + * @group legacy + */ public function testCreatesTheTemplateFromTheClassName(): void { + $this->expectDeprecation('Since contao/core-bundle 5.0: Creating fragments with legacy templates is deprecated and will not work anymore in Contao 6.'); + $controller = $this->getTestController(); $response = $controller(new Request(), $this->getContentModel(), 'main'); @@ -60,8 +68,13 @@ public function testCreatesTheTemplateFromTheClassName(): void $this->assertSame('ce_test', $template['templateName']); } + /** + * @group legacy + */ public function testCreatesTheTemplateFromTheTypeFragmentOptions(): void { + $this->expectDeprecation('Since contao/core-bundle 5.0: Creating fragments with legacy templates is deprecated and will not work anymore in Contao 6.'); + $controller = $this->getTestController(['type' => 'foo']); $response = $controller(new Request(), $this->getContentModel(), 'main'); @@ -70,8 +83,13 @@ public function testCreatesTheTemplateFromTheTypeFragmentOptions(): void $this->assertSame('ce_foo', $template['templateName']); } + /** + * @group legacy + */ public function testCreatesTheTemplateFromTheTemplateFragmentOption(): void { + $this->expectDeprecation('Since contao/core-bundle 5.0: Creating fragments with legacy templates is deprecated and will not work anymore in Contao 6.'); + $controller = $this->getTestController(['template' => 'ce_bar']); $response = $controller(new Request(), $this->getContentModel(), 'main'); @@ -80,8 +98,13 @@ public function testCreatesTheTemplateFromTheTemplateFragmentOption(): void $this->assertSame('ce_bar', $template['templateName']); } + /** + * @group legacy + */ public function testCreatesTheTemplateFromACustomTpl(): void { + $this->expectDeprecation('Since contao/core-bundle 5.0: Creating fragments with legacy templates is deprecated and will not work anymore in Contao 6.'); + $this->container->set('request_stack', new RequestStack()); $controller = $this->getTestController(['template' => 'ce_bar']); @@ -94,8 +117,13 @@ public function testCreatesTheTemplateFromACustomTpl(): void $this->assertSame('ce_bar', $template['templateName']); } + /** + * @group legacy + */ public function testDoesNotCreateTheTemplateFromACustomTplInTheBackend(): void { + $this->expectDeprecation('Since contao/core-bundle 5.0: Creating fragments with legacy templates is deprecated and will not work anymore in Contao 6.'); + $request = new Request([], [], ['_scope' => 'backend']); $requestStack = new RequestStack(); $requestStack->push($request); @@ -113,8 +141,13 @@ public function testDoesNotCreateTheTemplateFromACustomTplInTheBackend(): void $this->assertSame('ce_test', $template['templateName']); } + /** + * @group legacy + */ public function testSetsTheClassFromTheType(): void { + $this->expectDeprecation('Since contao/core-bundle 5.0: Creating fragments with legacy templates is deprecated and will not work anymore in Contao 6.'); + $controller = $this->getTestController(); $response = $controller(new Request(), $this->getContentModel(), 'main'); @@ -124,8 +157,13 @@ public function testSetsTheClassFromTheType(): void $this->assertSame('ce_test', $template['class']); } + /** + * @group legacy + */ public function testSetsTheHeadlineFromTheModel(): void { + $this->expectDeprecation('Since contao/core-bundle 5.0: Creating fragments with legacy templates is deprecated and will not work anymore in Contao 6.'); + $controller = $this->getTestController(); $model = $this->getContentModel(['headline' => serialize(['unit' => 'h6', 'value' => 'foobar'])]); @@ -137,8 +175,13 @@ public function testSetsTheHeadlineFromTheModel(): void $this->assertSame('h6', $template['hl']); } + /** + * @group legacy + */ public function testSetsTheCssIdAndClassFromTheModel(): void { + $this->expectDeprecation('Since contao/core-bundle 5.0: Creating fragments with legacy templates is deprecated and will not work anymore in Contao 6.'); + $controller = $this->getTestController(); $model = $this->getContentModel(['cssID' => serialize(['foo', 'bar'])]); @@ -150,8 +193,13 @@ public function testSetsTheCssIdAndClassFromTheModel(): void $this->assertSame('ce_test bar', $template['class']); } + /** + * @group legacy + */ public function testSetsTheLayoutSection(): void { + $this->expectDeprecation('Since contao/core-bundle 5.0: Creating fragments with legacy templates is deprecated and will not work anymore in Contao 6.'); + $controller = $this->getTestController(); $response = $controller(new Request(), $this->getContentModel(), 'left'); @@ -160,8 +208,13 @@ public function testSetsTheLayoutSection(): void $this->assertSame('left', $template['inColumn']); } + /** + * @group legacy + */ public function testSetsTheClasses(): void { + $this->expectDeprecation('Since contao/core-bundle 5.0: Creating fragments with legacy templates is deprecated and will not work anymore in Contao 6.'); + $controller = $this->getTestController(); $response = $controller(new Request(), $this->getContentModel(), 'main', ['first', 'last']); @@ -224,8 +277,13 @@ public function provideScope(): \Generator yield 'backend' => [true]; } + /** + * @group legacy + */ public function testAddsTheCacheTags(): void { + $this->expectDeprecation('Since contao/core-bundle 5.0: Creating fragments with legacy templates is deprecated and will not work anymore in Contao 6.'); + $model = $this->getContentModel(['id' => 42]); $entityCacheTags = $this->createMock(EntityCacheTags::class); @@ -242,8 +300,13 @@ public function testAddsTheCacheTags(): void $controller(new Request(), $model, 'main'); } + /** + * @group legacy + */ public function testSetsTheSharedMaxAgeIfTheElementHasAStartDate(): void { + $this->expectDeprecation('Since contao/core-bundle 5.0: Creating fragments with legacy templates is deprecated and will not work anymore in Contao 6.'); + ClockMock::withClockMock(true); $time = time(); @@ -262,8 +325,13 @@ public function testSetsTheSharedMaxAgeIfTheElementHasAStartDate(): void ClockMock::withClockMock(false); } + /** + * @group legacy + */ public function testSetsTheSharedMaxAgeIfTheElementHasAStopDate(): void { + $this->expectDeprecation('Since contao/core-bundle 5.0: Creating fragments with legacy templates is deprecated and will not work anymore in Contao 6.'); + ClockMock::withClockMock(true); $time = time(); @@ -282,8 +350,13 @@ public function testSetsTheSharedMaxAgeIfTheElementHasAStopDate(): void ClockMock::withClockMock(false); } + /** + * @group legacy + */ public function testDoesNotSetTheSharedMaxAgeIfTheElementHasNeitherAStartNorAStopDate(): void { + $this->expectDeprecation('Since contao/core-bundle 5.0: Creating fragments with legacy templates is deprecated and will not work anymore in Contao 6.'); + $controller = new TestSharedMaxAgeController(); $controller->setContainer($this->container); diff --git a/core-bundle/tests/Controller/ContentElement/MarkdownControllerTest.php b/core-bundle/tests/Controller/ContentElement/MarkdownControllerTest.php index ce0e728e7f4..7e48c4707c2 100644 --- a/core-bundle/tests/Controller/ContentElement/MarkdownControllerTest.php +++ b/core-bundle/tests/Controller/ContentElement/MarkdownControllerTest.php @@ -21,6 +21,7 @@ use Contao\FrontendTemplate; use Contao\Input; use Contao\System; +use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait; use Symfony\Component\DependencyInjection\Container; use Symfony\Component\Filesystem\Filesystem; use Symfony\Component\HttpFoundation\Request; @@ -28,6 +29,8 @@ class MarkdownControllerTest extends TestCase { + use ExpectDeprecationTrait; + protected function tearDown(): void { $this->resetStaticProperties([System::class]); @@ -35,8 +38,13 @@ protected function tearDown(): void parent::tearDown(); } + /** + * @group legacy + */ public function testWithCodeInput(): void { + $this->expectDeprecation('Since contao/core-bundle 5.0: Creating fragments with legacy templates is deprecated and will not work anymore in Contao 6.'); + $container = $this->mockContainer('

Headline

'."\n"); $contentModel = $this->mockClassWithProperties(ContentModel::class); @@ -48,8 +56,13 @@ public function testWithCodeInput(): void $controller(new Request(), $contentModel, 'main'); } + /** + * @group legacy + */ public function testDisallowedTagsAreCorrectlyStripped(): void { + $this->expectDeprecation('Since contao/core-bundle 5.0: Creating fragments with legacy templates is deprecated and will not work anymore in Contao 6.'); + $expectedHtml = <<<'HTML'

Headline

<iframe src="https://example.com"></iframe> @@ -86,8 +99,13 @@ public function testDisallowedTagsAreCorrectlyStripped(): void $controller(new Request(), $contentModel, 'main'); } + /** + * @group legacy + */ public function testWithFileInput(): void { + $this->expectDeprecation('Since contao/core-bundle 5.0: Creating fragments with legacy templates is deprecated and will not work anymore in Contao 6.'); + $fs = new Filesystem(); $tempTestFile = $fs->tempnam($this->getTempDir(), ''); $fs->dumpFile($tempTestFile, '# Headline'); diff --git a/core-bundle/tests/Controller/ContentElement/TemplateControllerTest.php b/core-bundle/tests/Controller/ContentElement/TemplateControllerTest.php index d9261914b51..3cb7fd737f5 100644 --- a/core-bundle/tests/Controller/ContentElement/TemplateControllerTest.php +++ b/core-bundle/tests/Controller/ContentElement/TemplateControllerTest.php @@ -18,6 +18,7 @@ use Contao\CoreBundle\Routing\ScopeMatcher; use Contao\CoreBundle\Tests\TestCase; use Contao\FrontendTemplate; +use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait; use Symfony\Component\DependencyInjection\Container; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\RequestStack; @@ -25,8 +26,15 @@ class TemplateControllerTest extends TestCase { + use ExpectDeprecationTrait; + + /** + * @group legacy + */ public function testWithDataInput(): void { + $this->expectDeprecation('Since contao/core-bundle 5.0: Creating fragments with legacy templates is deprecated and will not work anymore in Contao 6.'); + $data = [ ['key' => 'Key 1', 'value' => 'Value 1'], ['key' => 'Key 1', 'value' => 'Value 1'], @@ -43,8 +51,13 @@ public function testWithDataInput(): void $controller(new Request(), $contentModel, 'main'); } + /** + * @group legacy + */ public function testWithoutDataInput(): void { + $this->expectDeprecation('Since contao/core-bundle 5.0: Creating fragments with legacy templates is deprecated and will not work anymore in Contao 6.'); + $container = $this->mockContainer([], 'ce_template'); $contentModel = $this->mockClassWithProperties(ContentModel::class); @@ -56,8 +69,13 @@ public function testWithoutDataInput(): void $controller(new Request(), $contentModel, 'main'); } + /** + * @group legacy + */ public function testWithCustomTemplate(): void { + $this->expectDeprecation('Since contao/core-bundle 5.0: Creating fragments with legacy templates is deprecated and will not work anymore in Contao 6.'); + $data = [ ['key' => 'Key 1', 'value' => 'Value 1'], ['key' => 'Key 1', 'value' => 'Value 1'], diff --git a/core-bundle/tests/Controller/FrontendModule/FrontendModuleControllerTest.php b/core-bundle/tests/Controller/FrontendModule/FrontendModuleControllerTest.php index e0601f36dd6..12322c3b949 100644 --- a/core-bundle/tests/Controller/FrontendModule/FrontendModuleControllerTest.php +++ b/core-bundle/tests/Controller/FrontendModule/FrontendModuleControllerTest.php @@ -22,6 +22,7 @@ use Contao\CoreBundle\Twig\Loader\ContaoFilesystemLoader; use Contao\ModuleModel; use Contao\System; +use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\RequestStack; @@ -29,6 +30,8 @@ class FrontendModuleControllerTest extends TestCase { + use ExpectDeprecationTrait; + private ContainerBuilder $container; protected function setUp(): void @@ -50,8 +53,13 @@ protected function tearDown(): void parent::tearDown(); } + /** + * @group legacy + */ public function testCreatesTheTemplateFromTheClassName(): void { + $this->expectDeprecation('Since contao/core-bundle 5.0: Creating fragments with legacy templates is deprecated and will not work anymore in Contao 6.'); + $controller = $this->getTestController(); $response = $controller(new Request([], [], ['_scope' => 'frontend']), $this->getModuleModel(), 'main'); @@ -60,8 +68,13 @@ public function testCreatesTheTemplateFromTheClassName(): void $this->assertSame('mod_test', $template['templateName']); } + /** + * @group legacy + */ public function testCreatesTheTemplateFromTheTypeFragmentOption(): void { + $this->expectDeprecation('Since contao/core-bundle 5.0: Creating fragments with legacy templates is deprecated and will not work anymore in Contao 6.'); + $controller = $this->getTestController(['type' => 'foo']); $response = $controller(new Request(), $this->getModuleModel(), 'main'); @@ -70,8 +83,13 @@ public function testCreatesTheTemplateFromTheTypeFragmentOption(): void $this->assertSame('mod_foo', $template['templateName']); } + /** + * @group legacy + */ public function testCreatesTheTemplateFromTheTemplateFragmentOption(): void { + $this->expectDeprecation('Since contao/core-bundle 5.0: Creating fragments with legacy templates is deprecated and will not work anymore in Contao 6.'); + $controller = $this->getTestController(['template' => 'mod_bar']); $response = $controller(new Request(), $this->getModuleModel(), 'main'); @@ -80,8 +98,13 @@ public function testCreatesTheTemplateFromTheTemplateFragmentOption(): void $this->assertSame('mod_bar', $template['templateName']); } + /** + * @group legacy + */ public function testCreatesTheTemplateFromACustomTpl(): void { + $this->expectDeprecation('Since contao/core-bundle 5.0: Creating fragments with legacy templates is deprecated and will not work anymore in Contao 6.'); + $this->container->set('request_stack', new RequestStack()); $controller = $this->getTestController(); @@ -94,8 +117,13 @@ public function testCreatesTheTemplateFromACustomTpl(): void $this->assertSame('mod_bar', $template['templateName']); } + /** + * @group legacy + */ public function testSetsTheClassFromTheType(): void { + $this->expectDeprecation('Since contao/core-bundle 5.0: Creating fragments with legacy templates is deprecated and will not work anymore in Contao 6.'); + $controller = $this->getTestController(); $response = $controller(new Request(), $this->getModuleModel(), 'main'); @@ -105,8 +133,13 @@ public function testSetsTheClassFromTheType(): void $this->assertSame('mod_test', $template['class']); } + /** + * @group legacy + */ public function testSetsTheHeadlineFromTheModel(): void { + $this->expectDeprecation('Since contao/core-bundle 5.0: Creating fragments with legacy templates is deprecated and will not work anymore in Contao 6.'); + $controller = $this->getTestController(); $model = $this->getModuleModel(['headline' => serialize(['unit' => 'h6', 'value' => 'foobar'])]); @@ -118,8 +151,13 @@ public function testSetsTheHeadlineFromTheModel(): void $this->assertSame('h6', $template['hl']); } + /** + * @group legacy + */ public function testSetsTheCssIdAndClassFromTheModel(): void { + $this->expectDeprecation('Since contao/core-bundle 5.0: Creating fragments with legacy templates is deprecated and will not work anymore in Contao 6.'); + $controller = $this->getTestController(); $model = $this->getModuleModel(['cssID' => serialize(['foo', 'bar'])]); @@ -131,8 +169,13 @@ public function testSetsTheCssIdAndClassFromTheModel(): void $this->assertSame('mod_test bar', $template['class']); } + /** + * @group legacy + */ public function testSetsTheLayoutSection(): void { + $this->expectDeprecation('Since contao/core-bundle 5.0: Creating fragments with legacy templates is deprecated and will not work anymore in Contao 6.'); + $controller = $this->getTestController(); $response = $controller(new Request(), $this->getModuleModel(), 'left'); @@ -248,8 +291,13 @@ public function provideScope(): \Generator yield 'backend' => [true]; } + /** + * @group legacy + */ public function testAddsTheCacheTags(): void { + $this->expectDeprecation('Since contao/core-bundle 5.0: Creating fragments with legacy templates is deprecated and will not work anymore in Contao 6.'); + $model = $this->getModuleModel(); $model->id = 42; diff --git a/core-bundle/tests/Controller/FrontendModule/TwoFactorControllerTest.php b/core-bundle/tests/Controller/FrontendModule/TwoFactorControllerTest.php index 3b93e384a7e..17fae706f88 100644 --- a/core-bundle/tests/Controller/FrontendModule/TwoFactorControllerTest.php +++ b/core-bundle/tests/Controller/FrontendModule/TwoFactorControllerTest.php @@ -26,6 +26,7 @@ use Contao\System; use PHPUnit\Framework\MockObject\MockObject; use Scheb\TwoFactorBundle\Security\Authentication\Exception\InvalidTwoFactorCodeException; +use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; @@ -38,6 +39,8 @@ class TwoFactorControllerTest extends TestCase { + use ExpectDeprecationTrait; + protected function tearDown(): void { $this->resetStaticProperties([System::class]); @@ -65,8 +68,13 @@ public function testReturnsEmptyResponseIfTheUserIsNotFullyAuthenticated(): void $this->assertSame(204, $response->getStatusCode()); } + /** + * @group legacy + */ public function testReturnsIfTheUserIsNotAFrontendUser(): void { + $this->expectDeprecation('Since contao/core-bundle 5.0: Creating fragments with legacy templates is deprecated and will not work anymore in Contao 6.'); + $user = $this->createMock(BackendUser::class); $container = $this->getContainerWithFrameworkTemplate( @@ -86,8 +94,13 @@ public function testReturnsIfTheUserIsNotAFrontendUser(): void $this->assertSame(Response::HTTP_NO_CONTENT, $response->getStatusCode()); } + /** + * @group legacy + */ public function testReturnsAResponseIfTheUserIsAFrontendUser(): void { + $this->expectDeprecation('Since contao/core-bundle 5.0: Creating fragments with legacy templates is deprecated and will not work anymore in Contao 6.'); + $user = $this->mockClassWithProperties(FrontendUser::class); $user->secret = ''; $user->useTwoFactor = '1'; @@ -111,8 +124,13 @@ public function testReturnsAResponseIfTheUserIsAFrontendUser(): void $this->assertSame(Response::HTTP_OK, $response->getStatusCode()); } + /** + * @group legacy + */ public function testReturnsIfTwoFactorAuthenticationIsAlreadyDisabled(): void { + $this->expectDeprecation('Since contao/core-bundle 5.0: Creating fragments with legacy templates is deprecated and will not work anymore in Contao 6.'); + $user = $this->mockClassWithProperties(FrontendUser::class); $user->secret = ''; $user->useTwoFactor = ''; @@ -137,8 +155,13 @@ public function testReturnsIfTwoFactorAuthenticationIsAlreadyDisabled(): void $this->assertSame(Response::HTTP_OK, $response->getStatusCode()); } + /** + * @group legacy + */ public function testRedirectsAfterTwoFactorHasBeenDisabled(): void { + $this->expectDeprecation('Since contao/core-bundle 5.0: Creating fragments with legacy templates is deprecated and will not work anymore in Contao 6.'); + $user = $this->mockClassWithProperties(FrontendUser::class); $user->secret = ''; $user->useTwoFactor = '1'; @@ -182,8 +205,13 @@ public function testRedirectsAfterTwoFactorHasBeenDisabled(): void $this->assertSame('https://localhost.wip/foobar', $response->getTargetUrl()); } + /** + * @group legacy + */ public function testReturnsIfTwoFactorAuthenticationIsAlreadyEnabled(): void { + $this->expectDeprecation('Since contao/core-bundle 5.0: Creating fragments with legacy templates is deprecated and will not work anymore in Contao 6.'); + $user = $this->mockClassWithProperties(FrontendUser::class); $user->secret = ''; $user->useTwoFactor = '1'; @@ -213,8 +241,13 @@ public function testReturnsIfTwoFactorAuthenticationIsAlreadyEnabled(): void $this->assertSame(Response::HTTP_OK, $response->getStatusCode()); } + /** + * @group legacy + */ public function testFailsIfTheTwoFactorCodeIsInvalid(): void { + $this->expectDeprecation('Since contao/core-bundle 5.0: Creating fragments with legacy templates is deprecated and will not work anymore in Contao 6.'); + $user = $this->mockClassWithProperties(FrontendUser::class); $user->secret = ''; $user->useTwoFactor = ''; @@ -243,8 +276,13 @@ public function testFailsIfTheTwoFactorCodeIsInvalid(): void $controller($request, $module, 'main', null, $page); } + /** + * @group legacy + */ public function testDoesNotRedirectIfTheTwoFactorCodeIsInvalid(): void { + $this->expectDeprecation('Since contao/core-bundle 5.0: Creating fragments with legacy templates is deprecated and will not work anymore in Contao 6.'); + $user = $this->mockClassWithProperties(FrontendUser::class); $user->secret = ''; $user->useTwoFactor = ''; @@ -275,8 +313,13 @@ public function testDoesNotRedirectIfTheTwoFactorCodeIsInvalid(): void $controller($request, $module, 'main', null, $page); } + /** + * @group legacy + */ public function testRedirectsIfTheTwoFactorCodeIsValid(): void { + $this->expectDeprecation('Since contao/core-bundle 5.0: Creating fragments with legacy templates is deprecated and will not work anymore in Contao 6.'); + $user = $this->mockClassWithProperties(FrontendUser::class); $user->secret = ''; $user->useTwoFactor = ''; @@ -314,8 +357,13 @@ public function testRedirectsIfTheTwoFactorCodeIsValid(): void $this->assertInstanceOf(RedirectResponse::class, $response); } + /** + * @group legacy + */ public function testShowsTheBackupCodes(): void { + $this->expectDeprecation('Since contao/core-bundle 5.0: Creating fragments with legacy templates is deprecated and will not work anymore in Contao 6.'); + $user = $this->mockClassWithProperties(FrontendUser::class); $user->secret = ''; $user->useTwoFactor = '1'; @@ -341,8 +389,13 @@ public function testShowsTheBackupCodes(): void $this->assertSame(Response::HTTP_OK, $response->getStatusCode()); } + /** + * @group legacy + */ public function testGeneratesTheBackupCodes(): void { + $this->expectDeprecation('Since contao/core-bundle 5.0: Creating fragments with legacy templates is deprecated and will not work anymore in Contao 6.'); + $user = $this->mockClassWithProperties(FrontendUser::class); $user->secret = ''; $user->useTwoFactor = '1'; From 6ed574a0e83a11eef7ef5c586426c80af738a1e8 Mon Sep 17 00:00:00 2001 From: "M. Vondano" Date: Sat, 9 Apr 2022 12:30:06 +0200 Subject: [PATCH 20/28] Revert "expect self deprecations" This reverts commit f6f303dff0dcf99c118c5f0238e2392620735326. --- .../ContentElementControllerTest.php | 73 ------------------- .../ContentElement/MarkdownControllerTest.php | 18 ----- .../ContentElement/TemplateControllerTest.php | 18 ----- .../FrontendModuleControllerTest.php | 48 ------------ .../TwoFactorControllerTest.php | 53 -------------- 5 files changed, 210 deletions(-) diff --git a/core-bundle/tests/Controller/ContentElement/ContentElementControllerTest.php b/core-bundle/tests/Controller/ContentElement/ContentElementControllerTest.php index 5c6b9512785..848fed7d4bf 100644 --- a/core-bundle/tests/Controller/ContentElement/ContentElementControllerTest.php +++ b/core-bundle/tests/Controller/ContentElement/ContentElementControllerTest.php @@ -22,15 +22,12 @@ use Contao\CoreBundle\Twig\Loader\ContaoFilesystemLoader; use Contao\System; use Symfony\Bridge\PhpUnit\ClockMock; -use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\RequestStack; class ContentElementControllerTest extends TestCase { - use ExpectDeprecationTrait; - private ContainerBuilder $container; protected function setUp(): void @@ -53,13 +50,8 @@ protected function tearDown(): void parent::tearDown(); } - /** - * @group legacy - */ public function testCreatesTheTemplateFromTheClassName(): void { - $this->expectDeprecation('Since contao/core-bundle 5.0: Creating fragments with legacy templates is deprecated and will not work anymore in Contao 6.'); - $controller = $this->getTestController(); $response = $controller(new Request(), $this->getContentModel(), 'main'); @@ -68,13 +60,8 @@ public function testCreatesTheTemplateFromTheClassName(): void $this->assertSame('ce_test', $template['templateName']); } - /** - * @group legacy - */ public function testCreatesTheTemplateFromTheTypeFragmentOptions(): void { - $this->expectDeprecation('Since contao/core-bundle 5.0: Creating fragments with legacy templates is deprecated and will not work anymore in Contao 6.'); - $controller = $this->getTestController(['type' => 'foo']); $response = $controller(new Request(), $this->getContentModel(), 'main'); @@ -83,13 +70,8 @@ public function testCreatesTheTemplateFromTheTypeFragmentOptions(): void $this->assertSame('ce_foo', $template['templateName']); } - /** - * @group legacy - */ public function testCreatesTheTemplateFromTheTemplateFragmentOption(): void { - $this->expectDeprecation('Since contao/core-bundle 5.0: Creating fragments with legacy templates is deprecated and will not work anymore in Contao 6.'); - $controller = $this->getTestController(['template' => 'ce_bar']); $response = $controller(new Request(), $this->getContentModel(), 'main'); @@ -98,13 +80,8 @@ public function testCreatesTheTemplateFromTheTemplateFragmentOption(): void $this->assertSame('ce_bar', $template['templateName']); } - /** - * @group legacy - */ public function testCreatesTheTemplateFromACustomTpl(): void { - $this->expectDeprecation('Since contao/core-bundle 5.0: Creating fragments with legacy templates is deprecated and will not work anymore in Contao 6.'); - $this->container->set('request_stack', new RequestStack()); $controller = $this->getTestController(['template' => 'ce_bar']); @@ -117,13 +94,8 @@ public function testCreatesTheTemplateFromACustomTpl(): void $this->assertSame('ce_bar', $template['templateName']); } - /** - * @group legacy - */ public function testDoesNotCreateTheTemplateFromACustomTplInTheBackend(): void { - $this->expectDeprecation('Since contao/core-bundle 5.0: Creating fragments with legacy templates is deprecated and will not work anymore in Contao 6.'); - $request = new Request([], [], ['_scope' => 'backend']); $requestStack = new RequestStack(); $requestStack->push($request); @@ -141,13 +113,8 @@ public function testDoesNotCreateTheTemplateFromACustomTplInTheBackend(): void $this->assertSame('ce_test', $template['templateName']); } - /** - * @group legacy - */ public function testSetsTheClassFromTheType(): void { - $this->expectDeprecation('Since contao/core-bundle 5.0: Creating fragments with legacy templates is deprecated and will not work anymore in Contao 6.'); - $controller = $this->getTestController(); $response = $controller(new Request(), $this->getContentModel(), 'main'); @@ -157,13 +124,8 @@ public function testSetsTheClassFromTheType(): void $this->assertSame('ce_test', $template['class']); } - /** - * @group legacy - */ public function testSetsTheHeadlineFromTheModel(): void { - $this->expectDeprecation('Since contao/core-bundle 5.0: Creating fragments with legacy templates is deprecated and will not work anymore in Contao 6.'); - $controller = $this->getTestController(); $model = $this->getContentModel(['headline' => serialize(['unit' => 'h6', 'value' => 'foobar'])]); @@ -175,13 +137,8 @@ public function testSetsTheHeadlineFromTheModel(): void $this->assertSame('h6', $template['hl']); } - /** - * @group legacy - */ public function testSetsTheCssIdAndClassFromTheModel(): void { - $this->expectDeprecation('Since contao/core-bundle 5.0: Creating fragments with legacy templates is deprecated and will not work anymore in Contao 6.'); - $controller = $this->getTestController(); $model = $this->getContentModel(['cssID' => serialize(['foo', 'bar'])]); @@ -193,13 +150,8 @@ public function testSetsTheCssIdAndClassFromTheModel(): void $this->assertSame('ce_test bar', $template['class']); } - /** - * @group legacy - */ public function testSetsTheLayoutSection(): void { - $this->expectDeprecation('Since contao/core-bundle 5.0: Creating fragments with legacy templates is deprecated and will not work anymore in Contao 6.'); - $controller = $this->getTestController(); $response = $controller(new Request(), $this->getContentModel(), 'left'); @@ -208,13 +160,8 @@ public function testSetsTheLayoutSection(): void $this->assertSame('left', $template['inColumn']); } - /** - * @group legacy - */ public function testSetsTheClasses(): void { - $this->expectDeprecation('Since contao/core-bundle 5.0: Creating fragments with legacy templates is deprecated and will not work anymore in Contao 6.'); - $controller = $this->getTestController(); $response = $controller(new Request(), $this->getContentModel(), 'main', ['first', 'last']); @@ -277,13 +224,8 @@ public function provideScope(): \Generator yield 'backend' => [true]; } - /** - * @group legacy - */ public function testAddsTheCacheTags(): void { - $this->expectDeprecation('Since contao/core-bundle 5.0: Creating fragments with legacy templates is deprecated and will not work anymore in Contao 6.'); - $model = $this->getContentModel(['id' => 42]); $entityCacheTags = $this->createMock(EntityCacheTags::class); @@ -300,13 +242,8 @@ public function testAddsTheCacheTags(): void $controller(new Request(), $model, 'main'); } - /** - * @group legacy - */ public function testSetsTheSharedMaxAgeIfTheElementHasAStartDate(): void { - $this->expectDeprecation('Since contao/core-bundle 5.0: Creating fragments with legacy templates is deprecated and will not work anymore in Contao 6.'); - ClockMock::withClockMock(true); $time = time(); @@ -325,13 +262,8 @@ public function testSetsTheSharedMaxAgeIfTheElementHasAStartDate(): void ClockMock::withClockMock(false); } - /** - * @group legacy - */ public function testSetsTheSharedMaxAgeIfTheElementHasAStopDate(): void { - $this->expectDeprecation('Since contao/core-bundle 5.0: Creating fragments with legacy templates is deprecated and will not work anymore in Contao 6.'); - ClockMock::withClockMock(true); $time = time(); @@ -350,13 +282,8 @@ public function testSetsTheSharedMaxAgeIfTheElementHasAStopDate(): void ClockMock::withClockMock(false); } - /** - * @group legacy - */ public function testDoesNotSetTheSharedMaxAgeIfTheElementHasNeitherAStartNorAStopDate(): void { - $this->expectDeprecation('Since contao/core-bundle 5.0: Creating fragments with legacy templates is deprecated and will not work anymore in Contao 6.'); - $controller = new TestSharedMaxAgeController(); $controller->setContainer($this->container); diff --git a/core-bundle/tests/Controller/ContentElement/MarkdownControllerTest.php b/core-bundle/tests/Controller/ContentElement/MarkdownControllerTest.php index 7e48c4707c2..ce0e728e7f4 100644 --- a/core-bundle/tests/Controller/ContentElement/MarkdownControllerTest.php +++ b/core-bundle/tests/Controller/ContentElement/MarkdownControllerTest.php @@ -21,7 +21,6 @@ use Contao\FrontendTemplate; use Contao\Input; use Contao\System; -use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait; use Symfony\Component\DependencyInjection\Container; use Symfony\Component\Filesystem\Filesystem; use Symfony\Component\HttpFoundation\Request; @@ -29,8 +28,6 @@ class MarkdownControllerTest extends TestCase { - use ExpectDeprecationTrait; - protected function tearDown(): void { $this->resetStaticProperties([System::class]); @@ -38,13 +35,8 @@ protected function tearDown(): void parent::tearDown(); } - /** - * @group legacy - */ public function testWithCodeInput(): void { - $this->expectDeprecation('Since contao/core-bundle 5.0: Creating fragments with legacy templates is deprecated and will not work anymore in Contao 6.'); - $container = $this->mockContainer('

Headline

'."\n"); $contentModel = $this->mockClassWithProperties(ContentModel::class); @@ -56,13 +48,8 @@ public function testWithCodeInput(): void $controller(new Request(), $contentModel, 'main'); } - /** - * @group legacy - */ public function testDisallowedTagsAreCorrectlyStripped(): void { - $this->expectDeprecation('Since contao/core-bundle 5.0: Creating fragments with legacy templates is deprecated and will not work anymore in Contao 6.'); - $expectedHtml = <<<'HTML'

Headline

<iframe src="https://example.com"></iframe> @@ -99,13 +86,8 @@ public function testDisallowedTagsAreCorrectlyStripped(): void $controller(new Request(), $contentModel, 'main'); } - /** - * @group legacy - */ public function testWithFileInput(): void { - $this->expectDeprecation('Since contao/core-bundle 5.0: Creating fragments with legacy templates is deprecated and will not work anymore in Contao 6.'); - $fs = new Filesystem(); $tempTestFile = $fs->tempnam($this->getTempDir(), ''); $fs->dumpFile($tempTestFile, '# Headline'); diff --git a/core-bundle/tests/Controller/ContentElement/TemplateControllerTest.php b/core-bundle/tests/Controller/ContentElement/TemplateControllerTest.php index 3cb7fd737f5..d9261914b51 100644 --- a/core-bundle/tests/Controller/ContentElement/TemplateControllerTest.php +++ b/core-bundle/tests/Controller/ContentElement/TemplateControllerTest.php @@ -18,7 +18,6 @@ use Contao\CoreBundle\Routing\ScopeMatcher; use Contao\CoreBundle\Tests\TestCase; use Contao\FrontendTemplate; -use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait; use Symfony\Component\DependencyInjection\Container; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\RequestStack; @@ -26,15 +25,8 @@ class TemplateControllerTest extends TestCase { - use ExpectDeprecationTrait; - - /** - * @group legacy - */ public function testWithDataInput(): void { - $this->expectDeprecation('Since contao/core-bundle 5.0: Creating fragments with legacy templates is deprecated and will not work anymore in Contao 6.'); - $data = [ ['key' => 'Key 1', 'value' => 'Value 1'], ['key' => 'Key 1', 'value' => 'Value 1'], @@ -51,13 +43,8 @@ public function testWithDataInput(): void $controller(new Request(), $contentModel, 'main'); } - /** - * @group legacy - */ public function testWithoutDataInput(): void { - $this->expectDeprecation('Since contao/core-bundle 5.0: Creating fragments with legacy templates is deprecated and will not work anymore in Contao 6.'); - $container = $this->mockContainer([], 'ce_template'); $contentModel = $this->mockClassWithProperties(ContentModel::class); @@ -69,13 +56,8 @@ public function testWithoutDataInput(): void $controller(new Request(), $contentModel, 'main'); } - /** - * @group legacy - */ public function testWithCustomTemplate(): void { - $this->expectDeprecation('Since contao/core-bundle 5.0: Creating fragments with legacy templates is deprecated and will not work anymore in Contao 6.'); - $data = [ ['key' => 'Key 1', 'value' => 'Value 1'], ['key' => 'Key 1', 'value' => 'Value 1'], diff --git a/core-bundle/tests/Controller/FrontendModule/FrontendModuleControllerTest.php b/core-bundle/tests/Controller/FrontendModule/FrontendModuleControllerTest.php index 12322c3b949..e0601f36dd6 100644 --- a/core-bundle/tests/Controller/FrontendModule/FrontendModuleControllerTest.php +++ b/core-bundle/tests/Controller/FrontendModule/FrontendModuleControllerTest.php @@ -22,7 +22,6 @@ use Contao\CoreBundle\Twig\Loader\ContaoFilesystemLoader; use Contao\ModuleModel; use Contao\System; -use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\RequestStack; @@ -30,8 +29,6 @@ class FrontendModuleControllerTest extends TestCase { - use ExpectDeprecationTrait; - private ContainerBuilder $container; protected function setUp(): void @@ -53,13 +50,8 @@ protected function tearDown(): void parent::tearDown(); } - /** - * @group legacy - */ public function testCreatesTheTemplateFromTheClassName(): void { - $this->expectDeprecation('Since contao/core-bundle 5.0: Creating fragments with legacy templates is deprecated and will not work anymore in Contao 6.'); - $controller = $this->getTestController(); $response = $controller(new Request([], [], ['_scope' => 'frontend']), $this->getModuleModel(), 'main'); @@ -68,13 +60,8 @@ public function testCreatesTheTemplateFromTheClassName(): void $this->assertSame('mod_test', $template['templateName']); } - /** - * @group legacy - */ public function testCreatesTheTemplateFromTheTypeFragmentOption(): void { - $this->expectDeprecation('Since contao/core-bundle 5.0: Creating fragments with legacy templates is deprecated and will not work anymore in Contao 6.'); - $controller = $this->getTestController(['type' => 'foo']); $response = $controller(new Request(), $this->getModuleModel(), 'main'); @@ -83,13 +70,8 @@ public function testCreatesTheTemplateFromTheTypeFragmentOption(): void $this->assertSame('mod_foo', $template['templateName']); } - /** - * @group legacy - */ public function testCreatesTheTemplateFromTheTemplateFragmentOption(): void { - $this->expectDeprecation('Since contao/core-bundle 5.0: Creating fragments with legacy templates is deprecated and will not work anymore in Contao 6.'); - $controller = $this->getTestController(['template' => 'mod_bar']); $response = $controller(new Request(), $this->getModuleModel(), 'main'); @@ -98,13 +80,8 @@ public function testCreatesTheTemplateFromTheTemplateFragmentOption(): void $this->assertSame('mod_bar', $template['templateName']); } - /** - * @group legacy - */ public function testCreatesTheTemplateFromACustomTpl(): void { - $this->expectDeprecation('Since contao/core-bundle 5.0: Creating fragments with legacy templates is deprecated and will not work anymore in Contao 6.'); - $this->container->set('request_stack', new RequestStack()); $controller = $this->getTestController(); @@ -117,13 +94,8 @@ public function testCreatesTheTemplateFromACustomTpl(): void $this->assertSame('mod_bar', $template['templateName']); } - /** - * @group legacy - */ public function testSetsTheClassFromTheType(): void { - $this->expectDeprecation('Since contao/core-bundle 5.0: Creating fragments with legacy templates is deprecated and will not work anymore in Contao 6.'); - $controller = $this->getTestController(); $response = $controller(new Request(), $this->getModuleModel(), 'main'); @@ -133,13 +105,8 @@ public function testSetsTheClassFromTheType(): void $this->assertSame('mod_test', $template['class']); } - /** - * @group legacy - */ public function testSetsTheHeadlineFromTheModel(): void { - $this->expectDeprecation('Since contao/core-bundle 5.0: Creating fragments with legacy templates is deprecated and will not work anymore in Contao 6.'); - $controller = $this->getTestController(); $model = $this->getModuleModel(['headline' => serialize(['unit' => 'h6', 'value' => 'foobar'])]); @@ -151,13 +118,8 @@ public function testSetsTheHeadlineFromTheModel(): void $this->assertSame('h6', $template['hl']); } - /** - * @group legacy - */ public function testSetsTheCssIdAndClassFromTheModel(): void { - $this->expectDeprecation('Since contao/core-bundle 5.0: Creating fragments with legacy templates is deprecated and will not work anymore in Contao 6.'); - $controller = $this->getTestController(); $model = $this->getModuleModel(['cssID' => serialize(['foo', 'bar'])]); @@ -169,13 +131,8 @@ public function testSetsTheCssIdAndClassFromTheModel(): void $this->assertSame('mod_test bar', $template['class']); } - /** - * @group legacy - */ public function testSetsTheLayoutSection(): void { - $this->expectDeprecation('Since contao/core-bundle 5.0: Creating fragments with legacy templates is deprecated and will not work anymore in Contao 6.'); - $controller = $this->getTestController(); $response = $controller(new Request(), $this->getModuleModel(), 'left'); @@ -291,13 +248,8 @@ public function provideScope(): \Generator yield 'backend' => [true]; } - /** - * @group legacy - */ public function testAddsTheCacheTags(): void { - $this->expectDeprecation('Since contao/core-bundle 5.0: Creating fragments with legacy templates is deprecated and will not work anymore in Contao 6.'); - $model = $this->getModuleModel(); $model->id = 42; diff --git a/core-bundle/tests/Controller/FrontendModule/TwoFactorControllerTest.php b/core-bundle/tests/Controller/FrontendModule/TwoFactorControllerTest.php index 17fae706f88..3b93e384a7e 100644 --- a/core-bundle/tests/Controller/FrontendModule/TwoFactorControllerTest.php +++ b/core-bundle/tests/Controller/FrontendModule/TwoFactorControllerTest.php @@ -26,7 +26,6 @@ use Contao\System; use PHPUnit\Framework\MockObject\MockObject; use Scheb\TwoFactorBundle\Security\Authentication\Exception\InvalidTwoFactorCodeException; -use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; @@ -39,8 +38,6 @@ class TwoFactorControllerTest extends TestCase { - use ExpectDeprecationTrait; - protected function tearDown(): void { $this->resetStaticProperties([System::class]); @@ -68,13 +65,8 @@ public function testReturnsEmptyResponseIfTheUserIsNotFullyAuthenticated(): void $this->assertSame(204, $response->getStatusCode()); } - /** - * @group legacy - */ public function testReturnsIfTheUserIsNotAFrontendUser(): void { - $this->expectDeprecation('Since contao/core-bundle 5.0: Creating fragments with legacy templates is deprecated and will not work anymore in Contao 6.'); - $user = $this->createMock(BackendUser::class); $container = $this->getContainerWithFrameworkTemplate( @@ -94,13 +86,8 @@ public function testReturnsIfTheUserIsNotAFrontendUser(): void $this->assertSame(Response::HTTP_NO_CONTENT, $response->getStatusCode()); } - /** - * @group legacy - */ public function testReturnsAResponseIfTheUserIsAFrontendUser(): void { - $this->expectDeprecation('Since contao/core-bundle 5.0: Creating fragments with legacy templates is deprecated and will not work anymore in Contao 6.'); - $user = $this->mockClassWithProperties(FrontendUser::class); $user->secret = ''; $user->useTwoFactor = '1'; @@ -124,13 +111,8 @@ public function testReturnsAResponseIfTheUserIsAFrontendUser(): void $this->assertSame(Response::HTTP_OK, $response->getStatusCode()); } - /** - * @group legacy - */ public function testReturnsIfTwoFactorAuthenticationIsAlreadyDisabled(): void { - $this->expectDeprecation('Since contao/core-bundle 5.0: Creating fragments with legacy templates is deprecated and will not work anymore in Contao 6.'); - $user = $this->mockClassWithProperties(FrontendUser::class); $user->secret = ''; $user->useTwoFactor = ''; @@ -155,13 +137,8 @@ public function testReturnsIfTwoFactorAuthenticationIsAlreadyDisabled(): void $this->assertSame(Response::HTTP_OK, $response->getStatusCode()); } - /** - * @group legacy - */ public function testRedirectsAfterTwoFactorHasBeenDisabled(): void { - $this->expectDeprecation('Since contao/core-bundle 5.0: Creating fragments with legacy templates is deprecated and will not work anymore in Contao 6.'); - $user = $this->mockClassWithProperties(FrontendUser::class); $user->secret = ''; $user->useTwoFactor = '1'; @@ -205,13 +182,8 @@ public function testRedirectsAfterTwoFactorHasBeenDisabled(): void $this->assertSame('https://localhost.wip/foobar', $response->getTargetUrl()); } - /** - * @group legacy - */ public function testReturnsIfTwoFactorAuthenticationIsAlreadyEnabled(): void { - $this->expectDeprecation('Since contao/core-bundle 5.0: Creating fragments with legacy templates is deprecated and will not work anymore in Contao 6.'); - $user = $this->mockClassWithProperties(FrontendUser::class); $user->secret = ''; $user->useTwoFactor = '1'; @@ -241,13 +213,8 @@ public function testReturnsIfTwoFactorAuthenticationIsAlreadyEnabled(): void $this->assertSame(Response::HTTP_OK, $response->getStatusCode()); } - /** - * @group legacy - */ public function testFailsIfTheTwoFactorCodeIsInvalid(): void { - $this->expectDeprecation('Since contao/core-bundle 5.0: Creating fragments with legacy templates is deprecated and will not work anymore in Contao 6.'); - $user = $this->mockClassWithProperties(FrontendUser::class); $user->secret = ''; $user->useTwoFactor = ''; @@ -276,13 +243,8 @@ public function testFailsIfTheTwoFactorCodeIsInvalid(): void $controller($request, $module, 'main', null, $page); } - /** - * @group legacy - */ public function testDoesNotRedirectIfTheTwoFactorCodeIsInvalid(): void { - $this->expectDeprecation('Since contao/core-bundle 5.0: Creating fragments with legacy templates is deprecated and will not work anymore in Contao 6.'); - $user = $this->mockClassWithProperties(FrontendUser::class); $user->secret = ''; $user->useTwoFactor = ''; @@ -313,13 +275,8 @@ public function testDoesNotRedirectIfTheTwoFactorCodeIsInvalid(): void $controller($request, $module, 'main', null, $page); } - /** - * @group legacy - */ public function testRedirectsIfTheTwoFactorCodeIsValid(): void { - $this->expectDeprecation('Since contao/core-bundle 5.0: Creating fragments with legacy templates is deprecated and will not work anymore in Contao 6.'); - $user = $this->mockClassWithProperties(FrontendUser::class); $user->secret = ''; $user->useTwoFactor = ''; @@ -357,13 +314,8 @@ public function testRedirectsIfTheTwoFactorCodeIsValid(): void $this->assertInstanceOf(RedirectResponse::class, $response); } - /** - * @group legacy - */ public function testShowsTheBackupCodes(): void { - $this->expectDeprecation('Since contao/core-bundle 5.0: Creating fragments with legacy templates is deprecated and will not work anymore in Contao 6.'); - $user = $this->mockClassWithProperties(FrontendUser::class); $user->secret = ''; $user->useTwoFactor = '1'; @@ -389,13 +341,8 @@ public function testShowsTheBackupCodes(): void $this->assertSame(Response::HTTP_OK, $response->getStatusCode()); } - /** - * @group legacy - */ public function testGeneratesTheBackupCodes(): void { - $this->expectDeprecation('Since contao/core-bundle 5.0: Creating fragments with legacy templates is deprecated and will not work anymore in Contao 6.'); - $user = $this->mockClassWithProperties(FrontendUser::class); $user->secret = ''; $user->useTwoFactor = '1'; From 8e74730fd10cd4600ea4a4b9ceb21e852f140ff6 Mon Sep 17 00:00:00 2001 From: "M. Vondano" Date: Sat, 9 Apr 2022 12:33:32 +0200 Subject: [PATCH 21/28] remove triggering deprecation for legacy fragment templates for now --- core-bundle/src/Controller/AbstractFragmentController.php | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/core-bundle/src/Controller/AbstractFragmentController.php b/core-bundle/src/Controller/AbstractFragmentController.php index 342581db2b9..c98b6c92a53 100644 --- a/core-bundle/src/Controller/AbstractFragmentController.php +++ b/core-bundle/src/Controller/AbstractFragmentController.php @@ -80,10 +80,7 @@ protected function getPageModel(): PageModel|null protected function createTemplate(Model $model, string|null $fallbackTemplateName = null): FragmentTemplate { $templateName = $this->getTemplateName($model, $fallbackTemplateName); - - if ($isLegacyTemplate = $this->isLegacyTemplate($templateName)) { - trigger_deprecation('contao/core-bundle', '5.0', 'Creating fragments with legacy templates is deprecated and will not work anymore in Contao 6.'); - } + $isLegacyTemplate = $this->isLegacyTemplate($templateName); // Allow calling render() without a view $this->view = !$isLegacyTemplate ? "@Contao/$templateName.html.twig" : null; From d0d73f1f8b37062f39d00acf6a64b95c6702ff82 Mon Sep 17 00:00:00 2001 From: "M. Vondano" Date: Wed, 20 Apr 2022 18:31:16 +0200 Subject: [PATCH 22/28] enforce type hints --- .../contao/library/Contao/System.php | 4 +-- .../contao/library/Contao/Template.php | 14 ++++---- core-bundle/src/Twig/FragmentTemplate.php | 35 ++++--------------- 3 files changed, 16 insertions(+), 37 deletions(-) diff --git a/core-bundle/src/Resources/contao/library/Contao/System.php b/core-bundle/src/Resources/contao/library/Contao/System.php index 7630289c786..1e7da5f438d 100644 --- a/core-bundle/src/Resources/contao/library/Contao/System.php +++ b/core-bundle/src/Resources/contao/library/Contao/System.php @@ -125,9 +125,9 @@ protected function __construct() * * @param string $strKey The property name * - * @return mixed|null The property value or null + * @return mixed The property value or null */ - public function __get($strKey) + public function __get(string $strKey): mixed { if (!isset($this->arrObjects[$strKey])) { diff --git a/core-bundle/src/Resources/contao/library/Contao/Template.php b/core-bundle/src/Resources/contao/library/Contao/Template.php index 04408355a1e..3431c6e4dcf 100644 --- a/core-bundle/src/Resources/contao/library/Contao/Template.php +++ b/core-bundle/src/Resources/contao/library/Contao/Template.php @@ -132,7 +132,7 @@ public function __construct($strTemplate='', $strContentType='text/html') * @param string $strKey The property name * @param mixed $varValue The property value */ - public function __set($strKey, $varValue) + public function __set(string $strKey, mixed $varValue): void { $this->arrData[$strKey] = $varValue; } @@ -144,7 +144,7 @@ public function __set($strKey, $varValue) * * @return mixed The property value */ - public function __get($strKey) + public function __get(string $strKey): mixed { if (isset($this->arrData[$strKey])) { @@ -186,7 +186,7 @@ public function __call($strKey, $arrParams) * * @return boolean True if the property is set */ - public function __isset($strKey) + public function __isset(string $strKey): bool { return isset($this->arrData[$strKey]); } @@ -196,7 +196,7 @@ public function __isset($strKey) * * @param array $arrData The data array */ - public function setData($arrData) + public function setData(array $arrData): void { $this->arrData = $arrData; } @@ -206,7 +206,7 @@ public function setData($arrData) * * @return array The data array */ - public function getData() + public function getData(): array { return $this->arrData; } @@ -216,7 +216,7 @@ public function getData() * * @param string $strTemplate The template name */ - public function setName($strTemplate) + public function setName(string $strTemplate): void { $this->strTemplate = $strTemplate; } @@ -226,7 +226,7 @@ public function setName($strTemplate) * * @return string The template name */ - public function getName() + public function getName(): string { return $this->strTemplate; } diff --git a/core-bundle/src/Twig/FragmentTemplate.php b/core-bundle/src/Twig/FragmentTemplate.php index 9562ee7d8ce..0d97f637df1 100644 --- a/core-bundle/src/Twig/FragmentTemplate.php +++ b/core-bundle/src/Twig/FragmentTemplate.php @@ -41,29 +41,17 @@ public function __construct(private string $templateName, private \Closure $onGe // Do not call parent constructor } - /** - * @param string $key - * @param mixed $value - */ - public function __set(/* string */ $key, $value): void + public function __set(string $key, mixed $value): void { $this->set($key, $value); } - /** - * @param string $key - * - * @return mixed - */ - public function __get(/* string */ $key) + public function __get(string $key): mixed { return $this->get($key); } - /** - * @param string $key - */ - public function __isset(/* string */ $key): bool + public function __isset(string $key): bool { return $this->has($key); } @@ -73,18 +61,12 @@ public function __call($strKey, $arrParams): never self::throwOnAccess(); } - /** - * @param mixed $value - */ - public function set(string $key, $value): void + public function set(string $key, mixed $value): void { $this->context[$key] = $value; } - /** - * @return mixed - */ - public function get(string $key) + public function get(string $key): mixed { return $this->context[$key] ?? throw new \RuntimeException(sprintf('Key "%s" does not exist.', $key)); } @@ -97,7 +79,7 @@ public function has(string $key): bool /** * @param array $data */ - public function setData(/* array */ $data): void + public function setData(array $data): void { $this->context = $data; } @@ -110,10 +92,7 @@ public function getData(): array return $this->context; } - /** - * @param string $name - */ - public function setName(/* string */ $name): void + public function setName(string $name): void { $this->templateName = $name; } From b498e2d08bfb79077d35950287a31a97ad1bb2bf Mon Sep 17 00:00:00 2001 From: "M. Vondano" Date: Fri, 22 Apr 2022 10:46:30 +0200 Subject: [PATCH 23/28] Revert "enforce type hints" This reverts commit d0d73f1f8b37062f39d00acf6a64b95c6702ff82. --- .../contao/library/Contao/System.php | 4 +-- .../contao/library/Contao/Template.php | 14 ++++---- core-bundle/src/Twig/FragmentTemplate.php | 35 +++++++++++++++---- 3 files changed, 37 insertions(+), 16 deletions(-) diff --git a/core-bundle/src/Resources/contao/library/Contao/System.php b/core-bundle/src/Resources/contao/library/Contao/System.php index 1e7da5f438d..7630289c786 100644 --- a/core-bundle/src/Resources/contao/library/Contao/System.php +++ b/core-bundle/src/Resources/contao/library/Contao/System.php @@ -125,9 +125,9 @@ protected function __construct() * * @param string $strKey The property name * - * @return mixed The property value or null + * @return mixed|null The property value or null */ - public function __get(string $strKey): mixed + public function __get($strKey) { if (!isset($this->arrObjects[$strKey])) { diff --git a/core-bundle/src/Resources/contao/library/Contao/Template.php b/core-bundle/src/Resources/contao/library/Contao/Template.php index 3431c6e4dcf..04408355a1e 100644 --- a/core-bundle/src/Resources/contao/library/Contao/Template.php +++ b/core-bundle/src/Resources/contao/library/Contao/Template.php @@ -132,7 +132,7 @@ public function __construct($strTemplate='', $strContentType='text/html') * @param string $strKey The property name * @param mixed $varValue The property value */ - public function __set(string $strKey, mixed $varValue): void + public function __set($strKey, $varValue) { $this->arrData[$strKey] = $varValue; } @@ -144,7 +144,7 @@ public function __set(string $strKey, mixed $varValue): void * * @return mixed The property value */ - public function __get(string $strKey): mixed + public function __get($strKey) { if (isset($this->arrData[$strKey])) { @@ -186,7 +186,7 @@ public function __call($strKey, $arrParams) * * @return boolean True if the property is set */ - public function __isset(string $strKey): bool + public function __isset($strKey) { return isset($this->arrData[$strKey]); } @@ -196,7 +196,7 @@ public function __isset(string $strKey): bool * * @param array $arrData The data array */ - public function setData(array $arrData): void + public function setData($arrData) { $this->arrData = $arrData; } @@ -206,7 +206,7 @@ public function setData(array $arrData): void * * @return array The data array */ - public function getData(): array + public function getData() { return $this->arrData; } @@ -216,7 +216,7 @@ public function getData(): array * * @param string $strTemplate The template name */ - public function setName(string $strTemplate): void + public function setName($strTemplate) { $this->strTemplate = $strTemplate; } @@ -226,7 +226,7 @@ public function setName(string $strTemplate): void * * @return string The template name */ - public function getName(): string + public function getName() { return $this->strTemplate; } diff --git a/core-bundle/src/Twig/FragmentTemplate.php b/core-bundle/src/Twig/FragmentTemplate.php index 0d97f637df1..9562ee7d8ce 100644 --- a/core-bundle/src/Twig/FragmentTemplate.php +++ b/core-bundle/src/Twig/FragmentTemplate.php @@ -41,17 +41,29 @@ public function __construct(private string $templateName, private \Closure $onGe // Do not call parent constructor } - public function __set(string $key, mixed $value): void + /** + * @param string $key + * @param mixed $value + */ + public function __set(/* string */ $key, $value): void { $this->set($key, $value); } - public function __get(string $key): mixed + /** + * @param string $key + * + * @return mixed + */ + public function __get(/* string */ $key) { return $this->get($key); } - public function __isset(string $key): bool + /** + * @param string $key + */ + public function __isset(/* string */ $key): bool { return $this->has($key); } @@ -61,12 +73,18 @@ public function __call($strKey, $arrParams): never self::throwOnAccess(); } - public function set(string $key, mixed $value): void + /** + * @param mixed $value + */ + public function set(string $key, $value): void { $this->context[$key] = $value; } - public function get(string $key): mixed + /** + * @return mixed + */ + public function get(string $key) { return $this->context[$key] ?? throw new \RuntimeException(sprintf('Key "%s" does not exist.', $key)); } @@ -79,7 +97,7 @@ public function has(string $key): bool /** * @param array $data */ - public function setData(array $data): void + public function setData(/* array */ $data): void { $this->context = $data; } @@ -92,7 +110,10 @@ public function getData(): array return $this->context; } - public function setName(string $name): void + /** + * @param string $name + */ + public function setName(/* string */ $name): void { $this->templateName = $name; } From f5bf5458ff5c3f4efd89b9d1c23088a85bba0e62 Mon Sep 17 00:00:00 2001 From: "M. Vondano" Date: Tue, 26 Apr 2022 15:20:25 +0200 Subject: [PATCH 24/28] apply changes from #4536 + adjust tests --- .../Controller/AbstractFragmentController.php | 2 +- .../AbstractContentElementController.php | 2 +- .../AbstractFrontendModuleController.php | 2 +- .../ContentElementControllerTest.php | 39 +++++++------------ .../FrontendModuleControllerTest.php | 31 ++++++--------- .../TwoFactorControllerTest.php | 22 +++++------ 6 files changed, 40 insertions(+), 58 deletions(-) diff --git a/core-bundle/src/Controller/AbstractFragmentController.php b/core-bundle/src/Controller/AbstractFragmentController.php index c98b6c92a53..013ebf12a68 100644 --- a/core-bundle/src/Controller/AbstractFragmentController.php +++ b/core-bundle/src/Controller/AbstractFragmentController.php @@ -111,7 +111,7 @@ protected function createTemplate(Model $model, string|null $fallbackTemplateNam $template = new FragmentTemplate($templateName, $onGetResponse); if ($isLegacyTemplate) { - $template->setData((array) $model->row()); + $template->setData($model->row()); } return $template; diff --git a/core-bundle/src/Controller/ContentElement/AbstractContentElementController.php b/core-bundle/src/Controller/ContentElement/AbstractContentElementController.php index a8782b8a5f3..d88bd20ef6d 100644 --- a/core-bundle/src/Controller/ContentElement/AbstractContentElementController.php +++ b/core-bundle/src/Controller/ContentElement/AbstractContentElementController.php @@ -28,7 +28,7 @@ public function __invoke(Request $request, ContentModel $model, string $section, $this->addDefaultDataToTemplate( $template, - (array) $model->row(), + $model->row(), $section, $classes ?? [], $request->attributes->get('templateProperties', []), diff --git a/core-bundle/src/Controller/FrontendModule/AbstractFrontendModuleController.php b/core-bundle/src/Controller/FrontendModule/AbstractFrontendModuleController.php index 99d8403cbe4..5d15626f79b 100644 --- a/core-bundle/src/Controller/FrontendModule/AbstractFrontendModuleController.php +++ b/core-bundle/src/Controller/FrontendModule/AbstractFrontendModuleController.php @@ -33,7 +33,7 @@ public function __invoke(Request $request, ModuleModel $model, string $section, $this->addDefaultDataToTemplate( $template, - (array) $model->row(), + $model->row(), $section, $classes ?? [], $request->attributes->get('templateProperties', []), diff --git a/core-bundle/tests/Controller/ContentElement/ContentElementControllerTest.php b/core-bundle/tests/Controller/ContentElement/ContentElementControllerTest.php index 3cbc8bd9bfa..021b4c2ac40 100644 --- a/core-bundle/tests/Controller/ContentElement/ContentElementControllerTest.php +++ b/core-bundle/tests/Controller/ContentElement/ContentElementControllerTest.php @@ -54,7 +54,7 @@ public function testCreatesTheTemplateFromTheClassName(): void { $controller = $this->getTestController(); - $response = $controller(new Request(), $this->getContentModel(), 'main'); + $response = $controller(new Request(), $this->mockClassWithProperties(ContentModel::class), 'main'); $template = json_decode($response->getContent(), true); $this->assertSame('ce_test', $template['templateName']); @@ -64,7 +64,7 @@ public function testCreatesTheTemplateFromTheTypeFragmentOptions(): void { $controller = $this->getTestController(['type' => 'foo']); - $response = $controller(new Request(), $this->getContentModel(), 'main'); + $response = $controller(new Request(), $this->mockClassWithProperties(ContentModel::class), 'main'); $template = json_decode($response->getContent(), true); $this->assertSame('ce_foo', $template['templateName']); @@ -74,7 +74,7 @@ public function testCreatesTheTemplateFromTheTemplateFragmentOption(): void { $controller = $this->getTestController(['template' => 'ce_bar']); - $response = $controller(new Request(), $this->getContentModel(), 'main'); + $response = $controller(new Request(), $this->mockClassWithProperties(ContentModel::class), 'main'); $template = json_decode($response->getContent(), true); $this->assertSame('ce_bar', $template['templateName']); @@ -86,7 +86,7 @@ public function testCreatesTheTemplateFromACustomTpl(): void $controller = $this->getTestController(['template' => 'ce_bar']); - $model = $this->getContentModel(['customTpl' => 'ce_bar']); + $model = $this->mockClassWithProperties(ContentModel::class, ['customTpl' => 'ce_bar']); $response = $controller(new Request(), $model, 'main'); $template = json_decode($response->getContent(), true); @@ -105,7 +105,7 @@ public function testDoesNotCreateTheTemplateFromACustomTplInTheBackend(): void $controller = $this->getTestController(); - $model = $this->getContentModel(['customTpl' => 'ce_bar']); + $model = $this->mockClassWithProperties(ContentModel::class, ['customTpl' => 'ce_bar']); $response = $controller($request, $model, 'main'); $template = json_decode($response->getContent(), true); @@ -117,7 +117,7 @@ public function testSetsTheClassFromTheType(): void { $controller = $this->getTestController(); - $response = $controller(new Request(), $this->getContentModel(), 'main'); + $response = $controller(new Request(), $this->mockClassWithProperties(ContentModel::class), 'main'); $template = json_decode($response->getContent(), true); $this->assertSame('', $template['cssID']); @@ -128,7 +128,7 @@ public function testSetsTheHeadlineFromTheModel(): void { $controller = $this->getTestController(); - $model = $this->getContentModel(['headline' => serialize(['unit' => 'h6', 'value' => 'foobar'])]); + $model = $this->mockClassWithProperties(ContentModel::class, ['headline' => serialize(['unit' => 'h6', 'value' => 'foobar'])]); $response = $controller(new Request(), $model, 'main'); $template = json_decode($response->getContent(), true); @@ -141,7 +141,7 @@ public function testSetsTheCssIdAndClassFromTheModel(): void { $controller = $this->getTestController(); - $model = $this->getContentModel(['cssID' => serialize(['foo', 'bar'])]); + $model = $this->mockClassWithProperties(ContentModel::class, ['cssID' => serialize(['foo', 'bar'])]); $response = $controller(new Request(), $model, 'main'); $template = json_decode($response->getContent(), true); @@ -154,7 +154,7 @@ public function testSetsTheLayoutSection(): void { $controller = $this->getTestController(); - $response = $controller(new Request(), $this->getContentModel(), 'left'); + $response = $controller(new Request(), $this->mockClassWithProperties(ContentModel::class), 'left'); $template = json_decode($response->getContent(), true); $this->assertSame('left', $template['inColumn']); @@ -164,7 +164,7 @@ public function testSetsTheClasses(): void { $controller = $this->getTestController(); - $response = $controller(new Request(), $this->mockContentModel(), 'main', ['foo', 'bar']); + $response = $controller(new Request(), $this->mockClassWithProperties(ContentModel::class), 'main', ['foo', 'bar']); $template = json_decode($response->getContent(), true); $this->assertSame('ce_test foo bar', $template['class']); @@ -201,7 +201,7 @@ public function testSetsTemplateContextForModernFragments(bool $backendScope): v $controller = $this->getTestController(['type' => 'text', 'template' => 'content_element/text']); - $model = $this->getContentModel([ + $model = $this->mockClassWithProperties(ContentModel::class, [ 'headline' => serialize(['value' => 'foo', 'unit' => 'h3']), 'cssID' => serialize(['foo-id', 'foo-class']), ]); @@ -226,7 +226,7 @@ public function provideScope(): \Generator public function testAddsTheCacheTags(): void { - $model = $this->getContentModel(['id' => 42]); + $model = $this->mockClassWithProperties(ContentModel::class, ['id' => 42]); $entityCacheTags = $this->createMock(EntityCacheTags::class); $entityCacheTags @@ -250,7 +250,7 @@ public function testSetsTheSharedMaxAgeIfTheElementHasAStartDate(): void $start = strtotime('+2 weeks', $time); $expires = $start - $time; - $model = $this->getContentModel(['start' => (string) $start]); + $model = $this->mockClassWithProperties(ContentModel::class, ['start' => (string) $start]); $controller = new TestSharedMaxAgeController(); $controller->setContainer($this->container); @@ -270,7 +270,7 @@ public function testSetsTheSharedMaxAgeIfTheElementHasAStopDate(): void $stop = strtotime('+2 weeks', $time); $expires = $stop - $time; - $model = $this->getContentModel(['stop' => (string) $stop]); + $model = $this->mockClassWithProperties(ContentModel::class, ['stop' => (string) $stop]); $controller = new TestSharedMaxAgeController(); $controller->setContainer($this->container); @@ -287,20 +287,11 @@ public function testDoesNotSetTheSharedMaxAgeIfTheElementHasNeitherAStartNorASto $controller = new TestSharedMaxAgeController(); $controller->setContainer($this->container); - $response = $controller(new Request(), $this->getContentModel(), 'main'); + $response = $controller(new Request(), $this->mockClassWithProperties(ContentModel::class), 'main'); $this->assertNull($response->getMaxAge()); } - private function getContentModel(array $data = []): ContentModel - { - /** @var ContentModel $model */ - $model = (new \ReflectionClass(ContentModel::class))->newInstanceWithoutConstructor(); - $model->setRow($data); - - return $model; - } - private function getTestController(array $fragmentOptions = []): TestController { $controller = new TestController(); diff --git a/core-bundle/tests/Controller/FrontendModule/FrontendModuleControllerTest.php b/core-bundle/tests/Controller/FrontendModule/FrontendModuleControllerTest.php index e0601f36dd6..6efc7cbeb5a 100644 --- a/core-bundle/tests/Controller/FrontendModule/FrontendModuleControllerTest.php +++ b/core-bundle/tests/Controller/FrontendModule/FrontendModuleControllerTest.php @@ -54,7 +54,7 @@ public function testCreatesTheTemplateFromTheClassName(): void { $controller = $this->getTestController(); - $response = $controller(new Request([], [], ['_scope' => 'frontend']), $this->getModuleModel(), 'main'); + $response = $controller(new Request([], [], ['_scope' => 'frontend']), $this->mockClassWithProperties(ModuleModel::class), 'main'); $template = json_decode($response->getContent(), true); $this->assertSame('mod_test', $template['templateName']); @@ -64,7 +64,7 @@ public function testCreatesTheTemplateFromTheTypeFragmentOption(): void { $controller = $this->getTestController(['type' => 'foo']); - $response = $controller(new Request(), $this->getModuleModel(), 'main'); + $response = $controller(new Request(), $this->mockClassWithProperties(ModuleModel::class), 'main'); $template = json_decode($response->getContent(), true); $this->assertSame('mod_foo', $template['templateName']); @@ -74,7 +74,7 @@ public function testCreatesTheTemplateFromTheTemplateFragmentOption(): void { $controller = $this->getTestController(['template' => 'mod_bar']); - $response = $controller(new Request(), $this->getModuleModel(), 'main'); + $response = $controller(new Request(), $this->mockClassWithProperties(ModuleModel::class), 'main'); $template = json_decode($response->getContent(), true); $this->assertSame('mod_bar', $template['templateName']); @@ -86,7 +86,7 @@ public function testCreatesTheTemplateFromACustomTpl(): void $controller = $this->getTestController(); - $model = $this->getModuleModel(['customTpl' => 'mod_bar']); + $model = $this->mockClassWithProperties(ModuleModel::class, ['customTpl' => 'mod_bar']); $response = $controller(new Request(), $model, 'main'); $template = json_decode($response->getContent(), true); @@ -98,7 +98,7 @@ public function testSetsTheClassFromTheType(): void { $controller = $this->getTestController(); - $response = $controller(new Request(), $this->getModuleModel(), 'main'); + $response = $controller(new Request(), $this->mockClassWithProperties(ModuleModel::class), 'main'); $template = json_decode($response->getContent(), true); $this->assertSame('', $template['cssID']); @@ -109,7 +109,7 @@ public function testSetsTheHeadlineFromTheModel(): void { $controller = $this->getTestController(); - $model = $this->getModuleModel(['headline' => serialize(['unit' => 'h6', 'value' => 'foobar'])]); + $model = $this->mockClassWithProperties(ModuleModel::class, ['headline' => serialize(['unit' => 'h6', 'value' => 'foobar'])]); $response = $controller(new Request(), $model, 'main'); $template = json_decode($response->getContent(), true); @@ -122,7 +122,7 @@ public function testSetsTheCssIdAndClassFromTheModel(): void { $controller = $this->getTestController(); - $model = $this->getModuleModel(['cssID' => serialize(['foo', 'bar'])]); + $model = $this->mockClassWithProperties(ModuleModel::class, ['cssID' => serialize(['foo', 'bar'])]); $response = $controller(new Request(), $model, 'main'); $template = json_decode($response->getContent(), true); @@ -135,7 +135,7 @@ public function testSetsTheLayoutSection(): void { $controller = $this->getTestController(); - $response = $controller(new Request(), $this->getModuleModel(), 'left'); + $response = $controller(new Request(), $this->mockClassWithProperties(ModuleModel::class), 'left'); $template = json_decode($response->getContent(), true); $this->assertSame('left', $template['inColumn']); @@ -169,7 +169,7 @@ public function testSetsTemplateContextForModernFragments(): void $controller = $this->getTestController(['type' => 'html', 'template' => 'frontend_module/html']); - $model = $this->getModuleModel([ + $model = $this->mockClassWithProperties(ModuleModel::class, [ 'headline' => serialize(['value' => 'foo', 'unit' => 'h3']), 'cssID' => serialize(['foo-id', 'foo-class']), ]); @@ -230,7 +230,7 @@ public function testReturnsWildCardInBackendScope(): void $controller = $this->getTestController(); - $model = $this->getModuleModel([ + $model = $this->mockClassWithProperties(ModuleModel::class, [ 'id' => 42, 'type' => 'foobar', 'name' => 'foo', @@ -250,7 +250,7 @@ public function provideScope(): \Generator public function testAddsTheCacheTags(): void { - $model = $this->getModuleModel(); + $model = $this->mockClassWithProperties(ModuleModel::class); $model->id = 42; $entityCacheTags = $this->createMock(EntityCacheTags::class); @@ -290,15 +290,6 @@ function (string $class, array $params) use ($templateName): FragmentTemplate { return $this->container; } - private function getModuleModel(array $data = []): ModuleModel - { - /** @var ModuleModel $model */ - $model = (new \ReflectionClass(ModuleModel::class))->newInstanceWithoutConstructor(); - $model->setRow($data); - - return $model; - } - private function getTestController(array $fragmentOptions = []): TestController { $controller = new TestController(); diff --git a/core-bundle/tests/Controller/FrontendModule/TwoFactorControllerTest.php b/core-bundle/tests/Controller/FrontendModule/TwoFactorControllerTest.php index 3b93e384a7e..ef17166b957 100644 --- a/core-bundle/tests/Controller/FrontendModule/TwoFactorControllerTest.php +++ b/core-bundle/tests/Controller/FrontendModule/TwoFactorControllerTest.php @@ -56,7 +56,7 @@ public function testReturnsEmptyResponseIfTheUserIsNotFullyAuthenticated(): void $controller = new TwoFactorController(); $controller->setContainer($container); - $module = $this->createMock(ModuleModel::class); + $module = $this->mockClassWithProperties(ModuleModel::class); $page = $this->mockPageModel(); $response = $controller(new Request(), $module, 'main', null, $page); @@ -78,7 +78,7 @@ public function testReturnsIfTheUserIsNotAFrontendUser(): void $controller = new TwoFactorController(); $controller->setContainer($container); - $module = $this->createMock(ModuleModel::class); + $module = $this->mockClassWithProperties(ModuleModel::class); $page = $this->mockPageModel(); $response = $controller(new Request(), $module, 'main', null, $page); @@ -101,7 +101,7 @@ public function testReturnsAResponseIfTheUserIsAFrontendUser(): void $controller = new TwoFactorController(); $controller->setContainer($container); - $module = $this->createMock(ModuleModel::class); + $module = $this->mockClassWithProperties(ModuleModel::class); $page = $this->mockPageModel(); $page->enforceTwoFactor = '1'; @@ -129,7 +129,7 @@ public function testReturnsIfTwoFactorAuthenticationIsAlreadyDisabled(): void $request = new Request(); $request->request->set('FORM_SUBMIT', 'tl_two_factor_disable'); - $module = $this->createMock(ModuleModel::class); + $module = $this->mockClassWithProperties(ModuleModel::class); $page = $this->mockPageModel(); $response = $controller($request, $module, 'main', null, $page); @@ -164,7 +164,7 @@ public function testRedirectsAfterTwoFactorHasBeenDisabled(): void $request = new Request(); $request->request->set('FORM_SUBMIT', 'tl_two_factor_disable'); - $module = $this->createMock(ModuleModel::class); + $module = $this->mockClassWithProperties(ModuleModel::class); $page = $this->mockPageModel(); $page @@ -200,7 +200,7 @@ public function testReturnsIfTwoFactorAuthenticationIsAlreadyEnabled(): void $request = new Request(); $request->request->set('2fa', 'enable'); - $module = $this->createMock(ModuleModel::class); + $module = $this->mockClassWithProperties(ModuleModel::class); $page = $this->mockPageModel(); $page @@ -231,7 +231,7 @@ public function testFailsIfTheTwoFactorCodeIsInvalid(): void $request = new Request(); $request->request->set('2fa', 'enable'); - $module = $this->createMock(ModuleModel::class); + $module = $this->mockClassWithProperties(ModuleModel::class); $page = $this->mockPageModel(); $page @@ -263,7 +263,7 @@ public function testDoesNotRedirectIfTheTwoFactorCodeIsInvalid(): void $request->request->set('FORM_SUBMIT', 'tl_two_factor'); $request->request->set('verify', '123456'); - $module = $this->createMock(ModuleModel::class); + $module = $this->mockClassWithProperties(ModuleModel::class); $page = $this->mockPageModel(); $page @@ -300,7 +300,7 @@ public function testRedirectsIfTheTwoFactorCodeIsValid(): void $request->request->set('FORM_SUBMIT', 'tl_two_factor'); $request->request->set('verify', '123456'); - $module = $this->createMock(ModuleModel::class); + $module = $this->mockClassWithProperties(ModuleModel::class); $page = $this->mockPageModel(); $page @@ -332,7 +332,7 @@ public function testShowsTheBackupCodes(): void $request = new Request(); $request->request->set('FORM_SUBMIT', 'tl_two_factor_show_backup_codes'); - $module = $this->createMock(ModuleModel::class); + $module = $this->mockClassWithProperties(ModuleModel::class); $page = $this->mockPageModel(); /** @var RedirectResponse $response */ @@ -366,7 +366,7 @@ public function testGeneratesTheBackupCodes(): void $request = new Request(); $request->request->set('FORM_SUBMIT', 'tl_two_factor_generate_backup_codes'); - $module = $this->createMock(ModuleModel::class); + $module = $this->mockClassWithProperties(ModuleModel::class); $page = $this->mockPageModel(); /** @var RedirectResponse $response */ From facfa23b0fe6df75c5deb3e779c1681e59bad694 Mon Sep 17 00:00:00 2001 From: "M. Vondano" Date: Tue, 26 Apr 2022 15:31:11 +0200 Subject: [PATCH 25/28] drop removed methods --- core-bundle/src/Twig/FragmentTemplate.php | 97 ----------------------- 1 file changed, 97 deletions(-) diff --git a/core-bundle/src/Twig/FragmentTemplate.php b/core-bundle/src/Twig/FragmentTemplate.php index 9562ee7d8ce..b4b572e9c8e 100644 --- a/core-bundle/src/Twig/FragmentTemplate.php +++ b/core-bundle/src/Twig/FragmentTemplate.php @@ -13,7 +13,6 @@ namespace Contao\CoreBundle\Twig; use Contao\CoreBundle\Asset\ContaoContext; -use Contao\FilesModel; use Contao\Model; use Contao\Template; use Symfony\Component\DependencyInjection\ContainerInterface; @@ -231,14 +230,6 @@ public static function isVisibleElement(Model $objElement): never self::throwOnAccess(); } - /** - * @internal - */ - public static function replaceInsertTags($strBuffer, $blnCache = true): never - { - self::throwOnAccess(); - } - /** * @internal */ @@ -271,14 +262,6 @@ public static function redirect($strLocation, $intStatus = 303): never self::throwOnAccess(); } - /** - * @internal - */ - public static function generateFrontendUrl(array $arrRow, $strParams = null, $strForceLang = null, $blnFixDomain = false): never - { - self::throwOnAccess(); - } - /** * @internal */ @@ -311,14 +294,6 @@ public static function resetControllerCache(): never self::throwOnAccess(); } - /** - * @internal - */ - public static function addImageToTemplate($template, array $rowData, $maxWidth = null, $lightboxGroupIdentifier = null, FilesModel $filesModel = null): never - { - self::throwOnAccess(); - } - /** * @internal */ @@ -359,38 +334,6 @@ public static function addFilesUrlTo($script): void self::throwOnAccess(); } - /** - * @internal - */ - public static function restoreBasicEntities($strBuffer): void - { - self::throwOnAccess(); - } - - /** - * @internal - */ - public static function generateImage($src, $alt = '', $attributes = ''): void - { - self::throwOnAccess(); - } - - /** - * @internal - */ - public static function optionSelected($strOption, $varValues): void - { - self::throwOnAccess(); - } - - /** - * @internal - */ - public static function optionChecked($strOption, $varValues): void - { - self::throwOnAccess(); - } - /** * @internal */ @@ -399,14 +342,6 @@ public function parse(): never self::throwOnAccess(); } - /** - * @internal - */ - public function output(): void - { - self::throwOnAccess(); - } - /** * @internal */ @@ -423,14 +358,6 @@ public static function importStatic($strClass, $strKey = null, $blnForce = false self::throwOnAccess(); } - /** - * @internal - */ - public static function log($strText, $strFunction, $strCategory): never - { - self::throwOnAccess(); - } - /** * @internal */ @@ -455,30 +382,6 @@ public static function isInstalledLanguage($strLanguage): never self::throwOnAccess(); } - /** - * @internal - */ - public static function getCountries(): never - { - self::throwOnAccess(); - } - - /** - * @internal - */ - public static function getLanguages($blnInstalledOnly = false): never - { - self::throwOnAccess(); - } - - /** - * @internal - */ - public static function getTimeZones(): never - { - self::throwOnAccess(); - } - /** * @internal */ From abd3fc62c4d8c1c7bd2b8024aeb454e2d2951aaf Mon Sep 17 00:00:00 2001 From: Leo Feyer Date: Tue, 26 Apr 2022 17:13:38 +0200 Subject: [PATCH 26/28] CS --- core-bundle/src/Twig/FragmentTemplate.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/core-bundle/src/Twig/FragmentTemplate.php b/core-bundle/src/Twig/FragmentTemplate.php index b4b572e9c8e..ec269992fa1 100644 --- a/core-bundle/src/Twig/FragmentTemplate.php +++ b/core-bundle/src/Twig/FragmentTemplate.php @@ -44,7 +44,7 @@ public function __construct(private string $templateName, private \Closure $onGe * @param string $key * @param mixed $value */ - public function __set(/* string */ $key, $value): void + public function __set($key, $value): void { $this->set($key, $value); } @@ -54,7 +54,7 @@ public function __set(/* string */ $key, $value): void * * @return mixed */ - public function __get(/* string */ $key) + public function __get($key) { return $this->get($key); } @@ -62,7 +62,7 @@ public function __get(/* string */ $key) /** * @param string $key */ - public function __isset(/* string */ $key): bool + public function __isset($key): bool { return $this->has($key); } @@ -96,7 +96,7 @@ public function has(string $key): bool /** * @param array $data */ - public function setData(/* array */ $data): void + public function setData($data): void { $this->context = $data; } @@ -112,7 +112,7 @@ public function getData(): array /** * @param string $name */ - public function setName(/* string */ $name): void + public function setName($name): void { $this->templateName = $name; } From a3d3aed9e7a5faf293173c5723652a6f8ba035c8 Mon Sep 17 00:00:00 2001 From: "M. Vondano" Date: Tue, 26 Apr 2022 17:17:37 +0200 Subject: [PATCH 27/28] Update core-bundle/tests/Twig/FragmentTemplateTest.php Co-authored-by: Leo Feyer --- core-bundle/tests/Twig/FragmentTemplateTest.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/core-bundle/tests/Twig/FragmentTemplateTest.php b/core-bundle/tests/Twig/FragmentTemplateTest.php index 82a7637e9f6..ed0d4be27a1 100644 --- a/core-bundle/tests/Twig/FragmentTemplateTest.php +++ b/core-bundle/tests/Twig/FragmentTemplateTest.php @@ -87,11 +87,10 @@ public function provideIllegalParentMethods(): \Generator $args = array_map( function (\ReflectionParameter $parameter) { - if (!($type = $parameter->getType()) instanceof \ReflectionType) { + if (!($type = $parameter->getType()) instanceof \ReflectionNamedType) { return null; } - /** @phpstan-ignore-next-line because PHPStan doesn't think getName() exists */ return match ($name = $type->getName()) { 'bool' => false, 'string' => '', From 8815efda2c29ba72ccf06490039527903f69fc6c Mon Sep 17 00:00:00 2001 From: Leo Feyer Date: Tue, 26 Apr 2022 17:51:01 +0200 Subject: [PATCH 28/28] Adjust the deprecation message --- core-bundle/src/Resources/contao/library/Contao/Template.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core-bundle/src/Resources/contao/library/Contao/Template.php b/core-bundle/src/Resources/contao/library/Contao/Template.php index b8dd11db3f3..ea187109bbc 100644 --- a/core-bundle/src/Resources/contao/library/Contao/Template.php +++ b/core-bundle/src/Resources/contao/library/Contao/Template.php @@ -63,7 +63,8 @@ * @property array $trustedDevices * @property string $currentDevice * - * @deprecated this class will be removed in Contao 6, use Twig templates instead + * @deprecated Deprecated since Contao 5.0, to be removed in Contao 6.0; + * use Twig templates instead */ abstract class Template extends Controller {