Search

Google
 

Monday, July 16, 2007

Extensions Available in Radiant

  • Blog Tags (svn)
Provides useful tags for blogs, like getting the next and previous articles or showing the post time in words (eg, “Posted by Sean 2 days ago”)Adds blog-like comment functionality to Radiant (still very alpha)It allows for a page to be copied (or moved) under a new parent.Allows pages to be rendered as CSS
  • Default Page Parts (docs)
With this extension you can create a page of !DefaultPageParts type and specify the default parts of all its children.With this extension you can create a page of Dynamic type that will be updated every time that one of its children will be created/saved/destroyed.
  • Enkoder Tags (svn)
Provides tags for enkoding email addresses (or arbitrary content) using Dan Benjamin’s Enkoder to block spam harvesters
  • Event Calendar (iCal) (svn)
Create calendars in Radiant admin which have iCal subscriptions attached to them. Retrieve events to each calendar through refreshes from the iCal subscription. Works best when used in conjunction with an iCal publishing front-end app like Google Calendars or .Mac and a cron job setup on your server to run the refresh automatically every hour or more.
  • Flickr Tags (svn)
Provides a tag for embedding a Flickr slideshow like the one in my portfolio.The Gallery extension allows Radiant to manage galleries of images.This Radiant extension allow you to define /~user routes like apache userdir module.
  • Language Redirect (svn)
Redirects the browser to a different page root based on the accepted locale.A Markdown variant extension for RadiantAdds support for file uploads via attachmentsThis extension/hack of Radiant gives all your pages an “attributes” tab where you can add named strings, bools, links, and files… (and then reference them from r: tags).Allows for a Ruby on Rails application to integrate with the features offered by Radiant CMS.Adds tags for displaying database records. A natural front end for custom, back-end models.
  • Resizing Text Area (docs)
This extension appears to be defunct
  • RSS Reader 0.2 (docs)
This extension appears to be defunct
  • Smooth Gallery (docs)
This extension appears to be defunctAllows you to edit Radiant pages from VI.Makes each top-level page the root of a different website (based on the host name).Based on the TinyMCE editor

Thursday, July 12, 2007

WHAT'S NEW IN RADIANT

* Added support for extensions--an extremely flexible way to extend
Radiant
* Merged Behaviors into the Page class; subclass page now instead
* Improved database support for Postgres and Sqlite
* Limited support for SQL Server
* Exceptions from tags now bubble up during testing
* Page parts are now sorted by ID so the order that you create them in
is preserved [Josh Ferguson]
* Implemented tag documentation DSL and UI [Sean Cribbs]
* Reworked the setup code
* Renamed script/setup_database to rake db:bootstrap
* Reworked the upgrade code to work around rake tasks
* Added rake tasks for freezing and unfreezing radiant to the edge
* r:children:each, r:children:first, and r:children:last now all accept
the same ordering and limit attributes and have the same defaults
* Snippets are now responsive to global context via the r:page tag. This
means that any tags inside r:page will refer to the page currently
being rendered, i.e. the page requested, not the local contextual page
via tags like r:children:each, etc. This is most relevant to recursive
snippets like the sitemapper example [Sean Cribbs]
* r:navigation now uses the pipe character ("|") to delimit URLs in the
urls attribute rather than the semi-colon
* :date now accepts a "for" attribute that specifies which attribute of
the page to render. Valid values of the attribute are published_at,
updated_at, created_at, and now.
* Created the r:cycle tag to make alternating tables and lists possible
* Added popups for filter and tag documentation inside the page editing
interface
* Added support for optimistic locking for all models [Daniel Shepherd]
* Added support to Radiant::Config for boolean values [Sean Cribbs]
* Caching no longer stores the headers and body in the same file [Daniel
Shepherd]
* Added support for the X-Sendfile header that works in conjunction with
caching to speed it up (by default X-Sendfile support is off) [Daniel
Shepherd]
* Moved the images and stylesheets into images/admin and
stylesheets/admin respectively to make it easier for Radiant's assets
to coexist easily with the site's assets
* Improved the Javascript that automatically updates the slug and
breadcrumb based off of the title so that it now response to all
change events

Radiant CMS, Managing Web Sites on Rails

Managing the content of a web site is still a painful task, more that 10 years after the web took of. Web site management is an holy grail and Content Management Systems (CMS) have try to fill the gap without much success…

The problem is that mostly all Open Source CMS you can find are generally too complex. They want to achieve everything possible with an unique system. That leads to too many screens, too many options and too many plugins… Non expert people just can’t use those geeky systems and it’s very hard to train people on how to use the admin interface…

Here comes Radiant, an Open Source CMS made with Ruby on Rails. Radiant is more pragmatic and adopted a “less is more” philosophy. It focus on managing medium-size web sites with a page-centric approach that most people are used to.

The project is in early stages of development but already useable. It been able to power a web site I’m building in only 1 hour. I’ve put all my content into Radiant, pages, templates, styles and so on. Everything is in the system and I can manage it right from the administration interface. Neat!

The administration interface included with Radiant is simple enough for people to understand it… One of the real value of Radiant.

Concerning development, I needed a French localized interface for my web site and I’ve been able to submit a patch in no time. Since Radiant is written with Ruby on Rails, it’s quite extensible and you can use any plugin developed for Rails such as GLoc.

Radiant is an awesome CMS

I think Radiant is an awesome CMS, the clean interface, helpful community, and benefits of Ruby on Rails make it shine in the world of bloated, clunky CMS systems.

Knowing Ruby and Rails makes it easy to extended and customize. This new Extentions concept is a step forward from the old Behaviors. Being able to essentially embedd your Ruby on Rails applications into the CMS is a dream. I really think this CMS is will continue to lead the pack in the Ruby based CMS solutions.

Tuesday, July 10, 2007

Exploring the Radiant CMS

If you are a Ruby on Rails developer you should learn about Radiant.

(If you are a non-developer looking for a good CMS, befriend a Rails developer to get some help with the install.)

Simple as that, it’s a very good CMS. It manages to stay simple enough to be useful for most kinds of sites instead of packing so many bells and whistles that you get lost immediately (I’m looking at you, Textpattern).

The Good

Radiant has a good interface, and is extremely easy to navigate and use. I like that it has several text filters (including my favorite, Textile), and that is also makes it easy to assign pages layouts and behaviors.

The hierarchical pages are well done, and upon clicking on the Pages tab it is immediately clear how the website is organized.

Snippets make perfect sense, and are easy to use.

While it took me a bit to get used to Radius tags, it is the best solution I’ve seen to giving power to the user to sprinkle in bits of code without having to actually touch the underlying ruby code of the application.

I was also pleased to see that Radiant uses a lot of the caching power of Rails and caches content pages as static YAML files. This bodes well for Radiant’s ability to work as a CMS for heavily trafficked websites.

The Bad

I have a few gripes as well about Radiant, but luckily many are being addressed.

There is no way yet to upload images and files, but the Radiant dev says that is it being addressed, so it should show up in a later release. Current you just have to manually put any resources like those in the public directory.

Also right now Stylesheets must exist as a mix of a layout and pages. I’d like to see Stylesheets given their own tab in the interface and the ability to assign stylesheets to layouts and pages the same way layouts are assigned to pages.

There is also very little documentation for Radiant, but this is such a new project that documentation will come with time. I plan to write an installation tutorial for Radiant to help with this disparity.

The Ugly

My one gripe with Radiant is really just a wart it gets from being a Rails app, and not anything inherently bad with Radiant inself. Deploying a Rails app of any kind still requires some experience with the framework and hosting services. You need to know how to set up Apache / Lighttpd, how to edit your database.yml correctly, and run any setup scripts to give your database any base data.

This is something that Rails core team knows about, and the deployment tutorials across the web are getting better and better. Shared hosts are also getting more comfortable with Rails as well, which is easing much of the pain. I’m confident many Rails deployment challenges will be solved within the coming year.

Final Notes

I still believe that content management systems are the best bridge we have right now to bring web developers, designers, and clients / users together. I also think WYSIWYG webdesign programs are responsible for a lot of problems that content management systems solve nicely (Frontpage still makes my eyes bleed).

