Skip to content

Commit

Permalink
Merge pull request #18660 from totten/master-bundle-ext
Browse files Browse the repository at this point in the history
(REF) Make it easier for extensions to define basic bundles
  • Loading branch information
eileenmcnaughton authored Oct 2, 2020
2 parents 63bb878 + 96622c8 commit 02bd631
Show file tree
Hide file tree
Showing 4 changed files with 105 additions and 21 deletions.
38 changes: 37 additions & 1 deletion CRM/Core/Resources/Bundle.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,46 @@ class CRM_Core_Resources_Bundle implements CRM_Core_Resources_CollectionInterfac
* @param string|NULL $name
* @param string[]|NULL $types
* List of resource-types to permit in this bundle. NULL for a default list.
* Ex: ['styleFile', 'styleUrl']
* The following aliases are allowed: '*all*', '*default*', '*script*', '*style*'
*/
public function __construct($name = NULL, $types = NULL) {
$this->name = $name;
$this->types = $types ?: ['script', 'scriptFile', 'scriptUrl', 'settings', 'style', 'styleFile', 'styleUrl'];

$typeAliases = [
'*all*' => ['script', 'scriptFile', 'scriptUrl', 'settings', 'style', 'styleFile', 'styleUrl', 'markup', 'template', 'callback'],
'*default*' => ['script', 'scriptFile', 'scriptUrl', 'settings', 'style', 'styleFile', 'styleUrl'],
'*style*' => ['style', 'styleFile', 'styleUrl'],
'*script*' => ['script', 'scriptFile', 'scriptUrl'],
];
$mapType = function ($t) use ($typeAliases) {
return $typeAliases[$t] ?? [$t];
};
$types = $types ?: ['*default*'];
$this->types = array_unique(array_merge(...array_map($mapType, (array) $types)));
}

/**
* Fill in default values for the 'region' property.
*
* @return static
*/
public function fillDefaults() {
$this->filter(function ($s) {
if (!isset($s['region'])) {
if ($s['type'] === 'settings') {
$s['region'] = NULL;
}
elseif (preg_match(';^(markup|template|callback);', $s['type'])) {
$s['region'] = 'page-header';
}
else {
$s['region'] = CRM_Core_Resources_Common::REGION;
}
}
return $s;
});
return $this;
}

}
53 changes: 33 additions & 20 deletions CRM/Core/Resources/Common.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,36 @@ class CRM_Core_Resources_Common {

const REGION = 'html-header';

/**
* Create a "basic" (generic) bundle.
*
* The bundle goes through some lifecycle events (like `hook_alterBundle`).
*
* To define default content for a basic bundle, you may either give an
* `$init` function or subscribe to `hook_alterBundle`.
*
* @param string $name
* Symbolic name of the bundle.
* @param callable|NULL $init
* Optional initialization function. Populate default resources.
* Signature: `function($bundle): void`
* Example: `function myinit($b) { $b->addScriptFile(...)->addStyleFile(...); }`
* @param string|string[] $types
* List of resource-types to permit in this bundle. NULL for a default list.
* Example: ['styleFile', 'styleUrl']
* The following aliases are allowed: '*all*', '*default*', '*script*', '*style*'
* @return CRM_Core_Resources_Bundle
*/
public static function createBasicBundle($name, $init = NULL, $types = NULL) {
$bundle = new CRM_Core_Resources_Bundle($name, $types);
if ($init !== NULL) {
$init($bundle);
}
CRM_Utils_Hook::alterBundle($bundle);
$bundle->fillDefaults();
return $bundle;
}

/**
* The 'bundle.bootstrap3' service is a collection of resources which are
* loaded when a page needs to support Boostrap CSS v3.
Expand Down Expand Up @@ -47,7 +77,7 @@ public static function createBootstrap3Bundle($name) {
);

CRM_Utils_Hook::alterBundle($bundle);
self::useRegion($bundle, self::REGION);
$bundle->fillDefaults();
return $bundle;
}

Expand Down Expand Up @@ -76,7 +106,7 @@ public static function createStyleBundle($name) {
$bundle->addStyleFile('civicrm', 'css/crm-i.css', -101);

CRM_Utils_Hook::alterBundle($bundle);
self::useRegion($bundle, self::REGION);
$bundle->fillDefaults();
return $bundle;
}

Expand Down Expand Up @@ -133,7 +163,7 @@ public static function createFullBundle($name) {
]);

CRM_Utils_Hook::alterBundle($bundle);
self::useRegion($bundle, self::REGION);
$bundle->fillDefaults();
return $bundle;
}

Expand Down Expand Up @@ -273,21 +303,4 @@ protected static function coreResourceList($region) {
return $items;
}

/**
* Ensure that all elements of the bundle are in the same region.
*
* @param CRM_Core_Resources_Bundle $bundle
* @param string $region
* @return CRM_Core_Resources_Bundle
*/
protected static function useRegion($bundle, $region) {
$bundle->filter(function ($s) use ($region) {
if ($s['type'] !== 'settings' && !isset($s['region'])) {
$s['region'] = $region;
}
return $s;
});
return $bundle;
}

}
16 changes: 16 additions & 0 deletions tests/phpunit/CRM/Core/Resources/BundleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,20 @@ public function testMergeIntoRegion() {
$this->assertEquals('http://example.com/region.css', $region->get('http://example.com/region.css')['styleUrl']);
}

/**
* Add some resources - sometimes forgetting to set a 'region'. Fill in missing regions.
*/
public function testFillDefaults() {
$bundle = new CRM_Core_Resources_Bundle(__FUNCTION__, ['scriptUrl', 'styleUrl', 'markup']);
$bundle->addScriptUrl('http://example.com/myscript.js');
$bundle->addStyleUrl('http://example.com/yonder-style.css', ['region' => 'yonder']);
$bundle->addMarkup('<b>Cheese</b>', ['name' => 'cheese']);

$bundle->fillDefaults();

$this->assertEquals('html-header', $bundle->get('http://example.com/myscript.js')['region']);
$this->assertEquals('yonder', $bundle->get('http://example.com/yonder-style.css')['region']);
$this->assertEquals('page-header', $bundle->get('cheese')['region']);
}

}
19 changes: 19 additions & 0 deletions tests/phpunit/CRM/Core/ResourcesTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,25 @@ public function tearDown() {
$_GET = $this->originalGet;
}

public function testCreateBasicBundle() {
$hits = [];

$init = function(CRM_Core_Resources_Bundle $b) use (&$hits) {
$hits[] = 'init_' . $b->name;
$b->addScript('doStuff();');
};
$alter = function ($e) use (&$hits) {
$hits[] = 'alter_' . $e->bundle->name;
$e->bundle->addScript('alert();');
};

Civi::dispatcher()->addListener('hook_civicrm_alterBundle', $alter);
$b = CRM_Core_Resources_Common::createBasicBundle('cheese', $init);
$this->assertEquals('cheese', $b->name);
$this->assertEquals(['init_cheese', 'alter_cheese'], $hits);
$this->assertEquals(['doStuff();', 'alert();'], array_values(CRM_Utils_Array::collect('script', $b->getAll())));
}

/**
* Make two bundles (multi-regional). Add them to CRM_Core_Resources.
* Ensure that the resources land in the right regions.
Expand Down

0 comments on commit 02bd631

Please sign in to comment.