Composr Tutorial: Aggregate Content Types

Written by Chris Graham (ocProducts)
Aggregate content types are complex content structures that you can replicate them out as you require. It is very useful for the quick creation of sophisticated template-based sub-sites, for example. Aggregate content types are an advanced feature, and require an understanding of XML.

Aggregate content types are built on top of the Composr repository.

This is advanced functionality designed to be set up by experienced developers.


The admin process

Image

Editing the aggregate type XML

Editing the aggregate type XML

(Click to enlarge)

The administrator, looking to make use of aggregate content types, would follow the following two-step process:
  1. Define aggregate content types in XML, from Admin Zone > Structure > Aggregate content types > Edit XML definition.
  2. Add instances of aggregate content types, from Admin Zone > Structure > Aggregate content types > Add aggregate content type instance.

The key feature of aggregate content types of course is that step 2 may be repeated many times, to build out complex structures with great efficiency.

You can delete an aggregate content type from the bottom of its edit form.

XML definition

Aggregate content types are defined in XML in the data/xml_config/aggregate_types.xml (overridden as data_custom/xml_config/aggregate_types.xml).

Here is an example of the definition of two aggregate content types: Course, and Country…

Code

<aggregateTypes>
    <aggregateType name="Course">
        <resource type="group" label="{LABEL*}" template_label="Newbie" />
        <resource type="catalogue_category" subpath="courses" label="{LABEL*}" template_label="_course">
            <property key="description">{DESCRIPTION*}</property>
            <access usergroup="{LABEL*}" value="1" />
            <privilege usergroup="{LABEL*}" preset="submit" />
            <privilege usergroup="{LABEL*}" name="submit_midrange_content" value="1" />
        </resource>
    </aggregateType>

    <aggregateType name="Country">
        <resource type="zone" label="{LABEL|*}" template_label="_country"> <!-- NB: the 'label' in this case is the zone codename -->
            <property key="human_title">{LABEL*}</property>
            <access usergroup="*" value="1" />
        </resource>
        <resource type="comcode_page" subpath="{LABEL|*}" label="start">
            <property resync="false" key="text">
                All about {LABEL*}.
            </property>
            <access usergroup="*" value="1" />
        </resource>
        <resource type="comcode_page" subpath="{LABEL|*}" label="panel_left">
            <property key="text">
                [block="{LABEL|*}"]menu[/block]
            </property>
            <access usergroup="*" value="1" />
        </resource>
        <resource type="menu_item" subpath="{LABEL|*}" label="Home">
            <property key="page_link">{LABEL|*}:start</property>
        </resource>
    </aggregateType>
</aggregateTypes>

How the XML is applied when adding an instance

Image

Adding an instance

Adding an instance

(Click to enlarge)

Image

Choosing an aggregate type to add an instance to

Choosing an aggregate type to add an instance to

(Click to enlarge)

For each resource rule (resource element) defined within the aggregate type we are creating an instance of…

A match is first found against the label given in the subpath attribute of the resource element. If none is found, a new content item will be created under that subpath with the given label, defaulting resource options from the resource identified by the template attribute (if a template attribute is supplied).

A label is not a repository filename, but rather it is generally what you would expect as human readable. What this actually relates to within Composr varies from content-type-to-content-type, but in most cases it is a Title field or a Name field.

For example, with:

Code

<resource type="usergroup" label="{LABEL*}" template_label="Newbie" />
We would look for a usergroup with a name that matches {LABEL*}, which is the label that the administrator would have provided for the new instance.

If there was a matching usergroup, we don't need to do anything in this case. If there was not, we would create it, by copying the usergroup named Newbie.

Resource rules may contain property rules, access rules, and privilege rules. Property rules set a property for the resource. You may set any property which the Composr repository supports for that resource type. Access rules and privilege rules are for setting permissions.

More detail on labels

Usually in the repository we would use filenames, but labels are a much more appropriate key when it comes to aggregate content types. We are looking up and creating based upon labels. Fortunately Composr can look up the label because the repository is able to search against labels.

At no point do you reference raw content IDs. The repository does not use them (except within the internal code) and content-buildout naturally uses human-friendly naming.

More detail on subpaths

If no subpath is given for a resource rule, it is considered '' (i.e. blank). This means we are either:
  1. Making an entry for a content type that does not have categories at all.
  2. Making a new folder (a category) for a content type that doesn't have hierarchical categories.

Subpaths are relative to where the content type is stored in the repository (associated categories and entries are both stored under the same place - e.g. download categories and downloads are both under /var/downloads). The path components are similar to labels, but monikerified (turned into a codename, which will be enforced as unique). You can find what they are by browsing the repository. Or, if it's a case where the content type uses a human readable codename, the subpath is usually the same as the codename (e.g. the name of a zone, or a gallery).

More detail on permissions

'Access' rules set view permissions, and privilege rules set privileges. 1 grants access, and 0 denies access.

The usergroup attribute references are to usergroup labels. * is understood specially as a wildcard, meaning to set against all usergroups.
Instead of a usergroup attribute, you can specify a member attribute, which is a reference to a member label (i.e. username).

The preset attribute values may be any of the following:
  • read
  • submit
  • unvetted
  • moderate
presets work off the same system as in the Permissions Tree Editor. They are for bulk-setting privileges to common combinations.

More detail on parameters

The XML is post-processed with Tempcode, which is why we write {LABEL*} as if it was a Tempcode parameter. 'LABEL' is bound by a required parameter when creating any aggregate type instance. Any other parameters referenced within the XML will be automatically represented on the instance-creation form.

More detail on templates

Templates allow cloning of resources, so the XML doesn't need to define every new property required. The template is simply an existing instance of a resource that we will clone our new resource from.

template_subpath works the same as subpath, but for a template. Similarly, template_label works the same as label, but to identify the template.

Synching

If a resource rule matches to an existing resource (i.e. no new resource needs adding), it won't skip over that resource completely when an aggregate content type instance is being added. It will actually change the properties of the matched resource to that specified. If you do not want this, you need to set resync="false" on either the resource rule, or individual sub-rules.

You can actually manually re-sync all instances. This is useful if, for example, you have extended the XML structure with additional categories, and you want to create them out for your existing instances.
You can re-sync all instances of all aggregate content types, or for just a selection.

See also


Feedback

Please rate this tutorial:

Have a suggestion? Report an issue on the tracker.