Olivier Massot 1 anno fa
parent
commit
3bb51c3ce0
57 ha cambiato i file con 457 aggiunte e 322 eliminazioni
  1. 37 37
      composer.json
  2. 1 1
      doc/docker.md
  3. 5 3
      ot_admin/Classes/Command/AddRedirectionCommand.php
  4. 2 2
      ot_admin/Classes/Command/ClearObsoleteWebsitesSiteCommand.php
  5. 2 2
      ot_admin/Classes/Command/ClearSiteCacheCommand.php
  6. 2 2
      ot_admin/Classes/Command/CreateSiteCommand.php
  7. 2 2
      ot_admin/Classes/Command/DeleteSiteCommand.php
  8. 2 2
      ot_admin/Classes/Command/GetSiteStatusCommand.php
  9. 2 2
      ot_admin/Classes/Command/RegenConfigFilesCommand.php
  10. 2 2
      ot_admin/Classes/Command/RemoveRedirectionCommand.php
  11. 2 2
      ot_admin/Classes/Command/ResetBeUserPermsCommand.php
  12. 2 2
      ot_admin/Classes/Command/ScanCommand.php
  13. 2 2
      ot_admin/Classes/Command/SetSiteDomainCommand.php
  14. 1 1
      ot_admin/Classes/Command/UndeleteSiteCommand.php
  15. 2 2
      ot_admin/Classes/Command/UpdateSiteCommand.php
  16. 28 25
      ot_admin/Classes/Controller/SiteController.php
  17. 11 0
      ot_admin/Configuration/Icons.php
  18. BIN
      ot_admin/Resources/Public/Icons/Extension.png
  19. 8 1
      ot_admin/ext_tables.sql
  20. 0 2
      ot_connect/Classes/Middleware/RequestHandler.php
  21. 11 0
      ot_connect/Configuration/Icons.php
  22. BIN
      ot_connect/Resources/Public/Icons/Extension.png
  23. 0 7
      ot_core/Classes/Controller/SelectedSiteController.php
  24. 27 15
      ot_core/Classes/Website/OtWebsiteRepository.php
  25. 11 0
      ot_core/Configuration/Icons.php
  26. BIN
      ot_core/Resources/Public/Icons/Extension.png
  27. BIN
      ot_core/Resources/Public/Icons/Extension_white.png
  28. 4 8
      ot_core/composer.json
  29. 11 0
      ot_optimizer/Configuration/Icons.php
  30. 2 2
      ot_optimizer/Configuration/RequestMiddlewares.php
  31. BIN
      ot_optimizer/Resources/Public/Icons/Extension.png
  32. 3 5
      ot_stats/Classes/Domain/Repository/MatomoWebsiteRepository.php
  33. 27 0
      ot_stats/Configuration/Backend/Modules.php
  34. 15 0
      ot_stats/Configuration/Icons.php
  35. 7 6
      ot_stats/Resources/Private/Layouts/Backend/Default.html
  36. BIN
      ot_stats/Resources/Public/Icons/Extension.png
  37. BIN
      ot_stats/Resources/Public/Icons/Extension_white.png
  38. 0 32
      ot_stats/ext_tables.php
  39. 2 2
      ot_templating/Classes/Controller/OtCustomizerController.php
  40. 135 29
      ot_templating/Classes/ViewHelpers/CObjectViewHelper.php
  41. 0 49
      ot_templating/Classes/ViewHelpers/ImagePViewHelper.php
  42. 25 0
      ot_templating/Configuration/Backend/Modules.php
  43. 6 1
      ot_templating/Configuration/Icons.php
  44. 3 0
      ot_templating/Configuration/Services.yaml
  45. 1 1
      ot_templating/Configuration/TypoScript/constants.typoscript
  46. 8 0
      ot_templating/Configuration/TypoScript/setup.typoscript
  47. 4 5
      ot_templating/Resources/Private/Layouts/Backend/Default.html
  48. 1 1
      ot_templating/Resources/Private/Partials/Classic/Carousel.html
  49. 2 1
      ot_templating/Resources/Private/Partials/Classic/UserToolbar.html
  50. 1 1
      ot_templating/Resources/Private/Partials/Modern/Carousel.html
  51. 16 16
      ot_templating/Resources/Private/Templates/OtCustomizer/Index.html
  52. 9 3
      ot_templating/Resources/Private/Templates/Page/Error/403.html
  53. 6 4
      ot_templating/Resources/Private/Templates/Page/Error/404.html
  54. 3 1
      ot_templating/Resources/Private/Templates/Page/Error/500.html
  55. 2 1
      ot_templating/Resources/Public/assets/Backend/style/ot_customizer.css
  56. 2 2
      ot_templating/ext_localconf.php
  57. 0 38
      ot_templating/ext_tables.php

+ 37 - 37
composer.json

