Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Enrichments (e9s) module for a pluggable CMS frontend

This branch is 0 commits ahead and 34 commits behind master

Fetching latest commit…

Cannot retrieve the latest commit at this time

README.textile

Rich-CMS

A Rails gem for a pluggable CMS frontend

Introduction

Rich-CMS is a module of E9s (http://github.com/archan937/e9s) which provides a frontend for your CMS content.
Please check out an online demo of Rich-CMS at the Rich-CMS or E9s page at http://codehero.es.

Installation

Using Rich-CMS in Rails 3

Add Rich-CMS in Gemfile as a gem dependency:

  gem "rich_cms"

Run the following in your console to install with Bundler:

  bundle install

Using Rich-CMS in Rails 2

Add Rich-CMS in environment.rb as a gem dependency:

  config.gem "rich_cms"

Run the following in your console:

  sudo rake gems:install

Fancy contributing to Rich-CMS yourself?

1. Create your own Rich-CMS fork https://github.com/archan937/rich_cms/fork
2. Clone your Rich-CMS fork repository

  git clone git@github.com:<your_username>/rich_cms.git && cd rich_cms

3. Setup your environment in order to use the GemSuit tests provided within Rich-CMS

  gem install gem_suit && suit fit -v

4. Run tests (e.g. the GemSuit integration tests) as follows (make sure you have Firefox installed)

  suit -v

Note: Running the GemSuit integration tests take in a couple of minutes as it will test 10 different environments:

Every GemSuit integration test starts with a blank Rails application, runs the required Rails generators and tests the front-end within Firefox with Capybara

  • Rails 2Authlogic authenticated with Moneta in-memory storage
  • Rails 2Devise authenticated with Moneta in-memory storage
  • Rails 2non-authenticated with Moneta in-memory storage
  • Rails 2non-authenticated with Moneta ActiveRecord storage
  • Rails 2non-authenticated with Rich-i18n forgery (combined keys)
  • Rails 3Authlogic authenticated with Moneta in-memory storage
  • Rails 3Devise authenticated with Moneta in-memory storage
  • Rails 3non-authenticated with Moneta in-memory storage
  • Rails 3non-authenticated with Moneta ActiveRecord storage
  • Rails 3non-authenticated with Rich-i18n forgery (combined keys)

To run unit tests in both Rails 2 and 3

  suit test unit -v

Note: All tests are running successfully using RVM in combination with ruby-1.8.7-p334, ree-1.8.7-2011.03 and ruby-1.9.2-p180.

5. You can list all the command line commands with the following

  suit help

6. Start either of the dummy Rails applications for development purposes

  suit s

Note: This runs the Rails 3 dummy app

  suit s -r2

7. Open the Rails application in your browser with http://localhost:3000/cms and log in with paul.engel@holder.nl and testrichcms.

8. Get on programming and send your pull request! ;)

Note: For more information about GemSuit, please visit its Github page.

Use the provided Rails generators

Rich-CMS requires one entity:

  • A model used for CMS content storage (which is powered with Moneta)

For (optional) authentication, Rich-CMS requires one of the following:

  • A Devise authenticated admin model
  • An Authlogic authenticated admin model

Fortunately, Rich-CMS is provided with two Rails generators with which you can generate those entities.

CMS admin

In Rails 3

Run the following in your console:

  rails g rich:cms_admin -m

Note: At default, it will create a (Devise powered) User model, the CreateUsers migration and it will configure your routes.

You can alter the class name as follows:

  rails g rich:cms_admin CodeHeroes::User -m

Note: Both generators have the -m or --migrate option which runs rake db:migrate after creating the files.

Using Authlogic

You can use Authlogic by specifying the -a or --authlogic option:

  rails g rich:cms_admin CodeHeroes::User -a -m

Note: As mentioned earlier, Devise is the default authentication logic. Having that said, you can explicitly specify Devise with the -d or --devise option.

