MuckContents

Add contents to you website. News stories, blog posts and pages are all basically a page with content and a title. Muck content makes it easy to add this content to your website.

Gems

Note that all gems below should be installed automatically as a muck-contents has them set as a dependency.

sudo gem install muck-contents

Configure environment.rb

Paste in the following:

config.gem "will_paginate"
config.gem "authlogic"
config.gem "searchlogic"
config.gem "bcrypt-ruby", :lib => "bcrypt", :version => ">=2.0.5"
config.gem "acts-as-taggable-on"
config.gem "awesome_nested_set"
config.gem "friendly_id"
config.gem "sanitize"
config.gem 'paperclip'
config.gem 'tiny_mce'
config.gem "uploader"
config.gem "babelphish"
config.gem 'muck-engine', :lib => 'muck_engine'
config.gem 'muck-users', :lib => 'muck_users'
config.gem 'muck-activities', :lib => 'muck_activities'
config.gem 'muck-solr', :lib => 'acts_as_solr'
config.gem 'muck-comments', :lib => 'muck_comments'

Gem configuration and setup

In your app do

script/generate friendly_id
script/generate acts_as_taggable_on_migration
rake db:migrate

Setup

Global Config

Add the following settings to your global_config.yml file and change as is appropriate for your application. In particular be sure to set content_css to any css files you want to have show up in your tinymce editor.

# Content options
content_git_repository: "#{File.join(RAILS_ROOT, 'repo', RAILS_ENV)}"
enable_auto_translations: true
content_enable_solr: false
content_css: ['/stylesheets/reset.css', '/stylesheets/styles.css']

Initializer

Running rake muck:contents:sync will copy all the needed javascript, image and css files into your project. It will also create an initializer called mce_options.rb. That file contains two basic configurations for tinymce as well as examples on how to create templates. It is recommended that you don’t modify that file as it will be overwritten the next time you run rake muck:contents:sync. Instead use it as an example and create the desired tinymce setup.

User model

Add the following method to your user model and modify it according to your specific needs:

def can_add_root_content?
  admin?
end

This method determines who can add content to any url on your website. For example, if you type in www.example.com/a/test/page and that page does not exist, muck contents will automatically create a page for you at that location if the logged in user has a method can_add_root_content? that returns true.

Content models

Create the following models in your project. This let’s you add any other methods that you see fit.

class Content < ActiveRecord::Base
  # This assumes you are using GlobalConfig.  You can also set these values directly in the model
  acts_as_muck_content(
    :git_repository => GlobalConfig.content_git_repository,
    :enable_translations => GlobalConfig.enable_auto_translations,
    :enable_solr => GlobalConfig.content_enable_solr
  )
end

class ContentTranslation < ActiveRecord::Base
  acts_as_muck_content_translation
end

class ContentPermission < ActiveRecord::Base
  acts_as_muck_content_permission
end

Application controller

Add acts_as_muck_content_handler if you want muck_contents to intercept pages that are not found and provide authorized users and opportunity to add them.

class ApplicationController < ActionController::Base
  acts_as_muck_content_handler
end

Contents controller

Create a ContentsController and inherit from Muck::ContentsController. Unfortunately, due to routing issues this is required or we’d have to hard code the routes to go to muck/contents which prevent modification of the contents_controller.

class ContentsController < Muck::ContentsController
end

Add a route for the new controller:

ActionController::Routing::Routes.draw do |map|
  map.resource :contents
end

Override the contents controller to change the the security model. For example:

class ContentsController < Muck::ContentsController

  # Modify this method to change how permissions are checked to see if a user can content.
  # Each model that implements 'has_muck_content' can (and should) override can_add_content? to 
  # change how content permissions are handled.
  def has_permission_to_add_content(user, parent, content)
    parent.can_add_content?(user)
  end

  # Setups up the layouts that are available in the 'layouts' pulldown.
  # Override this method to change the available layouts. Note that the
  # layout will need to exist in your 'views/layouts' directory
  def setup_layouts
    @content_layouts = []
    @content_layouts << OpenStruct.new(:name => 'A Great Layout', :value => 'great_layout')
    @content_layouts << OpenStruct.new(:name => 'Default', :value => 'default')
  end

end

Uploader

muck-contents uses the uploader gem and thus requires all the configuration to make that gem function. This includes the addition of and uploads controller and upload model. See that gem for more information. If you have generated your project using the rails template generator (github.com/jbasdf/rails-templates) then it should be setup for you. The actual uploading of files and images will be handled by the uploads controller. The security for uploads to your site should be handled in that controller.

Example uploads controller:

class UploadsController < Uploader::UploadsController

  before_filter :login_required
  before_filter :setup_parent, :only => [:index, :create, :swfupload, :photos, :files]

  def index
    @upload = Upload.new
    @uploads = @parent.uploads.paginate(:page => @page, :per_page => @per_page, :order => 'created_at desc')
    respond_to do |format|
      format.html { render }
      format.rss { render :layout => false }
    end
  end

  def photos
    @images = @parent.uploads.images.paginate(:page => @page, :per_page => @per_page, :order => 'created_at desc')
    respond_to do |format|
      format.html { render }
      format.rss { render :layout => false }
    end
  end

  def files
    @files = @parent.uploads.files.paginate(:page => @page, :per_page => @per_page, :order => 'created_at desc')
    respond_to do |format|
      format.js { render :json => basic_uploads_json(@files) }
    end
  end

  protected

    def get_upload_text(upload)
      render_to_string( :partial => 'uploads/upload_row', :object => upload, :locals => { :style => 'style="display:none;"', :parent => @parent } )
    end

    def get_redirect
      @parent
    end

    def has_permission_to_upload(user, upload_parent)
      return true if upload_parent.blank?
      upload_parent.can_edit?(user)
    end

    def permission_denied
      message = t("uploader.permission_denied")
      respond_to do |format|
        format.html do
          flash[:notice] = message
          redirect_to get_redirect
        end
        format.js { render :text => message }
        format.json { render :json => { :success => false, :message => message } }
      end
    end

end

Example upload model:

NOTE: you must define icon, thumb, small, medium and large as styles. They can be any size you like.

class Upload < ActiveRecord::Base

  acts_as_uploader  :enable_s3 => false,
                    :has_attached_file => {
                      :url     => "/system/:attachment/:id_partition/:style/:basename.:extension",
                      :path    => ":rails_root/public/system/:attachment/:id_partition/:style/:basename.:extension",
                      :styles  => { :icon => "30x30!", 
                                    :thumb => "100>", 
                                    :small => "150>", 
                                    :medium => "300>", 
                                    :large => "660>" },
                      :default_url => "/images/profile_default.jpg",
                      :storage => :s3,
                      :s3_credentials => AMAZON_S3_CREDENTIALS,
                      :bucket => "assets.#{GlobalConfig.application_url}",
                      :s3_host_alias => "assets.#{GlobalConfig.application_url}",
                      :convert_options => {
                         :all => '-quality 80'
                       }
                    },
                    :s3_path => ':id_partition/:style/:basename.:extension'

  acts_as_taggable

  named_scope :public, :conditions => "is_public = true"
  named_scope :tagged_with, lambda {|tag_name| {:conditions => ["tags.name = ?", tag_name], :include => :tags} }

end

Copyright © 2009 Tatemae, released under the MIT license