Fix handling of non-breaking spaces
authorColeman Watts <coleman@civicrm.org>
Tue, 3 Dec 2019 18:04:13 +0000 (13:04 -0500)
committerCiviCRM <info@civicrm.org>
Wed, 16 Sep 2020 02:13:20 +0000 (19:13 -0700)
See https://stackoverflow.com/questions/59215211/why-does-domdocument-mess-up-unicode-nonbreaking-spaces

ext/afform/core/CRM/Afform/ArrayHtml.php
ext/afform/core/Civi/Api4/Utils/AfformFormatTrait.php
ext/afform/mock/tests/phpunit/api/v4/formatExamples/apple.php
ext/afform/mock/tests/phpunit/api/v4/formatExamples/banana.php

index 59c8cae0a5c69997d33d0321ccbdf54419245dc6..1c0e9f30ee75ac5b0497c2a1c5394b3c4ad4d0ec 100644 (file)
@@ -150,6 +150,12 @@ class CRM_Afform_ArrayHtml {
     return $buf . $end;
   }
 
+  /**
+   * Converts a subset of items into html markup
+   *
+   * @param array $children
+   * @return string html
+   */
   public function convertArraysToHtml($children) {
     $buf = '';
     $this->indent++;
@@ -167,6 +173,17 @@ class CRM_Afform_ArrayHtml {
     return $buf;
   }
 
+  /**
+   * Converts a full array of items into html markup
+   *
+   * @param array $tree
+   * @return string html
+   */
+  public function convertTreeToHtml($tree) {
+    $this->indent = -1;
+    return $this->replaceUnicodeChars($this->convertArraysToHtml($tree));
+  }
+
   /**
    * @param string $html
    *   Ex: '<div class="greeting">Hello world</div>'
@@ -180,7 +197,7 @@ class CRM_Afform_ArrayHtml {
 
     $doc = new DOMDocument();
     $doc->preserveWhiteSpace = !$this->formatWhitespace;
-    @$doc->loadHTML("<html><body>$html</body></html>");
+    @$doc->loadHTML("<?xml encoding=\"utf-8\" ?><html><body>$html</body></html>");
 
     // FIXME: Validate expected number of child nodes
 
@@ -210,7 +227,7 @@ class CRM_Afform_ArrayHtml {
         if (!$this->deepCoding && !$this->isNodeEditable($arr)) {
           $arr['#markup'] = '';
           foreach ($node->childNodes as $child) {
-            $arr['#markup'] .= $child->ownerDocument->saveXML($child);
+            $arr['#markup'] .= $this->replaceUnicodeChars($child->ownerDocument->saveXML($child));
           }
         }
         else {
@@ -220,7 +237,7 @@ class CRM_Afform_ArrayHtml {
       return $arr;
     }
     elseif ($node instanceof DOMText) {
-      return ['#text' => $node->textContent];
+      return ['#text' => $this->replaceUnicodeChars($node->textContent)];
     }
     elseif ($node instanceof DOMComment) {
       return ['#comment' => $node->nodeValue];
@@ -336,6 +353,25 @@ class CRM_Afform_ArrayHtml {
     }
   }
 
+  /**
+   * Convert non-breaking space character to html notation.
+   *
+   * Makes html files easier to read.
+   *
+   * Note: This function does NOT convert all html entities (< to &lt;, etc.)
+   * as the input string is assumed to already be valid markup.
+   *
+   * @param string $markup - some html
+   * @return string
+   */
+  public function replaceUnicodeChars($markup) {
+    // TODO: Potentially replace other unicode characters that can be represented as html entities
+    $replace = [
+      ["\xc2\xa0", '&nbsp;'],
+    ];
+    return str_replace(array_column($replace, 0), array_column($replace, 1), $markup);
+  }
+
   /**
    * Determine if a node is recognized by the gui editor.
    *
index 4383a11066a432aa1eacee4025ccc2e902fe79c5..ea4af10d3634f8b7c6844bdbe9f4844d8593bd20 100644 (file)
@@ -58,7 +58,7 @@ trait AfformFormatTrait {
       return $mixed;
     }
     $converter = new \CRM_Afform_ArrayHtml($this->layoutFormat !== 'shallow', $this->formatWhitespace);
-    return $converter->convertArraysToHtml($mixed);
+    return $converter->convertTreeToHtml($mixed);
   }
 
 }
index 18a4bb0eabe800f63566ca0133753b560b2d2b6c..b082ae0a5a74c1a3cee5f60ca0c76f15c2b802f2 100644 (file)
@@ -1,12 +1,12 @@
 <?php
 
 return [
-  'html' => '<strong>New text!</strong>',
-  'pretty' => "<strong>New text!</strong>\n",
+  'html' => '<strong>New &nbsp; text!</strong>',
+  'pretty' => "<strong>New &nbsp; text!</strong>\n",
   'shallow' => [
-    ['#tag' => 'strong', '#markup' => 'New text!'],
+    ['#tag' => 'strong', '#markup' => 'New &nbsp; text!'],
   ],
   'deep' => [
-    ['#tag' => 'strong', '#children' => [['#text' => 'New text!']]],
+    ['#tag' => 'strong', '#children' => [['#text' => 'New &nbsp; text!']]],
   ],
 ];
index da70ac501c14dcead1967ec6117366b707c8e133..122d455b002640a954360eb8aa5c49864228af9f 100644 (file)
@@ -1,10 +1,10 @@
 <?php
 
 return [
-  'html' => '<div class="af-block"><strong>  New text!</strong><strong class="af-text"> No whitespace! </strong><af-field name="do_not_sms" defn="{label: \'Do not do any of the emailing\'}" /></div>',
+  'html' => '<div class="af-block"><strong>  New text!</strong><strong class="af-text"> &nbsp; Get a trim! </strong><af-field name="do_not_sms" defn="{label: \'Do not do any of the emailing\'}" /></div>',
   'pretty' => '<div class="af-block">
   <strong>  New text!</strong>
-  <strong class="af-text">No whitespace!</strong>
+  <strong class="af-text">&nbsp; Get a trim!</strong>
   <af-field name="do_not_sms" defn="{label: \'Do not do any of the emailing\'}" />
 </div>
 ',
@@ -14,7 +14,7 @@ return [
       'class' => 'af-block',
       '#children' => [
         ['#tag' => 'strong', '#markup' => '  New text!'],
-        ['#tag' => 'strong', 'class' => 'af-text', '#children' => [['#text' => 'No whitespace!']]],
+        ['#tag' => 'strong', 'class' => 'af-text', '#children' => [['#text' => "&nbsp; Get a trim!"]]],
         ['#tag' => 'af-field', 'name' => 'do_not_sms', 'defn' => "{label: 'Do not do any of the emailing'}"],
       ],
     ],
@@ -25,7 +25,7 @@ return [
       'class' => 'af-block',
       '#children' => [
         ['#tag' => 'strong', '#markup' => '  New text!'],
-        ['#tag' => 'strong', 'class' => 'af-text', '#children' => [['#text' => ' No whitespace! ']]],
+        ['#tag' => 'strong', 'class' => 'af-text', '#children' => [['#text' => " &nbsp; Get a trim! "]]],
         ['#tag' => 'af-field', 'name' => 'do_not_sms', 'defn' => "{label: 'Do not do any of the emailing'}"],
       ],
     ],
@@ -36,7 +36,7 @@ return [
       'class' => 'af-block',
       '#children' => [
         ['#tag' => 'strong', '#children' => [['#text' => '  New text!']]],
-        ['#tag' => 'strong', 'class' => 'af-text', '#children' => [['#text' => ' No whitespace! ']]],
+        ['#tag' => 'strong', 'class' => 'af-text', '#children' => [['#text' => " &nbsp; Get a trim! "]]],
         ['#tag' => 'af-field', 'name' => 'do_not_sms', 'defn' => ['label' => 'Do not do any of the emailing']],
       ],
     ],