From 8edb9c124e33f5b38b528ba7a0a0f38a08c547b4 Mon Sep 17 00:00:00 2001
From: Christian Kuhn <lolli@schwarzbu.ch>
Date: Fri, 17 Mar 2017 18:53:30 +0100
Subject: [PATCH] [BUGFIX] Language label and css loading in inline and flex
 sections
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Loading additional stylesheets from elements in inline scope fails if using
'EXT:' syntax, for flex section containers, this was not evaluated at all.
The patch adds code to resolve an EXT: prefix stylesheet file from an element
result array ['stylesheetFiles'] to properly handle loading of this CSS in
JS, and uses it in both inline and flex ajax controller. This issue is easily
reproducible with ext:styleguide 'elements_t3editor', which registers
t3editor.css to load. Without patch, This fail (404) in 'in inline'
't3editor_inline_1' 'Create New'.
Additionally, additional language file handling was not implemented for
flex section container ajax calls, which is also added by the patch,
similar to the solution done in inline ajax.

Change-Id: I617b9f1ed3e0c4718fb0867a42f2c37b604d7afe
Resolves: #80335
Releases: master
Reviewed-on: https://review.typo3.org/52089
Tested-by: TYPO3com <no-reply@typo3.com>
Reviewed-by: Frank Nägler <frank.naegler@typo3.org>
Tested-by: Frank Nägler <frank.naegler@typo3.org>
Reviewed-by: Susanne Moog <susanne.moog@typo3.org>
Tested-by: Susanne Moog <susanne.moog@typo3.org>
---
 .../AbstractFormEngineAjaxController.php      | 62 +++++++++++++++++++
 .../Controller/FormFlexAjaxController.php     | 27 +++++++-
 .../Controller/FormInlineAjaxController.php   | 45 ++------------
 .../Public/JavaScript/FormEngineFlexForm.js   |  9 +++
 4 files changed, 102 insertions(+), 41 deletions(-)

diff --git a/typo3/sysext/backend/Classes/Controller/AbstractFormEngineAjaxController.php b/typo3/sysext/backend/Classes/Controller/AbstractFormEngineAjaxController.php
index 028ed08963f9..8135d762657b 100644
--- a/typo3/sysext/backend/Classes/Controller/AbstractFormEngineAjaxController.php
+++ b/typo3/sysext/backend/Classes/Controller/AbstractFormEngineAjaxController.php
@@ -15,6 +15,11 @@ namespace TYPO3\CMS\Backend\Controller;
  * The TYPO3 project - inspiring people to share!
  */
 
+use TYPO3\CMS\Core\Localization\LocalizationFactory;
+use TYPO3\CMS\Core\Utility\ArrayUtility;
+use TYPO3\CMS\Core\Utility\GeneralUtility;
+use TYPO3\CMS\Core\Utility\PathUtility;
+
 /**
  * Abstract class for a couple of FormEngine controllers triggered by
  * ajax calls. The class containers some helpers to for instance prepare
@@ -65,4 +70,61 @@ abstract class AbstractFormEngineAjaxController
         }
         return $requireJs;
     }
+
+    /**
+     * Resolve a CSS file position, possibly prefixed with 'EXT:'
+     *
+     * @param string $stylesheetFile Given file, possibly prefixed with EXT:
+     * @return string Web root relative position to file
+     */
+    protected function getRelativePathToStylesheetFile(string $stylesheetFile): string
+    {
+        if (strpos($stylesheetFile, 'EXT:') === 0) {
+            $stylesheetFile = GeneralUtility::getFileAbsFileName($stylesheetFile);
+            $stylesheetFile = PathUtility::getRelativePathTo($stylesheetFile);
+            $stylesheetFile = rtrim($stylesheetFile, '/');
+        } else {
+            $stylesheetFile = GeneralUtility::resolveBackPath($stylesheetFile);
+        }
+        $stylesheetFile = GeneralUtility::createVersionNumberedFilename($stylesheetFile);
+        return PathUtility::getAbsoluteWebPath($stylesheetFile);
+    }
+
+    /**
+     * Parse a language file and get a label/value array from it.
+     *
+     * @param string $file EXT:path/to/file
+     * @return array Label/value array
+     */
+    protected function getLabelsFromLocalizationFile($file)
+    {
+        /** @var $languageFactory LocalizationFactory */
+        $languageFactory = GeneralUtility::makeInstance(LocalizationFactory::class);
+        $language = $GLOBALS['LANG']->lang;
+        $localizationArray = $languageFactory->getParsedData(
+            $file,
+            $language,
+            'utf-8',
+            1
+        );
+        if (is_array($localizationArray) && !empty($localizationArray)) {
+            if (!empty($localizationArray[$language])) {
+                $xlfLabelArray = $localizationArray['default'];
+                ArrayUtility::mergeRecursiveWithOverrule($xlfLabelArray, $localizationArray[$language], true, false);
+            } else {
+                $xlfLabelArray = $localizationArray['default'];
+            }
+        } else {
+            $xlfLabelArray = [];
+        }
+        $labelArray = [];
+        foreach ($xlfLabelArray as $key => $value) {
+            if (isset($value[0]['target'])) {
+                $labelArray[$key] = $value[0]['target'];
+            } else {
+                $labelArray[$key] = '';
+            }
+        }
+        return $labelArray;
+    }
 }
