BrowserCMS Developer GuideThis guide covers how to use the tools that come with BrowserCMS to extend the functionality of your BrowserCMS site. The target audience for this guide is intermediate to advanced Rails developers. You should also read the Building a Site with BrowserCMS guide first as well, so you know how to create pages and put content on pages. This guide will cover:
1 Content BlocksContent Blocks are the basic unit used to add content to pages in a BrowserCMS site. There are several pre-built content types that are part of BrowserCMS core. The most commonly used one is the HtmlBlock content type, so we will use that as a starting example. If you look at the HtmlBlock model in BrowserCMS core, you will see that it is an ActiveRecord model, just like any model you create in a Rails application. It also calls the acts_as_content_block method, which just sets up several Behaviors on the model. One of the key Behaviors to understand is the Rendering behavior. Essentially what this behavior does is to allow a model (by model I mean a subclass of ActiveRecord::Base) to act as a mini-controller. This is the same thing conceptually as Merb Parts or Rails Cells, the only difference is that content blocks are models as well, so they have attributes that are stored in the database like any other model. In the BrowserCMS implementation, first the render method of the object is called, which is like an action. This is where instance variables are set. In the case of an HtmlBlock, there are no other instance variables to set up, so this step is just skipped. It is similar to a controller action that has no action method, it just goes straight to the view. The view is the render template, which is inside the cms/html_blocks directory in the view path. If you look in the render template, you will see all it does is print the value of @content_block.content. Content is the name of the property that holds the HTML content. 2 Creating A Custom Content BlockLet’s say that you are using BrowserCMS to build a website that will display a list of products. The first step would be to create a product content block. This will give the users of the CMS a way to create products. To create a product content block, in an existing BrowserCMS project, run the content block generator: $ script/generate content_block product name:string price:integer description:html You will notice that several things have been created:
If you look at app/models/product.rb, you will see that it is nothing more than: class Product < ActiveRecord::Base
acts_as_content_block
end The controller created at app/controllers/cms/products_controller.rb is equally empty: class Cms::ProductsController < Cms::ContentBlockController
end This controller is the controller used in the actual CMS interface. If you want to create a controller to use in the front end of your application, create a products controller that is not in the CMS namespace, so as not to conflict the CMS functionality. You will most likely not need to customize the CMS controller at all. The form partial at app/views/cms/products/_form.html.erb is the form that will be used when creating/editing the product content block in the content library of the CMS. The contents of this file will be: <%= f.cms_text_field :name %>
<%= f.cms_text_field :price %>
<%= f.cms_text_editor :description %> The form helper methods that are prefixed with cms_ will wrap that form helper with the CMS styling and extra functionality. The cms_text_editor will show the user a WYSIWYG editor for editing HTML. Additional CMS form builder extensions are covered in the BrowserCMS Designer Guide. The render template at app/views/cms/products/render.html.erb is the template that will be used to render the HTML when this product is placed directly on a page. It is also what is shown when you view a product within the content library of the CMS. As you will see in the next section, custom content blocks are often not placed directly on a page, instead the data of a product is displayed through a portlet. For this reason, a more informational display, similar to what is automatically generated by the generator, is what the render template often contains. Depending on what your content block is, you may want to place the content block directly on a page, in which case you would most likely customize the render template. 2.1 Attribute Types This section covers some of the different attribute types that Content Blocks can have. Each attribute represents a persistent column in the database, will appear as an editable control on the form.html.erb, and may also need to need to be configured on the contentblock itself.List of Content Types
2.1.1 Drop Down / Selects This is similar to the traditional ‘select’ helper, it renders a stylized select control which allows users to choose one item from a list.2.1.1.1 cms_drop_down In _form.html.erb:<%= f.cms_drop_down :category_id, Category.all(:order => "name").map{|c| [c.name, c.id]},
:prompt => "Select a Category", :label => "Category",
:instructions=>"Determines which category is used on the homepage." %> In product.rb: class Product < ActiveRecord::Base
belongs_to :category
end 2.1.2 Attachments Each content block can have a single file attachment, which will allow users to upload files into the content repository. After uploading, the file will be stored in a section within the CMS. Since sections determine permissions, this will allow you to control which users can access the file itself. Attached files will have their own versioning history, which will keep track of changes.2.1.2.1 cms_file_field This helper will create a stylized file upload file. An uploaded file will be associated with the content_block to which it is attached.In _form.html.erb (View) <%= f.cms_file_field :attachment_file, :label => "File" %> In product.rb (Model) class Product < ActiveRecord::Base
acts_as_content_block :belongs_to_attachment => true
end In create_products.rb (Migration) create_content_table :products do |t|
t.belongs_to :attachment
t.integer :attachment_version
end 3 Creating A Custom PortletOnce you have created the product content block and created a few products in the content library, you need a way to display them on a page. To do that, you will want to create a portlet. A portlet is used to display dynamic data on a page. A portlet is a content block. A portlet will typically perform some kind of database query in the render method and then render it’s view. One difference between a portlet and typical content block is that each instance of a portlet can have a unique template because that template is stored as data along with the portlet. Let’s generate a portlet to display the most recently created products: $ script/generate portlet recent_products limit:integer What you will see created is:
What you don’t see created is a migration. Portlets use the DynamicAttributes behavior in order to store associated values in the database without having to create custom database tables. What this means is that you can set and then store a value for any attribute for a portlet. So if you look at the form partial that was generate for this portlet, you will see this: <%= f.cms_text_field :name %>
<%= f.cms_text_field :limit %>
<%= f.cms_drop_down :handler, ActionView::Template.template_handler_extensions, :default_value => "erb" %>
<%= f.cms_text_area :template, :default_value => @block.class.default_template %> Every portlet instance has to have a name and that is stored in the main portlets table, but limit is stored in the portlet_attributes table. You could add anything to this form, such as <%= f.cms_text_field :foobar %>, and whatever the user enters for foobar would get saved with the portlet. If you look at the code for the portlet, you will see: class RecentProducts < Portlet
def render
# Your Code Goes Here
end
end As the comment says, you will want to fill in your application logic here. We’re going to get the most recent products and use the value the user entered for limit when they filled out the form. So edit the code to look like: class RecentProducts < Portlet
def render
@products = Product.all(:order => "created_at desc", :limit => self.limit)
end
end
<%=h @portlet.name %> This is simply a place holder, you should override this code with something similar to what you expect the user of your portlet to want to use. In this case, let’s go with: <ul>
<% @products.each do |product| %>
<li><%=h product.name %></li>
<% end %>
</ul> Notice that in the last paragraph I said “similar to what you expect the user of your portlet to want to use”. This value is simply the default starting point for the template of a portlet. The actual value is entered into the form when the user creates an instance of this portlet. If you look back at the form partial that was generated, you’ll see: <%= f.cms_text_area :template, :default_value => @block.class.default_template %> What this does is preload the template with whatever you entered into the render template. The user is free to change it as they see fit and even have different values from one portlet instance to the next. 3.1 Using File system templatesIf you do not want to have the portlet’s render template stored in the CMS content libary, but would rather have it just render the file from the file system, simply add this to your portlet code: class RecentProducts < Portlet
render_inline false
def render
@products = Product.all(:order => "created_at desc", :limit => self.limit)
end
end The key line being the <%= f.cms_drop_down :handler,
ActionView::Template.template_handler_extensions,
:default_value => "erb" %> This generates a drop-down menu for the user creating the portlet to select from the various portlet handlers installed in your Rails app. The default handler for portlet views is erb, but you could use alternative ones, like Haml. To do that, install the HAML gem and set the default_value to ‘haml’ like so: <%= f.cms_drop_down :handler,
ActionView::Template.template_handler_extensions,
:default_value => "haml" %> 3.2 Pre-selecting a handlerIf you would rather not have the user select the handler type, or if you are using the file system instead of storing the template code in the CMS, you can set the handler to a different value like this: class RecentProducts < Portlet
handler "haml"
render_inline false
def render
@products = Product.all(:order => "created_at desc", :limit => self.limit)
end
end The key line being 4 Page RoutesTODO |