Afform - Read afform defn from .aff.php files as well as .aff.json
authorcolemanw <coleman@civicrm.org>
Thu, 21 Sep 2023 23:16:46 +0000 (19:16 -0400)
committercolemanw <coleman@civicrm.org>
Fri, 22 Sep 2023 13:27:25 +0000 (09:27 -0400)
By switching the file format, defn fields like `title` and `description` can be wrapped in ts().
This is intended for developers and extensions authors. They can generate these php files by calling `Afform.get`
with the api explorer, and copying the result (minus the "layout" field).
FormBuilder still saves files locally in `aff.json` format.
Overriding a packaged `.aff.php` form with a local `aff.json` works fine.

ext/afform/core/CRM/Afform/AfformScanner.php
ext/afform/core/CRM/Afform/Upgrader.php
ext/afform/core/Civi/Api4/Action/Afform/Revert.php
ext/afform/core/Civi/Api4/Utils/AfformSaveTrait.php

index ae136c30c509d2a7d38e2da102990064958a41bc..8b2d01e571ffaf3de62017bceff422f258ee7627 100644 (file)
  */
 class CRM_Afform_AfformScanner {
 
-  const METADATA_FILE = 'aff.json';
+  const METADATA_JSON = 'aff.json';
+
+  const METADATA_PHP = 'aff.php';
 
   const LAYOUT_FILE = 'aff.html';
 
-  const FILE_REGEXP = '/\.aff\.(json|html)$/';
+  const FILE_REGEXP = '/\.aff\.(json|html|php)$/';
 
   const DEFAULT_REQUIRES = 'afCore';
 
@@ -138,11 +140,10 @@ class CRM_Afform_AfformScanner {
    *     'requires' => ['afform'],
    *   ]
    */
-  public function getMeta($name) {
+  public function getMeta(string $name): ?array {
     // FIXME error checking
 
     $defaults = [
-      'name' => $name,
       'requires' => [],
       'title' => '',
       'description' => '',
@@ -152,22 +153,30 @@ class CRM_Afform_AfformScanner {
       'permission' => ['access CiviCRM'],
       'type' => 'system',
     ];
+    $defn = [];
 
-    $metaFile = $this->findFilePath($name, self::METADATA_FILE);
-    if ($metaFile !== NULL) {
-      $r = array_merge($defaults, json_decode(file_get_contents($metaFile), 1));
-      // Previous revisions of GUI allowed permission==''. array_merge() doesn't catch all forms of missing-ness.
-      if (empty($r['permission'])) {
-        $r['permission'] = $defaults['permission'];
-      }
-      return $r;
-    }
-    elseif ($this->findFilePath($name, self::LAYOUT_FILE)) {
-      return $defaults;
+    // If there is a local file it will be json - read from that first
+    $jsonFile = $this->findFilePath($name, self::METADATA_JSON);
+    if ($jsonFile !== NULL) {
+      $defn = json_decode(file_get_contents($jsonFile), 1);
     }
+    // Extensions may provide afform definitions in php files
     else {
+      $phpFile = $this->findFilePath($name, self::METADATA_PHP);
+      if ($phpFile !== NULL) {
+        $defn = include $phpFile;
+      }
+    }
+    // A form must have at least a layout file (if no metadata file, just use defaults)
+    if (!$defn && !$this->findFilePath($name, self::LAYOUT_FILE)) {
       return NULL;
     }
+    $defn = array_merge($defaults, $defn, ['name' => $name]);
+    // Previous revisions of GUI allowed permission==''. array_merge() doesn't catch all forms of missing-ness.
+    if (empty($defn['permission'])) {
+      $defn['permission'] = $defaults['permission'];
+    }
+    return $defn;
   }
 
   /**
@@ -248,15 +257,4 @@ class CRM_Afform_AfformScanner {
     return Civi::paths()->getPath('[civicrm.files]/ang');
   }
 
-  /**
-   * @return string
-   */
-  private function getMarkerRegexp() {
-    static $v;
-    if ($v === NULL) {
-      $v = '/\.(' . preg_quote(self::LAYOUT_FILE, '/') . '|' . preg_quote(self::METADATA_FILE, '/') . ')$/';
-    }
-    return $v;
-  }
-
 }
index 3f4011555bf97be8b96038c0cf8106ed87842678..953cb659ac94fd4186ca038c93dc404689f67a45 100644 (file)
@@ -45,7 +45,7 @@ class CRM_Afform_Upgrader extends CRM_Extension_Upgrader_Base {
       'join' => 'join_entity',
       'block' => 'entity_type',
     ];
-    foreach (glob("$localDir/*." . $scanner::METADATA_FILE) as $fileName) {
+    foreach (glob("$localDir/*." . $scanner::METADATA_JSON) as $fileName) {
       $meta = json_decode(file_get_contents($fileName), TRUE);
       foreach ($replacements as $oldKey => $newKey) {
         if (isset($meta[$oldKey])) {
index 070ecc9ad3c2ffabd86bd15dd9772aaf10363eec..a30c2d76b140bd1567fbc0c161356e7ec380a30b 100644 (file)
@@ -49,7 +49,7 @@ class Revert extends \Civi\Api4\Generic\BasicBatchAction {
     /** @var \CRM_Afform_AfformScanner $scanner */
     $scanner = \Civi::service('afform_scanner');
     $files = [
-      \CRM_Afform_AfformScanner::METADATA_FILE,
+      \CRM_Afform_AfformScanner::METADATA_JSON,
       \CRM_Afform_AfformScanner::LAYOUT_FILE,
     ];
 
index 42416f11e2777a4351c01264b7ea969d134716f4..699f3b6498208f09ff63fca4a17819f939444b1a 100644 (file)
@@ -22,8 +22,8 @@ trait AfformSaveTrait {
       $item['name'] = _afform_angular_module_name($prefix . '-' . \CRM_Utils_String::munge($item['title'], '-'));
       $suffix = '';
       while (
-        file_exists($scanner->createSiteLocalPath($item['name'] . $suffix, \CRM_Afform_AfformScanner::METADATA_FILE))
-        || file_exists($scanner->createSiteLocalPath($item['name'] . $suffix, 'aff.html'))
+        file_exists($scanner->createSiteLocalPath($item['name'] . $suffix, \CRM_Afform_AfformScanner::METADATA_JSON))
+        || file_exists($scanner->createSiteLocalPath($item['name'] . $suffix, \CRM_Afform_AfformScanner::LAYOUT_FILE))
       ) {
         $suffix++;
       }
@@ -57,7 +57,7 @@ trait AfformSaveTrait {
       $meta['permission'] = explode(',', $meta['permission']);
     }
     if (!empty($meta)) {
-      $metaPath = $scanner->createSiteLocalPath($item['name'], \CRM_Afform_AfformScanner::METADATA_FILE);
+      $metaPath = $scanner->createSiteLocalPath($item['name'], \CRM_Afform_AfformScanner::METADATA_JSON);
       \CRM_Utils_File::createDir(dirname($metaPath));
       // Add eof newline to make files git-friendly
       file_put_contents($metaPath, json_encode($meta, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . "\n");