Radiant Overview

Simple Admin Interface

Built from the ground up to be as simple as possible, Radiant features an elegant administrative interface that centers around three key components: pages, snippets, and layouts.

Pages

Pages contain the meat of the content for a Web site and may use Markdown, Textile, or plain HTML. Pages are composed of multiple parts such as a body and sidebar.

Snippets

Content that is used in multiple places can be stored in a snippet. Snippets are very similar to PHP includes or Rails partials.

Layouts

Layouts generally contain most of the HTML for a page’s design. Layouts can render pages parts in any way they choose. One layout could choose to render the body and sidebar of a page, while another layout (a print layout) could render only the body.

Flexible Site Structure

Unlike many other blogging engines Radiant allows you to arrange pages according to any hierarchy. A Weblog in Radiant can be as simple as a collection of child pages underneath a parent page.

Radius Template Language

Radiant has a special macro language (similar to HTML) called Radius which makes it easy to include content from other pages, iterate over page children, and display content conditionally. Radius tags are available in pages, snippets, and layouts.

Custom Text Filters

Radiant ships with support for Markdown and Textile, but developers can easily create text filters for any markup language such as RDoc, BBCode, or Structured Text.

Intelligent Page Caching

Radiant includes an intelligent caching mechanism which allows content to be cached for a maximum of 5 minutes. This ensures that content is always fresh and provides an optimal level of performance.

Built with Ruby on Rails

Radiant is built using Ruby on Rails. This means it’s easy for developers to extend Radiant because it’s built on a widely accepted (and understood) Web application development platform.

Licensed Under the MIT License

Radiant is licensed under the MIT License. This means that Radiant is free for commercial and non-profit use. It also means that you are free to modify and distribute Radiant as long as you don’t remove the appropriate notices from the source code.

Not impressed yet? Try the live demo.

How To: Understanding Radius Tags

Ever wondered how Radiant does its magic? What do all those tags mean and how do they make stuff happen? How to use the tags has already been discussed, but not how they work.

In the second of our “How To” series, I’m going to walk you through how to read and understand Radiant’s Radius tag definitions (i.e. code). For this How To, it helps to understand Ruby and know some stuff about Rails, but just in case you don’t, I’ll fill you in on the needed knowledge as we go.

Reading and Understanding Tags

Radius tags are what give you the hooks into cool functionality offered by Radiant. There are a number of tags which are available to every page and some that are specific to individual behaviors. Let’s start by analyzing one of the built-in tags that can be found in the file app/models/page_context.rb. These tags are available to any page, snippet, or layout in Radiant.

Anatomy of a Tag Definition: r:content

The r:content tag in Radiant displays the content of a given “part” of a page—those tabs you see when you are editing one. With no part attribute specified, it will display the “body” part/tab. Let’s take a look at how it works.

If you want to follow along, please look at the source of page_context.rb around line 194. This is a fairly complex tag, but we’ll take it slow.

  define_tag 'content' do |tag|  

This is the standard tag definition. It begins with define_tag which says we’re going to define a new tag. The next thing is 'content', which is the name of the tag. If this tag were scoped (we’ll talk about that more below), like children:each, we would have a string of colon-separated names. The next two things are do |tag| which signifies we’re going to start a block that has a single parameter named tag. Blocks are a Ruby feature that lets you define code that can be reused in different contexts, kind of like anonymous functions in Java or Javascript. For our purposes, though, just know that the do |tag| means, “here comes the tag definition, so listen up and give me a hook into the context called tag”.

This type of declaration is only valid in PageContext, whereas in your cool new behavior that you’ll be writing after this How To, the same tag would look like:

  define_tags do
    tag 'content' do |tag|

This is a subtle difference, but one to take note of. Also notice that the declaration in PageContext takes place in the initialize method, whereas the second one would be at the class level of your behavior (i.e. not inside initialize). If that last sentence is gibberish to you, don’t worry about it; you don’t need to understand that to get the rest of this How To.

The end of each tag definition is marked by the end keyword, which you can see on line 216 (Actually, all blocks like these are terminated with end).

Okay, now that we have our tag definition started, let’s look at what happens next. We’ll take the first two lines.

  page = tag.locals.page 
  part_name = tag_part_name(tag)

The first line creates a variable named page that refers to tag.locals.page. The nice thing about the tag parameter that gets passed in is all the goodies that come along with it. tag.locals is a collection of variables that refer to the current context, and were set by tags that occur outside the current tag. So if you need to get information about where you are and what’s around, tag.locals will tell you. In this case, we’re grabbing the page variable to figure out what the current page is. Interestingly enough, the page variable refers to a Rails model named Page. (Models are objects that abstract and encapsulate information that a web application (RadiantCMS) uses. In this case, it’s a web page that you can browse to.)

