[BUGFIX] RTE Image Wizard 26/46826/2
authorMarkus Klein <markus.klein@typo3.org>
Tue, 15 Dec 2015 10:12:32 +0000 (11:12 +0100)
committerBenni Mack <benni@typo3.org>
Tue, 23 Feb 2016 10:41:39 +0000 (11:41 +0100)
Make inserting/editing images in RTE possible again.

Resolves: #71494
Releases: master, 7.6
Change-Id: If561562d9330c39544b7bd514a273641538d03f8
Reviewed-on: https://review.typo3.org/46826
Reviewed-by: Benni Mack <benni@typo3.org>
Tested-by: Benni Mack <benni@typo3.org>
17 files changed:
typo3/sysext/recordlist/Classes/Browser/FileBrowser.php
typo3/sysext/recordlist/Classes/LinkHandler/LinkHandlerInterface.php
typo3/sysext/recordlist/Classes/View/FolderUtilityRenderer.php
typo3/sysext/recordlist/Resources/Public/JavaScript/BrowseFiles.js
typo3/sysext/rtehtmlarea/Classes/Controller/SelectImageController.php
typo3/sysext/rtehtmlarea/Classes/ImageHandler/AddImageHandler.php [new file with mode: 0644]
typo3/sysext/rtehtmlarea/Classes/ImageHandler/EditImageHandler.php [new file with mode: 0644]
typo3/sysext/rtehtmlarea/Classes/SelectImage.php [deleted file]
typo3/sysext/rtehtmlarea/Configuration/Backend/AjaxRoutes.php [new file with mode: 0644]
typo3/sysext/rtehtmlarea/Configuration/PageTSconfig/Image/pageTSConfig.txt
typo3/sysext/rtehtmlarea/Documentation/Configuration/PageTsconfig/interfaceConfiguration/Index.rst
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/AddImage.js [new file with mode: 0644]
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/EditImage.js [new file with mode: 0644]
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/Modules/SelectImage.js [deleted file]
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/Plugins/TYPO3Image.js
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/SelectImage.js [new file with mode: 0644]
typo3/sysext/rtehtmlarea/ext_tables.php

index c40fe5f..f825384 100644 (file)
@@ -113,8 +113,8 @@ class FileBrowser extends AbstractElementBrowser implements ElementBrowserInterf
 
         // The key number 3 of the bparams contains the "allowed" string. Disallowed is not passed to
         // the element browser at all but only filtered out in TCEMain afterwards
-        $allowedFileExtensions = explode('|', $this->bparams)[3];
-        if (!empty($allowedFileExtensions) && $allowedFileExtensions !== 'sys_file' && $allowedFileExtensions !== '*') {
+        $allowedFileExtensions = GeneralUtility::trimExplode(',', explode('|', $this->bparams)[3], true);
+        if (!empty($allowedFileExtensions) && $allowedFileExtensions[0] !== 'sys_file' && $allowedFileExtensions[0] !== '*') {
             // Create new filter object
             $filterObject = GeneralUtility::makeInstance(FileExtensionFilter::class);
             $filterObject->setAllowedFileExtensions($allowedFileExtensions);
@@ -125,7 +125,6 @@ class FileBrowser extends AbstractElementBrowser implements ElementBrowserInterf
                 $storage->addFileAndFolderNameFilter(array($filterObject, 'filterFileList'));
             }
         }
