Prechádzať zdrojové kódy

Merge branch 'release/0.7'

Olivier Massot 2 rokov pred
rodič
commit
5c7f1a5da8
50 zmenil súbory, kde vykonal 570 pridanie a 148 odobranie
  1. 77 0
      composer.json
  2. 221 0
      doc/historic/upgrade_v11.md
  3. 1 0
      doc/index.md
  4. 13 0
      doc/installation.md
  5. 1 1
      ot_admin/Classes/Controller/SiteController.php
  6. 0 3
      ot_admin/ext_emconf.php
  7. 41 0
      ot_connect/Classes/Service/OtAuthenticationService.php
  8. 0 3
      ot_connect/ext_emconf.php
  9. 1 1
      ot_connect/ext_localconf.php
  10. 2 1
      ot_core/Classes/Controller/ActionController.php
  11. 9 5
      ot_core/Classes/Controller/SelectedSiteController.php
  12. 1 1
      ot_core/Classes/Domain/Repository/BaseApiRepository.php
  13. 2 1
      ot_core/Classes/Middleware/Frontend/OtSiteResolver.php
  14. 8 6
      ot_core/Classes/Middleware/OtBackendUserAuthenticator.php
  15. 2 1
      ot_core/Classes/Service/OpentalentApiService.php
  16. 3 2
      ot_core/Classes/ViewHelpers/OtAbstractViewHelper.php
  17. 12 9
      ot_core/Classes/Website/OtPageRepository.php
  18. 12 8
      ot_core/Classes/Website/OtWebsiteRepository.php
  19. 2 1
      ot_core/Configuration/RequestMiddlewares.php
  20. 6 3
      ot_core/Configuration/TCA/Overrides/pages.php
  21. 2 1
      ot_core/Tests/Unit/Service/OpentalentApiServiceTest.php
  22. 6 3
      ot_core/Tests/Unit/Website/OtPageRepositoryTest.php
  23. 2 1
      ot_core/Tests/Unit/Website/OtWebsiteRepositoryTest.php
  24. 5 5
      ot_core/composer.json
  25. 4 7
      ot_core/ext_emconf.php
  26. 1 1
      ot_core/ext_localconf.php
  27. 0 3
      ot_optimizer/ext_emconf.php
  28. 1 1
      ot_optimizer/ext_localconf.php
  29. 15 10
      ot_stats/Classes/Controller/OtStatsController.php
  30. 2 3
      ot_stats/composer.json
  31. 3 6
      ot_stats/ext_emconf.php
  32. 6 3
      ot_stats/ext_tables.php
  33. 8 9
      ot_templating/Classes/Controller/OtCustomizerController.php
  34. 2 1
      ot_templating/Classes/Page/ErrorHandler.php
  35. 3 2
      ot_templating/Classes/ViewHelpers/CObjectViewHelper.php
  36. 2 1
      ot_templating/Classes/ViewHelpers/Organizations/GetChildFederationViewHelper.php
  37. 2 1
      ot_templating/Classes/ViewHelpers/Organizations/GetFederationStructuresViewHelper.php
  38. 5 4
      ot_templating/Classes/ViewHelpers/Social/MetadataViewHelper.php
  39. 6 1
      ot_templating/Classes/XClass/Form/Configuration/ConfigurationManager.php
  40. 25 0
      ot_templating/Classes/XClass/Form/Finishers/EmailFinisher.php
  41. 1 1
      ot_templating/Configuration/TypoScript/setup.txt
  42. 26 20
      ot_templating/Resources/Private/Forms/Contact.form.yaml
  43. 5 5
      ot_templating/Resources/Private/Partials/Classic/Menu.html
  44. 0 1
      ot_templating/Resources/Private/Partials/Modern/Menu.html
  45. 6 0
      ot_templating/Resources/Public/assets/Classic/script/main.js
  46. 4 0
      ot_templating/Resources/Public/assets/Modern/style/custom.css
  47. 4 5
      ot_templating/composer.json
  48. 0 3
      ot_templating/ext_emconf.php
  49. 4 1
      ot_templating/ext_localconf.php
  50. 6 3
      ot_templating/ext_tables.php

+ 77 - 0
composer.json

@@ -0,0 +1,77 @@
+{
+    "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",
+        "fluidtypo3/flux": "^9.7",
+        "fluidtypo3/vhs": "dev-development",
+        "georgringer/news": "^9",
+        "helhum/typo3-console": "^7.1",
+        "causal/image_autoresize": "^2.2",
+        "guzzlehttp/guzzle": "^7.7",
+        "twig/twig": "^3.3",
+        "phpunit/phpunit": "^9.5",
+        "sgalinski/lfeditor": "^7.1",
+        "waldhacker/hcaptcha": "^2.1",
+        "nimut/typo3-complete": "11.5",
+        "nimut/testing-framework": "^6.0",
+        "ext-json": "*",
+        "co-stack/logs": "*"
+    },
+    "scripts": {
+        "typo3-cms-scripts": [
+            "typo3cms install:fixfolderstructure"
+        ],
+        "post-autoload-dump": [
+            "@typo3-cms-scripts"
+        ]
+    },
+    "autoload": {
+        "psr-4": {
+            "Opentalent\\OtCore\\": "public/typo3conf/ext/ot_core/Classes",
+            "Opentalent\\OtConnect\\": "public/typo3conf/ext/ot_connect/Classes",
+            "Opentalent\\OtStats\\": "public/typo3conf/ext/ot_stats/Classes",
+            "Opentalent\\OtTemplating\\": "public/typo3conf/ext/ot_templating/Classes",
+            "Opentalent\\OtOptimizer\\": "public/typo3conf/ext/ot_optimizer/Classes",
+            "Opentalent\\OtAdmin\\": "public/typo3conf/ext/ot_admin/Classes"
+        }
+    },
+    "config": {
+        "allow-plugins": {
+            "typo3/class-alias-loader": true,
+            "typo3/cms-composer-installers": true
+        }
+    },
+    "require-dev": {
+        "ssch/typo3-rector": "^1.3"
+    }
+}

+ 221 - 0
doc/historic/upgrade_v11.md

