Civi::url() - Add support for path-variables
authorTim Otten <totten@civicrm.org>
Fri, 21 Jul 2023 23:29:19 +0000 (16:29 -0700)
committerTim Otten <totten@civicrm.org>
Mon, 24 Jul 2023 08:12:31 +0000 (01:12 -0700)
Civi.php
Civi/Core/Url.php

index e9d0dcab550cf70ae0fc456a3c5401bfc508632b..c7c48001d90782e511c1fb11ba7607f4ddbdeb4c 100644 (file)
--- a/Civi.php
+++ b/Civi.php
@@ -244,6 +244,9 @@ class Civi {
    * Ex: Link to a dynamically generated asset-file.
    *   $url = Civi::url('assetBuilder://crm-l10n.js?locale=en_US');
    *
+   * Ex: Link to a static asset (resource-file) in one of core's configurable paths.
+   *   $url = Civi::url('[civicrm.root]/js/Common.js');
+   *
    * Ex: Link to a static asset (resource-file) in an extension.
    *   $url = Civi::url('ext://org.civicrm.search_kit/css/crmSearchTasks.css');
    *
@@ -257,6 +260,7 @@ class Civi {
    *     - 'backend://' (Back-end page-route for staff)
    *     - 'service://` (Web-service page-route for automated integrations; aka webhooks and IPNs)
    *     - 'current://' (Whichever UI is currently active)
+   *     - 'asset://' (Static asset-file; see \Civi::paths())
    *     - 'assetBuilder://' (Dynamically-generated asset-file; see \Civi\Core\AssetBuilder)
    *     - 'ext://' (Static asset-file provided by an extension)
    *   An empty scheme (`//hello.txt`) is equivalent to `current://hello.txt`.
index bedea2aaa205c986f3288821a87531f216115a39..91b20d81ce2630db136b3d6d01a939b1ad6351eb 100644 (file)
@@ -93,6 +93,9 @@ final class Url {
     if ($logicalUri[0] === '/') {
       $logicalUri = 'current:' . $logicalUri;
     }
+    elseif ($logicalUri[0] === '[') {
+      $logicalUri = 'asset://' . $logicalUri;
+    }
 
     $parsed = parse_url($logicalUri);
     $this->scheme = $parsed['scheme'] ?? NULL;
@@ -413,17 +416,25 @@ final class Url {
         $result = \Civi::service('asset_builder')->getUrl($assetName, $assetParams);
         break;
 
-      case 'ext':
-        $parts = explode('/', $this->getPath(), 2);
-        $result = \Civi::resources()->getUrl($parts[0], $parts[1] ?? NULL, FALSE);
-        if ($this->query) {
-          $result .= '?' . $this->query;
+      case 'asset':
+        if (preg_match(';^\[([\w\.]+)\](.*)$;', $this->getPath(), $m)) {
+          [, $var, $rest] = $m;
+          $result = rtrim(\Civi::paths()->getVariable($var, 'url'), '/');
+          if ($preferFormat === 'relative') {
+            $result = \CRM_Utils_Url::toRelative($result);
+          }
+          $result .= $rest . $this->composeSuffix();
         }
-        if ($this->fragment) {
-          $result .= '#' . $this->fragment;
+        else {
+          throw new \RuntimeException("Malformed asset path: {$this->getPath()}");
         }
         break;
 
+      case 'ext':
+        $parts = explode('/', $this->getPath(), 2);
+        $result = \Civi::resources()->getUrl($parts[0], $parts[1] ?? NULL, FALSE) . $this->composeSuffix();
+        break;
+
       default:
         throw new \RuntimeException("Unknown URL scheme: {$this->getScheme()}");
     }
@@ -452,6 +463,17 @@ final class Url {
     return $this->htmlEscape ? htmlentities($result) : $result;
   }
 
+  private function composeSuffix(): string {
+    $result = '';
+    if ($this->query) {
+      $result .= '?' . $this->query;
+    }
+    if ($this->fragment) {
+      $result .= '#' . $this->fragment;
+    }
+    return $result;
+  }
+
   private static function detectFormat(): string {
     // Some environments may override default - e.g. cv-cli prefers absolute URLs
     // WISHLIST: If handling `Job.*`, then 'absolute'