From 1f2dd9ddcec615db4cba4f94450f3dad801c8232 Mon Sep 17 00:00:00 2001
From: Carsten  Rose <carsten.rose@math.uzh.ch>
Date: Thu, 6 Oct 2016 16:48:58 +0200
Subject: [PATCH] Support.php: escapes double ticks with &quot; instead of \" -
 needed for JSON encoded config strings for TinyMCE. AbstractBuildForm.php:
 customize default setup for TinyMCE.

---
 extension/qfq/qfq/AbstractBuildForm.php    | 45 +++++++++++++++++++---
 extension/qfq/qfq/Constants.php            |  4 ++
 extension/qfq/qfq/helper/Support.php       | 24 ++++++++++--
 extension/qfq/tests/phpunit/DeleteTest.php |  2 +-
 extension/qfq/tests/phpunit/StoreTest.php  |  3 ++
 5 files changed, 68 insertions(+), 10 deletions(-)

diff --git a/extension/qfq/qfq/AbstractBuildForm.php b/extension/qfq/qfq/AbstractBuildForm.php
index d8834c186..bf71873a3 100644
--- a/extension/qfq/qfq/AbstractBuildForm.php
+++ b/extension/qfq/qfq/AbstractBuildForm.php
@@ -1980,6 +1980,9 @@ abstract class AbstractBuildForm {
     }
 
     /**
+     * Build a HTML 'textarea' element which becomes a TinyMCE Editor.
+     * List of possible plugins: https://www.tinymce.com/docs/plugins/
+     *
      * @param array $formElement
      * @param $htmlFormElementId
      * @param $value
@@ -1990,20 +1993,25 @@ abstract class AbstractBuildForm {
      */
     public function buildEditor(array $formElement, $htmlFormElementId, $value, array &$json, $mode = FORM_LOAD) {
 
+        //TODO plugin autoresize nutzen um Editorgroesse anzugeben
+
         $this->adjustMaxLength($formElement);
 
         $attribute = Support::doAttribute('name', $htmlFormElementId);
-//        $attribute = Support::doAttribute('name', 'rte');
         $attribute .= Support::doAttribute('id', $htmlFormElementId);
 
         $attribute .= Support::doAttribute('class', 'qfq-tinymce');
         $attribute .= Support::doAttribute('data-control-name', "$htmlFormElementId");
         $attribute .= Support::doAttribute('data-placeholder', $formElement['placeholder']);
-//        $attribute .= Support::doAttribute('data-value', htmlentities($value), false);
 //        $attribute .= Support::doAttribute('data-autofocus', $formElement['autofocus']);
         $attribute .= Support::doAttribute('data-load', ($formElement['dynamicUpdate'] === 'yes') ? 'data-load' : '');
         $attribute .= Support::doAttribute('data-title', $formElement['tooltip']);
-        $attribute .= Support::doAttribute('data-config', $this->getPrefixedElementsAsJSON(FE_EDITOR_PREFIX, $formElement));
+
+        $formElement = $this->setEditorConfig($formElement, $htmlFormElementId);
+        // $formElement['editor-plugins']='autoresize code'
+        // $formElement['editor-contextmenu']='link image | cell row column'
+        $json = $this->getPrefixedElementsAsJSON(FE_EDITOR_PREFIX, $formElement);
+        $attribute .= Support::doAttribute('data-config', $json, true, ESCAPE_WITH_HTML_QUOTE);
 
 
         $attribute .= $this->getAttributeFeMode($formElement[FE_MODE]);
@@ -2015,6 +2023,33 @@ abstract class AbstractBuildForm {
         return $element . $this->getHelpBlock();
     }
 
+    /**
+     * @param array $formElement
+     * @param $htmlFormElementId
+     * @return array
+     */
+    private function setEditorConfig(array $formElement, $htmlFormElementId) {
+
+
+        if (!isset($formElement[FE_EDITOR_PREFIX . 'plugins'])) {
+            $formElement[FE_EDITOR_PREFIX . 'plugins'] = 'code link searchreplace table textcolor textpattern visualchars';
+        }
+
+        if (!isset($formElement[FE_EDITOR_PREFIX . 'toolbar'])) {
+            $formElement[FE_EDITOR_PREFIX . 'toolbar'] = 'code link searchreplace table forecolor backcolor visualchars';
+        }
+
+        if (isset($formElement['autofocus']) && $formElement['autofocus'] == 'yes') {
+            $formElement[FE_EDITOR_PREFIX . 'auto_focus'] = $htmlFormElementId;
+        }
+
+
+        // Disable the element path within the status bar at the bottom of the editor.
+        $formElement[FE_EDITOR_PREFIX . 'elementpath'] = 'false';
+
+        return $formElement;
+    }
+
     /**
      * Searches for '$prefix*' elements in $formElement. Collect all found elements, strip $prefix (=$keyName) and
      *   returns keys/values JSON encoded.
@@ -2035,8 +2070,8 @@ abstract class AbstractBuildForm {
 
                 $keyName = substr($key, strlen($prefix));
 
-                if ($keyName == '' || !ctype_alpha($keyName)) {
-                    throw new UserFormException("Empty '" . $prefix . "*' keyname or invalid characters: '" . $keyName . "'", ERROR_INVALID_EDITOR_PROPERTY_NAME);
+                if ($keyName == '') {
+                    throw new UserFormException("Empty '" . $prefix . "*' keyname: '" . $keyName . "'", ERROR_INVALID_EDITOR_PROPERTY_NAME);
                 }
 
                 if ($value != '') {
diff --git a/extension/qfq/qfq/Constants.php b/extension/qfq/qfq/Constants.php
index 97f9f9071..1e3fb96cd 100644
--- a/extension/qfq/qfq/Constants.php
+++ b/extension/qfq/qfq/Constants.php
@@ -149,6 +149,8 @@ const ERROR_MISSING_TABLE_NAME = 1064;
 const ERROR_MISSING_TABLE = 1065;
 const ERROR_RECORD_NOT_FOUND = 1066;
 const ERROR_INVALID_EDITOR_PROPERTY_NAME = 1067;
+const ERROR_UNKNOWN_ESCAPE_MODE = 1068;
+
 
 // Subrecord
 const ERROR_SUBRECORD_MISSING_COLUMN_ID = 1100;
@@ -481,6 +483,8 @@ const ACTION_KEYWORD_SLAVE_ID = 'slaveId';
 // SUPPORT
 const PARAM_T3_ALL = 't3 all';
 const PARAM_T3_NO_ID = "t3 no id";
+const ESCAPE_WITH_BACKSLASH = 'backslash';
+const ESCAPE_WITH_HTML_QUOTE = 'htmlquote';
 
 // AbstractBuildForm
 const FLAG_ALL = 'flagAll';
diff --git a/extension/qfq/qfq/helper/Support.php b/extension/qfq/qfq/helper/Support.php
index 407b3910c..e37cc6bbe 100644
--- a/extension/qfq/qfq/helper/Support.php
+++ b/extension/qfq/qfq/helper/Support.php
@@ -92,7 +92,7 @@ class Support {
      * @param bool $flagOmitEmpty
      * @return string
      */
-    public static function doAttribute($type, $value, $flagOmitEmpty = true) {
+    public static function doAttribute($type, $value, $flagOmitEmpty = true, $modeEscape = ESCAPE_WITH_BACKSLASH) {
         if ($flagOmitEmpty && $value === "") {
             return '';
         }
@@ -114,23 +114,39 @@ class Support {
                 break;
         }
 
-        $value = self::escapeDoubleTick(trim($value));
+        $value = self::escapeDoubleTick(trim($value), $modeEscape);
+
         return $type . '="' . $value . '" ';
+
     }
 
     /**
      * Escapes Double Ticks ("), which are not already escaped.
+     * modeEscape: ESCAPE_WITH_BACKSLASH | ESCAPE_WITH_HTML_QUOTE
+     *
+     * TinyMCE: Encoding JS Attributes (keys & values) for TinyMCE needs to be encapsulated in '&quot;' instead of '\"'.
      *
      * @param $str
+     * @param string $modeEscape
      * @return string
+     * @throws CodeException
      */
-    public static function escapeDoubleTick($str) {
+    public static function escapeDoubleTick($str, $modeEscape = ESCAPE_WITH_BACKSLASH) {
         $newStr = '';
 
         for ($ii = 0; $ii < strlen($str); $ii++) {
             if ($str[$ii] === '"') {
                 if ($ii === 0 || $str[$ii - 1] != '\\') {
-                    $newStr .= '\\';
+                    switch ($modeEscape) {
+                        case ESCAPE_WITH_BACKSLASH:
+                            $newStr .= '\\';
+                            break;
+                        case ESCAPE_WITH_HTML_QUOTE:
+                            $newStr .= '&quot;';
+                            continue 2;
+                        default:
+                            throw new CodeException('Unknown modeEscape=' . $modeEscape, ERROR_UNKNOWN_ESCAPE_MODE);
+                    }
                 }
             }
             $newStr .= $str[$ii];
diff --git a/extension/qfq/tests/phpunit/DeleteTest.php b/extension/qfq/tests/phpunit/DeleteTest.php
index 2e1cff860..89b6b0d5d 100644
--- a/extension/qfq/tests/phpunit/DeleteTest.php
+++ b/extension/qfq/tests/phpunit/DeleteTest.php
@@ -90,7 +90,7 @@ class DeleteTest extends \AbstractDatabaseTest {
 
         $this->assertEquals(false, $rc);
 
-        $expect = ['content' => "Record 100 not found in table 'Person'.", 'errorCode' => 1065];
+        $expect = ['content' => "Record 100 not found in table 'Person'.", 'errorCode' => 1066];
         $this->assertEquals($expect, $msg);
     }
 
diff --git a/extension/qfq/tests/phpunit/StoreTest.php b/extension/qfq/tests/phpunit/StoreTest.php
index fa58bb752..279283535 100644
--- a/extension/qfq/tests/phpunit/StoreTest.php
+++ b/extension/qfq/tests/phpunit/StoreTest.php
@@ -66,6 +66,9 @@ class StoreTest extends \PHPUnit_Framework_TestCase {
 
     public function testSetVarStoreSystem() {
 
+        // set new Sessionname
+        $this->store->setVar(SYSTEM_SQL_LOG_MODE, "all", STORE_SYSTEM);
+
         // Sessionname: default value
         $this->assertEquals('all', $this->store->getVar(SYSTEM_SQL_LOG_MODE, STORE_SYSTEM), "System: SQL_LOG");
 
-- 
GitLab