/**
* 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.
* @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
*
*/
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([
'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;
}
/**
*/
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'];
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;
}
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;
}
}