@@ -1,45 +1,46 @@
 {
     "require": {
-        "typo3/cms-about": "^11.5",
-        "typo3/cms-adminpanel": "^11.5",
-        "typo3/cms-backend": "^11.5",
-        "typo3/cms-belog": "^11.5",
-        "typo3/cms-beuser": "^11.5",
-        "typo3/cms-core": "^11.5",
-        "typo3/cms-extbase": "^11.5",
-        "typo3/cms-extensionmanager": "^11.5",
-        "typo3/cms-felogin": "^11.5",
-        "typo3/cms-filelist": "^11.5",
-        "typo3/cms-filemetadata": "^11.5",
-        "typo3/cms-fluid": "^11.5",
-        "typo3/cms-fluid-styled-content": "^11.5",
-        "typo3/cms-form": "^11.5",
-        "typo3/cms-frontend": "^11.5",
-        "typo3/cms-impexp": "^11.5",
-        "typo3/cms-info": "^11.5",
-        "typo3/cms-install": "^11.5",
-        "typo3/cms-lowlevel": "^11.5",
-        "typo3/cms-recycler": "^11.5",
-        "typo3/cms-redirects": "^11.5",
-        "typo3/cms-reports": "^11.5",
-        "typo3/cms-rte-ckeditor": "^11.5",
-        "typo3/cms-scheduler": "^11.5",
-        "typo3/cms-seo": "^11.5",
-        "typo3/cms-setup": "^11.5",
-        "typo3/cms-t3editor": "^11.5",
-        "typo3/cms-tstemplate": "^11.5",
-        "typo3/cms-viewpage": "^11.5",
-        "typo3/cms-dashboard": "^11.5",
-        "typo3/cms-opendocs": "^11.5",
-        "typo3/cms-recordlist": "^11.5",
-        "helhum/typo3-console": "^7.1",
-        "sgalinski/lfeditor": "^7.1",
+        "typo3/cms-about": "^12.4",
+        "typo3/cms-adminpanel": "^12.4",
+        "typo3/cms-backend": "^12.4",
+        "typo3/cms-belog": "^12.4",
+        "typo3/cms-beuser": "^12.4",
+        "typo3/cms-core": "^12.4",
+        "typo3/cms-extbase": "^12.4",
+        "typo3/cms-extensionmanager": "^12.4",
+        "typo3/cms-felogin": "^12.4",
+        "typo3/cms-filelist": "^12.4",
+        "typo3/cms-filemetadata": "^12.4",
+        "typo3/cms-fluid": "^12.4",
+        "typo3/cms-fluid-styled-content": "^12.4",
+        "typo3/cms-form": "^12.4",
+        "typo3/cms-frontend": "^12.4",
+        "typo3/cms-impexp": "^12.4",
+        "typo3/cms-info": "^12.4",
+        "typo3/cms-install": "^12.4",
+        "typo3/cms-lowlevel": "^12.4",
+        "typo3/cms-recycler": "^12.4",
+        "typo3/cms-redirects": "^12.4",
+        "typo3/cms-reports": "^12.4",
+        "typo3/cms-rte-ckeditor": "^12.4",
+        "typo3/cms-scheduler": "^12.4",
+        "typo3/cms-seo": "^12.4",
+        "typo3/cms-setup": "^12.4",
+        "typo3/cms-tstemplate": "^12.4",
+        "typo3/cms-viewpage": "^12.4",
+        "typo3/cms-dashboard": "^12.4",
+        "typo3/cms-opendocs": "^12.4",
+        "typo3/cms-recordlist": "^12.4",
+        "helhum/typo3-console": "*",
+        "sgalinski/lfeditor": "^8.0",
         "opentalent/ot_core": "^1.0",
         "opentalent/ot_admin": "^1.0",
         "opentalent/ot_templating": "^1.0",
         "opentalent/ot_stats": "^1.0",
         "opentalent/ot_optimizer": "^1.0",
-        "opentalent/ot_connect": "^1.0"
+        "opentalent/ot_connect": "^1.0",
+        "typo3/cms-linkvalidator": "^12.4",
+        "typo3/cms-t3editor": "^12.4"
     },
     "repositories": [
         {
@@ -53,7 +54,7 @@
     ],
     "scripts": {
         "typo3-cms-scripts": [
-            "typo3cms install:fixfolderstructure"
+            "typo3 install:fixfolderstructure"
         ],
         "post-autoload-dump": [
             "@typo3-cms-scripts"
@@ -66,7 +67,6 @@
         }
     },
     "require-dev": {
-        "ssch/typo3-rector": "^1.3",
         "phpspec/prophecy": "^1.19"
     }
 }

+ 1 - 1
doc/docker.md

@@ -27,7 +27,7 @@ Pour répliquer manuellement l'installation Typo3 telle qu'elle existe sur test:
 1. Accéder en SSH au serveur preprod, et lancer:
 
     cd /var/www/typo3
-    tar cvzf ./typo3_install.tar.gz . --exclude='./public/fileadmin/user_upload' --exclude='./archive' --exclude='./public/fileadmin/_processed_' --exclude='./var/log/*.log'
+    tar --exclude='./public/fileadmin/user_upload' --exclude='./archive.del' --exclude='./public/fileadmin/_processed_' --exclude='./var/log/*.log' -cvzf ./typo3_install.tar.gz .
 
 2. Télécharger l'archive
 

+ 5 - 3
ot_admin/Classes/Command/AddRedirectionCommand.php

@@ -3,6 +3,7 @@
 namespace Opentalent\OtAdmin\Command;
 
 
+use Doctrine\DBAL\Driver\Exception;
 use Opentalent\OtAdmin\Controller\SiteController;
 use Symfony\Component\Console\Command\Command;
 use Symfony\Component\Console\Input\InputArgument;
@@ -33,7 +34,7 @@ class AddRedirectionCommand extends Command
      * Allows to add a description, a help text, and / or define arguments.
      *
      */
-    protected function configure()
+    protected function configure(): void
     {
         $this
             ->setName("ot:redirection:add")
@@ -55,9 +56,10 @@ class AddRedirectionCommand extends Command
      *
      * @param InputInterface $input
      * @param OutputInterface $output
-     * @throws \Exception
+     * @return int
+     * @throws Exception
      */
-    protected function execute(InputInterface $input, OutputInterface $output)
+    protected function execute(InputInterface $input, OutputInterface $output): int
     {
         $fromDomain = $input->getArgument('from-domain');
         $toDomain = $input->getArgument('to-domain');

+ 2 - 2
ot_admin/Classes/Command/ClearObsoleteWebsitesSiteCommand.php

@@ -36,7 +36,7 @@ class ClearObsoleteWebsitesSiteCommand extends Command
      * Allows to add a description, a help text, and / or define arguments.
      *
      */
-    protected function configure()
+    protected function configure(): void
     {
         $this
             ->setName("ot:clear-obsoletes-websites")
@@ -66,7 +66,7 @@ class ClearObsoleteWebsitesSiteCommand extends Command
      * @throws \TYPO3\CMS\Extbase\Object\Exception
      * @throws \Throwable
      */
-    protected function execute(InputInterface $input, OutputInterface $output)
+    protected function execute(InputInterface $input, OutputInterface $output): int
     {
         $preview = $input->getOption('preview');
 

+ 2 - 2
ot_admin/Classes/Command/ClearSiteCacheCommand.php

@@ -34,7 +34,7 @@ class ClearSiteCacheCommand extends Command
      * Allows to add a description, a help text, and / or define arguments.
      *
      */
-    protected function configure()
+    protected function configure(): void
     {
         $this
             ->setName("ot:site:clear-cache")
@@ -59,7 +59,7 @@ class ClearSiteCacheCommand extends Command
      * @param OutputInterface $output
      * @throws \Exception
      */
-    protected function execute(InputInterface $input, OutputInterface $output)
+    protected function execute(InputInterface $input, OutputInterface $output): int
     {
         $org_id = $input->getArgument('organization-id');
         $clearAll = $input->getOption('all');

+ 2 - 2
ot_admin/Classes/Command/CreateSiteCommand.php

@@ -34,7 +34,7 @@ class CreateSiteCommand extends Command
      * Allows to add a description, a help text, and / or define arguments.
      *
      */
-    protected function configure()
+    protected function configure(): void
     {
         $this
             ->setName("ot:site:create")
@@ -63,7 +63,7 @@ class CreateSiteCommand extends Command
      * @return int
      * @throws \Exception
      */
-    protected function execute(InputInterface $input, OutputInterface $output)
+    protected function execute(InputInterface $input, OutputInterface $output): int
     {
         $org_id = $input->getArgument('organization-id');
 

+ 2 - 2
ot_admin/Classes/Command/DeleteSiteCommand.php

@@ -33,7 +33,7 @@ class DeleteSiteCommand extends Command
      * Allows to add a description, a help text, and / or define arguments.
      *
      */
-    protected function configure()
+    protected function configure(): void
     {
         $this
             ->setName("ot:site:delete")
@@ -77,7 +77,7 @@ class DeleteSiteCommand extends Command
      * @return int
      * @throws \Exception
      */
-    protected function execute(InputInterface $input, OutputInterface $output)
+    protected function execute(InputInterface $input, OutputInterface $output): int
     {
         $org_id = $input->getArgument('organization-id');
         $hard = $input->getOption('hard');

+ 2 - 2
ot_admin/Classes/Command/GetSiteStatusCommand.php

@@ -34,7 +34,7 @@ class GetSiteStatusCommand extends Command
      * Allows to add a description, a help text, and / or define arguments.
      *
      */
-    protected function configure()
+    protected function configure(): void
     {
         $this
             ->setName("ot:site:status")
@@ -62,7 +62,7 @@ class GetSiteStatusCommand extends Command
      * @throws \Opentalent\OtCore\Exception\NoSuchWebsiteException
      * @throws \TYPO3\CMS\Extbase\Object\Exception
      */
-    protected function execute(InputInterface $input, OutputInterface $output)
+    protected function execute(InputInterface $input, OutputInterface $output): int
     {
         $org_id = $input->getArgument('organization-id');
         $full = $input->getOption('full');

+ 2 - 2
ot_admin/Classes/Command/RegenConfigFilesCommand.php

@@ -37,7 +37,7 @@ class RegenConfigFilesCommand extends Command
      * Allows to add a description, a help text, and / or define arguments.
      *
      */
-    protected function configure()
+    protected function configure(): void
     {
         $this
             ->setName("ot:regen-config-files")
@@ -52,7 +52,7 @@ class RegenConfigFilesCommand extends Command
      * @return int
      * @throws \Exception
      */
-    protected function execute(InputInterface $input, OutputInterface $output)
+    protected function execute(InputInterface $input, OutputInterface $output): int
     {
         $io = new SymfonyStyle($input, $output);
         $this->siteController->regenConfigFilesAction();

+ 2 - 2
ot_admin/Classes/Command/RemoveRedirectionCommand.php

@@ -33,7 +33,7 @@ class RemoveRedirectionCommand extends Command
      * Allows to add a description, a help text, and / or define arguments.
      *
      */
-    protected function configure()
+    protected function configure(): void
     {
         $this
             ->setName("ot:redirection:remove")
@@ -59,7 +59,7 @@ class RemoveRedirectionCommand extends Command
      * @return int
      * @throws \Exception
      */
-    protected function execute(InputInterface $input, OutputInterface $output)
+    protected function execute(InputInterface $input, OutputInterface $output): int
     {
         $fromDomain = $input->getArgument('from-domain');
         $hard = $input->getOption('hard');

+ 2 - 2
ot_admin/Classes/Command/ResetBeUserPermsCommand.php

@@ -36,7 +36,7 @@ class ResetBeUserPermsCommand extends Command
      * Allows to add a description, a help text, and / or define arguments.
      *
      */
-    protected function configure()
+    protected function configure(): void
     {
         $this
             ->setName("ot:site:reset-perms")
@@ -70,7 +70,7 @@ class ResetBeUserPermsCommand extends Command
      * @return int
      * @throws \Exception
      */
-    protected function execute(InputInterface $input, OutputInterface $output)
+    protected function execute(InputInterface $input, OutputInterface $output): int
     {
         $org_id = $input->getArgument('organization-id');
         $all = $input->getOption('all');

+ 2 - 2
ot_admin/Classes/Command/ScanCommand.php

@@ -34,7 +34,7 @@ class ScanCommand extends Command
      * Allows to add a description, a help text, and / or define arguments.
      *
      */
-    protected function configure()
+    protected function configure(): void
     {
         $this
             ->setName("ot:site:scan")
@@ -61,7 +61,7 @@ class ScanCommand extends Command
      * @return int
      * @throws \Exception
      */
-    protected function execute(InputInterface $input, OutputInterface $output)
+    protected function execute(InputInterface $input, OutputInterface $output): int
     {
         $deep = $input->getOption('deep');
 

+ 2 - 2
ot_admin/Classes/Command/SetSiteDomainCommand.php

@@ -33,7 +33,7 @@ class SetSiteDomainCommand extends Command
      * Allows to add a description, a help text, and / or define arguments.
      *
      */
-    protected function configure()
+    protected function configure(): void
     {
         $this
             ->setName("ot:site:setdomain")
@@ -69,7 +69,7 @@ class SetSiteDomainCommand extends Command
      * @return int
      * @throws \Exception
      */
-    protected function execute(InputInterface $input, OutputInterface $output)
+    protected function execute(InputInterface $input, OutputInterface $output): int
     {
         $org_id = $input->getArgument('organization-id');
         $domain = $input->getArgument('domain');

+ 1 - 1
ot_admin/Classes/Command/UndeleteSiteCommand.php

@@ -33,7 +33,7 @@ class UndeleteSiteCommand extends Command
      * Allows to add a description, a help text, and / or define arguments.
      *
      */
-    protected function configure()
+    protected function configure(): void
     {
         $this
             ->setName("ot:site:undelete")

+ 2 - 2
ot_admin/Classes/Command/UpdateSiteCommand.php

@@ -37,7 +37,7 @@ class UpdateSiteCommand extends Command
      * Allows to add a description, a help text, and / or define arguments.
      *
      */
-    protected function configure()
+    protected function configure(): void
     {
         $this
             ->setName("ot:site:update")
@@ -79,7 +79,7 @@ class UpdateSiteCommand extends Command
      * @throws \TYPO3\CMS\Extbase\Object\Exception
      * @throws \Throwable
      */
-    protected function execute(InputInterface $input, OutputInterface $output)
+    protected function execute(InputInterface $input, OutputInterface $output): int
     {
         $org_id = $input->getArgument('organization-id');
         $all = $input->getOption('all');

+ 28 - 25
ot_admin/Classes/Controller/SiteController.php

@@ -573,19 +573,17 @@ class SiteController extends ActionController
             $queryBuilder->insert('sys_filemounts')
                 ->values([
                     'title' => 'Documents',
-                    'path' => rtrim($uploadRelPath, '/') . '/',
-                    'base' => 1
+                    'identifier' => '1:' . rtrim($uploadRelPath, '/') . '/'
                 ])
-                ->execute();
+                ->executeStatement();
 
             $queryBuilder = $this->connectionPool->getQueryBuilderForTable('sys_filemounts');
             $queryBuilder->insert('sys_filemounts')
                 ->values([
                     'title' => 'Forms_' . $organizationId,
-                    'path' => rtrim($formsRelPath, '/') . '/',
-                    'base' => 1
+                    'identifier' => '1:' . rtrim($formsRelPath, '/') . '/'
                 ])
-                ->execute();
+                ->executeStatement();
 
             // Create the BE Editors group
             // -- NB: this user will then be auto-updated by the ot_connect extension --
@@ -854,9 +852,9 @@ class SiteController extends ActionController
             $queryBuilder
                 ->select('uid')
                 ->from('sys_filemounts')
-                ->where("path LIKE '%user_upload/" . $organizationId . "/%'")
-                ->orWhere("path LIKE '%form_definitions/" . $organizationId . "/%'");
-            $statement = $queryBuilder->execute();
+                ->where("identifier LIKE '1:%user_upload/" . $organizationId . "/%'")
+                ->orWhere("identifier LIKE '1:%form_definitions/" . $organizationId . "/%'");
+            $statement = $queryBuilder->executeQuery();
             $rows = $statement->fetchAllAssociative();
             foreach ($rows as $row) {
                 $this->delete('sys_filemounts', 'uid', $row['uid'], $hard);
@@ -900,15 +898,15 @@ class SiteController extends ActionController
                             $subdir = $dir . $subdir;
                             if (!is_dir($subdir)) {
                                 throw new RuntimeException(
-                                    'The directory ' . $dir . ' contains non-directory files' .
-                                    ', this humble script prefers not to take care of them automatically. Abort.');
+                                    'The directory ' . $dir . ' contains directories' .
+                                    ', please delete them manually. Abort.');
                             }
                             if (is_readable($subdir)) {
                                 foreach (scandir($subdir) as $filename) {
                                     if ($filename !== '.' && $filename !== '..') {
                                         throw new RuntimeException(
                                             'The directory ' . $subdir . ' is not empty, ' .
-                                            'this humble script prefers not to take care of them automatically. Abort.');
+                                            'please delete it manually. Abort.');
                                     }
                                 }
                             }
@@ -1113,15 +1111,22 @@ class SiteController extends ActionController
                 ->update('sys_template')
                 ->set('deleted', 0)
                 ->where($queryBuilder->expr()->eq('pid', $rootUid))
-                ->execute();
+                ->executeStatement();
 
             // remove filemounts
             $queryBuilder = $this->connectionPool->getQueryBuilderForTable('sys_filemounts');
             $queryBuilder
                 ->update('sys_filemounts')
                 ->set('deleted', 0)
-                ->where($queryBuilder->expr()->eq('path', "'/user_upload/" . $organizationId . "/'"))
-                ->execute();
+                ->where($queryBuilder->expr()->eq('identifier', "'1:/user_upload/" . $organizationId . "/'"))
+                ->executeStatement();
+
+            $queryBuilder = $this->connectionPool->getQueryBuilderForTable('sys_filemounts');
+            $queryBuilder
+                ->update('sys_filemounts')
+                ->set('deleted', 0)
+                ->where($queryBuilder->expr()->eq('identifier', "'1:/form_definitions/" . $organizationId . "/'"))
+                ->executeStatement();
 
             try {
                 $editorsGroupUid = $this->findEditorsBeGroupUid($rootUid, false);
@@ -1131,7 +1136,7 @@ class SiteController extends ActionController
                         ->update('be_groups')
                         ->set('deleted', 0)
                         ->where($queryBuilder->expr()->eq('uid', $editorsGroupUid))
-                        ->execute();
+                        ->executeStatement();
                 }
             } catch (NoSuchRecordException $e) {}
 
@@ -1143,7 +1148,7 @@ class SiteController extends ActionController
                         ->update('be_users')
                         ->set('deleted', 0)
                         ->where($queryBuilder->expr()->eq('uid', $adminBeUserUid))
-                        ->execute();
+                        ->executeStatement();
                 }
             } catch (NoSuchRecordException $e) {}
 
@@ -1638,7 +1643,7 @@ class SiteController extends ActionController
         $rootUid = $this->otWebsiteRepository->findRootUidForOrganization($organizationId);
 
         $organizationExtraData = $this->fetchOrganizationExtraData($organizationId);
-        $isPremium = self::IS_PRODUCT_PREMIUM[$organizationExtraData['admin']['product'] ?? false];
+        $isPremium = self::IS_PRODUCT_PREMIUM[$organizationExtraData['admin']['product']] ?? false;
 
         if ($editorsGroupUid === null) {
             try {
@@ -2006,7 +2011,6 @@ class SiteController extends ActionController
             'pid' => $pid,
             'perms_groupid' => 3,
             'perms_user' => 27,
-            'cruser_id' => 1,
             'dokType' => self::DOK_PAGE,
             'title' => $title,
             'slug' => $slug,
@@ -2073,7 +2077,6 @@ class SiteController extends ActionController
     {
         $defaultValues = [
             'pid' => $pid,
-            'cruser_id' => 1,
             'CType' => $cType,
             'colPos' => $colPos,
             'bodyText' => $bodyText
@@ -2395,9 +2398,9 @@ class SiteController extends ActionController
         $queryBuilder
             ->select('uid')
             ->from('sys_filemounts')
-            ->where("path LIKE '%user_upload/" . $organizationId . "/'")
-            ->orWhere("path LIKE '%form_definitions/" . $organizationId . "/'");
-        $statement = $queryBuilder->execute();
+            ->where("identifier LIKE '1:%user_upload/" . $organizationId . "/'")
+            ->orWhere("identifier LIKE '1:%form_definitions/" . $organizationId . "/'");
+        $statement = $queryBuilder->executeQuery();
         $rows = $statement->fetchAllAssociative() ?: [];
         $fileMounts = [];
         foreach ($rows as $row) {
@@ -2427,14 +2430,14 @@ class SiteController extends ActionController
             foreach ($values as $k => $v) {
                 $q->set($k, $v);
             }
-            $q->execute();
+            $q->executeStatement();
 
             return $updateUid;
         }
 
         $queryBuilder->insert('be_groups')
             ->values($values)
-            ->execute();
+            ->executeStatement();
 
         return $queryBuilder->getConnection()->lastInsertId();
     }

+ 11 - 0
ot_admin/Configuration/Icons.php

@@ -0,0 +1,11 @@
+<?php
+declare(strict_types=1);
+
+use TYPO3\CMS\Core\Imaging\IconProvider\BitmapIconProvider;
+
+return [
+    'tx-ot_admin-logo' => [
+        'provider' => BitmapIconProvider::class,
+        'source' => 'EXT:ot_admin/Resources/Public/Icons/Extension.png',
+    ],
+];

BIN
ot_admin/Resources/Public/Icons/Extension.png


+ 8 - 1
ot_admin/ext_tables.sql

@@ -8,8 +8,15 @@ CREATE TABLE pages (
 );
 
 #
-# Table structure for table 'pages'
+# Table structure for table 'tt_content'
 #
 CREATE TABLE tt_content (
    manually_deleted smallint(5) unsigned NOT NULL DEFAULT 0
 );
+
+#
+# Table structure for table 'sys_filemounts'
+#
+CREATE TABLE sys_filemounts (
+   organization_id int unsigned
+);

+ 0 - 2
ot_connect/Classes/Middleware/RequestHandler.php

@@ -10,8 +10,6 @@ use TYPO3\CMS\Core\Http\RedirectResponse;
 /**
  * Hooks into the frontend request and process the request in order to
  * rewrite the uri and remove the 'logintype=logout' part after it was processed.
- *
- * @internal
  */
 class RequestHandler implements MiddlewareInterface
 {

+ 11 - 0
ot_connect/Configuration/Icons.php

@@ -0,0 +1,11 @@
+<?php
+declare(strict_types=1);
+
+use TYPO3\CMS\Core\Imaging\IconProvider\BitmapIconProvider;
+
+return [
+    'tx-ot_connect-logo' => [
+        'provider' => BitmapIconProvider::class,
+        'source' => 'EXT:ot_connect/Resources/Public/Icons/Extension.png',
+    ],
+];

BIN
ot_connect/Resources/Public/Icons/Extension.png


+ 0 - 7
ot_core/Classes/Controller/SelectedSiteController.php

@@ -2,16 +2,10 @@
 
 namespace Opentalent\OtCore\Controller;
 
-use Opentalent\OtAdmin\Controller\SiteController;
 use Opentalent\OtCore\Exception\NoSiteSelected;
 use Opentalent\OtCore\Exception\NoSuchWebsiteException;
-use Opentalent\OtCore\Website\OtPageRepository;
 use Psr\Http\Message\ResponseInterface;
-use TYPO3\CMS\Core\Database\ConnectionPool;
-use TYPO3\CMS\Extbase\Mvc\Controller\Arguments;
-use TYPO3\CMS\Extbase\Mvc\Exception\StopActionException;
 use TYPO3\CMS\Extbase\Mvc\RequestInterface;
-use TYPO3\CMS\Extbase\Object\ObjectManager;
 
 /**
  * Base class for all controllers of backend modules that need
@@ -45,7 +39,6 @@ class SelectedSiteController extends ActionController
 
     /**
      *
-     * @throws StopActionException
      * @throws NoSuchWebsiteException
      * @throws \Exception
      */

+ 27 - 15
ot_core/Classes/Website/OtWebsiteRepository.php

@@ -3,6 +3,7 @@
 namespace Opentalent\OtCore\Website;
 
 use Doctrine\DBAL\DBALException;
+use Doctrine\DBAL\Driver\Exception;
 use Opentalent\OtCore\Exception\InvalidWebsiteConfigurationException;
 use Opentalent\OtCore\Exception\NoSuchRecordException;
 use Opentalent\OtCore\Exception\NoSuchWebsiteException;
@@ -45,8 +46,8 @@ class OtWebsiteRepository
         if ($withRestrictions) {
             $q->where($q->expr()->eq('deleted', 0));
         }
-        return $q->execute()
-                 ->fetchAll();
+        return $q->executeQuery()
+                 ->fetchAllAssociative();
     }
 
     /**
@@ -64,7 +65,9 @@ class OtWebsiteRepository
         if ($withRestrictions) {
             $q->andWhere($q->expr()->eq('deleted', 0));
         }
-        $website = $q->execute()->fetch();
+        $website = $q
+            ->executeQuery()
+            ->fetchAssociative();
 
         if (!isset($website['uid'])) {
             throw new NoSuchWebsiteException('No website found with uid ' . $uid);
@@ -88,7 +91,9 @@ class OtWebsiteRepository
         if ($withRestrictions) {
             $q->andWhere($q->expr()->eq('deleted', 0));
         }
-        $website = $q->execute()->fetch();
+        $website = $q
+            ->executeQuery()
+            ->fetchAssociative();
 
         if (!isset($website['uid'])) {
             throw new NoSuchWebsiteException('No website found for organization ' . $organizationId);
@@ -115,7 +120,10 @@ class OtWebsiteRepository
         if ($withRestrictions) {
             $q->andWhere($q->expr()->eq('w.deleted', 0));
         }
-        $website = $q->execute()->fetch();
+        $website = $q
+            ->executeQuery()
+            ->fetchAssociative();
+
         if (!isset($website['uid'])) {
             throw new NoSuchWebsiteException('No website found for page ' . $pageUid);
         }
@@ -134,8 +142,8 @@ class OtWebsiteRepository
             ->select('*')
             ->from('ot_websites')
             ->where($queryBuilder->expr()->eq('config_identifier', $queryBuilder->expr()->literal($identifier)))
-            ->execute()
-            ->fetch();
+            ->executeQuery()
+            ->fetchAssociative();
         if (!isset($website['uid'])) {
             throw new NoSuchWebsiteException('No website found for identifier ' . $identifier);
         }
@@ -163,7 +171,7 @@ class OtWebsiteRepository
         if ($withRestrictions) {
             $q->andWhere($q->expr()->eq('deleted', 0));
         }
-        $rootUid = $q->execute()->fetchColumn(0);
+        $rootUid = $q->executeQuery()->fetchOne();
         if (!($rootUid > 0)) {
             throw new NoSuchRecordException('No root page found for website ' . $websiteUid);
         }
@@ -194,7 +202,10 @@ class OtWebsiteRepository
         if ($withRestrictions) {
             $q->andWhere($q->expr()->eq('w.deleted', 0));
         }
-        $rootUid = $q->execute()->fetchColumn(0);
+        $rootUid = $q
+            ->executeQuery()
+            ->fetchOne();
+
         if (!$rootUid) {
             throw new NoSuchWebsiteException("No website found for organization " . $organizationId);
         }
@@ -365,8 +376,7 @@ class OtWebsiteRepository
             $q = $q->andWhere($queryBuilder->expr()->eq('deleted', 0));
         }
 
-        $website = $q->execute()
-                    ->fetch();
+        $website = $q->executeQuery()->fetchAssociative();
 
         if (!isset($website['uid'])) {
             throw new NoSuchWebsiteException('No website found for this URI: ' . $uri);
@@ -376,11 +386,13 @@ class OtWebsiteRepository
     }
 
     /**
+     * @param array $otWebsite
      * @param UriInterface $uri
      * @param bool $devMode
-     * @param array|null $website
+     * @param bool $withRestrictions
      * @return int
-     * @throws NoSuchWebsiteException
+     * @throws Exception
+     * @throws \Doctrine\DBAL\Exception
      */
     public function matchUriToPage(array $otWebsite, UriInterface $uri, bool $devMode=false, bool $withRestrictions = true): int
     {
@@ -401,8 +413,8 @@ class OtWebsiteRepository
             ->from('pages')
             ->where($q->expr()->eq('ot_website_uid', $otWebsite['uid']))
             ->andWhere($q->expr()->eq('slug', $q->expr()->literal($tail)))
-            ->execute()
-            ->fetchColumn(0);
+            ->executeQuery()
+            ->fetchOne();
     }
 
     /**

+ 11 - 0
ot_core/Configuration/Icons.php

@@ -0,0 +1,11 @@
+<?php
+declare(strict_types=1);
+
+use TYPO3\CMS\Core\Imaging\IconProvider\BitmapIconProvider;
+
+return [
+    'tx-ot_core-logo' => [
+        'provider' => BitmapIconProvider::class,
+        'source' => 'EXT:ot_core/Resources/Public/Icons/Extension.png',
+    ],
+];

BIN
ot_core/Resources/Public/Icons/Extension.png


BIN
ot_core/Resources/Public/Icons/Extension_white.png


+ 4 - 8
ot_core/composer.json

@@ -10,17 +10,16 @@
         }
     ],
     "require": {
-        "typo3/cms-core": "^11.5 | ^12.4",
+        "typo3/cms-core": "^12.4",
         "fluidtypo3/flux": "^10.0",
         "fluidtypo3/vhs": "^7.0",
         "georgringer/news": "^11.4",
         "causal/image_autoresize": "^2.4",
         "guzzlehttp/guzzle": "^7.7",
         "twig/twig": "^3.3",
-        "phpunit/phpunit": "^9.5",
-        "waldhacker/hcaptcha": "^2.1",
-        "nimut/typo3-complete": "11.5",
-        "nimut/testing-framework": "^6.0",
+        "phpunit/phpunit": "^10.1.3",
+        "dreistromland/typo3-hcaptcha": "^2.2",
+        "typo3/testing-framework": "^8.2",
         "ext-json": "*",
         "co-stack/logs": "*"
     },
@@ -49,9 +48,6 @@
     "scripts": {
         "post-autoload-dump": [
             "@prepare-extension-test-structure"
-        ],
-        "prepare-extension-test-structure": [
-            "Nimut\\TestingFramework\\Composer\\ExtensionTestEnvironment::prepare"
         ]
     },
     "extra": {

+ 11 - 0
ot_optimizer/Configuration/Icons.php

@@ -0,0 +1,11 @@
+<?php
+declare(strict_types=1);
+
+use TYPO3\CMS\Core\Imaging\IconProvider\BitmapIconProvider;
+
+return [
+    'tx-ot_optimizer-logo' => [
+        'provider' => BitmapIconProvider::class,
+        'source' => 'EXT:ot_optimizer/Resources/Public/Icons/Extension.png',
+    ],
+];

+ 2 - 2
ot_optimizer/Configuration/RequestMiddlewares.php

@@ -8,10 +8,10 @@ return [
         'typo3/cms-frontend/page-resolver' => [
             'target' => Opentalent\OtOptimizer\Middleware\Frontend\OtPageResolver::class,
             'before' => [
-                'typo3/frontendediting/initiator'
+                'typo3/cms-frontend/preview-simulator'
             ],
             'after' => [
-                'typo3/cms-frontend/preview-simulator'
+                'typo3/cms-frontend/base-redirect-resolver'
             ],
         ],
     ],

BIN
ot_optimizer/Resources/Public/Icons/Extension.png


+ 3 - 5
ot_stats/Classes/Domain/Repository/MatomoWebsiteRepository.php

@@ -15,7 +15,7 @@ class MatomoWebsiteRepository
     /**
      * Database connection data
      */
-    const MATOMO_DB_HOST = 'stats';
+    const MATOMO_DB_HOST = 'stats.2iopenservice.com';
     const MATOMO_DB_NAME = 'matomo';
     const MATOMO_DB_USER = 'matomo';
     const MATOMO_DB_PWD = ']:*j8GYU/n9mp+';
@@ -129,8 +129,7 @@ class MatomoWebsiteRepository
      */
     public function findByWebsiteUid(int $websiteUid): ?MatomoWebsite
     {
-        $otWebsiteRepository = GeneralUtility::makeInstance(ObjectManager::class)->get(OtWebsiteRepository::class);
-        $website = $otWebsiteRepository->getWebsiteByUid($websiteUid);
+        $website = $this->otWebsiteRepository->getWebsiteByUid($websiteUid);
 
         if ($website['matomo_site_id'] == null) {
             return null;
@@ -147,8 +146,7 @@ class MatomoWebsiteRepository
      */
     public function findByRootUid(int $rootUid): ?MatomoWebsite
     {
-        $otWebsiteRepository = GeneralUtility::makeInstance(ObjectManager::class)->get(OtWebsiteRepository::class);
-        $website = $otWebsiteRepository->getWebsiteByPageUid($rootUid);
+        $website = $this->otWebsiteRepository->getWebsiteByPageUid($rootUid);
 
         if ($website['matomo_site_id'] == null) {
             return null;

+ 27 - 0
ot_stats/Configuration/Backend/Modules.php

@@ -0,0 +1,27 @@
+<?php
+
+use Opentalent\OtStats\Controller\OtStatsController;
+
+/**
+ * Registers the statistics backend module
+ */
+return [
+    'web_OtStatsOtStats' => [
+        'parent' => 'web',
+        'position' => ['after' => 'web_OtTemplatingOtcustomizer'],
+        'access' => 'user',
+        'iconIdentifier' => 'tx-ot_stats-pie-chart',
+        'path' => '/module/web/OtStats',
+        'labels' => 'LLL:EXT:ot_stats/Resources/Private/Language/locallang_mod.xlf',
+        'extensionName' => 'OtStats',
+        'controllerActions' => [
+            OtStatsController::class => [
+                'index',
+                'askForActivationConfirmation',
+                'askForDeactivationConfirmation',
+                'enableStats',
+                'disableStats',
+            ],
+        ],
+    ],
+];

+ 15 - 0
ot_stats/Configuration/Icons.php

@@ -0,0 +1,15 @@
+<?php
+declare(strict_types=1);
+
+use TYPO3\CMS\Core\Imaging\IconProvider\BitmapIconProvider;
+
+return [
+    'tx-ot_stats-logo' => [
+        'provider' => BitmapIconProvider::class,
+        'source' => 'EXT:ot_stats/Resources/Public/Icons/Extension.png',
+    ],
+    'tx-ot_stats-pie-chart' => [
+        'provider' => BitmapIconProvider::class,
+        'source' => 'EXT:ot_stats/Resources/Public/Icons/pie-chart.png',
+    ],
+];

+ 7 - 6
ot_stats/Resources/Private/Layouts/Backend/Default.html

@@ -1,8 +1,9 @@
 {namespace v=FluidTYPO3\Vhs\ViewHelpers}
 
-<f:be.container includeCssFiles="{ot_core: '{f:uri.resource(path:\'assets/Backend/style/ot_be_module.css\', extensionName:\'ot_core\')}',
-                                  ot_stats: '{f:uri.resource(path:\'assets/Backend/style/ot_stats.css\')}'}"
-                includeJsFiles="{iframeresizer: '{f:uri.resource(path:\'assets/Backend/script/iframeResizer.min.js\')}'}">
-    <f:flashMessages />
-    <f:render section="content" />
-</f:be.container>
+<link rel="stylesheet" type="text/css" href="{f:uri.resource(path: 'EXT:backend/Resources/Public/Css/backend.css')}" />
+<link rel="stylesheet" type="text/css" href="{f:uri.resource(path: 'EXT:ot_core/Resources/Public/assets/Backend/style/ot_be_module.css')}" />
+<link rel="stylesheet" type="text/css" href="{f:uri.resource(path: 'EXT:ot_stats/Resources/Public/assets/Backend/style/ot_stats.css')}" />
+
+<f:flashMessages />
+
+<f:render section="content" />

BIN
ot_stats/Resources/Public/Icons/Extension.png


BIN
ot_stats/Resources/Public/Icons/Extension_white.png


+ 0 - 32
ot_stats/ext_tables.php

@@ -1,32 +0,0 @@
-<?php
-
-use Opentalent\OtStats\Controller\OtStatsController;
-use Psr\Http\Message\ServerRequestInterface;
-use TYPO3\CMS\Core\Http\ApplicationType;
-
-defined('TYPO3') || die('Access denied.');
-
-// ext_tables.php contient les directives permettant de configurer le backend
-
-call_user_func(
-    function()
-    {
-        /**
-         * Registers the statistics backend module
-         */
-        \TYPO3\CMS\Extbase\Utility\ExtensionUtility::registerModule(
-            'OtStats',
-            'web', // Make module a submodule of 'web'
-            'otstats', // Submodule key
-            'after:OtTemplatingOtcustomizer', // Position
-            array(
-                OtStatsController::class => 'index,askForActivationConfirmation,askForDeactivationConfirmation,enableStats,disableStats'
-            ),
-            array(
-                'access' => 'user,group',
-                'icon' => 'EXT:ot_stats/Resources/Public/Icons/pie-chart.png',
-                'labels' => 'LLL:EXT:ot_stats/Resources/Private/Language/locallang_mod.xlf',
-            )
-        );
-    }
-);

+ 2 - 2
ot_templating/Classes/Controller/OtCustomizerController.php

@@ -51,7 +51,7 @@ class OtCustomizerController extends SelectedSiteController {
         $queryBuilder->update('ot_websites')
             ->where($queryBuilder->expr()->eq('uid', $this->currentWebsite['uid']))
             ->set('template', $templateKey)
-            ->execute();
+            ->executeStatement();
 
         // Clear the site's cache
         $this->otCacheManager->clearSiteCache($this->currentRootUid);
@@ -89,7 +89,7 @@ class OtCustomizerController extends SelectedSiteController {
         $queryBuilder->update('ot_websites')
             ->where($queryBuilder->expr()->eq('uid', $this->currentWebsite['uid']))
             ->set('template_preferences', json_encode($prefs))
-            ->execute();
+            ->executeStatement();
 
         // Clear the site's cache
         $this->otCacheManager->clearSiteCache($this->currentRootUid);

+ 135 - 29
ot_templating/Classes/ViewHelpers/CObjectViewHelper.php

@@ -1,43 +1,70 @@
 <?php
+declare(strict_types=1);
 
 namespace Opentalent\OtTemplating\ViewHelpers;
 
+use Psr\Http\Message\ServerRequestInterface;
+use TYPO3\CMS\Core\Context\Context;
+use TYPO3\CMS\Core\Routing\PageArguments;
+use TYPO3\CMS\Core\Site\Entity\SiteInterface;
+use TYPO3\CMS\Core\Site\SiteFinder;
+use TYPO3\CMS\Core\TimeTracker\TimeTracker;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 use TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface;
 use TYPO3\CMS\Extbase\Reflection\ObjectAccess;
+use TYPO3\CMS\Fluid\Core\Rendering\RenderingContext;
+use TYPO3\CMS\Frontend\Authentication\FrontendUserAuthentication;
+use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
 use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
 use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
+use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
 use TYPO3Fluid\Fluid\Core\ViewHelper\Exception;
+use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithContentArgumentAndRenderStatic;
 
 /**
- *   This view helper is a wrapper for the Fluid/CObjectViewhelper class
- *   that allow to override the TS setup of the object by given variables
+ * This view helper is a modified version of the TYPO3\CMS\Fluid\ViewHelpers\CObjectViewHelper class
+ * that allow to override the TS setup of the object by given variables
  *
- *   It accepts the same arguments as \TYPO3\CMS\Fluid\ViewHelpers\CObjectViewHelper,
- *   and an additional argument 'settings'
+ * It accepts the same arguments as \TYPO3\CMS\Fluid\ViewHelpers\CObjectViewHelper,
+ * and an additional argument 'settings'
  *
- *   @see https://docs.typo3.org/p/georgringer/news/master/en-us/Introduction/Index.html
+ * @see https://docs.typo3.org/p/georgringer/news/master/en-us/Introduction/Index.html
  *
- *   example:
+ * example:
  *
- *     {namespace ot=Opentalent\OtTemplating\ViewHelpers}
- *
- *     <ot:cObject typoscriptObjectPath="lib.tx_ottemplating.widgets.news_list"
- *                 settings="{'settings.defaultPageUid': 1}" />
+ *   {namespace ot=Opentalent\OtTemplating\ViewHelpers}
  *
+ *   <ot:cObject typoscriptObjectPath="lib.tx_ottemplating.widgets.news_list"
+ *               settings="{'settings.defaultPageUid': 1}" />
  *
  * @package Opentalent\OtTemplating\ViewHelpers
  */
-class CObjectViewHelper extends \TYPO3\CMS\Fluid\ViewHelpers\CObjectViewHelper {
+final class CObjectViewHelper extends AbstractViewHelper
+{
+    use CompileWithContentArgumentAndRenderStatic;
 
     /**
-     * Initialize arguments.
+     * Disable escaping of child nodes' output
      *
-     * @throws Exception
+     * @var bool
+     */
+    protected $escapeChildren = false;
+
+    /**
+     * Disable escaping of this node's output
+     *
+     * @var bool
      */
-    public function initializeArguments()
+    protected $escapeOutput = false;
+
+    public function initializeArguments(): void
     {
-        parent::initializeArguments();
+        $this->registerArgument('data', 'mixed', 'the data to be used for rendering the cObject. Can be an object, array or string. If this argument is not set, child nodes will be used');
+        $this->registerArgument('typoscriptObjectPath', 'string', 'the TypoScript setup path of the TypoScript object to render', true);
+        $this->registerArgument('currentValueKey', 'string', 'currentValueKey');
+        $this->registerArgument('table', 'string', 'the table name associated with "data" argument. Typically tt_content or one of your custom tables. This argument should be set if rendering a FILES cObject where file references are used, or if the data argument is a database record.', false, '');
+
+        // <-- Additional paramter
         $this->registerArgument(
             'settings',
             'array',
@@ -48,26 +75,23 @@ class CObjectViewHelper extends \TYPO3\CMS\Fluid\ViewHelpers\CObjectViewHelper {
     }
 
     /**
-     * <!> This is a copy/paste of the parent method, slighly modified
-     * to override the $setup variable with the 'settings' argument
-     *
      * Renders the TypoScript object in the given TypoScript setup path.
      *
-     * @param array $arguments
-     * @param \Closure $renderChildrenClosure
-     * @param RenderingContextInterface $renderingContext
-     * @return mixed
      * @throws Exception
      */
-    public static function renderStatic(array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext)
+    public static function renderStatic(array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext): string
     {
         $data = $renderChildrenClosure();
-        $typoscriptObjectPath = $arguments['typoscriptObjectPath'];
+        $typoscriptObjectPath = (string)$arguments['typoscriptObjectPath'];
         $currentValueKey = $arguments['currentValueKey'];
         $table = $arguments['table'];
-        $contentObjectRenderer = static::getContentObjectRenderer($renderingContext->getRequest());
+        /** @var RenderingContext $renderingContext */
+        $request = $renderingContext->getRequest();
+        $contentObjectRenderer = self::getContentObjectRenderer($request);
+        $contentObjectRenderer->setRequest($request);
+        $tsfeBackup = null;
         if (!isset($GLOBALS['TSFE']) || !($GLOBALS['TSFE'] instanceof TypoScriptFrontendController)) {
-            static::simulateFrontendEnvironment();
+            $tsfeBackup = self::simulateFrontendEnvironment();
         }
         $currentValue = null;
         if (is_object($data)) {
@@ -83,8 +107,8 @@ class CObjectViewHelper extends \TYPO3\CMS\Fluid\ViewHelpers\CObjectViewHelper {
             $contentObjectRenderer->setCurrentVal($data[$currentValueKey]);
         }
         $pathSegments = GeneralUtility::trimExplode('.', $typoscriptObjectPath);
-        $lastSegment = array_pop($pathSegments);
-        $setup = static::getConfigurationManager()->getConfiguration(ConfigurationManagerInterface::CONFIGURATION_TYPE_FULL_TYPOSCRIPT);
+        $lastSegment = (string)array_pop($pathSegments);
+        $setup = self::getConfigurationManager()->getConfiguration(ConfigurationManagerInterface::CONFIGURATION_TYPE_FULL_TYPOSCRIPT);
         foreach ($pathSegments as $segment) {
             if (!array_key_exists($segment . '.', $setup)) {
                 throw new Exception(
@@ -107,12 +131,93 @@ class CObjectViewHelper extends \TYPO3\CMS\Fluid\ViewHelpers\CObjectViewHelper {
 
         $content = self::renderContentObject($contentObjectRenderer, $setup, $typoscriptObjectPath, $lastSegment);
         if (!isset($GLOBALS['TSFE']) || !($GLOBALS['TSFE'] instanceof TypoScriptFrontendController)) {
-            static::resetFrontendEnvironment();
+            self::resetFrontendEnvironment($tsfeBackup);
+        }
+        return $content;
+    }
+
+    /**
+     * Renders single content object and increases time tracker stack pointer
+     */
+    protected static function renderContentObject(ContentObjectRenderer $contentObjectRenderer, array $setup, string $typoscriptObjectPath, string $lastSegment): string
+    {
+        $timeTracker = GeneralUtility::makeInstance(TimeTracker::class);
+        if ($timeTracker->LR) {
+            $timeTracker->push('/f:cObject/', '<' . $typoscriptObjectPath);
+        }
+        $timeTracker->incStackPointer();
+        $content = $contentObjectRenderer->cObjGetSingle($setup[$lastSegment], $setup[$lastSegment . '.'] ?? [], $typoscriptObjectPath);
+        $timeTracker->decStackPointer();
+        if ($timeTracker->LR) {
+            $timeTracker->pull($content);
         }
         return $content;
     }
 
+    protected static function getConfigurationManager(): ConfigurationManagerInterface
+    {
+        // @todo: this should be replaced by DI once Fluid can handle DI properly
+        return GeneralUtility::getContainer()->get(ConfigurationManagerInterface::class);
+    }
+
+    protected static function getContentObjectRenderer(ServerRequestInterface $request): ContentObjectRenderer
+    {
+        if (($GLOBALS['TSFE'] ?? null) instanceof TypoScriptFrontendController) {
+            $tsfe = $GLOBALS['TSFE'];
+        } else {
+            $site = $request->getAttribute('site');
+            if (!($site instanceof SiteInterface)) {
+                $sites = GeneralUtility::makeInstance(SiteFinder::class)->getAllSites();
+                $site = reset($sites);
+            }
+            $language = $request->getAttribute('language') ?? $site->getDefaultLanguage();
+            $pageArguments = $request->getAttribute('routing') ?? new PageArguments(0, '0', []);
+            $tsfe = GeneralUtility::makeInstance(
+                TypoScriptFrontendController::class,
+                GeneralUtility::makeInstance(Context::class),
+                $site,
+                $language,
+                $pageArguments,
+                GeneralUtility::makeInstance(FrontendUserAuthentication::class)
+            );
+        }
+        $contentObjectRenderer = GeneralUtility::makeInstance(ContentObjectRenderer::class, $tsfe);
+        $parent = $request->getAttribute('currentContentObject');
+        if ($parent instanceof ContentObjectRenderer) {
+            $contentObjectRenderer->setParent($parent->data, $parent->currentRecord);
+        }
+        return $contentObjectRenderer;
+    }
+
+    /**
+     * \TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer->cObjGetSingle() relies on $GLOBALS['TSFE']
+     */
+    protected static function simulateFrontendEnvironment(): ?TypoScriptFrontendController
+    {
+        $tsfeBackup = $GLOBALS['TSFE'] ?? null;
+        $GLOBALS['TSFE'] = new \stdClass();
+        $GLOBALS['TSFE']->cObj = GeneralUtility::makeInstance(ContentObjectRenderer::class);
+        return $tsfeBackup;
+    }
+
+    /**
+     * Resets $GLOBALS['TSFE'] if it was previously changed by simulateFrontendEnvironment()
+     */
+    protected static function resetFrontendEnvironment(?TypoScriptFrontendController $tsfeBackup): void
+    {
+        $GLOBALS['TSFE'] = $tsfeBackup;
+    }
+
+    /**
+     * Explicitly set argument name to be used as content.
+     */
+    public function resolveContentArgumentName(): string
+    {
+        return 'data';
+    }
+
     /**
+     * -- Additional method --
      * Recursively replace any {$var} value in the given configuration
      * if 'var' is a key in the 'overrideSetup' array
      *
@@ -143,6 +248,7 @@ class CObjectViewHelper extends \TYPO3\CMS\Fluid\ViewHelpers\CObjectViewHelper {
     }
 
     /**
+     * -- Additional method --
      * Similar to array_merge_recursive, except that the end nodes of the
      * base array is replaced and not merged.
      *

+ 0 - 49
ot_templating/Classes/ViewHelpers/ImagePViewHelper.php

@@ -1,49 +0,0 @@
-<?php
-
-namespace Opentalent\OtTemplating\ViewHelpers;
-
-use TYPO3\CMS\Core\Resource\Exception\ResourceDoesNotExistException;
-use TYPO3\CMS\Fluid\ViewHelpers\ImageViewHelper;
-
-/**
- * -- Wrapper for the TYPO3\CMS\Fluid\ViewHelpers\ImageViewHelper --
- * Display the image like the original viewhelper does, but does not
- * throw an error if the file is not an image or if the
- * image can not be displayed.
- *
- *     {namespace ot=Opentalent\OtTemplating\ViewHelpers}
- *
- *     {ot:imageP()}
- *
- * @package Opentalent\OtTemplating\ViewHelpers
- */
-class ImagePViewHelper extends ImageViewHelper
-{
-    /**
-     * -- This method is expected by Fluid --
-     * Declares the viewhelper's parameters
-     */
-    public function initializeArguments()
-    {
-        parent::initializeArguments();
-    }
-
-    /**
-     * -- This method is expected by Fluid --
-     * Renders the content as html
-     *
-     * @return string Rendered tag
-     */
-    public function render() {
-        try {
-            return parent::render();
-        } catch (ResourceDoesNotExistException |
-                \UnexpectedValueException |
-                \RuntimeException |
-                \InvalidArgumentException
-                $e) {
-            return "";
-        }
-    }
-
-}

+ 25 - 0
ot_templating/Configuration/Backend/Modules.php

@@ -0,0 +1,25 @@
+<?php
+
+use Opentalent\OtTemplating\Controller\OtCustomizerController;
+
+/**
+ * Registers the customizer backend module
+ */
+return [
+    'web_OtTemplatingOtcustomizer' => [
+        'parent' => 'web',
+        'position' => ['before' => 'web_OtStatsOtStats'],
+        'access' => 'user',
+        'iconIdentifier' => 'tx-ot_templating-logo',
+        'path' => '/module/web/Otcustomizer',
+        'labels' => 'LLL:EXT:ot_templating/Resources/Private/Language/locallang_mod.xlf',
+        'extensionName' => 'OtCustomizer',
+        'controllerActions' => [
+            OtCustomizerController::class => [
+                'index',
+                'selectTemplate',
+                'updatePreferences',
+            ],
+        ],
+    ],
+];

+ 6 - 1
ot_templating/Configuration/Icons.php

@@ -1,8 +1,13 @@
 <?php
-
 declare(strict_types=1);
 
+use TYPO3\CMS\Core\Imaging\IconProvider\BitmapIconProvider;
+
 return [
+    'tx-ot_templating-logo' => [
+        'provider' => BitmapIconProvider::class,
+        'source' => 'EXT:ot_templating/Resources/Public/Icons/Extension.png',
+    ],
     'opentalent-icon-144' => [
         'source' => 'EXT:ot_templating/Resources/Public/Icons/opentalent-144x144.png',
     ],

+ 3 - 0
ot_templating/Configuration/Services.yaml

@@ -8,3 +8,6 @@ services:
     resource: '../Classes/*'
 
   TYPO3\CMS\Form\Mvc\Configuration\ConfigurationManager: '@Opentalent\OtTemplating\XClass\Form\Configuration\ConfigurationManager'
+
+  GeorgRinger\News\Controller\NewsController:
+    public: true

+ 1 - 1
ot_templating/Configuration/TypoScript/constants.txt → ot_templating/Configuration/TypoScript/constants.typoscript

@@ -13,7 +13,7 @@ plugin.tx_ottemplating {
     view {
         templateRootPaths.0 = EXT:ot_templating/Resources/Private/Templates/
         partialRootPaths.0 = EXT:ot_templating/Resources/Private/Partials/
-        layoutRootPaths.0 = EXT:ot_templating/Resources/Public/Layouts/
+        layoutRootPaths.0 = EXT:ot_templating/Resources/Private/Layouts/
     }
     settings {
         opentalent {

+ 8 - 0
ot_templating/Configuration/TypoScript/setup.txt → ot_templating/Configuration/TypoScript/setup.typoscript

@@ -108,6 +108,14 @@ plugin.tx_ottemplating {
     }
 }
 
+module.tx_otcustomizer {
+    view {
+        templateRootPaths.0 = {$templateRootPath}
+        partialRootPaths.0 = {$partialRootPath}
+        layoutRootPaths.0 = {$layoutRootPath}
+    }
+}
+
 # * Form extension
 # @see https://docs.typo3.org/c/typo3/cms-form/9.5/en-us/Introduction/Index.html
 

+ 4 - 5
ot_templating/Resources/Private/Layouts/Backend/Default.html

@@ -1,8 +1,7 @@
 {namespace v=FluidTYPO3\Vhs\ViewHelpers}
 
-<f:be.container includeCssFiles="{ot_core: '{f:uri.resource(path:\'assets/Backend/style/ot_be_module.css\', extensionName:\'ot_core\')}',
-                                  ot_customizer: '{f:uri.resource(path:\'assets/Backend/style/ot_customizer.css\')}'}">
-    <f:render section="content" />
-</f:be.container>
-
+<link rel="stylesheet" type="text/css" href="{f:uri.resource(path: 'EXT:backend/Resources/Public/Css/backend.css')}" />
+<link rel="stylesheet" type="text/css" href="{f:uri.resource(path: 'EXT:ot_core/Resources/Public/assets/Backend/style/ot_be_module.css')}" />
+<link rel="stylesheet" type="text/css" href="{f:uri.resource(path: 'EXT:ot_templating/Resources/Public/assets/Backend/style/ot_customizer.css')}" />
 
+<f:render section="content" />

+ 1 - 1
ot_templating/Resources/Private/Partials/Classic/Carousel.html

@@ -13,7 +13,7 @@
         <div class="carousel">
             <f:for each="{images}" as="image">
                 <div>
-                    <ot:imageP src="{image.url}"
+                    <f:image src="{image.url}"
                              alt="{image.alternative}"
                              title="{image.title}"
                              class="carousel-img"

+ 2 - 1
ot_templating/Resources/Private/Partials/Classic/UserToolbar.html

@@ -79,7 +79,8 @@
                 </f:then>
                 <f:else>
                     <a href="{ot:request.getOtEnvVar(argument: 'LOGIN_PAGE_URL')}" target="_blank">
-                        <i class="fas fa-power-off"></i> <f:translate key="login"/>
+                        <i class="fas fa-power-off"></i>
+                        <f:translate key="LLL:EXT:ot_templating/Resources/Private/Language/locallang.xlf:login"/>
                     </a>
                 </f:else>
             </f:if>

+ 1 - 1
ot_templating/Resources/Private/Partials/Modern/Carousel.html

@@ -58,7 +58,7 @@
                                 data-param10=""
                                 data-description="">
 
-                                <ot:imageP src="{image.url}"
+                                <f:image src="{image.url}"
                                      alt="{image.alternative}"
                                      class="rev-slidebg"
                                      height="500c"

+ 16 - 16
ot_templating/Resources/Private/Templates/OtCustomizer/Index.html

@@ -6,7 +6,7 @@
 <f:section name="content">
     <div class="ot-be-module ot-customizer">
         <div class="templates">
-            <h3><f:translate key="available_themes"/></h3>
+            <h3><f:translate key="LLL:EXT:ot_templating/Resources/Private/Language/locallang.xlf:available_themes"/></h3>
             <div class="templates-gallery">
                 <f:for each="{templates}" as="template" key="template_key">
 
@@ -24,7 +24,7 @@
 
                             <f:if condition="{template_key}=={currentTemplate}">
                                 <f:then>
-                                    <div class="active"><f:translate key="active_theme"/></div>
+                                    <div class="active"><f:translate key="LLL:EXT:ot_templating/Resources/Private/Language/locallang.xlf:active_theme"/></div>
                                 </f:then>
                                 <f:else>
                                     <f:link.action
@@ -33,7 +33,7 @@
                                             title="select"
                                             class="ot-btn"
                                     >
-                                        <f:translate key="use_this_theme"/>
+                                        <f:translate key="LLL:EXT:ot_templating/Resources/Private/Language/locallang.xlf:use_this_theme"/>
                                     </f:link.action>
                                 </f:else>
                             </f:if>
@@ -46,41 +46,41 @@
 
         <div class="customizer">
 
-            <h3><f:translate key="other_customization_settings"/></h3>
+            <h3><f:translate key="LLL:EXT:ot_templating/Resources/Private/Language/locallang.xlf:other_customization_settings"/></h3>
 
             <f:form action="updatePreferences">
                 <div class="form-group">
                     <label>Couleur du thème</label>
                     <f:form.select class="form-control"
                                    name="themeColor"
-                                   options="{light-blue: '{f:translate(key: \'light_blue\')}',
-                                               blue: '{f:translate(key: \'blue\')}',
-                                               green: '{f:translate(key: \'green\')}',
-                                               orange: '{f:translate(key: \'orange\')}',
-                                               grey: '{f:translate(key: \'grey\')}',
-                                               red: '{f:translate(key: \'red\')}',
-                                               light-red: '{f:translate(key: \'light_red\')}',
-                                               purple: '{f:translate(key: \'purple\')}'}"
+                                   options="{light-blue: '{f:translate(key: \'LLL:EXT:ot_templating/Resources/Private/Language/locallang.xlf:light_blue\')}',
+                                               blue: '{f:translate(key: \'LLL:EXT:ot_templating/Resources/Private/Language/locallang.xlf:blue\')}',
+                                               green: '{f:translate(key: \'LLL:EXT:ot_templating/Resources/Private/Language/locallang.xlf:green\')}',
+                                               orange: '{f:translate(key: \'LLL:EXT:ot_templating/Resources/Private/Language/locallang.xlf:orange\')}',
+                                               grey: '{f:translate(key: \'LLL:EXT:ot_templating/Resources/Private/Language/locallang.xlf:grey\')}',
+                                               red: '{f:translate(key: \'LLL:EXT:ot_templating/Resources/Private/Language/locallang.xlf:red\')}',
+                                               light-red: '{f:translate(key: \'LLL:EXT:ot_templating/Resources/Private/Language/locallang.xlf:light_red\')}',
+                                               purple: '{f:translate(key: \'LLL:EXT:ot_templating/Resources/Private/Language/locallang.xlf:purple\')}'}"
                                    value="{preferences.themeColor}">
                     </f:form.select>
                 </div>
 
                 <div class="form-group">
-                    <label><f:translate key="display_carousel"/></label>
+                    <label><f:translate key="LLL:EXT:ot_templating/Resources/Private/Language/locallang.xlf:display_carousel"/></label>
                     <f:form.checkbox name="displayCarousel"
                                      value="1"
                                      checked="{preferences.displayCarousel}"
                     />
                 </div>
                 <div class="form-group">
-                    <label><f:translate key="display_breadcrumb"/></label>
+                    <label><f:translate key="LLL:EXT:ot_templating/Resources/Private/Language/locallang.xlf:display_breadcrumb"/></label>
                     <f:form.checkbox name="displayBreadcrumb"
                                      value="2"
                                      checked="{preferences.displayBreadcrumb}"
                     />
                 </div>
                 <div class="form-group">
-                    <label><f:translate key="static_donors"/></label>
+                    <label><f:translate key="LLL:EXT:ot_templating/Resources/Private/Language/locallang.xlf:static_donors"/></label>
                     <f:form.checkbox name="staticDonors"
                                      value="3"
                                      checked="{preferences.staticDonors}"
@@ -89,7 +89,7 @@
 
                 <div class="actions">
                     <f:form.button type="submit" class="ot-btn">
-                        <f:translate key="apply"/>
+                        <f:translate key="LLL:EXT:ot_templating/Resources/Private/Language/locallang.xlf:apply"/>
                     </f:form.button>
                 </div>
 

+ 9 - 3
ot_templating/Resources/Private/Templates/Page/Error/403.html

@@ -8,7 +8,13 @@
 </f:section>
 
 <f:section name="Message">
-    <p><f:translate key="youre_not_allowed_to_view_this_page"/></p>
-    <p><f:translate key="did_you_login"/></p>
-    <a href="{homeUri}"><f:translate key="back_to_homepage"/></a>
+    <p>
+        <f:translate key="LLL:EXT:ot_templating/Resources/Private/Language/locallang.xlf:youre_not_allowed_to_view_this_page"/>
+    </p>
+    <p>
+        <f:translate key="LLL:EXT:ot_templating/Resources/Private/Language/locallang.xlf:did_you_login"/>
+    </p>
+    <a href="{homeUri}">
+        <f:translate key="LLL:EXT:ot_templating/Resources/Private/Language/locallang.xlf:back_to_homepage"/>
+    </a>
 </f:section>

+ 6 - 4
ot_templating/Resources/Private/Templates/Page/Error/404.html

@@ -3,14 +3,16 @@
 <f:layout name="ErrorPage" />
 
 <f:section name="Configuration">
-    <flux:form id="err404"  label="LLL:template_err404" extensionName="Opentalent.OtTemplating">
+    <flux:form id="err404" label="LLL:template_err404" extensionName="Opentalent.OtTemplating">
     </flux:form>
 </f:section>
 
 <f:section name="Message">
-    <p><f:translate key="page_not_available"/></p>
+    <p><f:translate key="LLL:EXT:ot_templating/Resources/Private/Language/locallang.xlf:page_not_available"/></p>
     <p>
-        <f:translate key="control_url_or"/>
-        <a href="{homeUri}"><f:translate key="click_here_to_go_back_home"/>.</a>
+        <f:translate key="LLL:EXT:ot_templating/Resources/Private/Language/locallang.xlf:control_url_or"/>
+        <a href="{homeUri}">
+            <f:translate key="LLL:EXT:ot_templating/Resources/Private/Language/locallang.xlf:click_here_to_go_back_home"/>.
+        </a>
     </p>
 </f:section>

+ 3 - 1
ot_templating/Resources/Private/Templates/Page/Error/500.html

@@ -8,5 +8,7 @@
 </f:section>
 
 <f:section name="Message">
-    <p><f:translate key="an_error_occured"/></p>
+    <p>
+        <f:translate key="LLL:EXT:ot_templating/Resources/Private/Language/locallang.xlf:an_error_occured"/>
+    </p>
 </f:section>

+ 2 - 1
ot_templating/Resources/Public/assets/Backend/style/ot_customizer.css

@@ -13,6 +13,7 @@
     min-height: 100vh;
     box-shadow: 2px 3px 3px #bfbfbf;
     z-index: 1;
+    padding-top: 30px;
 }
 
 .ot-customizer .templates-gallery {
@@ -70,9 +71,9 @@
 
 .ot-customizer .customizer {
     flex: 1;
-    padding: 0 3%;
     min-height: 100vh;
     background-color: #e6e6e6;
+    padding: 30px 3%;
 }
 
 .ot-customizer .customizer form {

+ 2 - 2
ot_templating/ext_localconf.php

@@ -22,6 +22,6 @@ $GLOBALS['TYPO3_CONF_VARS']['SYS']['Objects'][TYPO3\CMS\Form\Mvc\Configuration\C
 $GLOBALS['TYPO3_CONF_VARS']['SYS']['Objects'][TYPO3\CMS\Form\Domain\Finishers\EmailFinisher::class] = [
     'className' => Opentalent\OtTemplating\XClass\Form\Finishers\EmailFinisher::class
 ];
-$GLOBALS['TYPO3_CONF_VARS']['SYS']['Objects'][GeorgRinger\News\Controller\NewsController::class] = [
-    'className' => Opentalent\OtTemplating\XClass\News\NewsController::class
+$GLOBALS['TYPO3_CONF_VARS']['SYS']['Objects'][\GeorgRinger\News\Controller\NewsController::class] = [
+    'className' => \Opentalent\OtTemplating\XClass\News\NewsController::class
 ];

+ 0 - 38
ot_templating/ext_tables.php

@@ -1,38 +0,0 @@
-<?php
-
-use Opentalent\OtTemplating\Controller\OtCustomizerController;
-use Psr\Http\Message\ServerRequestInterface;
-use TYPO3\CMS\Core\Http\ApplicationType;
-
-defined('TYPO3') || die('Access denied.');
-
-// ext_tables.php contient les directives permettant de configurer le backend
-
-call_user_func(
-    function()
-    {
-        // Include the configuration files
-        \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addStaticFile(
-            'ot_templating',
-            'Configuration/TypoScript',
-            'Templating');
-
-        /**
-         * Registers the customizer backend module
-         */
-        \TYPO3\CMS\Extbase\Utility\ExtensionUtility::registerModule(
-            'OtTemplating',
-            'web', // Make module a submodule of 'web'
-            'otcustomizer', // Submodule key
-            'before:OtStatsOtstats', // Position
-            array(
-                OtCustomizerController::class => 'index,selectTemplate,updatePreferences',
-            ),
-            array(
-                'access' => 'user,group',
-                'icon' => 'EXT:ot_templating/Resources/Public/Icons/Extension.png',
-                'labels' => 'LLL:EXT:ot_templating/Resources/Private/Language/locallang_mod.xlf',
-            )
-        );
-    }
-);