Afform.convert - Add stateless helper API to convert between layouts
authorTim Otten <totten@civicrm.org>
Wed, 25 Nov 2020 10:07:34 +0000 (02:07 -0800)
committerTim Otten <totten@civicrm.org>
Wed, 2 Dec 2020 02:47:39 +0000 (18:47 -0800)
ext/afform/core/Civi/Api4/Action/Afform/Convert.php [new file with mode: 0644]
ext/afform/core/Civi/Api4/Afform.php
ext/afform/mock/tests/phpunit/api/v4/AfformTest.php

diff --git a/ext/afform/core/Civi/Api4/Action/Afform/Convert.php b/ext/afform/core/Civi/Api4/Action/Afform/Convert.php
new file mode 100644 (file)
index 0000000..638ccfb
--- /dev/null
@@ -0,0 +1,77 @@
+<?php
+
+namespace Civi\Api4\Action\Afform;
+
+use Civi\Api4\Generic\AbstractAction;
+use Civi\Api4\Generic\Result;
+
+/**
+ * Convert afform layouts between different representations, e.g. from
+ * a deep array to HTML.
+ *
+ * @method setLayout(mixed $layout)
+ * @method getLayout(): mixed
+ * @method setFrom(string $layoutFormat)
+ * @method getFrom(): string
+ * @method setTo(string $layoutFormat)
+ * @method getTo(): string
+ * @method setFormatWhitespace(string $layoutFormat)
+ * @method getFormatWhitespace(): string
+ *
+ * @package Civi\Api4\Action\Afform
+ */
+class Convert extends AbstractAction {
+
+  /**
+   * @var string|array
+   */
+  protected $layout;
+
+  /**
+   * How is the input `$layout` formatted?
+   *
+   * @var string
+   * @options html,shallow,deep
+   */
+  protected $from = NULL;
+
+  /**
+   * How should the `$layout` be returned?
+   *
+   * @var string
+   * @options html,shallow,deep
+   */
+  protected $to = NULL;
+
+  /**
+   * Normalize whitespace?
+   *
+   * @var bool
+   */
+  protected $formatWhitespace = FALSE;
+
+  public function _run(Result $result) {
+    // Normalize to HTML
+    if ($this->from === 'html') {
+      $interimHtml = $this->layout;
+    }
+    else {
+      $converter = new \CRM_Afform_ArrayHtml($this->from !== 'shallow', $this->formatWhitespace);
+      $interimHtml = $converter->convertTreeToHtml($this->layout);
+    }
+
+    // And go to preferred format
+    if ($this->to === 'html') {
+      $final = $interimHtml;
+    }
+    else {
+      $converter = new \CRM_Afform_ArrayHtml($this->to !== 'shallow', $this->formatWhitespace);
+      $final = $converter->convertHtmlToArray($interimHtml);
+    }
+
+    $result[] = [
+      'layout' => $final,
+    ];
+  }
+
+}
index 8f1a400bd945c9dcde2f8fe42fcd1c7f180fc84e..a69ee5c29207f9497607a4efa59c3e506e889376 100644 (file)
@@ -57,6 +57,15 @@ class Afform extends Generic\AbstractEntity {
       ->setCheckPermissions($checkPermissions);
   }
 
+  /**
+   * @param bool $checkPermissions
+   * @return Action\Afform\Convert
+   */
+  public static function convert($checkPermissions = TRUE) {
+    return (new Action\Afform\Convert('Afform', __FUNCTION__))
+      ->setCheckPermissions($checkPermissions);
+  }
+
   /**
    * @param bool $checkPermissions
    * @return Action\Afform\Prefill
index d80e994395e141ca82e9c8c11c248dc94f314b7a..66c80aa4b13bcc425db666939035dca556bd78b1 100644 (file)
@@ -115,6 +115,48 @@ class api_v4_AfformTest extends api_v4_AfformTestCase {
     return $ex;
   }
 
+  /**
+   * In this test, we receive a layout
+   *
+   * @param string $formName
+   *   The symbolic name of the form.
+   * @param string $updateFormat
+   *   The format with which to write the data.
+   *   'html' or 'array'
+   * @param mixed $updateLayout
+   *   The new value to set
+   * @param string $readFormat
+   *   The format with which to read the data.
+   *   'html' or 'array'
+   * @param mixed $readLayout
+   *   The value that we expect to read.
+   * @param string $exampleName
+   *   (For debug messages) A symbolic name of the example data-set being tested.
+   * @dataProvider getFormatExamples
+   */
+  public function testBasicConvert($formName, $updateFormat, $updateLayout, $readFormat, $readLayout, $exampleName) {
+    $actual = Civi\Api4\Afform::convert()->setLayout($updateLayout)
+      ->setFrom($updateFormat)
+      ->setTo($readFormat)
+      ->execute();
+
+    $cb = function($m) {
+      return '<' . rtrim($m[1]) . '/>';
+    };
+    $norm = function($layout) use ($cb, &$norm) {
+      if (is_string($layout)) {
+        return preg_replace_callback(';<((br|img)[^>]*)/>;', $cb, $layout);
+      }
+      elseif (is_array($layout)) {
+        foreach ($layout as &$item) {
+          $item = $norm($item);
+        }
+      }
+    };
+
+    $this->assertEquals($norm($readLayout), $norm($actual->single()['layout']), "Based on \"$exampleName\", writing content as \"$updateFormat\" and reading back as \"$readFormat\".");
+  }
+
   /**
    * In this test, we update the layout and in one format and then read it back
    * in another format.