Adding LibreJS back after bundles refactor upstream
[civicrm-core.git] / CRM / Core / Region.php
index 06cece6bc9e7fd08e0da124e6e16fb39b945f710..82b93889e57c8e5a02ddbb6e7e1ef815e28dfa50 100644 (file)
@@ -3,8 +3,7 @@
 /**
  * Maintain a set of markup/templates to inject inside various regions
  */
-class CRM_Core_Region {
-  static private $_instances = NULL;
+class CRM_Core_Region implements CRM_Core_Resources_CollectionInterface, CRM_Core_Resources_CollectionAdderInterface {
 
   /**
    * Obtain the content for a given region.
@@ -15,12 +14,14 @@ class CRM_Core_Region {
    * @return CRM_Core_Region
    */
   public static function &instance($name, $autocreate = TRUE) {
-    if ($autocreate && !isset(self::$_instances[$name])) {
-      self::$_instances[$name] = new CRM_Core_Region($name);
+    if ($autocreate && !isset(Civi::$statics[__CLASS__][$name])) {
+      Civi::$statics[__CLASS__][$name] = new CRM_Core_Region($name);
     }
-    return self::$_instances[$name];
+    return Civi::$statics[__CLASS__][$name];
   }
 
+  use CRM_Core_Resources_CollectionTrait;
+
   /**
    * Symbolic name of this region
    *
@@ -28,28 +29,13 @@ class CRM_Core_Region {
    */
   public $_name;
 
-  /**
-   * List of snippets to inject within region.
-   *
-   * e.g. $this->_snippets[3]['type'] = 'template';
-   *
-   * @var array
-   */
-  public $_snippets;
-
-  /**
-   * Whether the snippets array has been sorted
-   *
-   * @var bool
-   */
-  public $_isSorted;
-
   /**
    * @param string $name
    */
   public function __construct($name) {
     $this->_name = $name;
-    $this->_snippets = [];
+    $this->types = ['markup', 'template', 'callback', 'scriptFile', 'scriptUrl', 'script', 'jquery', 'settings', 'style', 'styleFile', 'styleUrl'];
+    $this->defaults['region'] = $name;
 
     // Placeholder which represents any of the default content generated by the main Smarty template
     $this->add([
@@ -58,92 +44,6 @@ class CRM_Core_Region {
       'markup' => '',
       'weight' => 0,
     ]);
-    $this->_isSorted = TRUE;
-  }
-
-  /**
-   * Add a snippet of content to a region.
-   *
-   * ```
-   * CRM_Core_Region::instance('page-header')->add(array(
-   *   'markup' => '<div style="color:red">Hello!</div>',
-   * ));
-   * CRM_Core_Region::instance('page-header')->add(array(
-   *   'script' => 'alert("Hello");',
-   * ));
-   * CRM_Core_Region::instance('page-header')->add(array(
-   *   'template' => 'CRM/Myextension/Extra.tpl',
-   * ));
-   * CRM_Core_Region::instance('page-header')->add(array(
-   *   'callback' => 'myextension_callback_function',
-   * ));
-   * ```
-   *
-   * Note: This function does not perform any extra encoding of markup, script code, or etc. If
-   * you're passing in user-data, you must clean it yourself.
-   *
-   * @param array $snippet
-   *   Array; keys:.
-   *   - type: string (auto-detected for markup, template, callback, script, scriptUrl, jquery, style, styleUrl)
-   *   - name: string, optional
-   *   - weight: int, optional; default=1
-   *   - disabled: int, optional; default=0
-   *   - markup: string, HTML; required (for type==markup)
-   *   - template: string, path; required (for type==template)
-   *   - callback: mixed; required (for type==callback)
-   *   - arguments: array, optional (for type==callback)
-   *   - script: string, Javascript code
-   *   - scriptUrl: string, URL of a Javascript file
-   *   - jquery: string, Javascript code which runs inside a jQuery(function($){...}); block
-   *   - style: string, CSS code
-   *   - styleUrl: string, URL of a CSS file
-   *
-   * @return array
-   */
-  public function add($snippet) {
-    static $types = ['markup', 'template', 'callback', 'scriptUrl', 'script', 'jquery', 'style', 'styleUrl'];
-    $defaults = [
-      'region' => $this->_name,
-      'weight' => 1,
-      'disabled' => FALSE,
-    ];
-    $snippet += $defaults;
-    if (!isset($snippet['type'])) {
-      foreach ($types as $type) {
-        // auto-detect
-        if (isset($snippet[$type])) {
-          $snippet['type'] = $type;
-          break;
-        }
-      }
-    }
-    if (!isset($snippet['name'])) {
-      $snippet['name'] = count($this->_snippets);
-    }
-
-    $this->_snippets[$snippet['name']] = $snippet;
-    $this->_isSorted = FALSE;
-    return $snippet;
-  }
-
-  /**
-   * @param string $name
-   * @param $snippet
-   */
-  public function update($name, $snippet) {
-    $this->_snippets[$name] = array_merge($this->_snippets[$name], $snippet);
-    $this->_isSorted = FALSE;
-  }
-
-  /**
-   * Get snippet.
-   *
-   * @param string $name
-   *
-   * @return mixed
-   */
-  public function get($name) {
-    return !empty($this->_snippets[$name]) ? $this->_snippets[$name] : NULL;
   }
 
   /**
@@ -157,23 +57,19 @@ class CRM_Core_Region {
    */
   public function render($default, $allowCmsOverride = TRUE) {
     // $default is just another part of the region
-    if (is_array($this->_snippets['default'])) {
-      $this->_snippets['default']['markup'] = $default;
+    if (is_array($this->snippets['default'])) {
+      $this->snippets['default']['markup'] = $default;
     }
-    // We hand as much of the work off to the CMS as possible
-    $cms = CRM_Core_Config::singleton()->userSystem;
 
-    if (!$this->_isSorted) {
-      uasort($this->_snippets, ['CRM_Core_Region', '_cmpSnippet']);
-      $this->_isSorted = TRUE;
-    }
+    Civi::dispatcher()->dispatch('civi.region.render', \Civi\Core\Event\GenericHookEvent::create(['region' => $this]));
 
+    $this->sort();
+
+    $cms = CRM_Core_Config::singleton()->userSystem;
     $smarty = CRM_Core_Smarty::singleton();
     $html = '';
-    foreach ($this->_snippets as $snippet) {
-      if ($snippet['disabled']) {
-        continue;
-      }
+
+    $renderSnippet = function($snippet) use (&$html, $smarty, $cms, $allowCmsOverride, &$renderSnippet) {
       switch ($snippet['type']) {
         case 'markup':
           $html .= $snippet['markup'];
@@ -198,11 +94,27 @@ class CRM_Core_Region {
           break;
 
         case 'jquery':
-          $snippet['script'] = sprintf("CRM.\$(function(\$) {\n%s\n});", $snippet['jquery']);
-          // no break - continue processing as script
+          $renderSnippet([
+            'type' => 'script',
+            'script' => sprintf("CRM.\$(function(\$) {\n%s\n});", $snippet['jquery']),
+          ]);
+          break;
+
+        case 'scriptFile':
+          foreach ($snippet['scriptFileUrls'] as $url) {
+            $html .= $renderSnippet(['type' => 'scriptUrl', 'scriptUrl' => $url] + $snippet);
+          }
+          break;
+
         case 'script':
           if (!$allowCmsOverride || !$cms->addScript($snippet['script'], $this->_name)) {
-            $html .= sprintf("<script type=\"text/javascript\">\n%s\n</script>\n", $snippet['script']);
+            $html .= sprintf("<script type=\"text/javascript\">\n// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3\n%s\n// @license-end\n</script>\n", $snippet['script']);
+          }
+          break;
+
+        case 'styleFile':
+          foreach ($snippet['styleFileUrls'] as $url) {
+            $html .= $renderSnippet(['type' => 'styleUrl', 'styleUrl' => $url] + $snippet);
           }
           break;
 
@@ -218,35 +130,26 @@ class CRM_Core_Region {
           }
           break;
 
+        case 'settings':
+          $settingsData = json_encode($this->getSettings(), JSON_UNESCAPED_SLASHES);
+          $js = "(function(vars) {
+            if (window.CRM) CRM.$.extend(true, CRM, vars); else window.CRM = vars;
+            })($settingsData)";
+          $html .= sprintf("<script type=\"text/javascript\">\n// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3\n%s\n// @license-end\n</script>\n", $js);
+          break;
+
         default:
           throw new CRM_Core_Exception(ts('Snippet type %1 is unrecognized',
             [1 => $snippet['type']]));
       }
-    }
-    return $html;
-  }
+    };
 
-  /**
-   * @param $a
-   * @param $b
-   *
-   * @return int
-   */
-  public static function _cmpSnippet($a, $b) {
-    if ($a['weight'] < $b['weight']) {
-      return -1;
-    }
-    if ($a['weight'] > $b['weight']) {
-      return 1;
-    }
-    // fallback to name sort; don't really want to do this, but it makes results more stable
-    if ($a['name'] < $b['name']) {
-      return -1;
-    }
-    if ($a['name'] > $b['name']) {
-      return 1;
+    foreach ($this->snippets as $snippet) {
+      if (empty($snippet['disabled'])) {
+        $renderSnippet($snippet);
+      }
     }
-    return 0;
+    return $html;
   }
 
 }