In Rails 2

Run the following in your console:

  script/generate rich_cms_admin -m

Attention: The Devise Rails generator code is (practically) a copy of the Devise 1.0.9 generator code. For there are problems calling the original Devise generators in Rails 2. See also this Stackoverflow issue.

CMS content

In Rails 3

Run the following in your console:

  rails g rich:cms_content -m

Note: At default, it will create the CmsContent model and CreateCmsContents migration. You can alter the class name with the following:

  rails g rich:cms_content CmsItem -m

In Rails 2

Run the following in your console:

  script/generate rich_cms_content -m

In case you have used the Rails generators, you can skip the Create required entities manually and go straight to Render Rich-CMS in your views.

Create required entities manually

Specify the authentication mechanism

Rich-CMS can be used without an authentication mechanism (which is the default by the way), you just have to open “/cms” in your browser and you are ready to go. But it is common to have authentication and Rich-CMS thus supports Devise and Authlogic.

Provide the authentication logic as a symbol (e.g. :devise) and the authenticated class like this:

  Rich::Cms::Auth.setup do |config|
    config.logic = :devise
    config.klass = "User"
  end

The following specifications are optional as Rich-Cms uses defaults:

  • :inputsdefault: [:email, :password]
    The attributes used for the login panel of Rich-CMS
  • :identifierdefault: based on inputs
    The method used for displaying the identity of the current Rich-CMS admin (this is the first entry of inputs, so usually :email)
  • :current_admin_methoddefault: based on klass
    The controller method used to retrieve the current Rich-CMS admin (e.g. current_user when configured User as authenticated class)

Create your CMS content class

The storage mechanism of Rich-CMS content is powered with Moneta. Doing this gives you the freedom to choose the storage engine you want to use (e.g. Mongo, Redis, Tyrant, Memcache, DataMapper, ActiveRecord and many others).

Go to app/models and create a file like the following (e.g. called Content):

  class Content
    include Rich::Cms::Content
    storage :memory
  end

In short: include the Rich::Cms::Content module and specify which storage engine you want to use. And that was it actually!

Depending on your choice of storage engine, you will need to add some extra specs. See the Moneta repo for more documentation.

There are also additional class methods which you can call of course:

Identifiers

As already mentioned, Rich-CMS uses Moneta for an unified key / value store interface. But you can still let the key (identifier) consist out of multiple segments. Rich-CMS will concatenate the segments with a default delimiter ; in the specified order within the class definition.

  class Content
    include Rich::Cms::Content
    storage :memory
    identifiers :locale, :key # <= Rich-CMS will concatenate the identifier in this order
  end

As an example, :locale => :nl, :key => "application.index.header" will result in "nl:application.index.header". You can change the delimiter by overriding the delimiter method:

  ...
    identifiers :locale, :key

    def delimiter
      ";" # <= {:locale => :nl, :key => "application.index.header"} will result in "nl;application.index.header"
    end
  end

Note: You might want to check out rich/i18n_forgery.rb for further documentation (especially the protected method identity_hash_for).

Configure

When it comes to rendering Rich-CMS content in the front-end, there are three points of attention regarding the Rich-CMS content:

  • the CSS class of the Rich-CMS content class in question
  • the Javascript function called before showing the edit form of a content item
  • the Javascript function called after updating a content item

Rich-CMS uses the CSS class to match the corresponding Rich-CMS content class in the front-end. At default, the CSS class is based on the underscored content class name prefixed with "rcms_".

So Content will result in “.rcms_content” and Translation in ".rcms_translation". When the class name starts with Cms, it will be ignored. For instance, CmsContent will result in ".rcms_content".

At default, the before_edit hook is not assigned and the after_update hook is assign to the Javascript function Rich.Cms.Editor.afterUpdate.

A few examples

A simple Rich-CMS class:

  class Content
    include Rich:Cms::Content
    storage :memory
    css_class "custom_css_class"
  end

