Skip to content

Bundle Reference

Bundles were introduced in CiviCRM 5.31.

A resource is an element that you can mix into the page -- such as a static Javascript file or a dynamic snippet of CSS code. A resource bundle is a collection of logically related resources. Using a bundle makes it easy to activate, deactivate, or replace resources as a group.

Example

Suppose we've got an improved calendar widget named mycalendar. The calendar widget requires several resources:

  • JS file (to define the rendering logic and interactive behaviors)
  • CSS file (to refine the styling and layout)
  • Settings data (to designate some date preferences)

Using the Resources API, we could add each indivudally:

Civi::resources()
  ->addScriptFile(E::LONG_NAME, 'js/mycalendar.js')
  ->addStyleFile(E::LONG_NAME, 'css/mycalendar.css')
  ->addVars('mycalendar', [
    'fmt' => Civi::settings()->get('myDateFormat'),
    'minYear' => date('Y', strtotime('-10 years')),
    'maxYear' => date('Y', strtotime('+10 years')),
  ]);

A calendar widget is interesting because it may be used on many different pages (e.g. dashboard, activity management, event management). One could duplicate the above calls (add them to every page with calendar), but then you'd have to keep them in sync. Alternatively, one could add the resources site-wide, but that would bloat other pages (especially if all components adopt the practice of adding themselves site-wide).

With a bundle, all of these resources are grouped together with a name (e.g. mycalendar). Any page that needs mycalendar will activate the whole bundle:

Civi::resources()->addBundle('mycalendar');

Register a bundle

The author of mycalendar needs to define the bundle. Let's look at the basic form and then the typical form.

Going by first principles, a bundle is basically an instance of CRM_Core_Resources_Bundle. The class has various methods (addScriptFile(), addScriptUrl(), etc) for registering resources. These are the same methods found in Civi::resources().

$b = new CRM_Core_Resources_Bundle('mycalendar', ['*default*']);
$b->addScriptFile(E::LONG_NAME, 'js/mycalendar.js')
  ->addStyleFile(E::LONG_NAME, 'css/mycalendar.css')
  ->addVars('mycalendar', [
    'fmt' => Civi::settings()->get('myDateFormat'),
    'minYear' => date('Y', strtotime('-10 years')),
    'maxYear' => date('Y', strtotime('+10 years')),
  ]);

To activate that bundle (loading all its scripts/styles/etc into the current page-view), one calls addBundle(...)

Civi::resources()->addBundle($b);

In practice, the typical formulation should be a little different. In particular:

  • We want a clean separation between the author/provider who specifies the bundle -- and the consumer who activates the bundle. This can be achieved by registering in the service container.
  • We want to allow hooks in case an extension or deployment needs to add/remove/filter/supplement elements of the bundle. The helpers in CRM_Core_Resources_Common facilitate hooks.

Consider this example:

use \Symfony\Component\Config\Resource\FileResource;
use \Symfony\Component\DependencyInjection\Definition;

function mymodule_civicrm_container($container) {
  $container->addResource(new FileResource(__FILE__));
  $container->setDefinition('bundle.mycalendar',
    new Definition('CRM_Core_Resources_Bundle', ['mycalendar'])
  )->setFactory('CRM_Core_Resources_Common::createBasicBundle')->setPublic(TRUE);
}

The above registers bundle.mycalendar. When a consumer requests the mycalendar bundle, it calls createBasicBundle(). That method creates the bundle object, sets some defaults, and fires a hook.

If you want to add some default resources to the bundle, then use an init function like _mymodule_init_mycalendar:

function mymodule_civicrm_container($container) {
  $container->addResource(new FileResource(__FILE__));
  $container->setDefinition('bundle.mycalendar',
    new Definition('CRM_Core_Resources_Bundle',  ['mycalendar', '_mymodule_init_mycalendar'])
  )->setFactory('CRM_Core_Resources_Common::createBasicBundle')->setPublic(TRUE);
}

function _mymodule_init_mycalendar($bundle) {
  $bundle
    ->addScriptFile(E::LONG_NAME, 'js/mycalendar.js')
    ->addStyleFile(E::LONG_NAME, 'css/mycalendar.css')
    ->addVars('mycalendar', [
      'fmt' => Civi::settings()->get('myDateFormat'),
      'minYear' => date('Y', strtotime('-10 years')),
      'maxYear' => date('Y', strtotime('+10 years')),
    ]);
}

With the bundle registered, anyone who needs mycalendar can activate it by name:

Civi::resources()->addBundle('mycalendar');

Alter a bundle

Any bundle defined via CRM_Core_Resources_Common can be altered with hook_civicrm_alterBundle.

For example, if anothermodule needs to supplement the styling rules in mycalendar, it might add:

function anothermodule_civicrm_alterBundle(CRM_Core_Resources_Bundle $bundle) {
  if ($bundle->name === 'mycalendar') {
    $bundle->addStyleFile(E::LONG_NAME, 'supplemental-calendar-styles.css');
  }
}

The file supplemental-calendar-styles.css will be loaded if (and only if) the mycalendar bundle is active.

Standard bundles

There are a few standard bundles defined in civicrm-core.

bootstrap3

  • Name: bootstrap3
  • CiviCRM Version: 5.31+
  • Functional description: Loaded only on pages that use Bootstrap v3 CSS classes
  • Default content: This is a placeholder; there is no default content. Themes should supply content. If no one provides content, this end-user will see a warning.

coreResources

  • Name: coreResources
  • CiviCRM Version: 5.31+
  • Functional description: Loaded as a baseline on all Civi-based pages.
  • Default content: jQuery, select2, and a number of Civi utilities.
  • Comment: This bundle is closely related to hook_civicrm_coreResourceList. hook_civicrm_coreResourceList was introduced earlier, and it defines array &$list with a list of script URLs and style URLs. hook_civicrm_alterBundle provides expanded functionality (including script URLs, style URLs, and more). For backward-compatibility, it uses this flow:

    • Initialize $list with common script+style URLs.
    • Fire hook_civicrm_coreResourceList(&$list, $region).
    • Create the coreResources bundle and copy in the $list.
    • Fire hook_civicrm_alterBundle(Bundle $bundle).

    In the near term, this should give good interoperability with existing consumers of hook_civicrm_coreResourceList. In the longer term, the former cannot access as much data and may not work as well in the future.

coreStyles

  • Name: coreStyles
  • CiviCRM Version: 5.31+
  • Functional description: Loaded as a baseline on all Civi-based pages and some CMS-based pages. (Ex: Drupal "View User" may embed Civi profiles.)
  • Default content: civicrm.css and/or the customCSSURL