diff --git a/typo3/sysext/backend/Classes/Controller/FormFlexAjaxController.php b/typo3/sysext/backend/Classes/Controller/FormFlexAjaxController.php
index 5eba4d00a2d5..7606c92d22f9 100644
--- a/typo3/sysext/backend/Classes/Controller/FormFlexAjaxController.php
+++ b/typo3/sysext/backend/Classes/Controller/FormFlexAjaxController.php
@@ -21,6 +21,7 @@ use TYPO3\CMS\Backend\Form\FormDataCompiler;
 use TYPO3\CMS\Backend\Form\FormDataGroup\TcaDatabaseRecord;
 use TYPO3\CMS\Backend\Form\NodeFactory;
 use TYPO3\CMS\Core\Configuration\FlexForm\FlexFormTools;
+use TYPO3\CMS\Core\Utility\ArrayUtility;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 use TYPO3\CMS\Core\Utility\StringUtility;
 
@@ -113,6 +114,7 @@ class FormFlexAjaxController extends AbstractFormEngineAjaxController
 
         $jsonResult = [
             'html' => $newContainerResult['html'],
+            'stylesheetFiles' => [],
             'scriptCall' => [],
         ];
 
@@ -124,7 +126,30 @@ class FormFlexAjaxController extends AbstractFormEngineAjaxController
         foreach ($newContainerResult['additionalJavaScriptPost'] as $singleAdditionalJavaScriptPost) {
             $jsonResult['scriptCall'][] = $singleAdditionalJavaScriptPost;
         }
-        // @todo: handle stylesheetFiles, additionalInlineLanguageLabelFiles
+        foreach ($newContainerResult['stylesheetFiles'] as $stylesheetFile) {
+            $jsonResult['stylesheetFiles'][] = $this->getRelativePathToStylesheetFile($stylesheetFile);
+        }
+        if (!empty($newContainerResult['additionalInlineLanguageLabelFiles'])) {
+            $labels = [];
+            foreach ($newContainerResult['additionalInlineLanguageLabelFiles'] as $additionalInlineLanguageLabelFile) {
+                ArrayUtility::mergeRecursiveWithOverrule(
+                    $labels,
+                    $this->getLabelsFromLocalizationFile($additionalInlineLanguageLabelFile)
+                );
+            }
+            $javaScriptCode = [];
+            $javaScriptCode[] = 'if (typeof TYPO3 === \'undefined\' || typeof TYPO3.lang === \'undefined\') {';
+            $javaScriptCode[] = '   TYPO3.lang = {}';
+            $javaScriptCode[] = '}';
+            $javaScriptCode[] = 'var additionalInlineLanguageLabels = ' . json_encode($labels) . ';';
+            $javaScriptCode[] = 'for (var attributeName in additionalInlineLanguageLabels) {';
+            $javaScriptCode[] = '   if (typeof TYPO3.lang[attributeName] === \'undefined\') {';
+            $javaScriptCode[] = '       TYPO3.lang[attributeName] = additionalInlineLanguageLabels[attributeName]';
+            $javaScriptCode[] = '   }';
+            $javaScriptCode[] = '}';
+
+            $jsonResult['scriptCall'][] = implode(LF, $javaScriptCode);
+        }
 
         $requireJsModule = $this->createExecutableStringRepresentationOfRegisteredRequireJsModules($newContainerResult);
         $jsonResult['scriptCall'] = array_merge($requireJsModule, $jsonResult['scriptCall']);