So once we have this Page, we can get all kinds of information about what content was typed into the current page, when it was published, who wrote it, etc. The next line calls a function that extracts the designated part. Parts, or PagePart models, are the tabs that you see when editing a page. The second line of code will call a custom function that gives us the value of the tag’s attribute part. Jump down to line 508 to see what the function does. The function clues us into another useful item on the tag variable, tag.attr. This gives us access to the attributes that the user put on the tag in their page. The function grabs the “part” attribute, or in case it wasn’t defined, gives us “body”. Head back to line 197 and we’ll go on.

  boolean_attr = proc do |attribute_name, default| 
    attribute = (tag.attr[attribute_name] || default).to_s 
    raise TagError.new(%{`#{attribute_name}' attribute of `content' tag must be set to either "true" or "false"}) unless attribute =~ /true|false/i 
    (attribute.downcase == 'true') ? true : false 
  end

We’re going to gloss over these lines a little bit because they involve some Ruby “magic”. Remember when I talked about blocks before? Here we’re defining a block inline and assigning it to a variable so we can use it later in the tag definition. Overall, this code says:

  1. Make me a block with two parameters, attribute_name and default, and hold onto that block in the variable boolean_attr.
  2. Inside this block, grab the tag’s attribute of the name that we passed in and keep a copy of it, or the default value, in the attribute variable.
  3. If somebody gave us something other than “true” or “false”, throw up a red flag (TagError).
  4. Assuming there’s no error, if the attribute is the text “true”, give me back the boolean true value, otherwise give me false.

If you look at the next line, we can see how the block we just created is used.

  inherit = boolean_attr['inherit', false]

This says, get the value of the attribute “inherit” as a boolean or give me false, then store it into our local variable “inherit”. The “inherit” attribute on the r:content tag says that if the selected page part doesn’t exist, get whatever the parent or closest ancestor page has; and that’s exactly what the next few lines of code do.

  part_page = page 
  if inherit 
    while (part_page.part(part_name).nil? and (not part_page.parent.nil?)) do 
      part_page = part_page.parent 
    end 
  end
  1. Start with the current page, store it in part_page.
  2. If we’re supposed to inherit the content from an ancestor page,
  3. Search through the ancestor pages until one of them has the required part and hold onto that in part_page.

The next line checks another boolean attribute, this time it’s contextual.

  contextual = boolean_attr['contextual', true]

The contextual attribute says whether an inherited page part, that is, if the current page doesn’t have it and we used the inherited attribute, should be rendered in the context of the current page. In most cases, this is what we want, so it defaults to true. If it’s false, we’ll render it in the context of the page it belongs to.

Our next section of code finally does the output of the content, now that we’re all prepared.

  if inherit and contextual 
    part = part_page.part(part_name) 
    page.behavior.render_snippet(part) unless part.nil? 
  else 
    part_page.behavior.render_page_part(part_name) 
  end

A quick analysis:

  1. If we inherited the part and we’re rendering it in the context of the current page,
    1. Get the part of the page that we found.
    2. Use the current page’s behavior to render the part, unless it’s empty.
  2. Otherwise, render the page part in the context of the page it came from.

There you have it! An analysis of one of the built-in tag definitions.

Tying up Loose Ends

We saw a lot of code above, but unfortunately, we didn’t see everything you’ll need to know to read tag definitions or to create your own. Here’s a few more pointers.

  • tag.locals is very flexible, and anything you set will be available to any tags nested inside the current one. So, if you want all the containing tags to have access to your local foo variable, set tag.locals.foo = foo and then those tags can access it. Set tag.locals.page if you want to tell containing tags to refer to a different page. This is how r:children:each works.
  • tag.globals works just like tag.locals, except it generally isn’t changed in the context. If you want the current page that is being rendered (ignoring the context), access tag.globals.page.
  • tag.attr, as we saw above, contains key-value pairs for every attribute that the author put on the Radius tag. So if I wrote , the tag.attr['part'] would be assigned “mine” inside the ‘content’ tag definition.
  • Whatever is mentioned on the last line of the tag definition is what will be passed back to the parent tags in the nesting order, and ultimately output on your page. So if I created a tag like so:
·           define_tag "foo" do |tag|
·             "bar" 
  end

It would output “bar” (without the quotes) anytime I use the r:foo tag in a page.

  • You can find out if the tag was called as a singleton or a container (singleton tags end in a slash and don’t contain other tags). If it’s a singleton tag, tag.single? will return true, if it’s a container, tag.double? will return true.
  • Most tags either output something or redefine the context and then pass on the output to tags contained inside, or a little of both. To process the output of the contained tags and text, we call tag.expand. Take, for example, this simple tag definition that allows you to access the parent page:
·           define_tag "parent" do |tag|
·             tag.locals.page = tag.locals.page.parent
·             tag.expand unless tag.locals.page.nil?
  end

Notice how we reset the context to the parent page (assigned tag.locals.page), then evaluated the contents inside the r:parent tag if the parent page exists.

  • In addition to the context created by tag.locals, tags can have a scoped context. This is signified by the colons in the tag definition. The colons signify the nesting level of the tag. So if I write this in my page,
  

Radiant would look for a tag definition for “foo:bar”, and if it can’t find that, one named “bar”. In addition to this, it will call the tag definition for “foo”, which should have a call to tag.expand which ultimately renders “foo:bar” or “bar”. So, in a sense, the above tag is equivalent to:

  

In this way, you can redefine how certain tags are rendered in different nesting combinations. If I wanted to change how the r:link tag operates inside my foo tag, I’d create a tag definition for foo:link, then do what I want inside that tag definition.

Monday, July 9, 2007

More about Radiant CMS

After a long haul with more than 6 months of development a new stable release of Radiant has been released. Check out the announcement on their blog here

The big new thing in 0.6, or “Lapidary”, is the new extension system.

Full changelog:

  • Added support for extensions—an extremely flexible way to extend radiant
  • Merged Behaviors into the Page class; subclass page now instead
  • Improved database support for Postgres and Sqlite
  • Limited support for SQL Server
  • Exceptions from tags now bubble up during testing
  • Page parts are now sorted by ID so the order that you create them in is preserved [Josh Ferguson]
  • Implemented tag documentation DSL and UI [Sean Cribbs]
  • Reworked the setup code
  • Renamed script/setup_database to rake db:bootstrap
  • Reworked the upgrade code to work around rake tasks
  • Added rake tasks for freezing and unfreezing radiant to the edge
  • r:children:each, r:children:first, and r:children:last now all accept the same ordering and limit attributes and have the same defaults
  • Snippets are now responsive to global context via the r:page tag. This means that any tags inside r:page will refer to the page currently being rendered, i.e. the page requested, not the local contextual page via tags like r:children:each, etc. This is most relevant to recursive snippets like the sitemapper example [Sean Cribbs]
  • r:navigation now uses the pipe character (”|”) to delimit URLs in the urls attribute rather than the semi-colon
  • :date now accepts a “for” attribute that specifies which attribute of the page to render. Valid values of the attribute are published_at, updated_at, created_at, and now.
  • Created the r:cycle tag to make alternating tables and lists possible
  • Added popups for filter and tag documentation inside the page editing interface
  • Added support for optimistic locking for all models [Daniel Shepherd]
  • Added support to Radiant::Config for boolean values [Sean Cribbs]
  • Caching no longer stores the headers and body in the same file [Daniel Shepherd]
  • Added support for the X-Sendfile header that works in conjunction with caching to speed it up (by default X-Sendfile support is off) [Daniel Shepherd]
  • Moved the images and stylesheets into images/admin and stylesheets/admin respectively to make it easier for Radiant’s assets to coexist easily with the site’s assets
  • Improved the Javascript that automatically updates the slug and breadcrumb based off of the title so that it now response to all change events
  • For the full scoop on what’s changed see Sean Cribbs’ detailed blog post here

Radiant Feedback Extension




Technologies: Radiant CMS, Radiant Extension API, Radiant Mailer Extension

The Solution:
some of projects required a highly customisable email form page for the Radiant CMS backed by database storage of data collected from users. Radiant’s Mailer extension provided a large component of what project desired, so we worked together to identify the missing functionality.

By creating a new Feedback Extension which inherited from the Mailer extension, I was able to provide the additional features for exporting the user’s feedback into the database, without modifying the core of either Radiant or the Mailer extension.

A “User Feedback” tab was added to the default Radiant administration area, allowing the administrator to view and delete feedback submitted by users, and the administrator can now specify required fields which must be completed to permit form submission.

Despite the fact that this project was the first time I had worked with the Radiant CMS at all, I was able to very quickly get up to speed and understand the Extension API, which is very straightforward, yet allowed me to customise the interface exactly as I wanted.

Custom Tags in Radius

Defining Tags

Before you can parse a template with Radius you need to create a Context object which defines the tags that will be used in the template. This is actually quite simple:

require 'radius'

context = Context.new
context.define_tag "hello" do |tag|
"Hello #{tag.attr['name'] || 'World'}!"
end

Once you have defined a context you can easily create a Parser:

parser = Radius::Parser.new(context)
puts parser.parse('< p >< radius:hello />< /p >')
puts parser.parse('< p >< radius:hello name="John" />< /p >')

This code will output:

< p >Hello World!< /p >
< p >Hello John!< /p >

Note how you can pass attributes from the template to the context using the attributes hash. Above, the first tag that was parsed didn’t have a name attribute so the code in the hello tag definition uses "World" instead. The second time the tag is parsed the name attribute is set to "John" which is used to create the string "Hello John!". Tags that do not follow this rule will be treated as if they were undefined (like normal methods).

Container Tags

Radius also allows you to define "container" tags. That is, tags that contain content and that may optionally manipulate it in some way. For example, if you have RedCloth installed you could define another tag to parse and create Textile output:

require 'redcloth'

context.define_tag "textile" do |tag|
contents = tag.expand
RedCloth.new(contents).to_html
end

(The code tag.expand above returns the contents of the template between the start and end tags.)

With the code above your parser can easily handle Textile:

parser.parse('h1. Hello **World**!')

This code will output:

< h1 > Hello < strong>World< /strong>!< /h1 >

Nested Tags

But wait!—it gets better. Because container tags can manipulate the content they contain you can use them to iterate over collections:

context = Context.new

context.define_tag "stooge" do |tag|
content = ''
["Larry", "Moe", "Curly"].each do |name|
tag.locals.name = name
content << tag.expand
end
content
end

context.define_tag "stooge:name" do
tag.locals.name
end

parser = Radius::Parser.new(context)

template = <<-TEMPLATE
< ul >
< radius:stooge >
< li >< radius:name />< /li >
< /radius:stooge>
< /ul >
TEMPLATE

puts parser.parse(template)

This code will output:

< ul >

< li >Larry< /li >

< li >Moe < /li >

< li >Curly< /li >

< /ul >

Note how the definition for the name tag is defined. Because "name" is prefixed with "stooge:" the name tag cannot appear outside the stooge tag. Had it been defined simply as "name" it would be valid anywhere, even outside the stooge tag (which was not what we wanted). Using the colon operator you can define tags with any amount of nesting.

Exposing Objects to Templates

During normal operation, you will often want to expose certain objects to your templates. Writing the tags to do this all by hand would be cumbersome of Radius did not provide several mechanisms to make this easier. The first is a way of exposing objects as tags on the context object. To expose an object simply call the define_tag method with the for option:

context.define_tag "count", :for => 1

This would expose the object 1 to the template as the count tag. It’s basically the equivalent of writing:

context.define_tag("count") { 1 }

So far this doesn’t save you a whole lot of typing, but suppose you want to expose certain methods that are on that object? You could do this:

context.define_tag "user", :for => user, :expose => [ :name, :age, :email ]

This will add a total of four tags to the context. One for the user variable, and one for each of the three methods listed in the expose clause. You could now get the user’s name inside your template like this:



If "John" was the value stored in user.name the template would render as "John".

Tag Shorthand

In the example above we made reference to user.name in our template by using the following code:

< radius:user>

There is a much easer way to refer to the user.name variable. Use the colon operator to "scope" the reference to name:

< radius:user:name />

Radius allows you to use this shortcut for all tags.

Changing the Tag Prefix

By default, all Radius tags must begin with "radius". You can change this by altering the tag_prefix attribute on a Parser. For example:

parser = Radius::Parser.new(context, :tag_prefix => 'r')

Now, when parsing templates with parser, Radius will require that every tag begin with "r" instead of "radius".

Custom Behavior for Undefined Tags

Context#tag_missing behaves much like Object#method_missing only it allows you to define specific behavior for when a tag is not defined on a Context. For example:

class LazyContext < Radius::Context
def tag_missing(tag, attr, &block)
"< strong >ERROR: Undefined tag `#{tag}' with attributes #{attr.inspect}< /strong >"
end
end

parser = Radius::Parser.new(LazyContext.new, :tag_prefix => 'lazy')
puts parser.parse('')

This will output:

< strong >ERROR: Undefined tag `weird' with attributes {"value"=>"true"}< /strong >

Normally, when the Radius Parser encounters an undefined tag for a Context it raises an UndefinedTagError, but since we have defined tag_missing on LazyContext the Parser now outputs a nicely formated error message when we parse a string that does not contain a valid tag.

Tag Bindings

Radius passes a TagBinding into the block of the Context#define_tag method. The tag binding is useful for a number of tasks. A tag binding has an expand instance method which processes a tag’s contents and returns the result. It also has a attr method which returns a hash of the attributes that were passed into the tag. TagBinding also contains the TagBinding#single? and TagBinding#double? methods which return true or false based on wether the tag is a container tag or not. More about the methods which are available on tag bindings can be found on the Radius::TagBinding documentation page.

Tag Binding Locals, Globals, and Context Sensitive Tags

A TagBinding also contains two OpenStruct-like objects which are useful when developing tags. TagBinding#globals is useful for storing variables which you would like to be accessible to all tags:

context.define_tag "inc" do |tag|
tag.globals.count ||= 0
tag.globals.count += 1
end

context.define_tag "count" do |tag|
tag.globals.count || 0
end

TagBinding#locals mirrors the variables that are in TagBinding#globals, but allows child tags to redefine variables. This is valuable when defining context sensitive tags:

require 'radius'

class Person
attr_accessor :name, :friend
def initialize(name)
@name = name
end
end

jack = Person.new('Jack')
jill = Person.new('Jill')
jack.friend = jill
jill.friend = jack

context = Radius::Context.new do |c|
c.define_tag "jack" do |tag|
tag.locals.person = jack
tag.expand
end
c.define_tag "jill" do |tag|
tag.locals.person = jill
tag.expand
end
c.define_tag "name" do |tag|
tag.locals.person.name rescue tag.missing!
end
c.define_tag "friend" do |tag|
tag.locals.person = tag.locals.person.friend rescue tag.missing!
tag.expand
end
end

parser = Radius::Parser.new(context, :tag_prefix => 'r')

parser.parse('') #=> "Jack"
parser.parse('') #=> "Jill"
parser.parse('') #=> "Jack"
parser.parse('') #=> "Jack"
parser.parse(' and ') #=> "Jack and Jill"
parser.parse('') # raises an UndefinedTagError exception

Notice how TagBinding#locals enables intelligent nesting. "" evaluates to "Jill", but "" evaluates to "Jack". Locals loose scope as soon as the tag they were defined in closes. Globals on the other hand, never loose scope.

The final line in the example above demonstrates that calling "" raises a TagMissing error. This is because of the way the name tag was defined:

tag.locals.person.name rescue tag.missing!

If person is not defined on locals it will return nil. Calling name on nil would normally raise a NoMethodError exception, but because of the ‘rescue’ clause the TagBinding#missing! method is called which fires off Context#tag_missing. By default Context#tag_missing raises a UndefinedTagError exception. The ‘rescue tag.missing!’ idiom is extremly useful for adding simple error checking to context sensitive tags.

Tag Specificity

When Radius is presented with two tags that have the same name, but different nesting Radius uses an algorithm similar to the way winning rules are calculated in Cascading Style Sheets (CSS) to determine which definition should be used. Each time a tag is encountered in a template potential tags are assigned specificity values and the tag with the highest specificity wins.

For example, given the following tag definitions:

nesting
extra:nesting
parent:child:nesting

And template:

< r:parent:extra:child:nesting />

Radius will calculate specificity values like this:

nesting => 1.0.0.0
extra:nesting => 1.0.1.0
parent:child:nesting => 1.1.0.1

Meaning that parent:child:nesting will win. If a template contained:

< r:parent:child:extra:nesting />

The following specificity values would be assigned to each of the tag definitions:

nesting => 1.0.0.0
extra:nesting => 1.1.0.0
parent:child:nesting => 1.0.1.1

Meaning that extra:nesting would win because it is more "specific".

Values are assigned by assigning points to each of the tags from right to left. Given a tag found in a template with nesting four levels deep, the maximum specificity a tag could be assigned would be:

1.1.1.1

One point for each of the levels.

A deep understanding of tag specificity is not necessary to be effective with Radius. For the most part you will find that Radius resolves tags precisely the way that you would expect. If you find this section confusing forget about it and refer back to it if you find that tags are resolving differently from the way that you expected.

Why Radiant?

    My vision for Radiant, even though I'm not a member of the core team
would be one of a 'base' or 'core' CMS that Radiant is today. Radiant
would be a relatively neutral system from which developers could easily
extend to solve problems within their specific domain. My ideal
situation would be to simply be able to type something like
'script/radiant_plugin install radiant_blog' and automatically get
comments, trackbacks, tagging and other blog functionality automatically
integrated into the database and admin interface. Make a note that I
don't want to drop modules on top of the CMS, I want to fundamentally
change its operation to suit the problem domain.

What has to be covered?
I will assume that the installation of Radiant is clean and has not
been installed over anything else. My proposal is for a plugin system
that works easily and seamlessly alongside of the rails plugin system.
The goals of my proposals are the following:

1) The plugins should be compatible with the rails plugin system
2) The plugins should not be dependent on any plugins not controlled by
the radiant core team
3) All plugins should be non-destructive and should not heavily modify
the workings of the core CMS
4) All plugins should be easily removed and all changes should be
reversible (this is particularly important to migrations)
5) The burden of extending the functionality of the system should lie on
the developer of the plugin which means as little modification as
possible should be done to the current radiant system.

There are 4 main considerations when extending Radiant in a way that
allows things to be easily packaged. They are as follows:

1) Modifying controllers and models
2) Adding behaviors and radius tags
3) Adding table to the database
4) Modifying the administration views

Proposed Solutions
1) Modifying controllers and models
This is what rails plugins were meant to do. Since our plugins are
rails plugins we get this for free. There is one consideration. In order
for a plugin to not break compatibility as radiant gets upgraded the
preferred method of modifying controller and model logic is to use
before and after hooks so that if Radiant's core functionality is
changed in the future the plugin will not break that functionality.

2) Adding behaviors and radius tags
If you use the normal rails plugin structure then adding tags and
behaviors to radiant is easy.

3) Migrating the database
Migrating the database is slight more difficult as rails doesn't
have any built in methods for migrating out of plugin directories. This
is where I want to suggest another file in the main plugin directory
that sits alongside init.rb called radiant.rb. Radiant.rb is a file that
contains initialization code that is specific to the plugin being a
radiant plugin. Radiant.rb does not replace init.rb. This file will
refer to the directory with the database migrations along with any
future improvements to the plugin system that might come along.
A separate rake task will be needed to run the plugin migrations but
can easily be included in lib/tasks. This task will look for all plugins
with a radiant.rb file and attempt to migrate them into the current
environment's database. The one rule that should be applied to radiant
plugin migrations is that forward migrations must be non-destructive to
the 'core' CMS structure. In the event that two different plugins'
migrations clash the migration process itself will cause an error to be
reported to the user. There is the possibility that these migrations
don't increment the version number of the database and simply work as an
atomic database change that the normal migrations are unaware of. This
needs to be investigated a bit further, but this can be solved.
There should be a separate script command that gets the plugin like
a normal rails plugin, and then runs the migrations if the user chooses
to do so. There should also be a script for reverting the migrations and
deleting the plugin. These are relatively trivial to code.

4) Modifying the administration views
The problem of modifying the admin interface really can be a show
stopper. I have thought of a few solutions and I will present the one
solution that would get most of the way there and is easiest to
implement into the current system.

The solution is to rely on helpers to place callbacks during the
rendering of the of the administrative views. These filters would be
before each item in a list, form, or set of tabs and before the ending
of the list, form or set of tabs. The goal is to break down the
administrative interface into smaller pieces that can be independently
added onto by either changing the data before a list item or form
control gets rendered or by rendering new list items or form controls.

How coarse or fine should these helper callbacks be? Well my idea
for instance on the 'Edit Page' action is to place a call to a blank
helper method called before_title_input just before the title input box
and before_body_input just before the body input. This would allow
plugin developers to easily extend the existing forms and lists by
assigning before filters to the form or list element that is about to be
rendered. Tabs could be achieved through a bit of coercing of the same
methodology only with helpers that make callbacks inside of javascript
blocks (this might take some more investigation)

I'm of the opinion that simplicity should trump flexibility in most
of these cases and that the callbacks should be kept to a minimum to
allow modification of the default behavior. What might an implementation
of this system look like? Lets try and add a trackback URL to our New
Page and Edit Page action.

The *plugin helper* might look like

module Admin::PageHelper
before_filter :trackback_input, :only => [:end_new_form, :end_edit_form]

def trackback_input(*args)
trackback_input = ''
end
end

The *plugin controller* might look like

class Admin::PageController < ApplicationController
after_filter :do_trackback, :only => [:new, :edit]

def do_trackback
if request.post?
if @page.errors.blank?
# code for making a trackback requests using params[:trackbacks]...
end
end
end
end

What might we have to do to the radiant system to have this functionality?

The *radiant helpers* might look like

module ApplicationHelper
# holds the list of filter functions in some structure (possibly a
nested hash?)
attr_accessor :filters

def before_filter(filtered_method, options = {})
# add a new lambda with appropriate options to the filters list
end

def method_missing(*args)
# check if it starts with 'filters_for' and then run filters for the
proper helper
end
end

module Admin::PageHelper
def end_new_form(*args)
filters_for_end_new_form(args)
end

def end_edit_form(*args)
filters_for_end_edit_form(args)
end

... More Callbacks Go Here ...
end

The *radiant view* might look like

<%= start_form_tag %>

... default form stuff here ...

<%= end_new_form %>

<%= end_form_tag %>

The potential to have a long list of callbacks is definitely there
but I still think it is one of the easiest way to implement flexibility
into the administration system without hacking deeper into rails to
alter how views are rendered. I'd say there would be about 7 or 8
callbacks per helper. It might end up not being manageable but if there
is a standard naming convention it shouldn't be bad at all. This would
allow new callbacks to be added easily without breaking old
functionality. It also allows more than one plugin to register into a
callback chain so that more than one domain specific plugin can be
present at one time, leading to a cross-domain specific CMS.

Vijayta: What's New in Radiant 0.6

Vijayta: What's New in Radiant 0.6

Choosing a Rails Blogging System

For a long time if you wanted to use Ruby on Rails to run a blog you had one choice, Typo. In the past six months or so more Blogging systems and CMS applications have been released and there is finally some choices to be made. So here’s a roundup of all the available blogging systems that use Ruby on Rails.

Typo

After a long wait Typo 4 is out, and this release firmly cements Typo as the gorilla of the Rails blogging systems. It literally encompasses about every feature you could want.

  • Ability to post from desktop clients
  • Atom and RSS feeds
  • Comments, Trackbacks, and spam filtering to go along with it
  • Gravatar support
  • Themes (with a healthy stock of existing themes to back it up)
  • Installer
  • The kitchen sink.

Typo has been around for awhile, which explains its hefty feature set. It also has a reputation for being a bit of a hog and not very lean, which is saying a lot for Rails apps (which usually clock in at 20 mb of memory minimum). But if you want your blogging system to allow you blog and make you breakfast, this is the way to go.

Mephisto

While it’s been in development for awhile, Mephisto is a fairly new, and really only has the official 0.5 release as of this posting. The nice thing about Mephisto is that its developers understand that a lot of people want to be able to have several regular pages exist along side a blog. In the past you usually had to hack a blogging system to make this popular (ask the Textpattern camp about this).

Mephisto ends up being part blog, part CMS, and allows you make custom templates using Liquid. It has a nice simple interface and doesn’t have a huge learning curve. Mephisto is still early in the development phase though, and lacks features like image / file uploads.

Simplelog

[Update 8/22: Added Simplelog upon reader request. I had completely forgotten about it, even though I’m actually subscribed to Garrett Murray’s feed. Sorry Garrett!]

Simplelog is a basic blogging system that is geared specifically at allowing people to do nothing more than blog. All the things you commonly find in blog sidebars just aren’t there (blog rolls, del.icio.us links, flickr streams, etc). Simplelog is intentionally kept simple and given a low feature count in order to keep the application easy to use.

I haven’t personally used Simplelog yet, but I think the user interface looks clean and easy to understand, and I like the fact that it is geared to have less features on purpose.

Simplelog also gets some good karma since half of any donation to the project is given to the National MS Society.

Radiant

The Radiant CMS is a true CMS that strives to manage content pages first and be a blogging system second. One of Radiant’s biggest features is a dynamic page hierarchy. You can easily create root pages and add on child pages. The admin UI also makes it easy to see how the site is structured. Radiant also allows for content snippets and layouts for reuse to make a site as easily maintainable as possible. Radiant also uses the Radius tag system to add ERB-like scripting into pages.

Radiant’s other big feature is behaviors, which are ways to make a page behave in a certain way. The default archive behavior supplied makes it easy to quickly add a blog to a site powered by Radiant.

I’d recommend people use Radiant when they have a lot of non-blog content for a site and don’t need a blog to be the focus of the site.

Comatose

Comatose is a micro CMS plugin for Rails by Matt McCray. It supports a dynamic hierarchical page structure just like Radiant, but assumes you will build your site around Comatose and extend it to suit your needs.

I’d recommend Comatose for Rails developers who either have an existing site they’d like to add a CMS to with minimal hassle, or for someone who doesn’t want to have to extend the larger blogging systems to fit their needs.

The Final Verdict

I’d like to be able to declare one of these five applications the “winner”, but they all serve such different niches that it is difficult to directly compare them. My best advice is to decide what kind of site you are looking to build and choose your Rails blogging system appropriately. From the full-featured Typo to the do-it-yourself plugin nature of Comatose there is something for every level of Rails developer.

Radiant CMS, Managing Web Sites on Rails

Managing the content of a web site is still a painful task, more that 10 years after the web took of. Web site management is an holy grail and Content Management Systems (CMS) have try to fill the gap without much success…

The problem is that mostly all Open Source CMS you can find are generally too complex. They want to achieve everything possible with an unique system. That leads to too many screens, too many options and too many plugins… Non expert people just can’t use those geeky systems and it’s very hard to train people on how to use the admin interface…

Here comes Radiant, an Open Source CMS made with Ruby on Rails. Radiant is more pragmatic and adopted a “less is more” philosophy. It focus on managing medium-size web sites with a page-centric approach that most people are used to.

The project is in early stages of development but already useable. It been able to power a web site I’m building in only 1 hour. I’ve put all my content into Radiant, pages, templates, styles and so on. Everything is in the system and I can manage it right from the administration interface. Neat!

The administration interface included with Radiant is simple enough for people to understand it… One of the real value of Radiant.

Concerning development, I needed a French localized interface for my web site and I’ve been able to submit a patch in no time. Since Radiant is written with Ruby on Rails, it’s quite extensible and you can use any plugin developed for Rails such as GLoc.

Top 20 Ruby on Rails Content Management Systems CMS Tools

Ready for the Top 20 Ruby on Rails CMS?

This article contains a breakdown of all known (to me) Ruby on Rails content management systems to date. It is revised from its previous version, fixing a few inconsitencies and adding two new content management systems to the list!

It has continually been asked in the "rails" community about the quality of Rails cms systems, and several different groups/individuals have branched off independantly to create a rails cms. Some are trying to make the most ajaxified cms with all the bells and whistles, while others seem to be focussing on feature sets and plugin modules.

People are trying to rapidly deploy applications and with rails as the promised magic potion for doing so, CMS development is on the rise. Before I break down all the CMS I'm aware of in the rails community, I want to say this. I am dissapointed in seeing less cooperation between independent cms projects. The one thing that did brighten my day was seeing that Ajax Scaffold and Streamlined are working together. If more people were making active contributions to these projects, and these project leaders were coordinating their efforts with eachother, a full featured and fleshed out rails cms would come about in an expontentially less amount of time than it is going to take at the current rate.

And now.. drumroll... Here's a breakdown of some of the rails cms advertised as of August, 2006.

Radiant, the minimalistic CMS for Rails

Radiant is a no-fluff content management system designed for small teams. It's similar to Movable Type or Textpattern, but is more than a blogging engine. It's a Web developer's CMS with just enough features to make it worthwhile. We've intentionally left the kitchen sink out!

Radiant CMS Features

  • An elegant user interface
  • The ability to arrange pages in a hierarchy
  • Flexible templating with layouts, snippets, page parts, and a custom tagging language (Radius: http://radius.rubyforge.org)
  • Special page-oriented plugins called behaviors
  • A simple user management/permissions system
  • Support for Markdown and Textile as well as traditional HTML (it's easy to create other filters)
  • Operates in two modes: dev and production depending on the URL
  • A caching system which expires pages every 5 minutes
  • Built using Ruby on Rails (which means that extending Radiant is as easy as any other Rails application)
  • Licensed under the MIT-License
  • And much more...

Live demo of Radiant can be found here.

Typo Blogware

Typo is a blogging tool that can be used as a CMS with some minor hacking. It is designed out of the box as a blog that runs on ruby on rails and it even includes some helpful ajax libraries. It's under constant development as well, so give it a chance if you are looking for a very simple cms system with just articles.

Mephisto

Mephisto is another brand of blogware from rails written by technoweenie (that's his name). It seems more accustomed for sites that don't appear like blogs, but actually are! You know.. what are they called again.. websites!

To download mephisto, you just do:

svn co  http://svn.techno-weenie.net/projects/mephisto/trunk your_directory

A quick install of mephisto is as follows:

Quick Install 1. Create a database named mephisto (or one of your choosing). 2. Copy config/database.example.yml to config/database.yml 3. Edit database.yml and set your database credentials. 4. Upload the entire mephisto directory to your webserver. 5. Create your log directory MEPHISTO/log 6. Run rake db:bootstrap from a terminal of some sorts 7. Login to your administration at http://domain.com/admin with the username: admin and password: test

Streamlined for Rapid Rails Deployment (cms tool, not a cms)

Streamlined is becoming increasingly powerful as people begin to understand better what it is. It is essentially a framework "ontop of the already existing Rails framework" that assists in rapid deployment of your applications. Here's what the creators have to say about it:
Streamlined is a framework for rapidly developing CRUD applications using Ruby and Rails. With Streamlined, we are trying to accomplish:
  • provide the same declarative ease-of-use to the view layer that ActiveRecord provides for the data layer
  • automatically manage relationships in the view layer so that users don't have to keep writing the same relationship management code
  • replace the default scaffolding of Rails with more robust, useful and meaningful management screens
  • provide a meta-framework which allows you to assimilate different Rails plugins and generators
If you already have a database schema, Streamlined's intent is to allow you to create a usable, robust, and above all, customizable administrative interface for it in five minutes or less.

Rubricks - Ajax bells and whistles

Rubricks is probably one of the most feature-enhanced publicly available rails cms I've seen. I played with it extensively and even used it on a project in its early stages. It has since then been continually developed by a team of 6 Japanese developers. The current version as of this post for Rubricks is 0.4.1. It includes english and japanese versions of the system and some of the documentation is in english also.

Rubricks features include:

  • RSS Reader
  • News reader
  • CMS Search System

Ajax Scaffold Generator (cms tool, not a cms)

  • Generates a production ready, fully styled, interface for managing models
  • Generates valid XHTML and CSS.
  • Guaranteed to work on Firefox 1+, IE 6+ and Safari 2+
  • Gracefully degrades when Javascript is not available
  • Designed to be used as a Rails component so you can easily create an admin console by dropping in multiple scaffolds.
  • Released under the MIT License, the same one as Rails itself, so you can use it freely in your commercial applications.

Check out Ajax Scaffold here.

Eribium

I'm not completely clear on the features available in Eribium because I believe the website is poorly laid out. However, I am aware the author is working on some very interesting modules such as Juggernaut described later in this article. I will say that you should have a look at the Eribium CMS site and see if it fits your needs.

RCMS-0.3

RCMS is so early in its life, I'd almost venture to say, "be careful". That isn't to say it isn't worth trying out. Currently it's a very minimalistic cms with theming capabilities. It's worth a look just to see what they've done so far.

Admin Pages CMS with Tree Tiers

The Admin Pages CMS (if that really is its name) is worth a look but hardly production worthy. It has some concepts that people may want to copy for their own but I really don't see this system up to par as something you could use without considerable work required.

Spread CMS

The Spread CMS looks very nice! From the screenshots that is. I couldn't find a downloadable version but that may change in the future. I think this CMS has a ton of potential and I'd love to see more people help them further develop it. It really is designed nicely.

Here is the Spread CMS Trac.

Pimpki - Personal Information Manager

Pimki 1.8 is a PIM (Personal Information Manager) loosely based on Instiki's Wiki technology. This is the place to dump your brain, organise your thoughts and Get Things Done. The ease of use and immediacy of a wiki combined with extended view to slice and dice the data give you a unique power to store, manage and retrieve all loose bit of information in your life.

There are a lot of features added over a regular Wiki, features that only make sense when you view it as a personal application and not so much as a group oriented application. See below for a full list of goodies.

RealApps CMS - A Dead Simple CMS

RealApps is a very simple design for a CMS on Rails. t's implemented as a plugin you can download, which then generates scaffolded code for administering your content. It's designed to be a starter system for small sites, and of course you can build on it as you want. It's also a good place to look if you want to see how generators and plugins are implemented.

CMS Compontent - DateBocks 2.0

DateBocks is growing very popular amongst rails users, as a pure JavaScript library using DHTML and advanced date parsing to generate the date based on the grammar passed. The purpose of DateBocks is to simplify the date entering process using common terms we are all familiar with.

Ruby on Rails is the development framework of choice. Future implementations in such core JavaScript libraries as the Dojo Toolkit, YUI, GWT, and others are likely to follow.

CMS Component - ClockingIT

ClockingIt is a task-listing, time-tracking web application built in Ruby on Rails. Here you can download ClockingI.

ClockingIT Features include:

  • unlimited projects, tasklists and tasks
  • milestones and due dates
  • time tracking and reports
  • project timeline
  • multiple user accounts
  • ajax and comet interface

Juggernaut - A Rails Plugin (not a cms)

Juggernaut for Ruby on Rails initiates a flash xmlsocket between server and browser allowing real time communication between the two. This opens the door to all sorts of functionality such as chats and collaborative wikis/cms. The open socket connection allows the server to 'push' base64 encoded javascript to the browser which is subsequently decoded and evaluated. Juggernaut provides a Comet interface via flash to web-apps.

Juggernaut Features:

  • Allows a real time connection with a client - Rails can literally push javascript in real time to the client which is then evaluated.
  • Push server - written in Ruby.
  • Integrated, as a plugin, into Rails.
  • Subscribers can subscribe to multiple channels, and broadcasters can broadcast to multiple channels. Channels are sent as a base64 encoded array - from the session.
  • Uses Flash 6 - installed on more than 95% of computers.
  • Supports all the major browsers (uses fscommand): Firefox 1+, IE 6+ and Safari 2+.

Comatose - CMS Plugin App

Comatose 0.6 is out! Here’s a quick summary of the new stuff:

  • ComatoseController has been split into ComatoseController and ComatoseAdminController
  • Both controllers extend your ApplicationController
  • The views, stylesheets, and javascripts have been updated and renamed to reflect the controller change
  • Configuration via Comatose::Options class
  • Liquid is the default text processor
  • Support for named routes
  • A Getting Started Guide
  • Pages now have a created_on field
  • Bug-fixes

Conclusion of CMS Breakdown

As you can see, there are a number of ongoing Ruby on Rails CMS projects. Some of them are moving along faster than others, and a few have not even got off the ground yet: Railfrog or become public such as Rubish by Kyle Shank. That doesn't mean they won't however.

One important thing to keep in mind when selecting your rails CMS is if you need a system that is going to be continually developed, or if you simply want their skeleton to build on. Some of these systems are being developed quickly such as Rubricks and Radiant and that is worth considering when you make your choice.

There will always be new CMS components and systems popping up. My final plea after writing this is that the developers working on these systems cooperate with one another and strive to share their goals and vision so that it can benefit the community as a whole and increase the maturation of Ruby on Rails CMS systems into their next generation.

Sunday, July 8, 2007

CMS blues

Somehow CMS systems always have disavantages (even the ones I wrote myself e.g. BackStream's ECMS :) ). They are always of one or more of too proprietary, too large, too strict, too flexible, not multilingual, without workflow, too data oriented (instead of task oriented), too hard to configure, too hard to program, too slow, too ugly URLs, too etc. etc.

I am curerntly looking into a Ruby on Rails CMS called Radiant. So far it has the following pros:
- it is nice to look at
- it is still small
- it pretty powerfull while still easy to follow (possible thanks to Ruby)

It still needs a lot of work. For example there is currently not even support for images and stylesheets. Still the way things are structured make me think this CMS will come a long way towards the ultimate CMS. Lets spend some more time on this...

Radiant CMS

One of the hardest parts of designing a website is figuring out how to manage the content – the text, images, graphics, and various other elements that make up a website. It’s usually a trade-off between “how easy can we make it” and “how powerful do we need it”. In the past, I’ve hacked blogging engines like Typo and WordPress into pretty good content management systems. But the problem with blogging engines is that they’re not meant for managing web pages – a problem when you have a site with lots of pages.

Because of this, I’m on a constant quest to find a good content management system (CMS). Along the way I started a CMS StikiPad to try to gather others’ opinions about which CMS systems are best suited for different purposes. I even created my own CMS (albeit a very specialized one for missionaries) called CrossConnector

But ever since I started using Ruby on Rails, I figured it would only be a matter of time before somebody comes out with a really good CMS written in Rails. I’ve been watching some very promising CMS projects, and I recently had the pleasure of taking Radiant for a test drive with a real project.

This an overview, albeit a fairly superficial one, of how I set up Hawkins Cosmetic Dentistry using Radiant.

The family tree

radiant-index.jpg

Radiant is very family-oriented, and follows a hierarchical model. Each page can have any number of children, and children can have their own children and so on. To add a new page, just find the parent level of the page you’d like to add, and click “Add Child”, and a new baby page comes out.

All pages originate from a single top-level page, and properties of this great-grandparent page can propagate to all the children and grandchildren unless they are overridden by a particular page. If a property is overridden by a page, that page’s new properties will descend to its own children. Got that? It’s okay, I’ll explain more about that when I talk about Navigation.

radiant-edit.jpg

Radius Tags

Radius tags take some getting used to, but really give you a lot of flexibility to build templates without exposing any programming code. Radius tags look a lot like XML tags, and if you can write or understand XHTML or XML, then you should have no trouble figuring out Radius. Very briefly (Radiant’s Trac repository has good documentation on Radius tags), Radius tags look like , where “something” is a command that is passed to the Radiant CMS. Notice that, like XML, the Radius tag has to be closed with a ”/” character. Similarly, Radius tags can enclose other tags, like this: , which would output a list of links for a page’s children.

Page Layout

Radiant gives you a lot of flexibility to create different layouts for different purposes. For this site, I created two layouts: “Normal” and “Homepage”. Both are pretty similar, except that the Homepage layout has a different column configuration. I could probably have used just one layout to avoid repeating HTML code, but it’s very convenient to be able to choose which layout I want for each page. I could just as easily have created layouts called “two-column”, “three-column”, “upside-down”, and so on.

radiant-layouts.jpg

Navigation

To create the navigation, I used a combination of Snippets and Page Parts.

Header Navigation

Take a look at Listing 1, and notice the Radius code inside the CSS Style tag. For this site, we needed to highlight both the current page and the current section. To do this, I created a Snippet called “screen.css”, which uses regular CSS mixed with Radius tags to define a special CSS style based on the current page. In addition, I created a page part called “top_menu” in each of the five main pages – “Home”, “About Us”, “Cosmetic Dentistry”, “General Dentistry”, and “Locations”.

Thursday, July 5, 2007

Radiant Photo Gallery with lightbox Effect

Gallery

Created by: Andrea Franz - bigchieflabs.com/blog/
Version: 0.5.2

The Gallery extension allows to manage galleries of images.
Features:

* Image/file upload (or retrieving by url)
* Import (FTP)
* Images/files sorting (drag-n-drop)
* Automatic thumbnail generation for images.
* Image crop (ALPHA version)

Installation

Get the latest version of gallery extension with darcs:

darcs get http://darcs.bigchieflabs.com/radiant/extensions/gallery/

…or download it in your vendor/extensions folder from here:

http://darcs.bigchieflabs.com/radiant/extensions/gallery/gallery.tar.gz

From your Rails app root type theese commands:

rake radiant:extensions:gallery:install
rake radiant:extensions:gallery:example

The second one creates an example of GalleryPage you can find at "your_site_url/gallery-example". Check it out after creating a new Gallery in your radiant administation panel.
Dependencies

Gallery extension requires RMagick

Usage
List all gallery in a Gallery page

Create a new page and set Gallery as PageType.


Galleries



List gallery items in a simple page

You can list all the items of a gallery specifying its id:

Direct access to gallery items

You can access directly to a gallery item, but you must specify 2 attribute. The gallery id, and the position of the item:


Lightbox style

In order for lightbox to work you have to include this Radiant tag in your layout HEAD section:



Now you can use the tag in a simple page specifying the gallery id:



…or in a Gallery page:


Galleries


Enjoy!

What's New in Radiant 0.6

You may have heard through the grapevine that Radiant 0.6 is to be released this Saturday. If not, here’s your notice!

UPDATE: OK, so we didn’t release yesterday. John and I were up late adding generators for extension models and controllers and trying to fix the fixtures issue. Hopefully we’ll release today (Sunday 4/22).

Since there are a lot of changes since the release of 0.5.2 (250+ commits), I will summarize them for you here. Most of the changes affect developers more than end-users, but there are still some major differences that could slip you up when upgrading or building a new site.

This is pretty long, so be prepared for a big read after the jump.
Changes for End Users and Site Managers
Installation/Upgrade

* script/setup_database is gone. To create a new Radiant site, go to the directory where you created an instance, and run rake production db:bootstrap. This will run the wizard that builds the database for you. Make sure you have edited or created config/database.yml already.
* If you are upgrading from an older version of Radiant, BACKUP YOUR DATABASE and then run rake db:migrate. If you used custom behaviors, try to find and install extension replacements for them before you attempt to upgrade.
* To upgrade an instance with the most current files, run rake radiant:update. This will copy the latest stuff from vendor/radiant or the gem.

Database

* sqlite3 is now better supported; Postgresql and MS SQL Server should work too.

Performance

* Radiant now supports “X-Sendfile” headers on cached pages, which should speed up the page delivery for some web servers in proxy configurations.
* Radiant now supports “304 Not Modified” status codes, which will prevent the client for downloading pages that have not changed from the version in the client-side cache. The client must support this status code, of course.

Radius Tags

* r:children:each, r:children:first, and r:children:last now all accept the same ordering and limit attributes and have the same defaults.
* Snippets are now responsive to global context via the r:page tag. This means that any tags inside r:page will refer to the page currently being rendered, i.e. the page requested, not the local contextual page via tags like r:children:each, etc. This is most relevant to recursive snippets like the sitemapper example.
* r:navigation now uses the pipe character (”|”) to delimit URLs in the urls attribute rather than the semi-colon.
* r:date now accepts a “when” attribute that specifies which attribute of the page to render. Valid values of the attribute are published_at, updated_at, created_at, and now. now renders the current date/time, regardless of which page is being rendered.
* r:cycle is a new tag that creates or continues a global cycle in your page. A cycle basically emits text you define based on the number of times you’ve called it. This is nice for alternating the style of rows in a table, etc.

Admin and Page-editing Interfaces

* Page parts/tabs are now preserved in the order you created them.
* Tag documentation – there’s a popup in the page-editing interface that will show what tags are available for the current page, and some descriptions of how to use them. The popup includes a live-search so you can find a certain tag definition by typing in the box.
* Filter documentation – there’s a popup in the page-editing interface that will show how to format your content for the currently selected filter.
* All items in the administration interface (users, pages, snippets, layouts) have optimistic locking. That is, if someone else saves something while you have it open, you will receive a warning to reload the item you are editing.

Extensions

* Any installed extensions can be en/disabled and de/activated via the administration interface. Click “Extensions” next to “Users” if you’re an administrator.

Changes for Developers
Systemic / Base

* Radiant 0.6 is frozen to, and includes in its distribution, Rails 1.2.3 – you don’t have to have Rails already installed! This has many implications and I suggest you investigate the many articles that detail the differences between Rails 1.2 and 1.1.
* The loading/boot-up process for Rails has been dramatically changed to allow for extensions. Be wary about any changes you might have made to config/environment.rb in the past, or any changes made via plugins. Try to move your changes into extensions instead.
* The Radiant codebase can be run out of the vendor directory as well as from the gem. This means (only in instance mode):
o To keep up on the latest trunk or branch code, you should checkout into vendor/radiant rather than doing a plain checkout.
o To freeze to a specific version of Radiant, either checkout the revision you want, or use the rake radiant:freeze command, which comes in two flavors: radiant:freeze:gems and radiant:freeze:edge. radiant:freeze:edge works just like rails:freeze:edge, with support for TAG and REVISION.
o To return to fluid use of the system gems, run rake radiant:unfreeze.
* To quickly rollback and run all Radiant core migrations, use rake db:remigrate. THIS WILL DESTROY ANY DATA IN YOUR DATABASE! USE WITH CAUTION.
* Radiant::Config (useful in extensions) now supports boolean values. Add a question mark to the end of the key (e.g. Radiant::Config["boolean?"]) if it should return boolean values (true/false).

Performance

* Caching no longer stores the headers and body in the same file. This allows Radiant to support X-SendFile headers.
* Cache files are stored in the /cache directory in your instance, not tmp/cache. (not sure if this was changed or the same, but good to know nonetheless).
* Pages can set a specific cache timeout via response.cache_timeout. This should probably be set in the process method.

Assets

* All images and stylesheets that support the Radiant administration interface have moved to images/admin and stylesheets/admin respectively.

Extensions

This is it! The Big One. If you want to change the way Radiant works, use an extension. I’ve detailed some aspects of extensions in my previous presentation, but here’s the nitty-gritty.

* Extensions can be generated in instance mode. This is important if you want keep your install clean.
* Behaviors are no more. If you want to change the way a page behaves, subclass the Page model. Make sure your new subclass is accessible by naming the file it’s defined in using the Rails naming convention. You should probably also ‘declare’ it in the activate method of your extension to be sure it’s loaded. For example:

# app/models/hello_world_page.rb
class HelloWorldPage < Page
def render
"Hello, world!"
end
end

# hello_world_extension.rb
def activate
HelloWorldPage
end

* Pages no longer have support for the ‘config’ part by default. If you need this functionality in your Page subclass, you must implement it yourself. You can look at MailerPage in the mailer extension for a sample implementation.
* You can add tabs to the administration interface using admin.tabs.add in the activate method of your extension. Here’s some examples:

admin.tabs.add "Assets", "/admin/assets", :after => "Layouts"
admin.tabs.add "Welcome", "/admin/help", :before => "Pages"
admin.tabs.add "LDAP", "/admin/ldap"

* Migrations defined in your extensions can be run via rake db:migrate:extensions to migrate all extensions, or rake radiant:extensions:extension_name:migrate for a specific extension named ‘extension_name’. Extension migrations are managed independently of the main Radiant migrations, having their own version numbers, etc.
* Extensions can define custom Rake tasks in lib/tasks. Some tasks are provided for you when you generate your extension.
* Extensions can override the way SiteController invokes page processing by overriding SiteController#process_page.

Radius Tags

* Global tags are now defined in the Page model (the defaults via inclusion of StandardTags). To define your own global tags, open up the Page model and either define with a ‘tag’ block or include your own module that itself includes Radiant::Taggable. Example:

# app/models/hello_world_tags.rb
module HelloWorldTags
include Radiant::Taggable

tag "hello" do |tag|
"Hello, world!"
end
end

# hello_world_extension.rb
def activate
Page.class_eval { include HelloWorldTags }
end

* Tags now have their own documentation DSL. Precede the tag definition with desc %{ My tag documentation }. Your documentation will be formatted with Textile. See standard_tags.rb for examples.

Testing

* Exceptions thrown inside Radius tag definitions now bubble up during tests.
* Tests work under sqlite3.
* Test tasks work in instance mode.
* Extensions can define their own “test helpers” in test/helpers. Test helpers are modules that can be easily mixed into your tests – with the test_helper :helper_name command – to add custom assertions, etc. There are a wealth of test helpers included with Radiant.
* Extensions can define their own fixtures in test/fixtures. Use extension_fixtures :model_names to use them in a test case.

Last-minute stuff

The core team has also been given until Saturday to commit any last minute changes. I’ll add them below as they come up. I will not include updates to individual extensions, so you’ll have to follow those yourself. As Gabriel Lamounier always says in his emails to the list, “Be Radiant!”

* r371 (Developers) – You can modify the ‘meta’ (more/less) area and the area just above the buttons on the page-editing interface. The ‘meta’ area can only display fields on the Page model and uses a specialized Hash format, but the buttons area can display any accessible partial. I called this changeset “facets-lite”.
* r372 (End users) – The slug/breadcrumb auto-updating should be responsive to mouse and other events as well as just typing.
* r373 (Bug fix)
* r374 (Bug fix)
* r377 (End users) Make r:date tag use “for” attribute instead of “when”.
* r378, 383 (Developers) Add extension controller generator.
* r379 Updated CONTRIBUTORS.
* r380, 383, 384, 387 – Add extension model generator.
* r381, 382, 385, 386, 393, 394, 395 (Developers) Fixing extension fixtures issues. The generated test_helper.rb should work out-of-the-box, but you may need to update existing extensions.
* r388-392 (End users) Fixed IE6 CSS bugs.
* r393-395 (Developers) Extensions use the standard fixtures command in tests now, rather than extension_fixtures. Generated tests and test helpers are adjusted to reflect this.
* r398 (End users) Denied page saves due to the optimistic locking feature should retain your changes in the interface rather than reloading.
* r400 (Developers) Extension tests can be run in instance mode.
* r403 (Developers) Extension controller and model generators can be run in instance mode.
* r404 (Developers) Extension tests are run in isolation rather than together.

UPDATE 4/25: Those are all the significant changes up to the tagging of the 0.6 release. I’m sure there will be bug fixes in the near future as they are revealed.

Is this post help u.