A slight more advanced example of a Rich-CMS class:

  class Translation
    include Rich::Cms::Content
    storage     :active_record, :key => :custom_key_column, :value => :custom_value_column
    identifiers :locale, :key

    configure do |config| # for a custom CSS class: configure "translation" do |config|
      config.before_edit  "Rich.I18n.beforeEdit"
      config.after_update "Rich.I18n.afterUpdate"
    end
  end

Note: Please notice that Translation uses ActiveRecord as storage engine

Render Rich-CMS in your views

Alter your layout

Add the following line at the beginning of the <body> tag:

  <body>
    <%= rich_cms %>
    ...
  </body>

Use the Rich-CMS helper method

Rich-CMS requires a rendered DOM element provided with meta data of the content entry. Fortunately, you can call a helper method to render Rich-CMS content tags. It accepts the following arguments:

  • css_class – The CSS class of the corresponding Rich-CMS content class
  • identifier – The identifier of the Rich-CMS content entry (use a hash when dealing with a combined identifier)
  • options – Options used to customize the rendered content tag

Please take notice: The CSS class is only obligated when having more than one Rich-CMS content class defined!

  >> key = "hello_world"
  => "hello_world"
  >> ActionView::Base.new.rich_cms_tag(key)
  => "<div class='rcms_content' data-store_key='hello_world' data-store_value='Hello world!'>Hello world!</div>"

Note: There is only one Rich-CMS content defined called Content

When using a combined key for content identification, just call it as follows:

  >> ActionView::Base.new.rich_cms_tag({:key => key, :locale => I18n.locale})
  => "<div class='rcms_content' data-store_key='nl:hello_world' data-store_value='Hallo wereld!'>Hallo wereld!</div>"

Note: In this case, the (only) Rich-CMS content class was defined with identifiers :locale, :key

When having multiple Rich-CMS content classes defined, pass the corresponding CSS class:

  >> ActionView::Base.new.rich_cms_tag(".rcms_translation", {:key => key, :locale => I18n.locale})
  => "<div class='rcms_translation' data-store_key='nl:hello_world' data-store_value='Hallo wereld!'>Hallo wereld!</div>"

The (optional) options hash can contain the following keys:

  • :asdefault: auto-determined :string or :text
    Specify the input type shown in the edit form (:string for an input text, :text for a textarea and :html for a WYSIWYG HTML editor).
  • :tagdefault: auto-determined :div or :span
    The HTML tag used for content items. You can also specify :none as value: in that case, no HTML tag will be wrapped around the content when no admin is logged in (essential in case the extra span will bork the layout).
  • :htmldefault: {}
    HTML attributes added to the content tag (e.g. :id, :class)
  • :localsdefault: nil
    When assigned a Hash, Rich-CMS assumes the content concerns a Mustache template and will render it with the passed locals.
  • :collectiondefault: nil
    When assigned an array, Rich-CMS assumes the content concerns a Mustache template and will render it with the (optional) passed locals and the attr_cmsable attributes.

Note: The WYSIWYG editor used is the jQuery based CLEditor

A few examples

  ...
  <%= rich_cms_tag ".rcms_content", "about_us_page", :as => :html %>
  <%= rich_cms_tag ".rcms_content", {:key => "welcome_text", :locale => I18n.locale}, :tag => :p %>
  <%= rich_cms_tag "application.index.welcome_text", :tag => :p, :html => {:class => "welcome", :style => "display: none"} %>
  ...

Note: The last statement passes when only one Rich-CMS content class is defined

A locals example

The Rich-CMS content used:

  header = Content.new :key => "welcome_header"
  header.value = "Hi, {{name}}!"
  header.save

The ERB template:

  ...
  <%= rich_cms_tag "welcome_header", :tag => :h1, :locals => {:name => "Vicky"} %>
  ...

