Skip to content

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 name civicrm.
  • 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 the page-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 the html-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 as jQuery and not cj)

    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:

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 the CollectionAdderInterface.
  • Examining and modifying resources (get(), update(), filter(), etc). These are part of the CollectionInterface.

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.