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

Add the following to your Gemfile:

gem 'muck-contents'

Note about TinyMCE

Watch out for updates to the TinyMCE gem. The author has chosen to overwrite all files in the tiny mce asset directories when the server starts up. This will delete the muck related plugins in the tiny_mce/plugins directory.

Gem configuration and setup

In your app do

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

Setup

Muck Contents Initializer

Create a new initializer or add the following to muck.rb 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. Example:

MuckContents.configure do |config|
  config.sanitize_content = true
  config.enable_auto_translations = false         # If true then all content objects will automatically be translated into all languages supported by Google Translate
  config.enable_sunspot = false                   # This enables or disables sunspot for profiles. Only use acts_as_solr or sunspot not both. Sunspot does not include multicore support.
  config.enable_solr = true                       # Enables solr for the content system.  If you are using solr then set this to true.  If you do not wish to setup and manage solr 
                                                  # then set this value to false (but search will be disabled).
  config.enable_comments = false

  # CSS files that should be fed into the tiny_mce content editor.  
  # Note that Rails will typically generate a single all.css stylesheet.  Setting the stylesheets here let's 
  # the site administrator control which css is present in the content editor and thus which css an end 
  # user has access to to style their content.
  if Rails.env.production?
    config.content_css = ['/stylesheets/all.css']
  else
    config.content_css = ['/stylesheets/reset.css', '/stylesheets/styles.css']
  end
end

Tests

Muck-Contents inserts an exception handler. If this handler is not loaded the gem will prevent your application from loading. This is mainly a problem in tests where show_exceptions is set to false. Enable ‘show_exceptions’ in test.rb:

RailsTest::Application.configure do
  config.action_dispatch.show_exceptions = true # Must be true to include the rack middleware require to generate missing page exceptions
end

Tiny MCE 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.

Attach content to a parent model

Content can be scoped to a specific model. ie

class User < ActiveRecord::Base
  include MuckContents::Models::MuckContentable
end

Then the user’s content can be accessed via:

@user.contents

Content Permissions

By default users are not allowed to add content. Add ‘can_add_root_content?’ to your user model to determine which users can add content to the root of the website. A default configuration might be admins only:

class User < ActiveRecord::Base
  def can_add_root_content?
    admin?
  end
end

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
  include MuckContents::Models::MuckContent
end

class ContentTranslation < ActiveRecord::Base
  include MuckContents::Models::MuckContentTranslation
end

class ContentPermission < ActiveRecord::Base
  include MuckContents::Models::MuckContentPermission
end

Contents controller

Optionally create a ContentsController and inherit from Muck::ContentsController. This step is only needed if you intend to override the default contents controller functionality.

class ContentsController < Muck::ContentsController
end

Add a route for the new controller:

MyApp::Application.routes.draw do
  resources :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, :multiupload, :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. Be sure to look at the ‘uploader’ gem documentation for more information on setting up uploads.

class Upload < ActiveRecord::Base

  include Uploader::Models::Upload

  # only allow images:
  # validates_attachment_content_type :file, :content_type => ['image/jpeg', 'image/pjpeg', 'image/jpg']

  # limit uploads to 10 MB
  # validates_attachment_size :local, :less_than => 10.megabytes

  # The following method is implemented in the upload model mixin.  This is the method destroy will check to see if
  # the user has permission to delete the object.  Add additional logic as needed or if the existing logic
  # looks fine then feel free to delete this comment and the can_edit? method.
  def can_edit?(check_user)
    return false if user.blank?
    check_user == self.user
  end

end

Tips

If friendly_id starts producing errors like this then you need to redo your slugs:

ActiveRecord::StatementInvalid (Mysql::Error: Duplicate entry 'my-content-page-/-2' for key 2: UPDATE `slugs` SET `scope` = '/', `sequence` = 2 WHERE `id` = 189):

Run this and you should be good to go:

rake friendly_id:redo_slugs MODEL=Content

Cleanup old slugs with:

rake friendly_id:remove_old_slugs MODEL=Content

Testing

This gem uses a full rails application in the test directory for testing and development. Running rake spec there will run the tests.

Copyright © 2009-2010 Tatemae, released under the MIT license