Resource Registry¶
Description
Plone 5 has modernized the JavaScript and CSS development experience by incorporating tools like Bower, Grunt, RequireJS and Less. JavaScript and CSS resources for core Plone and add-on packages are managed in the new Plone 5 Resource Registry.
The Resource Registry was completely rewritten in Plone 5 to support a new dependency-based approach built on RequireJS. It also allows developers to create JavaScript and CSS bundles Through-The-Web for a low barrier to entry.
This chapter will help you to gain a basic understanding of the new Resource Registry.
Introduction To Plone 5 Resources¶
Plone 5 introduces new concepts for working with JavaScript and CSS in Plone.
JavaScript And AMD¶
Prior to Plone 5, JavaScript resources in Plone have been registered with the portal_javascripts
tool using the Generic Setup import step jsregistry.xml
.
This approach allowed a developer to specify the order in which individual JavaScript resources were loaded into an HTML page, and controlled compiling and minifying these resources for production into a minimal set of files.
As the use of JavaScript in front-end development expanded, this model proved insufficient to solve complex dependency management issues. At the same time, developments in the JavaScript community led to a new approach to handling complex dependencies.
Starting in Plone 5, we use a new module-based solution on the Asynchronous Module Definition (AMD) approach.
The new Plone 5 Resource Registry uses RequireJS to allow developers to define individual JavaScript modules and the dependencies they will require.
Because RequireJS uses the AMD approach, these modules and their dependencies can be served in an uncompiled form during development and then compiled and minified Through-The-Web for use in production. This allows a “development mode” where changes in JavaScript source files are reflected in the browser in near real-time.
It is also possible in the new Resource Registry to provide bundles with simple JavaScript that does not make use of AMD. See Non-AMD Bundles below for an example of such a bundle.
CSS And Less¶
Modern web development relies on CSS (Cascading Style Sheets). To support complex CSS, “preprocessors” have developed that allow a more programmatic way of defining styles and sharing common elements.
Plone 5 has chosen to use the Less CSS preprocessor because it is compiled by JavaScript tools, which fit with the new Plone Resource Registry.
Less provides nice features like inheritance, scoping, functions, mixins and variables, which are not available in pure CSS.
Resources And Bundles¶
In the Plone 5 Resource Registry, JavaScript and CSS are organized into resources and bundles. A resource consists of one JavaScript and either none, one or multiple Less or CSS files. A bundle combines several resources into a single unit that is used in a webpage.
In “development mode” the resources for a bundle may be served individually and unminified to facilitate development. In “production mode” a bundle’s resources are compiled and minified to minimize the number of requests needed to deliver the bundle to a client.
In the sections below we will discuss resources and bundles in depth.
Resources¶
Resources are the main unit of the resource registry. A resource may contain at most one JavaScript file and zero or more CSS/Less files. RequireJS identifies resources by name.
Resources - as well as bundles - are registered with a records
element in the registry.xml
Generic Setup import step.
The Plone 5 Resource Registry reads these records
and builds RequireJS configuration for compiling resources into bundles.
A resource record element must have two attributes: “prefix” and “interface”.
The value of “interface” must be Products.CMFPlone.interfaces.IResourceRegistry
.
The value of “prefix” must begin with plone.resources/
followed by a unique value that will be used by RequireJS as the name of the resource.
To ensure that your bundle has a unique name, we suggest that you use the name of your package.
You should convert dots to dashes to conform to RequireJS naming standards. See below for some examples of different types of resource records.
JavaScript resources registered should conform to the RequireJS module pattern.
We can also include non-module, legacy resources which do not make use of the RequireJS define
and require
methods.
If you must register such resources, use the shim options defined below.
Resource Options¶
Options are defined on a resource record using value elements in the form <value name="option_name">option_value</value>
.
The options that may be used on any resource record are:
- js
URL of the JavaScript file.
- css
URLs of CSS/Less elements.
- url
Base URL for loading additional resources like text files. See below for an example.
For these options, the URL you provide as a value must point to a file in a resource folder.
Optionally, you may choose to register a directory in your package using the ++plone++static traversal namespace.
Shim Resources¶
If the JavaScript you wish to register does not follow the RequireJS module pattern (using define
and require
), you may still register it in a resource.
You will need to use the shim
options for your resource record.
We refer to this kind of JavaScript as “legacy”, as it doesn’t follow our proposed best practices.
For more information on configuring shims in RequireJS, see: http://requirejs.org/docs/api.html#config-shim
- export
Shim export option to define a global variable where the JavaScript module should be made available.
- deps
Shim depends option to define which other RequireJS resources should be loaded before this shim module.
- init
Shim init option to define some JavaScript code to be run on initialization.
Example Resource Records¶
Here are some examples of different types of resource records (all examples below are from Products.CMFPlone
).
An example of a resource record for a single JavaScript module:
<records prefix="plone.resources/mockup-router"
interface='Products.CMFPlone.interfaces.IResourceRegistry'>
<value key="js">++resource++mockupjs/router.js</value>
</records>
An example of a resource record for a single Less file:
<records prefix="plone.resources/bootstrap-variables"
interface='Products.CMFPlone.interfaces.IResourceRegistry'>
<value key="css">
<element>++plone++static/components/bootstrap/less/variables.less</element>
</value>
</records>
An example of a resource for multiple Less files:
<records prefix="plone.resources/bootstrap-basic"
interface='Products.CMFPlone.interfaces.IResourceRegistry'>
<value key="css">
<element>++plone++static/components/bootstrap/less/utilities.less</element>
<element>++plone++static/components/bootstrap/less/forms.less</element>
<element>++plone++static/components/bootstrap/less/navs.less</element>
<element>++plone++static/components/bootstrap/less/navbar.less</element>
</value>
</records>
An example of a resource combining JavaScript and Less/CSS:
<records prefix="plone.resources/picker.date"
interface='Products.CMFPlone.interfaces.IResourceRegistry'>
<value key="js">++plone++static/components/pickadate/lib/picker.date.js</value>
<value key="css">
<element>++plone++static/components/pickadate/lib/themes/classic.date.css</element>
</value>
<value key="deps">picker</value>
</records>
Note
- Please note that because a resource may contain at most one JavaScript file, the url for that file is placed directly into the
<value key="js" />
option. However, as a resource may contain any number of CSS/Less files, each url must be added to the
<value key="css" />
in an<element />
tag.
The URL Resource Option¶
The URL option allows you to define the base URL for loading other resources needed by your JavaScript.
In the following example from the mockup
package, the url
option is used to register a URL base from which an XML template may be loaded.
The name of the resource is set as mockup-patterns-structure
.
In the resource is register in registry.xml
(from Products.CMFPlone
):
<records prefix="plone.resources/mockup-patterns-structure"
interface='Products.CMFPlone.interfaces.IResourceRegistry'>
<value key="js">++resource++mockup/structure/pattern.js</value>
<value key="url">++resource++mockup/structure</value>
<value key="css">
<element>++resource++mockup/structure/less/pattern.structure.less</element>
</value>
</records>
Then in mockup/configure.zcml
we register a resource directory called mockup
.
The resource traversal namespace ++resource++mockup
points to the filesystem directory mockup/patterns
.
<browser:resourceDirectory
name="mockup"
directory="./patterns" />
Finally, in mockup/patterns/structure/js/views/actionmenu.js
, we can list a text dependency.
The URL base for the dependency is listed as mockup-patterns-structure-url
.
The path that follows will be resolved from the registered resource directory set in the URL option for this resource record: mockup/patterns/structure
.
define([
'jquery',
'underscore',
'backbone',
'mockup-ui-url/views/base',
'mockup-utils',
'text!mockup-patterns-structure-url/templates/actionmenu.xml',
'bootstrap-dropdown'
], function($, _, Backbone, BaseView, utils, ActionMenuTemplate) {
'use strict';
var ActionMenu = BaseView.extend({
className: 'btn-group actionmenu',
template: _.template(ActionMenuTemplate),
// ...
});
return ActionMenu;
});
Shim Resource Examples¶
Here is an example of a resource record using shim options (from Products.CMFPlone.profiles.dependencies
).
Here, the variable tinyMCE
is exported as an attribute of window
, the global JavaScript namespace.
The init
option is used to define a simple function that will be executed when the tinymce.js
JavaScript file has been loaded.
TODO: Verify that the above description is true.
<records prefix="plone.resources/tinymce"
interface='Products.CMFPlone.interfaces.IResourceRegistry'>
<value key="js">++plone++static/components/tinymce/tinymce.js</value>
<value key="export">window.tinyMCE</value>
<value key="init">function() { this.tinyMCE.DOM.events.domLoaded = true; return this.tinyMCE; }</value>
<value key="css">
<element>++plone++static/components/tinymce/skins/lightgray/skin.min.css</element>
<element>++plone++static/components/tinymce/skins/lightgray/content.inline.min.css</element>
</value>
</records>
In this example, we configure the shim for the backbone
resource.
This resource exports the backbone javascript library as the Backbone
attribute of window
, the global JavaScript namespace.
The deps
option is used to list two resources required by backbone: underscore
and jquery
.
Note that the format for deps
is a comma-separated list of resource names.
All resources named in deps
must also be registered with the Plone 5 Resource Registry.
<records prefix="plone.resources/backbone"
interface='Products.CMFPlone.interfaces.IResourceRegistry'>
<value key="js">++plone++static/components/backbone/backbone.js</value>
<value key="export">window.Backbone</value>
<value key="deps">underscore,jquery</value>
</records>
Default Resources In Plone¶
Plone 5 ships with a list of Mockup and Bower components for Plone 5’s new UI.
These resources can be found in the static folder (Products.CMFPlone.static
), where you can also find the bower.json file.
These resources are preconfigured in the registry (registry.xml in Products.CMFPlone.profiles.dependencies
).
The ++plone++ Traversal Namespace¶
We have a new plone.resource
based traversal namespace called ++plone++
.
Plone registers the Products.CMFPlone.static
folder for this traversal namespace.
Resource contained in this namespace can be stored in the ZODB (where they are looked up first, by default) or in the filesystem.
This allows us to customize filesystem based resources Through-The-Web.
One advantage of this new namespace over the existing ++resource++
and ++theme++
namespaces is that you may override resources in this namespace one file at a time, rather than needing to override the entire directory.
You may configure a folder in your add-on package to be included in this namespace. To configure a directory in your package, add the following ZCML:
<plone:static
directory="static"
type="plone"
name="myresources"
/>
Now we can access the contents of the “static” folder in your package by using a URL that starts with ++plone++myresources/
.
Additional path segments in your URL will be resolved within the “static” folder in your package.
For example, ++plone++myresources/js/my-package.js
will correspond to static/js/my-package.js
within your package.
Note
When providing static resources (JavaScript/Less/CSS) for Plone 5’s resource registry, use plone.resource
based resources instead of Zope’s browser resources.
The latter are cached heavily and you won’t get your changes compiled into bundles, even after Zope restarts.
Bundles¶
A bundle combines multiple resources into a single unit, identified by a name. Bundles can be used to group resources for different purposes.
For example, the “plone” bundle provides resources that could be of use to any client, but the “plone-logged-in” bundle supplies resources needed only for those who are logged in to the Plone site.
Generally speaking, when a Plone page is delivered to a client, only bundles will be loaded. There are exceptions, you can register individual resources to be loaded for a specific request via an API method.
We will discuss this a bit later.
Like resources, bundles are registered with a records
element in the registry.xml
Generic Setup import step.
A bundle record element must have two attributes: “prefix” and “interface”.
The value of “interface” must be Products.CMFPlone.interfaces.IBundleRegistry
.
The value of “prefix” must begin with plone.bundles/
followed by a unique value that will be used as the name of the bundle.
See below for some examples of different types of resource records.
When developing an add-on you will create your own bundle. Your bundle should include all resources required for your JavaScript or CSS/Less to work properly.
If your bundle will be used only on a single page, you can define it to include it only there.
You can use the “expression” option to control including an enabled bundle.
You can also use API methods from Products.CMFPlone.resources
to add disabled bundles to a single request.
For example, the resourceregistry
bundle is only used for the @@resourceregistry-controlpanel
view.
(see the section Controlling Resource And Bundle Rendering for more information)
Note
A bundle can depend on other bundles. Declaring such a dependency only controls the order in which bundles are loaded and is mostly relevant for legacy bundles. Currently, bundle dependencies don’t make use of RequireJS dependencies or AMD. Each bundle will be compiled with all dependencies, even if a dependency was already used for another bundle. This raises the response payload unnecessarily.
To avoid this, use the stub_js_modules
option for your bundle record listed in Bundle Options below.
Development VS. Production Mode¶
In development mode, each bundle loads all of its resources individually. This allows modifications to resources to be immediately available.
You do not need to compile any bundles beforehand.
You should be aware that this feature does lead to a lot of requests and slow response times, even though RequireJS loads dependencies asynchronously.
In production environments you will compile your bundles to combine and minify all the necessary resources into a single JavaScript and CSS file. Since the dependencies of each resource in the bundle are all now well-defined, they can all be included in these files. Compiling bundles minimizes the number of web requests and the payload of data sent over the network. In Production mode, only one or two files are included in the output for each active bundle: a JavaScript and a CSS file.
Bundle Options¶
Options are defined on a bundle record using value elements in the form <value name="option_name">option_value</value>
.
The possible options for a bundle are:
- enabled
Enable or disable the bundle.
- depends
List other bundles as dependencies of this bundle. Currently used for the order of inclusion in the rendered content. The defined bundle will only be included in a page after any bundles listed.
- resources
List the resources that are included in this bundle.
- compile
Set the value to
True
orFalse
. Your bundle must be compiled if it has any Less or RequireJS resources. If you wish, you may precompile your bundles using command line tools provided by Plone or your own preferred toolchain. For more information, see below.If this value is
False
, no button will be provided to compile this bundle Through-The-Web (eg. used for theplone-legacy
bundle).- expression
A TALES expression. If the expression evaluates as
True
, the bundle will be included.- merge_with
Indicate in which of the bundle aggregations this bundle should be included. The valid values for this option are
default
orlogged-in
. (see below).- conditionalcomment
Provide a conditional comment for Internet Explorer hacks.
- stub_js_modules
Provide a list of resources that are required by this bundle, but already provided by another active bundle. This prevents the stub module from being included multiple times and can reduce the download size of bundles.
New in version 5.0.1.
The following options are used when you provide a pre-compiled bundle.
The values will be automatically set when the bundle is built Through-The-Web.
If you use the plone-compile-resources
script, or your own custom toolchain to compile your own bundle JS or CSS, you will need to manage these values yourself.
- jscompilation
URL of the compiled and minified JavaScript file.
- csscompilation
URL of the compiled and minified CSS file.
- last_compilation
Date of the last compilation time. The value of this option is automatically used as version parameter for cache-busting in production mode. (eg.
plone-logged-in-compiled.min.js?version=2015-05-07%2000:00:00.000003
)
Compiling Bundles¶
There are three ways to provide a compiled version of a bundle for production:
Compile the bundle Through-The-Web and store it in the ZODB
When using this option, all an add-on developer or an integrator needs to do is register a bundle with the “compile” option set to True
.
In the Plone 5 Resource Registry control panel, a button will be available to compile the bundle.
Pressing this button will compile the bundle and store it for production delivery.
Compile the bundle from the command line:
Plone provides a script which will compile a specific bundle available in the resource registry.
To use this option, you must specifically request the script in your buildout.
Add a new part called “resources” and list it in your buildout “parts”, then re-run buildout.
You will find the plone-compile-resources
script in your buildout bin
directory.
[resources]
recipe = zc.recipe.egg
eggs =
plone.staticresources
scripts = plone-compile-resources
Once the script has been created you may invoke it. You will need to provide options indicating the ID of the Plone site in which your package is installed, and the name of the bundle you wish to compile:
./bin/plone-compile-resources --site-id=myplonesite --bundle=mybundle
This script will start up your Plone site, extract the required information and compile the bundle. Because of this, you will need to stop a Plone instance before running this script.
Use your own compilation chain
The Plone 5 Resource Registry can be used with your favorite build system. Use the tool you prefer create a compiled version of your bundle. Your bundle registration must provide a URL for the “jscompilation” and “csscompilation” options. Your compiled files must be in the filesystem locations that are indicated by these values.
Default Plone Bundles¶
There are three main bundles defined by Plone:
- plone:
This is the main compiled bundle with all the JavaScript and CSS components required for the Plone Toolbar and the main Mockup patterns.
- plone-logged-in:
This bundle is only included for logged in users. It contains patterns like the “tinymce” pattern, the “querystring” pattern for collection edit forms and others.
- plone-legacy:
This bundle is not compiled and contains code that doesn’t use RequireJS or Less. Addons which continue to install resources to
portal_javascripts
orportal_css
are registered as resources in the plone-legacy bundle automatically.
The Legacy Bundle¶
The legacy bundle exists to support packages with code that does not work with the new Plone 5 Resource Registry. Code that cannot be migrated to use RequireJS can be included in the legacy bundle.
Code that uses RequireJS in a way which is incompatible with Plone’s use of it (e.g. it’s using its own RequireJS setup) can be included in the legacy bundle.
Note
Some JavaScript use its own setup of RequireJS.
Others - like Leaflet 0.7 or DataTables 1.10 - try to register themselves for RequireJS.
This can lead to the infamous “mismatched anonymous define” errors (see below).
You can register such scripts in the plone-legacy
bundle by including them in the jsregistry.xml
import step.
The define
and require
methods are unset before these scripts are included in the output and reset again after all scripts have been included.
See yourself: https://github.com/plone/Products.CMFPlone/pull/870/files
Resources which are registered into portal_javascripts
or portal_css
registries via an addon are automatically registered in the legacy bundle and cleared from portal_javascripts
and portal_css
.
Note
JavaScript which doesn’t use RequireJS can still be managed by it by including it as a resource with configured shim options.
The plone-legacy bundle treats resources differently: they are not compiled, but simply concatenated and minified.
Example Bundle Records¶
Here are some examples of Bundle records from Plone and popular add-ons
The record for Plone’s plone
bundle names a single resource, plone
.
This is a good example of using a single resource with a require
call to bundle a number of other resources, many of which use define
, in order to avoid The mismatched anonymous define error.
(see Products/CMFPlone/profiles/dependencies/registry.xml
and Products/CMFPlone/static/plone.js
, and for an example of the bundled resources mockup/patterns/autotoc/pattern.js
)
<records prefix="plone.bundles/plone"
interface='Products.CMFPlone.interfaces.IBundleRegistry'>
<value key="resources">
<element>plone</element>
</value>
<value key="enabled">True</value>
<value key="jscompilation">++plone++static/plone-compiled.js</value>
<value key="csscompilation">++plone++static/plone-compiled.css</value>
<value key="last_compilation">2014-08-14 00:00:00</value>
</records>
The record for the plone-legacy
bundle names the only javascript resource left in Plone that does not work with the Resource registry.
Note that any JavaScript or CSS registered with the old portal_javascripts
or portal_css
tools will be included automatically in this bundle.
Note too that the plone-legacy
bundle declares a dependency on the plone
bundle, which ensures only that the plone
bundle will be loaded into the page before this one.
<records prefix="plone.bundles/plone-legacy"
interface='Products.CMFPlone.interfaces.IBundleRegistry'>
<value key="resources" purge="false">
<element>jquery-highlightsearchterms</element>
</value>
<value key="depends">plone</value>
<value key="jscompilation">++plone++static/plone-legacy-compiled.js</value>
<value key="csscompilation">++plone++static/plone-legacy-compiled.css</value>
<value key="last_compilation">2014-08-14 00:00:00</value>
<value key="compile">False</value>
<value key="enabled">True</value>
</records>
A bundle is registered in the Plone add-on package Plomino.
Here, a number of resources are aggregated and compiled via the plone-compile-resources
script.
They may also be compiled Through-The-Web, using the Resource Registry.
Notice that in contrast to the plone
bundle, the resources combined here all use require
at the top level to avoid The mismatched anonymous define error.
(see Products/CMFPlomino/profiles/default/registry.xml
and for an example of the resources included Products/CMFPlomino/browser/static/js/table.js
)
<records prefix="plone.bundles/plomino"
interface='Products.CMFPlone.interfaces.IBundleRegistry'>
<value key="resources">
<element>plominoformula</element>
<element>plominotable</element>
<element>plominodesign</element>
<element>plominodynamic</element>
</value>
<value key="enabled">True</value>
<value key="depends">plone</value>
<value key="jscompilation">++resource++Products.CMFPlomino/js/plomino-compiled.js</value>
<value key="csscompilation">++resource++Products.CMFPlomino/css/plomino-compiled.css</value>
<value key="last_compilation">2015-12-08 00:00:00</value>
</records>
In Rapido, another Plone add-on, the JavaScript registered for the bundle is manually compiled.
By listing the plone
default bundle as a dependency, this JavaScript is able to rely on Plone default resources such as jQuery
, mockup
and the patterns registry being present.
(see rapido/plone/profiles/default/registry.xml
and rapido/plone/browser/rapido.js
)
<records prefix="plone.bundles/rapido"
interface='Products.CMFPlone.interfaces.IBundleRegistry'>
<value key="enabled">True</value>
<value key="jscompilation">++resource++rapido.js</value>
<value key="csscompilation"></value>
<value key="last_compilation">2019-11-26 00:00:00</value>
<value key="compile">False</value>
<value key="depends">plone</value>
</records>
Non-AMD Bundles¶
Sometimes it may be useful to register a simple javascript without using the AMD pattern.
An example of such a bundle is provided in the example.p4p5 package.
In this case, there is a simple JavaScript which appends a status div to a chart (example/p4p5/browser/static/chart.js
):
$(document).ready(function() {
var chart = $('#chart');
var done = parseInt(chart.attr('done'));
var inprogress = parseInt(chart.attr('inprogress'));
var total = done + inprogress;
if(total == 0) {
total = 1;
}
var done_rate = Math.round(100 * done / total);
var inprogress_rate = Math.round(100 * inprogress / total);
chart.append('<div class="done" style="width:'+done_rate+'%"> </div>');
chart.append('<div class="inprogress" style="width:'+inprogress_rate+'%"> </div>');
});
In this case, the JavaScript is dependent only on a global $ which is expected to be bound to jQuery.
Plone provides this in the plone
bundle, so that is the only dependency we need to specify.
For such a case, the package can register this JavaScript in jsregistry.xml
for Plone versions before 5.0.
And in Plone 5, the following bundle record added in registry.xml
will do the trick (example/p4p5/profiles/plone5/registry.xml
):
<records prefix="plone.bundles/examplep4p5"
interface='Products.CMFPlone.interfaces.IBundleRegistry'>
<value key="enabled">True</value>
<value key="jscompilation">++resource++example.p4p5/chart.js</value>
<value key="csscompilation">++resource++example.p4p5/chart.css</value>
<value key="last_compilation">2016-01-01 00:00:00</value>
<value key="compile">False</value>
<value key="depends">plone</value>
</records>
Notice that this bundle provides no resources.
The JavaScript file from the package is provided as the value of the jscompilation
option.
The CSS file is likewise provided as a pre-compiled value.
Finally the value of the compile
option is set to False
.
This ensures that the Resource Registry will make no attempt to re-compile this bundle.
Controlling Resource And Bundle Rendering¶
To control whether a bundle is included in a rendered page, we have already discussed several options.
You may globally enable or disable a bundle using the enabled
option of the bundle record.
You may conditionally render the bundle using the expression
option of the bundle record.
A Diazo Theme may also include or exclude specific bundles, regardless of whether they are enabled or disabled in the Resource Registry.
To do so, use the enabled-bundles
or disabled-bundles
settings in the manifest.cfg
file for the theme.
These settings take a comma separated list of the names of bundles.
A browser page can include or exclude a specific bundle by using the API methods from Products.CMFPlone.resources
.
This will override the value of enabled
in the Resource Registry for the named bundle.
Here are the API methods (from Products.CMFPlone.resources
):
add_bundle_on_request(request, bundle)
:The value provided for the
bundle
parameter must be the name of a bundle. The named bundle will be added to the provided request.remove_bundle_on_request(request, bundle)
:The value provided for the
bundle
parameter must be the name of a bundle. The named bundle will be removed from the provided request if it is present.
A browser page may also force the rendering of an individual resource on a particular request. Thus specific resources may be included regardless of whether they are included in a rendered bundle.
Here is the API method to do so (from Products.CMFPlone.resources
):
add_resource_on_request(request, resource)
:The value provided for
resource
must be the name of a resource. The named resource will be added to the current request.
Aggregate Bundles For Production¶
Plone defines several bundles. Add-ons that you include in your Plone site may also define bundles of their own. In production, each of these bundles will result in the loading of one JavaScript and one CSS file. To reduce the number of loaded files to an absolute minimum, we use “bundle aggregation”.
There are two bundle aggregations available in Plone.
A first aggregation named default
contains all the bundles that must be available at all times.
It creates 2 output files (one JavaScript and one CSS).
A second aggregation named logged-in
contains bundles only needed for authenticated users.
It also creates 2 output files (one JavaScript and one CSS).
Aggregation of bundles is triggered by the registry.xml
Generic Setup import step.
Installing any profile containing a registry.xml
file will automatically refresh the current aggregations.
Any bundles declared in that file will be included, if they declare that they should be merged with one of the two available aggregations.
As bundles can be defined or modified Through-The-Web, Plone also provides a “Merge bundles for production” button in the Resource Registry. This allows us to re-generate the aggregations manually after any Through-The-Web modifications have been made.
Declare An Aggregation¶
Custom bundles from an add-on or from a theme may be aggregated with the standard Plone bundles.
To do so, use the merge_with
option in your bundle declaration in registry.xml
.
The valid values are default
or logged-in
.
If the merge_with
option is not present or is empty, the bundle will not be aggregated and is published separately.
<records prefix="plone.bundles/my-bundle"
interface='Products.CMFPlone.interfaces.IBundleRegistry'>
<value key="merge_with">logged-in</value>
...
</record>
Note
Bundles cannot be conditionally included in an aggregation. If the value of the merge_with option is default or logged-in, the value of the expression option will be ignored.
Note
In Development mode, aggregation is disabled, all bundles are published separately.
Diazo Bundles¶
The point with Diazo is to create standalone static themes which work without Plone. Diazo themes can use - and will use - their own resources and compiling systems.
Diazo was extended to support bundles.
Bundles can be defined in the theme’s manifest.cfg
file.
Bundles configured in the manifest.cfg
file are included in the output by the renderer additionally to the ones registered in the resource registry.
This allows us to just overwrite or drop the link
and script
tags from the theme but still include the theme-specific resources without having to register them in the resource registry.
The options are:
- enabled-bundles / disabled-bundles:
A comma-separated list of Resource Registry bundles that should be included or excluded when rendering the Diazo theme. See Controlling Resource And Bundle Rendering.
- development-css / development-js:
Uncompiled/unminified Less/CSS file and RequireJS files which should be included in development environments. Any required compilation will be handled by the browser on the fly.
- production-css / production-js:
Compiled CSS or JavaScript files that will be included in production mode.
- tinymce-content-css:
A CSS file to include for the TinyMCE editor. This allows theme developers to ensure that TinyMCE gives you the best possible WYSIWYG experience.
Note
Files referenced by production-css
and production-js
must be present in the theme and pre-compiled.
Less and RequireJS files named in Diazo Bundles cannot be compiled by the Resource Registry Through-The-Web.
Nor can they be compiled by the plone-compile-resources
script.
For Diazo Bundles, the theme must provide its own compilation toolchain.
Example manifest.cfg
¶
This example is from plonetheme.barceloneta
, the default theme in Plone 5 (plonetheme/barceloneta/theme/manifest.cfg
).
Here, a Less file for development, a compiled CSS file for production and a second compiled CSS file meant specifically for use with TinyMCE are all named.
The package itself provides a Gruntfile.js
and package.json
file for compiling Less to CSS.
[theme]
title = Barceloneta Theme
description = The default theme for Plone 5
preview = preview.png
rules = /++theme++barceloneta/rules.xml
prefix = /++theme++barceloneta
doctype = <!DOCTYPE html>
enabled-bundles =
disabled-bundles =
development-css = /++theme++barceloneta/less/barceloneta.plone.less
production-css = /++theme++barceloneta/less/barceloneta-compiled.css
tinymce-content-css = /++theme++barceloneta/less/barceloneta-compiled.css
development-js =
production-js =
Migrating Older Add-ons¶
Many add-ons in the Plone ecosystem include JavaScript and CSS resources. To take advantage of the dependency management capabilities of the new Resource Registry, they will need to be migrated.
Compatibility With Deprecated Registries¶
The portal_css
and portal_javascript
registries have been deprecated in Plone 5.
Older Add-ons register CSS and JavaScript resource with these registries using the cssregistry.xml
and jsregistry.xml
Generic Setup import steps.
Plone 5 will still recognize these import steps, and resources registered with them will be added to the plone-legacy bundle.
Thus, older add-ons with JavaScript and CSS have a reasonable chance of working without migrating…yet.
However, scripts included in this fashion receive none of the dependency management benefits of the new Resource Registry.
The plone-legacy
bundle includes a global jQuery object and then includes bundled resources in order.
The define
and require
APIs from RequireJS are unset before the plone-legacy
bundle is included, and then re-defined after.
Updating non-AMD Scripts¶
To take advantage of the dependency management of the new Resource Registry, you should upgrade your existing JavaScript files to use the AMD pattern. To do so, wrap existing JavaScript using this recipe:
require([
'jquery',
'other-library'
], function($, otherLibrary) {
'use strict';
...
// All the previous JavaScript file code here
...
});
(For a description of the require(Array, Function)
used here, See the AMD API documentation)
Dependencies required by your JavaScript code must be listed in the Array
argument to the require
API.
You must use the RequireJS name identifiers of your dependencies here.
These will be the names of the Plone Resources which provide those JavaScript modules.
Listed dependencies are be passed to the Function
argument as parameters.
They will be available to the code inside this function.
Register your modeified files as resources in registry.xml
.
Finally, register a bundle in registry.xml
which includes any of your resources.
Note
When using require
instead of define
, the anonymous function is immediately called.
If you would use define
instead, you’d have to make a require
call somewhere, with the dependency to your resource.
This recipe should work for many JavaScript files. Other patterns for module definition may be found in the AMD API definitions or the RequireJS API documentation.
The mismatched anonymous define
error¶
If you have worked with RequireJS before, you are likely to be aware of the mismatched anonymous define() error.
It arises from misuse of the require
and define
APIs.
To work in RequireJS, code that uses a call to define
must be loaded into a page only through a call to require
.
You may not load such code using a <script>
tag.
When applied to the concept of resources and bundles this means that bundles should only ever be require
calls.
If you try to use a JavaScript file that has a define
call with a bundle, you’ll likely cause the mismatched anonymous define()
error.
Make sure to use a JavaScript file with a require
call to include all your define
resources.
This is a fact of how RequireJS works. It is normal behavior. Keeping it in mind can save you headaches.
Including non-RequireJS Scripts In A Diazo Theme¶
We have already described how to add resources to the legacy bundle.
We have also discussed that the legacy bundle unsets the define
and require
statements before loading its resources so as to avoid the mismatched anonymous define() error and other possible problems.
If you have scripts in your Diazo theme that you don’t want to register with the resource registry and which are not compatible with RequireJS, you can take a similar approach.
Add these scripts below the Plone scripts and unset define
and require
yourself.
Here is an example Diazo rule which does so:
<before theme="/html/head/script[1]"> <!-- ... before your own scripts -->
<xsl:apply-templates select="/html/head/script" /> <!-- include the Plone scripts -->
<script> <!-- and then unset require and define -->
require = undefined
define = undefined
</script>
</before>