ArrayHtml - Encode self-closing HTML tags (`<img />`, `<br />`) normally
authorTim Otten <totten@civicrm.org>
Tue, 29 Oct 2019 23:07:12 +0000 (16:07 -0700)
committerCiviCRM <info@civicrm.org>
Wed, 16 Sep 2020 02:13:19 +0000 (19:13 -0700)
ext/afform/core/CRM/Afform/ArrayHtml.php
ext/afform/mock/tests/phpunit/api/v4/AfformTest.php
ext/afform/mock/tests/phpunit/api/v4/formatExamples/self-closing.php [new file with mode: 0644]

index 98204aa65f7f5d830cb2494b3665fcbc618d3a3b..748dadfec3408a5e82ca0fd3af38f4742e2f7833 100644 (file)
@@ -33,6 +33,23 @@ class CRM_Afform_ArrayHtml {
     'af-fieldset' => [
       'model' => 'text',
     ],
+    'area' => ['#selfClose' => TRUE],
+    'base' => ['#selfClose' => TRUE],
+    'br' => ['#selfClose' => TRUE],
+    'col' => ['#selfClose' => TRUE],
+    'command' => ['#selfClose' => TRUE],
+    'embed' => ['#selfClose' => TRUE],
+    'hr' => ['#selfClose' => TRUE],
+    'iframe' => ['#selfClose' => TRUE],
+    'img' => ['#selfClose' => TRUE],
+    'input' => ['#selfClose' => TRUE],
+    'keygen' => ['#selfClose' => TRUE],
+    'link' => ['#selfClose' => TRUE],
+    'meta' => ['#selfClose' => TRUE],
+    'param' => ['#selfClose' => TRUE],
+    'source' => ['#selfClose' => TRUE],
+    'track' => ['#selfClose' => TRUE],
+    'wbr' => ['#selfClose' => TRUE],
   ];
 
   /**
@@ -93,9 +110,15 @@ class CRM_Afform_ArrayHtml {
         ]);
       }
     }
-    $buf .= '>';
-    $buf .= $this->convertArraysToHtml($children);
-    $buf .= '</' . $tag . '>';
+
+    if (empty($children) && $this->isSelfClosing($tag)) {
+      $buf .= ' />';
+    }
+    else {
+      $buf .= '>';
+      $buf .= $this->convertArraysToHtml($children);
+      $buf .= '</' . $tag . '>';
+    }
     return $buf;
   }
 
@@ -182,6 +205,17 @@ class CRM_Afform_ArrayHtml {
     return $children;
   }
 
+  /**
+   * @param string $tag
+   *   Ex: 'img', 'div'
+   * @return bool
+   *   TRUE if the tag should look like '<img/>'.
+   *   FALSE if the tag should look like '<div></div>'.
+   */
+  protected function isSelfClosing($tag) {
+    return $this->protoSchema[$tag]['#selfClose'] ?? FALSE;
+  }
+
   /**
    * Determine the type of data that is stored in an attribute.
    *
index f63272b6fe3c800efdf6c0cff076334029e9e3a2..0fdbc9ac403d41cf0ff2df4e245c18f5d8998322 100644 (file)
@@ -65,7 +65,7 @@ class api_v4_AfformTest extends api_v4_AfformTestCase {
   public function getFormatExamples() {
     $es = [];
 
-    foreach (['empty', 'string', 'comments', 'apple', 'banana', 'cherry'] as $exampleName) {
+    foreach (['empty', 'string', 'comments', 'self-closing', 'apple', 'banana', 'cherry'] as $exampleName) {
       $exampleFile = '/formatExamples/' . $exampleName . '.php';
       $example = require __DIR__ . $exampleFile;
       $formats = ['html', 'shallow', 'deep'];
diff --git a/ext/afform/mock/tests/phpunit/api/v4/formatExamples/self-closing.php b/ext/afform/mock/tests/phpunit/api/v4/formatExamples/self-closing.php
new file mode 100644 (file)
index 0000000..ab68d02
--- /dev/null
@@ -0,0 +1,27 @@
+<?php
+
+return [
+  'html' => '<span class="one"></span><img class="two" /><div><br class="three" /><br /></div>',
+  'shallow' => [
+    ['#tag' => 'span', 'class' => 'one'],
+    ['#tag' => 'img', 'class' => 'two'],
+    [
+      '#tag' => 'div',
+      '#children' => [
+        ['#tag' => 'br', 'class' => 'three'],
+        ['#tag' => 'br'],
+      ],
+    ],
+  ],
+  'deep' => [
+    ['#tag' => 'span', 'class' => 'one'],
+    ['#tag' => 'img', 'class' => 'two'],
+    [
+      '#tag' => 'div',
+      '#children' => [
+        ['#tag' => 'br', 'class' => 'three'],
+        ['#tag' => 'br'],
+      ],
+    ],
+  ],
+];