[TASK] Move T3Editor logic into T3editorElement 26/42226/5
authorFrank Nägler <frank.naegler@typo3.org>
Mon, 3 Aug 2015 14:37:31 +0000 (16:37 +0200)
committerWouter Wolters <typo3@wouterwolters.nl>
Wed, 5 Aug 2015 20:59:24 +0000 (22:59 +0200)
Resolves: #68754
Releases: master
Change-Id: I6de64c8fd5bb07d9c6c697deb94622399b594930
Reviewed-on: http://review.typo3.org/42226
Reviewed-by: Christian Kuhn <lolli@schwarzbu.ch>
Tested-by: Christian Kuhn <lolli@schwarzbu.ch>
Reviewed-by: Anja Leichsenring <aleichsenring@ab-softlab.de>
Tested-by: Anja Leichsenring <aleichsenring@ab-softlab.de>
Reviewed-by: Wouter Wolters <typo3@wouterwolters.nl>
Tested-by: Wouter Wolters <typo3@wouterwolters.nl>
typo3/sysext/backend/Classes/Form/FormEngine.php
typo3/sysext/t3editor/Classes/Form/Element/T3editorElement.php
typo3/sysext/t3editor/Classes/T3editor.php
typo3/sysext/t3editor/Resources/Private/Templates/t3editor.html
typo3/sysext/t3editor/Resources/Public/Css/t3editor.css
typo3/sysext/t3editor/Resources/Public/Icons/spinner.gif [deleted file]
typo3/sysext/t3skin/icons/gfx/alt_menu_mainitem_bg.gif [deleted file]