diff --git a/typo3/sysext/backend/Classes/Controller/FormInlineAjaxController.php b/typo3/sysext/backend/Classes/Controller/FormInlineAjaxController.php
index 9e273e257dbe..97a8e4463e62 100644
--- a/typo3/sysext/backend/Classes/Controller/FormInlineAjaxController.php
+++ b/typo3/sysext/backend/Classes/Controller/FormInlineAjaxController.php
@@ -23,7 +23,6 @@ use TYPO3\CMS\Backend\Form\InlineStackProcessor;
 use TYPO3\CMS\Backend\Form\NodeFactory;
 use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
 use TYPO3\CMS\Core\DataHandling\DataHandler;
-use TYPO3\CMS\Core\Localization\LocalizationFactory;
 use TYPO3\CMS\Core\Utility\ArrayUtility;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 use TYPO3\CMS\Core\Utility\MathUtility;
@@ -576,7 +575,10 @@ class FormInlineAjaxController extends AbstractFormEngineAjaxController
     protected function mergeChildResultIntoJsonResult(array $jsonResult, array $childResult)
     {
         $jsonResult['data'] .= $childResult['html'];
-        $jsonResult['stylesheetFiles'] = $childResult['stylesheetFiles'];
+        $jsonResult['stylesheetFiles'] = [];
+        foreach ($childResult['stylesheetFiles'] as $stylesheetFile) {
+            $jsonResult['stylesheetFiles'][] = $this->getRelativePathToStylesheetFile($stylesheetFile);
+        }
         if (!empty($childResult['inlineData'])) {
             $jsonResult['scriptCall'][] = 'inline.addToDataArray(' . json_encode($childResult['inlineData']) . ');';
         }
@@ -593,7 +595,7 @@ class FormInlineAjaxController extends AbstractFormEngineAjaxController
             foreach ($childResult['additionalInlineLanguageLabelFiles'] as $additionalInlineLanguageLabelFile) {
                 ArrayUtility::mergeRecursiveWithOverrule(
                     $labels,
-                    $this->addInlineLanguageLabelFile($additionalInlineLanguageLabelFile)
+                    $this->getLabelsFromLocalizationFile($additionalInlineLanguageLabelFile)
                 );
             }
             $javaScriptCode = [];
@@ -615,43 +617,6 @@ class FormInlineAjaxController extends AbstractFormEngineAjaxController
         return $jsonResult;
     }
 
-    /**
-     * @param string $file
-     *
-     * @return array
-     */
-    protected function addInlineLanguageLabelFile($file)
-    {
-        /** @var $languageFactory LocalizationFactory */
-        $languageFactory = GeneralUtility::makeInstance(LocalizationFactory::class);
-        $language = $GLOBALS['LANG']->lang;
-        $localizationArray = $languageFactory->getParsedData(
-            $file,
-            $language,
-            'utf-8',
-            1
-        );
-        if (is_array($localizationArray) && !empty($localizationArray)) {
-            if (!empty($localizationArray[$language])) {
-                $xlfLabelArray = $localizationArray['default'];
-                ArrayUtility::mergeRecursiveWithOverrule($xlfLabelArray, $localizationArray[$language], true, false);
-            } else {
-                $xlfLabelArray = $localizationArray['default'];
-            }
-        } else {
-            $xlfLabelArray = [];
-        }
-        $labelArray = [];
-        foreach ($xlfLabelArray as $key => $value) {
-            if (isset($value[0]['target'])) {
-                $labelArray[$key] = $value[0]['target'];
-            } else {
-                $labelArray[$key] = '';
-            }
-        }
-        return $labelArray;
-    }
-
     /**
      * Gets an array with the uids of related records out of a list of items.
      * This list could contain more information than required. This methods just
diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/FormEngineFlexForm.js b/typo3/sysext/backend/Resources/Public/JavaScript/FormEngineFlexForm.js
index 3d6277839895..458b3d1e5166 100644
--- a/typo3/sysext/backend/Resources/Public/JavaScript/FormEngineFlexForm.js
+++ b/typo3/sysext/backend/Resources/Public/JavaScript/FormEngineFlexForm.js
@@ -264,6 +264,15 @@ define(['jquery',
 							eval(value);
 						});
 					}
+					if (response.stylesheetFiles && response.stylesheetFiles.length > 0) {
+						$.each(response.stylesheetFiles, function (index, stylesheetFile) {
+							var element = document.createElement('link');
+							element['rel'] = 'stylesheet';
+							element['type'] = 'text/css';
+							element['href'] = stylesheetFile;
+							document.head.appendChild(element);
+						});
+					}
 					TYPO3.FormEngine.reinitialize();
 					TYPO3.FormEngine.Validation.initializeInputFields();
 					TYPO3.FormEngine.Validation.validate();
-- 
GitLab