Browse Source

Merge branch 'release/1.0'

Olivier Massot 1 năm trước cách đây
mục cha
commit
79c4d037c9
100 tập tin đã thay đổi với 1265 bổ sung610 xóa
  1. 399 0
      .htaccess
  2. 37 47
      composer.json
  3. 3 3
      doc/docker.md
  4. 16 11
      doc/installation.md
  5. 3 3
      docker/clone-install.sh
  6. 8 0
      icon.svg
  7. 5 3
      ot_admin/Classes/Command/AddRedirectionCommand.php
  8. 2 2
      ot_admin/Classes/Command/ClearObsoleteWebsitesSiteCommand.php
  9. 2 2
      ot_admin/Classes/Command/ClearSiteCacheCommand.php
  10. 2 2
      ot_admin/Classes/Command/CreateSiteCommand.php
  11. 2 2
      ot_admin/Classes/Command/DeleteSiteCommand.php
  12. 2 2
      ot_admin/Classes/Command/GetSiteStatusCommand.php
  13. 2 2
      ot_admin/Classes/Command/RegenConfigFilesCommand.php
  14. 2 2
      ot_admin/Classes/Command/RemoveRedirectionCommand.php
  15. 2 2
      ot_admin/Classes/Command/ResetBeUserPermsCommand.php
  16. 2 2
      ot_admin/Classes/Command/ScanCommand.php
  17. 2 2
      ot_admin/Classes/Command/SetSiteDomainCommand.php
  18. 1 1
      ot_admin/Classes/Command/UndeleteSiteCommand.php
  19. 2 2
      ot_admin/Classes/Command/UpdateSiteCommand.php
  20. 28 25
      ot_admin/Classes/Controller/SiteController.php
  21. 1 4
      ot_admin/Classes/Http/ApiController.php
  22. 11 0
      ot_admin/Configuration/Icons.php
  23. BIN
      ot_admin/Resources/Public/Icons/Extension.png
  24. 8 1
      ot_admin/ext_tables.sql
  25. 0 2
      ot_connect/Classes/Middleware/RequestHandler.php
  26. 11 0
      ot_connect/Configuration/Icons.php
  27. BIN
      ot_connect/Resources/Public/Icons/Extension.png
  28. 0 7
      ot_core/Classes/Controller/SelectedSiteController.php
  29. 1 1
      ot_core/Classes/Domain/Repository/DonorRepository.php
  30. 1 3
      ot_core/Classes/Domain/Repository/FederationStructureRepository.php
  31. 7 4
      ot_core/Classes/Service/OpentalentApiService.php
  32. 1 1
      ot_core/Classes/Service/OpentalentEnvService.php
  33. 33 0
      ot_core/Classes/Service/OpentalentImageService.php
  34. 28 15
      ot_core/Classes/Website/OtWebsiteRepository.php
  35. 11 0
      ot_core/Configuration/Icons.php
  36. BIN
      ot_core/Resources/Public/Icons/Extension.png
  37. BIN
      ot_core/Resources/Public/Icons/Extension_white.png
  38. 0 0
      ot_core/Tests/Build/.phpunit.result.cache
  39. 0 1
      ot_core/Tests/Build/UnitTests.xml
  40. 6 5
      ot_core/Tests/Unit/Controller/SelectedSiteControllerTest.php
  41. 2 2
      ot_core/Tests/Unit/Domain/Model/DonorTest.php
  42. 2 2
      ot_core/Tests/Unit/Domain/Model/EventTest.php
  43. 1 1
      ot_core/Tests/Unit/Domain/Model/MemberTest.php
  44. 2 3
      ot_core/Tests/Unit/Domain/Model/OrganizationTest.php
  45. 2 4
      ot_core/Tests/Unit/Domain/Repository/AbstractApiRepositoryTestCase.php
  46. 2 2
      ot_core/Tests/Unit/Domain/Repository/ApiPagedCollectionTest.php
  47. 2 2
      ot_core/Tests/Unit/Exception/ApiRequestExceptionTest.php
  48. 19 0
      ot_core/Tests/Unit/OtUnitTestCase.php
  49. 2 3
      ot_core/Tests/Unit/Service/OpentalentApiServiceTest.php
  50. 2 2
      ot_core/Tests/Unit/Service/OpentalentEnvServiceTest.php
  51. 2 2
      ot_core/Tests/Unit/Website/OtPageRepositoryTest.php
  52. 2 3
      ot_core/Tests/Unit/Website/OtWebsiteRepositoryTest.php
  53. 4 8
      ot_core/composer.json
  54. 1 1
      ot_core/ext_emconf.php
  55. 2 0
      ot_core/ext_localconf.php
  56. 11 0
      ot_optimizer/Configuration/Icons.php
  57. 2 2
      ot_optimizer/Configuration/RequestMiddlewares.php
  58. BIN
      ot_optimizer/Resources/Public/Icons/Extension.png
  59. 3 5
      ot_stats/Classes/Domain/Repository/MatomoWebsiteRepository.php
  60. 27 0
      ot_stats/Configuration/Backend/Modules.php
  61. 15 0
      ot_stats/Configuration/Icons.php
  62. 7 6
      ot_stats/Resources/Private/Layouts/Backend/Default.html
  63. BIN
      ot_stats/Resources/Public/Icons/Extension.png
  64. BIN
      ot_stats/Resources/Public/Icons/Extension_white.png
  65. 0 32
      ot_stats/ext_tables.php
  66. 2 2
      ot_templating/Classes/Controller/OtCustomizerController.php
  67. 135 29
      ot_templating/Classes/ViewHelpers/CObjectViewHelper.php
  68. 65 0
      ot_templating/Classes/ViewHelpers/Image/GetSrcByIdViewHelper.php
  69. 59 0
      ot_templating/Classes/ViewHelpers/Image/GetSrcByPathViewHelper.php
  70. 0 49
      ot_templating/Classes/ViewHelpers/ImagePViewHelper.php
  71. 2 11
      ot_templating/Classes/ViewHelpers/Page/GetFirstWithTemplateViewHelper.php
  72. 25 0
      ot_templating/Configuration/Backend/Modules.php
  73. 6 1
      ot_templating/Configuration/Icons.php
  74. 3 0
      ot_templating/Configuration/Services.yaml
  75. 1 1
      ot_templating/Configuration/TypoScript/constants.typoscript
  76. 8 0
      ot_templating/Configuration/TypoScript/setup.typoscript
  77. 3 0
      ot_templating/Resources/Private/Language/locallang.xlf
  78. 4 5
      ot_templating/Resources/Private/Layouts/Backend/Default.html
  79. 1 1
      ot_templating/Resources/Private/Layouts/Classic/StructureDetails.html
  80. 1 1
      ot_templating/Resources/Private/Layouts/Modern/StructureDetails.html
  81. 28 40
      ot_templating/Resources/Private/Partials/Classic/Assets.html
  82. 1 1
      ot_templating/Resources/Private/Partials/Classic/Carousel.html
  83. 1 1
      ot_templating/Resources/Private/Partials/Classic/EventsIndex.html
  84. 11 0
      ot_templating/Resources/Private/Partials/Classic/Footer.html
  85. 1 1
      ot_templating/Resources/Private/Partials/Classic/MembersList.html
  86. 1 1
      ot_templating/Resources/Private/Partials/Classic/Topbar.html
  87. 2 1
      ot_templating/Resources/Private/Partials/Classic/UserToolbar.html
  88. 99 158
      ot_templating/Resources/Private/Partials/Modern/Assets.html
  89. 1 1
      ot_templating/Resources/Private/Partials/Modern/Carousel.html
  90. 11 0
      ot_templating/Resources/Private/Partials/Modern/Footer.html
  91. 1 1
      ot_templating/Resources/Private/Partials/Modern/MembersList.html
  92. 1 1
      ot_templating/Resources/Private/Partials/Modern/Menu.html
  93. 16 16
      ot_templating/Resources/Private/Templates/OtCustomizer/Index.html
  94. 9 3
      ot_templating/Resources/Private/Templates/Page/Error/403.html
  95. 6 4
      ot_templating/Resources/Private/Templates/Page/Error/404.html
  96. 3 1
      ot_templating/Resources/Private/Templates/Page/Error/500.html
  97. 2 1
      ot_templating/Resources/Public/assets/Backend/style/ot_customizer.css
  98. 1 0
      ot_templating/Resources/Public/assets/Classic/style/module/_members.scss
  99. 2 2
      ot_templating/ext_localconf.php
  100. 0 38
      ot_templating/ext_tables.php

+ 399 - 0
.htaccess