The compiled HTML:

  <h1>
    Hi, Vicky!
  </h1>

A collection example

The Rich-CMS content and collection used:

  class AttrCmsableContent # probably < ActiveRecord::Base
    attr_accessor :name    # an attribute provided by ActiveRecord::Base
    attr_cmsable  :name
  end

  @collection = %w(Vicky Johnny Paul).collect{|name| AttrCmsableContent.new.tap{|c| c.name = name}}

  header = Content.new :key => "welcome_header"
  header.value = "Hi, {{name}}!"
  header.save

The ERB template:

  ...
  <ul>
    <%= rich_cms_tag "welcome_header", :tag => :li, :collection => @collection %>
  <ul>
  ...

The compiled HTML:

  <ul>
    <li>Hi, Vicky!</li>
    <li>Hi, Johnny!</li>
    <li>Hi, Paul!</li>
  </ul>

Rich-CMS in your browser

Open http://localhost:3000/cms, log in and start managing CMS content.

Customizing the after update implementation

The update action response of Rich::CmsController consists of JSON data regarding the updated content item. The response will be passed to the after_update Javascript function which contains a simple JSON hash. Its default is as follows:

  {"__css_class__": "rcms_content", "__identifier__": {"store_key": "hello_world"}, "store_value": "Hello world!"}

Note: __css_class__, __identifier__ and store_value are always provided in the JSON data.

When specifying a custom after update Javascript function, you probably want to acquire more information than provided in the default JSON data. You can customize this by defining the to_rich_cms_response method in the CMS content model class and the hash will be merged with the default response hash:

  class Translation
    include Rich::Cms::Content
    storage     :memory
    identifiers :locale, :key

    configure "translation" do |config|
      config.before_edit  "Rich.I18n.beforeEdit"
      config.after_update "Rich.I18n.afterUpdate"
    end

    def to_tag(options = {})
      super options.merge(:data => {:derivative_key => derivative_key})
    end

    def to_rich_cms_response(params)
      {:translations => Hash[*params[:derivative_keys].split(";").uniq.collect{|x| [x, x.t]}.flatten]}
    end
  end

The JSON data returned will look like:

  {"__css_class__": "translation", "__identifier__": {"store_key": "nl:word.user"}, "store_value": "gebruiker", "translations": {"users": "gebruikers"}}

Contact me

For support, remarks and requests please mail me at paul.engel@holder.nl.

Credit

This Rails gem depends on:

jQuery
http://jquery.com

GemSuit
https://github.com/archan937/gem_suit

Devise (optional)
http://github.com/plataformatec/devise

AuthLogic (optional)
http://github.com/binarylogic/authlogic

Moneta
https://github.com/wycats/moneta

SASS
http://sass-lang.com

Jzip
http://codehero.es/rails_gems_plugins/jzip
http://github.com/archan937/jzip

RaccoonTip
http://codehero.es/jquery_libraries/raccoon_tip
http://github.com/archan937/raccoon_tip

SeatHolder
http://codehero.es/jquery_libraries/seat_holder
http://github.com/archan937/seat_holder

CLEditor
http://premiumsoftware.net/cleditor/index.html

Contributors

ToDo’s

  • Add cache feature which uses the standard Rails cache
  • Provide web pages management (e.g. Rich-Pages)
  • Provide object management (e.g. articles and products)
  • Provide file uploads (e.g. images)
  • Check out compatibility with Devise 1.2 and 1.3
  • Provide better conventions for content rendering
  • Provide tools to use Textile, MarkDown

Enrichments

The all-in-one gem at – http://codehero.es/rails_gems_plugins/e9shttp://github.com/archan937/e9s

E9s modules

License

Copyright © 2011 Paul Engel, released under the MIT license

http://holder.nlhttp://codehero.eshttp://gettopup.comhttp://twitter.com/archan937paul.engel@holder.nl

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Something went wrong with that request. Please try again.