Improve Content APIs

peakpg edited this page Jul 19, 2012 · 2 revisions
Clone this wiki locally

There are a number of features that could be added to portlets/CMS APIs to reduce the amount of custom code required to do interesting things. Here's a few

A. Accessing the current page

required current behavior

class SomeCustomPortlet < Cms::Portlet
  def render
    @page = @controller.instance_variable_get("@page")
  end
end

B. Enhancing existing modules or blocks

  1. Create a project specific subdirectory i.e. lib/project_name
  2. Create a module with a matching name:
# In lib/project_name/page_comment_extensions.rb
module ProjectName
  module PageCommentExtensions
    extend ActiveSupport::Concern

    included do
      scope :published, where(:published => true)
    end

    def some_method
      # Do whatever
    end

    module ClassMethods
      def columns_for_index
        [{:label => "Path", :method => :path},
         {:label => "Name", :method => :name}]         
      end
    end
  end
end
  1. Include the module so it will work in both development and production
# In config/application.rb
config.to_prepare do
  BcmsPageComments::PageComment.send(:include, ProjectName::PageCommentsExtensions)
end

C. Migrating data

Updating the views that are stored in the database is a bit of duplication. First, you need to update the code view (i.e. app/views/cms/model_name/render.html.erb. Then you need to write some code to update the view, like so:

# In db/migrate/XYZ_my_update.rb
  def change
    update_portlet_template('My Custom Portlet') do |template|
      template.gsub("<% form_tag", "<%= form_tag") # Rails 3 syntax change
    end
  end

  def update_portlet_template(name)
    portlet = Cms::Portlet.where(:name => name).first
    if portlet
      portlet.template = yield(portlet.template)
      portlet.save!
      puts "Updated #{portlet} template"
    else
      puts "Couldn't find a portlet named #{name}' to update its template. You will need to manually update it."
    end
  end

D. Filters make customizations harder

Modules that add filters to controller are hard to customize. You need to write a bunch of skip_filter then rebuild the entire change to add your own filters. It would probably be good to reevaluate how these work, possibly as middleware to be more reorderable. Here's an example which shows the problem, specifically a custom controller than needs CMS authentication behavior.

# Want to return the current user
class UserController < ApplicationController

  # These add filters, but we want to insert some of our own.
  include Cms::Acts::ContentPage
  include Cas::Authentication
  include NetForum::Enterprise::Authentication

  # So we have to remove ALL filters
  skip_filter(*_process_action_callbacks.map(&:filter))

  # Then explicitly readd them.
  before_filter :verify_cas_configured
  before_filter CASClient::Frameworks::Rails::GatewayFilter, :only => :logout
  before_filter :login_from_cas_ticket
  before_filter :check_access_to_page, :only => :logout

  # Return JSON data on the current user.
  def index
    ActiveRecord::Base.include_root_in_json = false
    user = current_user
    respond_to do |format|
      format.json { render :json => user }
    end
  end

  def logout
    @final_destination = session[:final_destination]
    if @final_destination
      redirect_to @final_destination
    else
      redirect_to "/"
    end
  end
end