@@ -0,0 +1,399 @@
+
+SetEnv TYPO3_CONTEXT Development
+
+# Enable / Disable the opentalent ot_optimizer middlewares
+SetEnv TYPO3_OPTIMIZE 1
+
+#####
+#
+# Example .htaccess file for TYPO3 CMS - for use with Apache Webserver
+#
+# This file includes settings for the following configuration options:
+#
+# - Compression
+# - Caching
+# - MIME types
+# - Cross Origin requests
+# - Rewriting and Access
+# - Miscellaneous
+# - PHP optimisation
+#
+# If you want to use it, you have to copy it to the root folder of your TYPO3 installation (if its
+# not there already) and rename it to '.htaccess'. To make .htaccess files work, you might need to
+# adjust the 'AllowOverride' directive in your Apache configuration file.
+#
+# IMPORTANT: You may need to change this file depending on your TYPO3 installation!
+#            Consider adding this file's content to your webserver's configuration directly for speed improvement
+#
+# Lots of the options are taken from https://github.com/h5bp/html5-boilerplate/blob/master/dist/.htaccess
+#
+####
+
+
+### Begin: Compression ###
+
+# Compressing resource files will save bandwidth and so improve loading speed especially for users
+# with slower internet connections. TYPO3 can compress the .js and .css files for you.
+# *) Uncomment the following lines and
+# *) Set $GLOBALS['TYPO3_CONF_VARS']['BE']['compressionLevel'] = 9 for the Backend
+# *) Set $GLOBALS['TYPO3_CONF_VARS']['FE']['compressionLevel'] = 9 together with the TypoScript properties
+#    config.compressJs and config.compressCss for GZIP compression of Frontend JS and CSS files.
+
+<FilesMatch "\.js\.gz">
+	AddType "text/javascript" .gz
+</FilesMatch>
+<FilesMatch "\.css\.gz">
+	AddType "text/css" .gz
+</FilesMatch>
+AddEncoding x-gzip .gz
+
+<IfModule mod_deflate.c>
+	# Force compression for mangled `Accept-Encoding` request headers
+	<IfModule mod_setenvif.c>
+		<IfModule mod_headers.c>
+			SetEnvIfNoCase ^(Accept-EncodXng|X-cept-Encoding|X{15}|~{15}|-{15})$ ^((gzip|deflate)\s*,?\s*)+|[X~-]{4,13}$ HAVE_Accept-Encoding
+			RequestHeader append Accept-Encoding "gzip,deflate" env=HAVE_Accept-Encoding
+		</IfModule>
+	</IfModule>
+
+	# Compress all output labeled with one of the following media types.
+	#
+	# (!) For Apache versions below version 2.3.7 you don't need to
+	# enable `mod_filter` and can remove the `<IfModule mod_filter.c>`
+	# and `</IfModule>` lines as `AddOutputFilterByType` is still in
+	# the core directives.
+	#
+	# https://httpd.apache.org/docs/current/mod/mod_filter.html#addoutputfilterbytype
+
+	<IfModule mod_filter.c>
+		AddOutputFilterByType DEFLATE application/atom+xml \
+			application/javascript \
+			application/json \
+			application/ld+json \
+			application/manifest+json \
+			application/rdf+xml \
+			application/rss+xml \
+			application/schema+json \
+			application/vnd.geo+json \
+			application/geo+json \
+			application/vnd.ms-fontobject \
+			application/x-font-ttf \
+			application/x-javascript \
+			application/x-web-app-manifest+json \
+			application/xhtml+xml \
+			application/xml \
+			font/eot \
+			font/opentype \
+			font/otf \
+			font/ttf \
+			image/bmp \
+			image/svg+xml \
+			image/vnd.microsoft.icon \
+			image/x-icon \
+			text/cache-manifest \
+			text/css \
+			text/html \
+			text/javascript \
+			text/plain \
+			text/vcard \
+			text/vnd.rim.location.xloc \
+			text/vtt \
+			text/x-component \
+			text/x-cross-domain-policy \
+			text/xml
+	</IfModule>
+
+	<IfModule mod_mime.c>
+		AddEncoding gzip svgz
+	</IfModule>
+</IfModule>
+
+### End: Compression ###
+
+
+
+### Begin: Browser caching of resource files ###
+
+# This affects Frontend and Backend and increases performance.
+<IfModule mod_expires.c>
+
+	ExpiresActive On
+	ExpiresDefault                                      "access plus 1 month"
+
+	ExpiresByType text/css                              "access plus 1 year"
+
+	ExpiresByType application/json                      "access plus 0 seconds"
+	ExpiresByType application/ld+json                   "access plus 0 seconds"
+	ExpiresByType application/schema+json               "access plus 0 seconds"
+	ExpiresByType application/vnd.geo+json              "access plus 0 seconds"
+	ExpiresByType application/geo+json                  "access plus 0 seconds"
+	ExpiresByType application/xml                       "access plus 0 seconds"
+	ExpiresByType text/xml                              "access plus 0 seconds"
+
+	ExpiresByType image/vnd.microsoft.icon              "access plus 1 week"
+	ExpiresByType image/x-icon                          "access plus 1 week"
+
+	ExpiresByType text/x-component                      "access plus 1 month"
+
+	ExpiresByType text/html                             "access plus 0 seconds"
+
+	ExpiresByType application/javascript                "access plus 1 year"
+	ExpiresByType application/x-javascript              "access plus 1 year"
+	ExpiresByType text/javascript                       "access plus 1 year"
+
+	ExpiresByType application/manifest+json             "access plus 1 week"
+	ExpiresByType application/x-web-app-manifest+json   "access plus 0 seconds"
+	ExpiresByType text/cache-manifest                   "access plus 0 seconds"
+
+	ExpiresByType audio/ogg                             "access plus 1 month"
+	ExpiresByType image/apng                            "access plus 1 month"
+	ExpiresByType image/avif                            "access plus 1 month"
+	ExpiresByType image/avif-sequence                   "access plus 1 month"
+	ExpiresByType image/bmp                             "access plus 1 month"
+	ExpiresByType image/gif                             "access plus 1 month"
+	ExpiresByType image/jpeg                            "access plus 1 month"
+	ExpiresByType image/jxl                             "access plus 1 month"
+	ExpiresByType image/png                             "access plus 1 month"
+	ExpiresByType image/svg+xml                         "access plus 1 month"
+	ExpiresByType image/webp                            "access plus 1 month"
+	ExpiresByType video/mp4                             "access plus 1 month"
+	ExpiresByType video/ogg                             "access plus 1 month"
+	ExpiresByType video/webm                            "access plus 1 month"
+
+	ExpiresByType application/atom+xml                  "access plus 1 hour"
+	ExpiresByType application/rdf+xml                   "access plus 1 hour"
+	ExpiresByType application/rss+xml                   "access plus 1 hour"
+
+	ExpiresByType font/collection                       "access plus 1 month"
+	ExpiresByType application/vnd.ms-fontobject         "access plus 1 month"
+	ExpiresByType font/eot                              "access plus 1 month"
+	ExpiresByType font/opentype                         "access plus 1 month"
+	ExpiresByType font/otf                              "access plus 1 month"
+	ExpiresByType application/x-font-ttf                "access plus 1 month"
+	ExpiresByType font/ttf                              "access plus 1 month"
+	ExpiresByType application/font-woff                 "access plus 1 month"
+	ExpiresByType application/x-font-woff               "access plus 1 month"
+	ExpiresByType font/woff                             "access plus 1 month"
+	ExpiresByType application/font-woff2                "access plus 1 month"
+	ExpiresByType font/woff2                            "access plus 1 month"
+
+	ExpiresByType text/x-cross-domain-policy            "access plus 1 week"
+
+</IfModule>
+
+### End: Browser caching of resource files ###
+
+
+### Begin: MIME types ###
+
+# Proper MIME types for all files
+<IfModule mod_mime.c>
+	# Security configuration
+	RemoveType .html .htm
+	<FilesMatch ".+\.html?$">
+		AddType text/html .html .htm
+	</FilesMatch>
+
+	RemoveType .svg .svgz
+	<FilesMatch ".+\.svgz?$">
+		AddType image/svg+xml .svg .svgz
+	</FilesMatch>
+
+	# Data interchange
+	AddType application/atom+xml                        atom
+	AddType application/json                            json map topojson
+	AddType application/ld+json                         jsonld
+	AddType application/rss+xml                         rss
+	AddType application/vnd.geo+json                    geojson
+	AddType application/xml                             rdf xml
+
+	# JavaScript
+	AddType application/javascript                      js
+
+	# Manifest files
+	AddType application/manifest+json                   webmanifest
+	AddType application/x-web-app-manifest+json         webapp
+	AddType text/cache-manifest                         appcache
+
+	# Media files
+
+	AddType audio/mp4                                   f4a f4b m4a
+	AddType audio/ogg                                   oga ogg opus
+	AddType image/avif                                  avif
+	AddType image/avif-sequence                         avifs
+	AddType image/bmp                                   bmp
+	AddType image/jxl                                   jxl
+	AddType image/webp                                  webp
+	AddType video/mp4                                   f4v f4p m4v mp4
+	AddType video/ogg                                   ogv
+	AddType video/webm                                  webm
+	AddType video/x-flv                                 flv
+	AddType image/x-icon                                cur ico
+
+	# Web fonts
+	AddType font/woff                                   woff
+	AddType font/woff2                                  woff2
+	AddType application/vnd.ms-fontobject               eot
+	AddType font/ttf                                    ttc ttf
+	AddType font/otf                                    otf
+
+	# Other
+	AddType application/octet-stream                    safariextz
+	AddType application/x-bb-appworld                   bbaw
+	AddType application/x-chrome-extension              crx
+	AddType application/x-opera-extension               oex
+	AddType application/x-xpinstall                     xpi
+	AddType text/vcard                                  vcard vcf
+	AddType text/vnd.rim.location.xloc                  xloc
+	AddType text/vtt                                    vtt
+	AddType text/x-component                            htc
+
+</IfModule>
+
+# UTF-8 encoding
+AddDefaultCharset utf-8
+<IfModule mod_mime.c>
+	AddCharset utf-8 .atom .css .js .json .manifest .rdf .rss .vtt .webapp .webmanifest .xml
+</IfModule>
+
+### End: MIME types ###
+
+
+
+### Begin: Cross Origin ###
+
+# Send the CORS header for images when browsers request it.
+<IfModule mod_setenvif.c>
+	<IfModule mod_headers.c>
+		<FilesMatch "\.(avifs?|bmp|cur|gif|ico|jpe?g|png|svgz?|webp)$">
+			SetEnvIf Origin ":" IS_CORS
+			Header set Access-Control-Allow-Origin "*" env=IS_CORS
+		</FilesMatch>
+	</IfModule>
+</IfModule>
+
+# Allow cross-origin access to web fonts.
+<IfModule mod_headers.c>
+	<FilesMatch "\.(eot|otf|tt[cf]|woff2?)$">
+		Header set Access-Control-Allow-Origin "*"
+	</FilesMatch>
+</IfModule>
+
+### End: Cross Origin ###
+
+
+
+### Begin: Rewriting and Access ###
+
+<IfModule mod_rewrite.c>
+
+	# Enable URL rewriting
+	RewriteEngine On
+
+	# Store the current location in an environment variable CWD to use
+	# mod_rewrite in .htaccess files without knowing the RewriteBase
+	RewriteCond $0#%{REQUEST_URI} ([^#]*)#(.*)\1$
+	RewriteRule ^.*$ - [E=CWD:%2]
+
+	# Rules to set ApplicationContext based on hostname
+	#RewriteCond %{HTTP_HOST} ^dev\.example\.com$
+	#RewriteRule .? - [E=TYPO3_CONTEXT:Development]
+	#RewriteCond %{HTTP_HOST} ^staging\.example\.com$
+	#RewriteRule .? - [E=TYPO3_CONTEXT:Production/Staging]
+	#RewriteCond %{HTTP_HOST} ^www\.example\.com$
+	#RewriteRule .? - [E=TYPO3_CONTEXT:Production]
+
+	# Rule for versioned static files, configured through:
+	# - $GLOBALS['TYPO3_CONF_VARS']['BE']['versionNumberInFilename']
+	# - $GLOBALS['TYPO3_CONF_VARS']['FE']['versionNumberInFilename']
+	# IMPORTANT: This rule has to be the very first RewriteCond in order to work!
+	RewriteCond %{REQUEST_FILENAME} !-f
+	RewriteCond %{REQUEST_FILENAME} !-d
+	RewriteRule ^(.+)\.(\d+)\.(php|js|css|png|jpg|gif|gz)$ %{ENV:CWD}$1.$3 [L]
+
+	# Access block for folders
+	RewriteRule _(?:recycler|temp)_/ - [F]
+	RewriteRule fileadmin/templates/.*\.(?:txt|ts)$ - [F]
+	RewriteRule ^(?:vendor|typo3_src|typo3temp/var) - [F]
+	RewriteRule (?:typo3conf/ext|typo3/sysext|typo3/ext)/[^/]+/(?:Configuration|Resources/Private|Tests?|Documentation|docs?)/ - [F]
+
+	# Block access to all hidden files and directories with the exception of
+	# the visible content from within the `/.well-known/` hidden directory (RFC 5785).
+	RewriteCond %{REQUEST_URI} "!(^|/)\.well-known/([^./]+./?)+$" [NC]
+	RewriteCond %{SCRIPT_FILENAME} -d [OR]
+	RewriteCond %{SCRIPT_FILENAME} -f
+	RewriteRule (?:^|/)\. - [F]
+
+	# Stop rewrite processing, if we are in any other known directory
+	# NOTE: Add your additional local storages here
+	RewriteRule ^(?:fileadmin/|typo3conf/|typo3temp/|uploads/) - [L]
+
+	# If the file/symlink/directory does not exist but is below /typo3/, redirect to the TYPO3 Backend entry point.
+	RewriteCond %{REQUEST_FILENAME} !-f
+	RewriteCond %{REQUEST_FILENAME} !-d
+	RewriteCond %{REQUEST_FILENAME} !-l
+	RewriteRule ^typo3/(.*)$ %{ENV:CWD}typo3/index.php [QSA,L]
+
+	# If the file/symlink/directory does not exist => Redirect to index.php.
+	# For httpd.conf, you need to prefix each '%{REQUEST_FILENAME}' with '%{DOCUMENT_ROOT}'.
+	RewriteCond %{REQUEST_FILENAME} !-f
+	RewriteCond %{REQUEST_FILENAME} !-d
+	RewriteCond %{REQUEST_FILENAME} !-l
+	RewriteRule ^.*$ %{ENV:CWD}index.php [QSA,L]
+
+</IfModule>
+
+# Access block for files
+# Apache < 2.3
+<IfModule !mod_authz_core.c>
+	<FilesMatch "(?i:^\.|^#.*#|^(?:ChangeLog|ToDo|Readme|License)(?:\.md|\.txt)?|^composer\.(?:json|lock)|^ext_conf_template\.txt|^ext_typoscript_constants\.txt|^ext_typoscript_setup\.txt|flexform[^.]*\.xml|locallang[^.]*\.(?:xml|xlf)|\.(?:bak|co?nf|cfg|ya?ml|ts|typoscript|tsconfig|dist|fla|in[ci]|log|sh|sql(?:\..*)?|sqlite(?:\..*)?|sw[op]|git.*|rc)|.*~)$">
+		Order allow,deny
+		Deny from all
+		Satisfy All
+	</FilesMatch>
+</IfModule>
+# Apache ≥ 2.3
+<IfModule mod_authz_core.c>
+	<If "%{REQUEST_URI} =~ m#(?i:/\.|/\x23.*\x23|/(?:ChangeLog|ToDo|Readme|License)(?:\.md|\.txt)?|/composer\.(?:json|lock)|/ext_conf_template\.txt|/ext_typoscript_constants\.txt|/ext_typoscript_setup\.txt|flexform[^.]*\.xml|locallang[^.]*\.(?:xml|xlf)|\.(?:bak|co?nf|cfg|ya?ml|ts|typoscript|tsconfig|dist|fla|in[ci]|log|sh|sql(?:\..*)?|sqlite(?:\..*)?|sw[op]|git.*|rc)|.*~)$#">
+		Require all denied
+	</If>
+</IfModule>
+
+# Block access to vcs directories
+<IfModule mod_alias.c>
+	RedirectMatch 404 /\.(?:git|svn|hg)/
+</IfModule>
+
+### End: Rewriting and Access ###
+
+
+
+### Begin: Miscellaneous ###
+
+# 404 error prevention for non-existing redirected folders
+Options -MultiViews
+
+# Make sure that directory listings are disabled.
+<IfModule mod_autoindex.c>
+	Options -Indexes
+</IfModule>
+
+<IfModule mod_headers.c>
+	# Force IE to render pages in the highest available mode
+	Header set X-UA-Compatible "IE=edge"
+	<FilesMatch "\.(appcache|avifs?|crx|css|eot|gif|htc|ico|jpe?g|js|m4a|m4v|manifest|mp4|oex|oga|ogg|ogv|otf|pdf|png|safariextz|svgz?|ttf|vcf|webapp|webm|webp|woff2?|xml|xpi)$">
+		Header unset X-UA-Compatible
+	</FilesMatch>
+
+	# Reducing MIME type security risks
+	Header set X-Content-Type-Options "nosniff"
+</IfModule>
+
+# ETag removal
+<IfModule mod_headers.c>
+	Header unset ETag
+</IfModule>
+FileETag None
+
+### End: Miscellaneous ###
+

