class Management::CmsController < Management::ApplicationController # :nodoc:
  before_filter :check_permissions
  before_filter :block_basic_users, :except => [
    :index, :edit_page_content,
    :include_codepress, :disable_caching, :garbage_collect,
    :select_page, :list_pages_select, :request_review,
    :toolbar_preview, :toolbar_edit,
    
    :create_file_link, :upload_file, :receive_file,
    
    :upload_image, :receive_image, :crop_image, :save_crop, :upload_status,
    :upload_thumb, :crop_thumb, :save_crop_thumb,
    :upload_feature_image, :crop_feature_image, :save_crop_feature_image,
    
    :receive_gallery, :complete_gallery, :gallery_setup, :add_to_gallery,
    :gallery_management, :select_gallery, :set_gallery_order, :save_gallery_settings,
    :sort_images, :sort_images_save,
    :image_details, :update_caption,
    :delete_photo, :delete_gallery,
    
    :pages, :list_pages, :edit_page, :page_attribute, :set_page_version
  ]
  
  before_filter :convert_invalid_chars_in_params
  
  upload_status_for :receive_image
  upload_status_for :add_to_gallery
  
  cache_sweeper :cms_content_sweeper
  
  def check_permissions
    if !user_has_permission?(:manage_cms)
      render '/imagine_cms/errors/permission_denied'
      return false
    end
  end
  
  def block_basic_users
    return true unless UseCmsAccessLevels
    unless user_has_permission?(:manage_cms_full_access) && @user.cms_allowed_sections.to_s.strip.blank?
      render '/imagine_cms/errors/permission_denied'
      return false
    end
  end
  
  def validate_user_access
    unless @user.cms_allowed_sections.to_s.strip.blank?
      allowed_sections = @user.cms_allowed_sections.split(',').map { |s| s.strip }.reject { |s| s.blank? }
      if @pg
        path = '/' + @pg.path
      else
        parent = CmsPage.find_by_id(params[:parent_id] || params[:pg][:parent_id]) rescue nil
        return false if !parent
        path = '/' + parent.path
      end
      
      allowed = false
      allowed_sections.each { |s| allowed ||= (path =~ /^#{s}/) }
      
      if !allowed
        respond_to do |wants|
          wants.js    { render :text => "Sorry, you don't have permission to edit this page."         }
          wants.html  { redirect_to "/#{@pg.path}#{@pg.path == '' ? '' : '/'}version/#{@pg.version}"  }
        end
        return false
      end
    end
    
    return true
  end
  
  
  def index
  end
  
  def templates
    @temps = CmsTemplate.find(:all, :order => 'name')
  end
  
  def edit_template
    @temp = CmsTemplate.find_by_id(params[:id]) || CmsTemplate.new
    
    if request.post?
      @temp.assign_attributes(params[:temp])
      
      begin
        @pg = CmsPage.new
        @page_objects = HashObject.new
        render_to_string :inline => @temp.content
      rescue Exception => e
        message = e.message
        flash.now[:error] = "<pre>#{ERB::Util.html_escape(message)}</pre>".html_safe
        logger.debug e
        return
      end
      
      # this must come after the render_to_string so that we capture template
      # options embedded in snippets
      @temp.options = @template_options
      
      if !@temp.save
        flash.now[:error] = @temp.errors.full_messages.join('<br/>')
      else
        flash[:notice] = 'Template saved.'
        redirect_to :action => 'edit_template', :id => @temp.id and return
      end
    end
  end
  
  def snippets
    @snippets = CmsSnippet.find(:all, :order => 'name')
  end
  
  def edit_snippet
    @snip = CmsSnippet.find_by_id(params[:id]) || CmsSnippet.new
    
    if request.post?
      @snip.assign_attributes(params[:snip])
      
      begin
        @pg = CmsPage.new
        @page_objects = HashObject.new
        render_to_string :inline => @snip.content
      rescue Exception => e
        message = e.message
        flash.now[:error] = "<pre>#{ERB::Util.html_escape(message)}</pre>".html_safe
        logger.debug e
        return
      end
      
      if !@snip.save
        @error = @snip.errors.full_messages.join('<br/>')
      else
        flash[:notice] = 'Snippet saved.'
        redirect_to :action => 'edit_snippet', :id => @snip.id and return
      end
    end
  end
  
  #
  # AKN 2012-11-08: This was never really a good idea. Need to figure out a better way. Disabling for now.
  #
  
  # def edit_master
  #   @file_type = case params[:id]
  #     when 'template' then 'html'
  #     when 'web_stylesheet', 'print_stylesheet' then 'css'
  #     else nil
  #   end
  #   
  #   filename = case params[:id]
  #     when 'template'         then File.join('app', 'views', 'layouts', 'application.rhtml')
  #     when 'web_stylesheet'   then File.join('public', 'stylesheets', 'default.css')
  #     when 'print_stylesheet' then File.join('public', 'stylesheets', 'print.css')
  #     when 'ie_stylesheet'    then File.join('public', 'stylesheets', 'ie.css')
  #     when 'ie6_stylesheet'   then File.join('public', 'stylesheets', 'ie6.css')
  #   end
  #   filename = File.join(Rails.root, filename)
  #   
  #   case request.method
  #   when :get
  #     @file_content = File.open(filename, 'r').read
  #   when :post
  #     begin
  #       @pg = CmsPage.new
  #       @page_objects = HashObject.new
  #       render_to_string :inline => params[:file_content]
  #     rescue Exception => e
  #       message = e.message
  #       flash.now[:error] = "<pre>#{ERB::Util.html_escape(message)}</pre>"
  #       logger.debug e
  #       return
  #     end
  #     
  #     begin
  #       if params[:file_content].blank?
  #         flash[:error] = 'An error occurred, please contact support.'
  #       else
  #         File.open(filename, 'w') { |f| f.write(params[:file_content]) }
  #         flash[:notice] = 'File saved.'
  #       end
  #       
  #       CmsPage.find(:all).each do |page|
  #         expire_page :controller => 'cms/content', :action => 'show', :content_path => page.path.split('/')
  #       end
  #       
  #       redirect_to :action => 'edit_master', :id => params[:id]
  #     rescue Exception => e
  #       message = e.message
  #       flash.now[:error] = "<pre>#{ERB::Util.html_escape(message)}</pre>"
  #       log_error(e)
  #     end
  #   end
  # end
  
  def pages
    @page_levels = [ '' ].concat((params[:path] || session[:cms_pages_path] || '').split('/').reject { |l| l.blank? })
    @page_levels << ''
    @path = ''
    @page = nil
  end
  
  def list_pages
    @page_level = params[:level].to_i
    @parent = CmsPage.find_by_id(params[:parent_id])
    
    if @page_level == 0
      render :partial => 'list_page', :locals => { :list_page => CmsPage.find(1) } and return
    else
      if @parent
        @pages = @parent.children
        session[:cms_pages_path] = @parent.path
      else
        @pages = nil
      end
    end
    
    render :partial => 'list_pages'
  end
  
  def edit_page
    @pg = CmsPage.find_by_id(params[:id])
    validate_user_access or return
    @pg ||= CmsPage.new
    
    @parent = @pg.parent || CmsPage.find_by_id(params[:parent_id])
    if @parent
      @pg.parent ||= @parent
      @pg.template ||= @parent.template
    end
    
    @attrs = CmsPageObject.find(:all, :conditions => [ "obj_type = 'attribute'" ], :order => 'name').map { |attr| attr.name }.uniq
    
    if params[:mode] == 'ajax_new' || params[:mode] == 'ajax_edit'
      @pg.published_version = -1 if params[:mode] == 'ajax_new'
      load_page_objects
      load_template_options
      render :partial => 'edit_page' and return
    end
    
    if request.post?
      params[:pg] ||= {}
      
      if params[:pg][:article_date_year]
        params[:pg][:article_date] = Time.utc(params[:pg].delete(:article_date_year),
                                                 params[:pg].delete(:article_date_month),
                                                 params[:pg].delete(:article_date_day))
      end
      if params[:pg][:article_end_date_year]
        params[:pg][:article_end_date] = Time.utc(params[:pg].delete(:article_end_date_year),
                                                     params[:pg].delete(:article_end_date_month),
                                                     params[:pg].delete(:article_end_date_day))
      end
      if params[:pg][:published_date_year]
        params[:pg][:published_date] = Time.utc(params[:pg].delete(:published_date_year),
                                                   params[:pg].delete(:published_date_month),
                                                   params[:pg].delete(:published_date_day))
      end
      if params[:pg][:expires]
        date = Time.utc(params[:pg].delete(:expiration_date_year),
                           params[:pg].delete(:expiration_date_month),
                           params[:pg].delete(:expiration_date_day))
        params[:pg][:expiration_date] = date if params[:pg][:expires] == 'true'
      end
      
      @pg.assign_attributes(params[:pg])
      unless params[:use_article_date_range].to_i > 0
        @pg.article_end_date = nil
      end
      @pg.updated_by ||= session[:user_id]
      @pg.updated_by_username ||= session[:user_username]
      
      save_function = @pg.new_record? ? 'save' : 'save_without_revision'
      
      if @pg.send(save_function)
        # now try to save tags
        begin
          tags_to_delete = @pg.tags
          params[:tags].split(',').map { |t| t.strip }.reject { |t| t.blank? }.compact.each do |t|
            @pg.tags.create(:name => t) unless @pg.tags.find_by_name(t)
            tags_to_delete.reject! { |tag| tag.name == t }
          end
          tags_to_delete.each { |t| t.destroy }
        rescue Exception => e
          logger.debug e
        end
        
        # now try to save page objects (just attributes in this case)
        begin
          objects_to_delete = @pg.objects.find(:all, :conditions => [ "obj_type = 'attribute' or obj_type = 'option'" ])
          
          (params[:page_objects] || {}).each do |key,val|
            next if key.blank? || val.blank?
            
            key =~ /^obj-(\w+?)-(.+?)$/
            obj = @pg.objects.find(:first, :conditions => [ "name = ? and obj_type = ?", $2, $1 ])
            obj ||= @pg.objects.build(:name => $2, :obj_type => $1)
            obj.content = val
            obj.save
            objects_to_delete.reject! { |obj| obj.name == $2 }
          end
          
          objects_to_delete.each { |t| t.destroy }
        rescue Exception => e
          logger.debug e
        end
        
        render :update do |page|
          case params[:return_to]
          when 'preview'
            page.redirect_to "/#{@pg.path}/version/#{@pg.published_version > 0 ? @pg.published_version : @pg.version}"
          else
            flash[:notice] = 'Page saved.'
            session[:cms_pages_path] = @pg.path
            page.redirect_to :action => 'pages'
          end
        end
        
      else
        # save failed, display errors
        render :update do |page|
          page.replace_html 'save_errors', @pg.errors.full_messages.join('<br/>')
          page << "try { $('btn_next').disabled = false; } catch (e) {}"
          page << "try { $('btn_finish').disabled = false; } catch (e) {}"
          page << "try { $('btn_save').disabled = false; $('btn_save').value = 'Save'; } catch (e) {}"
        end and return
      end
    end
  end
  
  def delete_page
    @pg = CmsPage.find_by_id(params[:id])
    
    if !@pg
      flash[:error] = "Sorry, couldn't find the requested page."
    elsif @pg.children.size > 0
      flash[:error] = "This page contains other pages. Please delete those first if you are sure you want to delete this page."
    elsif @pg.id == 1
      flash[:error] = "You cannot delete the home page."
    else
      flash[:notice] = "Page deleted."
      session[:cms_pages_path] = @pg.parent.path rescue nil
      @pg.destroy
    end
    
    redirect_to :action => 'pages'
  end
  
  def select_page
    @page_levels = [ '' ].concat((params[:path] || session[:cms_pages_path] || '').split('/').reject { |l| l.empty? })
    @page_levels << ''
    @path = ''
    @page = nil
    
    render :layout => false
  end
  
  def list_pages_select
    @page_level = params[:level].to_i
    @parent = CmsPage.find_by_id(params[:parent_id])
    
    if @page_level == 0
      render :partial => 'list_page_select', :locals => { :list_page_select => CmsPage.find(1) } and return
    else
      if @parent
        @pages = @parent.children
        session[:cms_pages_path] = @parent.path
      else
        @pages = nil
      end
    end
    
    render :partial => 'list_pages_select'
  end
  
  def show_template_options
    @pg = CmsPage.find_by_id(params[:id]) || CmsPage.new
    @pg.cms_template_id = params[:template_id]
    
    load_page_objects
    load_template_options
    
    render :partial => 'template_options'
  end
  
  def page_attribute
    render :nothing => true and return unless params[:name]
    
    @page_objects = HashObject.new({ params[:name] => params[:value] })
    render :partial => 'page_attribute', :locals => { :name => params[:name] }
  end
  
  def edit_page_content
    @pg = CmsPage.find(params[:id])
    validate_user_access or return
    
    @page_objects = HashObject.new(params[:page_objects] || {})
    
    if request.get?
      @pg.revert_to(params[:version]) if params[:version]
      @pg.objects.find(:all, :conditions => [ 'cms_page_version = ?', @pg.version ]).each do |obj|
        key = "obj-#{obj.obj_type.to_s}-#{obj.name}"
        @page_objects[key] = obj.content.html_safe
      end
      
      # set "legacy" vars
      @content_levels = @pg.path.split('/')
      params[:section] = @content_levels.size < 1 ? '' : @content_levels.first
      params[:subsection] = @content_levels[1] unless @content_levels.size < 3
      if @content_levels.size == 1
        params[:page] = 'index'
      elsif @content_levels.size > 1
        params[:page] = @content_levels.last
      end
      
      @page_title = @pg.title
      
      @cms_head ||= ''
      @cms_head << "<script type=\"text/javascript\" src=\"#{url_for(:action => 'page_tags_for_lookup')}\"></script>"
      
      @template_content = substitute_placeholders(@pg.template.content, @pg)
      render :layout => 'application'
    
    elsif request.post?
      CmsPage.transaction do
        # need to revise this later if we implement deletion of page objects
        old_objs = @pg.objects.find(:all, :conditions => [ 'cms_page_version = ?', @pg.version ])
        
        @pg.updated_by = session[:user_id]
        @pg.updated_by_username = session[:user_username]
        
        # if basic user, make sure published version is not set to 'latest'
        if (UseCmsAccessLevels && !user_has_permission?(:manage_cms_full_access)) && @pg.published_version == 0
          @pg.published_version = @pg.version
        end
        
        @pg.updated_on = Time.now.utc
        @pg.save
        
        # do a little bit of classification... for now, just identify page lists
        page_lists = []
        @page_objects.each do |key,val|
          key =~ /^obj-(\w+?)-(\w+?)-sources-tag-count$/
          if $1 == 'page_list'
            page_lists << "obj-#{$1}-#{$2}"
          end
        end
        
        # run through page lists and do a little housekeeping
        page_lists.each do |key|
          # optimize source lists: tags
          if @page_objects["#{key}-sources-tag-count"].to_i > 0
            tags = []
            for i in 0...@page_objects["#{key}-sources-tag-count"].to_i
              tags << @page_objects["#{key}-sources-tag#{i}"]
            end
            tags.reject! { |tag| tag.blank? }
            @page_objects["#{key}-sources-tag-count"] = tags.size
            tags.each_with_index do |tag, i|
              @page_objects["#{key}-sources-tag#{i}"] = tag
            end
          end
          # optimize source lists: folders
          if @page_objects["#{key}-sources-folder-count"].to_i > 0
            folders = []
            for i in 0...@page_objects["#{key}-sources-folder-count"].to_i
              folders << @page_objects["#{key}-sources-folder#{i}"]
            end
            folders.reject! { |folder| folder.blank? }
            @page_objects["#{key}-sources-folder-count"] = folders.size
            folders.each_with_index do |folder, i|
              @page_objects["#{key}-sources-folder#{i}"] = folder
            end
          end
          
          # consolidate date picker fields
          if @page_objects["#{key}-date-range-custom-start_year"]
            @page_objects["#{key}-date-range-custom-start"] = 
              Time.utc(@page_objects.delete("#{key}-date-range-custom-start_year"),
                          @page_objects.delete("#{key}-date-range-custom-start_month"),
                          @page_objects.delete("#{key}-date-range-custom-start_day"))
          end
          if @page_objects["#{key}-date-range-custom-end_year"]
            @page_objects["#{key}-date-range-custom-end"] = 
              Time.utc(@page_objects.delete("#{key}-date-range-custom-end_year"),
                          @page_objects.delete("#{key}-date-range-custom-end_month"),
                          @page_objects.delete("#{key}-date-range-custom-end_day"))
          end
        end
        
        @page_objects.each do |key,val|
          key =~ /^obj-(\w+?)-(.+?)$/
          obj = @pg.objects.build(:name => $2, :obj_type => $1)
          
          # do a little bit of "censorship" to fix up Word pastes
          if val.is_a?(String)
            # all meta and link tags
            val.gsub!(/(<\/?)(meta|link)(.*?)>/m, '')
            
            # the dreaded MsoNormal
            val.gsub!(' class="MsoNormal"', '')
            
            # remove all font-family/font-size css styles, as well as font tags
            val.gsub!(/font-(?:family|size):.*?(;|")/, '\3')
            val.gsub!(/<font.*?>(.*?)<\/font>/, '\1')
            
            # strange conditional IE stuff
            val.gsub!(/<!--\[if(.*?)<!(--)?\[endif\]-->/m, '')
            
            # not even sure what these are supposed to be
            val.gsub!(/<xml>(.*?)<\/xml>/m, '')
            
            # pirate styles not welcome
            val.gsub!(/<style>(.*?)<\/style>/m, '')
            
            # images pointing to the local drive??
            val.gsub!(/<img src="file:(.*?)>/, '')
            
            # miscellany
            val.gsub!('<!--StartFragment-->', '')
            val.gsub!('<!--EndFragment-->', '')
            val.gsub!(/<span style="(?:;*)">(.*?)<\/span>/m, '\1')
            val.gsub!(' style="(?:;*)"', '')
            val.gsub!('<b></b>', '')
            
            # we could try to catch all strange ms tags...
            #val.gsub!(/(<\/?)(?:o|u1):p>/, '\1p>')
            #val.gsub!(/(<\/?)(?:st1):(.*?)>/, '')
            
            # but it's easier just remove all tags with colons in them
            val.gsub!(/(<\/?)[\w\d]+:[\w\d]+(.*?)>/, '')
          end
          
          obj.content = val
          obj.save
        end
        
        old_objs.each do |obj|
          unless @pg.objects.find(:all, :conditions => [ 'name = ? and cms_page_version = ?', obj.name, @pg.version])
            obj = @pg.objects.build(:name => obj.name, :obj_type => obj.type, :content => obj.content)
          end
        end
        
        # update index for searching
        @pg.update_index
        @pg.save_without_revision
      end
      
      redirect_to "/#{@pg.path}#{@pg.path == '' ? '' : '/'}version/#{@pg.version}"
    end
  end
  
  def insert_page_object_config
    @pg = CmsPage.find(params[:id])
    
    load_page_objects
    @pg.revert_to(params[:version]) if params[:version]
    @pg.objects.find(:all, :conditions => [ 'cms_page_version = ?', @pg.version]).each do |obj|
      key = "obj-#{obj.obj_type.to_s}-#{obj.name}"
      @page_objects[key] = obj.content
    end
    
    name = params[:name]
    render :nothing => true and return unless name
    
    parent_key = params[:parent_key]
    type = params[:type]
    render :nothing => true and return unless type == 'page_list'
    
    key = "obj-#{type}-#{name.gsub(/[^\w]/, '_')}"
    
    render :update do |page|
      page.insert_html :bottom, "page_object_config_#{parent_key}", :partial => 'page_list',
                                                                    :locals => { :name => name, :key => key }
    end
  end
  
  def page_tags_for_lookup
    @tags = CmsPageTag.find(:all, :order => 'name').map { |tag| tag.name }.uniq
    headers['content-type'] = 'text/javascript'
    render :layout => false
  end
  
  def page_attributes_for_lookup
    @attrs = CmsPageObject.find(:all, :conditions => [ "obj_type = 'attribute'" ], :order => 'name').map { |attr| attr.name }.uniq
    headers['content-type'] = 'text/javascript'
    render :layout => false
  end
  
  def page_list_add_tag
    render :partial => 'page_list_source_tag', :locals => { :i => params[:i], :key => params[:key] }
  end
  
  def page_list_add_folder
    render :partial => 'page_list_source_folder', :locals => { :i => params[:i], :key => params[:key] }
  end
  
  def set_page_version
    if (UseCmsAccessLevels && user_has_permission?(:manage_cms_publishing) || user_has_permission?(:manage_cms_full_access)) || user_has_permission?(:manage_cms)
      if params[:id] && params[:pg]
        @pg = CmsPage.find(params[:id])
        validate_user_access or return
        @pg.published_version = params[:pg][:published_version]
        @pg.update_index
        @pg.save_without_revision
        logger.debug @pg.errors.full_messages.inspect
      end
    end
    
    render :nothing => true
  end
  
  def request_review
    @pg = CmsPage.find(params[:id])
    @version = params[:version].to_i
    
    # send email to request administrative review
    # find all users with email address set
    User.find(:all).reject { |u| !u.active? || !u.can_manage_cms_publishing? || !u.cms_allowed_sections.blank? }.each do |u|
      next unless valid_email_address?(u.email_address)
      begin
        Mailer.deliver_cms_request_review(url_for(:controller => '/cms/content', :action => 'show', :content_path => []) + @pg.path, @pg.title, @version, u, @user, params[:change_description].to_s)
      rescue Exception => e
        log_error(e)
      end
    end
    
    render :nothing => true
  end
  
  #
  # helpers
  #
  
  def insert_object(name, type = :text, options = {}, html_options = {})
    extend ActionView::Helpers::FormHelper
    extend ActionView::Helpers::JavaScriptHelper
    extend ActionView::Helpers::PrototypeHelper
    extend ActionView::Helpers::TagHelper
    extend ActionView::Helpers::TextHelper
    
    key = "obj-#{type.to_s}-#{name.gsub(/[^\w]/, '_')}"
    @page_objects[key] ||= ''
    
    case type.to_sym
    when :string
      @page_objects[key] = options[:content] if @page_objects[key].blank?
      text_field(:page_objects, key, options)
    when :text
      @page_objects[key] = options[:content] if @page_objects[key].blank?
      focusOnLoad = !defined?(@cms_text_editor_placed)
      @cms_text_editor_placed = true
      content = ''.html_safe
      content << text_area(:page_objects, key, { :dojoType => 'Editor2', :toolbarGroup => 'main', :isToolbarGroupLeader => 'false',
                           :focusOnLoad => focusOnLoad.to_s, :style => 'border: 2px dashed gray; padding: 5px',
                           :minHeight => '100px' }.update(html_options))
      content << content_tag(:div, ''.html_safe, :id => "page_object_config_#{key}")
      content << javascript_tag("addLoadEvent(function () { scanForPageObjects(#{@pg.id}, '#{key}', #{@pg.version}); });")
      content << observe_field("page_objects_#{key}", :function => "scanForPageObjects(#{@pg.id}, '#{key}', #{@pg.version});", :frequency => 2)
      content
    when :page_list
      @page_objects["#{key}-min-item-count"] ||= 3 unless options[:item_min_count]
      @page_objects["#{key}-max-item-count"] ||= 5 unless options[:item_count]
      @page_objects["#{key}-item-offset"] ||= 0 unless options[:item_offset]
      @page_objects["#{key}-sort-first-field"] ||= options[:primary_sort_key]
      @page_objects["#{key}-sort-first-direction"] ||= options[:primary_sort_direction]
      @page_objects["#{key}-sort-second-field"] ||= options[:secondary_sort_key]
      @page_objects["#{key}-sort-second-direction"] ||= options[:secondary_sort_direction]
      
      render_to_string(:partial => 'page_list', :locals => { :name => name, :key => key }).html_safe
    when :snippet
      @snippet = CmsSnippet.find_by_name(name)
      if @snippet
        erb_render(substitute_placeholders(@snippet.content, @pg))
      else
        'Could not find snippet "' + name + '" in the database.'
      end
    else
      "Unknown object type: #{type.to_s}"
    end
  end
  helper_method :insert_object
  
  def disable_caching ; end
  helper_method :disable_caching
  
  #
  # cms toolbars
  #
  
  def toolbar_edit
    @pg = CmsPage.find_by_id(params[:id])
    render :layout => false
  end
  
  def toolbar_preview
    @pg = CmsPage.find_by_id(params[:id])
    render :layout => false
  end
  
  #
  # image upload
  #
  
  def upload_image
    @pg = CmsPage.find_by_id(params[:id])
    target_dir = File.join(Rails.root, 'public', 'assets', 'content', @pg.path)
    
    if File.exists?(target_dir)
      redirect_to :action => 'select_gallery', :id => @pg, :gallery_id => params[:gallery_id]
    else
      render :partial => 'upload_image'
    end
  end
  
  def receive_image
    @pg = CmsPage.find_by_id(params[:id])
    
    target_dir = File.join(Rails.root, 'public', 'assets', 'content', @pg.path)
    FileUtils.mkdir_p target_dir
    
    data = params[:file][:data]
    original_filename = data.original_filename.strip.gsub(/[\?\s\/\:\\]+/, '-').gsub(/^-/, '').gsub(/-$/, '')
    localfile = File.join(target_dir, original_filename)
    FileUtils.cp(data.tempfile, localfile)
    
    finish_upload_status "'#{File.basename(localfile)}'"
  end
  
  def crop_image
    @pg = CmsPage.find_by_id(params[:id])
    localfile = File.join(Rails.root, 'public', 'assets', 'content', @pg.path, File.basename(params[:filename]))
    
    # get out now if user clicked finish
    if params[:next_clicked].to_i != 1
      @image_file = localfile + "?#{File.mtime(localfile).to_i}"
      upload_to_s3(localfile, @pg)
      
      render :partial => 'crop_results' and return
    end
    
    
    # if we're still here... let's crop!
    target_dir = File.join(Rails.root, 'public', 'assets', 'content', @pg.path)
    testfile = File.join(target_dir, File.basename(localfile, File.extname(localfile))) + '-croptest' + File.extname(localfile)
    
    # make a smaller version to help with cropping
    im = MiniMagick::Image.from_file(localfile)
    im.resize "500x400>"
    im.write(testfile)
    File.chmod(0644, testfile)
    
    @width = im[:width]
    @height = im[:height]
    @height = 1 if @height == 0
    @image_file = File.basename(testfile)
    @aspect_ratio = @width.to_f/@height
    
    render :partial => 'crop_image'
  end
  
  def save_crop
    @pg = CmsPage.find_by_id(params[:id])
    target_dir = File.join(Rails.root, 'public', 'assets', 'content', @pg.path)
    testfile = File.join(target_dir, File.basename(params[:filename]))
    localfile = testfile.split(/-croptest/).join('')
    
    # need to scale up requested position/dimensions based on how big test image
    # is relative to original image
    orig_im = MiniMagick::Image.from_file(localfile)
    test_im = MiniMagick::Image.from_file(testfile)
    scale = orig_im[:width].to_f / test_im[:width]
    
    x1 = params[:image][:x1].to_i * scale
    y1 = params[:image][:y1].to_i * scale
    width = params[:image][:width].to_i * scale
    height = params[:image][:height].to_i * scale
    
    max_width = params[:image][:max_width].to_i
    max_height = params[:image][:max_height].to_i
    dirty = false
    
    # crop if user selected something
    if params[:image][:width].to_i > 0
      logger.debug "cropping @ (#{x1}, #{y1}) to size #{width} x #{height}"
      orig_im.crop "#{width}x#{height}+#{x1}+#{y1}"
      dirty = true
    end
    
    # resize if the resultant image is bigger than max dims
    if max_width > 0 && max_height > 0
      if orig_im[:width] > max_width || orig_im[:height] > max_height
        logger.debug "resizing to max dims #{max_width} x #{max_height}"
        orig_im.resize "#{max_width}x#{max_height}"
        dirty = true
      end
    end
    
    orig_im.write(localfile) if dirty
    @image_file = localfile + "?#{File.mtime(localfile).to_i}"
    File.unlink testfile
    upload_to_s3(localfile, @pg)
    
    render :partial => 'crop_results'
  end
  
  def receive_gallery
    @pg = CmsPage.find_by_id(params[:id])
    
    target_dir = File.join(Rails.root, 'public', 'assets', 'content', @pg.path)
    FileUtils.mkdir_p target_dir
    
    count = 1
    localdir = File.join(target_dir, 'gallery_1')
    while File.exists?(localdir) && count < 100
      count += 1
      localdir = File.join(target_dir, "gallery_#{count}")
    end
    FileUtils.mkdir_p File.join(localdir, 'temp')
    
    data = params[:gallery_file][:data]
    # read zip file
    
    entries = []
    Zip::ZipFile.foreach(data.path) do |zipentry|
      next if ![ '.jpg', '.jpeg', '.png', '.gif' ].include?(File.extname(zipentry.name).downcase) || zipentry.size < 1000
      next if File.basename(zipentry.name) =~ /^\._/
      
      entries << zipentry
    end
    entries.sort! { |a,b| File.basename(a.name).downcase <=> File.basename(b.name).downcase }
    
    Zip::ZipFile.open(data.path) do |zipfile|
      entries.each_with_index do |zipentry, index|
        upload_progress.message = "Extracting #{File.basename(zipentry.name)}"
        ext = File.extname(zipentry.name)
        localfile = File.join(localdir, 'temp', (index+1).to_s + ext.downcase)
        jpgfile = File.join(localdir, 'temp', (index+1).to_s + '.jpg')
        
        begin
          zipentry.extract(localfile)
          
          im = MiniMagick::Image.from_file(localfile)
          im.write(jpgfile)
          
          File.unlink(localfile) if localfile != jpgfile
          
        rescue Exception => e
          log_error(e)
        end
      end
    end
    
    finish_upload_status "'#{File.basename(localdir)}'"
  end
  
  def gallery_setup
    @pg = CmsPage.find_by_id(params[:id])
    target_dir = File.join('assets', 'content', @pg.path)
    @dirname = File.join(target_dir, File.basename(params[:dirname]), 'temp')
    Dir.chdir(File.join(Rails.root, 'public'))
    @images = Dir.glob("#{@dirname}/*.{jpg,jpeg,png,gif}").sort
    Dir.chdir(Rails.root)
    @thumbs = []
    
    @images.each do |img|
      next if img.include?('-thumb')
      
      thumbfile = File.join(Rails.root, 'public', @dirname, File.basename(img, File.extname(img))) + '-thumb.jpg'
      @thumbs << File.join(@dirname, File.basename(img, File.extname(img))) + '-thumb.jpg'
      
      next if File.exists?(thumbfile)
      
      im = MiniMagick::Image.from_file(File.join(Rails.root, 'public', img))
      im.resize "80x80" # hardcoded!
      im.write(thumbfile)
      File.chmod(0644, thumbfile)
    end
    
    @thumbs.sort! { |a,b| File.basename(a, File.extname(a)).to_i <=> File.basename(b, File.extname(b)).to_i }
    session[:gallery_thumbs_ordered] = @thumbs.map { |thumb| File.basename(thumb, File.extname(thumb)) }
    
    render :partial => 'gallery_setup'
  end
  
  
  def complete_gallery
    @pg = CmsPage.find(params[:id])
    target_dir = File.join('assets', 'content', @pg.path)
    @dirname = File.join(target_dir, File.basename(params[:dirname]))
    @thumbs = session[:gallery_thumbs_ordered]
    max_width = params[:max_width].to_i
    max_width = GalleryMaxWidth unless max_width > 0
    max_height = params[:max_height].to_i
    max_height = GalleryMaxHeight unless max_height > 0
    
    create_captions_file(@pg.id, { :gallery_id => File.basename(params[:dirname]) })
    
    @thumbs.each_with_index do |thumb, index|
      thumb.gsub!(/-thumb/, '')
      tempfile = File.join(Rails.root, 'public', @dirname, 'temp', thumb + '.jpg')
      tempthumbfile = File.join(Rails.root, 'public', @dirname, 'temp', thumb + '-thumb.jpg')
      
      localfile = File.join(Rails.root, 'public', @dirname, (index+1).to_s + '.jpg')
      thumbfile = File.join(Rails.root, 'public', @dirname, (index+1).to_s + '-thumb.jpg')
      
      im = MiniMagick::Image.from_file(tempfile)
      if im[:width] > max_width || im[:height] > max_height
        im.resize("#{max_width}x#{max_height}")
      end
      im.write(localfile)
      
      small = MiniMagick::Image.from_file(tempfile)
      small.crop_resized(GalleryThumbWidth, GalleryThumbHeight)
      small.write(thumbfile)
      
      File.chmod(0644, localfile, thumbfile)
      
      begin
        File.unlink(tempfile)
        File.unlink(tempthumbfile)
      rescue Exception => e
        # not that big a deal if we can't delete
      end
    end
    
    begin
      Dir.rmdir(File.join(Rails.root, 'public', @dirname, 'temp'))
    rescue Exception => e
      # not that big a deal if we can't delete
    end
    
    create_preview_images
    
    render :partial => 'complete_gallery'
  end

  def gallery_management
    @pg = CmsPage.find_by_id(params[:id])
    galleries_dir = File.join(Rails.root, 'public', 'assets', 'content', @pg.path)
    @galleries = Dir.glob("#{galleries_dir}/gallery_*")
    gallery_dir = File.join(galleries_dir, params[:gallery_id].to_s)
    
    @images = Dir.glob("#{gallery_dir}/*.{jpg,jpeg,png,gif}").reject { |img| img.include?('thumb') }.map { |img| File.basename(img).split('.').first.to_i }.sort
    
    create_preview_images
    
    if params[:gallery_id]
      @gallery = load_gallery_settings_from_file(params[:gallery_id])
    end
    
    render :layout => false
  end
  
  def set_gallery_order
    session[:gallery_thumbs_ordered] = params[:image_sorter]
    render :nothing => true
  end
  
  def select_gallery
    @pg = CmsPage.find_by_id(params[:id])
    @target_dir = File.join(Rails.root, 'public', 'assets', 'content', @pg.path)
    @galleries = Dir.glob("#{@target_dir}/gallery_*")
    
    create_preview_images
    
    if request.post?
      unless params[:gallery_id].downcase == "new"
        redirect_to :action => 'gallery_management', :id => @pg, :gallery_id => params[:gallery_id] and return
      else
        render :partial => 'upload_image' and return
      end
    else
      render :partial => 'select_gallery' and return
    end
  end
  
  def save_gallery_settings
    if request.post?
      @pg = CmsPage.find_by_id(params[:id])
      save_gallery_settings_to_file(params[:gallery_id], params[:gallery])
      
      render :nothing => true
    end
  end
  
  def sort_images
    @pg = CmsPage.find_by_id(params[:id])
    gallery_dir = File.join(Rails.root, 'public', 'assets', 'content', @pg.path, params[:gallery_id])
    
    @images = Dir.glob("#{gallery_dir}/*.{jpg,jpeg,png,gif}").reject { |img| img.include?('thumb') }.map { |img| File.basename(img).split('.').first.to_i }.sort
    
    if params[:images]
      session[:gallery_images_sorted] = params[:images]
      render :nothing => true
    else    
      render :partial => 'sort_images'
    end
  end
  
  def sort_images_save
    @pg = CmsPage.find_by_id(params[:id])
    gallery_dir = File.join(Rails.root, 'public', 'assets', 'content', @pg.path, params[:gallery_id])
    temp_dir = File.join(gallery_dir, 'temp')
    sorted_images = session[:gallery_images_sorted] || []
    
    if sorted_images == []
      redirect_to :action => 'gallery_management', :id => @pg, :gallery_id => params[:gallery_id]
      return
    end
    
    FileUtils.rm_rf(temp_dir)
    Dir.mkdir(temp_dir)
    
    # create blank captions.yml if it doesn't already exist
    create_captions_file(@pg.id)
    
    original_captions = YAML.load_file(File.join(gallery_dir, 'captions.yml')).to_a
    captions = []
    captions << original_captions[0]
    
    sorted_images.each_with_index do |img, i|
      localfile = File.join(gallery_dir, img)
      thumbfile = File.join(gallery_dir, img.split('.')[0] + '-thumb.jpg')
      
      temp_localfile = File.join(temp_dir, (i + 1).to_s + '.jpg')
      temp_thumbfile = File.join(temp_dir, (i + 1).to_s + '-thumb.jpg')
      
      begin ; FileUtils.cp(localfile, temp_localfile) ; rescue ; end
      begin ; FileUtils.cp(thumbfile, temp_thumbfile) ; rescue ; end
      
      captions[i + 1] = original_captions[img.split('.')[0].to_i] || ''
    end
    
    File.open(File.join(gallery_dir, 'captions.yml'), 'w') { |f| YAML.dump(captions, f) }
    
    images = Dir.glob("#{gallery_dir}/*.{jpg,jpeg,png,gif}")
    temp_images = Dir.glob("#{temp_dir}/*.{jpg,jpeg,png,gif}")
    
    images.each { |img| File.delete(img) }
    temp_images.each { |img| FileUtils.cp(img, File.join(gallery_dir, File.basename(img))) }
    
    FileUtils.rm_rf(File.join(gallery_dir, 'management'))
    create_preview_images(:force => 1)
    
    FileUtils.rm_rf(temp_dir)
    session[:gallery_images_sorted] = nil
    
    redirect_to :action => 'gallery_management', :id => @pg, :gallery_id => params[:gallery_id]
  end
  
  def image_details
    gallery_dir = File.join(Rails.root, 'public', 'assets', 'content', params[:path].to_s, params[:gallery_id])
    
    # create blank captions.yml if it doesn't already exist
    create_captions_file(params[:id])
    
    captions = YAML.load(File.open(File.join(gallery_dir, 'captions.yml')).read)
    image_id = params[:image].split('.')[0].to_i
    @caption = captions[image_id]
    
    render :partial => 'image_details'
  end
  
  def update_caption
    if request.post?
      gallery_dir = File.join(Rails.root, 'public', 'assets', 'content', params[:path].to_s, params[:gallery_id])
      
      image_id = params[:image].split('.')[0].to_i
      
      # create blank captions.yml if it doesn't already exist
      create_captions_file(params[:id])
      
      captions = YAML.load_file(File.join(gallery_dir, 'captions.yml')).to_a
      captions[image_id] = params[:caption]
      
      File.open(File.join(gallery_dir, 'captions.yml'), "w") { |f| YAML.dump(captions, f) }
    end
    
    redirect_to :action => 'gallery_management', :id => params[:id], :gallery_id => params[:gallery_id]
  end
  
  def add_to_gallery
    @pg = CmsPage.find_by_id(params[:id])
    galleries_dir = File.join(Rails.root, 'public', 'assets', 'content', @pg.path)
    @galleries = Dir.entries(galleries_dir).sort
    @gallery_dir = File.join(galleries_dir, params[:gallery_id])    
    images = Dir.glob("#{@gallery_dir}/*-thumb.{jpg,jpeg,png,gif}")
    
    temp_location = File.join(@gallery_dir ,'temp')
    FileUtils.rm_rf(temp_location)
    Dir.mkdir(temp_location)
    
    data = params[:gallery_file][:data]
    data_dest = File.join(temp_location, data.original_filename)
    File.open(data_dest, "w") { |f| f.write(data.read) }
    
    last_id = images.size
    ext = File.extname(data_dest).downcase
    
    if ext != '.zip'
      localfile = File.join(@gallery_dir, (last_id + 1).to_s + ext)
      thumbfile = File.join(@gallery_dir, (last_id + 1).to_s + '-thumb' + ext)
      
      File.open(localfile, "w") { |f| f.write(File.open(data_dest).read) }
      
      # create blank captions.yml if it doesn't already exist
      create_captions_file(@pg.id)
      
      localfile = resize_image(localfile)
      
      small = MiniMagick::Image.from_file(localfile)
      small.crop_resized(GalleryThumbWidth, GalleryThumbHeight)
      small.write(thumbfile)
      
      File.chmod(0644, localfile, thumbfile)
      
      create_preview_images(:force => 1)
      
    elsif ext == '.zip'
      begin
        Zip::ZipFile.foreach(data.path) do |zipentry|
          next if ![ '.jpg', '.jpeg', '.png', '.gif' ].include?(File.extname(zipentry.name).downcase) || zipentry.size < 1000
          upload_progress.message = "Extracting #{File.basename(zipentry.name)}"
          localfile = File.join(temp_location, ((last_id+1).to_s + File.extname(zipentry.name)).downcase)
          
          begin
            zipentry.extract(localfile)
            last_id += 1
          rescue Exception => e
            log_error(e)
          end
        end
      rescue Exception => e
        logger.debug params.inspect
        log_error(e)
        finish_upload_status "''" and return
      end
      
      @images = Dir.glob("#{temp_location}/*.{jpg,jpeg,png,gif}")
      
      @images.each do |img|
        localfile = File.join(@gallery_dir, File.basename(img, File.extname(img))) + '.jpg'
        tempfile = File.join(temp_location, File.basename(img, File.extname(img))) + File.extname(img)
        thumbfile = File.join(@gallery_dir, File.basename(img, File.extname(img))) + '-thumb.jpg'
        
        small = MiniMagick::Image.from_file(tempfile)
        small.crop_resized(GalleryThumbWidth, GalleryThumbHeight)
        small.write(thumbfile)
        
        FileUtils.cp(tempfile, localfile)
        resize_image(localfile)
        
        File.chmod(0644, localfile, thumbfile)
      end
      
      # smaller images for gallery index
      management_dir = File.join(@gallery_dir, 'management')
      
      preview_images = []
      Dir.glob("#{@gallery_dir}/*.{jpg,jpeg,png,gif}").each { |img| preview_images << img unless File.basename(img).include?('thumb') }
      
      preview_images.each { |img| create_preview_image(img, management_dir, 1) }
    end
    
    File.delete(data_dest)
    
    upload_progress.message = "File received successfully."
    finish_upload_status "'#{File.basename(data_dest)}'" and return
  end
  
  def delete_photo
    if request.post?
      gallery_dir = File.join(Rails.root, 'public', 'assets', 'content', params[:path].to_s, params[:gallery_id])
      
      image_id = params[:image].split('.')[0].to_i
      
      begin ; File.delete(File.join(gallery_dir, image_id.to_s + '.jpg')) ; rescue ; end
      begin ; File.delete(File.join(gallery_dir, image_id.to_s + '-thumb.jpg')) ; rescue ; end
      begin ; File.delete(File.join(gallery_dir, 'management', image_id.to_s + '.jpg')) ; rescue ; end
      
      all_images = Dir.glob(File.join(gallery_dir, '*.{jpg,jpeg,png,gif}'))
      images = []
      all_images.each { |img| images << img if !File.basename(img).include?('thumb') and File.basename(img).split('.')[0].to_i > image_id }
      
      image_names = []
      images.each_with_index { |img, index| image_names << File.basename(img).split('.')[0].to_i }
      image_names.sort!
      
      # create blank captions.yml if it doesn't already exist
      create_captions_file(params[:id])
      captions = YAML.load(File.open(File.join(gallery_dir, 'captions.yml')).read).to_a
      
      new_captions = []
      for i in 0...image_id do
        new_captions[i] = captions[i] || ''
      end
      
      image_names.each do |img|
        FileUtils.mv(File.join(gallery_dir, img.to_s + '.jpg'), File.join(gallery_dir, image_id.to_s + '.jpg'))
        FileUtils.mv(File.join(gallery_dir, img.to_s + '-thumb.jpg'), File.join(gallery_dir, image_id.to_s + '-thumb.jpg'))
        FileUtils.mv(File.join(gallery_dir, 'management', img.to_s + '.jpg'), File.join(gallery_dir, 'management', image_id.to_s + '.jpg'))
        
        new_captions[image_id] = captions[img] || ''
        
        image_id += 1
      end
      
      File.open(File.join(gallery_dir, 'captions.yml'), "w") { |f| f.write(YAML.dump(new_captions)) }
    end
    
    redirect_to :action => 'gallery_management', :id => params[:id], :gallery_id => params[:gallery_id]
  end
  
  def delete_gallery
    @pg = CmsPage.find_by_id(params[:id])
    galleries_dir = File.join(Rails.root, 'public', 'assets', 'content', @pg.path)
    gallery_dir = File.join(galleries_dir, params[:gallery_id])
    
    FileUtils.rm_rf(gallery_dir)
    
    redirect_to :action => 'select_gallery', :id => params[:id]
  end
  
  
  def upload_file
    @pg = CmsPage.find_by_id(params[:id])
    render :partial => 'upload_file'
  end
  
  def receive_file
    @pg = CmsPage.find_by_id(params[:id])
    
    target_dir = File.join(Rails.root, 'public', 'assets', 'content', @pg.path)
    FileUtils.mkdir_p target_dir
    
    data = params[:file][:data]
    original_filename = data.original_filename.strip.gsub(/[\?\s\/\:\\]+/, '-').gsub(/^-/, '').gsub(/-$/, '')
    localfile = File.join(target_dir, original_filename)
    FileUtils.cp(data.tempfile, localfile)
    
    finish_upload_status "'#{File.basename(localfile)}'"
  end
  
  def create_file_link
    @pg = CmsPage.find_by_id(params[:id])
    localfile = File.join(Rails.root, 'public', 'assets', 'content', @pg.path, File.basename(params[:filename]))
    
    @filename = localfile + "?#{File.mtime(localfile).to_i}"
    upload_to_s3(localfile, @pg, ImagineCmsConfig['amazon_s3'][Rails.env]['file_bucket'],
                                 ImagineCmsConfig['amazon_s3']['file_prefix'])
    
    render :partial => 'create_file_link'
  end
  
  
  def upload_thumb
    @pg = CmsPage.find_by_id(params[:id])
    render :partial => 'upload_thumb'
  end
  
  def crop_thumb
    @pg = CmsPage.find_by_id(params[:id])
    localfile = File.join(Rails.root, 'public', 'assets', 'content', @pg.path, File.basename(params[:filename]))
    
    # get out now if user clicked finish
    if params[:next_clicked].to_i != 1
      @image_file = localfile + "?#{File.mtime(localfile).to_i}"
      upload_to_s3(localfile, @pg)
      render :partial => 'crop_results_thumb' and return
    end
    
    
    # if we're still here... let's crop!
    target_dir = File.join(Rails.root, 'public', 'assets', 'content', @pg.path)
    testfile = File.join(target_dir, File.basename(localfile, File.extname(localfile))) + '-croptest' + File.extname(localfile)
    
    # make a smaller version to help with cropping
    im = MiniMagick::Image.from_file(localfile)
    im.resize("500x400>")
    im.write(testfile)
    File.chmod(0644, testfile)
    
    @width = im[:width]
    @height = im[:height]
    @height = 1 if @height == 0
    @image_file = File.basename(testfile)
    @aspect_ratio = @width.to_f/@height
    
    render :partial => 'crop_thumb'
  end
  
  def save_crop_thumb
    @pg = CmsPage.find_by_id(params[:id])
    target_dir = File.join(Rails.root, 'public', 'assets', 'content', @pg.path)
    testfile = File.join(target_dir, File.basename(params[:filename]))
    localfile = testfile.split(/-croptest/).join('')
    
    # need to scale up requested position/dimensions based on how big test image
    # is relative to original image
    orig_im = MiniMagick::Image.from_file(localfile)
    test_im = MiniMagick::Image::from_file(testfile)
    scale = orig_im[:width].to_f / test_im[:width]
    
    x1 = params[:image][:x1].to_i * scale
    y1 = params[:image][:y1].to_i * scale
    width = params[:image][:width].to_i * scale
    height = params[:image][:height].to_i * scale
    
    max_width = params[:image][:max_width].to_i
    max_height = params[:image][:max_height].to_i
    dirty = false
    
    # crop if user selected something
    if params[:image][:width].to_i > 0
      logger.debug "cropping @ (#{x1}, #{y1}) to size #{width} x #{height}"
      orig_im.crop("#{width}x#{height}+#{x1}+#{y1}")
      dirty = true
    end
    
    # resize if the resultant image is bigger than max dims
    if max_width > 0 && max_height > 0
      if orig_im[:width] > max_width || orig_im[:height] > max_height
        logger.debug "resizing to max dims #{max_width} x #{max_height}"
        orig_im.resize("#{max_width}x#{max_height}>")
        dirty = true
      end
    end
    
    orig_im.write(localfile) if dirty
    File.chmod(0644, localfile)
    
    @image_file = localfile + "?#{File.mtime(localfile).to_i}"
    File.unlink testfile
    upload_to_s3(localfile, @pg)
    
    render :partial => 'crop_results_thumb'
  end
  
  def upload_feature_image
    @pg = CmsPage.find_by_id(params[:id])
    render :partial => 'upload_feature_image'
  end
  
  def crop_feature_image
    @pg = CmsPage.find_by_id(params[:id])
    localfile = File.join(Rails.root, 'public', 'assets', 'content', @pg.path, File.basename(params[:filename]))
    
    # get out now if user clicked finish
    if params[:next_clicked].to_i != 1
      @image_file = localfile + "?#{File.mtime(localfile).to_i}"
      upload_to_s3(localfile, @pg)
      render :partial => 'crop_results_feature_image' and return
    end
    
    
    # if we're still here... let's crop!
    target_dir = File.join(Rails.root, 'public', 'assets', 'content', @pg.path)
    testfile = File.join(target_dir, File.basename(localfile, File.extname(localfile))) + '-croptest' + File.extname(localfile)
    
    # make a smaller version to help with cropping
    im = MiniMagick::Image.from_file(localfile)
    im.resize("500x400>")
    im.write(testfile)
    File.chmod(0644, testfile)
    
    @width = im[:width]
    @height = im[:height]
    @height = 1 if @height == 0
    @image_file = File.basename(testfile)
    @aspect_ratio = @width.to_f/@height
    
    render :partial => 'crop_feature_image'
  end
  
  def save_crop_feature_image
    @pg = CmsPage.find_by_id(params[:id])
    target_dir = File.join(Rails.root, 'public', 'assets', 'content', @pg.path)
    testfile = File.join(target_dir, File.basename(params[:filename]))
    localfile = testfile.split(/-croptest/).join('')
    
    # need to scale up requested position/dimensions based on how big test image
    # is relative to original image
    orig_im = MiniMagick::Image.from_file(localfile)
    test_im = MiniMagick::Image::from_file(testfile)
    scale = orig_im[:width].to_f / test_im[:width]
    
    x1 = params[:image][:x1].to_i * scale
    y1 = params[:image][:y1].to_i * scale
    width = params[:image][:width].to_i * scale
    height = params[:image][:height].to_i * scale
    
    max_width = params[:image][:max_width].to_i
    max_height = params[:image][:max_height].to_i
    dirty = false
    
    # crop if user selected something
    if params[:image][:width].to_i > 0
      logger.debug "cropping @ (#{x1}, #{y1}) to size #{width} x #{height}"
      orig_im.crop("#{width}x#{height}+#{x1}+#{y1}")
      dirty = true
    end
    
    # resize if the resultant image is bigger than max dims
    if max_width > 0 && max_height > 0
      if orig_im[:width] > max_width || orig_im[:height] > max_height
        logger.debug "resizing to max dims #{max_width} x #{max_height}"
        orig_im.resize("#{max_width}x#{max_height}>")
        dirty = true
      end
    end
    
    orig_im.write(localfile) if dirty
    File.chmod(0644, localfile)
    
    @image_file = localfile + "?#{File.mtime(localfile).to_i}"
    File.unlink testfile
    upload_to_s3(localfile, @pg)
    
    render :partial => 'crop_results_feature_image'
  end
  
  
  protected
  
    def load_page_objects
      @page_objects = HashObject.new
      @template_options = HashObject.new
      
      if @pg.new_record? && @parent
        @parent.objects.find(:all, :conditions => [ "obj_type = 'attribute'" ]).each do |obj|
          key = "obj-#{obj.obj_type.to_s}-#{obj.name}"
          @page_objects[key] = obj.content
        end
        @parent.objects.find(:all, :conditions => [ "obj_type = 'option'" ]).each do |obj|
          key = "obj-#{obj.obj_type.to_s}-#{obj.name}"
          @page_objects[key] = obj.content
        end
      else
        @tags = @pg.tags.collect { |t| t.name }.join(', ')
        @pg.objects.find(:all, :conditions => [ "obj_type = 'attribute'" ]).each do |obj|
          key = "obj-#{obj.obj_type.to_s}-#{obj.name}"
          @page_objects[key] = obj.content
        end
        @pg.objects.find(:all, :conditions => [ "obj_type = 'option'" ]).each do |obj|
          key = "obj-#{obj.obj_type.to_s}-#{obj.name}"
          @page_objects[key] = obj.content
        end
      end
    end
    
    def load_template_options
      begin
        render_to_string :inline => @pg.template.content
      rescue Exception => e
        logger.debug e
      end
    end
    
    def garbage_collect
      GC.start
    end
    
    def create_captions_file(pg_id, options = {})
      gallery_id = (!options[:gallery_id] ? params[:gallery_id] : options[:gallery_id])
      
      @pg = CmsPage.find_by_id(pg_id)
      galleries_dir = File.join(Rails.root, 'public', 'assets', 'content', @pg.path)
      gallery_dir = File.join(galleries_dir, gallery_id)
      captions_location = File.join(gallery_dir, 'captions.yml')
      
      return if File.exists?(captions_location)
      
      File.open(captions_location, 'w') { |f| YAML.dump([0], f) }
    end
    
    # prerequisites: @pg (CmsPage)
    def load_gallery_settings_from_file(gallery_id, options = {})
      galleries_dir = File.join(Rails.root, 'public', 'assets', 'content', @pg.path)
      gallery_dir = File.join(galleries_dir, gallery_id)
      settings_location = File.join(gallery_dir, 'settings.yml')
      
      ret = {}
      
      if File.exists?(settings_location)
        File.open(settings_location, 'r') { |f| ret = YAML.load(f.read) }
      else
        File.open(settings_location, 'w') { |f| YAML.dump({}, f) }
      end
      
      # set a few defaults
      ret[:slide_duration] ||= 0
      ret[:show_thumbs] ||= true
      
      return HashObject.new(ret)
    end
    
    # prerequisites: @pg (CmsPage)
    def save_gallery_settings_to_file(gallery_id, settings_hash, options = {})
      settings_hash = settings_hash.hash if settings_hash.kind_of?(HashObject)
      
      galleries_dir = File.join(Rails.root, 'public', 'assets', 'content', @pg.path)
      gallery_dir = File.join(galleries_dir, gallery_id)
      settings_location = File.join(gallery_dir, 'settings.yml')
      
      File.open(settings_location, 'w') { |f| YAML.dump(settings_hash, f) }
    end
    
    def resize_image(localfile)
      im = MiniMagick::Image::from_file(localfile)
      
      if im[:width] > GalleryMaxWidth || im[:height] > GalleryMaxHeight
        im.resize("#{GalleryMaxWidth}x#{GalleryMaxHeight}")
        im.write(localfile)
      end
      
      localfile
    end
    
    def create_preview_image(src_file, dest, force = 0, overlay = 'gallery_small_overlay.png', thumb_size = 90)
      dest = File.join(dest, File.basename(src_file)) if File.directory?(dest)
      if !File.exists?(dest) || force == 1
        im = Magick::Image::read(src_file)[0]
        im_overlay = Magick::Image::read(File.join(ImagineCms::Engine.root, 'assets', 'images', 'management', overlay))[0]
        
        im.crop_resized!(thumb_size, thumb_size)
        im = im.composite(im_overlay, Magick::CenterGravity, Magick::OverCompositeOp)
        
        im.write(dest)
        File.chmod(0644, dest)
        
        im = im_overlay = nil
        GC.start
        
        nil
      end
    end
    
    # prerequisites: @pg (CmsPage)
    def create_preview_images(options = {})
      galleries_dir = File.join(Rails.root, 'public', 'assets', 'content', @pg.path)
      session[:broken_galleries] = []
      
      # create preview images if not already made
      Dir.glob("#{galleries_dir}/gallery_*").each do |g|
        begin
          management_dir = File.join(g, 'management')
          FileUtils.mkdir_p(management_dir) unless File.exists?(management_dir)
          
          images = Dir.glob("#{g}/*.{jpg,jpeg,png,gif}")
          preview_images = []
          images.each { |img| preview_images << img unless File.basename(img).include?('thumb') }
          
          # gallery preview image
          preview_image_location = File.join(management_dir, 'preview.jpg')
          unless File.exists?(preview_image_location)
            preview_image = preview_images.first
            create_preview_image(preview_image, preview_image_location, options[:force], 'gallery_preview_overlay.png', 130)
          end
          
          # photo preview images
          preview_images.each { |img| create_preview_image(img, management_dir, options[:force]) }
          
        rescue Exception => e
          # some error handling here
          session[:broken_galleries] << File.basename(g)
          
          log_error(e)
        end
      end
    end
    
    def upload_to_s3(filename, page, bucket = nil, prefix = nil)
      s3retries = 0
      s3success = false
      
      if ImagineCmsConfig['amazon_s3'] && ImagineCmsConfig['amazon_s3']['enabled']
        bucket ||= ImagineCmsConfig['amazon_s3'][Rails.env]['image_bucket']
        prefix ||= ImagineCmsConfig['amazon_s3']['image_prefix']
        
        # set options + metadata
        options = ImagineCmsConfig['amazon_s3']['metadata']
        options[:access] = :public_read
        
        while s3retries < 2 && !s3success
          response = AWS::S3::S3Object.store("#{prefix}/#{page.path.blank? ? 'index' : page.path}/#{File.basename(filename)}", open(filename), bucket, options)
          s3success = response.code == 200
          s3retries += 1
        end
        File.unlink(filename) if s3success
      end
      
      s3success
    end
    
end

module MiniMagick
  class Image
    def crop_resized(ncols, nrows, gravity='Center')
      columns = self[:width].to_i
      rows = self[:height].to_i

      if ncols != columns || nrows != rows
        scale = [ncols/columns.to_f, nrows/rows.to_f].max
        resize("#{scale*(columns+0.5).to_i}x#{scale*(rows+0.5).to_i}")
      end
      
      columns = self[:width].to_i
      rows = self[:height].to_i
      crop("#{ncols}x#{nrows}+0+0", "-gravity", "#{gravity}") if ncols != columns || nrows != rows
    end
  end
end