@@ -28,6 +28,8 @@ Un ticket est créé pour la mise à niveau : [V8-4761](https://assistance.opent
 | friendsoftypo3/frontend-editing | 2.0              | 3.1.1            | ^11.5                  | ?           |
 | sgalinski/lfeditor              | 6.0              | 7.1.11           | ^11.5                  | ?           |
 | waldhacker/hcaptcha             | 2.0              | 2.1.1            | ^11.5                  | ^7.2 - ^8   |
+| guzzle/guzzle                   | 6.x              | 7.7              |                        | ^7.2 - ^8   |
+| nimut/testing-framework         | 5.2              | 6.0.1            | ^11.5.3                | ^7.2 - ^8.0 |
 
 A priori pas de blocage, la mise à jour des extensions sera à faire en même temps que celle de typo3
 
@@ -39,3 +41,222 @@ A priori pas de blocage, la mise à jour des extensions sera à faire en même t
 3. Maj les dépendances à leur dernière version compatible SAUF news, qui reste à la v9 (voir paragraphe précédent)
 4. Aller à la page /setup de typo3, et faire passer les différentes opérations de mise à jour
 5. Tester
+
+
+### Notes upgrade en local
+
+* (réglé, voir: https://github.com/FluidTYPO3/vhs/issues/1869) J'ai dû modifier le type du param ... de la classe FluidTYPO3\Vhs\ViewHelpers\Resource\CollectionViewHelper qui 
+  générait une erreur (mettre Repository comme type à la place de RecordCollectionRepository débloque la situation...)
+* Il a fallu maj le fichier cli, le nouvel emplacement de la console est `/var/www/typo3/vendor/bin/typo3`
+* il faut mettre à jour le .htaccess (prendre celui ici: public/typo3/sysext/install/Resources/Private/FolderStructureTemplateFiles/root-htaccess )
+  et le copier dans public/, puis rajouter les lignes custom en début de fichier)
+* j'ai un bug avec `gzuncompress` au chargement du backend, on peut le corriger avec un `sh cli cache:flush`
+* comme suggéré dans la doc typo3, j'ai lancé un `sh cli referenceindex:update`
+
+### Notes upgrade sur test
+
+* déploiement sur test5
+
+      cd /var/opentalent/git/ot_typo3
+      git fetch origin feature/v8-4762-upgrade_v11
+      git checkout feature/v8-4762-upgrade_v11
+      
+      cd /var/www/typo3
+
+* je copie colle le contenu du composer.json dans celui de test5 
+
+      php7.4 composer.phar update
+
+* je met à jour le fichier `cli` avec le bon chemin d'accès : `/var/www/typo3/vendor/bin/typo3`
+
+      mv public/.htaccess public/.htaccess.old
+      cp public/typo3/sysext/install/Resources/Private/FolderStructureTemplateFiles/root-htaccess public/.htaccess
+
+* je rajoute les premières lignes custom du .htaccess
+
+      touch public/typo3conf/ENABLE_INSTALL_TOOL
+
+* je me rends à l'adresse `https://sub.test5.opentalent.fr/typo3/install.php`
+* je lance la maj de la structure de la DB
+* je lance l'upgrade wizard, et j'exécute toutes les tâches
+* je reset les préférences du backend user
+* je maj les packs de langues
+* je flush le cache
+* j'ajoute la ligne suivante au fichier `nano /etc/php/7.4/cli/conf.d/20-apcu.ini` : `apc.enable_cli=on`
+
+    sh cli cache:flush
+    sh cli referenceindex:update
+
+* je retire le plugin frontend-editing qui ne fonctionne pas : php7.4 composer.phar remove friendsoftypo3/frontend-editing
+
+
+Up:
+
+Je renomme le fichier composer.json et je créé un symlink vers le nouveau composer.json versionné dans le repo ot_typo3 :
+
+    mv composer.json composer.json.orig
+    ln -s /var/opentalent/git/ot_typo3/composer.json ./composer.json
+    php7.4 composer.phar install
+
+
+## Mise en oeuvre
+
+Je me connecte en SSH à prod-front
+
+    ssh exploitation@prod-front
+
+
+
+### Mise en maintenance
+
+Je met à jour le message de maintenance :
+
+    cd /var/www/typo3
+    nano public/maintenance.html
+
+J'active le mode maintenance :
+    
+    nano public/.htaccess
+
+
+Je décommente les lignes :
+
+    #RewriteCond %{REMOTE_ADDR} !^10\.8\.0\.
+    #RewriteCond %{REQUEST_URI} !/maintenance.html$ [NC]
+    #RewriteRule .* /maintenance.html [R=302,L]
+
+Je vérifie depuis un appareil hors VPN que la maintenance est bien active.
+
+### Snapshot
+
+Tony réalise un snapshot de prod-front
+
+### Dump de la DB
+
+Je lance un dump de la DB :
+
+    cd ~
+    ./dump_typo3.sh
+
+### Upgrade
+
+    cd /var/opentalent/git/ot_typo3
+    git fetch origin release/0.7
+    git checkout release/0.7
+
+    cd /var/www/typo3
+
+
+Je renomme le fichier composer.json et je créé un symlink vers le nouveau composer.json versionné dans le repo ot_typo3 :
+
+    mv composer.json composer.json.orig
+    ln -s /var/opentalent/git/ot_typo3/composer.json ./composer.json
+    rm composer.lock
+    rm -r vendor/*
+    php7.4 composer.phar install
+
+
+Je met à jour le fichier `cli` avec le bon chemin d'accès :
+
+     nano cli
+
+On remplace `/var/www/typo3/vendor/bin/typo3cms` par `/var/www/typo3/vendor/bin/typo3`
+
+On va devoir remplacer le fichier .htaccess, sans désactiver le mode maintenance :
+
+    cp public/typo3/sysext/install/Resources/Private/FolderStructureTemplateFiles/root-htaccess public/.htaccess.new
+    nano public/.htaccess.new
+
+Ajouter les lignes suivantes :
+
+    # <--- Opentalent rules
+
+    #SetEnvIf Remote_Addr ^10\.8\.0\.[0-255] TYPO3_CONTEXT=Development
+    
+    # Enable / Disable the opentalent ot_optimizer middlewares
+    SetEnv TYPO3_OPTIMIZE 1
+    
+    RewriteCond %{REMOTE_ADDR} !^10\.8\.0\.
+    RewriteCond %{REQUEST_URI} !/maintenance.html$ [NC]
+    RewriteRule .* /maintenance.html [R=302,L]
+
+    # Opentalent rules --->
+
+Puis remplacer le .htaccess existant :
+
+    mv public/.htaccess public/.htaccess.old
+    mv public/.htaccess.new public/.htaccess
+
+Vérifier que le mode maintenance est toujours activé à l'extérieur
+
+J'active le mode install de typo3 :
+
+      touch public/typo3conf/ENABLE_INSTALL_TOOL
+
+Je me rends à l'adresse `https://ohcluses.opentalent.fr/typo3/install.php`
+
+Je lance la maj de la structure de la DB
+
+Je lance l'upgrade wizard, et j'exécute toutes les tâches
+
+Je reset les préférences du backend user
+
+Je maj les packs de langues
+
+Je flush le cache
+
+
+J'ouvre en édition le fichier suivant :
+
+    sudo nano /etc/php/7.4/cli/conf.d/20-apcu.ini
+
+Et j'ajoute la ligne :
+
+    apc.enable_cli=on
+
+Puis je lance les dernières commandes :
+
+    sh cli cache:flush
+    sh cli referenceindex:update
+
+
+
+
+Je vérifie l'installation
+
+
+### Désactivation du mode maintenance 
+
+J'édite le fichier .htaccess
+
+    cd /var/www/typo3
+    nano public/.htaccess
+
+
+Je commente les lignes :
+
+    RewriteCond %{REMOTE_ADDR} !^10\.8\.0\.
+    RewriteCond %{REQUEST_URI} !/maintenance.html$ [NC]
+    RewriteRule .* /maintenance.html [R=302,L]
+
+### Nettoyage après upgrade
+
+Si tout fonctionne bien, je retire les fichiers devenus inutiles :
+
+    rm composer.json.orig
+    rm public/.htaccess.old
+
+
+Supprimer les champs et tables en zzz_ depuis le BE typo3
+
+
+### Release
+
+Finaliser la release dans GitKraken
+
+Switcher sur master sur prod-front :
+
+    cd /var/opentalent/git/ot_typo3
+    git checkout master
+    git pull
+

+ 1 - 0
doc/index.md

@@ -39,3 +39,4 @@ plus environ 2000 sites archivés.
 2. [Résolution des problèmes d'authentification cross-domaine (2021)](historic/cross_domain_auth.md)
 3. [Upgrade Typo3 v9.5 vers v10.4 (2021)](historic/upgrade_v10.md)
 4. [Contribution / Retour d'expérience à la communauté Typo3 (2021)](historic/typo3_contrib.md)
+5. [Upgrade Typo3 v10.4 vers v11.5 (2023)](historic/upgrade_v11.md)

+ 13 - 0
doc/installation.md

@@ -20,6 +20,19 @@ Les [extensions Opentalent](..) sont installées dans: `/var/opentalent/git/ot_t
 Ces extensions sont ensuite intégrées à l'installation Typo3 sous forme de liens symboliques dans le répertoire
 `/var/www/typo3/public/typo3conf/ext/`
 
+    ln -s /var/opentalent/git/ot_typo3/ot_admin ./public/typo3conf/ext/ot_admin
+    ln -s /var/opentalent/git/ot_typo3/ot_connect ./public/typo3conf/ext/ot_connect
+    ln -s /var/opentalent/git/ot_typo3/ot_core ./public/typo3conf/ext/ot_core
+    ln -s /var/opentalent/git/ot_typo3/ot_optimizer ./public/typo3conf/ext/ot_optimizer
+    ln -s /var/opentalent/git/ot_typo3/ot_stats ./public/typo3conf/ext/ot_stats
+    ln -s /var/opentalent/git/ot_typo3/ot_templating ./public/typo3conf/ext/ot_templating
+
+De la même façon, un lien symbolique est créé depuis le répertoire d'installation Typo3 vers le fichier `composer.json`
+à la racine du repository ot_typo3.
+
+    ln -s /var/opentalent/git/ot_typo3/composer.json ./composer.json
+
+
 ## Opérations courantes
 
 ### Faire une sauvegarde des données

+ 1 - 1
ot_admin/Classes/Controller/SiteController.php

@@ -2275,7 +2275,7 @@ class SiteController extends ActionController
 
         // Flush cache:
         try {
-            $cacheSystem = $this->cacheManager->getCache('cache_core');
+            $cacheSystem = $this->cacheManager->getCache('core');
             $cacheSystem->remove('site-configuration');
             $cacheSystem->remove('pseudo-sites');
         } catch (NoSuchCacheException $e) {

+ 0 - 3
ot_admin/ext_emconf.php

@@ -15,9 +15,6 @@ $EM_CONF[$_EXTKEY] = [
     'author' => 'Olivier Massot',
     'author_email' => 'olivier.massot@2iopenservice.fr',
     'state' => 'stable',
-    'uploadfolder' => 0,
-    'createDirs' => '',
-    'clearCacheOnLoad' => 0,
     'version' => '1.0.0', //see ot_core/ext_emconf.php to get the current ot extensions version
     'constraints' => [
         'depends' => [

+ 41 - 0
ot_connect/Classes/Service/OtAuthenticationService.php

@@ -11,6 +11,8 @@ use Opentalent\OtCore\Logging\OtLogger;
 use Opentalent\OtCore\Service\OpentalentApiService;
 use TYPO3\CMS\Core\Crypto\Random;
 use TYPO3\CMS\Core\Database\ConnectionPool;
+use TYPO3\CMS\Core\Database\Query\QueryHelper;
+use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction;
 use TYPO3\CMS\Core\TimeTracker\TimeTracker;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 use \TYPO3\CMS\Core\Authentication\AbstractAuthenticationService;
@@ -470,4 +472,43 @@ class OtAuthenticationService extends AbstractAuthenticationService
         setcookie($name, '', 1, '/', $_SERVER['HTTP_HOST']); // for custom domains (not in .opentalent.fr)
         setcookie($name, '', 1, '/', self::COOKIE_DOMAIN);   // for opentalent.fr subdomains
     }
+
+    /**
+     * Get a user from DB by username
+     *
+     * @param string $username User name
+     * @param string $extraWhere Additional WHERE clause: " AND ...
+     * @param array|string $dbUserSetup User db table definition, or empty string for $this->db_user
+     * @return mixed User array or FALSE
+     */
+    public function fetchUserRecordTemp($username, $extraWhere = '', $dbUserSetup = '')
+    {
+        $dbUser = is_array($dbUserSetup) ? $dbUserSetup : $this->db_user;
+        $user = false;
+        if ($username || $extraWhere) {
+            $query = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($dbUser['table']);
+            $query->getRestrictions()->removeAll()
+                ->add(GeneralUtility::makeInstance(DeletedRestriction::class));
+            $constraints = array_filter([
+                QueryHelper::stripLogicalOperatorPrefix($dbUser['check_pid_clause']),
+                QueryHelper::stripLogicalOperatorPrefix($dbUser['enable_clause']),
+                QueryHelper::stripLogicalOperatorPrefix($extraWhere),
+            ]);
+            if (!empty($username)) {
+                array_unshift(
+                    $constraints,
+                    $query->expr()->eq(
+                        $dbUser['username_column'],
+                        $query->createNamedParameter($username, \PDO::PARAM_STR)
+                    )
+                );
+            }
+            $user = $query->select('*')
+                ->from($dbUser['table'])
+                ->where(...$constraints)
+                ->execute()
+                ->fetch();
+        }
+        return $user;
+    }
 }