+ 37 - 47
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,22 +54,12 @@
     ],
     "scripts": {
         "typo3-cms-scripts": [
-            "typo3cms install:fixfolderstructure"
+            "typo3 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,
@@ -76,7 +67,6 @@
         }
     },
     "require-dev": {
-        "ssch/typo3-rector": "^1.3",
         "phpspec/prophecy": "^1.19"
     }
 }

+ 3 - 3
doc/docker.md

@@ -16,18 +16,18 @@ répertoire `docker/typo3` qui **doit être ignoré par git**
 
 De sorte que l'on pourra monter séparément comme volumes docker les extensions (versionnées) et l'installation typo3 (non-versionnée).
 
-L'installation sur le docker suit ensuite [le même principe que celle qui existe sur les serveurs](installation.md) de prod et de preprod
+L'installation sur le docker suit ensuite [le même principe que celle qui existe sur les serveurs](installation.md) de prod et de test.
 
 
 
 ## Répliquer l'instance Typo3 de la prod vers docker
 
-Pour répliquer manuellement l'installation Typo3 telle qu'elle existe sur preprod:
+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
 

+ 16 - 11
doc/installation.md

@@ -14,24 +14,29 @@ Typo3 est installé sur prod-front et sur preprod en double instance:
 
 **Important: Pour la suite de cette documentation, on ne parlera plus que de l'installation la plus récente (v10.4)**
 
-
 Les [extensions Opentalent](..) sont installées dans: `/var/opentalent/git/ot_typo3`
 
