An extension for Radiant CMS that manages a Product and Category list that can be used to provide product lists and order forms across the site.
Pull request Compare This branch is 7 commits ahead of aurorasoft:master.
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Failed to load latest commit information.


Simple Product Manager

A RadiantCMS extension providing a mechanism to handle products and categories that can then be used across the rest of the RadiantCMS site.

Also provides Category and Product pages for each of the items listed, and these pages can be prepared using existing Layouts and Snippets.

It was once very simple, now it does a little bit more…


Although simple product/category systems can be created using child pages and built in Radant tags, this can become a hassle - especially when it comes to maintaining linked Ordering and Product List pages.

This extension was created to make this process simpler by storing it all in one location (the DB) so as that it is easy to reuse the information across the site.

It handles nested categories, although these are a tad difficult to work with in the layouts.




The Share Layouts Extension must be installed. See :

For full configuration, you will also require the Settings extension. See :

The product images code uses attachment_fu, so you will need to satify the dependencies for that library too. See :

If you receive errors or warnings about JSON, ensure that the json gem is installed.


The extension adds a “Products” page which provides a simple mechanism to add categories and products.

Products and Categories can be arbitrarily ordered within their groupings.


The categories and products are exposed to the site using the following tags:

Category Tags

<r:category:find [where=""] [tag=""]> ... </r:category:find>
<r:categories:each [where=""] [tag=""] [order="sequence ASC"] [parent="id or title"]> ... </r:categories:each>
<r:category:if [id=""] [title=""] [where=""]> ... </r:category:if>
<r:category:unless [id=""] [title=""] [where=""]> ... </r:category:unless>
<r:category:if_self> ... </r:category:if_self>
<r:category:unless_self> ... </r:category:unless_self>
<r:category:if_ancestor_or_self> ... </r:category:if_ancestor_or_self>
<r:category:unless_ancestor_or_self> ... </r:category:unless_ancestor_or_self>
<r:category:id />
<r:category:title />
<r:category:description />
<r:category:field name="" />
<r:category:link [selected="current"]>...</r:category:link>

Product Tags

Product tags can either be used stand-alone to search all products in the database, or within a <r:category:find> or <r:categories:each> tag to search only within that Category's products.

In this case, only Products directly owned by the current Category are shown - they do not recurse down into sub-Categories.

<r:product:find [where=""]> ... </r:product:find>
<r:products:each [where=""] [order="sequence ASC"]> ... </r:products:each>
<r:product:if [id=""] [title=""] [where=""]> ... </r:product:if>
<r:product:unless [id=""] [title=""] [where=""]> ... </r:product:unless>
<r:product:if_self> ... </r:product:if_self>
<r:product:unless_self> ... </r:product:unless_self>
<r:product:id />
<r:product:title />
<r:product:description />
<r:product:price [precision="2"] [unit="$"] [separator="."] [delimiter=","] />
<r:product:field name="" />
<r:product:link [selected="current"]>...</r:product:link>
<r:product:images:each [tag=""] [limit=""] [order="sequence ASC"]>...</r:product:images:each>
  <r:product:image:description />
  <r:product:image:filename />
  <r:product:image:url [type="product"] />
  <r:product:image:tag [type="product"] [alt="description"] [width=""] [height=""] />

Subcategory Tags

Subcategory tags are only valid within the <r:category:find> or <r:categories:each>. They will restrict their search to include only Categories which are direct descendants of the currently selected Category.

<r:subcategory:find [where=""] [tag=""]> ... </r:subcategory:find>
<r:subcategories:each [where=""] [tag=""] [order="sequence ASC"]> ... </r:subcategories:each>
<r:subcategory:id />
<r:subcategory:title />
<r:subcategory:description />
<r:subcategory:field name="" />

Layouts and Snippets

If you want to use the category and product page functionality, you will need to have two Layouts, and two Snippets.

Category pages will be displayed in the “Category” layout by default, but this can be changed by the “simple_product_manager.category_layout” setting.

Layouts can also be defined on a per-Category basis, so as that separate Categories (and their decendants) can use their own Custom layout. These are defined in the Category form.

The “category” snippet will be rendered within the relevant layout - regardless of whether it is the default setting, or a custom layout. The snippet will be rendered in the context of the Category, loaded by <r:category:find>.

Product pages will be displayed in the “Product” layout by default, but this can be changed by the “simple_product_manager.product_layout” setting, or by setting the “Custom Layout for Product” setting on the Category page.

Similarly to Categories, the “product” snippet will be rendered within this layout. The snippet will be rendered in the context of the Product, loaded by <r:product:find>.

No Category List is implemented, as it's fairly trivial to code your own using <r:category:each> and friends.

Product and Category Pages (and URLs)

By default, the extension adds routes for each Category and Product like :


And these are provided by <r:category:link> and <r:product:link> respectively.

When on a Category URL, the link element produced by the relevant <r:category:link> will include the class specified by “selected”, or the default of “current” if it is not provided.

When on a Product URL, relevant link elements produced by both <r:category:link> and <r:product:link> will include the relevant class specified by “selected”, or “current” if not set.

It's worth noting that the /products URL is NOT handled - this allows you to place a generic product listing by creating a standard Radiant Page with a slug of /products

Custom Fields

Both Categories and Products can be extended to include custom fields.

These custom fields must be defined using the Settings tab, and are called “Product fields” and “Category fields”.

These values must be a list of field names (letters only), separated by commas. (eg. “leadtime,weight”), and once they are saved they will be visible on the relevant form.

The data can then be accessed within the pages using <r:product:field name=“”> and <r:category:field name=“”> tags.

For example:

  <p><r:category:title />: Average lead time required is <r:category:field name="leadtime" /></p>

Photo Thumbnails

The size of the resized images can be changed by editing the simple_product_manager_extension,rb file, and changing the PRODUCT_ATTACHMENT_SIZES constant.

You will need to retain the :product setting, as that is the default size used when the “type” isn't specified for <r:product:image:url /> or <r:product:image:tag />

The size formats are those expected by attachment_fu.


Product Listing

A simple example for listing Categories and their products.

NOTE: This example assumes no subcategories

<h2>Product Listing</h2>
<r:categories:each order="title ASC">
  <div class="category">
    <h3><r:category:title /></h3>
    <r:products:each order="price ASC">
      <div class="product">
        <h4><r:product:link /> (<r:product:price precision="0" />)</h4>
        <p><r:product:image /><r:product:description /></p>

Order Form (for mailer extension)

<h2>Order Form</h2>
<r:categories:each order="title ASC">
  <tr><th colspan="3"><r:category:title /></th></tr>
 <r:products:each order="price ASC">
      <r:product:title />
      <input type="hidden" name="mailer[products][<r:product:id />][name]" value="<r:product:title />" />
    <td><r:product:price /></td>
    <td><input type="text" name="mailer[products][<r:product:id />][qty]" value="0" /></td>


  • Deperately need some integration tests

  • <r:product:url> and <r:category:url> tags

  • Tag definitions need some major cleanup work


See CHANGES.rdoc


Thanks to John Reilly, Justin Grammens who did most of the work to get the original extension working under Radiant-0.7.1 and 0.8.0.

Huge thanks to Ingvi Gudmundsson ( for sponsoring and helping test v0.4 to v0.7.

Thanks to Yves-Eric Martin for contributing the if_self unless_self tags.


Copyright (C) 2008-2009 Aurora Software (, released under the MIT license