+ 0 - 3
ot_connect/ext_emconf.php

@@ -15,9 +15,6 @@ $EM_CONF[$_EXTKEY] = [
     'author' => 'Olivier Massot',
     'author_email' => 'olivier.massot@2iopenservice.fr',
     'state' => 'stable',
-    'uploadfolder' => 0,
-    'createDirs' => '',
-    'clearCacheOnLoad' => 0,
     'version' => '1.0.0', //see ot_core/ext_emconf.php to get the current ot extensions version
     'constraints' => [
         'depends' => [

+ 1 - 1
ot_connect/ext_localconf.php

@@ -1,5 +1,5 @@
 <?php
-defined('TYPO3_MODE') || die();
+defined('TYPO3') || die();
 
 // Register Opentalent authentication service with TYPO3
 \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addService(

+ 2 - 1
ot_core/Classes/Controller/ActionController.php

@@ -4,6 +4,7 @@ namespace Opentalent\OtCore\Controller;
 
 use Opentalent\OtCore\Website\OtPageRepository;
 use Opentalent\OtCore\Website\OtWebsiteRepository;
+use TYPO3\CMS\Core\Localization\LanguageService;
 
 /**
  * Base class for all controllers of backend modules
@@ -33,7 +34,7 @@ class ActionController extends \TYPO3\CMS\Extbase\Mvc\Controller\ActionControlle
     /**
      * Returns the language service.
      *
-     * @return \TYPO3\CMS\Core\Localization\LanguageService
+     * @return LanguageService
      */
     protected function getLanguageService()
     {

+ 9 - 5
ot_core/Classes/Controller/SelectedSiteController.php

@@ -3,7 +3,11 @@
 namespace Opentalent\OtCore\Controller;
 
 use Opentalent\OtCore\Exception\NoSiteSelected;
+use Opentalent\OtCore\Exception\NoSuchWebsiteException;
 use Opentalent\OtCore\Website\OtPageRepository;
+use Psr\Http\Message\ResponseInterface;
+use TYPO3\CMS\Extbase\Mvc\Exception\StopActionException;
+use TYPO3\CMS\Extbase\Mvc\RequestInterface;
 use TYPO3\CMS\Extbase\Object\ObjectManager;
 
 /**
@@ -38,11 +42,11 @@ class SelectedSiteController extends ActionController
 
     /**
      *
-     * @throws \TYPO3\CMS\Extbase\Mvc\Exception\StopActionException
-     * @throws \Opentalent\OtCore\Exception\NoSuchWebsiteException
+     * @throws StopActionException
+     * @throws NoSuchWebsiteException
      * @throws \Exception
      */
-    protected function callActionMethod() {
+    protected function callActionMethod(RequestInterface $request): ResponseInterface {
 
         // Check if the current be-user has a db_mountpoint, and only has one.
         // If so, this will be considered as the selected site (since its the only one available)
@@ -69,7 +73,7 @@ class SelectedSiteController extends ActionController
         }
 
         // [TESTS ONLY]
-        if ($this->preventPropagation) { return; }
+        if ($this->preventPropagation) { return parent::callActionMethod($request); }
 
         // No site is selected, redirect to the warning page
         if ($this->actionMethodName != 'displayNoSelectedPageWarningAction' && $this->currentRootUid == null) {
@@ -78,7 +82,7 @@ class SelectedSiteController extends ActionController
             // $this->forward('displayNoSelectedPageWarning', 'SelectedSite', 'OtCore');
         }
 
-        parent::callActionMethod();
+        return parent::callActionMethod($request);
     }
 
     protected function displayNoSelectedPageWarningAction() {}

+ 1 - 1
ot_core/Classes/Domain/Repository/BaseApiRepository.php

@@ -52,7 +52,7 @@ abstract class BaseApiRepository
      * @return object
      * @throws ApiRequestException
      */
-    protected function getApiFirstRecord(array $params = [], ?string $forceUri = null): object
+    protected function getApiFirstRecord(array $params = [], ?string $forceUri = null): ?object
     {
         $params['page'] = '1';
         $params['totalItems'] = '1';

+ 2 - 1
ot_core/Classes/Middleware/Frontend/OtSiteResolver.php

@@ -11,12 +11,13 @@ use TYPO3\CMS\Core\Utility\GeneralUtility;
 use TYPO3\CMS\Extbase\Object\ObjectManager;
 use TYPO3\CMS\Core\Routing\SiteRouteResult;
 use TYPO3\CMS\Frontend\Controller\ErrorController;
+use TYPO3\CMS\Frontend\Middleware\SiteResolver;
 use TYPO3\CMS\Frontend\Page\PageAccessFailureReasons;
 
 /**
  *
  */
-class OtSiteResolver extends \TYPO3\CMS\Frontend\Middleware\SiteResolver
+class OtSiteResolver extends SiteResolver
 {
     /**
      * Resolve the site/language information by checking the page ID or the URL.

+ 8 - 6
ot_core/Classes/Middleware/OtBackendUserAuthenticator.php

@@ -1,8 +1,10 @@
 <?php
 namespace Opentalent\OtCore\Middleware;
 
+use Opentalent\OtAdmin\Http\ApiController;
 use PHPUnit\Exception;
 use TYPO3\CMS\Backend\Middleware\BackendUserAuthenticator;
+use TYPO3\CMS\Backend\Routing\Route;
 
 /**
  * Overrides (XClass) the core BackendUserAuthenticator middleware to extend
@@ -16,15 +18,15 @@ class OtBackendUserAuthenticator extends BackendUserAuthenticator
      * Check if the user is required for the request
      * If we're trying to do a login or an ajax login, don't require a user
      *
-     * @param string $routePath the Route path to check against
+     * @param Route $route
      * @return bool whether the request can proceed without a login required
      */
-    protected function isLoggedInBackendUserRequired(string $routePath): bool
+    protected function isLoggedInBackendUserRequired(Route $route): bool
     {
         if (class_exists('\Opentalent\OtAdmin\Http\ApiController')) {
             // The routes defined in the ot-admin extension are limited to some ips
-            if (preg_match('/\/otadmin\/.+/', $routePath)) {
-                if (\Opentalent\OtAdmin\Http\ApiController::isIpAllowed($_SERVER['REMOTE_ADDR'])) {
+            if (preg_match('/\/otadmin\/.+/', $route->getPath())) {
+                if (ApiController::isIpAllowed($_SERVER['REMOTE_ADDR'])) {
                     return true;
                 } else {
                     throw new \RuntimeException('An unauthorized IP (' . $_SERVER['REMOTE_ADDR'] . ') ' .
@@ -34,10 +36,10 @@ class OtBackendUserAuthenticator extends BackendUserAuthenticator
         }
 
         // The routes defined in the ot-core extension are public
-        if (preg_match('/\/otcore\/.+/', $routePath)) {
+        if (preg_match('/\/otcore\/.+/', $route->getPath())) {
             return true;
         }
 
-        return parent::isLoggedInBackendUserRequired($routePath);
+        return parent::isLoggedInBackendUserRequired($route);
     }
 }

+ 2 - 1
ot_core/Classes/Service/OpentalentApiService.php

@@ -7,6 +7,7 @@ use GuzzleHttp\Exception\GuzzleException;
 use Opentalent\OtCore\Exception\ApiRequestException;
 use Opentalent\OtCore\Logging\OtLogger;
 use Psr\Http\Message\ResponseInterface;
+use TYPO3\CMS\Core\Core\Environment;
 
 class OpentalentApiService
 {
@@ -25,7 +26,7 @@ class OpentalentApiService
     ) {
         // Get the current context (prod, dev...)
         if ($context === null) {
-            $this->context = \TYPO3\CMS\Core\Core\Environment::getContext();
+            $this->context = Environment::getContext();
         } else {
             $this->context = $context;
         }

+ 3 - 2
ot_core/Classes/ViewHelpers/OtAbstractViewHelper.php

@@ -7,6 +7,7 @@ use Opentalent\OtCore\Logging\OtLogger;
 use Opentalent\OtCore\Website\OtPageRepository;
 use Psr\Log\LoggerAwareInterface;
 use Psr\Log\LoggerAwareTrait;
+use TYPO3\CMS\Core\Localization\LanguageService;
 use TYPO3\CMS\Extbase\Utility\LocalizationUtility;
 use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
 
@@ -42,9 +43,9 @@ class OtAbstractViewHelper extends AbstractViewHelper {
     /**
      * Returns the language service.
      *
-     * @return \TYPO3\CMS\Core\Localization\LanguageService
+     * @return LanguageService
      */
-    protected function getLanguageService(): \TYPO3\CMS\Core\Localization\LanguageService
+    protected function getLanguageService(): LanguageService
     {
         return $GLOBALS['LANG'];
     }

+ 12 - 9
ot_core/Classes/Website/OtPageRepository.php

@@ -2,7 +2,10 @@
 
 namespace Opentalent\OtCore\Website;
 
+use FluidTYPO3\Vhs\Service\PageService;
 use Opentalent\OtCore\Exception\NoSiteSelected;
+use TYPO3\CMS\Core\Database\ConnectionPool;
+use TYPO3\CMS\Core\Domain\Repository\PageRepository;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 
 /**
@@ -15,31 +18,31 @@ use TYPO3\CMS\Core\Utility\GeneralUtility;
 class OtPageRepository
 {
     /**
-     * @var \FluidTYPO3\Vhs\Service\PageService
+     * @var PageService
      */
-    private \FluidTYPO3\Vhs\Service\PageService $pageService;
+    private PageService $pageService;
 
-    public function injectPageService(\FluidTYPO3\Vhs\Service\PageService $pageService)
+    public function injectPageService(PageService $pageService)
     {
         $this->pageService = $pageService;
     }
 
     /**
-     * @var \TYPO3\CMS\Core\Domain\Repository\PageRepository
+     * @var PageRepository
      */
-    private \TYPO3\CMS\Core\Domain\Repository\PageRepository $pageRepository;
+    private PageRepository $pageRepository;
 
-    public function injectPageRepository(\TYPO3\CMS\Core\Domain\Repository\PageRepository $pageRepository)
+    public function injectPageRepository(PageRepository $pageRepository)
     {
         $this->pageRepository = $pageRepository;
     }
 
     /**
-     * @var \TYPO3\CMS\Core\Database\ConnectionPool
+     * @var ConnectionPool
      */
-    private \TYPO3\CMS\Core\Database\ConnectionPool $connectionPool;
+    private ConnectionPool $connectionPool;
 
-    public function injectConnectionPool(\TYPO3\CMS\Core\Database\ConnectionPool $connectionPool)
+    public function injectConnectionPool(ConnectionPool $connectionPool)
     {
         $this->connectionPool = $connectionPool;
     }

+ 12 - 8
ot_core/Classes/Website/OtWebsiteRepository.php

@@ -2,12 +2,15 @@
 
 namespace Opentalent\OtCore\Website;
 
+use Doctrine\DBAL\DBALException;
 use Opentalent\OtCore\Exception\InvalidWebsiteConfigurationException;
 use Opentalent\OtCore\Exception\NoSuchRecordException;
 use Opentalent\OtCore\Exception\NoSuchWebsiteException;
 use Opentalent\OtCore\Utility\RouteNormalizer;
 use Psr\Http\Message\UriInterface;
+use Symfony\Component\Yaml\Exception\ParseException;
 use Symfony\Component\Yaml\Yaml;
+use TYPO3\CMS\Core\Database\ConnectionPool;
 use TYPO3\CMS\Core\Http\Uri;
 use TYPO3\CMS\Core\Site\Entity\Site;
 
@@ -19,11 +22,11 @@ use TYPO3\CMS\Core\Site\Entity\Site;
 class OtWebsiteRepository
 {
     /**
-     * @var \TYPO3\CMS\Core\Database\ConnectionPool
+     * @var ConnectionPool
      */
-    private \TYPO3\CMS\Core\Database\ConnectionPool $connectionPool;
+    private ConnectionPool $connectionPool;
 
-    public function injectConnectionPool(\TYPO3\CMS\Core\Database\ConnectionPool $connectionPool)
+    public function injectConnectionPool(ConnectionPool $connectionPool)
     {
         $this->connectionPool = $connectionPool;
     }
@@ -72,6 +75,7 @@ class OtWebsiteRepository
      * Get the OtWebsite of the given organization
      *
      * @throws NoSuchWebsiteException
+     * @throws DBALException
      */
     public function getWebsiteByOrganizationId(int $organizationId, bool $withRestrictions = true): array
     {
@@ -328,12 +332,12 @@ class OtWebsiteRepository
     /**
      * Try to retrieve the website matching the given Uri and return the given website
      *
-     * @param \Psr\Http\Message\UriInterface $uri
+     * @param UriInterface $uri
      * @param bool $devMode
      * @return Site
      * @throws NoSuchWebsiteException
      */
-    public function matchUriToWebsite(\Psr\Http\Message\UriInterface $uri, bool $devMode=false, bool $withRestrictions = true): array
+    public function matchUriToWebsite(UriInterface $uri, bool $devMode=false, bool $withRestrictions = true): array
     {
         $queryBuilder = $this->connectionPool->getQueryBuilderForTable('ot_websites');
 
@@ -416,7 +420,7 @@ class OtWebsiteRepository
             $filename = $configs_directory . $identifier . "/config.yaml";
             try {
                 $yamlConfig = Yaml::parseFile($filename);
-            } catch (\Symfony\Component\Yaml\Exception\ParseException $e) {
+            } catch (ParseException $e) {
                 throw new \RuntimeException("No configuration file found for identifier " . $identifier);
             }
 
@@ -442,7 +446,7 @@ class OtWebsiteRepository
                     if ($yamlConfig['rootPageId'] === $rootUid) {
                         return [$filename, $yamlConfig];
                     }
-                } catch (\Symfony\Component\Yaml\Exception\ParseException $e) {
+                } catch (ParseException $e) {
                     continue;
                 }
             }
@@ -456,7 +460,7 @@ class OtWebsiteRepository
                 if ($yamlConfig['rootPageId'] === $rootUid) {
                     return [$filename, $yamlConfig];
                 }
-            } catch (\Symfony\Component\Yaml\Exception\ParseException $e) {
+            } catch (ParseException $e) {
                 continue;
             }
         }

+ 2 - 1
ot_core/Configuration/RequestMiddlewares.php

@@ -1,4 +1,5 @@
 <?php
+use Opentalent\OtCore\Middleware\Frontend\OtSiteResolver;
 
 /**
  * Register middlewares, which will be triggered at each request
@@ -6,7 +7,7 @@
 return [
     'frontend' => [
         'typo3/cms-frontend/site' => [
-            'target' => Opentalent\OtCore\Middleware\Frontend\OtSiteResolver::class,
+            'target' => OtSiteResolver::class,
             'before' => [
                 'typo3/cms-frontend/page-resolver'
             ]

+ 6 - 3
ot_core/Configuration/TCA/Overrides/pages.php

@@ -1,5 +1,8 @@
 <?php
-defined('TYPO3_MODE') or die();
+
+use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
+
+defined('TYPO3') or die();
 
 // ** Add fields to the backend
 $columns = array (
@@ -12,12 +15,12 @@ $columns = array (
     )
 );
 
-\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addTCAcolumns(
+ExtensionManagementUtility::addTCAcolumns(
     'pages',
     $columns
 );
 
-\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addToAllTCAtypes(
+ExtensionManagementUtility::addToAllTCAtypes(
     'pages',
     'ot_website_uid',
     '',

+ 2 - 1
ot_core/Tests/Unit/Service/OpentalentApiServiceTest.php

@@ -3,6 +3,7 @@
 namespace Opentalent\OtCore\Tests\Unit\Service;
 
 use GuzzleHttp\Client;
+use GuzzleHttp\Exception\TransferException;
 use Nimut\TestingFramework\TestCase\UnitTestCase;
 use Opentalent\OtCore\Exception\ApiRequestException;
 use Opentalent\OtCore\Service\OpentalentApiService;
@@ -51,7 +52,7 @@ class OpentalentApiServiceTest extends UnitTestCase
         } catch (\RuntimeException $e) {
             $client->request($http_method, Argument::type('string'), [])
                 ->shouldBeCalled()
-                ->willThrow(new \GuzzleHttp\Exception\TransferException('bad prophecy request'));
+                ->willThrow(new TransferException('bad prophecy request'));
         }
 
         $reflectionObject = new \ReflectionObject($this->service);

+ 6 - 3
ot_core/Tests/Unit/Website/OtPageRepositoryTest.php

@@ -2,10 +2,13 @@
 
 namespace Opentalent\OtCore\Tests\Unit\Website;
 
+use FluidTYPO3\Vhs\Service\PageService;
 use Opentalent\OtCore\Website\OtPageRepository;
 use Nimut\TestingFramework\TestCase\UnitTestCase;
 use Opentalent\OtCore\Tests\Unit\Fixtures\PageFixtures;
 use Opentalent\OtCore\Tests\Unit\QueryBuilderProphet;
+use TYPO3\CMS\Core\Database\ConnectionPool;
+use TYPO3\CMS\Core\Domain\Repository\PageRepository;
 
 
 class OtPageRepositoryTest extends UnitTestCase
@@ -18,9 +21,9 @@ class OtPageRepositoryTest extends UnitTestCase
 
     public function setUp() {
         // Prophecies to be injected in the under test OtPageRepository
-        $this->pageService = $this->prophesize(\FluidTYPO3\Vhs\Service\PageService::class);
-        $this->pageRepository = $this->prophesize(\TYPO3\CMS\Core\Domain\Repository\PageRepository::class);
-        $this->connectionPool = $this->prophesize(\TYPO3\CMS\Core\Database\ConnectionPool::class);
+        $this->pageService = $this->prophesize(PageService::class);
+        $this->pageRepository = $this->prophesize(PageRepository::class);
+        $this->connectionPool = $this->prophesize(ConnectionPool::class);
 
         // Fixtures
         $this->pageFixtures = new PageFixtures();

+ 2 - 1
ot_core/Tests/Unit/Website/OtWebsiteRepositoryTest.php

@@ -12,6 +12,7 @@ use Opentalent\OtCore\Tests\Unit\Fixtures\WebsiteFixtures;
 use Opentalent\OtCore\Tests\Unit\QueryBuilderProphet;
 use Opentalent\OtCore\Website\OtPageRepository;
 use Opentalent\OtCore\Website\OtWebsiteRepository;
+use TYPO3\CMS\Core\Database\ConnectionPool;
 
 class OtWebsiteRepositoryTest extends UnitTestCase
 {
@@ -22,7 +23,7 @@ class OtWebsiteRepositoryTest extends UnitTestCase
 
     public function setUp() {
         // Prophecies to be injected in the under test OtPageRepository
-        $this->connectionPool = $this->prophesize(\TYPO3\CMS\Core\Database\ConnectionPool::class);
+        $this->connectionPool = $this->prophesize(ConnectionPool::class);
 
         // Fixtures
         $this->pageFixtures = new PageFixtures();

+ 5 - 5
ot_core/composer.json

@@ -9,12 +9,12 @@
         }
     ],
     "require": {
-        "typo3/cms-core": "^10.4",
-        "fluidtypo3/vhs": "^6.0",
-        "fluidtypo3/flux": "^9.5",
+        "typo3/cms-core": "^11.5",
+        "fluidtypo3/vhs": "^6.1",
+        "fluidtypo3/flux": "^9.7",
         "guzzlehttp/guzzle": "^6",
-        "nimut/typo3-complete": "10.4",
-        "nimut/testing-framework": "^5.2"
+        "nimut/typo3-complete": "11.5",
+        "nimut/testing-framework": "^6.0"
     },
     "replace": {
         "opentalent/ot_core": "self.version",

+ 4 - 7
ot_core/ext_emconf.php

@@ -15,15 +15,12 @@ $EM_CONF[$_EXTKEY] = [
     'author' => 'Olivier Massot',
     'author_email' => 'olivier.massot@2iopenservice.fr',
     'state' => 'stable',
-    'uploadfolder' => 0,
-    'createDirs' => '',
-    'clearCacheOnLoad' => 0,
-    'version' => '0.6.6',
+    'version' => '0.7',
     'constraints' => [
         'depends' => [
-            'typo3' => '8.7.0-10.4.99',
-            'flux'  => '9.3.0-9.4.99',
-            'vhs'  => '6.0.0-6.0.99',
+            'typo3' => '10.4.0-11.5.99',
+            'flux'  => '9.3.0-9.7.99',
+            'vhs'  => '6.0.0-6.1.99',
         ],
         'conflicts' => [],
         'suggests' => [],

+ 1 - 1
ot_core/ext_localconf.php

@@ -1,6 +1,6 @@
 <?php
 
-if (!defined('TYPO3_MODE')) {
+if (!defined('TYPO3')) {
     die('Access denied.');
 }
 

+ 0 - 3
ot_optimizer/ext_emconf.php

@@ -15,9 +15,6 @@ $EM_CONF[$_EXTKEY] = [
     'author' => 'Olivier Massot',
     'author_email' => 'olivier.massot@2iopenservice.fr',
     'state' => 'stable',
-    'uploadfolder' => 0,
-    'createDirs' => '',
-    'clearCacheOnLoad' => 0,
     'version' => '1.0.0',  //see ot_core/ext_emconf.php to get the current ot extensions version
     'constraints' => [
         'depends' => [

+ 1 - 1
ot_optimizer/ext_localconf.php

@@ -1,5 +1,5 @@
 <?php
-defined('TYPO3_MODE') || die();
+defined('TYPO3') || die();
 
 $GLOBALS['TYPO3_CONF_VARS']['SYS']['Objects'][TYPO3\CMS\Core\Routing\PageSlugCandidateProvider::class] = [
     'className' => Opentalent\OtOptimizer\XClass\Core\Routing\OtPageSlugCandidateProvider::class

+ 15 - 10
ot_stats/Classes/Controller/OtStatsController.php

@@ -5,8 +5,10 @@ namespace Opentalent\OtStats\Controller;
 use Opentalent\OtCore\Controller\SelectedSiteController;
 use Opentalent\OtCore\Logging\OtLogger;
 use Opentalent\OtStats\Domain\Repository\MatomoWebsiteRepository;
+use Psr\Http\Message\ResponseInterface;
 use TYPO3\CMS\Core\Messaging\AbstractMessage;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
+use TYPO3\CMS\Extbase\Http\ForwardResponse;
 use TYPO3\CMS\Extbase\Mvc\Exception\StopActionException;
 use TYPO3\CMS\Extbase\Object\ObjectManager;
 
@@ -26,10 +28,10 @@ class OtStatsController extends SelectedSiteController {
      * Index action (default action)
      * Displays the customizer page on the backend
      */
-    public function indexAction() {
+    public function indexAction(): ResponseInterface {
         $matomoId = $this->currentWebsite['matomo_site_id'];
         if ($matomoId == null) {
-            $this->forward('askForActivationConfirmation');
+            return new ForwardResponse('askForActivationConfirmation');
         }
         $this->view->assign('matomoSiteId', (int)$matomoId);
         $this->view->assign('matomoToken', self::MATOMO_TOKEN);
@@ -38,26 +40,29 @@ class OtStatsController extends SelectedSiteController {
         // Default interval
         $period = $args['period'] ?? 'month';
         $this->view->assign('period', $period);
-
+        return $this->htmlResponse();
     }
 
     /**
      * Display a confirmation page before enabling the stats module
      */
-    public function askForActivationConfirmationAction() {}
+    public function askForActivationConfirmationAction(): ResponseInterface {
+        return $this->htmlResponse();
+    }
 
     /**
      * Display a confirmation page before disabling the stats module
      */
-    public function askForDeactivationConfirmationAction() {
+    public function askForDeactivationConfirmationAction(): ResponseInterface {
         $this->view->assign('rootUid', $this->currentRootUid);
+        return $this->htmlResponse();
     }
 
     /**
      * Creates a matomo site record if none exists and
      * save its id
      */
-    public function enableStatsAction() {
+    public function enableStatsAction(): ResponseInterface {
         $matomoRepository = GeneralUtility::makeInstance(ObjectManager::class)->get(MatomoWebsiteRepository::class);
 
         try {
@@ -72,9 +77,9 @@ class OtStatsController extends SelectedSiteController {
                 '',
                 AbstractMessage::ERROR
             );
-            $this->forward('askForActivationConfirmation');
+            return new ForwardResponse('askForActivationConfirmation');
         }
-        $this->forward('index');
+        return new ForwardResponse('index');
     }
 
     /**
@@ -85,7 +90,7 @@ class OtStatsController extends SelectedSiteController {
      *                          confirmation page.
      * @throws StopActionException
      */
-    public function disableStatsAction(int $rootUid) {
+    public function disableStatsAction(int $rootUid): ResponseInterface {
 
         $matomoRepository = GeneralUtility::makeInstance(ObjectManager::class)->get(MatomoWebsiteRepository::class);
 
@@ -101,6 +106,6 @@ class OtStatsController extends SelectedSiteController {
                 AbstractMessage::ERROR
             );
         }
-        $this->forward('askForActivationConfirmation');
+        return new ForwardResponse('askForActivationConfirmation');
     }
 }

+ 2 - 3
ot_stats/composer.json

@@ -9,10 +9,9 @@
         }
     ],
     "require": {
-        "typo3/cms-core": "^9.5 || ^10.4",
+        "typo3/cms-core": "^11.5",
         "fluidtypo3/vhs": "^6",
-        "fluidtypo3/flux": "^9",
-        "fluidtypo3/fluidpages": "^5"
+        "fluidtypo3/flux": "^9"
     },
     "replace": {
         "ot_stats": "self.version",

+ 3 - 6
ot_stats/ext_emconf.php

@@ -15,15 +15,12 @@ $EM_CONF[$_EXTKEY] = [
     'author' => 'Olivier Massot',
     'author_email' => 'olivier.massot@2iopenservice.fr',
     'state' => 'stable',
-    'uploadfolder' => 0,
-    'createDirs' => '',
-    'clearCacheOnLoad' => 0,
     'version' => '1.0.0',  //see ot_core/ext_emconf.php to get the current ot extensions version
     'constraints' => [
         'depends' => [
-            'typo3' => '8.7.0-10.4.99',
-            'flux'  => '9.3.0-9.4.99',
-            'vhs'  => '6.0.0-6.0.99',
+            'typo3' => '10.4.00-11.5.99',
+            'flux'  => '9.3.0-9.7.99',
+            'vhs'  => '6.0.0-6.1.99',
             'ot_core' => '*'
         ],
         'conflicts' => [],

+ 6 - 3
ot_stats/ext_tables.php

@@ -1,5 +1,8 @@
 <?php
-defined('TYPO3_MODE') || die('Access denied.');
+
+use Opentalent\OtStats\Controller\OtStatsController;
+
+defined('TYPO3') || die('Access denied.');
 
 // ext_tables.php contient les directives permettant de configurer le backend
 
@@ -11,12 +14,12 @@ call_user_func(
              * Registers the statistics backend module
              */
             \TYPO3\CMS\Extbase\Utility\ExtensionUtility::registerModule(
-                'Opentalent.OtStats',
+                'OtStats',
                 'web', // Make module a submodule of 'web'
                 'otstats', // Submodule key
                 'after:OtTemplatingOtcustomizer', // Position
                 array(
-                    'OtStats' => 'index,askForActivationConfirmation,askForDeactivationConfirmation,enableStats,disableStats'
+                    OtStatsController::class => 'index,askForActivationConfirmation,askForDeactivationConfirmation,enableStats,disableStats'
                 ),
                 array(
                     'access' => 'user,group',

+ 8 - 9
ot_templating/Classes/Controller/OtCustomizerController.php

@@ -7,6 +7,8 @@ use Opentalent\OtCore\Cache\OtCacheManager;
 use Opentalent\OtCore\Website\OtWebsiteRepository;
 use Opentalent\OtTemplating\Templating\TemplateRepository;
 use PDO;
+use Psr\Http\Message\ResponseInterface;
+use TYPO3\CMS\Extbase\Http\ForwardResponse;
 use TYPO3\CMS\Core\Database\ConnectionPool;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 use TYPO3\CMS\Extbase\Object\ObjectManager;
@@ -19,11 +21,11 @@ use TYPO3\CMS\Extbase\Object\ObjectManager;
 class OtCustomizerController extends SelectedSiteController {
 
     /**
-     * @var \TYPO3\CMS\Core\Database\ConnectionPool
+     * @var ConnectionPool
      */
     private $connectionPool;
 
-    public function injectConnectionPool(\TYPO3\CMS\Core\Database\ConnectionPool $connectionPool)
+    public function injectConnectionPool(ConnectionPool $connectionPool)
     {
         $this->connectionPool = $connectionPool;
     }
@@ -32,7 +34,7 @@ class OtCustomizerController extends SelectedSiteController {
      * Index action (default action)
      * Displays the customizer page on the backend
      */
-    public function indexAction() {
+    public function indexAction(): ResponseInterface {
         $this->view->assign('rootPage', $this->currentRootUid);
         $this->view->assign('website', $this->currentWebsite);
         $this->view->assign('templates', TemplateRepository::templates);
@@ -40,6 +42,7 @@ class OtCustomizerController extends SelectedSiteController {
         $templateRepository = GeneralUtility::makeInstance(ObjectManager::class)->get(TemplateRepository::class);
         $this->view->assign('currentTemplate', $templateRepository->getTemplate($this->currentWebsite));
         $this->view->assign('preferences', $templateRepository->getTemplatePreferences($this->currentWebsite));
+        return $this->htmlResponse();
     }
 
     /**
@@ -62,7 +65,7 @@ class OtCustomizerController extends SelectedSiteController {
             'LLL:EXT:ot_templating/Resources/Private/Language/locallang.xlf:theme_updated'
         ));
 
-        $this->forward('index');
+        return new ForwardResponse('index');
     }
 
     /**
@@ -100,10 +103,6 @@ class OtCustomizerController extends SelectedSiteController {
             'LLL:EXT:ot_templating/Resources/Private/Language/locallang.xlf:settings_updated'
         ));
 
-        $this->forward(
-            'index',
-            'OtCustomizer',
-            'OtTemplating'
-        );
+        return (new ForwardResponse('index'))->withControllerName('OtCustomizer')->withExtensionName('OtTemplating');
     }
 }

+ 2 - 1
ot_templating/Classes/Page/ErrorHandler.php

@@ -11,6 +11,7 @@ use TYPO3\CMS\Core\Error\PageErrorHandler\PageErrorHandlerInterface;
 use TYPO3\CMS\Core\Http\HtmlResponse;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 use TYPO3\CMS\Extbase\Mvc\Controller\ControllerContext;
+use TYPO3\CMS\Extbase\Mvc\Request;
 use TYPO3\CMS\Extbase\Mvc\View\ViewInterface;
 use TYPO3\CMS\Extbase\Mvc\Web\Routing\UriBuilder;
 use TYPO3\CMS\Extbase\Object\ObjectManager;
@@ -58,7 +59,7 @@ class ErrorHandler implements PageErrorHandlerInterface
         $objectManager = GeneralUtility::makeInstance(ObjectManager::class);
         $controllerContext = $objectManager->get(ControllerContext::class);
         $renderingContext = $objectManager->get(RenderingContext::class);
-        $webRequest = $objectManager->get(\TYPO3\CMS\Extbase\Mvc\Request::class);
+        $webRequest = $objectManager->get(Request::class);
         $webRequest->setControllerExtensionName('ot_templating');
         $controllerContext->setRequest($webRequest);
         $renderingContext->setControllerContext($controllerContext);

+ 3 - 2
ot_templating/Classes/ViewHelpers/CObjectViewHelper.php

@@ -4,6 +4,7 @@ namespace Opentalent\OtTemplating\ViewHelpers;
 
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 use TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface;
+use TYPO3\CMS\Extbase\Reflection\ObjectAccess;
 use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
 use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
 use TYPO3Fluid\Fluid\Core\ViewHelper\Exception;
@@ -64,13 +65,13 @@ class CObjectViewHelper extends \TYPO3\CMS\Fluid\ViewHelpers\CObjectViewHelper {
         $typoscriptObjectPath = $arguments['typoscriptObjectPath'];
         $currentValueKey = $arguments['currentValueKey'];
         $table = $arguments['table'];
-        $contentObjectRenderer = static::getContentObjectRenderer();
+        $contentObjectRenderer = static::getContentObjectRenderer($renderingContext->getRequest());
         if (!isset($GLOBALS['TSFE']) || !($GLOBALS['TSFE'] instanceof TypoScriptFrontendController)) {
             static::simulateFrontendEnvironment();
         }
         $currentValue = null;
         if (is_object($data)) {
-            $data = \TYPO3\CMS\Extbase\Reflection\ObjectAccess::getGettableProperties($data);
+            $data = ObjectAccess::getGettableProperties($data);
         } elseif (is_string($data) || is_numeric($data)) {
             $currentValue = (string)$data;
             $data = [$data];

+ 2 - 1
ot_templating/Classes/ViewHelpers/Organizations/GetChildFederationViewHelper.php

@@ -4,6 +4,7 @@ namespace Opentalent\OtTemplating\ViewHelpers\Organizations;
 
 use FluidTYPO3\Vhs\Traits\TemplateVariableViewHelperTrait;
 use Opentalent\OtCore\Domain\Repository\FederationStructureRepository;
+use Opentalent\OtCore\Exception\ApiRequestException;
 use Opentalent\OtCore\ViewHelpers\OtAbstractViewHelper;
 
 /**
@@ -66,7 +67,7 @@ class GetChildFederationViewHelper extends OtAbstractViewHelper {
      * Renders the content as html
      *
      * @return string
-     * @throws \Opentalent\OtCore\Exception\ApiRequestException
+     * @throws ApiRequestException
      */
     public function render()
     {

+ 2 - 1
ot_templating/Classes/ViewHelpers/Organizations/GetFederationStructuresViewHelper.php

@@ -4,6 +4,7 @@ namespace Opentalent\OtTemplating\ViewHelpers\Organizations;
 
 use FluidTYPO3\Vhs\Traits\TemplateVariableViewHelperTrait;
 use Opentalent\OtCore\Domain\Repository\FederationStructureRepository;
+use Opentalent\OtCore\Exception\ApiRequestException;
 use Opentalent\OtCore\ViewHelpers\OtAbstractViewHelper;
 
 /**
@@ -73,7 +74,7 @@ class GetFederationStructuresViewHelper extends OtAbstractViewHelper {
      * Renders the content as html
      *
      * @return string
-     * @throws \Opentalent\OtCore\Exception\ApiRequestException
+     * @throws ApiRequestException
      */
     public function render()
     {

+ 5 - 4
ot_templating/Classes/ViewHelpers/Social/MetadataViewHelper.php

@@ -8,6 +8,7 @@ use Opentalent\OtCore\Exception\ApiRequestException;
 use Opentalent\OtCore\ViewHelpers\OtAbstractViewHelper;
 use TYPO3\CMS\Core\Database\ConnectionPool;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
+use TYPO3\CMS\Extbase\Service\ImageService;
 
 /**
  *   This view helper give access to the data needed by the Opengraph and twitter meta tags
@@ -34,14 +35,14 @@ class MetadataViewHelper extends OtAbstractViewHelper
     protected $escapeOutput = false;
 
     /**
-     * @var \TYPO3\CMS\Extbase\Service\ImageService
+     * @var ImageService
      */
-    protected \TYPO3\CMS\Extbase\Service\ImageService $imageService;
+    protected ImageService $imageService;
 
     /**
-     * @param \TYPO3\CMS\Extbase\Service\ImageService $imageService
+     * @param ImageService $imageService
      */
-    public function injectImageService(\TYPO3\CMS\Extbase\Service\ImageService $imageService)
+    public function injectImageService(ImageService $imageService)
     {
         $this->imageService = $imageService;
     }

+ 6 - 1
ot_templating/Classes/XClass/Form/Configuration/ConfigurationManager.php

@@ -2,10 +2,13 @@
 
 namespace Opentalent\OtTemplating\XClass\Form\Configuration;
 
+use Opentalent\OtCore\Exception\NoSuchWebsiteException;
 use Opentalent\OtCore\Website\OtPageRepository;
 use Opentalent\OtCore\Website\OtWebsiteRepository;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
+use TYPO3\CMS\Extbase\Object\Exception;
 use TYPO3\CMS\Extbase\Object\ObjectManager;
+use TYPO3\CMS\Form\Mvc\Configuration\Exception\ExtensionNameRequiredException;
 
 class ConfigurationManager extends \TYPO3\CMS\Form\Mvc\Configuration\ConfigurationManager
 {
@@ -15,7 +18,9 @@ class ConfigurationManager extends \TYPO3\CMS\Form\Mvc\Configuration\Configurati
      *
      * @param string $extensionName
      * @return array
-     * @throws \TYPO3\CMS\Form\Mvc\Configuration\Exception\ExtensionNameRequiredException
+     * @throws ExtensionNameRequiredException
+     * @throws NoSuchWebsiteException
+     * @throws Exception
      */
     protected function getConfigurationFromYamlFile(string $extensionName): array
     {

+ 25 - 0
ot_templating/Classes/XClass/Form/Finishers/EmailFinisher.php

@@ -0,0 +1,25 @@
+<?php
+
+namespace Opentalent\OtTemplating\XClass\Form\Finishers;
+
+use Symfony\Component\Mime\Address;
+
+class EmailFinisher extends \TYPO3\CMS\Form\Domain\Finishers\EmailFinisher
+{
+    /**
+     * Get mail recipients
+     *
+     * @param string $listOption List option name
+     * @return array
+     */
+    protected function getRecipients(string $listOption): array {
+        if ($listOption === 'recipients') {
+            $address = $this->parseOption($listOption) ?? null;
+
+            if ($address && is_string($address)) {
+                return [new Address($address, 'Contact')];
+            }
+        }
+        return parent::getRecipients($listOption);
+    }
+}

+ 1 - 1
ot_templating/Configuration/TypoScript/setup.txt

@@ -138,7 +138,7 @@ plugin.tx_form {
                 finishers {
                     0 {
                         options {
-                            recipientAddress = {$plugin.tx_ottemplating.settings.organization.email}
+                            recipients = {$plugin.tx_ottemplating.settings.organization.email}
                         }
                     }
                 }

+ 26 - 20
ot_templating/Resources/Private/Forms/Contact.form.yaml

@@ -13,28 +13,33 @@ finishers:
     identifier: EmailToReceiver
     options:
       subject: 'Informations request'
-      recipientAddress:
-      recipientName: ''
       senderAddress: ne-pas-repondre@opentalent.fr
       senderName: ''
-      replyToAddress: ''
-      carbonCopyAddress: ''
-      blindCarbonCopyAddress: ''
+      replyToRecipients:
+        - ''
+      carbonCopyRecipients:
+        - ''
+      blindCarbonCopyRecipients:
+        - ''
+      recipients: ''
       format: html
-      attachUploads: 'true'
+      attachUploads: true
       translation:
-        language: ''
+        language: default
   -
     identifier: EmailToSender
     options:
       subject: 'Your message has been posted'
-      recipientAddress: '{email}'
-      recipientName: '{firstname} {lastname}'
       senderAddress: ne_pas_repondre@opentalent.fr
       senderName: ''
-      replyToAddress: ''
-      carbonCopyAddress: ''
-      blindCarbonCopyAddress: ''
+      replyToRecipients:
+        - ''
+      carbonCopyRecipients:
+        - ''
+      blindCarbonCopyRecipients:
+        - ''
+      recipients:
+        '{email}': '{firstname} {lastname}'
       format: html
       attachUploads: 'false'
       templatePathAndFilename: 'EXT:ot_templating/Resources/Private/Templates/Forms/EmailToSender.html'
@@ -60,7 +65,7 @@ renderables:
         identifier: mandatory-fields-notice
         properties:
           text: 'Note: Fields marked with a star (*) are mandatory'
-          elementClassAttribute: 'contact-form-info'
+          elementClassAttribute: contact-form-info
         label: ''
       -
         type: GridRow
@@ -69,14 +74,15 @@ renderables:
         renderables:
           -
             identifier: lastname
-            label: 'Name'
+            label: Name
             type: Text
             defaultValue: ''
             properties:
               fluidAdditionalAttributes:
                 required: required
             validators:
-              - identifier: NotEmpty
+              -
+               identifier: NotEmpty
 
           -
             identifier: firstname
@@ -121,18 +127,18 @@ renderables:
 
           -
             identifier: city
-            label: 'City'
+            label: City
             type: Text
             defaultValue: ''
 
           -
             identifier: phone
-            label: 'Phone'
+            label: Phone
             type: Text
             defaultValue: ''
       -
         identifier: subject
-        label: 'Subject'
+        label: Subject
         type: Text
         defaultValue: ''
         properties:
@@ -143,7 +149,7 @@ renderables:
             identifier: NotEmpty
       -
         identifier: message
-        label: 'Message'
+        label: Message
         type: Textarea
         defaultValue: ''
         properties:
@@ -158,7 +164,7 @@ renderables:
         identifier: rgpd-notice
         properties:
           text: 'By submitting this form, you agree that theses informations can be processed in order to contact you back and answer your demand'
-          elementClassAttribute: 'contact-form-info'
+          elementClassAttribute: contact-form-info
         label: ''
       -
         validators:

+ 5 - 5
ot_templating/Resources/Private/Partials/Classic/Menu.html

@@ -9,8 +9,9 @@
 <f:comment><!--Navbar: Menu de premier niveau.
            Si les items ont des sous-pages, on leur donne la classe dropdown--></f:comment>
 <v:menu as="mainMenu"
-        classHasSubpages="dropdown"
-        pageUid="{ot:rootPage.getId()}">
+        pageUid="{ot:rootPage.getId()}"
+        >
+
     <div id="menu-container">
 
         <f:comment><!-- Button for mobile view, hidden by default --></f:comment>
@@ -21,7 +22,7 @@
         <ul id="menu">
             <f:for each="{mainMenu}" as="mainMenuItem">
 
-                <li class="{mainMenuItem.class} delayed-collapsing">
+                <li class="{mainMenuItem.class} first-level-menu delayed-collapsing">
 
                     <div class="menu-label">
                         <f:link.page pageUid="{mainMenuItem.uid}" title="{mainMenuItem.linktext}">
@@ -41,8 +42,7 @@
                     <f:if condition="{mainMenuItem.hasSubPages}">
 
                         <v:menu as="subMenu"
-                                pageUid="{mainMenuItem.uid}"
-                                classHasSubpages="dropdown">
+                                pageUid="{mainMenuItem.uid}">
 
                             <ul class="dropdown-menu">
 

+ 0 - 1
ot_templating/Resources/Private/Partials/Modern/Menu.html

@@ -41,7 +41,6 @@
                         <div class="menu-bar">
 
                             <v:menu as="mainMenu"
-                                    classHasSubpages="dropdown"
                                     classActive="active"
                                     pageUid="{ot:rootPage.getId()}">
 

+ 6 - 0
ot_templating/Resources/Public/assets/Classic/script/main.js

@@ -1,6 +1,12 @@
 $(document).ready(function(){
 
     // ******* Navbars & dropdowns *******
+    // add the 'dropdown' class on menus with children
+    $('.first-level-menu').each((i, menu) => {
+        if ($(menu).find('.subMenu')) {
+            $(menu).addClass('dropdown');
+        }
+    })
 
     // delay in ms before dropdown collapse after the cursor leave
     var collapsingDelay = 400;

+ 4 - 0
ot_templating/Resources/Public/assets/Modern/style/custom.css

@@ -62,6 +62,10 @@
         margin-bottom: 24px;
     }
 
+    /* @see https://assistance.opentalent.fr/browse/V8-2578 */
+    .form-inline {
+        margin-bottom: 6px;
+    }
     /*------------------------
         Barre réseau
     ------------------------*/

+ 4 - 5
ot_templating/composer.json

@@ -9,14 +9,13 @@
         }
     ],
     "require": {
-        "typo3/cms-core": "^9.5 || ^10.4",
+        "typo3/cms-core": "^11.5",
         "fluidtypo3/vhs": "^6",
         "fluidtypo3/flux": "^9",
-        "fluidtypo3/fluidpages": "^5",
-        "co-stack/logs": "",
-        "guzzlehttp/guzzle": "^6",
+        "co-stack/logs": "*",
+        "guzzlehttp/guzzle": "^7",
         "ext-json": "^1.6",
-        "sgalinski/lfeditor": "^6"
+        "sgalinski/lfeditor": "^7"
     },
     "replace": {
         "ot_templating": "self.version",

+ 0 - 3
ot_templating/ext_emconf.php

@@ -15,9 +15,6 @@ $EM_CONF[$_EXTKEY] = [
     'author' => 'Olivier Massot',
     'author_email' => 'olivier.massot@2iopenservice.fr',
     'state' => 'stable',
-    'uploadfolder' => 0,
-    'createDirs' => '',
-    'clearCacheOnLoad' => 0,
     'version' => '1.0.0',  //see ot_core/ext_emconf.php to get the current ot extensions version
     'constraints' => [
         'depends' => [

+ 4 - 1
ot_templating/ext_localconf.php

@@ -1,6 +1,6 @@
 <?php
 
-if (!defined('TYPO3_MODE')) {
+if (!defined('TYPO3')) {
     die('Access denied.');
 }
 
@@ -21,3 +21,6 @@ $GLOBALS['TYPO3_CONF_VARS']['EXT']['news']['Controller/NewsController.php']['cre
 $GLOBALS['TYPO3_CONF_VARS']['SYS']['Objects'][TYPO3\CMS\Form\Mvc\Configuration\ConfigurationManager::class] = [
     'className' => Opentalent\OtTemplating\XClass\Form\Configuration\ConfigurationManager::class
 ];
+$GLOBALS['TYPO3_CONF_VARS']['SYS']['Objects'][TYPO3\CMS\Form\Domain\Finishers\EmailFinisher::class] = [
+    'className' => Opentalent\OtTemplating\XClass\Form\Finishers\EmailFinisher::class
+];

+ 6 - 3
ot_templating/ext_tables.php

@@ -1,5 +1,8 @@
 <?php
-defined('TYPO3_MODE') || die('Access denied.');
+
+use Opentalent\OtTemplating\Controller\OtCustomizerController;
+
+defined('TYPO3') || die('Access denied.');
 
 // ext_tables.php contient les directives permettant de configurer le backend
 
@@ -17,12 +20,12 @@ call_user_func(
              * Registers the customizer backend module
              */
             \TYPO3\CMS\Extbase\Utility\ExtensionUtility::registerModule(
-                'Opentalent.OtTemplating',
+                'OtTemplating',
                 'web', // Make module a submodule of 'web'
                 'otcustomizer', // Submodule key
                 'before:OtStatsOtstats', // Position
                 array(
-                    'OtCustomizer' => 'index,selectTemplate,updatePreferences',
+                    OtCustomizerController::class => 'index,selectTemplate,updatePreferences',
                 ),
                 array(
                     'access' => 'user,group',