index 611b1cd..6e5b498 100644 (file)
@@ -399,7 +399,10 @@ class FormEngine {
                        $this->hiddenFieldAccum[] = $element;
                }
                foreach ($resultArray['additionalHeadTags'] as $element) {
-                       $this->additionalCode_pre[] = $element;
+                       // prevent multiple include of the same files
+                       if (!in_array($element, $this->additionalCode_pre)) {
+                               $this->additionalCode_pre[] = $element;
+                       }
                }
 
                if (!empty($resultArray['inlineData'])) {
@@ -1284,7 +1287,10 @@ class FormEngine {
                                \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::extPath('lang') . 'locallang_core.xlf',
                                'file_upload'
                        );
-
+                       // Load codemirror for T3Editor
+                       if (ExtensionManagementUtility::isLoaded('t3editor')) {
+                               $this->loadJavascriptLib('sysext/t3editor/Resources/Public/JavaScript/Contrib/codemirror/js/codemirror.js');
+                       }
                        // We want to load jQuery-ui inside our js. Enable this using requirejs.
                        $this->loadJavascriptLib('sysext/backend/Resources/Public/JavaScript/jsfunc.inline.js');
                        $out .= '
index 645c304..42764fb 100644 (file)
@@ -15,7 +15,10 @@ namespace TYPO3\CMS\T3editor\Form\Element;
  */
 
 use TYPO3\CMS\Backend\Form\Element\AbstractFormElement;
+use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
+use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
+use TYPO3\CMS\Lang\LanguageService;
 use TYPO3\CMS\T3editor\T3editor;
 use TYPO3\CMS\Core\Utility\MathUtility;
 
@@ -24,38 +27,274 @@ use TYPO3\CMS\Core\Utility\MathUtility;
  */
 class T3editorElement extends AbstractFormElement {
 
+       const MODE_CSS = 'css';
+       const MODE_HTML = 'html';
+       const MODE_JAVASCRIPT = 'javascript';
+       const MODE_MIXED = 'mixed';
+       const MODE_PHP = 'php';
+       const MODE_SPARQL = 'sparql';
+       const MODE_TYPOSCRIPT = 'typoscript';
+       const MODE_XML = 'xml';
+
+       /**
+        * @var array
+        */
+       protected $allowedModes = array(
+               self::MODE_CSS,
+               self::MODE_HTML,
+               self::MODE_JAVASCRIPT,
+               self::MODE_MIXED,
+               self::MODE_PHP,
+               self::MODE_SPARQL,
+               self::MODE_TYPOSCRIPT,
+               self::MODE_XML,
+       );
+
+       /**
+        * @var array
+        */
+       protected $resultArray;
+
+       /**
+        * @var string
+        */
+       protected $mode = '';
+
+       /**
+        * Counts the editors on the current page
+        *
+        * @var int
+        */
+       protected $editorCounter = 0;
+
+       /**
+        * Relative path to EXT:t3editor
+        *
+        * @var string
+        */
+       protected $relExtPath = '';
+
+       /**
+        * @var string
+        */
+       protected $codemirrorPath = 'sysext/t3editor/Resources/Public/JavaScript/Contrib/codemirror/js/';
+
+       /**
+        * RequireJS modules loaded for code completion
+        *
+        * @var array
+        */
+       protected $codeCompletionComponents = array('TsRef', 'CompletionResult', 'TsParser', 'TsCodeCompletion');
+
        /**
         * Render t3editor element
         *
         * @return array As defined in initializeResultArray() of AbstractNode
         */
        public function render() {
-               $resultArray = $this->initializeResultArray();
+               $this->getLanguageService()->includeLLFile('EXT:t3editor/Resources/Private/Language/locallang.xlf');
+               $this->relExtPath = ExtensionManagementUtility::extRelPath('t3editor');
+
+               $this->resultArray = $this->initializeResultArray();
 
                $parameterArray = $this->globalOptions['parameterArray'];
 
                $rows = MathUtility::forceIntegerInRange($parameterArray['fieldConf']['config']['rows'] ?: 10, 1, 40);
+               $this->setMode(isset($parameterArray['fieldConf']['config']['format']) ? $parameterArray['fieldConf']['config']['format'] : T3editor::MODE_MIXED);
 
-               $t3editor = GeneralUtility::makeInstance(T3editor::class);
-               $t3editor->setMode(isset($parameterArray['fieldConf']['config']['format']) ? $parameterArray['fieldConf']['config']['format'] : T3editor::MODE_MIXED);
+               $attributes = array();
+               $attributes['rows'] = $rows;
+               $attributes['wrap'] = 'off';
+               $attributes['style'] = 'width:100%;';
+               $attributes['onchange'] = GeneralUtility::quoteJSvalue($parameterArray['fieldChangeFunc']['TBE_EDITOR_fieldChanged']);
 
-               $doc = $GLOBALS['SOBE']->doc;
-               $attributes = 'rows="' . $rows . '"' .
-                       ' wrap="off"' .
-                       ' style="width:96%; height: 60%;"' .
-                       ' onchange="' . $parameterArray['fieldChangeFunc']['TBE_EDITOR_fieldChanged'] . '" ';
+               $attributeString = '';
+               foreach ($attributes as $param => $value) {
+                       $attributeString .= $param . '="' . htmlspecialchars($value) . '" ';
+               }
 
-               $resultArray['html'] = $t3editor->getCodeEditor(
+               $this->resultArray['html'] = $this->getHTMLCodeForEditor(
                        $parameterArray['itemFormElName'],
                        'text-monospace enable-tab',
                        $parameterArray['itemFormElValue'],
-                       $attributes,
+                       $attributeString,
                        $this->globalOptions['table'] . ' > ' . $this->globalOptions['fieldName'],
                        array('target' => 0)
                );
-               $resultArray['html'] .= $t3editor->getJavascriptCode($doc);
 
-               return $resultArray;
+               $this->initJavascriptCode();
+               return $this->resultArray;
+       }
+
+       /**
+        * Sets the type of code to edit, use one of the predefined constants
+        *
+        * @param string $mode Expects one of the predefined constants
+        * @throws \InvalidArgumentException
+        */
+       public function setMode($mode) {
+               if (!in_array($mode, $this->allowedModes, TRUE)) {
+                       throw new \InvalidArgumentException($mode . 'is not allowed', 1438352574);
+               }
+               $this->mode = $mode;
        }
 
+       /**
+        * Get mode
+        *
+        * @return string
+        */
+       public function getMode() {
+               return $this->mode;
+       }
+
+       /**
+        * Init the JavaScript code (header part) for editor
+        */
+       protected function initJavascriptCode() {
+               $this->resultArray['additionalHeadTags'][] = '<link rel="stylesheet" href="' . $this->relExtPath . 'Resources/Public/Css/t3editor.css' . '" />';
+               $this->resultArray['requireJsModules'][] = 'TYPO3/CMS/T3editor/T3editor';
+               if ($this->mode === self::MODE_TYPOSCRIPT) {
+                       foreach ($this->codeCompletionComponents as $codeCompletionComponent) {
+                               $this->resultArray['requireJsModules'][] = 'TYPO3/CMS/T3editor/Plugins/CodeCompletion/' . $codeCompletionComponent;
+                       }
+               }
+       }
+
+       /**
+        * Generates HTML with code editor
+        *
+        * @param string $name Name attribute of HTML tag
+        * @param string $class Class attribute of HTML tag
+        * @param string $content Content of the editor
+        * @param string $additionalParams Any additional editor parameters
+        * @param string $alt Alt attribute
+        * @param array $hiddenfields
+        * @return string Generated HTML code for editor
+        */
+       protected function getHTMLCodeForEditor($name, $class = '', $content = '', $additionalParams = '', $alt = '', array $hiddenfields = array()) {
+               $code = array();
+               $attributes = array();
+               $attributes['class'] = $class . ' t3editor';
+               $attributes['alt'] = $alt;
+               $attributes['id'] = 't3editor_' . $this->editorCounter;
+               $attributes['name'] = $name;
+               $attributes['data-labels'] = json_encode($this->getLanguageService()->getLabelsWithPrefix('js.', 'label_'));
+               $attributes['data-instance-number'] =  $this->editorCounter;
+               $attributes['data-editor-path'] =  $this->relExtPath;
+               $attributes['data-codemirror-path'] =  $this->codemirrorPath;
+               $attributes['data-ajaxsavetype'] = ''; // no ajax save in FormEngine at the moment
+               $attributes['data-parserfile'] = $this->getParserfileByMode($this->mode);
+               $attributes['data-stylesheet'] = $this->getStylesheetByMode($this->mode);
+
+               $attributesString = '';
+               foreach ($attributes as $attribute => $value) {
+                       $attributesString .= $attribute . '="' . htmlspecialchars($value) . '" ';
+               }
+               $attributesString .= $additionalParams;
+
+               $code[] = '<div class="t3editor">';
+               $code[] = '     <div class="t3e_wrap">';
+               $code[] = str_replace(array(CR, LF), '', GeneralUtility::getUrl(GeneralUtility::getFileAbsFileName('EXT:t3editor/Resources/Private/Templates/t3editor.html')));
+               $code[] = '     </div>';
+               $code[] = '     <textarea ' . $attributesString . '>' . htmlspecialchars($content) . '</textarea>';
+               $code[] = '</div>';
+
+               if (!empty($hiddenfields)) {
+                       foreach ($hiddenfields as $name => $value) {
+                               $code[] = '<input type="hidden" name="' . htmlspecialchars($name) . '" value="' . htmlspecialchars($value) . '" />';
+                       }
+               }
+               $this->editorCounter++;
+               return implode(LF, $code);
+       }
+
+       /**
+        * Determine the correct parser js file for given mode
+        *
+        * @param string $mode
+        * @return string Parser file name
+        */
+       protected function getParserfileByMode($mode) {
+               $parserfile = array();
+               switch ($mode) {
+                       case self::MODE_TYPOSCRIPT:
+                               $relPath = '../../../parse_typoscript/';
+                               $parserfile = array($relPath . 'tokenizetyposcript.js', $relPath . 'parsetyposcript.js');
+                               break;
+                       case self::MODE_JAVASCRIPT:
+                               $parserfile = array('tokenizetyposcript.js', 'parsejavascript.js');
+                               break;
+                       case self::MODE_CSS:
+                               $parserfile = array('parsecss.js');
+                               break;
+                       case self::MODE_XML:
+                               $parserfile = array('parsexml.js');
+                               break;
+                       case self::MODE_SPARQL:
+                               $parserfile = array('parsesparql.js');
+                               break;
+                       case self::MODE_HTML:
+                               $parserfile = array('tokenizejavascript.js', 'parsejavascript.js', 'parsecss.js', 'parsexml.js', 'parsehtmlmixed.js');
+                               break;
+                       case self::MODE_PHP:
+                       case self::MODE_MIXED:
+                               $parserfile = array('tokenizejavascript.js', 'parsejavascript.js', 'parsecss.js', 'parsexml.js', '../contrib/php/js/tokenizephp.js', '../contrib/php/js/parsephp.js', '../contrib/php/js/parsephphtmlmixed.js');
+                               break;
+               }
+               return json_encode($parserfile);
+       }
+
+       /**
+        * Determine the correct css file for given mode
+        *
+        * @param string $mode
+        * @return string css file name
+        */
+       protected function getStylesheetByMode($mode) {
+               switch ($mode) {
+                       case self::MODE_TYPOSCRIPT:
+                               $stylesheet = array($this->relExtPath . 'Resources/Public/Css/typoscriptcolors.css');
+                               break;
+                       case self::MODE_JAVASCRIPT:
+                               $stylesheet = array($this->codemirrorPath . '../css/jscolors.css');
+                               break;
+                       case self::MODE_CSS:
+                               $stylesheet = array($this->codemirrorPath . '../css/csscolors.css');
+                               break;
+                       case self::MODE_XML:
+                               $stylesheet = array($this->codemirrorPath . '../css/xmlcolors.css');
+                               break;
+                       case self::MODE_HTML:
+                               $stylesheet = array($this->codemirrorPath . '../css/xmlcolors.css', $this->codemirrorPath . '../css/jscolors.css', $this->codemirrorPath . '../css/csscolors.css');
+                               break;
+                       case self::MODE_SPARQL:
+                               $stylesheet = array($this->codemirrorPath . '../css/sparqlcolors.css');
+                               break;
+                       case self::MODE_PHP:
+                               $stylesheet = array($this->codemirrorPath . '../contrib/php/css/phpcolors.css');
+                               break;
+                       case self::MODE_MIXED:
+                               $stylesheet = array($this->codemirrorPath . '../css/xmlcolors.css', $this->codemirrorPath . '../css/jscolors.css', $this->codemirrorPath . '../css/csscolors.css', $this->codemirrorPath . '../contrib/php/css/phpcolors.css');
+                               break;
+                       default:
+                               $stylesheet = array();
+               }
+               $stylesheet[] = $this->relExtPath . 'Resources/Public/Css/t3editor_inner.css';
+               return json_encode($stylesheet);
+       }
+
+       /**
+        * @return LanguageService
+        */
+       protected function getLanguageService() {
+               return $GLOBALS['LANG'];
+       }
+
+       /**
+        * @return BackendUserAuthentication
+        */
+       protected function getBackendUserAuthentication() {
+               return $GLOBALS['BE_USER'];
+       }
 }
index 736aba4..2eb85ea 100644 (file)
@@ -99,6 +99,7 @@ class T3editor implements \TYPO3\CMS\Core\SingletonInterface {
         */
        public function setModeByFile($file) {
                $fileInfo = GeneralUtility::split_fileref($file);
+               // @TODO: @FIXME: the method setModeByType returns void, so this method will never return a string
                return $this->setModeByType($fileInfo['fileext']);
        }
 
index d9a3a63..c2e35ce 100644 (file)
@@ -1,6 +1,6 @@
-<div class="t3e_modalOverlay"></div>
+<div class="t3e_modalOverlay"><i class="fa fa-spinner fa-spin"></i></div>
 <div class="t3e_iframe_wrap"></div>
 <div class="t3e_statusbar_wrap">
        <span class="t3e_statusbar_title"></span>
        <span class="t3e_statusbar_status"></span>
-</div>
\ No newline at end of file
+</div>
index 7ad5ce5..675d397 100644 (file)
@@ -1,9 +1,11 @@
 .CodeMirror-wrapping {
        position: relative;
 }
+
 .CodeMirror-wrapping div {
        line-height: 13px !important;
 }
+
 div.CodeMirror-line-numbers {
        line-height: 13px !important;
        font-size: 10px;
@@ -16,21 +18,25 @@ div.CodeMirror-line-numbers {
 .TSREF_type_label, .TSREF_description_label{
        font-weight: bold;
 }
+
 .t3e_codeCompleteBox ul li span.word_definedTSREFProperty {
        color:#DF4E00;
        font-size:0.95em;
        font-weight:bold;
 }
+
 .t3e_codeCompleteBox ul li span.word_undefinedTSREFProperty {
        color:#FF8400;
        font-size:0.95em;
        font-weight:bold;
 }
+
 .t3e_codeCompleteBox ul li span.word_userProperty {
        color:#7c7c7c;
        font-size:0.95em;
        font-weight:bold;
 }
+
 .t3e_descriptionBox{
        height: 160px;
        width: 260px;
@@ -39,42 +45,46 @@ div.CodeMirror-line-numbers {
        background-color: #EFEFF4;
        z-index:190;
        border:2px solid silver;
-       padding:0px;
+       padding:0;
 }
+
 .t3e_toolbar{
        position:absolute;
        right:50px;
-       top:0pt;
+       top:0;
        width:200px;
-       /*border: 1px solid #CCCCCC;*/
 }
+
 .t3e_toolbar_icon{
        border: 1px solid #CCCCCC;
        width: 20px;
        height: 20px;
        float: left;
 }
+
 .t3e_codeCompleteWrap {
        position: absolute;
        top: 0; left: 0;
        z-index:190;
 }
+
 .t3e_codeCompleteBox {
        background-color: #EFEFF4;
        border:2px solid silver;
-       padding:0px;
+       padding:0;
 }
+
 .t3e_codeCompleteBox ul{
        list-style-type: none;
-       padding:0px;
-       margin:0px;
+       padding:0;
+       margin:0;
 
 }
 .t3e_codeCompleteBox ul li{
        padding-left:2px;
        padding-right:2px;
        font-weight:bold;
-       cursor: pointer; cursor: hand;
+       cursor: pointer;
 }
 .t3e_codeCompleteBox ul li.active{
        padding-left:2px;
@@ -87,22 +97,16 @@ div.CodeMirror-line-numbers {
        border: 1px solid gray;
        position: relative;
        background-color: #EFEFF4;
-       background-image: url('../../Public/Icons/spinner.gif');
-       background-position: 49% 50%;
-       background-repeat: no-repeat;
-       margin-right: 10px;
 }
 
 .t3e_modalOverlay {
        position: absolute;
        top: 0; left: 0;
        background-color: #EFEFF4;
-       background-image: url('../../Public/Icons/spinner.gif');
-       background-position: 49% 50%;
-       background-repeat: no-repeat;
        z-index:200;
        width: 100%;
        height: 100%;
+       text-align: center;
 }
 .t3e_autoCompleteBox {
        position: absolute;
@@ -110,18 +114,18 @@ div.CodeMirror-line-numbers {
        background-color: #EFEFF4;
        z-index:190;
        border:2px solid silver;
-       padding:0px;
+       padding:0;
 }
 .t3e_autoCompleteBox ul{
        list-style-type: none;
-       padding:0px;
-       margin:0px;
+       padding:0;
+       margin:0;
 }
 .t3e_autoCompleteBox ul li{
        padding-left:2px;
        padding-right:2px;
        font-weight:bold;
-       cursor: pointer; cursor: hand;
+       cursor: pointer;
 }
 .t3e_autoCompleteBox ul li.active{
        padding-left:2px;
@@ -152,19 +156,12 @@ div.CodeMirror-line-numbers {
        padding-right: 20px;
 }
 
-
-
-
-
 .t3e_statusbar_wrap {
        clear: both;
        font-size: 0.9em;
        padding-left: 3px;
        padding-top: 3px;
        background-color:#B8BEC9;
-       background-image: url(../../../../t3skin/icons/gfx/alt_menu_mainitem_bg.gif);
-       background-position:left top;
-       background-repeat:repeat-x;
        height: 20px;
 }
 .t3e_statusbar_wrap span {
@@ -178,19 +175,17 @@ div.CodeMirror-line-numbers {
        padding: 4px 14px 0 14px;
 }
 
-
 .t3e_clickable {
-       cursor:pointer;cursor:hand;
+       cursor: hand;
 }
 
-
 .t3e_statusbar_overlay {
        position: absolute; bottom: 20px; right: 17px;
        opacity: 0.85;
        background-color: #EFEFF4;
        width: 180px;
        height: 70%;
-       padding: 5px 0px 10px 0px;
+       padding: 5px 0 10px 0;
        border-left: 1px solid gray;
        border-right: 1px solid gray;
        border-top: 1px solid gray;
@@ -207,10 +202,10 @@ div.CodeMirror-line-numbers {
 .t3e_statusbar_overlay ul li {
        color: #212121;
        padding: 2px 6px 2px 6px;
-       cursor:pointer;cursor:hand;
+       cursor: hand;
 }
 .t3e_statusbar_overlay ul li label {
-       cursor:pointer;cursor:hand;
+       cursor:pointer;
 }
 .t3e_statusbar_overlay ul li:hover {
        background-color: #cfcfcf;
diff --git a/typo3/sysext/t3editor/Resources/Public/Icons/spinner.gif b/typo3/sysext/t3editor/Resources/Public/Icons/spinner.gif
deleted file mode 100644 (file)
index 12cc304..0000000
Binary files a/typo3/sysext/t3editor/Resources/Public/Icons/spinner.gif and /dev/null differ
diff --git a/typo3/sysext/t3skin/icons/gfx/alt_menu_mainitem_bg.gif b/typo3/sysext/t3skin/icons/gfx/alt_menu_mainitem_bg.gif
deleted file mode 100644 (file)
index aebe6ef..0000000
Binary files a/typo3/sysext/t3skin/icons/gfx/alt_menu_mainitem_bg.gif and /dev/null differ