-        // Create upload/create folder forms, if a path is given
         if ($this->expandFolder) {
             $fileOrFolderObject = null;
 
@@ -159,10 +158,8 @@ class FileBrowser extends AbstractElementBrowser implements ElementBrowserInterf
         $uploadForm = '';
         $createFolder = '';
         if ($this->selectedFolder) {
-            $pArr = explode('|', $this->bparams);
-            $allowedExtensions = isset($pArr[3]) ? GeneralUtility::trimExplode(',', $pArr[3], true) : [];
             $folderUtilityRenderer = GeneralUtility::makeInstance(FolderUtilityRenderer::class, $this);
-            $uploadForm = $folderUtilityRenderer->uploadForm($this->selectedFolder, $allowedExtensions);
+            $uploadForm = $folderUtilityRenderer->uploadForm($this->selectedFolder, $allowedFileExtensions);
             $createFolder = $folderUtilityRenderer->createFolder($this->selectedFolder);
         }
 
@@ -227,11 +224,11 @@ class FileBrowser extends AbstractElementBrowser implements ElementBrowserInterf
      * For TYPO3 Element Browser: Expand folder of files.
      *
      * @param Folder $folder The folder path to expand
-     * @param string $extensionList List of fileextensions to show
+     * @param array $extensionList List of fileextensions to show
      * @param bool $noThumbs Whether to show thumbnails or not. If set, no thumbnails are shown.
      * @return string HTML output
      */
-    public function renderFilesInFolder(Folder $folder, $extensionList = '', $noThumbs = false)
+    public function renderFilesInFolder(Folder $folder, array $extensionList = [], $noThumbs = false)
     {
         if (!$folder->checkActionPermission('read')) {
             return '';
@@ -242,7 +239,7 @@ class FileBrowser extends AbstractElementBrowser implements ElementBrowserInterf
         if ($this->searchWord !== '') {
             $files = $this->fileRepository->searchByName($folder, $this->searchWord);
         } else {
-            $extensionList = $extensionList === '*' ? '' : $extensionList;
+            $extensionList = !empty($extensionList) && $extensionList[0] === '*' ? [] : $extensionList;
             $files = $this->getFilesInFolder($folder, $extensionList);
         }
         $filesCount = count($files);
@@ -348,7 +345,7 @@ class FileBrowser extends AbstractElementBrowser implements ElementBrowserInterf
         }
 
         $out = '<h3>' . $lang->getLL('files', true) . ' ' . $filesCount . ':</h3>';
-        $out .= $this->getFileSearchField();
+        $out .= GeneralUtility::makeInstance(FolderUtilityRenderer::class, $this)->getFileSearchField($this->searchWord);
         $out .= '<div id="filelist">';
         $out .= $this->getBulkSelector($filesCount);
 
@@ -370,12 +367,12 @@ class FileBrowser extends AbstractElementBrowser implements ElementBrowserInterf
      * Get a list of Files in a folder filtered by extension
      *
      * @param Folder $folder
-     * @param string $extensionList
+     * @param array $extensionList
      * @return File[]
      */
-    protected function getFilesInFolder(Folder $folder, $extensionList)
+    protected function getFilesInFolder(Folder $folder, array $extensionList)
     {
-        if ($extensionList !== '') {
+        if (!empty($extensionList)) {
             /** @var FileExtensionFilter $filter */
             $filter = GeneralUtility::makeInstance(FileExtensionFilter::class);
             $filter->setAllowedFileExtensions($extensionList);
@@ -425,26 +422,6 @@ class FileBrowser extends AbstractElementBrowser implements ElementBrowserInterf
     }
 
     /**
-     * Get the HTML data required for the file search field of the TYPO3 Element Browser.
-     *
-     * @return string HTML data required for the search field in the file list of the Element Browser
-     */
-    protected function getFileSearchField()
-    {
-        $action = $this->getScriptUrl() . GeneralUtility::implodeArrayForUrl('', $this->getUrlParameters([]));
-        $out = '
-                       <form method="post" action="' . htmlspecialchars($action) . '" style="padding-bottom: 15px;">
-                               <div class="input-group">
-                                       <input class="form-control" type="text" name="searchWord" value="' . htmlspecialchars($this->searchWord) . '">
-                                       <span class="input-group-btn">
-                                               <button class="btn btn-default" type="submit">' . $this->getLanguageService()->sL('LLL:EXT:filelist/Resources/Private/Language/locallang.xlf:search', true) . '</button>
-                                       </span>
-                               </div>
-                       </form>';
-        return $out;
-    }
-
-    /**
      * Checks if the given file is selectable in the filelist.
      *
      * By default all files are selectable. This method may be overwritten in child classes.
index 6840042..c437c0a 100644 (file)
@@ -28,7 +28,7 @@ interface LinkHandlerInterface
     public function getLinkAttributes();
 
     /**
-     * @param string[] $fieldDefinitions Array of link attribute field defintions
+     * @param string[] $fieldDefinitions Array of link attribute field definitions
      * @return string[]
      */
     public function modifyLinkAttributes(array $fieldDefinitions);
index 9340fe4..f578ad8 100644 (file)
@@ -228,6 +228,34 @@ class FolderUtilityRenderer
     }
 
     /**
+     * Get the HTML data required for the file search field of the TYPO3 Element Browser.
+     *
+     * @param string $searchWord
+     * @return string HTML data required for the search field in the file list of the Element Browser
+     */
+    public function getFileSearchField($searchWord)
+    {
+        $action = $this->parameterProvider->getScriptUrl()
+            . GeneralUtility::implodeArrayForUrl('', $this->parameterProvider->getUrlParameters([]));
+
+        $markup = [];
+        $markup[] = '<form method="post" action="' . htmlspecialchars($action) . '" style="padding-bottom: 15px;">';
+        $markup[] = '   <div class="input-group">';
+        $markup[] = '       <input class="form-control" type="text" name="searchWord" value="'
+            . htmlspecialchars($searchWord) . '">';
+        $markup[] = '       <span class="input-group-btn">';
+        $markup[] = '           <button class="btn btn-default" type="submit">'
+            . htmlspecialchars(
+                $this->getLanguageService()->sL('LLL:EXT:filelist/Resources/Private/Language/locallang.xlf:search')
+            )
+            . '</button>';
+        $markup[] = '       </span>';
+        $markup[] = '   </div>';
+        $markup[] = '</form>';
+        return implode(LF, $markup);
+    }
+
+    /**
      * @return LanguageService
      */
     protected function getLanguageService()
index b544076..71c1f06 100644 (file)
@@ -58,8 +58,8 @@ define(['jquery', 'TYPO3/CMS/Recordlist/ElementBrowser', 'TYPO3/CMS/Backend/Lega
         */
        BrowseFiles.Selector = {
                // Toggle selection button is pressed
-               toggle: function(event) {
-                       event.preventDefault();
+               toggle: function(e) {
+                       e.preventDefault();
                        var items = BrowseFiles.Selector.getItems();
                        if (items.length) {
                                items.each(function(position, item) {
@@ -68,8 +68,8 @@ define(['jquery', 'TYPO3/CMS/Recordlist/ElementBrowser', 'TYPO3/CMS/Backend/Lega
                        }
                },
                // Import selection button is pressed
-               handle: function(event) {
-                       event.preventDefault();
+               handle: function(e) {
+                       e.preventDefault();
                        var items = BrowseFiles.Selector.getItems();
                        var selectedItems = [];
                        if (items.length) {
@@ -100,8 +100,8 @@ define(['jquery', 'TYPO3/CMS/Recordlist/ElementBrowser', 'TYPO3/CMS/Backend/Lega
        $(function() {
                $.extend(BrowseFiles.elements, $('body').data('elements'));
 
-               $('[data-close]').on('click', function (event) {
-                       event.preventDefault();
+               $('[data-close]').on('click', function (e) {
+                       e.preventDefault();
                        BrowseFiles.File.insertElement('file_' + $(this).data('fileIndex'), $(this).data('close'));
                });
 
index e65f820..7a825d9 100644 (file)
@@ -14,39 +14,330 @@ namespace TYPO3\CMS\Rtehtmlarea\Controller;
  * The TYPO3 project - inspiring people to share!
  */
 
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\ServerRequestInterface;
+use TYPO3\CMS\Backend\Utility\BackendUtility;
+use TYPO3\CMS\Core\Resource\ResourceFactory;
+use TYPO3\CMS\Core\Resource\Service\MagicImageService;
+use TYPO3\CMS\Core\Service\DependencyOrderingService;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
-use TYPO3\CMS\Recordlist\Browser\ElementBrowser;
-use TYPO3\CMS\Recordlist\Controller\ElementBrowserController;
-use TYPO3\CMS\Rtehtmlarea\SelectImage;
+use TYPO3\CMS\Recordlist\Controller\AbstractLinkBrowserController;
+use TYPO3\CMS\Recordlist\LinkHandler\LinkHandlerInterface;
 
 /**
- * Script class for the Element Browser window to select images in RTE
+ * Script class to select images in RTE
  */
-class SelectImageController extends ElementBrowserController
+class SelectImageController extends AbstractLinkBrowserController
 {
     /**
+     * These file extensions are allowed in the "plain" image selection mode.
+     *
+     * @const
+     */
+    const PLAIN_MODE_IMAGE_FILE_EXTENSIONS = 'jpg,jpeg,gif,png';
+
+    /**
+     * Active with TYPO3 Element Browser: Contains the name of the form field for which this window
+     * opens - thus allows us to make references back to the main window in which the form is.
+     * Example value: "data[pages][39][bodytext]|||tt_content|"
+     * or "data[tt_content][NEW3fba56fde763d][image]|||gif,jpg,jpeg,tif,bmp,pcx,tga,png,pdf,ai|"
+     *
+     * Values:
+     * 0: form field name reference, eg. "data[tt_content][123][image]"
+     * 1: htmlArea RTE parameters: editorNo:contentTypo3Language
+     * 2: RTE config parameters: RTEtsConfigParams
+     * 3: allowed types. Eg. "tt_content" or "gif,jpg,jpeg,tif,bmp,pcx,tga,png,pdf,ai"
+     *
+     * $pArr = explode('|', $this->bparams);
+     * $formFieldName = $pArr[0];
+     * $allowedTablesOrFileTypes = $pArr[3];
+     *
+     * @var string
+     */
+    protected $bparams;
+
+    /**
+     * RTE configuration
+     *
+     * @var array
+     */
+    protected $RTEProperties = [];
+
+    /**
+     * Used with the Rich Text Editor.
+     * Example value: "tt_content:NEW3fba58c969f5c:bodytext:23:text:23:"
+     *
+     * @var string
+     */
+    protected $RTEtsConfigParams;
+
+    /**
+     * @var int
+     */
+    protected $editorNo;
+
+    /**
+     * TYPO3 language code of the content language
+     *
+     * @var int
+     */
+    protected $contentTypo3Language;
+
+    /**
+     * @var array
+     */
+    protected $buttonConfig = [];
+
+    /**
      * Initialize controller
      */
     protected function init()
     {
         parent::init();
+        $this->getLanguageService()->includeLLFile('EXT:rtehtmlarea/Resources/Private/Language/locallang_dialogs.xlf');
+    }
+
+    /**
+     * @param ServerRequestInterface $request
+     */
+    protected function initVariables(ServerRequestInterface $request)
+    {
+        parent::initVariables($request);
+
+        $queryParameters = $request->getQueryParams();
+        $this->bparams = isset($queryParameters['bparams']) ? $queryParameters['bparams'] : '';
+        $this->currentLinkParts['currentImage'] = !empty($queryParameters['fileUid']) ? $queryParameters['fileUid'] : 0;
+
+        // Process bparams
+        $pArr = explode('|', $this->bparams);
+        $pRteArr = explode(':', $pArr[1]);
+        $this->editorNo = $pRteArr[0];
+        $this->contentTypo3Language = $pRteArr[1];
+        $this->RTEtsConfigParams = $pArr[2];
+        if (!$this->editorNo) {
+            $this->editorNo = GeneralUtility::_GP('editorNo');
+            $this->contentTypo3Language = GeneralUtility::_GP('contentTypo3Language');
+            $this->RTEtsConfigParams = GeneralUtility::_GP('RTEtsConfigParams');
+        }
+        $pArr[1] = implode(':', array($this->editorNo, $this->contentTypo3Language));
+        $pArr[2] = $this->RTEtsConfigParams;
+        $pArr[3] = $this->displayedLinkHandlerId === 'plain'
+            ? self::PLAIN_MODE_IMAGE_FILE_EXTENSIONS
+            : '';
+        $this->bparams = implode('|', $pArr);
+
+        $RTEtsConfigParts = explode(':', $this->RTEtsConfigParams);
+        $RTEsetup = $this->getBackendUser()->getTSConfig('RTE', BackendUtility::getPagesTSconfig($RTEtsConfigParts[5]));
+        $this->RTEProperties = $RTEsetup['properties'];
+
+        $thisConfig = BackendUtility::RTEsetup($this->RTEProperties, $RTEtsConfigParts[0], $RTEtsConfigParts[2], $RTEtsConfigParts[4]);
+        $this->buttonConfig = isset($thisConfig['buttons.']['image.'])
+            ? $thisConfig['buttons.']['image.']
+            : [];
+    }
+
+    /**
+     * Initialize hook objects implementing the interface
+     *
+     * @throws \UnexpectedValueException
+     * @return void
+     */
+    protected function initHookObjects()
+    {
+        if (
+            isset($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['RteImageSelector']['hooks'])
+            && is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['RteImageSelector']['hooks'])
+        ) {
+            $hooks = GeneralUtility::makeInstance(DependencyOrderingService::class)->orderByDependencies(
+                $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['RteImageSelector']['hooks']
+            );
+            foreach ($hooks as $key => $hook) {
+                $this->hookObjects[] = GeneralUtility::makeInstance($hook['handler']);
+            }
+        }
+    }
+
+    /**
+     * Reads the configured image handlers from page TSconfig
+     *
+     * @return array
+     * @throws \UnexpectedValueException
+     */
+    protected function getLinkHandlers()
+    {
+        $imageHandler = $this->buttonConfig['options.']['imageHandler.'];
+
+        foreach ($this->hookObjects as $hookObject) {
+            if (method_exists($hookObject, 'modifyImageHandlers')) {
+                $imageHandler = $hookObject->modifyImageHandlers($imageHandler, $this->currentLinkParts);
+            }
+        }
+
+        if (empty($imageHandler)) {
+            throw new \UnexpectedValueException('No image handlers are configured. Check page TSconfig RTE.default.buttons.image.options.imageHandler.', 1455499673);
+        }
+
+        return $imageHandler;
+    }
+
+    /**
+     * Initialize $this->currentLinkParts and $this->currentLinkHandler
+     *
+     * @return void
+     */
+    protected function initCurrentUrl()
+    {
+        if (empty($this->currentLinkParts)) {
+            return;
+        }
+
+        $orderedHandlers = GeneralUtility::makeInstance(DependencyOrderingService::class)->orderByDependencies($this->linkHandlers, 'scanBefore', 'scanAfter');
+
+        // find responsible handler for current image
+        foreach ($orderedHandlers as $key => $configuration) {
+            /** @var LinkHandlerInterface $handler */
+            $handler = $configuration['handlerInstance'];
+            if ($handler->canHandleLink($this->currentLinkParts)) {
+                $this->currentLinkHandler = $handler;
+                $this->currentLinkHandlerId = $key;
+                break;
+            }
+        }
+        // reset the image reference if we have no handler for it
+        if (!$this->currentLinkHandler) {
+            $this->currentLinkParts = [];
+        }
+    }
+
+    /**
+     * Render the currently set URL
+     *
+     * @return string
+     */
+    protected function renderCurrentUrl()
+    {
+        return '<!-- Print current URL -->
+                               <table border="0" cellpadding="0" cellspacing="0" id="typo3-curUrl">
+                                       <tr>
+                                               <td>' . htmlspecialchars($this->getLanguageService()->getLL('currentImage')) . ': ' . htmlspecialchars($this->currentLinkHandler->formatCurrentUrl()) . '</td>
+                                       </tr>
+                               </table>';
+    }
+
+    /**
+     * Get the allowed items or tabs
+     *
+     * @return string[]
+     */
+    protected function getAllowedItems()
+    {
+        $allowedItems = array_keys($this->linkHandlers);
 
-        $lang = $this->getLanguageService();
-        $lang->includeLLFile('EXT:rtehtmlarea/Resources/Private/Language/locallang_selectimagecontroller.xlf');
-        $lang->includeLLFile('EXT:rtehtmlarea/Resources/Private/Language/locallang_dialogs.xlf');
+        foreach ($this->hookObjects as $hookObject) {
+            if (method_exists($hookObject, 'modifyAllowedItems')) {
+                $allowedItems = $hookObject->modifyAllowedItems($allowedItems, $this->currentLinkParts);
+            }
+        }
 
-        $this->mode = 'rte';
+        return $allowedItems;
     }
 
     /**
-     * Get instance of ElementBrowser
+     * @param array $overrides
      *
-     * This method shall be overwritten in subclasses
+     * @return array Array of parameters which have to be added to URLs
+     */
+    public function getUrlParameters(array $overrides = null)
+    {
+        return [
+            'act' => isset($overrides['act']) ? $overrides['act'] : $this->displayedLinkHandlerId,
+            'bparams' => $this->bparams,
+            'editorNo' => $this->editorNo
+        ];
+    }
+
+    /**
+     * @return array
+     */
+    public function getButtonConfiguration() {
+        return $this->buttonConfig;
+    }
+
+    /**
+     * @return array
+     */
+    public function getRteProperties() {
+        return $this->RTEProperties;
+    }
+
+    /**
+     * Compile the final tags to be inserted into RTE
+     *
+     * @param ServerRequestInterface $request
+     * @param ResponseInterface $response
+     * @return ResponseInterface
+     */
+    public function buildImageMarkup(ServerRequestInterface $request, ResponseInterface $response)
+    {
+        $this->initVariables($request);
+        $uidList = GeneralUtility::_GP('uidList');
+        // handle ajax request for
+        $uids = explode('|', $uidList);
+        $tags = array();
+        foreach ($uids as $uid) {
+            $fileObject = ResourceFactory::getInstance()->getFileObject((int)$uid);
+            // Get default values for alt and title attributes from file properties
+            $altText = $fileObject->getProperty('alternative');
+            $titleText = $fileObject->getProperty('title');
+            if ($this->displayedLinkHandlerId === 'magic') {
+                // Create the magic image service
+                $magicImageService = GeneralUtility::makeInstance(MagicImageService::class);
+                $magicImageService->setMagicImageMaximumDimensions($this->RTEProperties['default.']);
+                // Create the magic image
+                $imageConfiguration = array(
+                    'width' => GeneralUtility::_GP('cWidth'),
+                    'height' => GeneralUtility::_GP('cHeight')
+                );
+                $fileObject = $magicImageService->createMagicImage($fileObject, $imageConfiguration);
+                $width = $fileObject->getProperty('width');
+                $height = $fileObject->getProperty('height');
+            } else {
+                $width = $fileObject->getProperty('width');
+                $height = $fileObject->getProperty('height');
+                if (!$width || !$height) {
+                    $filePath = $fileObject->getForLocalProcessing(false);
+                    $imageInfo = @getimagesize($filePath);
+                    $width = $imageInfo[0];
+                    $height = $imageInfo[1];
+                }
+            }
+            $imageUrl = $fileObject->getPublicUrl();
+            // If file is local, make the url absolute
+            if (strpos($imageUrl, 'http') !== 0) {
+                $imageUrl = GeneralUtility::getIndpEnv('TYPO3_SITE_URL') . $imageUrl;
+            }
+            $tags[] = '<img src="' . htmlspecialchars($imageUrl) . '" width="' . htmlspecialchars($width) . '" height="' . htmlspecialchars($height) . '"'
+                      . (isset($this->buttonConfig['properties.']['class.']['default'])
+                    ? ' class="' . trim($this->buttonConfig['properties.']['class.']['default']) . '"'
+                    : '')
+                      . ' alt = "' . ($altText ? htmlspecialchars($altText) : '') . '"'
+                      . ($titleText ? ' title="' . htmlspecialchars($titleText) . '"' : '')
+                      . ' data-htmlarea-file-uid="' . (int)$uid . '" />';
+        }
+        $finalHtmlCode = implode(' ', $tags);
+
+        $response->getBody()->write(json_encode(['images' => $finalHtmlCode]));
+        return $response;
+    }
+
+    /**
+     * Return the ID of current page
      *
-     * @return ElementBrowser
+     * @return int
+     * @throws \RuntimeException
      */
-    protected function getElementBrowserInstance()
+    protected function getCurrentPageId()
     {
-        return GeneralUtility::makeInstance(SelectImage::class);
+        throw new \RuntimeException('Invalid method call. This function is not supported for image handlers', 14554996791);
     }
 }
diff --git a/typo3/sysext/rtehtmlarea/Classes/ImageHandler/AddImageHandler.php b/typo3/sysext/rtehtmlarea/Classes/ImageHandler/AddImageHandler.php
new file mode 100644 (file)
index 0000000..2975c43
--- /dev/null
@@ -0,0 +1,623 @@
+<?php
+namespace TYPO3\CMS\Rtehtmlarea\ImageHandler;
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+use Psr\Http\Message\ServerRequestInterface;
+use TYPO3\CMS\Backend\Tree\View\ElementBrowserFolderTreeView;
+use TYPO3\CMS\Backend\Utility\BackendUtility;
+use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
+use TYPO3\CMS\Core\Imaging\Icon;
+use TYPO3\CMS\Core\Imaging\IconFactory;
+use TYPO3\CMS\Core\Page\PageRenderer;
+use TYPO3\CMS\Core\Resource\Exception;
+use TYPO3\CMS\Core\Resource\File;
+use TYPO3\CMS\Core\Resource\FileInterface;
+use TYPO3\CMS\Core\Resource\FileRepository;
+use TYPO3\CMS\Core\Resource\Filter\FileExtensionFilter;
+use TYPO3\CMS\Core\Resource\Folder;
+use TYPO3\CMS\Core\Resource\ProcessedFile;
+use TYPO3\CMS\Core\Resource\ResourceFactory;
+use TYPO3\CMS\Core\Utility\GeneralUtility;
+use TYPO3\CMS\Lang\LanguageService;
+use TYPO3\CMS\Recordlist\Controller\AbstractLinkBrowserController;
+use TYPO3\CMS\Recordlist\LinkHandler\LinkHandlerInterface;
+use TYPO3\CMS\Recordlist\Tree\View\LinkParameterProviderInterface;
+use TYPO3\CMS\Recordlist\View\FolderUtilityRenderer;
+use TYPO3\CMS\Rtehtmlarea\Controller\SelectImageController;
+
+class AddImageHandler implements LinkParameterProviderInterface, LinkHandlerInterface
+{
+    /**
+     * Current mode: One of 'magic' or 'plain'
+     *
+     * @var string
+     */
+    protected $mode;
+
+    /**
+     * @var SelectImageController
+     */
+    protected $selectImageController;
+
+    /**
+     * Relevant for RTE mode "plain": the maximum width an image must have to be selectable.
+     *
+     * @var int
+     */
+    protected $plainMaxWidth;
+
+    /**
+     * Relevant for RTE mode "plain": the maximum height an image must have to be selectable.
+     *
+     * @var int
+     */
+    protected $plainMaxHeight;
+
+    /**
+     * @var string|NULL
+     */
+    protected $expandFolder;
+
+    /**
+     * @var string
+     */
+    protected $defaultClass;
+
+    /**
+     * @var Folder
+     */
+    protected $selectedFolder;
+
+    /**
+     * Holds information about files
+     *
+     * @var mixed[][]
+     */
+    protected $elements = array();
+
+    /**
+     * @var string
+     */
+    protected $searchWord;
+
+    /**
+     * @var FileRepository
+     */
+    protected $fileRepository;
+
+    /**
+     * URL of current request
+     *
+     * @var string
+     */
+    protected $thisScript = '';
+
+    /**
+     * @var IconFactory
+     */
+    protected $iconFactory;
+
+    /**
+     * Initialize the handler
+     *
+     * @param AbstractLinkBrowserController $linkBrowser
+     * @param string $identifier
+     * @param array $configuration Page TSconfig
+     *
+     * @return void
+     * @throws \InvalidArgumentException
+     */
+    public function initialize(AbstractLinkBrowserController $linkBrowser, $identifier, array $configuration)
+    {
+        $this->fileRepository = GeneralUtility::makeInstance(FileRepository::class);
+        $this->iconFactory = GeneralUtility::makeInstance(IconFactory::class);
+
+        $this->expandFolder = GeneralUtility::_GP('expandFolder');
+        $this->searchWord = (string)GeneralUtility::_GP('searchWord');
+
+        if ($identifier !== 'plain' && $identifier !== 'magic') {
+            throw new \InvalidArgumentException('The given identifier "' . $identifier . '" is not supported by this handler."', 1455499720);
+        }
+        if (!$linkBrowser instanceof SelectImageController) {
+            throw new \InvalidArgumentException('The given $linkBrowser must be of type SelectImageController."', 1455499721);
+        }
+        $this->mode = $identifier;
+        $this->selectImageController = $linkBrowser;
+
+        $buttonConfiguration = $linkBrowser->getButtonConfiguration();
+        $this->plainMaxWidth = empty($buttonConfiguration['options.']['plain.']['maxWidth'])
+            ? 640
+            : $buttonConfiguration['options.']['plain.']['maxWidth'];
+        $this->plainMaxHeight = empty($buttonConfiguration['options.']['plain.']['maxHeight'])
+            ? 680
+            : $buttonConfiguration['options.']['plain.']['maxHeight'];
+
+        $this->getLanguageService()->includeLLFile('EXT:rtehtmlarea/Resources/Private/Language/locallang_selectimagecontroller.xlf');
+
+    }
+
+    /**
+     * Render the link handler
+     *
+     * @param ServerRequestInterface $request
+     *
+     * @return string
+     */
+    public function render(ServerRequestInterface $request)
+    {
+        GeneralUtility::makeInstance(PageRenderer::class)->loadRequireJsModule('TYPO3/CMS/Rtehtmlarea/AddImage');
+
+        $backendUser = $this->getBackendUser();
+
+        // The key number 3 of the bparams contains the "allowed" string. Disallowed is not passed to
+        // the element browser at all but only filtered out in TCEMain afterwards
+        $bparams = explode('|', $this->selectImageController->getUrlParameters()['bparams']);
+        if (isset($bparams[3])) {
+            $allowedFileExtensions = GeneralUtility::trimExplode(',', $bparams[3], true);
+        } else {
+            $allowedFileExtensions = GeneralUtility::trimExplode(
+                ',',
+                $this->mode === 'plain'
+                    ? SelectImageController::PLAIN_MODE_IMAGE_FILE_EXTENSIONS
+                    : $GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext'],
+                true
+            );
+        }
+        if (!empty($allowedFileExtensions) && $allowedFileExtensions[0] !== 'sys_file' && $allowedFileExtensions[0] !== '*') {
+            // Create new filter object
+            $filterObject = GeneralUtility::makeInstance(FileExtensionFilter::class);
+            $filterObject->setAllowedFileExtensions($allowedFileExtensions);
+            // Set file extension filters on all storages
+            $storages = $backendUser->getFileStorages();
+            /** @var $storage \TYPO3\CMS\Core\Resource\ResourceStorage */
+            foreach ($storages as $storage) {
+                $storage->addFileAndFolderNameFilter(array($filterObject, 'filterFileList'));
+            }
+        }
+        if ($this->expandFolder) {
+            $fileOrFolderObject = null;
+
+            // Try to fetch the folder the user had open the last time he browsed files
+            // Fallback to the default folder in case the last used folder is not existing
+            try {
+                $fileOrFolderObject = ResourceFactory::getInstance()->retrieveFileOrFolderObject($this->expandFolder);
+            } catch (Exception $accessException) {
+                // We're just catching the exception here, nothing to be done if folder does not exist or is not accessible.
+            } catch (\InvalidArgumentException $driverMissingExecption) {
+                // We're just catching the exception here, nothing to be done if the driver does not exist anymore.
+            }
+
+            if ($fileOrFolderObject instanceof Folder) {
+                // It's a folder
+                $this->selectedFolder = $fileOrFolderObject;
+            } elseif ($fileOrFolderObject instanceof FileInterface) {
+                // It's a file
+                $this->selectedFolder = $fileOrFolderObject->getParentFolder();
+            }
+        }
+        // Or get the user's default upload folder
+        if (!$this->selectedFolder) {
+            try {
+                $this->selectedFolder = $backendUser->getDefaultUploadFolder();
+            } catch (\Exception $e) {
+                // The configured default user folder does not exist
+            }
+        }
+        // Build the file upload and folder creation form
+        $uploadForm = '';
+        $createFolder = '';
+        if ($this->selectedFolder) {
+            $folderUtilityRenderer = GeneralUtility::makeInstance(FolderUtilityRenderer::class, $this);
+            $uploadForm = $folderUtilityRenderer->uploadForm($this->selectedFolder, $allowedFileExtensions);
+            $createFolder = $folderUtilityRenderer->createFolder($this->selectedFolder);
+        }
+
+        // Getting flag for showing/not showing thumbnails:
+        $noThumbs = $backendUser->getTSConfigVal('options.noThumbsInRTEimageSelect');
+        $_MOD_SETTINGS = array();
+        if (!$noThumbs) {
+            // MENU-ITEMS, fetching the setting for thumbnails from File>List module:
+            $_MOD_MENU = array('displayThumbs' => '');
+            $_MCONF['name'] = 'file_list';
+            $_MOD_SETTINGS = BackendUtility::getModuleData($_MOD_MENU, GeneralUtility::_GP('SET'), $_MCONF['name']);
+        }
+        $noThumbs = $noThumbs ?: !$_MOD_SETTINGS['displayThumbs'];
+        // Create folder tree:
+        /** @var ElementBrowserFolderTreeView $folderTree */
+        $folderTree = GeneralUtility::makeInstance(ElementBrowserFolderTreeView::class);
+        $folderTree->setLinkParameterProvider($this);
+        $tree = $folderTree->getBrowsableTree();
+        if ($this->selectedFolder) {
+            $files = $this->renderFilesInFolder($this->selectedFolder, $allowedFileExtensions, $noThumbs);
+        } else {
+            $files = '';
+        }
+
+        $content = '';
+        // Insert the upload form on top, if so configured
+        if ($backendUser->getTSConfigVal('options.uploadFieldsInTopOfEB')) {
+            $content .= $uploadForm;
+        }
+        // Putting the parts together, side by side:
+        $content .= '
+
+                       <!--
+                               Wrapper table for folder tree / filelist:
+                       -->
+                       <div class="element-browser-section element-browser-filetree">
+                       <table border="0" cellpadding="0" cellspacing="0" id="typo3-EBfiles">
+                               <tr>
+                                       <td class="c-wCell" valign="top"><h3>' . $this->getLanguageService()->getLL('folderTree', true) . ':</h3>' . $tree . '</td>
+                                       <td class="c-wCell" valign="top">' . $files . '</td>
+                               </tr>
+                       </table>
+                       </div>
+                       ';
+        // Add help message
+        switch ($this->mode) {
+            case 'plain':
+                $content .= sprintf($this->getLanguageService()->getLL('plainImage_msg'), $this->plainMaxWidth, $this->plainMaxHeight);
+                break;
+            case 'magic':
+                $content .= sprintf($this->getLanguageService()->getLL('magicImage_msg'));
+                break;
+        }
+        // Adding create folder + upload forms if applicable:
+        if (!$backendUser->getTSConfigVal('options.uploadFieldsInTopOfEB')) {
+            $content .= $uploadForm;
+        }
+        $content .= $createFolder;
+        // Add some space
+        $content .= '<br /><br />';
+
+        return $content;
+    }
+
+    /**
+     * For TYPO3 Element Browser: Expand folder of files.
+     *
+     * @param Folder $folder The folder path to expand
+     * @param array $extensionList List of fileextensions to show
+     * @param bool $noThumbs Whether to show thumbnails or not. If set, no thumbnails are shown.
+     * @return string HTML output
+     */
+    public function renderFilesInFolder(Folder $folder, array $extensionList = [], $noThumbs = false)
+    {
+        if (!$folder->checkActionPermission('read')) {
+            return '';
+        }
+        $lang = $this->getLanguageService();
+        $titleLen = (int)$this->getBackendUser()->uc['titleLen'];
+
+        if ($this->searchWord !== '') {
+            $files = $this->fileRepository->searchByName($folder, $this->searchWord);
+        } else {
+            $extensionList = !empty($extensionList) && $extensionList[0] === '*' ? [] : $extensionList;
+            $files = $this->getFilesInFolder($folder, $extensionList);
+        }
+        $filesCount = count($files);
+
+        $lines = array();
+
+        // Create the header of current folder:
+        $folderIcon = $this->iconFactory->getIconForResource($folder, Icon::SIZE_SMALL);
+
+        $lines[] = '
+                       <tr class="t3-row-header">
+                               <th class="col-title" nowrap="nowrap">' . $folderIcon . ' ' . htmlspecialchars(GeneralUtility::fixed_lgd_cs($folder->getIdentifier(), $titleLen)) . '</th>
+                               <th class="col-control" nowrap="nowrap"></th>
+                               <th class="col-clipboard" nowrap="nowrap">
+                                       <a href="#" class="btn btn-default" id="t3js-importSelection" title="' . $lang->getLL('importSelection', true) . '">' . $this->iconFactory->getIcon('actions-document-import-t3d', Icon::SIZE_SMALL) . '</a>
+                                       <a href="#" class="btn btn-default" id="t3js-toggleSelection" title="' . $lang->getLL('toggleSelection', true) . '">' . $this->iconFactory->getIcon('actions-document-select', Icon::SIZE_SMALL) . '</a>
+                               </th>
+                               <th nowrap="nowrap">&nbsp;</th>
+                       </tr>';
+
+        if ($filesCount === 0) {
+            $lines[] = '
+                               <tr>
+                                       <td colspan="4">No files found.</td>
+                               </tr>';
+        }
+
+        foreach ($files as $fileObject) {
+            $fileExtension = $fileObject->getExtension();
+            // Thumbnail/size generation:
+            $imgInfo = array();
+            if (!$noThumbs && GeneralUtility::inList(strtolower($GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext'] . ',' . $GLOBALS['TYPO3_CONF_VARS']['SYS']['mediafile_ext']), strtolower($fileExtension))) {
+                $processedFile = $fileObject->process(
+                    ProcessedFile::CONTEXT_IMAGEPREVIEW,
+                    array('width' => 64, 'height' => 64)
+                );
+                $imageUrl = $processedFile->getPublicUrl(true);
+                $imgInfo = array(
+                    $fileObject->getProperty('width'),
+                    $fileObject->getProperty('height')
+                );
+                $pDim = $imgInfo[0] . 'x' . $imgInfo[1] . ' pixels';
+                $clickIcon = '<img src="' . $imageUrl . '"'
+                             . ' width="' . $processedFile->getProperty('width') . '"'
+                             . ' height="' . $processedFile->getProperty('height') . '"'
+                             . ' hspace="5" vspace="5" border="1" />';
+            } else {
+                $clickIcon = '';
+                $pDim = '';
+            }
+            // Create file icon:
+            $size = ' (' . GeneralUtility::formatSize($fileObject->getSize()) . 'bytes' . ($pDim ? ', ' . $pDim : '') . ')';
+            $icon = '<span title="' . htmlspecialchars($fileObject->getName() . $size) . '">' . $this->iconFactory->getIconForResource($fileObject, Icon::SIZE_SMALL) . '</span>';
+            // Create links for adding the file:
+            $filesIndex = count($this->elements);
+            $this->elements['file_' . $filesIndex] = array(
+                'type' => 'file',
+                'table' => 'sys_file',
+                'uid' => $fileObject->getUid(),
+                'fileName' => $fileObject->getName(),
+                'filePath' => $fileObject->getUid(),
+                'fileExt' => $fileExtension,
+                'fileIcon' => $icon
+            );
+            if ($this->fileIsSelectableInFileList($fileObject, $imgInfo)) {
+                $ATag = '<a href="#" class="btn btn-default" title="' . htmlspecialchars($fileObject->getName()) . '" data-file-index="' . htmlspecialchars($filesIndex) . '" data-close="0">';
+                $ATag_alt = '<a href="#" title="' . htmlspecialchars($fileObject->getName()) . '" data-file-index="' . htmlspecialchars($filesIndex) . '" data-close="1">';
+                $ATag_e = '</a>';
+                $bulkCheckBox = '<label class="btn btn-default btn-checkbox"><input type="checkbox" class="typo3-bulk-item" name="file_' . $filesIndex . '" value="0" /><span class="t3-icon fa"></span></label>';
+            } else {
+                $ATag = '';
+                $ATag_alt = '';
+                $ATag_e = '';
+                $bulkCheckBox = '';
+            }
+            // Create link to showing details about the file in a window:
+            $Ahref = BackendUtility::getModuleUrl('show_item', array(
+                'type' => 'file',
+                'table' => '_FILE',
+                'uid' => $fileObject->getCombinedIdentifier(),
+                'returnUrl' => GeneralUtility::getIndpEnv('REQUEST_URI')
+            ));
+
+            // Combine the stuff:
+            $filenameAndIcon = $ATag_alt . $icon . htmlspecialchars(GeneralUtility::fixed_lgd_cs($fileObject->getName(), $titleLen)) . $ATag_e;
+            // Show element:
+            $lines[] = '
+                                       <tr class="file_list_normal">
+                                               <td class="col-title" nowrap="nowrap">' . $filenameAndIcon . '&nbsp;</td>
+                                               <td class="col-control">
+                                                       <div class="btn-group">' . $ATag . '<span title="' .  $lang->getLL('addToList', true) . '">' . $this->iconFactory->getIcon('actions-edit-add', Icon::SIZE_SMALL)->render() . '</span>' . $ATag_e . '
+                                                       <a href="' . htmlspecialchars($Ahref) . '" class="btn btn-default" title="' . $lang->getLL('info', true) . '">' . $this->iconFactory->getIcon('actions-document-info', Icon::SIZE_SMALL) . '</a>
+                                               </td>
+                                               <td class="col-clipboard" valign="top">'. $bulkCheckBox .'</td>
+                                               <td nowrap="nowrap">&nbsp;' . $pDim . '</td>
+                                       </tr>';
+            if ($pDim) {
+                $lines[] = '
+                                       <tr>
+                                               <td class="filelistThumbnail" colspan="4">' . $ATag_alt . $clickIcon . $ATag_e . '</td>
+                                       </tr>';
+            }
+        }
+
+        $out = '<h3>' . $lang->getLL('files', true) . ' ' . $filesCount . ':</h3>';
+        $out .= GeneralUtility::makeInstance(FolderUtilityRenderer::class, $this)->getFileSearchField($this->searchWord);
+        $out .= '<div id="filelist">';
+        $out .= $this->getBulkSelector($filesCount);
+
+        // Wrap all the rows in table tags:
+        $out .= '
+
+       <!--
+               Filelisting
+       -->
+                       <table class="table table-striped table-hover" id="typo3-filelist">
+                               ' . implode('', $lines) . '
+                       </table>';
+        // Return accumulated content for filelisting:
+        $out .= '</div>';
+        return $out;
+    }
+
+
+    /**
+     * Get a list of Files in a folder filtered by extension
+     *
+     * @param Folder $folder
+     * @param array $extensionList
+     * @return File[]
+     */
+    protected function getFilesInFolder(Folder $folder, array $extensionList)
+    {
+        if (!empty($extensionList)) {
+            /** @var FileExtensionFilter $filter */
+            $filter = GeneralUtility::makeInstance(FileExtensionFilter::class);
+            $filter->setAllowedFileExtensions($extensionList);
+            $folder->setFileAndFolderNameFilters(array(array($filter, 'filterFileList')));
+        }
+        return $folder->getFiles();
+    }
+
+    /**
+     * Get the HTML data required for a bulk selection of files of the TYPO3 Element Browser.
+     *
+     * @param int $filesCount Number of files currently displayed
+     * @return string HTML data required for a bulk selection of files - if $filesCount is 0, nothing is returned
+     */
+    protected function getBulkSelector($filesCount)
+    {
+        if (!$filesCount) {
+            return '';
+        }
+
+        $lang = $this->getLanguageService();
+        $out = '';
+
+        // Getting flag for showing/not showing thumbnails:
+        $noThumbsInEB = $this->getBackendUser()->getTSConfigVal('options.noThumbsInEB');
+        if (!$noThumbsInEB && $this->selectedFolder) {
+            // MENU-ITEMS, fetching the setting for thumbnails from File>List module:
+            $_MOD_MENU = array('displayThumbs' => '');
+            $_MCONF['name'] = 'file_list';
+            $_MOD_SETTINGS = BackendUtility::getModuleData($_MOD_MENU, GeneralUtility::_GP('SET'), $_MCONF['name']);
+            $addParams = GeneralUtility::implodeArrayForUrl('', $this->getUrlParameters(['identifier' => $this->selectedFolder->getCombinedIdentifier()]));
+            $thumbNailCheck = '<div class="checkbox" style="padding:5px 0 15px 0"><label for="checkDisplayThumbs">'
+                              . BackendUtility::getFuncCheck(
+                    '',
+                    'SET[displayThumbs]',
+                    $_MOD_SETTINGS['displayThumbs'],
+                    $this->thisScript,
+                    $addParams,
+                    'id="checkDisplayThumbs"'
+                )
+                              . $lang->sL('LLL:EXT:lang/locallang_mod_file_list.xlf:displayThumbs', true) . '</label></div>';
+            $out .= $thumbNailCheck;
+        } else {
+            $out .= '<div style="padding-top: 15px;"></div>';
+        }
+        return $out;
+    }
+
+    /**
+     * Checks if the given file is selectable in the filelist.
+     *
+     * In "plain" RTE mode only image files with a maximum width and height are selectable.
+     *
+     * @param FileInterface $file
+     * @param array $imgInfo Image dimensions from \TYPO3\CMS\Core\Imaging\GraphicalFunctions::getImageDimensions()
+     * @return bool TRUE if file is selectable.
+     */
+    protected function fileIsSelectableInFileList(FileInterface $file, array $imgInfo)
+    {
+        return $this->mode !== 'plain'
+               || (GeneralUtility::inList(SelectImageController::PLAIN_MODE_IMAGE_FILE_EXTENSIONS, strtolower($file->getExtension()))
+                && $imgInfo[0] <= $this->plainMaxWidth && $imgInfo[1] <= $this->plainMaxHeight);
+    }
+
+    /**
+     * @return string[] Array of body-tag attributes
+     */
+    public function getBodyTagAttributes()
+    {
+        return [
+            'data-elements' => json_encode($this->elements)
+        ];
+    }
+
+    /**
+     * Returns the URL of the current script
+     *
+     * @return string
+     */
+    public function getScriptUrl()
+    {
+        return $this->selectImageController->getScriptUrl();
+    }
+
+    /**
+     * Provides an array or GET parameters for URL generation
+     *
+     * @param array $values Array of values to include into the parameters or which might influence the parameters
+     *
+     * @return string[] Array of parameters which have to be added to URLs
+     */
+    public function getUrlParameters(array $values)
+    {
+        $parameters = [
+            'expandFolder' => isset($values['identifier']) ? $values['identifier'] : (string)$this->expandFolder
+        ];
+        return array_merge($this->selectImageController->getUrlParameters($values), $parameters);
+    }
+
+    /**
+     * Return TRUE if the handler supports to update a link.
+     *
+     * This is useful for file or page links, when only attributes are changed.
+     *
+     * @return bool
+     */
+    public function isUpdateSupported()
+    {
+        return false;
+    }
+
+    /**
+     * @return array
+     */
+    public function getLinkAttributes()
+    {
+        return [];
+    }
+
+    /**
+     * @param string[] $fieldDefinitions Array of link attribute field definitions
+     * @return string[]
+     */
+    public function modifyLinkAttributes(array $fieldDefinitions)
+    {
+        return $fieldDefinitions;
+    }
+
+    /**
+     * Check if given value is currently the selected item
+     *
+     * This method is only used in the page tree.
+     *
+     * @param array $values Values to be checked
+     *
+     * @return bool Returns TRUE if the given values match the currently selected item
+     */
+    public function isCurrentlySelectedItem(array $values)
+    {
+        return false;
+    }
+
+    /**
+     * Checks if this is the handler for the given link
+     *
+     * The handler may store this information locally for later usage.
+     *
+     * @param array $linkParts Link parts as returned from TypoLinkCodecService
+     *
+     * @return bool
+     */
+    public function canHandleLink(array $linkParts)
+    {
+        return false;
+    }
+
+    /**
+     * Format the current link for HTML output
+     *
+     * @return string
+     */
+    public function formatCurrentUrl()
+    {
+        return '';
+    }
+
+    /**
+     * @return LanguageService
+     */
+    protected function getLanguageService()
+    {
+        return $GLOBALS['LANG'];
+    }
+
+    /**
+     * @return BackendUserAuthentication
+     */
+    protected function getBackendUser()
+    {
+        return $GLOBALS['BE_USER'];
+    }
+}
diff --git a/typo3/sysext/rtehtmlarea/Classes/ImageHandler/EditImageHandler.php b/typo3/sysext/rtehtmlarea/Classes/ImageHandler/EditImageHandler.php
new file mode 100644 (file)
index 0000000..1c60743
--- /dev/null
@@ -0,0 +1,280 @@
+<?php
+namespace TYPO3\CMS\Rtehtmlarea\ImageHandler;
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+use Psr\Http\Message\ServerRequestInterface;
+use TYPO3\CMS\Core\Page\PageRenderer;
+use TYPO3\CMS\Core\Resource\Exception\FileDoesNotExistException;
+use TYPO3\CMS\Core\Resource\FileInterface;
+use TYPO3\CMS\Core\Resource\ResourceFactory;
+use TYPO3\CMS\Core\Utility\GeneralUtility;
+use TYPO3\CMS\Lang\LanguageService;
+use TYPO3\CMS\Recordlist\Controller\AbstractLinkBrowserController;
+use TYPO3\CMS\Recordlist\LinkHandler\LinkHandlerInterface;
+use TYPO3\CMS\Recordlist\Tree\View\LinkParameterProviderInterface;
+use TYPO3\CMS\Rtehtmlarea\Controller\SelectImageController;
+
+class EditImageHandler implements LinkHandlerInterface, LinkParameterProviderInterface
+{
+    /**
+     * @var SelectImageController
+     */
+    protected $selectImageController;
+
+    /**
+     * @var FileInterface
+     */
+    protected $currentFile;
+
+    /**
+     * Initialize the handler
+     *
+     * @param AbstractLinkBrowserController $linkBrowser
+     * @param string $identifier
+     * @param array $configuration Page TSconfig
+     *
+     * @return void
+     */
+    public function initialize(AbstractLinkBrowserController $linkBrowser, $identifier, array $configuration)
+    {
+        if (!$linkBrowser instanceof SelectImageController) {
+            throw new \InvalidArgumentException('The given $linkBrowser must be of type SelectImageController."', 1455499721);
+        }
+        $this->selectImageController = $linkBrowser;
+    }
+
+    /**
+     * Checks if this is the handler for the given link
+     *
+     * The handler may store this information locally for later usage.
+     *
+     * @param array $linkParts Link parts as returned from TypoLinkCodecService
+     *
+     * @return bool
+     */
+    public function canHandleLink(array $linkParts)
+    {
+        if (!empty($linkParts['currentImage'])) {
+            try {
+                $this->currentFile = ResourceFactory::getInstance()->getFileObject($linkParts['currentImage']);
+                return true;
+            } catch (FileDoesNotExistException $e) {
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Format the current link for HTML output
+     *
+     * @return string
+     */
+    public function formatCurrentUrl()
+    {
+        return $this->currentFile->getStorage()->getName() . ': ' . $this->currentFile->getIdentifier();
+    }
+
+    /**
+     * Disallow this handler if no image is there to edit
+     *
+     * @param array $allowedItems
+     * @return array
+     */
+    public function modifyAllowedItems($allowedItems, $linkParts)
+    {
+        $selfPosition = array_search('image', $allowedItems, true);
+        if (empty($linkParts['currentImage']) && $selfPosition !== false) {
+            unset($allowedItems[$selfPosition]);
+        }
+        return $allowedItems;
+    }
+
+    /**
+     * Render the link handler
+     *
+     * @param ServerRequestInterface $request
+     *
+     * @return string
+     */
+    public function render(ServerRequestInterface $request)
+    {
+        GeneralUtility::makeInstance(PageRenderer::class)->loadRequireJsModule('TYPO3/CMS/Rtehtmlarea/EditImage');
+
+        $buttonConfig = $this->selectImageController->getButtonConfiguration();
+        $removedProperties = array();
+        if (is_array($buttonConfig['properties.'])) {
+            if ($buttonConfig['properties.']['removeItems']) {
+                $removedProperties = GeneralUtility::trimExplode(',', $buttonConfig['properties.']['removeItems'], true);
+            }
+        }
+
+        $rteProperties = $this->selectImageController->getRteProperties();
+
+        $lockPlainWidth = false;
+        $lockPlainHeight = false;
+        if (isset($rteProperties['default.']['proc.']['plainImageMode'])) {
+            $plainImageMode = $rteProperties['default.']['proc.']['plainImageMode'];
+            $lockPlainWidth = $plainImageMode === 'lockDimensions';
+            $lockPlainHeight = $lockPlainWidth || $plainImageMode === 'lockRatio' || $plainImageMode === 'lockRatioWhenSmaller';
+        }
+
+        $lang = $this->getLanguageService();
+
+        $content = '<div><form name="imageData" class="t3js-editForm"><table class="htmlarea-window-table">';
+
+        if (!in_array('class', $removedProperties, true) && !empty($buttonConfig['properties.']['class.']['allowedClasses'])) {
+            $classesImageArray = GeneralUtility::trimExplode(',', $buttonConfig['properties.']['class.']['allowedClasses'], true);
+            $classesImageJSOptions = '<option value=""></option>';
+            foreach ($classesImageArray as $class) {
+                $classesImageJSOptions .= '<option value="' . $class . '">' . $class . '</option>';
+            }
+            $content .= '<tr><td><label for="iClass">' . $lang->getLL('class') . ': </label></td><td>
+                <select id="t3js-iClass" name="iClass" style="width:140px;">' . $classesImageJSOptions . '
+                </select></td></tr>';
+        }
+        if (!in_array('width', $removedProperties, true) && !($this->currentFile && $lockPlainWidth /* && check if it is a RTE magic image (no clue how to do that now with FAL)*/)) {
+            $content .= '<tr><td><label for="iWidth">' . $lang->getLL('width') . ': </label></td><td>
+                <input type="text" id="t3js-iWidth" name="iWidth" value="" style="width: 39px;" maxlength="4" /></td></tr>';
+        }
+        if (!in_array('height', $removedProperties, true) && !($this->currentFile && $lockPlainHeight /* && check if it is a RTE magic image (no clue how to do that now with FAL)*/)) {
+            $content .= '<tr><td><label for="iHeight">' . $lang->getLL('height') . ': </label></td><td>
+                <input type="text" id="t3js-iHeight" name="iHeight" value="" style="width: 39px;" maxlength="4" /></td></tr>';
+        }
+        if (!in_array('border', $removedProperties, true)) {
+            $content .= '<tr><td><label for="iBorder">' . $lang->getLL('border') . ': </label></td><td>
+                <input type="checkbox" id="t3js-iBorder" name="iBorder" value="1" /></td></tr>';
+        }
+        if (!in_array('float', $removedProperties, true)) {
+            $content .= '<tr><td><label for="iFloat">' . $lang->getLL('float') . ': </label></td><td>
+                <select id="t3js-iFloat" name="iFloat">'
+                        . '<option value="">' . $lang->getLL('notSet') . '</option>'
+                        . '<option value="none">' . $lang->getLL('nonFloating') . '</option>'
+                        . '<option value="left">' . $lang->getLL('left') . '</option>'
+                        . '<option value="right">' . $lang->getLL('right') . '</option>'
+                        . '</select>
+                </td></tr>';
+        }
+        if (!in_array('paddingTop', $removedProperties, true)) {
+            $content .= '<tr><td><label for="iPaddingTop">' . $lang->getLL('padding_top') . ': </label></td><td><input type="text" id="t3js-iPaddingTop" name="iPaddingTop" value="" style="width: 39px;" maxlength="4" /></td></tr>';
+        }
+        if (!in_array('paddingRight', $removedProperties, true)) {
+            $content .= '<tr><td><label for="iPaddingRight">' . $lang->getLL('padding_right') . ': </label></td><td><input type="text" id="t3js-iPaddingRight" name="iPaddingRight" value="" style="width: 39px;" maxlength="4" /></td></tr>';
+        }
+        if (!in_array('paddingBottom', $removedProperties, true)) {
+            $content .= '<tr><td><label for="iPaddingBottom">' . $lang->getLL('padding_bottom') . ': </label></td><td><input type="text" id="t3js-iPaddingBottom" name="iPaddingBottom" value="" style="width: 39px;" maxlength="4" /></td></tr>';
+        }
+        if (!in_array('paddingLeft', $removedProperties, true)) {
+            $content .= '<tr><td><label for="iPaddingLeft">' . $lang->getLL('padding_left') . ': </label></td><td><input type="text" id="t3js-iPaddingLeft" name="iPaddingLeft" value="" style="width: 39px;" maxlength="4" /></td></tr>';
+        }
+        if (!in_array('title', $removedProperties, true)) {
+            $content .= '<tr><td><label for="iTitle">' . $lang->getLL('title') . ': </label></td><td><input type="text" id="t3js-iTitle" name="iTitle" style="width:192px;" maxlength="256" /></td></tr>';
+        }
+        if (!in_array('alt', $removedProperties, true)) {
+            $content .= '<tr><td><label for="iAlt">' . $lang->getLL('alt') . ': </label></td><td><input type="text" id="t3js-iAlt" name="iAlt" style="width:192px;" maxlength="256" /></td></tr>';
+        }
+        if (!in_array('lang', $removedProperties, true)) {
+            $content .= '<tr id="t3js-languageSetting"><td><label for="iLang"></label></td><td><select id="t3js-iLang" name="iLang">###lang_selector###</select></td></tr>';
+        }
+        if (!in_array('data-htmlarea-clickenlarge', $removedProperties, true) && !in_array('clickenlarge', $removedProperties, true)) {
+            $content .= '<tr><td><label for="iClickEnlarge">' . $lang->sL('LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf:image_zoom') . ': </label></td><td><input type="checkbox" name="iClickEnlarge" id="t3js-iClickEnlarge" value="0" /></td></tr>';
+        }
+        $content .= '<tr><td></td><td><input class="btn btn-default" type="submit" value="' . $lang->getLL('update') . '"></td></tr></table></form>';
+
+        return $content;
+    }
+
+    /**
+     * Return TRUE if the handler supports to update a link.
+     *
+     * This is useful for file or page links, when only attributes are changed.
+     *
+     * @return bool
+     */
+    public function isUpdateSupported()
+    {
+        return false;
+    }
+
+    /**
+     * @return string[] Array of body-tag attributes
+     */
+    public function getBodyTagAttributes()
+    {
+        return [
+            'data-classes-image' => $this->selectImageController->getButtonConfiguration()['properties.']['class.']['allowedClasses'] || $this->selectImageController->getRteProperties()['default.']['classesImage']
+        ];
+    }
+
+    /**
+     * Returns the URL of the current script
+     *
+     * @return string
+     */
+    public function getScriptUrl()
+    {
+        return $this->selectImageController->getScriptUrl();
+    }
+
+    /**
+     * Provides an array or GET parameters for URL generation
+     *
+     * @param array $values Array of values to include into the parameters or which might influence the parameters
+     *
+     * @return string[] Array of parameters which have to be added to URLs
+     */
+    public function getUrlParameters(array $values)
+    {
+        return [];
+    }
+
+    /**
+     * Check if given value is currently the selected item
+     *
+     * This method is only used in the page tree.
+     *
+     * @param array $values Values to be checked
+     *
+     * @return bool Returns TRUE if the given values match the currently selected item
+     */
+    public function isCurrentlySelectedItem(array $values)
+    {
+        return false;
+    }
+
+    /**
+     * @return array
+     */
+    public function getLinkAttributes()
+    {
+        return [];
+    }
+
+    /**
+     * @param string[] $fieldDefinitions Array of link attribute field definitions
+     * @return string[]
+     */
+    public function modifyLinkAttributes(array $fieldDefinitions)
+    {
+        return $fieldDefinitions;
+    }
+
+    /**
+     * @return LanguageService
+     */
+    protected function getLanguageService()
+    {
+        return $GLOBALS['LANG'];
+    }
+}
diff --git a/typo3/sysext/rtehtmlarea/Classes/SelectImage.php b/typo3/sysext/rtehtmlarea/Classes/SelectImage.php
deleted file mode 100644 (file)
index bbdf0ae..0000000
+++ /dev/null
@@ -1,807 +0,0 @@
-<?php
-namespace TYPO3\CMS\Rtehtmlarea;
-
-/*
- * This file is part of the TYPO3 CMS project.
- *
- * It is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License, either version 2
- * of the License, or any later version.
- *
- * For the full copyright and license information, please read the
- * LICENSE.txt file that was distributed with this source code.
- *
- * The TYPO3 project - inspiring people to share!
- */
-
-use TYPO3\CMS\Backend\Tree\View\ElementBrowserFolderTreeView;
-use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
-use TYPO3\CMS\Core\Messaging\FlashMessage;
-use TYPO3\CMS\Core\Resource\File;
-use TYPO3\CMS\Core\Resource\FileInterface;
-use TYPO3\CMS\Core\Resource\Folder;
-use TYPO3\CMS\Core\Resource\ResourceFactory;
-use TYPO3\CMS\Core\Resource\Service\MagicImageService;
-use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
-use TYPO3\CMS\Core\Utility\GeneralUtility;
-use TYPO3\CMS\Lang\LanguageService;
-use TYPO3\CMS\Recordlist\Browser\ElementBrowser;
-
-/**
- * Script Class
- */
-class SelectImage extends ElementBrowser
-{
-    /**
-     * These file extensions are allowed in the "plain" image selection mode.
-     *
-     * @const
-     */
-    const PLAIN_MODE_IMAGE_FILE_EXTENSIONS = 'jpg,jpeg,gif,png';
-
-    /**
-     * @var string
-     */
-    public $extKey = 'rtehtmlarea';
-
-    /**
-     * @var string
-     */
-    public $content;
-
-    /**
-     * @var array
-     */
-    public $allowedItems;
-
-    /**
-     * @var array
-     */
-    public $allowedFileTypes = array();
-
-    /**
-     * @var string
-     */
-    protected $defaultClass;
-
-    /**
-     * Relevant for RTE mode "plain": the maximum width an image must have to be selectable.
-     *
-     * @var int
-     */
-    protected $plainMaxWidth;
-
-    /**
-     * Relevant for RTE mode "plain": the maximum height an image must have to be selectable.
-     *
-     * @var int
-     */
-    protected $plainMaxHeight;
-
-    /**
-     * @var string
-     */
-    protected $imgPath;
-
-    /**
-     * @var int
-     */
-    public $editorNo;
-
-    /**
-     * @var int
-     */
-    public $sys_language_content;
-
-    /**
-     * @var array
-     */
-    public $buttonConfig;
-
-    /**
-     * @var bool
-     */
-    public $addModifyTab;
-
-    /**
-     * @var string
-     */
-    protected $hookName = 'ext/rtehtmlarea/mod4/class.tx_rtehtmlarea_select_image.php';
-
-    /**
-     * Initialisation
-     *
-     * @return void
-     */
-    public function init()
-    {
-        $this->initVariables();
-
-        $this->initHookObjects();
-        $this->allowedItems = $this->getAllowedItems('magic,plain,image');
-        // Insert the image and exit
-        $this->insertImage();
-        // or render dialogue
-        $this->initDocumentTemplate();
-    }
-
-    /**
-     * Initialize class variables
-     *
-     * @return void
-     */
-    public function initVariables()
-    {
-        parent::initVariables();
-
-        // Get "act"
-        $this->act = GeneralUtility::_GP('act');
-        if (!$this->act) {
-            $this->act = false;
-        }
-        $this->addModifyTab = (bool)GeneralUtility::_GP('addModifyTab');
-        // Process bparams
-        $pArr = explode('|', $this->bparams);
-        $pRteArr = explode(':', $pArr[1]);
-        $this->editorNo = $pRteArr[0];
-        $this->sys_language_content = $pRteArr[1];
-        $this->RTEtsConfigParams = $pArr[2];
-        if (!$this->editorNo) {
-            $this->editorNo = GeneralUtility::_GP('editorNo');
-            $this->sys_language_content = GeneralUtility::_GP('sys_language_content');
-            $this->RTEtsConfigParams = GeneralUtility::_GP('RTEtsConfigParams');
-        }
-        $pArr[1] = implode(':', array($this->editorNo, $this->sys_language_content));
-        $pArr[2] = $this->RTEtsConfigParams;
-        if ($this->act === 'dragdrop' || $this->act === 'plain') {
-            $this->allowedFileTypes = explode(',', self::PLAIN_MODE_IMAGE_FILE_EXTENSIONS);
-        }
-        $pArr[3] = implode(',', $this->allowedFileTypes);
-        $this->bparams = implode('|', $pArr);
-
-        $this->buttonConfig = $this->getButtonConfig();
-        $this->imgPath = $this->getImgPath();
-        $this->defaultClass = $this->getDefaultClass();
-        $this->setMaximumPlainImageDimensions();
-    }
-
-    /**
-     * Initialize document template object
-     *
-     * @return void
-     */
-    protected function initDocumentTemplate()
-    {
-        parent::initDocumentTemplate();
-
-        $this->doc->bodyTagAdditions = 'onload="SelectImage.initEventListeners();"';
-
-        $pageRenderer = $this->getPageRenderer();
-        $pageRenderer->addCssFile(ExtensionManagementUtility::extRelPath('t3skin') . 'rtehtmlarea/htmlarea.css');
-        $pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/LegacyTree', 'function(Tree) {
-                       Tree.ajaxID = "sc_alt_file_navframe_expandtoggle";
-               }');
-        $pageRenderer->loadRequireJsModule('TYPO3/CMS/Rtehtmlarea/Modules/SelectImage', 'function(SelectImage) {
-                       SelectImage.editorNo = ' . GeneralUtility::quoteJSvalue($this->editorNo) . ';
-                       SelectImage.act = ' . GeneralUtility::quoteJSvalue($this->act ?: reset($this->allowedItems)) . ';
-                       SelectImage.sys_language_content = ' . GeneralUtility::quoteJSvalue($this->sys_language_content) . ';
-                       SelectImage.RTEtsConfigParams = ' . GeneralUtility::quoteJSvalue(rawurlencode($this->RTEtsConfigParams)) . ';
-                       SelectImage.bparams = ' . GeneralUtility::quoteJSvalue($this->bparams) . ';
-               }');
-    }
-
-    /**
-     * Insert the image in the editing area
-     *
-     * @return void
-     */
-    protected function insertImage()
-    {
-        $uidList = (string)GeneralUtility::_GP('uidList');
-        if (!GeneralUtility::_GP('insertImage') || !$uidList) {
-            return;
-        }
-        $uids = explode('|', $uidList);
-        $insertJsStatements = array();
-        foreach ($uids as $uid) {
-            /** @var $fileObject File */
-            $fileObject = ResourceFactory::getInstance()->getFileObject((int)$uid);
-            // Get default values for alt and title attributes from file properties
-            $altText = $fileObject->getProperty('alternative');
-            $titleText = $fileObject->getProperty('title');
-            switch ($this->act) {
-                case 'magic':
-                    $insertJsStatements[] = $this->insertMagicImage($fileObject, $altText, $titleText, 'data-htmlarea-file-uid="' . $uid . '"');
-                    break;
-                case 'plain':
-                    $insertJsStatements[] = $this->insertPlainImage($fileObject, $altText, $titleText, 'data-htmlarea-file-uid="' . $uid . '"');
-                    break;
-                default:
-                    // Call hook
-                    foreach ($this->hookObjects as $hookObject) {
-                        if (method_exists($hookObject, 'insertElement')) {
-                            $hookObject->insertElement($this->act);
-                        }
-                    }
-            }
-        }
-        $this->insertImages($insertJsStatements);
-        die;
-    }
-
-    /**
-     * Insert a magic image
-     *
-     * @param File $fileObject: the image file
-     * @param string $altText: text for the alt attribute of the image
-     * @param string $titleText: text for the title attribute of the image
-     * @param string $additionalParams: text representing more HTML attributes to be added on the img tag
-     * @return string the magic image JS insertion statement
-     */
-    public function insertMagicImage(File $fileObject, $altText = '', $titleText = '', $additionalParams = '')
-    {
-        // Create the magic image service
-        /** @var $magicImageService MagicImageService */
-        $magicImageService = GeneralUtility::makeInstance(MagicImageService::class);
-        $magicImageService->setMagicImageMaximumDimensions($this->RTEProperties['default.']);
-        // Create the magic image
-        $imageConfiguration = array(
-            'width' => GeneralUtility::_GP('cWidth'),
-            'height' => GeneralUtility::_GP('cHeight')
-        );
-        $magicImage = $magicImageService->createMagicImage($fileObject, $imageConfiguration);
-        $imageUrl = $magicImage->getPublicUrl();
-        // If file is local, make the url absolute
-        if (substr($imageUrl, 0, 4) !== 'http') {
-            $imageUrl = $this->siteURL . $imageUrl;
-        }
-        // Insert the magic image
-        return $this->imageInsertJsStatement($imageUrl, $magicImage->getProperty('width'), $magicImage->getProperty('height'), $altText, $titleText, $additionalParams);
-    }
-
-    /**
-     * Insert a plain image
-     *
-     * @param \TYPO3\CMS\Core\Resource\File $fileObject: the image file
-     * @param string $altText: text for the alt attribute of the image
-     * @param string $titleText: text for the title attribute of the image
-     * @param string $additionalParams: text representing more HTML attributes to be added on the img tag
-     * @return string the plain image JS insertion statement
-     */
-    public function insertPlainImage(File $fileObject, $altText = '', $titleText = '', $additionalParams = '')
-    {
-        $width = $fileObject->getProperty('width');
-        $height = $fileObject->getProperty('height');
-        if (!$width || !$height) {
-            $filePath = $fileObject->getForLocalProcessing(false);
-            $imageInfo = @getimagesize($filePath);
-            $width = $imageInfo[0];
-            $height = $imageInfo[1];
-        }
-        $imageUrl = $fileObject->getPublicUrl();
-        // If file is local, make the url absolute
-        if (substr($imageUrl, 0, 4) !== 'http') {
-            $imageUrl = $this->siteURL . $imageUrl;
-        }
-        return $this->imageInsertJsStatement($imageUrl, $width, $height, $altText, $titleText, $additionalParams);
-    }
-
-    /**
-     * Assemble the image insertion JS statement
-     *
-     * @param string $url: the url of the image
-     * @param int $width: the width of the image
-     * @param int $height: the height of the image
-     * @param string $altText: text for the alt attribute of the image
-     * @param string $titleText: text for the title attribute of the image
-     * @param string $additionalParams: text representing more html attributes to be added on the img tag
-     * @return string the image insertion JS statement
-     */
-    protected function imageInsertJsStatement($url, $width, $height, $altText = '', $titleText = '', $additionalParams = '')
-    {
-        return 'insertImage(' . GeneralUtility::quoteJSvalue($url) . ',' . $width . ',' . $height . ',' . GeneralUtility::quoteJSvalue($altText)
-            . ',' . GeneralUtility::quoteJSvalue($titleText) . ',' . GeneralUtility::quoteJSvalue($additionalParams) . ');';
-    }
-
-    /**
-     * Echo the HTML page and JS that will insert the images
-     *
-     * @param array $insertJsStatements: array of image insertion JS statements
-     * @return void
-     */
-    protected function insertImages($insertJsStatements)
-    {
-        echo '
-<!DOCTYPE html>
-<html>
-<head>
-       <title>Untitled</title>
-       <script type="text/javascript">
-               var plugin = window.parent.RTEarea[' . GeneralUtility::quoteJSvalue($this->editorNo) . '].editor.getPlugin("TYPO3Image");
-               var imageTags = [];
-               function insertImage(file,width,height,alt,title,additionalParams) {
-                       imageTags.push(\'<img src="\'+file+\'" width="\'+parseInt(width)+\'" height="\'+parseInt(height)+\'"\''
-            . ($this->defaultClass ? '+\' class="' . $this->defaultClass . '"\'' : '')
-            . '+ (alt ? \' alt="\' + alt + \'"\' : \'\') + (title ? \' title="\' + title + \'"\' : \'\') + (additionalParams ? \' \' + additionalParams : \'\') +\' />\');
-               }
-       </script>
-</head>
-<body>
-<script type="text/javascript">
-' . implode(LF, $insertJsStatements) . '
-plugin.insertImage(imageTags.join(\' \'));
-</script>
-</body>
-</html>';
-    }
-
-    /**
-     * Generate JS code to be used on the image insert/modify dialogue
-     *
-     * @param string $act: the action to be performed
-     * @param string $editorNo: the number of the RTE instance on the page
-     * @param string $sys_language_content: the language of the content element
-     * @return string the generated JS code
-     */
-    public function getJSCode($act, $editorNo, $sys_language_content)
-    {
-        return '
-                       function insertElement(table, uid, type, fileName, filePath, fileExt, fileIcon, action, close) {
-                               return SelectImage.jumpToUrl(' . GeneralUtility::quoteJSvalue($this->getThisScript()) . ' + "insertImage=1&uidList=" + uid);
-                       }
-                       function insertMultiple(table, uidList) {
-                               var uids = uidList.join("|");
-                               return SelectImage.jumpToUrl(' . GeneralUtility::quoteJSvalue($this->getThisScript()) . ' + "insertImage=1&uidList=" + uids);
-                       }
-                       function jumpToUrl(URL,anchor) {
-                               SelectImage.jumpToUrl(URL, anchor);
-                       };';
-    }
-
-    /**
-     * Session data for this class can be set from outside with this method.
-     * Call after init()
-     *
-     * @param array $data Session data array
-     * @return array Session data and boolean which indicates that data needs to be stored in session because it's changed
-     */
-    public function processSessionData($data)
-    {
-        $store = false;
-        if ($this->act !== 'image') {
-            if ($this->act) {
-                $data['act'] = $this->act;
-                $store = true;
-            } else {
-                $this->act = $data['act'];
-            }
-        }
-        if (isset($this->expandFolder)) {
-            $data['expandFolder'] = $this->expandFolder;
-            $store = true;
-        } else {
-            $this->expandFolder = $data['expandFolder'];
-        }
-        return array($data, $store);
-    }
-
-    /**
-     * Rich Text Editor (RTE) image selector
-     *
-     * @param bool $wiz Not used here, kept for method signature compatibility with parent class
-     * @return string Modified content variable.
-     * @return string
-     */
-    protected function main_rte($wiz = false)
-    {
-        // Starting content:
-        $lang = $this->getLanguageService();
-        $this->content = $this->doc->startPage($lang->getLL('Insert Image', true));
-
-        $this->content .= $this->doc->getTabMenuRaw($this->buildMenuArray($wiz, $this->allowedItems));
-        switch ($this->act) {
-            case 'image':
-                $classesImage = $this->buttonConfig['properties.']['class.']['allowedClasses'] || $this->RTEProperties['default.']['classesImage'] ? 'true' : 'false';
-                $removedProperties = array();
-                if (is_array($this->buttonConfig['properties.'])) {
-                    if ($this->buttonConfig['properties.']['removeItems']) {
-                        $removedProperties = GeneralUtility::trimExplode(',', $this->buttonConfig['properties.']['removeItems'], true);
-                    }
-                }
-                $classesImageJSOptions = '';
-                if ($this->buttonConfig['properties.']['class.']['allowedClasses']) {
-                    $classesImageArray = GeneralUtility::trimExplode(',', $this->buttonConfig['properties.']['class.']['allowedClasses'], true);
-                    $classesImageJSOptions = '<option value=""></option>';
-                    foreach ($classesImageArray as $class) {
-                        $classesImageJSOptions .= '<option value="' . $class . '">' . $class . '</option>';
-                    }
-                }
-                $lockPlainWidth = 'false';
-                $lockPlainHeight = 'false';
-                if (is_array($this->RTEProperties['default.']['proc.']) && $this->RTEProperties['default.']['proc.']['plainImageMode']) {
-                    $plainImageMode = $this->RTEProperties['default.']['proc.']['plainImageMode'];
-                    $lockPlainWidth = $plainImageMode == 'lockDimensions' ? 'true' : 'false';
-                    $lockPlainHeight = $lockPlainWidth || $plainImageMode == 'lockRatio' || $plainImageMode == 'lockRatioWhenSmaller' ? 'true' : 'false';
-                }
-                $labels = array('notSet','nonFloating','right','left','class','width','height','border','float','padding_top','padding_left','padding_bottom','padding_right','title','alt','update');
-                foreach ($labels as $label) {
-                    $localizedLabels[$label] = $lang->getLL($label);
-                }
-                $localizedLabels['image_zoom'] = $lang->sL('LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf:image_zoom', true);
-                $JScode = '
-                                       require(["TYPO3/CMS/Rtehtmlarea/Modules/SelectImage"], function(SelectImage) {
-                                               SelectImage.editorNo = "' . $this->editorNo . '";
-                                               SelectImage.act = "' . $this->act . '";
-                                               SelectImage.sys_language_content = "' . $this->sys_language_content . '";
-                                               SelectImage.RTEtsConfigParams = "' . rawurlencode($this->RTEtsConfigParams) . '";
-                                               SelectImage.bparams = "' . $this->bparams . '";
-                                               SelectImage.classesImage =  ' . $classesImage . ';
-                                               SelectImage.labels = ' . json_encode($localizedLabels) . ';
-                                               SelectImage.Form.build(' . GeneralUtility::quoteJSvalue($classesImageJSOptions) . ', ' . json_encode($removedProperties) . ', ' . $lockPlainWidth . ', ' . $lockPlainHeight . ');
-                                               SelectImage.Form.insertImageProperties();
-                                       });';
-                $this->content .= '<br />' . $this->doc->wrapScriptTags($JScode);
-                break;
-            case 'plain':
-            case 'magic':
-                // Create folder tree:
-                $foldertree = GeneralUtility::makeInstance(FolderTree::class);
-                $foldertree->thisScript = $this->thisScript;
-                $tree = $foldertree->getBrowsableTree();
-                // Get currently selected folder
-                if ($this->curUrlInfo['value'] && $this->curUrlInfo['act'] === $this->act) {
-                    $cmpPath = $this->curUrlInfo['value'];
-                    if (!isset($this->expandFolder)) {
-                        $this->expandFolder = $cmpPath;
-                    }
-                }
-                // Get the selected folder
-                $selectedFolder = false;
-                if ($this->expandFolder) {
-                    $fileOrFolderObject = null;
-                    try {
-                        $fileOrFolderObject = ResourceFactory::getInstance()->retrieveFileOrFolderObject($this->expandFolder);
-                    } catch (\Exception $e) {
-                        // No path is selected
-                    }
-                    if ($fileOrFolderObject instanceof Folder) {
-                        // it's a folder
-                        $selectedFolder = $fileOrFolderObject;
-                    } elseif ($fileOrFolderObject instanceof FileInterface) {
-                        // it's a file
-                        try {
-                            $selectedFolder = $fileOrFolderObject->getParentFolder();
-                        } catch (\Exception $e) {
-                            // Accessing the parent folder failed for some reason. e.g. permissions
-                        }
-                    }
-                }
-                // If no folder is selected, get the user's default upload folder
-                $backendUser = $this->getBackendUser();
-                if (!$selectedFolder) {
-                    try {
-                        $selectedFolder = $backendUser->getDefaultUploadFolder();
-                    } catch (\Exception $e) {
-                        // The configured default user folder does not exist
-                    }
-                }
-                // Build the file upload and folder creation form
-                $uploadForm = '';
-                $createFolder = '';
-                if ($selectedFolder) {
-                    $uploadForm = $this->uploadForm($selectedFolder);
-                    $createFolder = $this->createFolder($selectedFolder);
-                }
-                // Insert the upload form on top, if so configured
-                if ($backendUser->getTSConfigVal('options.uploadFieldsInTopOfEB')) {
-                    $this->content .= $uploadForm;
-                }
-                // Render the filelist if there is a folder selected
-                $files = '';
-                if ($selectedFolder) {
-                    $files = $this->TBE_expandFolder(
-                        $selectedFolder,
-                        $this->act === 'plain' ? self::PLAIN_MODE_IMAGE_FILE_EXTENSIONS : $GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext'],
-                        $backendUser->getTSConfigVal('options.noThumbsInRTEimageSelect')
-                    );
-                }
-                // Setup filelist indexed elements:
-                $this->doc->JScode .= $this->doc->wrapScriptTags('
-                               require(["TYPO3/CMS/Recordlist/BrowseFiles"], function(BrowseFiles) {
-                                       BrowseFiles.addElements(' . json_encode($this->elements) . ');
-                               });');
-                // Wrap tree
-                $this->content .= '
-
-                               <!--
-                                       Wrapper table for folder tree / file/folder list:
-                               -->
-                                               <table border="0" cellpadding="0" cellspacing="0" id="typo3-linkFiles">
-                                                       <tr>
-                                                               <td class="c-wCell" valign="top">' . $this->barheader(($lang->getLL('folderTree') . ':')) . $tree . '</td>
-                                                               <td class="c-wCell" valign="top">' . $files . '</td>
-                                                       </tr>
-                                               </table>
-                                               ';
-                // Add help message
-                $helpMessage = $this->getHelpMessage($this->act);
-                if ($helpMessage) {
-                    $this->content .= GeneralUtility::makeInstance(FlashMessage::class, $helpMessage, '', FlashMessage::INFO)
-                        ->render();
-                }
-                // Adding create folder + upload form if applicable
-                if (!$backendUser->getTSConfigVal('options.uploadFieldsInTopOfEB')) {
-                    $this->content .= $uploadForm;
-                }
-                $this->content .= $createFolder;
-                $this->content .= '<br />';
-                break;
-            case 'dragdrop':
-                $foldertree = GeneralUtility::makeInstance(ElementBrowserFolderTreeView::class);
-                $foldertree->setLinkParameterProvider($this);
-                $foldertree->thisScript = $this->thisScript;
-                $foldertree->ext_noTempRecyclerDirs = true;
-                $tree = $foldertree->getBrowsableTree();
-                // Get currently selected folder
-                if ($this->curUrlInfo['value'] && $this->curUrlInfo['act'] === $this->act) {
-                    $cmpPath = $this->curUrlInfo['value'];
-                    if (!isset($this->expandFolder)) {
-                        $this->expandFolder = $cmpPath;
-                    }
-                }
-                $selectedFolder = false;
-                if ($this->expandFolder) {
-                    try {
-                        $selectedFolder = ResourceFactory::getInstance()->getFolderObjectFromCombinedIdentifier($this->expandFolder);
-                    } catch (\Exception $e) {
-                    }
-                }
-                // Render the filelist if there is a folder selected
-                $files = '';
-                if ($selectedFolder) {
-                    $files = $this->TBE_dragNDrop($selectedFolder, implode(',', $this->allowedFileTypes));
-                }
-                // Wrap tree
-                $this->content .= '<table border="0" cellpadding="0" cellspacing="0">
-                                       <tr>
-                                               <td style="vertical-align: top;">' . $this->barheader(($lang->getLL('folderTree') . ':')) . $tree . '</td>
-                                               <td>&nbsp;</td>
-                                               <td style="vertical-align: top;">' . $files . '</td>
-                                       </tr>
-                                       </table>';
-                break;
-            default:
-                // Call hook
-                foreach ($this->hookObjects as $hookObject) {
-                    $this->content .= $hookObject->getTab($this->act);
-                }
-        }
-        $this->content .= $this->doc->endPage();
-        $this->doc->JScodeArray['rtehtmlarea'] = $this->getJSCode($this->act, $this->editorNo, $this->sys_language_content);
-        $this->content = $this->doc->insertStylesAndJS($this->content);
-        return $this->content;
-    }
-
-    /**
-     * Returns an array definition of the top menu
-     *
-     * @param bool $wiz
-     * @param array $allowedItems
-     * @return array
-     */
-    protected function buildMenuArray($wiz, $allowedItems)
-    {
-        $menuDef = array();
-        $lang = $this->getLanguageService();
-        if (in_array('image', $this->allowedItems) && ($this->act === 'image' || $this->addModifyTab)) {
-            $menuDef['image']['isActive'] = false;
-            $menuDef['image']['label'] = $lang->getLL('currentImage', true);
-            $menuDef['image']['url'] = '#';
-            $menuDef['image']['addParams'] = 'onclick="jumpToUrl(' . GeneralUtility::quoteJSvalue($this->getThisScript()) . ' + \'act=image\');return false;"';
-        }
-        if (in_array('magic', $this->allowedItems)) {
-            $menuDef['magic']['isActive'] = false;
-            $menuDef['magic']['label'] = $lang->getLL('magicImage', true);
-            $menuDef['magic']['url'] = '#';
-            $menuDef['magic']['addParams'] = 'onclick="jumpToUrl(' . GeneralUtility::quoteJSvalue($this->getThisScript()) . ' + \'act=magic\');return false;"';
-        }
-        if (in_array('plain', $this->allowedItems)) {
-            $menuDef['plain']['isActive'] = false;
-            $menuDef['plain']['label'] = $lang->getLL('plainImage', true);
-            $menuDef['plain']['url'] = '#';
-            $menuDef['plain']['addParams'] = 'onclick="jumpToUrl(' . GeneralUtility::quoteJSvalue($this->getThisScript()) . ' + \'act=plain\');return false;"';
-        }
-        if (in_array('dragdrop', $this->allowedItems)) {
-            $menuDef['dragdrop']['isActive'] = false;
-            $menuDef['dragdrop']['label'] = $lang->getLL('dragDropImage', true);
-            $menuDef['dragdrop']['url'] = '#';
-            $menuDef['dragdrop']['addParams'] = 'onclick="jumpToUrl(' . GeneralUtility::quoteJSvalue($this->getThisScript()) . ' + \'act=dragdrop\');return false;"';
-        }
-        // Call hook for extra options
-        foreach ($this->hookObjects as $hookObject) {
-            $menuDef = $hookObject->modifyMenuDefinition($menuDef);
-        }
-        // Order the menu items as specified in Page TSconfig
-        $menuDef = $this->orderMenuDefinition($menuDef);
-        // Set active menu item
-        reset($menuDef);
-        if ($this->act === false || !in_array($this->act, $this->allowedItems)) {
-            $this->act = key($menuDef);
-        }
-        $menuDef[$this->act]['isActive'] = true;
-        return $menuDef;
-    }
-
-    /**
-     * Get the path of the image to be inserted or modified
-     *
-     * @return string path to the image
-     */
-    protected function getImgPath()
-    {
-        $RTEtsConfigParts = explode(':', $this->RTEtsConfigParams);
-        return $RTEtsConfigParts[6];
-    }
-
-    /**
-     * Get the configuration of the image button
-     *
-     * @return array the configuration array of the image button
-     */
-    protected function getButtonConfig()
-    {
-        return isset($this->RTEProperties['default.']['buttons.']['image.'])
-            ? $this->RTEProperties['default.']['buttons.']['image.']
-            : array();
-    }
-
-    /**
-     * Get the allowed items or tabs
-     *
-     * @param string $items: initial list of possible items
-     * @return array the allowed items
-     */
-    public function getAllowedItems($items)
-    {
-        $allowedItems = explode(',', $items);
-        $clientInfo = GeneralUtility::clientInfo();
-        if ($clientInfo['BROWSER'] !== 'opera') {
-            $allowedItems[] = 'dragdrop';
-        }
-        // Call hook for extra options
-        foreach ($this->hookObjects as $hookObject) {
-            $allowedItems = $hookObject->addAllowedItems($allowedItems);
-        }
-        // Remove tab "image" if there is no current image
-        if ($this->act !== 'image' && !$this->addModifyTab) {
-            $allowedItems = array_diff($allowedItems, array('image'));
-        }
-        // Remove options according to RTE configuration
-        if (is_array($this->buttonConfig['options.']) && $this->buttonConfig['options.']['removeItems']) {
-            $allowedItems = array_diff($allowedItems, GeneralUtility::trimExplode(',', $this->buttonConfig['options.']['removeItems'], true));
-        }
-
-        reset($allowedItems);
-        if (!in_array($this->act, $allowedItems)) {
-            $this->act = current($allowedItems);
-        }
-        return $allowedItems;
-    }
-
-    /**
-     * Order the definition of menu items according to configured order
-     *
-     * @param array $menuDefinition: definition of menu items
-     * @return array ordered menu definition
-     */
-    public function orderMenuDefinition($menuDefinition)
-    {
-        $orderedMenuDefinition = array();
-        if (is_array($this->buttonConfig['options.']) && $this->buttonConfig['options.']['orderItems']) {
-            $orderItems = GeneralUtility::trimExplode(',', $this->buttonConfig['options.']['orderItems'], true);
-            $orderItems = array_intersect($orderItems, $this->allowedItems);
-            foreach ($orderItems as $item) {
-                $orderedMenuDefinition[$item] = $menuDefinition[$item];
-            }
-        } else {
-            $orderedMenuDefinition = $menuDefinition;
-        }
-        return $orderedMenuDefinition;
-    }
-
-    /**
-     * Get the default image class
-     *
-     * @return string the default class, if any
-     */
-    protected function getDefaultClass()
-    {
-        $defaultClass = '';
-        if (is_array($this->buttonConfig['properties.'])) {
-            if (is_array($this->buttonConfig['properties.']['class.']) && trim($this->buttonConfig['properties.']['class.']['default'])) {
-                $defaultClass = trim($this->buttonConfig['properties.']['class.']['default']);
-            }
-        }
-        return $defaultClass;
-    }
-
-    /**
-     * Set variables for maximum plain image dimensions
-     *
-     * @return void
-     */
-    protected function setMaximumPlainImageDimensions()
-    {
-        if (is_array($this->buttonConfig['options.']) && is_array($this->buttonConfig['options.']['plain.'])) {
-            if ($this->buttonConfig['options.']['plain.']['maxWidth']) {
-                $this->plainMaxWidth = $this->buttonConfig['options.']['plain.']['maxWidth'];
-            }
-            if ($this->buttonConfig['options.']['plain.']['maxHeight']) {
-                $this->plainMaxHeight = $this->buttonConfig['options.']['plain.']['maxHeight'];
-            }
-        }
-        if (!$this->plainMaxWidth) {
-            $this->plainMaxWidth = 640;
-        }
-        if (!$this->plainMaxHeight) {
-            $this->plainMaxHeight = 680;
-        }
-    }
-
-    /**
-     * Get the help message to be displayed on a given tab
-     *
-     * @param  string  $act: the identifier of the tab
-     * @return         string  the text of the message
-     */
-    public function getHelpMessage($act)
-    {
-        switch ($act) {
-            case 'plain':
-                return sprintf($this->getLanguageService()->getLL('plainImage_msg'), $this->plainMaxWidth, $this->plainMaxHeight);
-                break;
-            case 'magic':
-                return sprintf($this->getLanguageService()->getLL('magicImage_msg'));
-                break;
-            default:
-                return '';
-        }
-    }
-
-    /**
-     * Checks if the given file is selectable in the filelist.
-     *
-     * In "plain" RTE mode only image files with a maximum width and height are selectable.
-     *
-     * @param FileInterface $file
-     * @param array $imgInfo Image dimensions from \TYPO3\CMS\Core\Imaging\GraphicalFunctions::getImageDimensions()
-     * @return bool TRUE if file is selectable.
-     */
-    protected function fileIsSelectableInFileList(FileInterface $file, array $imgInfo)
-    {
-        return (
-            $this->act !== 'plain'
-            || (
-                GeneralUtility::inList(self::PLAIN_MODE_IMAGE_FILE_EXTENSIONS, strtolower($file->getExtension()))
-                && $imgInfo[0] <= $this->plainMaxWidth
-                && $imgInfo[1] <= $this->plainMaxHeight
-            )
-        );
-    }
-
-    /**
-     * @return LanguageService
-     */
-    protected function getLanguageService()
-    {
-        return $GLOBALS['LANG'];
-    }
-
-    /**
-     * @return BackendUserAuthentication
-     */
-    protected function getBackendUser()
-    {
-        return $GLOBALS['BE_USER'];
-    }
-}
diff --git a/typo3/sysext/rtehtmlarea/Configuration/Backend/AjaxRoutes.php b/typo3/sysext/rtehtmlarea/Configuration/Backend/AjaxRoutes.php
new file mode 100644 (file)
index 0000000..8c705f2
--- /dev/null
@@ -0,0 +1,16 @@
+<?php
+use TYPO3\CMS\Backend\Controller;
+
+/**
+ * Definitions for routes provided by EXT:backend
+ * Contains all AJAX-based routes for entry points
+ *
+ * Currently the "access" property is only used so no token creation + validation is made
+ * but will be extended further.
+ */
+return [
+    'rte_insert_image' => [
+        'path' => '/rte/insert-image',
+        'target' => \TYPO3\CMS\Rtehtmlarea\Controller\SelectImageController::class . '::buildImageMarkup',
+    ],
+];
index 4b689a6..c95847f 100644 (file)
@@ -23,3 +23,22 @@ RTE.default {
 RTE.default.FE.showButtons < RTE.default.showButtons
 RTE.default.FE.proc.allowTagsOutside < RTE.default.proc.allowTagsOutside
 RTE.default.FE.proc.entryHTMLparser_db.tags.img >
+
+RTE.default.buttons.image.options.imageHandler {
+    image {
+        handler = TYPO3\CMS\Rtehtmlarea\ImageHandler\EditImageHandler
+        label = LLL:EXT:rtehtmlarea/Resources/Private/Language/locallang_selectimagecontroller.xlf:currentImage
+    }
+    magic {
+        handler = TYPO3\CMS\Rtehtmlarea\ImageHandler\AddImageHandler
+        label = LLL:EXT:rtehtmlarea/Resources/Private/Language/locallang_selectimagecontroller.xlf:magicImage
+        displayAfter = image
+        scanAfter = image
+    }
+    plain {
+        handler = TYPO3\CMS\Rtehtmlarea\ImageHandler\AddImageHandler
+        label = LLL:EXT:rtehtmlarea/Resources/Private/Language/locallang_selectimagecontroller.xlf:plainImage
+        displayAfter = magic
+        scanAfter = magic
+    }
+}
index b34afbe..630469a 100644 (file)
@@ -1346,57 +1346,35 @@ buttons.image.TYPO3Browser.disabled
 
 
 
-.. _buttons-image-options-removeitems:
+.. _buttons-image-options-imageHandler:
 
-buttons.image.options.removeItems
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-.. container:: table-row
-
-   Property
-         buttons.image.options.removeItems
-
-   Data type
-         list of strings
-
-   Description
-         List of tab items to remove from the dialog of the image button.
-         Possible tab items are: magic, plain, dragdrop, image
-
-         Note: If key image is in the list, the properties editing tab for any
-         current image will not be presented.
-
-         Note: More tabs may be provided by extensions such as DAM.
-
-         Note: dragdrop is not available in Opera.
-
-
-
-.. _buttons-image-options-orderitems:
-
-buttons.image.options.orderItems
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+buttons.image.options.imageHandler
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 .. container:: table-row
 
    Property
-         buttons.image.options.orderItems
+         buttons.image.options.imageHandler
 
    Data type
-         list of strings
+         array
 
    Description
-         List of tab items in the order in which they should appear in the
-         dialogue window. Items not in the list will not be available.
+         Configuration of available image handlers.
 
-         Note: Items that are removed will not be available (see
-         buttons.image.options.removeItems).
+         Extension developers may add their own handler by adding their own
+         entry to this array.
+         The syntax is:
 
-         Note: The default order is: image, magic, plain, dragdrop.
-
-         Note: The list may include items added by extensions such as DAM.
+         .. code:: ts
+            <handler-id> {
+               handler = Vendor\Ext\YourHandlerClass::class
+               label = LLL:EXT:ext/Resources/Private/Language/locallang.xlf:the_label
+               displayAfter = image
+               scanAfter = image
+            }
 
-         Note: The default order may be modified by extensions such as DAM.
+         For a detailed description of the options, please refer to the link handler documentation.
 
 
 
@@ -1493,30 +1471,6 @@ buttons.image.options.plain.maxHeight
 
 
 
-.. _buttons-image-title-usedamcolumn:
-
-buttons.image.title.useDAMColumn
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-.. container:: table-row
-
-   Property
-         buttons.image.title.useDAMColumn
-
-   Data type
-         string
-
-   Description
-         Name of the column of the tx\_dam table that will be used to set the
-         image title attribute.
-
-         Default: caption
-
-         Note: This property is ignored if integration of DAM with the htmlArea
-         RTE is not set in the DAM extension.
-
-
-
 .. _buttons-image-properties-removeitems:
 
 buttons.image.properties.removeItems
@@ -2015,7 +1969,7 @@ buttons.acronym.recursive
 .. _buttons-acronym-lockbeusertodbmounts:
 
 buttons.acronym.lockBeUserToDBmounts
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 .. container:: table-row
 
diff --git a/typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/AddImage.js b/typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/AddImage.js
new file mode 100644 (file)
index 0000000..c295826
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+/**
+ * Module: TYPO3/CMS/Rtehtmlarea/AddImage
+ */
+define(['jquery', 'TYPO3/CMS/Rtehtmlarea/SelectImage', 'TYPO3/CMS/Backend/LegacyTree'], function($, SelectImage, Tree) {
+       'use strict';
+
+       /**
+        * @type {{elements: Object, toggle: Function, handle: Function}}
+        * @exports TYPO3/CMS/Rtehtmlarea/AddImage
+        */
+       var AddImage = {
+               elements: {},
+
+               toggle: function(event) {
+                       event.preventDefault();
+                       var items = AddImage.getItems();
+                       if (items.length) {
+                               items.each(function(position, item) {
+                                       item.checked = (item.checked ? null : 'checked');
+                               });
+                       }
+               },
+
+               handle: function(e) {
+                       e.preventDefault();
+                       var items = AddImage.getItems();
+                       var selectedItems = [];
+                       if (items.length) {
+                               items.each(function(position, item) {
+                                       if (item.checked && item.name) {
+                                               selectedItems.push(AddImage.elements[item.name].uid);
+                                       }
+                               });
+                               SelectImage.setImagesInRTE(selectedItems.join('|'));
+                       }
+               },
+
+               getItems: function () {
+                       return $('#typo3-filelist').find('.typo3-bulk-item');
+               }
+       };
+
+       Tree.ajaxID = 'sc_alt_file_navframe_expandtoggle';
+
+       $(function () {
+               $.extend(AddImage.elements, $('body').data('elements'));
+
+               $('[data-close]').on('click', function (e) {
+                       e.preventDefault();
+                       SelectImage.setImagesInRTE(AddImage.elements['file_' + $(this).data('fileIndex')].uid);
+               });
+
+               $('#t3js-importSelection').on('click', AddImage.handle);
+               $('#t3js-toggleSelection').on('click', AddImage.toggle);
+       });
+
+       return AddImage;
+});
diff --git a/typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/EditImage.js b/typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/EditImage.js
new file mode 100644 (file)
index 0000000..a38d972
--- /dev/null
@@ -0,0 +1,252 @@
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+
+/**
+ * Module: TYPO3/CMS/Rtehtmlarea/EditImage
+ */
+define(['jquery', 'TYPO3/CMS/Rtehtmlarea/SelectImage'], function($, SelectImage) {
+       'use strict';
+
+       /**
+        * @type {{classesImage: bool, initializeForm: Function, updateImage: Function}}
+        * @exports TYPO3/CMS/Rtehtmlarea/EditImage
+        */
+       var EditImage = {
+               classesImage: false,
+
+               /**
+                * Actions on the current image
+                */
+               updateImage: function (e) {
+                       e.preventDefault();
+
+                       var selectedImageRef = SelectImage.getCurrentImage();
+                       if (!selectedImageRef) {
+                               return;
+                       }
+
+                       var $element;
+                       $.each({ width: 'iWidth', height: 'iHeight'}, function(index, id) {
+                               $element = $('#t3js-' + id);
+                               if ($element) {
+                                       var value = $element.val();
+                                       if (value) {
+                                               var intValue = parseInt(value);
+                                               if (intValue) {
+                                                       selectedImageRef.style[index] = "";
+                                                       $(selectedImageRef).attr(index, intValue);
+                                               }
+                                       }
+                               }
+                       });
+
+                       $.each({
+                                       paddingTop: 'iPaddingTop',
+                                       paddingRight: 'iPaddingRight',
+                                       paddingBottom: 'iPaddingBottom',
+                                       paddingLeft: 'iPaddingLeft'
+                               },
+                               function(index, id) {
+                                       $element = $('#t3js-' + id);
+                                       if ($element) {
+                                               var value = $element.val();
+                                               if (value) {
+                                                       var intValue = parseInt(value);
+                                                       if (value !== "" && !isNaN(intValue)) {
+                                                               selectedImageRef.style[index] = intValue + "px";
+                                                       } else {
+                                                               selectedImageRef.style[index] = "";
+                                                       }
+                                               }
+                                       }
+                               }
+                       );
+
+                       $.each({ title: 'iTitle', alt: 'iAlt' }, function(index, id) {
+                               $element = $('#t3js-' + id);
+                               if ($element) {
+                                       $(selectedImageRef).attr(index, $element.val());
+                               }
+                       });
+
+                       $element = $('#t3js-iBorder');
+                       if ($element) {
+                               selectedImageRef.style.borderStyle = "";
+                               selectedImageRef.style.borderWidth = "";
+                               selectedImageRef.style.border = "";
+                               selectedImageRef.style.borderTopStyle = "";
+                               selectedImageRef.style.borderRightStyle = "";
+                               selectedImageRef.style.borderBottomStyle = "";
+                               selectedImageRef.style.borderLeftStyle = "";
+                               selectedImageRef.style.borderTopWidth = "";
+                               selectedImageRef.style.borderRightWidth = "";
+                               selectedImageRef.style.borderBottomWidth = "";
+                               selectedImageRef.style.borderLeftWidth = "";
+                               if ($element.prop('checked')) {
+                                       selectedImageRef.style.borderStyle = "solid";
+                                       selectedImageRef.style.borderWidth = "thin";
+                               }
+                               selectedImageRef.removeAttribute("border");
+                       }
+
+                       $element = $('#t3js-iFloat');
+                       if ($element) {
+                               var value = $element.val();
+                               selectedImageRef.style.cssFloat = value ? value : "";
+                       }
+
+                       if (SelectImage.classesImage) {
+                               $element = $('t3js-iClass');
+                               if ($element) {
+                                       var iClass;
+                                       if ($element.find('option').length > 0) {
+                                               iClass = $element.val();
+                                       }
+                                       if (iClass || $(selectedImageRef).attr('class')) {
+                                               selectedImageRef.className = iClass;
+                                       } else {
+                                               selectedImageRef.className = "";
+                                       }
+                               }
+                       }
+
+                       var languageObject = SelectImage.plugin.editor.getPlugin("Language");
+                       $element = $('#t3js-iLang');
+                       if ($element && languageObject) {
+                               var iLang = $element.val();
+                               if (iLang || languageObject.getLanguageAttribute(selectedImageRef)) {
+                                       languageObject.setLanguageAttributes(selectedImageRef, iLang);
+                               } else {
+                                       languageObject.setLanguageAttributes(selectedImageRef, "none");
+                               }
+                       }
+
+                       $element = $('#t3js-iClickEnlarge');
+                       if ($element) {
+                               if ($element.prop('checked')) {
+                                       selectedImageRef.setAttribute("data-htmlarea-clickenlarge","1");
+                               } else {
+                                       selectedImageRef.removeAttribute("data-htmlarea-clickenlarge");
+                                       selectedImageRef.removeAttribute("clickenlarge");
+                               }
+                       }
+
+                       SelectImage.plugin.close();
+               },
+
+               /**
+                * Actions on the form
+                */
+               initializeForm: function() {
+
+                       var plugin = SelectImage.plugin;
+                       var selectedImageRef = SelectImage.getCurrentImage();
+
+                       var languageButton = plugin.getButton('Language');
+                       var $languageElement = $('#t3js-languageSetting');
+                       if (languageButton && $languageElement) {
+                               var languageSelector = '';
+                               var options = languageButton.getOptions();
+                               for (var i = 0, n = options.length; i < n; i++) {
+                                       languageSelector += '<option value="' + options[i].value + '">' + options[i].innerHTML + '</option>';
+                               }
+                               languageSelector += '';
+
+                               $languageElement.find('label').text(plugin.getPluginInstance('Language').localize('Language-Tooltip') + ': ');
+                               $('#t3js-iLang').html(languageSelector);
+                       } else if ($languageElement) {
+                               $languageElement.remove();
+                       }
+
+                       var $element;
+                       $.each({ width: 'iWidth', height: 'iHeight'}, function(index, id) {
+                               $element = $('#t3js-' + id);
+                               if ($element) {
+                                       var value = selectedImageRef.style[index] ? selectedImageRef.style[index] : $(selectedImageRef).attr(index);
+                                       value = parseInt(value);
+                                       if (!isNaN(value) && value !== 0) {
+                                               $element.val(value);
+                                       }
+                               }
+                       });
+
+                       $.each({
+                                       paddingTop: ['iPaddingTop', 'vspace'],
+                                       paddingRight: ['iPaddingRight', 'hspace'],
+                                       paddingBottom: ['iPaddingBottom', 'vspace'],
+                                       paddingLeft: ['iPaddingLeft', 'hspace']
+                               },
+                               function(index, obj) {
+                                       $element = $('#t3js-' + obj[0]);
+                                       if ($element) {
+                                               var padding = selectedImageRef.style[obj[0]] ? selectedImageRef.style[obj[0]] : $(selectedImageRef).attr(obj[1]);
+                                               padding = parseInt(padding);
+                                               if (isNaN(padding) || padding <= 0) {
+                                                       padding = "";
+                                               }
+                                               $element.val(padding);
+                                       }
+                               }
+                       );
+
+                       $.each({ title: 'iTitle', alt: 'iAlt' }, function(index, id) {
+                               $element = $('#t3js-' + id);
+                               if ($element) {
+                                       $element.val($(selectedImageRef).attr(index));
+                               }
+                       });
+
+                       $element = $('#t3js-iBorder');
+                       if ($element) {
+                               $element.prop('checked', $(selectedImageRef).attr('border')
+                                       || selectedImageRef.style.borderStyle && selectedImageRef.style.borderStyle !== "none"
+                                       && selectedImageRef.style.borderStyle !== "none none none none");
+                       }
+
+                       $element = $('#t3js-iFloat');
+                       if ($element) {
+                               $element.val(selectedImageRef.style.cssFloat ? selectedImageRef.style.cssFloat : selectedImageRef.style.styleFloat);
+                       }
+
+                       $element = $('#t3js-iClass');
+                       if ($element && SelectImage.classesImage) {
+                               $element.val(selectedImageRef.className);
+                       }
+
+                       var languagePlugin = SelectImage.plugin.editor.getPlugin("Language");
+                       $element = $('#t3js-iLang');
+                       if ($element && languagePlugin) {
+                               $element.val(languagePlugin.getLanguageAttribute(selectedImageRef));
+                               if ($element.val()) {
+                                       $element.find('option')[0].text(languagePlugin.localize("Remove language mark"));
+                               }
+                       }
+
+                       $element = $('#t3js-iClickEnlarge');
+                       if ($element) {
+                               $element.prop('checked', selectedImageRef.getAttribute("data-htmlarea-clickenlarge") === "1" || selectedImageRef.getAttribute("clickenlarge") === "1");
+                       }
+               }
+       };
+
+       $(function () {
+               $.extend(EditImage, $('body').data());
+
+               $('.t3js-editForm').on('submit', EditImage.updateImage);
+
+               EditImage.initializeForm();
+       });
+
+       return EditImage;
+});
diff --git a/typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/Modules/SelectImage.js b/typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/Modules/SelectImage.js
deleted file mode 100644 (file)
index 640bd1a..0000000
+++ /dev/null
@@ -1,409 +0,0 @@
-/*
- * This file is part of the TYPO3 CMS project.
- *
- * It is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License, either version 2
- * of the License, or any later version.
- *
- * For the full copyright and license information, please read the
- * LICENSE.txt file that was distributed with this source code.
- *
- * The TYPO3 project - inspiring people to share!
- */
-
-/**
- * Module: TYPO3/CMS/Rtehtmlarea/HTMLArea/Modules/SelectImage
- * This module is used by the RTE SelectImage module
- */
-define(function () {
-       'use strict';
-
-       /**
-        *
-        * @type {{editorNo: string, act: string, sys_language_content: string, RTEtsConfigParams: string, bparams: string, classesImage: boolean, labels: {}, initEventListeners: Function, jumpToUrl: Function}}
-        * @exports TYPO3/CMS/Rtehtmlarea/HTMLArea/Modules/SelectImage
-        */
-       var SelectImage = {
-               // The id of the current editor
-               editorNo: '',
-               // The current action
-               act: '',
-               // The uid of the language of the content element
-               sys_language_content: '',
-               // The RTE config parameters
-               RTEtsConfigParams: '',
-               // The browser parameters
-               bparams: '',
-               // Whether a class selector should be rendered for the image
-               classesImage: false,
-               // Some labels localized on the server side
-               labels: {},
-
-               /**
-                * Initialize an event handler for dropping an image in WebKit browsers
-                *
-                * @return void
-         * @deprecated since TYPO3 CMS 7, will be removed in TYPO3 CMS 8
-                */
-               initEventListeners: function() {
-                       if (typeof console !== 'undefined') {
-                               console.log('SelectImage.initEventListeners() is deprecated since TYPO3 CMS 7, will be removed in TYPO3 CMS 8');
-                       }
-                       require(
-                               ['TYPO3/CMS/Rtehtmlarea/HTMLArea/UserAgent/UserAgent', 'TYPO3/CMS/Rtehtmlarea/HTMLArea/Event/Event'],
-                               function (UserAgent, Event) {
-                                       if (UserAgent.isWebKit) {
-                                               Event.one(window.document.body, 'dragend.TYPO3Image', function (event) { SelectImage.Plugin.get().onDrop(event); });
-                                       }
-                               }
-                       );
-               },
-
-               /**
-                * Jump to the specified url after adding some parameters specific to the RTE context
-                *
-                * @return bool
-                */
-               jumpToUrl: function(URL, anchor) {
-                       var selectedImageRef = SelectImage.CurrentImage.get();
-                       var add_act = URL.indexOf('act=') === -1 ? '&act=' + this.act : '';
-                       var add_editorNo = URL.indexOf('editorNo=') === -1 ? '&editorNo=' + this.editorNo : '';
-                       var add_sys_language_content = URL.indexOf('sys_language_content=') === -1 ? '&sys_language_content=' + this.sys_language_content : '';
-                       var RTEtsConfigParams = '&RTEtsConfigParams=' + this.RTEtsConfigParams;
-                       var bparams = URL.indexOf('bparams=') === -1 ? '&bparams=' + this.bparams : '';
-
-                       var cur_width = selectedImageRef ? '&cWidth=' + selectedImageRef.style.width : '';
-                       var cur_height = selectedImageRef ? '&cHeight=' + selectedImageRef.style.height : '';
-                       var addModifyTab = selectedImageRef ? '&addModifyTab=1' : '';
-
-                       window.location.href = URL + add_act + add_editorNo + add_sys_language_content + RTEtsConfigParams + bparams + addModifyTab + cur_width + cur_height + (typeof anchor === 'string' ? anchor : '');
-                       return false;
-               }
-       };
-
-       SelectImage.Plugin = {
-               /**
-                * Get a reference to the TYPO3Image plugin instance
-                *
-                * @returns {Object} a reference to the plugin instance
-                */
-               get: function() {
-                       return window.parent.RTEarea[SelectImage.editorNo].editor.getPlugin('TYPO3Image');
-               }
-       };
-
-       /**
-        * Actions on the current image
-        */
-       SelectImage.CurrentImage = {
-
-               /**
-                * Get a reference to the current image as established by the plugin
-                *
-                * @return {Object|null} a reference to the current image
-                */
-               get: function() {
-                       var plugin = SelectImage.Plugin.get();
-                       if (plugin.image) {
-                               return plugin.image;
-                       }
-                       return null;
-               },
-
-               /**
-                * Set the properties of the current image based on the data collected in the form
-                *
-                * @return void
-                */
-               setProperties: function () {
-                       var selectedImageRef = this.get();
-                       if (!selectedImageRef) {
-                               return;
-                       }
-                       var imageData = document.imageData;
-                       if (imageData.iWidth) {
-                               if (imageData.iWidth.value && parseInt(imageData.iWidth.value)) {
-                                       selectedImageRef.style.width = "";
-                                       selectedImageRef.width = parseInt(imageData.iWidth.value);
-                               }
-                       }
-                       if (imageData.iHeight) {
-                               if (imageData.iHeight.value && parseInt(imageData.iHeight.value)) {
-                                       selectedImageRef.style.height = "";
-                                       selectedImageRef.height = parseInt(imageData.iHeight.value);
-                               }
-                       }
-                       if (imageData.iPaddingTop) {
-                               if (imageData.iPaddingTop.value !== "" && !isNaN(parseInt(imageData.iPaddingTop.value))) {
-                                       selectedImageRef.style.paddingTop = parseInt(imageData.iPaddingTop.value) + "px";
-                               } else {
-                                       selectedImageRef.style.paddingTop = "";
-                               }
-                       }
-                       if (imageData.iPaddingRight) {
-                               if (imageData.iPaddingRight.value !== "" && !isNaN(parseInt(imageData.iPaddingRight.value))) {
-                                       selectedImageRef.style.paddingRight = parseInt(imageData.iPaddingRight.value) + "px";
-                               } else {
-                                       selectedImageRef.style.paddingRight = "";
-                               }
-                       }
-                       if (imageData.iPaddingBottom) {
-                               if (imageData.iPaddingBottom.value !== "" && !isNaN(parseInt(imageData.iPaddingBottom.value))) {
-                                       selectedImageRef.style.paddingBottom = parseInt(imageData.iPaddingBottom.value) + "px";
-                               } else {
-                                       selectedImageRef.style.paddingBottom = "";
-                               }
-                       }
-                       if (imageData.iPaddingLeft) {
-                               if (imageData.iPaddingLeft.value !== "" && !isNaN(parseInt(imageData.iPaddingLeft.value))) {
-                                       selectedImageRef.style.paddingLeft = parseInt(imageData.iPaddingLeft.value) + "px";
-                               } else {
-                                       selectedImageRef.style.paddingLeft = "";
-                               }
-                       }
-                       if (imageData.iTitle) {
-                               selectedImageRef.title = imageData.iTitle.value;
-                       }
-                       if (imageData.iAlt) {
-                               selectedImageRef.alt = imageData.iAlt.value;
-                       }
-                       if (imageData.iBorder) {
-                               selectedImageRef.style.borderStyle = "";
-                               selectedImageRef.style.borderWidth = "";
-                               selectedImageRef.style.border = "";  // this statement ignored by Mozilla 1.3.1
-                               selectedImageRef.style.borderTopStyle = "";
-                               selectedImageRef.style.borderRightStyle = "";
-                               selectedImageRef.style.borderBottomStyle = "";
-                               selectedImageRef.style.borderLeftStyle = "";
-                               selectedImageRef.style.borderTopWidth = "";
-                               selectedImageRef.style.borderRightWidth = "";
-                               selectedImageRef.style.borderBottomWidth = "";
-                               selectedImageRef.style.borderLeftWidth = "";
-                               if (imageData.iBorder.checked) {
-                                       selectedImageRef.style.borderStyle = "solid";
-                                       selectedImageRef.style.borderWidth = "thin";
-                               }
-                               selectedImageRef.removeAttribute("border");
-                       }
-                       if (imageData.iFloat) {
-                               var iFloat = imageData.iFloat.options[imageData.iFloat.selectedIndex].value;
-                               selectedImageRef.style.cssFloat = iFloat ? iFloat : "";
-                       }
-                       if (SelectImage.classesImage && imageData.iClass) {
-                               var iClass;
-                               if (imageData.iClass.options.length > 0) {
-                                       iClass = imageData.iClass.options[imageData.iClass.selectedIndex].value;
-                               }
-                               if (iClass || selectedImageRef.attributes["class"] && selectedImageRef.attributes["class"].value) {
-                                       selectedImageRef.className = iClass;
-                               } else {
-                                       selectedImageRef.className = "";
-                               }
-                       }
-                       if (imageData.iLang) {
-                               var iLang = imageData.iLang.options[imageData.iLang.selectedIndex].value;
-                               var languageObject = SelectImage.Plugin.get().editor.getPlugin("Language");
-                               if (iLang || languageObject.getLanguageAttribute(selectedImageRef)) {
-                                       languageObject.setLanguageAttributes(selectedImageRef, iLang);
-                               } else {
-                                       languageObject.setLanguageAttributes(selectedImageRef, "none");
-                               }
-                       }
-                       if (imageData.iClickEnlarge) {
-                               if (imageData.iClickEnlarge.checked) {
-                                       selectedImageRef.setAttribute("data-htmlarea-clickenlarge","1");
-                               } else {
-                                       selectedImageRef.removeAttribute("data-htmlarea-clickenlarge");
-                                       selectedImageRef.removeAttribute("clickenlarge");
-                               }
-                       }
-                       SelectImage.Plugin.get().close();
-               }
-       };
-
-       /**
-        * Actions on the form
-        */
-       SelectImage.Form = {
-
-               /**
-                * Build the form and append it to the body of the document
-                *
-                * @param {String} classesImageJSOptions options of the class selector
-                * @param {array} removedProperties array of properties configured to be rmoved
-                * @param {bool} lockPlainWidth true if the plain image width is locked
-                * @param {bool} lockPlainHeight true if the plain image height is locked
-                * @return void
-                */
-               build: function(classesImageJSOptions, removedProperties, lockPlainWidth, lockPlainHeight) {
-                       var plugin = SelectImage.Plugin.get();
-                       var selectedImageRef = SelectImage.CurrentImage.get();
-                       var styleSelector = '';
-                       if (SelectImage.classesImage) {
-                               styleSelector = '<select id="iClass" name="iClass" style="width:140px;">' + classesImageJSOptions + '</select>';
-                       }
-                       var floatSelector = '<select id="iFloat" name="iFloat">'
-                               + '<option value="">' + SelectImage.labels['notSet'] + '</option>'
-                               + '<option value="none">' + SelectImage.labels['nonFloating'] + '</option>'
-                               + '<option value="left">' + SelectImage.labels['left'] + '</option>'
-                               + '<option value="right">' + SelectImage.labels['right'] + '</option>'
-                               + '</select>';
-                       var languageSelector = '';
-                       if (plugin.getButton('Language')) {
-                               languageSelector = '<select id="iLang" name="iLang">';
-                               var options = plugin.getButton('Language').getOptions();
-                               for (var i = 0, n = options.length; i < n; i++) {
-                                       languageSelector += '<option value="' + options[i].value + '">' + options[i].innerHTML + '</option>';
-                               }
-                               languageSelector += '</select>';
-                       }
-                       var sz = '';
-                       sz += '<form name="imageData"><table class="htmlarea-window-table">';
-                       if (removedProperties.indexOf('class') === -1 && SelectImage.classesImage) {
-                               sz += '<tr><td><label for="iClass">' + SelectImage.labels['class'] + ': </label></td><td>' + styleSelector + '</td></tr>';
-                       }
-                       if (removedProperties.indexOf('width') === -1 && !(selectedImageRef && selectedImageRef.src.indexOf('RTEmagic') === -1 && lockPlainWidth)) {
-                               sz += '<tr><td><label for="iWidth">' + SelectImage.labels['width'] + ': </label></td><td><input type="text" id="iWidth" name="iWidth" value="" style="width: 39px;" maxlength="4" /></td></tr>';
-                       }
-                       if (removedProperties.indexOf('height') === -1 && !(selectedImageRef && selectedImageRef.src.indexOf('RTEmagic') === -1 && lockPlainHeight)) {
-                               sz += '<tr><td><label for="iHeight">' + SelectImage.labels['height'] + ': </label></td><td><input type="text" id="iHeight" name="iHeight" value="" style="width: 39px;" maxlength="4" /></td></tr>';
-                       }
-                       if (removedProperties.indexOf('border') === -1) {
-                               sz += '<tr><td><label for="iBorder">' + SelectImage.labels['border'] + ': </label></td><td><input type="checkbox" id="iBorder" name="iBorder" value="1" /></td></tr>';
-                       }
-                       if (removedProperties.indexOf('float') === -1) {
-                               sz += '<tr><td><label for="iFloat">' + SelectImage.labels['float'] + ': </label></td><td>' + floatSelector + '</td></tr>';
-                       }
-                       if (removedProperties.indexOf('paddingTop') === -1) {
-                               sz += '<tr><td><label for="iPaddingTop">' + SelectImage.labels['padding_top'] + ': </label></td><td><input type="text" id="iPaddingTop" name="iPaddingTop" value="" style="width: 39px;" maxlength="4" /></td></tr>';
-                       }
-                       if (removedProperties.indexOf('paddingRight') === -1) {
-                               sz += '<tr><td><label for="iPaddingRight">' + SelectImage.labels['padding_right'] + ': </label></td><td><input type="text" id="iPaddingRight" name="iPaddingRight" value="" style="width: 39px;" maxlength="4" /></td></tr>';
-                       }
-                       if (removedProperties.indexOf('paddingBottom') === -1) {
-                               sz += '<tr><td><label for="iPaddingBottom">' + SelectImage.labels['padding_bottom'] + ': </label></td><td><input type="text" id="iPaddingBottom" name="iPaddingBottom" value="" style="width: 39px;" maxlength="4" /></td></tr>';
-                       }
-                       if (removedProperties.indexOf('paddingLeft') === -1) {
-                               sz += '<tr><td><label for="iPaddingLeft">' + SelectImage.labels['padding_left'] + ': </label></td><td><input type="text" id="iPaddingLeft" name="iPaddingLeft" value="" style="width: 39px;" maxlength="4" /></td></tr>';
-                       }
-                       if (removedProperties.indexOf('title') === -1) {
-                               sz += '<tr><td><label for="iTitle">' + SelectImage.labels['title'] + ': </label></td><td><input type="text" id="iTitle" name="iTitle" style="width:192px;" maxlength="256" /></td></tr>';
-                       }
-                       if (removedProperties.indexOf('alt') === -1) {
-                               sz += '<tr><td><label for="iAlt">' + SelectImage.labels['alt'] + ': </label></td><td><input type="text" id="iAlt" name="iAlt" style="width:192px;" maxlength="256" /></td></tr>';
-                       }
-                       if (removedProperties.indexOf('lang') === -1 && plugin.getButton('Language')) {
-                               sz += '<tr><td><label for="iLang">' + plugin.getPluginInstance('Language').localize('Language-Tooltip') + ': </label></td><td>' + languageSelector + '</td></tr>';
-                       }
-                       if (removedProperties.indexOf('data-htmlarea-clickenlarge') === -1 && removedProperties.indexOf('clickenlarge') === -1 ) {
-                               sz += '<tr><td><label for="iClickEnlarge">' + SelectImage.labels['image_zoom'] + ' </label></td><td><input type="checkbox" name="iClickEnlarge" id="iClickEnlarge" value="0" /></td></tr>';
-                       }
-                       sz += '<tr><td></td><td><input class="btn btn-default" type="submit" value="' + SelectImage.labels['update'] + '" onclick="SelectImage.CurrentImage.setProperties(SelectImage.classesImage)"></td></tr>';
-                       sz += '</table></form>';
-
-                       var div = document.createElement('div');
-                       div.innerHTML = sz;
-                       document.body.appendChild(div);
-               },
-
-               /**
-                * Insert current image properties into the fields of the form
-                * @return void
-                */
-               insertImageProperties: function () {
-                       var plugin = SelectImage.Plugin.get();
-                       var selectedImageRef = SelectImage.CurrentImage.get();
-                       if (selectedImageRef) {
-                               var styleWidth, styleHeight, padding;
-                               if (document.imageData.iWidth) {
-                                       styleWidth = selectedImageRef.style.width ? selectedImageRef.style.width : selectedImageRef.width;
-                                       styleWidth = parseInt(styleWidth);
-                                       if (!isNaN(styleWidth) && styleWidth !== 0) {
-                                               document.imageData.iWidth.value = styleWidth;
-                                       }
-                               }
-                               if (document.imageData.iHeight) {
-                                       styleHeight = selectedImageRef.style.height ? selectedImageRef.style.height : selectedImageRef.height;
-                                       styleHeight = parseInt(styleHeight);
-                                       if (!isNaN(styleHeight) && styleHeight !== 0) {
-                                               document.imageData.iHeight.value = styleHeight;
-                                       }
-                               }
-                               if (document.imageData.iPaddingTop) {
-                                       padding = selectedImageRef.style.paddingTop ? selectedImageRef.style.paddingTop : selectedImageRef.vspace;
-                                       padding = parseInt(padding);
-                                       if (isNaN(padding) || padding <= 0) { padding = ""; }
-                                       document.imageData.iPaddingTop.value = padding;
-                               }
-                               if (document.imageData.iPaddingRight) {
-                                       padding = selectedImageRef.style.paddingRight ? selectedImageRef.style.paddingRight : selectedImageRef.hspace;
-                                       padding = parseInt(padding);
-                                       if (isNaN(padding) || padding <= 0) { padding = ""; }
-                                       document.imageData.iPaddingRight.value = padding;
-                               }
-                               if (document.imageData.iPaddingBottom) {
-                                       padding = selectedImageRef.style.paddingBottom ? selectedImageRef.style.paddingBottom : selectedImageRef.vspace;
-                                       padding = parseInt(padding);
-                                       if (isNaN(padding) || padding <= 0) { padding = ""; }
-                                       document.imageData.iPaddingBottom.value = padding;
-                               }
-                               if (document.imageData.iPaddingLeft) {
-                                       padding = selectedImageRef.style.paddingLeft ? selectedImageRef.style.paddingLeft : selectedImageRef.hspace;
-                                       padding = parseInt(padding);
-                                       if (isNaN(padding) || padding <= 0) { padding = ""; }
-                                       document.imageData.iPaddingLeft.value = padding;
-                               }
-                               if (document.imageData.iTitle) {
-                                       document.imageData.iTitle.value = selectedImageRef.title;
-                               }
-                               if (document.imageData.iAlt) {
-                                       document.imageData.iAlt.value = selectedImageRef.alt;
-                               }
-                               if (document.imageData.iBorder) {
-                                       if((selectedImageRef.style.borderStyle && selectedImageRef.style.borderStyle != "none" && selectedImageRef.style.borderStyle != "none none none none") || selectedImageRef.border) {
-                                               document.imageData.iBorder.checked = 1;
-                                       }
-                               }
-                               var fObj, value, a;
-                               if (document.imageData.iFloat) {
-                                       fObj = document.imageData.iFloat;
-                                       value = selectedImageRef.style.cssFloat ? selectedImageRef.style.cssFloat : selectedImageRef.style.styleFloat;
-                                       for (a = 0; a < fObj.length; a++) {
-                                               if (fObj.options[a].value == value) {
-                                                       fObj.selectedIndex = a;
-                                               }
-                                       }
-                               }
-                               if (SelectImage.classesImage && document.imageData.iClass) {
-                                       fObj = document.imageData.iClass;
-                                       value = selectedImageRef.className;
-                                       for (a = 0; a < fObj.length; a++) {
-                                               if (fObj.options[a].value == value) {
-                                                       fObj.selectedIndex = a;
-                                               }
-                                       }
-                               }
-                               if (document.imageData.iLang) {
-                                       fObj = document.imageData.iLang;
-                                       value = plugin.editor.getPlugin("Language").getLanguageAttribute(selectedImageRef);
-                                       for (var i = 0, n = fObj.length; i < n; i++) {
-                                               if (fObj.options[i].value == value) {
-                                                       fObj.selectedIndex = i;
-                                                       if (i) {
-                                                               fObj.options[0].text = plugin.editor.getPlugin("Language").localize("Remove language mark");
-                                                       }
-                                               }
-                                       }
-                               }
-                               if (document.imageData.iClickEnlarge) {
-                                       document.imageData.iClickEnlarge.checked = selectedImageRef.getAttribute("data-htmlarea-clickenlarge") === "1" || selectedImageRef.getAttribute("clickenlarge") === "1";
-                               }
-                       }
-               }
-       };
-
-       // public usage
-       window.SelectImage = SelectImage;
-
-       return SelectImage;
-});
index c4879c5..ad8fdb2 100644 (file)
@@ -16,9 +16,8 @@
  */
 define(['TYPO3/CMS/Rtehtmlarea/HTMLArea/Plugin/Plugin',
        'TYPO3/CMS/Rtehtmlarea/HTMLArea/UserAgent/UserAgent',
-       'TYPO3/CMS/Rtehtmlarea/HTMLArea/Event/Event',
        'TYPO3/CMS/Rtehtmlarea/HTMLArea/Util/Util'],
-       function (Plugin, UserAgent, Event, Util) {
+       function (Plugin, UserAgent, Util) {
 
        var TYPO3Image = function (editor, pluginName) {
                this.constructor.super.call(this, editor, pluginName);
@@ -81,7 +80,7 @@ define(['TYPO3/CMS/Rtehtmlarea/HTMLArea/Plugin/Plugin',
                                this.image = null;
                        }
                        if (this.image) {
-                               additionalParameter = '&act=image';
+                               additionalParameter = '&act=image&fileUid=' + this.image.getAttribute('data-htmlarea-file-uid');
                        }
                        this.openContainerWindow(
                                buttonId,
@@ -95,8 +94,6 @@ define(['TYPO3/CMS/Rtehtmlarea/HTMLArea/Plugin/Plugin',
                                ),
                                this.makeUrlFromModulePath(this.imageModulePath, additionalParameter)
                        );
-                       var self = this;
-                       Event.one(UserAgent.isIE ? this.editor.document.body : this.editor.document.documentElement, 'drop.TYPO3Image', function (event) { return self.onDrop(event); });
                        return false;
                },
 
@@ -111,25 +108,9 @@ define(['TYPO3/CMS/Rtehtmlarea/HTMLArea/Plugin/Plugin',
                },
 
                /**
-                * Handlers for drag and drop operations
-                */
-               onDrop: function (event) {
-                       if (UserAgent.isWebKit) {
-                               this.editor.iframe.onDrop(event);
-                       }
-                       // IE 11 needs the event to complete before the dialog gets closed, otherwise the image is always inserted at the end of body
-                       var self = this;
-                       window.setTimeout(function () {
-                               self.close();
-                       }, 50);
-                       return true;
-               },
-
-               /**
                 * Remove the event listeners
                 */
                removeListeners: function () {
-                       Event.off(UserAgent.isIE ? this.editor.document.body : this.editor.document.documentElement, '.TYPO3Image');
                },
 
                /**
diff --git a/typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/SelectImage.js b/typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/SelectImage.js
new file mode 100644 (file)
index 0000000..ca56507
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+
+
+/**
+ * Module: TYPO3/CMS/Rtehtmlarea/SelectImage
+ * This module is used by the RTE SelectImage module
+ */
+define(['jquery', 'TYPO3/CMS/Recordlist/LinkBrowser'], function($, LinkBrowser) {
+       'use strict';
+
+       /**
+        *
+        * @type {{plugin: Object, initialize: Function, getCurrentImage: Function, setImagesInRTE: Function}}
+        * @exports TYPO3/CMS/Rtehtmlarea/SelectImage
+        */
+       var SelectImage = {
+               plugin: null,
+
+               initialize: function() {
+                       SelectImage.plugin = window.parent.RTEarea[LinkBrowser.urlParameters.editorNo].editor.getPlugin("TYPO3Image");
+               },
+
+               getCurrentImage: function() {
+                       return SelectImage.plugin.image;
+               },
+
+               setImagesInRTE: function(uidList) {
+                       var parameters = LinkBrowser.urlParameters;
+
+                       parameters.uidList = uidList;
+
+                       var selectedImageRef = SelectImage.getCurrentImage();
+                       if (selectedImageRef) {
+                               parameters.cWidth = selectedImageRef.style.width;
+                               parameters.cHeight = selectedImageRef.style.height;
+                       }
+
+                       $.ajax({
+                               url: TYPO3.settings.ajaxUrls['rte_insert_image'],
+                               data: parameters,
+                               method: 'GET',
+                               success: function(data) {
+                                       if (data.images) {
+                                               SelectImage.plugin.insertImage(data.images);
+                                       }
+                               }
+                       });
+               }
+       };
+
+       $(SelectImage.initialize);
+
+       return SelectImage;
+});
index 88a5638..60bdf06 100644 (file)
@@ -24,6 +24,12 @@ foreach ($htmlAreaRteContextHelpFiles as $key => $file) {
 }
 unset($htmlAreaRteContextHelpFiles);
 
+if (TYPO3_MODE === 'BE') {
+    $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['RteImageSelector']['hooks']['editImageHandler'] = [
+        'handler' => \TYPO3\CMS\Rtehtmlarea\ImageHandler\EditImageHandler::class
+    ];
+}
+
 // Extend TYPO3 User Settings Configuration
 if (TYPO3_MODE === 'BE' && \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::isLoaded('setup') && is_array($GLOBALS['TYPO3_USER_SETTINGS'])) {
     $GLOBALS['TYPO3_USER_SETTINGS']['columns'] = array_merge($GLOBALS['TYPO3_USER_SETTINGS']['columns'], array(