Resources Reference¶
Introduction¶
The resources subsystem supports loading Javascript code, CSS code, settings, and so on. Most resources are static files included with CiviCRM. However, resources can also be external scripts, inline code-fragments, or dynamically-generated files.
In this chapter, we first skim some common tasks and idioms for getting started. We'll then dig deeper into the organization, mechanics and APIs.
Example values: "com.example.foo" and "http://www.example.com"
In the following discussion, we use a few placeholder values:
com.example.foo
: This is a hypothetical extension that builds on CiviCRM. To use resources from your own extension, substitute appropriately. To use files provided by CiviCRM core, use the special namecivicrm
.http://www.example.com
: This is the full URL of a website. It may be external or internal to CiviCRM.
Common tasks¶
Add Javascript¶
The following examples add Javascript to the current page-view. This can be done via PHP or Smarty.
Add Javascript (PHP)
// Add a Javascript file provided by an extension
Civi::resources()->addScriptFile('com.example.foo', 'bar.js');
// Add an external Javascript file
Civi::resources()->addScriptUrl('http://www.example.com/bar.js');
// Add raw Javascript code
Civi::resources()->addScript('alert("hello");');
Add Javascript (Smarty)
<!-- Add a Javascript file provided by an extension -->
{crmScript ext=com.example.foo file=bar.js}
<!-- Add an external Javascript file -->
{crmScript url="http://www.example.com/bar.js"}
In all these examples, the Javascript will (by default) be added to the page-footer
. This is the default region, and it is
a good place to put scripts that are not needed on every page of the website. Of course, some resources (such as jQuery plugins) may be
loaded in the html-header
or another region. We'll return to this in the section "Set positioning".
Add Javascript variables¶
If you need to transfer runtime data from PHP (server) to Javascript (browser/client), then you can register vars
with the resource manager. The data will be serialized (with JSON) and made available as part of Javascript's CRM
object.
Export data from server to client (PHP)
Civi::resources()->addVars('myNamespace', ['foo' => 'bar']);
Read exported data (JS)
CRM.alert(CRM.vars.myNamespace.foo); // Alerts "bar"
More infomation can be found in the Javascript reference.
Add stylesheet¶
As with Javascript, we may add CSS resources from PHP or Smarty:
Add stylesheet (PHP)
// Add a CSS file provided by an extension
Civi::resources()->addStyleFile('com.example.foo', 'bar.css');
// Add an external CSS file
Civi::resources()->addStyleUrl('http://www.example.com/bar.css');
// Add raw CSS code
Civi::resources()->addStyle('body { background: black; }');
Add stylesheet (Smarty)
<!-- Add a CSS file provided by an extension -->
{crmStyle ext=com.example.foo file=bar.css}
<!-- Add an external CSS file -->
{crmStyle url="http://www.example.com/bar.css"}
As with the Javascript examples, these CSS examples will be added to the page-footer
region by default. We'll revisit this in the section "Set positioning".
Add bundle¶
A bundle is a collection of related resources. For example, the bootstrap3 bundle provides support for Bootstrap 3 CSS classes. It (often) requires loading a mix of CSS and JS files (though the details may depend on the theme/environment). You can add the list of files as a bundle:
Add bundles (PHP)
// Add one bundle -- bootstrap3
Civi::resources()->addBundle('bootstrap3');
// Add two bundles -- foo and bar
Civi::resources()->addBundle(['foo', 'bar']);
Generate URLs¶
If you need to reference the URL for a file, then consider these examples:
Generate URLs (PHP)
// Get an image URL
Civi::resources()->getUrl('com.example.foo', 'bar.png');
// Get an extension's base URL
Civi::resources()->getUrl('com.example.bar');
Generate URLs (Smarty)
<!-- Get an image URL -->
{crmResURL ext=com.example.foo file=bar.png}
<!-- Get an extension's base URL -->
{crmResURL ext=com.example.foo }
How do {crmResURL}
and {crmURL}
compare?
{crmResURL}
sounds similar to another Smarty tag, {crmURL}
, but they are functionally distinct:
{crmURL}
constructs the URL of a dynamic web page, adjusting the path and query parameters of the URL based on the CMS.{crmResURL}
constructs the URL of a static resource file; because the file is static, one may link directly without using the CMS or manipulating query parameters.
Set positioning¶
When adding Javascript or CSS to an HTML page, the exact placement of the <script>
and <style>
tags can be significant. There are two options for manipulating placement:
-
Region: In
Civi::resources()
, the default behavior is to add JS and CSS to thepage-footer
. This is suitable for changes which tweak the layout. Of course, if you are adding a programmatic service (like a jQuery plugin), then it should be loaded sooner (e.g. in thehtml-header
).The most common regions are:
page-header
page-body
page-footer
(default)html-header
(should always be used for jQuery plugins that refer to jQuery asjQuery
and notcj
)
For more information, see Region Reference.
-
Weight: Within a given region, tags are sorted by a numerical weight (ascending order). The default weight is '0'
When adding resources via PHP or Smarty, you can optionally pass the weight
and region
.
Set positioning (PHP; ordered parameters)
Civi::resources()->addScriptFile('com.example.foo', 'jquery.bar.js', 10, 'html-header');
Civi::resources()->addScriptUrl('http://example.com/bar.css', 10, 'page-header');
Set positioning (PHP; named parameters; Civi 5.31+)
Civi::resources()->addScriptFile('com.example.foo', 'jquery.bar.js', [
'weight' => 10,
'region' => 'html-header'
]);
Civi::resources()->addScriptUrl('http://example.com/bar.css', [
'weight' => 10,
'region' => 'page-header'
]);
Set positioning (Smarty; named parameters)
{crmScript ext="com.example.foo" file=bar.js weight=10 region=page-footer}
{crmStyle url="http://example.com/bar.css" weight=10 region=page-footer}
Modify or remove¶
What if CiviCRM is loading a resource -- and your system has a compatibility problem or interaction with that resource? It is also possible to modify and remove resources. This will depend on how the resource was added to the screen. See also:
- hook_civicrm_coreResourceList: Manipulate the site-wide list of CSS/JS files. Deprecated in v5.31.
- hook_civicrm_alterBundle: Manipulate any bundle (e.g. "coreResources", "coreStyles", "bootstrap3"). Added in v5.31. Supports CollectionInterface.
- Lookup and manipulate any region. In 5.31+, this supports CollectionInterface.
Resource model (v5.31+)¶
In the "Common tasks" above, we used methods like Civi::resources()->addScriptFile(...)
. This method provides a good developer
experience -- e.g. the IDE will auto-complete the method; the parameters are named and documented; and simple mistakes will be recognized
as PHP errors. The listed methods are also compatible with a wide range of versions.
But what are these methods doing?
This section focuses CiviCRM v5.31+. Why?
Many of the ideas and technical details here originate circa Civi v4.2. But there was also some "drift" or "impedance mismatch" where related APIs diverged slightly. In v5.31, many of these were (re)unified. For simplicity, this discussion focuses on the unified model in v5.31+. Some details will differ in previous versions.
Let's step back a second. As we can see in the Region Reference, every HTML page outputted by CiviCRM is composed of regions. Pages resemble this idealized example:
<html>
<head>
{crmRegion name="html-header"}...{/crmRegion}
</head>
<body>
<div id="page-header">
{crmRegion name="page-header"}...{/crmRegion}
</div>
<div id="page-body">
{crmRegion name="page-body"}...{/crmRegion}
</div>
<div id="page-footer">
{crmRegion name="page-footer"}...{/crmRegion}
</div>
</body>
Each {crmRegion}
contains a list of zero or more resources. (Historically, these may also be called snippets.) A resource
is represented as a key-value array. For example, the html-header
may include this list of resources:
[
[
'name' => 'civicrm:bower_components/jquery/dist/jquery.min.js',
'type' => 'scriptFile',
'region' => 'html-header',
'weight' => 0,
'scriptFile' => ['civicrm', 'bower_components/jquery/dist/jquery.min.js'],
'disabled' => false,
'translate' => false,
],
[
'name' => 'civicrm:bower_components/jquery/dist/jquery-ui.min.js',
'type' => 'scriptFile',
'region' => 'html-header',
'weight' => 1,
'scriptFile' => ['civicrm', 'bower_components/jquery/dist/jquery-ui.min.js'],
'disabled' => false,
'translate' => false,
],
[
'name' => 'civicrm:bower_components/jquery-ui/themes/smoothness/jquery-ui.min.css',
'type' => 'styleFile',
'region' => 'html-header',
'weight' => 5,
'styleFile' => ['civicrm', 'bower_components/jquery-ui/themes/smoothness/jquery-ui.min.css'],
'disabled' => false,
]
]
In our "Common tasks", we had an example statement:
Civi::resources()->addScriptFile('com.example.foo', 'jquery.bar.js', 10, 'html-header');
This appends a new resource to html-header
:
[
'name' => 'com.example.foo:jquery.bar.js',
'type' => 'scriptFile',
'region' => 'html-header',
'weight' => 10,
'scriptFile' => ['com.example.foo', 'jquery.bar.js'],
'disabled' => false,
'translate' => true,
]
Let's define the resource properties in more detail and examine some of the methods for managing them.
Types¶
Depending on its type, each resource will have exactly one of the following properties:
Property | Data Type | Description |
---|---|---|
markup |
string |
Literal HTML markup. |
template |
string |
The name of a Smarty template to execute, yielding HTML markup |
callback |
mixed |
A PHP callable which can add/filter the region's content |
script |
string |
Literal Javascript code |
scriptFile |
array |
Tuple which identifies the JS file ([$ext, $file] ) |
scriptUrl |
string |
Fully-formed URL of the script |
style |
string |
Literal CSS code |
styleFile |
array |
Tuple which identifies the CSS file ([$ext, $file] ) |
styleUrl |
string |
Fully-formed URL of the stylesheet |
Properties¶
Additionally, each resource will have some mix of these generic properties:
Property | Data Type | Description |
---|---|---|
type |
string |
One of the following: markup , template , callback , script , scriptFile , scriptUrl , jquery , settings , style , styleFile , styleUrl |
name |
string |
A unique symbolic name. If not specified, a default may be generated based on the kind of resource. |
weight |
int |
Determines ordering. Lower weights come before higher weights. (If two resources have the same weight, then a secondary ordering may kick-in to provide reproducibility. However, the secondary ordering is not guaranteed among versions/implementations.) |
disabled |
bool|int |
Disables/deactivates/hides the resource |
region |
string |
Name of the region where this resource is displayed |
translate |
bool|string |
(Only applies to 'scriptFile'.) Whether to autoload translated strings. May be FALSE or TRUE . If a string is given, it specifies the translation domain. |
scriptFileUrls |
string[] |
(Only applies to 'scriptFile'.) List of auto-generated URL(s) for the files |
styleFileUrls |
string[] |
(Only applies to 'styleFile'.) List of auto-generated URL(s) for the files |
Methods¶
If you review the documentation for Common tasks, Regions, and Bundles, you will find a number of similar methods.
For example, addScriptFile()
and addStyleFile()
are available on all three:
// Add resources directly to a physical part of the page.
CRM_Core_Region::instance('page-footer')
->addScriptFile('com.example.foo', 'foo.js')
->addStyleFile('com.example.foo', 'foo.css');
// Add resources to the page. By default, use a common region like page-footer or html-header.
Civi::resources()
->addScriptFile('com.example.foo', 'foo.js')
->addStyleFile('com.example.foo', 'foo.css');
// Add resources to a logical bundle.
$foo = new CRM_Core_Resources_Bundle('foo');
$foo->addScriptFile('com.example.foo', 'foo.js')
->addStyleFile('com.example.foo', 'foo.css');
Methods fall in two general buckets:
- Adding resources (
addScriptFile()
,addStyleFile()
, etc). These are part of theCollectionAdderInterface
. - Examining and modifying resources (
get()
,update()
,filter()
, etc). These are part of theCollectionInterface
.
CollectionAdderInterface¶
To add a resource, the canonical method is add(array $resource)
, e.g.
Civi::resources()->add([
'scriptFile' => ['com.example.foo', 'foo.js'],
'weight' => 10,
'region' => 'html-header',
]);
To improve documentation and auto-completion, the interface also specifies several thin wrappers:
public function addMarkup(string $markup, ...$options);
public function addPermissions($permNames);
public function addScript(string $code, ...$options);
public function addScriptFile(string $ext, string $file, ...$options);
public function addScriptUrl(string $url, ...$options);
public function addString($text, $domain = 'civicrm');
public function addStyle(string $code, ...$options);
public function addStyleFile(string $ext, string $file, ...$options);
public function addStyleUrl(string $url, ...$options);
public function addVars(string $nameSpace, array $vars, ...$options);
public function addSetting(array $settings, ...$options);
public function addSettingsFactory($callable);
The final ...$options
allow you to set extra properties, such as weight
and region
.
Options may be given in a key-value notation or backward-compatible, ordered notation:
// Add markup with ordered options (backward-compatible)
Civi::resources()->addMarkup('<p>Hello</p>', -5, 'page-header');
// Add markup with key-value options (5.31+)
Civi::resources()->addMarkup('<p>Hello</p>', [
'weight' => -5,
'region' => 'page-header',
]);
For further information, see the docblocks in CollectionAdderInterface and CollectionAdderTrait.
CollectionInterface¶
On some occasions, you may need to examine or modify the list of resources. These methods are available on regions and bundles.
// Adding resources
public function add($resource);
public function merge(iterable $otherResources);
// Reading resources
public function &get($name);
public function getAll(): iterable;
public function find($callback): iterable;
// Modifying resources
public function clear();
public function filter($callback);
public function update($name, $resource);
For further information, see the docblocks in CollectionInterface and CollectionTrait.