cms_controller.rb 11.1 KB
class CmsController < MyProfileController
  
  define_option :page_class, Article
  
  protect 'post_content', :profile, :only => [:edit, :new, :reorder, :delete]

  protected

  def profile
    Profile.find_by_identifier(params[:profile]) 
  end

  def user
    current_user.person
  end


  public

  #############################################################
  # everything below was copied from comatose
  #############################################################

  define_option :original_template_root, nil
  define_option :plugin_layout_path, File.join( '..', '..', '..', 'vendor', 'plugins', 'comatose', 'views', 'layouts' )
  
  before_filter :handle_authorization
  before_filter :set_content_type
  
  # Shows the page tree
  def index
    @root_pages = [fetch_root_page].flatten
  end
  
  # Edit a specfic page (posts back)
  def edit
    # Clear the page cache for this page... ?
    @page = page_class.find params[:id]
    @root_pages = [fetch_root_page].flatten
    if request.post?
      @page.update_attributes(params[:page])
      @page.updated_on = Time.now
      @page.author = fetch_author_name
      if @page.save
        expire_cms_page @page
        expire_cms_fragment @page
        flash[:notice] = "Saved changes to '#{@page.title}'"
        redirect_to :controller=>self.controller_name, :action=>'index'
      end
    end
  end
  
  # Create a new page (posts back)
  def new
    @root_pages = [fetch_root_page].flatten
    if request.post?
      @page = page_class.new params[:page]
      @page.author = fetch_author_name
      if @page.save
        flash[:notice] = "Created page '#{@page.title}'"
        redirect_to :controller=>self.controller_name, :action=>'index'
      end
    else
      @page = page_class.new(:parent_id=>(params[:parent] || nil))
    end
  end
  
  # Saves position of child pages
  def reorder
    # If it's AJAX, do our thing and move on...
    if request.xhr?
      params["page_list_#{params[:id]}"].each_with_index { |id,idx| page_class.update(id, :position => idx) }
      expire_cms_page page_class.find(params[:id])
      render :text=>'Updated sort order', :layout=>false
    else
      @page = page_class.find params[:id]
      if params.has_key? :cmd
        @target = page_class.find params[:page]
        case params[:cmd]
          when 'up' then @target.move_higher
          when 'down' then @target.move_lower
        end
        redirect_to :action=>'reorder', :id=>@page
      end
    end
  end
  
  # Allows comparing between two versions of a page's content
  def versions
    @page = page_class.find params[:id]
    @version_num = (params[:version] || @page.versions.length).to_i
    @version = @page.find_version(@version_num)
  end
  
  # Reverts a page to a specific version...
  def set_version
    if request.post?
      @page = page_class.find params[:id]
      @version_num = params[:version]
      @page.revert_to!(@version_num)
    end
    redirect_to :controller=>self.controller_name, :action=>'index'
  end
  
  # Deletes the specified page
  def delete
    @page = page_class.find params[:id]
    if request.post?
      expire_cms_pages_from_bottom @page
      expire_cms_fragments_from_bottom @page
      @page.destroy
      flash[:notice] = "Deleted page '#{@page.title}'"
      redirect_to :controller=>self.controller_name, :action=>'index'
    end
  end

  # Returns a preview of the page content...
  def preview
    begin
      page = page_class.new(params[:page])
      page.author = fetch_author_name
      if params.has_key? :version
        content = page.to_html( {'params'=>params.stringify_keys, 'version'=>params[:version]} )
      else
        content = page.to_html( {'params'=>params.stringify_keys} )
      end
    rescue SyntaxError
      content = "<p>There was an error generating the preview.</p><p><pre>#{$!.to_s.gsub(/\</, '&lt;')}</pre></p>"
    rescue
      content = "<p>There was an error generating the preview.</p><p><pre>#{$!.to_s.gsub(/\</, '&lt;')}</pre></p>"
    end
    render :text=>content, :layout => false
  end
  
  # Expires the entire page cache
  def expire_page_cache
    expire_cms_pages_from_bottom( fetch_root_page )
    expire_cms_fragments_from_bottom( fetch_root_page )
    flash[:notice] = "Page cache has been flushed"
    redirect_to :controller=>self.controller_name, :action=>'index'
  end
  
  # Walks the page tree and generates HTML files in your /public
  # folder... It will skip pages that have a 'nocache' keyword
  # TODO: Make page cache generation work when in :plugin mode
  def generate_page_cache
    if runtime_mode == :plugin
      @errors = ["Page cache cannot be generated in plugin mode"]
    else
      @errors = generate_all_pages_html(params)
    end
    if @errors.length == 0 
      flash[:notice] = "Pages Cached Successfully"
    else
      flash[:notice] = "Pages Cache Error(s): #{@errors.join(', ')}"
      flash[:cache_errors] = @errors 
    end
    redirect_to :controller=>self.controller_name, :action=>'index'
  end

  
  protected

  def handle_authorization
    if Comatose.config.admin_authorization.is_a? Proc
      instance_eval &Comatose.config.admin_authorization
    elsif Comatose.config.admin_authorization.is_a? Symbol
      send(Comatose.config.admin_authorization)
    elsif defined? authorize
      authorize
    else
      true
    end
  end

  def fetch_author_name
    if Comatose.config.admin_get_author.is_a? Proc
      instance_eval &Comatose.config.admin_get_author
    elsif Comatose.config.admin_get_author.is_a? Symbol
      send(Comatose.config.admin_get_author)
    elsif defined? get_author
      get_author
    end
  end
  
  # Can be overridden -- return your root comtase page
  def fetch_root_page
    if Comatose.config.admin_get_root_page.is_a? Proc
      instance_eval &Comatose.config.admin_get_root_page
    elsif Comatose.config.admin_get_root_page.is_a? Symbol
      send(Comatose.config.admin_get_root_page)
    elsif defined? get_root_page
      get_root_page
    end      
  end
  
  # Sets the HTTP content-type header based on what's configured 
  # in Comatose.config.content_type
  def set_content_type
    response.headers["Content-Type"] = "text/html; charset=#{Comatose.config.content_type}" unless Comatose.config.content_type.nil?
  end
  
  # Calls generate_page_html for each mount point..
  def generate_all_pages_html(params={})
    @errors = []
    @been_cached = []
    Comatose.mount_points.each do |root_info|
      page_class.active_mount_info = root_info
      generate_page_html(page_class.find_by_path( root_info[:index] ), root_info, params)
    end
    @errors
  end
  
  # Accepts a Comatose Page and a root_info object to generate
  # the page as a static HTML page -- using the layout that was
  # defined on the mount point
  def generate_page_html(page, root_info, params={})
    @been_cached ||= []
    unless page.has_keyword? :nocache or @been_cached.include? page.id
      uri = page.uri
      uri = "#{uri}/index".split('/').flatten.join('/') if page.full_path == root_info[:index]
      @page = Comatose::PageWrapper.new(page)
      begin
        page_layout = get_page_layout(root_info)
        #puts "mode = #{runtime_mode}, layout = #{page_layout}, template_root = #{template_root}, original_template_root = #{original_template_root}"
        html = render_to_string( :text=>page.to_html({'params'=>params.stringify_keys}), :layout=>page_layout )
        cache_page( html, uri )
      rescue
        logger.error "Comatose CMS Page Cache Exception: #{$!}"
        @errors << "(#{page}/#{page.slug}) - #{$!}"
      end
      @been_cached << page.id
      # recurse...
      page.children.each do |child|
        generate_page_html(child, root_info)
      end
    end
  end
  
  # Calls the class methods of the same name...
  def expire_cms_page(page)
    self.class.expire_cms_page(page)
  end
  def expire_cms_pages_from_bottom(page)
    self.class.expire_cms_pages_from_bottom(page)
  end
  
  
  # expire the page from the fragment cache
  def expire_cms_fragment(page)
    key = page.full_path.gsub(/\//, '+')
    expire_fragment(key)
  end
  
  # expire pages starting at a specific node
  def expire_cms_fragments_from_bottom(page)
    pages = page.is_a?(Array) ? page : [page] 
    pages.each do |page|
      page.children.each {|c| expire_cms_fragments_from_bottom( c ) } if !page.children.empty?
      expire_cms_fragment( page )
    end
  end

  # Class Methods...
  class << self

    # Walks all the way down, and back up the tree -- the allows the expire_cms_page
    # to delete empty directories better
    def expire_cms_pages_from_bottom(page)
      pages = page.is_a?(Array) ? page : [page] 
      pages.each do |page|
        page.children.each {|c| expire_cms_pages_from_bottom( c ) } if !page.children.empty?
        expire_cms_page( page )
      end
    end

    # Expire the page from all the mount points...
    def expire_cms_page(page)
      Comatose.mount_points.each do |path_info|
        page_class.active_mount_info = path_info
        expire_page(page.uri)
        # If the page is the index page for the root, expire it too
        if path_info[:root] == page.uri
          expire_page("#{path_info[:root]}/index")
        end
        begin # I'm not sure this matters too much -- but it keeps things clean
          dir_path = File.join(RAILS_ROOT, 'public', page.uri[1..-1])
          Dir.delete( dir_path ) if FileTest.directory?( dir_path ) and !page.parent.nil?
        rescue
          # It probably isn't empty -- just as well we leave it be
          #STDERR.puts " - Couldn't delete dir #{dir_path} -> #{$!}"
        end 
      end
    end

    # Returns a path to plugin layout, if it's unspecified, otherwise
    # a path to an application layout...
    def get_page_layout(params)
      if params[:layout] == 'comatose_content'
        File.join(plugin_layout_path, params[:layout])
      else
        params[:layout]
      end
    end
  
    def configure_template_root
      if self.runtime_mode == :unknown
        if FileTest.exist? File.join(RAILS_ROOT, 'public', 'javascripts', 'comatose_admin.js')
          self.runtime_mode = :application
        else
          self.runtime_mode = :plugin
        end
      end
    end

    def runtime_mode
      @@runtime_mode ||= :unknown
    end
  
    def runtime_mode=(mode)
      case mode
      when :plugin
        self.original_template_root = self.template_root
        self.template_root = File.join( File.dirname(__FILE__), '..', '..', 'views')
      when :application
        self.template_root = self.original_template_root if self.original_template_root
      end
      @@runtime_mode = mode
    end

  end
  
  # Check to see if we are in 'embedded' mode, or are being 'customized'
  #  embedded   = runtime_mode of :plugin
  #  customized = runtime_mode of :application
  configure_template_root
  
  #
  # Include any modules...
  Comatose.config.admin_includes.each do |mod|
    if mod.is_a? String
      include mod.constantize
    elsif mod.is_a? Symbol
      include mod.to_s.classify.constantize
    else
      include mod
    end
  end

  # Include any helpers...
  Comatose.config.admin_helpers.each do |mod|
    if mod.is_a? String
      helper mod.constantize
    elsif mod.is_a? Symbol
      helper mod.to_s.classify.constantize
    else
      helper mod
    end
  end

end