-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/`
+## Première installation
+
+On créé un répertoire packages qui contiendra des liens symboliques vers les extensions : 
 
-    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
+    cd /var/www/typo3
+    mkdir packages
+    ln -s /var/opentalent/git/ot_typo3/ot_admin packages/ot_admin
+    ln -s /var/opentalent/git/ot_typo3/ot_connect packages/ot_connect
+    ln -s /var/opentalent/git/ot_typo3/ot_core packages/ot_core
+    ln -s /var/opentalent/git/ot_typo3/ot_stats packages/ot_stats
+    ln -s /var/opentalent/git/ot_typo3/ot_optimizer packages/ot_optimizer
+    ln -s /var/opentalent/git/ot_typo3/ot_templating packages/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.
+On créé un lien symbolique 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
 
+Puis on installe : 
+
+    php composer.phar install
 
 ## Opérations courantes
 

+ 3 - 3
docker/clone-install.sh

@@ -1,8 +1,8 @@
 
-# This script will compress the typo3 installation directory on preprod, download it and extract it here, in the typo3 subdirectory
+# This script will compress the typo3 installation directory on test, download it and extract it here, in the typo3 subdirectory
 # <!> this does not clone the database, use clonedb if needed
-ssh exploitation@preprod "cd /var/www/typo3/ ; tar cvzf ./typo3_install.tar.gz . --exclude=./public/fileadmin/user_upload/* --exclude=./archive --exclude=./public/fileadmin/_processed_ --exclude=./var/log/* --exclude=./var/cache/*" || exit
-scp exploitation@preprod:/var/www/typo3/typo3_install.tar.gz . || exit
+ssh exploitation@test "cd /var/www/typo3/ ; tar cvzf ./typo3_install.tar.gz . --exclude=./public/fileadmin/user_upload/* --exclude=./archive --exclude=./public/fileadmin/_processed_ --exclude=./var/log/* --exclude=./var/cache/*" || exit
+scp exploitation@test:/var/www/typo3/typo3_install.tar.gz . || exit
 rm -r ./typo3 || exit
 mkdir ./typo3
 echo "*" >> ./typo3/.gitignore

+ 8 - 0
icon.svg

@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
+<svg width="800px" height="800px" viewBox="-2 0 260 260" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid">
+    <g>
+        <path d="M109.525333,4.05333333 C104.810667,8.08533333 101.461333,12.8 101.461333,26.88 C101.461333,65.1946667 149.824,180.288 182.762667,180.288 C186.462268,180.338187 190.147176,179.812799 193.685333,178.730667 L193.616,178.75 L192.774258,180.100166 C164.346546,225.411559 130.133077,258.650903 109.429541,259.317782 L108.8,259.328 C63.8293333,259.328 0,123.562667 0,63.8293333 C0,54.4213333 2.13333333,47.04 5.376,42.4533333 C20.8426667,23.552 69.2053333,8.74666667 109.525333,4.05333333 Z M172.672,0 C214.314667,0 256,6.72 256,30.2293333 C256,77.9306667 225.749333,135.744 210.304,135.744 C182.762667,135.744 148.437333,59.136 148.437333,20.8213333 C148.437333,3.34933333 155.136,0 172.608,0 L172.672,0 Z" fill="#F49700">
+
</path>
+    </g>
+</svg>

+ 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();
     }

+ 1 - 4
ot_admin/Classes/Http/ApiController.php

@@ -15,7 +15,6 @@ use Psr\Log\LoggerAwareTrait;
 use TYPO3\CMS\Core\Http\JsonResponse;
 use TYPO3\CMS\Core\Http\ServerRequest;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
-use TYPO3\CMS\Extbase\Object\ObjectManager;
 
 /**
  * Actions for Http API calls
@@ -38,9 +37,7 @@ class ApiController implements LoggerAwareInterface
     private readonly SiteController $siteController;
 
     public function __construct() {
-        // TODO: trouver une solution pour faire fonctionner l'injection de dépendances
-        $objectManager = GeneralUtility::makeInstance(ObjectManager::class);
-        $this->siteController = $objectManager->get(SiteController::class);
+        $this->siteController = GeneralUtility::makeInstance(SiteController::class);
     }
 
     /**

+ 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
      */

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

@@ -65,7 +65,7 @@ class DonorRepository extends BaseApiRepository
         $donor->setWebsite($record['website']);
         $donor->setWording($record['wording']);
         $donor->setDisplayedOn($record['displayedOn']);
-        $donor->setLogo($record['logo'] ?? null);
+        $donor->setLogo($record['logo']);
 
         return $donor;
     }

+ 1 - 3
ot_core/Classes/Domain/Repository/FederationStructureRepository.php

@@ -62,9 +62,7 @@ class FederationStructureRepository extends BaseApiRepository
         $federationStructure->setLongitude($record['longitude']);
         $federationStructure->setCountry($record['country']);
         $federationStructure->setCategories($record['categories']);
-        if ($record['logoId']) {
-            $federationStructure->setLogoUri($this->apiService->getApiUri('_internal/secure/files/') . $record['logoId']);
-        }
+        $federationStructure->setLogoUri($record['logoId']);
         $federationStructure->setParentId($record['n1Id']);
         $federationStructure->setParentName($record['n1Name']);
         $federationStructure->setParents($record['parents']);

+ 7 - 4
ot_core/Classes/Service/OpentalentApiService.php

@@ -57,11 +57,12 @@ class OpentalentApiService
      * Return the API URI for the current repository
      *
      * @param string $trailing_part
+     * @param bool $public Si vrai, retourne l'url publique et non interne
      * @return string
      */
-    public function getApiUri(string $trailing_part = ""): string
+    public function getApiUri(string $trailing_part = "", bool $public = false): string
     {
-        $uri = OpentalentEnvService::get('API_BASE_URI');
+        $uri = OpentalentEnvService::get($public ? 'PUBLIC_API_BASE_URI' : 'API_BASE_URI');
         return rtrim($uri, '/') . '/' . ltrim($trailing_part, '/');
     }
 
@@ -95,15 +96,17 @@ class OpentalentApiService
      *
      * @param string $uri
      * @param array $params
+     * @param array $options
      * @return string
      * @throws ApiRequestException
      */
     public function getBody(
         string $uri,
-        array $params = []
+        array $params = [],
+        array $options = []
     ): string
     {
-        return (string)$this->get($uri, $params)->getBody();
+        return (string)$this->get($uri, $params, $options)->getBody();
     }
 
     /**

+ 1 - 1
ot_core/Classes/Service/OpentalentEnvService.php

@@ -18,7 +18,7 @@ class OpentalentEnvService
 {
     public static function get($varname) {
         if (!array_key_exists($varname, $GLOBALS['OT'])) {
-            throw new \RuntimeException('Un-existing OT environment variable requested: ' . $varname);
+            throw new \RuntimeException('Non-existing OT environment variable requested: ' . $varname);
         }
         return $GLOBALS['OT'][$varname];
     }

+ 33 - 0
ot_core/Classes/Service/OpentalentImageService.php

@@ -0,0 +1,33 @@
+<?php
+
+namespace Opentalent\OtCore\Service;
+
+use TYPO3\CMS\Core\Utility\GeneralUtility;
+
+class OpentalentImageService
+{
+    public const IMAGE_SM = 'sm';
+    public const IMAGE_MD = 'md';
+    public const IMAGE_LG = 'lg';
+
+    private OpentalentApiService $apiService;
+
+    public function __construct(
+        OpentalentApiService $apiService = null
+    )
+    {
+        $this->apiService = $apiService ?? GeneralUtility::makeInstance(OpentalentApiService::class);
+    }
+
+    public function getImageUrl(int $fileId, string $size = self::IMAGE_MD): string {
+        if (!in_array($size, [self::IMAGE_SM, self::IMAGE_MD, self::IMAGE_LG])) {
+            throw new \RuntimeException('Invalid $size : ' . $size);
+        }
+
+        $url = $this->apiService->getApiUri("api/public/files/$fileId/download/$size");
+
+        $path = $this->apiService->getBody($url, ['relativePath' => true]);
+
+        return $this->apiService->getApiUri($path, true);
+    }
+}

+ 28 - 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);
         }
@@ -283,6 +294,7 @@ class OtWebsiteRepository
                               ],
                 'rootPageId' => $rootUid,
                 'routes' => [],
+                'contentSecurityPolicies' => [], // En attendant résolution de https://review.typo3.org/c/Packages/TYPO3.CMS/+/85954
             ]
         );
     }
@@ -365,8 +377,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 +387,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 +414,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


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 0 - 0
ot_core/Tests/Build/.phpunit.result.cache


+ 0 - 1
ot_core/Tests/Build/UnitTests.xml

@@ -1,7 +1,6 @@
 <phpunit
         backupGlobals="true"
         backupStaticAttributes="false"
-        bootstrap="../../.Build/vendor/nimut/testing-framework/res/Configuration/UnitTestsBootstrap.php"
         colors="true"
         convertErrorsToExceptions="true"
         convertWarningsToExceptions="false"

+ 6 - 5
ot_core/Tests/Unit/Controller/SelectedSiteControllerTest.php

@@ -2,7 +2,6 @@
 
 namespace Opentalent\OtCore\Tests\Unit\Controller;
 
-use Nimut\TestingFramework\TestCase\UnitTestCase;
 use Opentalent\OtCore\Controller\SelectedSiteController;
 use Opentalent\OtCore\Exception\NoSiteSelected;
 use Opentalent\OtCore\Website\OtPageRepository;
@@ -10,6 +9,7 @@ use Opentalent\OtCore\Website\OtWebsiteRepository;
 use PHPUnit\Framework\MockObject\MockObject;
 use Psr\Http\Message\ResponseInterface;
 use TYPO3\CMS\Extbase\Mvc\RequestInterface;
+use Opentalent\OtCore\Tests\Unit\OtUnitTestCase;
 
 class TestableSelectedSiteController extends SelectedSiteController {
     public $currentWebsite;
@@ -26,7 +26,7 @@ class TestableSelectedSiteController extends SelectedSiteController {
     }
 }
 
-class SelectedSiteControllerTest extends UnitTestCase
+class SelectedSiteControllerTest extends OtUnitTestCase
 {
     protected MockObject | OtPageRepository $otPageRepository;
     protected MockObject | OtWebsiteRepository $otWebsiteRepository;
@@ -46,9 +46,10 @@ class SelectedSiteControllerTest extends UnitTestCase
 
     public function getMockForMethod(string $methodName): MockObject | TestableSelectedSiteController
     {
-        $controller = $this->getMockBuilder(TestableSelectedSiteController::class)
-            ->setMethodsExcept([$methodName, 'injectOtPageRepository', 'injectOtWebsiteRepository'])
-            ->getMock();
+        $controller = $this->getMockForMethods(
+            TestableSelectedSiteController::class,
+            [$methodName, 'injectOtPageRepository', 'injectOtWebsiteRepository']
+        );
 
         $controller->injectOtPageRepository($this->otPageRepository);
         $controller->injectOtWebsiteRepository($this->otWebsiteRepository);

+ 2 - 2
ot_core/Tests/Unit/Domain/Model/DonorTest.php

@@ -3,10 +3,10 @@
 namespace Opentalent\OtCore\Tests\Unit\Domain;
 
 use AssertionError;
-use Nimut\TestingFramework\TestCase\UnitTestCase;
 use Opentalent\OtCore\Domain\Model\Donor;
+use Opentalent\OtCore\Tests\Unit\OtUnitTestCase;
 
-class DonorTest extends UnitTestCase
+class DonorTest extends OtUnitTestCase
 {
     /**
      * Object should instantiate correctly, and properties

+ 2 - 2
ot_core/Tests/Unit/Domain/Model/EventTest.php

@@ -3,11 +3,11 @@
 namespace Opentalent\OtCore\Tests\Unit\Domain;
 
 use AssertionError;
-use Nimut\TestingFramework\TestCase\UnitTestCase;
 use Opentalent\OtCore\Domain\Model\Event;
 use Opentalent\OtCore\Domain\Model\Organization;
+use Opentalent\OtCore\Tests\Unit\OtUnitTestCase;
 
-class EventTest extends UnitTestCase
+class EventTest extends OtUnitTestCase
 {
     /**
      * Object should instantiate correctly, and properties

+ 1 - 1
ot_core/Tests/Unit/Domain/Model/MemberTest.php

@@ -3,8 +3,8 @@
 namespace Opentalent\OtCore\Tests\Unit\Domain;
 
 use AssertionError;
-use Nimut\TestingFramework\TestCase\UnitTestCase;
 use Opentalent\OtCore\Domain\Model\Member;
+use TYPO3\TestingFramework\Core\Unit\UnitTestCase;
 
 class MemberTest extends UnitTestCase
 {

+ 2 - 3
ot_core/Tests/Unit/Domain/Model/OrganizationTest.php

@@ -3,11 +3,10 @@
 namespace Opentalent\OtCore\Tests\Unit\Domain;
 
 use AssertionError;
-use Nimut\TestingFramework\TestCase\UnitTestCase;
-use Opentalent\OtCore\Domain\Model\Donor;
 use Opentalent\OtCore\Domain\Model\Organization;
+use Opentalent\OtCore\Tests\Unit\OtUnitTestCase;
 
-class OrganizationTest extends UnitTestCase
+class OrganizationTest extends OtUnitTestCase
 {
     /**
      * Object should instantiate correctly, and properties

+ 2 - 4
ot_core/Tests/Unit/Domain/Repository/AbstractApiRepositoryTestCase.php

@@ -3,21 +3,19 @@
 namespace Opentalent\OtCore\Tests\Unit\Domain\Repository;
 
 use GuzzleHttp\Client;
-use Nimut\TestingFramework\TestCase\UnitTestCase;
-use Opentalent\OtCore\Domain\Repository\BaseApiRepository;
 use Opentalent\OtCore\Service\OpentalentApiService;
 use Opentalent\OtCore\Tests\Unit\Fixtures\ApiResponseFixtures;
-use Prophecy\Argument;
 use Prophecy\PhpUnit\ProphecyTrait;
 use ReflectionClass;
 use TYPO3\CMS\Core\Core\ApplicationContext;
+use Opentalent\OtCore\Tests\Unit\OtUnitTestCase;
 
 /**
  * Base class for BaseApiRepositoryTest and its subclasses
  *
  * @package Opentalent\OtCore\Tests\Unit\Repository
  */
-abstract class AbstractApiRepositoryTestCase extends UnitTestCase
+abstract class AbstractApiRepositoryTestCase extends OtUnitTestCase
 {
     use ProphecyTrait;
 

+ 2 - 2
ot_core/Tests/Unit/Domain/Repository/ApiPagedCollectionTest.php

@@ -2,10 +2,10 @@
 
 namespace Opentalent\OtCore\Tests\Unit\Repository;
 
-use Nimut\TestingFramework\TestCase\UnitTestCase;
 use Opentalent\OtCore\Domain\Repository\ApiPagedCollection;
+use Opentalent\OtCore\Tests\Unit\OtUnitTestCase;
 
-class ApiPagedCollectionTest extends UnitTestCase
+class ApiPagedCollectionTest extends OtUnitTestCase
 {
     /**
      * @test

+ 2 - 2
ot_core/Tests/Unit/Exception/ApiRequestExceptionTest.php

@@ -3,10 +3,10 @@
 namespace Opentalent\OtCore\Tests\Unit\Exception;
 
 use Exception;
-use Nimut\TestingFramework\TestCase\UnitTestCase;
 use Opentalent\OtCore\Exception\ApiRequestException;
+use Opentalent\OtCore\Tests\Unit\OtUnitTestCase;
 
-class ApiRequestExceptionTest extends UnitTestCase
+class ApiRequestExceptionTest extends OtUnitTestCase
 {
     /**
      * Construction from another exception

+ 19 - 0
ot_core/Tests/Unit/OtUnitTestCase.php

@@ -0,0 +1,19 @@
+<?php
+declare(strict_types=1);
+
+namespace Opentalent\OtCore\Tests\Unit;
+
+use TYPO3\TestingFramework\Core\Unit\UnitTestCase;
+
+class OtUnitTestCase extends UnitTestCase
+{
+    protected function getMockForMethods($className, $methods): mixed {
+        $mockBuilder = $this->getMockBuilder($className);
+
+        $classMethods = get_class_methods($className);
+        $methodsToMock = array_diff($classMethods, $methods);
+        $mockBuilder->onlyMethods($methodsToMock);
+
+        return $mockBuilder->getMock();
+    }
+}

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

@@ -4,16 +4,15 @@ 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;
 use Opentalent\OtCore\Tests\Unit\Fixtures\ApiResponseFixtures;
-use PHPUnit\TextUI\RuntimeException;
 use Prophecy\Argument;
 use Prophecy\PhpUnit\ProphecyTrait;
 use TYPO3\CMS\Core\Core\ApplicationContext;
+use Opentalent\OtCore\Tests\Unit\OtUnitTestCase;
 
-class OpentalentApiServiceTest extends UnitTestCase
+class OpentalentApiServiceTest extends OtUnitTestCase
 {
     use ProphecyTrait;
 

+ 2 - 2
ot_core/Tests/Unit/Service/OpentalentEnvServiceTest.php

@@ -2,10 +2,10 @@
 
 namespace Opentalent\OtCore\Tests\Unit\Service;
 
-use Nimut\TestingFramework\TestCase\UnitTestCase;
 use Opentalent\OtCore\Service\OpentalentEnvService;
+use Opentalent\OtCore\Tests\Unit\OtUnitTestCase;
 
-class OpentalentEnvServiceTest extends UnitTestCase
+class OpentalentEnvServiceTest extends OtUnitTestCase
 {
     /**
      * get should return the requested variable default value,

+ 2 - 2
ot_core/Tests/Unit/Website/OtPageRepositoryTest.php

@@ -4,15 +4,15 @@ 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 Prophecy\PhpUnit\ProphecyTrait;
 use TYPO3\CMS\Core\Database\ConnectionPool;
 use TYPO3\CMS\Core\Domain\Repository\PageRepository;
+use Opentalent\OtCore\Tests\Unit\OtUnitTestCase;
 
 
-class OtPageRepositoryTest extends UnitTestCase
+class OtPageRepositoryTest extends OtUnitTestCase
 {
     use ProphecyTrait;
 

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

@@ -4,18 +4,17 @@
 namespace Opentalent\OtCore\Tests\Unit\Website;
 
 
-use Nimut\TestingFramework\TestCase\UnitTestCase;
 use Opentalent\OtCore\Exception\InvalidWebsiteConfigurationException;
 use Opentalent\OtCore\Exception\NoSuchWebsiteException;
 use Opentalent\OtCore\Tests\Unit\Fixtures\PageFixtures;
 use Opentalent\OtCore\Tests\Unit\Fixtures\WebsiteFixtures;
 use Opentalent\OtCore\Tests\Unit\QueryBuilderProphet;
-use Opentalent\OtCore\Website\OtPageRepository;
 use Opentalent\OtCore\Website\OtWebsiteRepository;
 use Prophecy\PhpUnit\ProphecyTrait;
 use TYPO3\CMS\Core\Database\ConnectionPool;
+use Opentalent\OtCore\Tests\Unit\OtUnitTestCase;
 
-class OtWebsiteRepositoryTest extends UnitTestCase
+class OtWebsiteRepositoryTest extends OtUnitTestCase
 {
     use ProphecyTrait;
 

+ 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": {

+ 1 - 1
ot_core/ext_emconf.php

@@ -15,7 +15,7 @@ $EM_CONF[$_EXTKEY] = [
     'author' => 'Olivier Massot',
     'author_email' => 'olivier.massot@2iopenservice.fr',
     'state' => 'stable',
-    'version' => '0.7',
+    'version' => '0.7.1',
     'constraints' => [
         'depends' => [
             'typo3' => '10.4.0-11.5.99',

+ 2 - 0
ot_core/ext_localconf.php

@@ -28,6 +28,7 @@ $GLOBALS['TYPO3_CONF_VARS']['SYS']['Objects'][TYPO3\CMS\Backend\Middleware\Backe
     'className' => Opentalent\OtCore\Middleware\OtBackendUserAuthenticator::class
 ];
 
+
 // ** Opentalent environment variables **
 // An array containing all or part of these variables could have been set before, for example
 // in the AdditionalConfiguration.php file.
@@ -35,6 +36,7 @@ $GLOBALS['TYPO3_CONF_VARS']['SYS']['Objects'][TYPO3\CMS\Backend\Middleware\Backe
 // (/!\ do not forget to clear the cache after any update here)
 $GLOBALS['OT'] = array_merge([
     'API_BASE_URI' => 'http://docker.nginx.opentalent.fr',
+    'PUBLIC_API_BASE_URI' => 'https://local.api.opentalent.fr',
     'DB_HOST' => 'db',
     'DB_USER' => 'dbcloner',
     'DB_PASSWORD' => 'wWZ4hYcrmHLW2mUK',

+ 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.
      *

+ 65 - 0
ot_templating/Classes/ViewHelpers/Image/GetSrcByIdViewHelper.php

@@ -0,0 +1,65 @@
+<?php
+
+namespace Opentalent\OtTemplating\ViewHelpers\Image;
+
+
+use Opentalent\OtCore\Service\OpentalentImageService;
+use Opentalent\OtCore\ViewHelpers\OtAbstractViewHelper;
+
+/**
+ * Returns the url of the requested image
+ *
+ *     {namespace ot=Opentalent\OtTemplating\ViewHelpers}
+ *
+ *     {ot:getImageUrl(fileId: 123, size: 'md')}
+ *
+ * @see ot_core/Classes/Service/OpentalentImageService.php
+ * @package Opentalent\OtTemplating\ViewHelpers
+ */
+class GetSrcByIdViewHelper extends OtAbstractViewHelper
+{
+    public OpentalentImageService $opentalentImageService;
+
+    /**
+     * @param OpentalentImageService $opentalentImageService
+     * @return void
+     */
+    public function injectOpentalentImageService(OpentalentImageService $opentalentImageService)
+    {
+        $this->opentalentImageService = $opentalentImageService;
+    }
+
+    /**
+     * -- This method is expected by Fluid --
+     * Declares the viewhelper's parameters
+     */
+    public function initializeArguments()
+    {
+        $this->registerArgument(
+            'fileId',
+            'int',
+            'The id of the File object',
+            true
+        );
+        $this->registerArgument(
+            'size',
+            'string',
+            'The expected size (sm, md, or lg)',
+            false,
+            'md'
+        );
+    }
+
+    /**
+     * -- This method is expected by Fluid --
+     * Renders the content as html
+     *
+     * @return string Rendered tag
+     */
+    public function render() {
+        $fileId = $this->arguments['fileId'];
+        $size = $this->arguments['size'];
+        return $this->opentalentImageService->getImageUrl($fileId, $size);
+    }
+
+}

+ 59 - 0
ot_templating/Classes/ViewHelpers/Image/GetSrcByPathViewHelper.php

@@ -0,0 +1,59 @@
+<?php
+
+namespace Opentalent\OtTemplating\ViewHelpers\Image;
+
+
+use Opentalent\OtCore\Exception\ApiRequestException;
+use Opentalent\OtCore\Service\OpentalentApiService;
+use Opentalent\OtCore\ViewHelpers\OtAbstractViewHelper;
+
+/**
+ * Returns the absolute url of the requested image
+ *
+ *     {namespace ot=Opentalent\OtTemplating\ViewHelpers}
+ *
+ *     {ot:image.getByUrl(path: "/media/cache/crop_md/organization/123/upload/my_picture.jpg")}
+ *
+ * @see ot_core/Classes/Service/OpentalentImageService.php
+ * @package Opentalent\OtTemplating\ViewHelpers
+ */
+class GetSrcByPathViewHelper extends OtAbstractViewHelper
+{
+    public OpentalentApiService $opentalentApiService;
+
+    /**
+     * @param OpentalentApiService $opentalentApiService
+     * @return void
+     */
+    public function injectOpentalentApiService(OpentalentApiService $opentalentApiService)
+    {
+        $this->opentalentApiService = $opentalentApiService;
+    }
+
+    /**
+     * -- This method is expected by Fluid --
+     * Declares the viewhelper's parameters
+     */
+    public function initializeArguments()
+    {
+        $this->registerArgument(
+            'path',
+            'string',
+            'The path (relative url) of the image in the storage',
+            true
+        );
+    }
+
+    /**
+     * -- This method is expected by Fluid --
+     * Renders the content as html
+     *
+     * @return string
+     * @throws ApiRequestException
+     */
+    public function render()
+    {
+        $path = $this->arguments['path'];
+        return $this->opentalentApiService->getApiUri($path, true);
+    }
+}

+ 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 "";
-        }
-    }
-
-}

+ 2 - 11
ot_templating/Classes/ViewHelpers/Page/GetFirstWithTemplateViewHelper.php

@@ -2,15 +2,9 @@
 
 namespace Opentalent\OtTemplating\ViewHelpers\Page;
 
-
-use Closure;
 use Opentalent\OtCore\Website\OtPageRepository;
 use Opentalent\OtCore\ViewHelpers\OtAbstractViewHelper;
 use Opentalent\OtCore\Website\OtWebsiteRepository;
-use Opentalent\OtTemplating\ViewHelpers\RootPage\GetIdViewHelper;
-use TYPO3\CMS\Core\Utility\GeneralUtility;
-use TYPO3\CMS\Extbase\Object\ObjectManager;
-use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
 
 /**
  * Returns the uid of the first page with the given template in the current website, or null if none
@@ -25,6 +19,7 @@ class GetFirstWithTemplateViewHelper extends OtAbstractViewHelper
 {
     public function __construct(
         private readonly OtPageRepository $otPageRepository,
+        private readonly OtWebsiteRepository $otWebsiteRepository,
     ) {}
 
     /**
@@ -49,11 +44,7 @@ class GetFirstWithTemplateViewHelper extends OtAbstractViewHelper
      */
     public function render(): ?int
     {
-        $rootId = GetIdViewHelper::renderStatic(
-            $this->arguments,
-            $this->renderChildrenClosure,
-            $this->renderingContext
-        );
+        $rootId = $this->otWebsiteRepository->getCurrentRootpageUidFromFEGlobals();
 
         $subpages = $this->otPageRepository->getAllSubpagesForPage($rootId);
 

+ 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
 

+ 3 - 0
ot_templating/Resources/Private/Language/locallang.xlf

@@ -1928,6 +1928,9 @@
 			<trans-unit id="OTAU" xml:space="preserve">
 			    <source>Autres</source>
 			</trans-unit>
+			<trans-unit id="practicalInfos" xml:space="preserve">
+			    <source>Infos pratiques</source>
+			</trans-unit>
 		</body>
 	</file>
 </xliff>

+ 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/Layouts/Classic/StructureDetails.html

@@ -17,7 +17,7 @@
     <div class="content">
         <div class="ot-structures">
             <iframe
-                    src="{ot:request.getOtEnvVar(argument: 'FRAMES_BASE_URI')}/structures/{settings.organizationId}?theme={ot:template.getPreference(key: 'themeColor')}"
+                    src="{ot:request.getOtEnvVar(argument: 'FRAMES_BASE_URI')}/structures/{settings.organizationId}?theme={ot:template.getPreference(key: 'themeColor')}&hideBackBtn=1"
                     referrerpolicy="strict-origin"
                     style="border: none;"
                     onload="iFrameResize()"

+ 1 - 1
ot_templating/Resources/Private/Layouts/Modern/StructureDetails.html

@@ -17,7 +17,7 @@
 
         <section class="page-section-ptb">
             <div class="ot-structures">
-                <iframe src="{ot:request.getOtEnvVar(argument: 'FRAMES_BASE_URI')}/structures/{settings.organizationId}?theme={ot:template.getPreference(key: 'themeColor')}"
+                <iframe src="{ot:request.getOtEnvVar(argument: 'FRAMES_BASE_URI')}/structures/{settings.organizationId}?theme={ot:template.getPreference(key: 'themeColor')}&hideBackBtn=1"
                         referrerpolicy="strict-origin"
                         style="border: none;"
                         onload="iFrameResize()"

+ 28 - 40
ot_templating/Resources/Private/Partials/Classic/Assets.html

@@ -10,68 +10,56 @@ Assets included with the VHS viewhelpers
 <v:variable.set name="assets_dir" value="EXT:ot_templating/Resources/Public/assets/Classic" />
 
 <f:comment><!-- Style assets --></f:comment>
-<v:asset.style name="classic-fontAwesome"
-               path="{assets_dir}/style/ext/font-awesome/css/all.min.css"/>
+<f:asset.css identifier="classic-fontAwesome"
+               href="{assets_dir}/style/ext/font-awesome/css/all.min.css"/>
 
-<v:asset.style name="classic-jquery-ui-css"
-               path="{assets_dir}/style/ext/jquery-ui.min.css"/>
+<f:asset.css identifier="classic-jquery-ui-css"
+               href="{assets_dir}/style/ext/jquery-ui.min.css"/>
 
-<v:asset.style name="classic-slick-css"
-               path="{assets_dir}/style/ext/slick.css"/>
+<f:asset.css identifier="classic-slick-css"
+               href="{assets_dir}/style/ext/slick.css"/>
 
-<v:asset.style name="classic-slick-theme"
-               path="{assets_dir}/style/ext/slick-theme.css"/>
+<f:asset.css identifier="classic-slick-theme"
+               href="{assets_dir}/style/ext/slick-theme.css"/>
 
 <f:comment><!-- Leaflet's path shall not be rewritten since
                 it won't find the images files anymore --></f:comment>
-<v:asset.style name="classic-leaflet-css"
-               path="{assets_dir}/style/ext/leaflet.css"
-               rewrite="0"
-               standalone="1"/>
+<f:asset.css identifier="classic-leaflet-css"
+               href="{assets_dir}/style/ext/leaflet.css"/>
 
-<v:asset.style name="classic-leaflet-clusters-css"
-               path="{assets_dir}/style/ext/MarkerCluster.css"
-               rewrite="0"
-               standalone="1"/>
+<f:asset.css identifier="classic-leaflet-clusters-css"
+               href="{assets_dir}/style/ext/MarkerCluster.css"/>
 
-<v:asset.style name="classic-leaflet-clusters-default-css"
-               path="{assets_dir}/style/ext/MarkerCluster.Default.css"
-               rewrite="0"
-               standalone="1"/>
+<f:asset.css identifier="classic-leaflet-clusters-default-css"
+               href="{assets_dir}/style/ext/MarkerCluster.Default.css"/>
 
 <f:comment><!-- Theme's file shall not be rewritten since
                 it may not be updated from a website to another --></f:comment>
-<v:asset.style name="classic-theme"
-               path="{assets_dir}/style/classic-{ot:template.getPreference(key: 'themeColor')}.css"
-               dependencies="fontAwesome,slick,slick-theme,leaflet"
-               rewrite="0"
-               standalone="1"/>
+<f:asset.css identifier="classic-theme"
+               href="{assets_dir}/style/classic-{ot:template.getPreference(key: 'themeColor')}.css"/>
 
 
 <f:comment><!-- Script assets --></f:comment>
-<v:asset.script name="classic-jquery"
-                path="{assets_dir}/script/jquery-3.4.1.min.js"
+<f:asset.script identifier="classic-jquery"
+                src="{assets_dir}/script/jquery-3.4.1.min.js"
                 defer="1"/>
 
-<v:asset.script name="classic-datepicker"
-                path="{assets_dir}/script/jquery-ui.min.js"
+<f:asset.script identifier="classic-datepicker"
+                src="{assets_dir}/script/jquery-ui.min.js"
                 defer="1"/>
 
-<v:asset.script name="classic-slick"
-                path="{assets_dir}/script/slick.min.js"
-                dependencies="classic-jquery"
+<f:asset.script identifier="classic-slick"
+                src="{assets_dir}/script/slick.min.js"
                 defer="1"/>
 
-<v:asset.script name="classic-leaflet"
-                path="{assets_dir}/script/leaflet.js"
+<f:asset.script identifier="classic-leaflet"
+                src="{assets_dir}/script/leaflet.js"
                 defer="1"/>
 
-<v:asset.script name="classic-leaflet-clusters"
-                path="{assets_dir}/script/leaflet.markercluster.js"
+<f:asset.script identifier="classic-leaflet-clusters"
+                src="{assets_dir}/script/leaflet.markercluster.js"
                 defer="1"/>
 
-<v:asset.script name="classic-main"
-                path="{assets_dir}/script/main.js"
-                dependencies="classic-jquery,classic-datepicker,classic-slick,classic-leaflet"
-                standalone="1"
+<f:asset.script identifier="classic-main"
+                src="{assets_dir}/script/main.js"
                 defer="1"/>

+ 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"

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

@@ -66,7 +66,7 @@
                     <div class="event-poster">
                         <f:if condition="{event.image}">
                             <f:then>
-                                <img src='{event.image}' alt="poster" />
+                                <img src="{event.image}" alt="poster" />
                             </f:then>
                             <f:else>
                                 <f:image src="EXT:ot_templating/Resources/Public/media/event-default.jpg" alt="poster" />

+ 11 - 0
ot_templating/Resources/Private/Partials/Classic/Footer.html

@@ -20,6 +20,17 @@
                             </f:link.page>
                         </li>
                     </f:for>
+
+                    <f:variable name="structureDetailsPageUid">
+                        {ot:page.getFirstWithTemplate(template:'structureDetails')}
+                    </f:variable>
+                    <f:if condition="{structureDetailsPageUid}">
+                        <li>
+                            <f:link.page pageUid="{structureDetailsPageUid}" title="{f:translate(key:'practicalInfos')}">
+                                <f:translate key="practicalInfos"/>
+                            </f:link.page>
+                        </li>
+                    </f:if>
                 </v:menu>
             </f:then>
         </f:if>

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

@@ -12,7 +12,7 @@
                         <p class="ot-member-image">
                             <f:if condition="{member.image}">
                                 <f:then>
-                                    <img src="{member.image}/160x0" alt=""/>
+                                    <img src="{member.image}" alt=""/>
                                 </f:then>
                                 <f:else if="{member.gender}=='MISTER'">
                                     <f:image src="EXT:ot_templating/Resources/Public/media/man-default.jpg"/>

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

@@ -6,7 +6,7 @@
     <div class="topbar-logo">
         <f:if condition="{settings.structureLogoId}">
             <a href="{ot:rootPage.getUri()}" title="{settings.structureName}">
-                <img src="{ot:request.getOtEnvVar(argument: 'FILE_STORAGE_URL')}{settings.structureLogoId}" alt="{settings.structureName}"/>
+                <img src="{ot:image.getSrcById(fileId: settings.structureLogoId, size: 'md')}" alt="{settings.structureName}"/>
             </a>
         </f:if>
     </div>

+ 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>

+ 99 - 158
ot_templating/Resources/Private/Partials/Modern/Assets.html

@@ -8,19 +8,16 @@ Assets included with the VHS viewhelpers
 --></f:comment>
 
 <v:variable.set name="assets_dir" value="EXT:ot_templating/Resources/Public/assets/Modern" />
-<v:variable.set name="force_standalone" value="0" />
 
 <f:comment><!-- Fonts assets --></f:comment>
 
-<v:asset.style name="modern-montserrat-font"
-               path="https://fonts.googleapis.com/css?family=Montserrat:300,300i,400,500,500i,600,700,800,900|Poppins:200,300,300i,400,400i,500,500i,600,600i,700,700i,800,800i,900"
-               external="1"
-               standalone="1"/>
+<f:asset.css identifier="modern-montserrat-font"
+             href="https://fonts.googleapis.com/css?family=Montserrat:300,300i,400,500,500i,600,700,800,900|Poppins:200,300,300i,400,400i,500,500i,600,600i,700,700i,800,800i,900"
+/>
 
-<v:asset.style name="modern-dosis-font"
-               path="https://fonts.googleapis.com/css?family=Dosis:300,400,500,600,700,800"
-               external="1"
-               standalone="1"/>
+<f:asset.css identifier="modern-dosis-font"
+             href="https://fonts.googleapis.com/css?family=Dosis:300,400,500,600,700,800"
+/>
 
 <style>
     @font-face {
@@ -44,90 +41,76 @@ Assets included with the VHS viewhelpers
 <f:comment><!-- Style assets --></f:comment>
 
 <f:comment><!-- includes all plugins ; can be removed if none of those plugins are used --></f:comment>
-<v:asset.style name="modern-plugins-css"
-               path="{assets_dir}/style/plugins-css.css"
-               standalone="{force_standalone}"/>
+<f:asset.css identifier="modern-plugins-css"
+               href="{assets_dir}/style/plugins-css.css"/>
 
 <f:comment><!-- includes revolution stylesheets --></f:comment>
-<v:asset.style name="modern-revolution-settings"
-               path="{assets_dir}/style/revolution/settings.css"
-               standalone="{force_standalone}"/>
+<f:asset.css identifier="modern-revolution-settings"
+               href="{assets_dir}/style/revolution/settings.css"/>
 
 <f:comment><!-- includes all typography stylesheets --></f:comment>
-<v:asset.style name="modern-typography"
-               path="{assets_dir}/style/typography.css"
-               standalone="{force_standalone}"/>
+<f:asset.css identifier="modern-typography"
+               href="{assets_dir}/style/typography.css"/>
 
-<v:asset.style name="modern-datetimepicker-css"
-               path="{assets_dir}/style/plugins/bootstrap-datetimepicker.min.css"
-               standalone="{force_standalone}"/>
+<f:asset.css identifier="modern-datetimepicker-css"
+               href="{assets_dir}/style/plugins/bootstrap-datetimepicker.min.css"/>
 
-<v:asset.style name="modern-slick-css"
-               path="{assets_dir}/style/plugins/slick.css"
-               standalone="{force_standalone}"/>
+<f:asset.css identifier="modern-slick-css"
+               href="{assets_dir}/style/plugins/slick.css"/>
 
-<v:asset.style name="modern-slick-theme-css"
-               path="{assets_dir}/style/plugins/slick-theme.css"
-               standalone="{force_standalone}"/>
+<f:asset.css identifier="modern-slick-theme-css"
+               href="{assets_dir}/style/plugins/slick-theme.css"/>
 
 <f:comment><!-- Leaflet's path shall not be rewritten since
                 it won't find the images files anymore --></f:comment>
-<v:asset.style name="modern-leaflet-css"
-               path="{assets_dir}/style/plugins/leaflet.css"
-               standalone="1"
-               rewrite="0"/>
+<f:asset.css identifier="modern-leaflet-css"
+               href="{assets_dir}/style/plugins/leaflet.css"/>
 
 <f:comment><!-- includes all shortcodes ; some of them may have to be removed --></f:comment>
-<v:asset.style group="modern-shortcodes" path="{assets_dir}/style/shortcodes/accordion.css"/>
-<v:asset.style group="modern-shortcodes" path="{assets_dir}/style/shortcodes/action-box.css"/>
-<v:asset.style group="modern-shortcodes" path="{assets_dir}/style/shortcodes/blockquote.css"/>
-<v:asset.style group="modern-shortcodes" path="{assets_dir}/style/shortcodes/bootstrap-typography.css"/>
-<v:asset.style group="modern-shortcodes" path="{assets_dir}/style/shortcodes/button.css"/>
-<v:asset.style group="modern-shortcodes" path="{assets_dir}/style/shortcodes/clients.css"/>
-<v:asset.style group="modern-shortcodes" path="{assets_dir}/style/shortcodes/contact-form.css"/>
-<v:asset.style group="modern-shortcodes" path="{assets_dir}/style/shortcodes/countdown.css"/>
-<v:asset.style group="modern-shortcodes" path="{assets_dir}/style/shortcodes/counter.css"/>
-<v:asset.style group="modern-shortcodes" path="{assets_dir}/style/shortcodes/divider.css"/>
-<v:asset.style group="modern-shortcodes" path="{assets_dir}/style/shortcodes/dropcap.css"/>
-<v:asset.style group="modern-shortcodes" path="{assets_dir}/style/shortcodes/feature-text.css"/>
-<v:asset.style group="modern-shortcodes" path="{assets_dir}/style/shortcodes/list-style.css"/>
-<v:asset.style group="modern-shortcodes" path="{assets_dir}/style/shortcodes/nice-select.css"/>
-<v:asset.style group="modern-shortcodes" path="{assets_dir}/style/shortcodes/owl-carousel.css"/>
-<v:asset.style group="modern-shortcodes" path="{assets_dir}/style/shortcodes/page-title.css"/>
-<v:asset.style group="modern-shortcodes" path="{assets_dir}/style/shortcodes/pie-chart.css"/>
-<v:asset.style group="modern-shortcodes" path="{assets_dir}/style/shortcodes/pricing.css"/>
-<v:asset.style group="modern-shortcodes" path="{assets_dir}/style/shortcodes/progress-bar.css"/>
-<v:asset.style group="modern-shortcodes" path="{assets_dir}/style/shortcodes/section-title.css"/>
-<v:asset.style group="modern-shortcodes" path="{assets_dir}/style/shortcodes/shortcodes.css"/>
-<v:asset.style group="modern-shortcodes" path="{assets_dir}/style/shortcodes/social-icons.css"/>
-<v:asset.style group="modern-shortcodes" path="{assets_dir}/style/shortcodes/tabs.css"/>
-<v:asset.style group="modern-shortcodes" path="{assets_dir}/style/shortcodes/team.css"/>
-<v:asset.style group="modern-shortcodes" path="{assets_dir}/style/shortcodes/testimonial.css"/>
-<v:asset.style group="modern-shortcodes" path="{assets_dir}/style/shortcodes/side-panel.css"/>
-<v:asset.style group="modern-shortcodes" path="{assets_dir}/style/shortcodes/onload-modal.css"/>
+<f:asset.css identifier="modern-shortcodes1" href="{assets_dir}/style/shortcodes/accordion.css"/>
+<f:asset.css identifier="modern-shortcodes2" href="{assets_dir}/style/shortcodes/action-box.css"/>
+<f:asset.css identifier="modern-shortcodes3" href="{assets_dir}/style/shortcodes/blockquote.css"/>
+<f:asset.css identifier="modern-shortcodes4" href="{assets_dir}/style/shortcodes/bootstrap-typography.css"/>
+<f:asset.css identifier="modern-shortcodes5" href="{assets_dir}/style/shortcodes/button.css"/>
+<f:asset.css identifier="modern-shortcodes6" href="{assets_dir}/style/shortcodes/clients.css"/>
+<f:asset.css identifier="modern-shortcodes7" href="{assets_dir}/style/shortcodes/contact-form.css"/>
+<f:asset.css identifier="modern-shortcodes8" href="{assets_dir}/style/shortcodes/countdown.css"/>
+<f:asset.css identifier="modern-shortcodes9" href="{assets_dir}/style/shortcodes/counter.css"/>
+<f:asset.css identifier="modern-shortcodes10" href="{assets_dir}/style/shortcodes/divider.css"/>
+<f:asset.css identifier="modern-shortcodes11" href="{assets_dir}/style/shortcodes/dropcap.css"/>
+<f:asset.css identifier="modern-shortcodes12" href="{assets_dir}/style/shortcodes/feature-text.css"/>
+<f:asset.css identifier="modern-shortcodes13" href="{assets_dir}/style/shortcodes/list-style.css"/>
+<f:asset.css identifier="modern-shortcodes14" href="{assets_dir}/style/shortcodes/nice-select.css"/>
+<f:asset.css identifier="modern-shortcodes15" href="{assets_dir}/style/shortcodes/owl-carousel.css"/>
+<f:asset.css identifier="modern-shortcodes16" href="{assets_dir}/style/shortcodes/page-title.css"/>
+<f:asset.css identifier="modern-shortcodes17" href="{assets_dir}/style/shortcodes/pie-chart.css"/>
+<f:asset.css identifier="modern-shortcodes18" href="{assets_dir}/style/shortcodes/pricing.css"/>
+<f:asset.css identifier="modern-shortcodes19" href="{assets_dir}/style/shortcodes/progress-bar.css"/>
+<f:asset.css identifier="modern-shortcodes20" href="{assets_dir}/style/shortcodes/section-title.css"/>
+<f:asset.css identifier="modern-shortcodes21" href="{assets_dir}/style/shortcodes/shortcodes.css"/>
+<f:asset.css identifier="modern-shortcodes22" href="{assets_dir}/style/shortcodes/social-icons.css"/>
+<f:asset.css identifier="modern-shortcodes23" href="{assets_dir}/style/shortcodes/tabs.css"/>
+<f:asset.css identifier="modern-shortcodes24" href="{assets_dir}/style/shortcodes/team.css"/>
+<f:asset.css identifier="modern-shortcodes25" href="{assets_dir}/style/shortcodes/testimonial.css"/>
+<f:asset.css identifier="modern-shortcodes26" href="{assets_dir}/style/shortcodes/side-panel.css"/>
+<f:asset.css identifier="modern-shortcodes27" href="{assets_dir}/style/shortcodes/onload-modal.css"/>
 
 
 <f:comment><!-- main stylesheet of the template --></f:comment>
-<v:asset.style name="modern-style-css"
-               path="{assets_dir}/style/style.css"
-               standalone="1"
-               rewrite="0"/>
+<f:asset.css identifier="modern-style-css"
+               href="{assets_dir}/style/style.css"/>
 
 <f:comment><!-- responsive stylesheet of the template (mediaqueries) --></f:comment>
-<v:asset.style name="modern-responsive-css"
-               path="{assets_dir}/style/responsive.css"
-               standalone="{force_standalone}"/>
+<f:asset.css identifier="modern-responsive-css"
+               href="{assets_dir}/style/responsive.css"/>
 
-<v:asset.style name="modern-custom-css"
-               path="{assets_dir}/style/custom.css"
-               standalone="1"/>
+<f:asset.css identifier="modern-custom-css"
+               href="{assets_dir}/style/custom.css"/>
 
 
 <f:comment><!-- Chosen theme --></f:comment>
-<v:asset.style name="modern-theme"
-               path="{assets_dir}/style/skins/modern-{ot:template.getPreference(key: 'themeColor')}.css"
-               standalone="1"
-               rewrite="0"/>
+<f:asset.css identifier="modern-theme"
+               href="{assets_dir}/style/skins/modern-{ot:template.getPreference(key: 'themeColor')}.css"/>
 
 <f:comment><!-- Script assets --></f:comment>
 
@@ -136,135 +119,93 @@ Assets included with the VHS viewhelpers
     var plugin_path = '/typo3conf/ext/ot_templating/Resources/Public/assets/Modern/script/';
 </script>
 
-<v:asset.script name="modern-jquery"
-                path="{assets_dir}/script/jquery-3.3.1.min.js"
-                standalone="{force_standalone}"
+<f:asset.script identifier="modern-jquery"
+                src="{assets_dir}/script/jquery-3.3.1.min.js"
                 defer="1"/>
 
 <f:comment><!-- includes all plugins ; can be removed if none of those plugins are used --></f:comment>
-<v:asset.script name="modern-plugins-jquery"
-                path="{assets_dir}/script/plugins-jquery.js"
-                standalone="{force_standalone}"
-                dependencies="modern-jquery"
+<f:asset.script identifier="modern-plugins-jquery"
+                src="{assets_dir}/script/plugins-jquery.js"
                 defer="1" />
 
-<v:asset.script name="modern-recaptcha"
-                path="https://www.google.com/recaptcha/api.js"
-                external="1"
-                standalone="{force_standalone}"
-                dependencies="modern-jquery"
+<f:asset.script identifier="modern-recaptcha"
+                src="https://www.google.com/recaptcha/api.js"
                 defer="1"/>
 
-<v:asset.script name="modern-jquery-tools"
-                path="{assets_dir}/script/revolution/jquery.themepunch.tools.min.js"
-                standalone="{force_standalone}"
+<f:asset.script identifier="modern-jquery-tools"
+                src="{assets_dir}/script/revolution/jquery.themepunch.tools.min.js"
                 defer="1"/>
 
-<v:asset.script name="modern-jquery-revolution"
-                path="{assets_dir}/script/revolution/jquery.themepunch.revolution.min.js"
-                standalone="{force_standalone}"
-                dependencies="modern-jquery"
+<f:asset.script identifier="modern-jquery-revolution"
+                src="{assets_dir}/script/revolution/jquery.themepunch.revolution.min.js"
                 defer="1"/>
 
-<v:asset.script name="modern-revolution-actions"
-                path="{assets_dir}/script/revolution/extensions/revolution.extension.actions.min.js"
-                standalone="{force_standalone}"
-                dependencies="modern-jquery"
+<f:asset.script identifier="modern-revolution-actions"
+                src="{assets_dir}/script/revolution/extensions/revolution.extension.actions.min.js"
                 defer="1"/>
 
-<v:asset.script name="modern-revolution-carousel"
-                path="{assets_dir}/script/revolution/extensions/revolution.extension.carousel.min.js"
-                standalone="{force_standalone}"
-                dependencies="modern-jquery"
+<f:asset.script identifier="modern-revolution-carousel"
+                src="{assets_dir}/script/revolution/extensions/revolution.extension.carousel.min.js"
                 defer="1"/>
 
-<v:asset.script name="modern-revolution-kenburn"
-                path="{assets_dir}/script/revolution/extensions/revolution.extension.kenburn.min.js"
-                standalone="{force_standalone}"
-                dependencies="modern-jquery"
+<f:asset.script identifier="modern-revolution-kenburn"
+                src="{assets_dir}/script/revolution/extensions/revolution.extension.kenburn.min.js"
                 defer="1"/>
 
-<v:asset.script name="modern-revolution-layeranimation"
-                path="{assets_dir}/script/revolution/extensions/revolution.extension.layeranimation.min.js"
-                standalone="{force_standalone}"
-                dependencies="modern-jquery"
+<f:asset.script identifier="modern-revolution-layeranimation"
+                src="{assets_dir}/script/revolution/extensions/revolution.extension.layeranimation.min.js"
                 defer="1"/>
 
-<v:asset.script name="modern-revolution-parallax"
-                path="{assets_dir}/script/revolution/extensions/revolution.extension.parallax.min.js"
-                standalone="{force_standalone}"
-                dependencies="modern-jquery"
+<f:asset.script identifier="modern-revolution-parallax"
+                src="{assets_dir}/script/revolution/extensions/revolution.extension.parallax.min.js"
                 defer="1"/>
 
-<v:asset.script name="modern-revolution-navigation"
-                path="{assets_dir}/script/revolution/extensions/revolution.extension.navigation.min.js"
-                standalone="{force_standalone}"
+<f:asset.script identifier="modern-revolution-navigation"
+                src="{assets_dir}/script/revolution/extensions/revolution.extension.navigation.min.js"
                 defer="1"/>
 
-<v:asset.script name="modern-revolution-slideanims"
-                path="{assets_dir}/script/revolution/extensions/revolution.extension.slideanims.min.js"
-                standalone="{force_standalone}"
-                dependencies="modern-jquery"
+<f:asset.script identifier="modern-revolution-slideanims"
+                src="{assets_dir}/script/revolution/extensions/revolution.extension.slideanims.min.js"
                 defer="1"/>
 
-<v:asset.script name="modern-revolution-navigation"
-                path="{assets_dir}/script/revolution/extensions/revolution.extension.navigation.min.js"
-                standalone="{force_standalone}"
-                dependencies="modern-jquery"
+<f:asset.script identifier="modern-revolution-navigation"
+                src="{assets_dir}/script/revolution/extensions/revolution.extension.navigation.min.js"
                 defer="1"/>
 
-<v:asset.script name="modern-revolution-video"
-                path="{assets_dir}/script/revolution/extensions/revolution.extension.video.min.js"
-                standalone="{force_standalone}"
-                dependencies="modern-jquery"
+<f:asset.script identifier="modern-revolution-video"
+                src="{assets_dir}/script/revolution/extensions/revolution.extension.video.min.js"
                 defer="1"/>
 
-<v:asset.script name="modern-revolution-custom"
-                path="{assets_dir}/script/revolution/revolution-custom.js"
-                standalone="{force_standalone}"
-                dependencies="modern-jquery"
+<f:asset.script identifier="modern-revolution-custom"
+                src="{assets_dir}/script/revolution/revolution-custom.js"
                 defer="1"/>
 
-<v:asset.script name="modern-moment-js"
-                path="{assets_dir}/script/bootstrap-datetimepicker/moment-datepicker.js"
-                standalone="{force_standalone}"
-                dependencies="modern-jquery"
+<f:asset.script identifier="modern-moment-js"
+                src="{assets_dir}/script/bootstrap-datetimepicker/moment-datepicker.js"
                 defer="1"/>
 
-<v:asset.script name="modern-datetimepicker-js"
-                path="{assets_dir}/script/bootstrap-datetimepicker/bootstrap-datetimepicker.min.js"
-                standalone="{force_standalone}"
-                dependencies="modern-jquery,modern-moment-js"
+<f:asset.script identifier="modern-datetimepicker-js"
+                src="{assets_dir}/script/bootstrap-datetimepicker/bootstrap-datetimepicker.min.js"
                 defer="1"/>
 
-<v:asset.script name="modern-slick-js"
-                path="{assets_dir}/script/slick/slick.min.js"
-                standalone="{force_standalone}"
-                dependencies="modern-jquery"
+<f:asset.script identifier="modern-slick-js"
+                src="{assets_dir}/script/slick/slick.min.js"
                 defer="1"/>
 
-<v:asset.script name="modern-leaflet-js"
-                path="{assets_dir}/script/leaflet/leaflet.js"
-                standalone="{force_standalone}"
-                dependencies="modern-jquery"
+<f:asset.script identifier="modern-leaflet-js"
+                src="{assets_dir}/script/leaflet/leaflet.js"
                 defer="1"/>
 
-<v:asset.script name="modern-magnific-popup"
-                path="{assets_dir}/script/magnific-popup/jquery.magnific-popup.min.js"
-                standalone="{force_standalone}"
-                dependencies="modern-jquery"
+<f:asset.script identifier="modern-magnific-popup"
+                src="{assets_dir}/script/magnific-popup/jquery.magnific-popup.min.js"
                 defer="1"/>
 
-<v:asset.script name="modern-custom-js"
-                path="{assets_dir}/script/custom.js"
-                standalone="{force_standalone}"
-                dependencies="modern-jquery"
+<f:asset.script identifier="modern-custom-js"
+                src="{assets_dir}/script/custom.js"
                 defer="1"/>
 
-<v:asset.script name="modern-custom-ot-js"
-                path="{assets_dir}/script/custom-ot.js"
-                standalone="{force_standalone}"
-                dependencies="modern-jquery"
+<f:asset.script identifier="modern-custom-ot-js"
+                src="{assets_dir}/script/custom-ot.js"
                 defer="1"/>
 
 

+ 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"

+ 11 - 0
ot_templating/Resources/Private/Partials/Modern/Footer.html

@@ -44,6 +44,17 @@
                                                 </f:link.page>
                                             </li>
                                         </f:for>
+                                        <f:variable name="structureDetailsPageUid">
+                                            {ot:page.getFirstWithTemplate(template:'structureDetails')}
+                                        </f:variable>
+                                        <f:if condition="{structureDetailsPageUid}">
+                                            <li class="list-inline-item">
+                                                &nbsp;&nbsp;|&nbsp;&nbsp;
+                                                <f:link.page pageUid="{structureDetailsPageUid}" title="{f:translate(key:'practicalInfos')}">
+                                                    <f:translate key="practicalInfos"/>
+                                                </f:link.page>
+                                            </li>
+                                        </f:if>
                                     </v:menu>
                                 </f:if>
                             </ul>

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

@@ -13,7 +13,7 @@
                             <f:if condition="{member.image}">
                                 <f:then>
                                     <f:image class="defer"
-                                             data="{'src':'{member.image}/160x0'}"
+                                             data="{'src':'{member.image}'}"
                                              src="EXT:ot_templating/Resources/Public/media/loading.png"/>
                                 </f:then>
                                 <f:else if="{member.gender}=='MISTER'">

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

@@ -25,7 +25,7 @@
                                     <f:then>
                                         <a href="{ot:rootPage.getUri()}" title="{settings.structureName}">
                                             <img id="logo_img"
-                                                 src="{ot:request.getOtEnvVar(argument: 'FILE_STORAGE_URL')}{settings.structureLogoId}"
+                                                 src="{ot:image.getSrcById(fileId: settings.structureLogoId, size: 'md')}"
                                                  alt="{settings.structureName}"/>
                                         </a>
                                     </f:then>

+ 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 {

+ 1 - 0
ot_templating/Resources/Public/assets/Classic/style/module/_members.scss

@@ -32,6 +32,7 @@ $otmembers-background-color: $otmembers-background-color;
 
   .ot-member-image {
     height: 150px;
+    width: 120px;
     @include flex;
     flex-direction: column;
     justify-content: center;

+ 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',
-            )
-        );
-    }
-);

Một số tệp đã không được hiển thị bởi vì quá nhiều tập tin thay đổi trong này khác