module RailsConnector
  #
  # This controller provides an interface for generating PDFs from CMS objects.
  # It has two modes: internal mode and external mode.
  #
  # =Internal mode
  #
  # In the internal mode the PDF generator comes with a ready-made action, <code>index</code>.
  # You can customize this action and/or add your own custom actions.
  #
  # To add your own action you need to provide:
  # - a controller action
  # - an XML template
  # - an XSL stylesheet
  #
  # The XML template must be named <code><em>ACTION_NAME</em>.html.erb</code> and the XSL stylesheet
  # must be named <code><em>ACTION_NAME</em>.xsl</code>. Place these files into <code>RAILS_ROOT/app/views/pdf/</code>.
  #
  # Use the module <code>RailsConnector::FopOnRails</code> to generate the PDF.
  # Inside your action, the XML template has already been rendered and can be referenced by means of <code>@xml_path</code>.
  # The XSL stylesheet can be referenced by means of <code>@xsl_path</code>.
  #
  # In your ERb template you can use <code>@obj</code> to reference the CMS object and the <code>display_value</code> method to
  # render its attributes:
  #   <h1><%= display_value(@obj.title) %></h1>
  #
  # The named route is <code>pdf_with_action</code>:
  #   <%= link_to('Generate custom PDF', pdf_with_action_path(:action => 'my_action', :id => 1234)) %>
  #   # This will produce <a href="/pdf/my_action/1234">Generate custom PDF</a>
  #
  # =External mode
  #
  # In the external mode the PDF generator has a named route <code>pdf_external</code>
  # and is accessed through the URL <code>/pdf/external</code>.
  #
  # In order to produce a PDF file, the GET parameters <code>xml_url</code> and <code>xsl_url</code>
  # need to be provided.
  #
  # Optional GET-parameters are:
  #   filename: string
  #   tidy:     true or false (default is false)
  #
  # Example:
  #   curl -d "xml_url=http://example.com/test.xml&xsl_url=http://example.com/test.xsl" -g http://localhost:3000/pdf/external
  #
  # To allow a host to be the source for XML or XSL, add it to the so called "white list". Add
  # following to <code>RAILS_ROOT/config/initializers/rails_connector.rb</code> to do that:
  #   RailsConnector::Configuration::PdfGenerator.host_whitelist(
  #     'example1.com',
  #     'example2.com',
  #     'example3.com'
  #   )
  #
  # You can add as many hosts as you need. If no hosts have been added to the "white list", then the
  # PDF generator will refuse to produce PDF files from any external source.
  #
  # If the host given through <code>xml_url</code> or <code>xsl_url</code> is not included in the
  # "white list", then an error message will be rendered as inline text with response status "403
  # Forbidden".
  #
  # @api public
  class DefaultPdfController < DefaultCmsController
    # Returns false by default to exclude this controller from regular Obj dispatch.
    # @see DefaultCmsController#use_for_obj_dispatch?
    def self.use_for_obj_dispatch?
      # If this method would to return true, an Obj with ObjClass "pdf" would be delivered by
      # the PdfController as an actual generated Pdf, which would be surprising to most developers.
      false
    end

    before_filter :dump_obj
    before_filter :compute_xsl_path
    before_filter :send_pdf

    after_filter :cleanup_xml

    #
    # Generates a PDF from a CMS object.
    #
    # Takes a GET-parameter, <code>id</code>, to determine the object.
    #
    # As a default, the built-in XML template and XSL stylesheet will be used. You can replace them
    # with the files <code>RAILS_ROOT/app/views/pdf/index.html.erb</code> and <code>RAILS_ROOT/app/views/pdf/index.xsl</code> respectively.
    #
    # By default, the PDF will contain the title and the body of the object.
    # The PDF will be named <code><em>NAME_OF_THE_OBJECT</em>.pdf</code>.
    #
    # The named route is called <code>pdf</code> and is accessed through
    # <code>/pdf/<em>OBJECT_ID</em></code>.
    #
    # Example:
    #
    #   <%= link_to 'Generate PDF', pdf_path(@obj) %>
    #   # This will generate a PDF containing the title and the body of the current object
    #
    # @api public
    def index
    end

    protected

    def send_pdf
      send_file(
        FopOnRails.generate_pdf(@xml_path, @xsl_path),
        :filename => "#{@obj.name}.pdf",
        :type => 'application/pdf'
      )
    end

    private

    def dump_obj
      base_folder = Rails.root + "tmp"
      FileUtils.mkdir_p(base_folder)
      @xml_path = File.join(base_folder, "#{Time.now.to_f}_#{$$}")
      data = render_to_string(:controller => 'pdf', :action => action_name, :layout => false)
      File.open(@xml_path, 'w') do |f|
        f.write(data)
      end
    end

    def compute_xsl_path
      @xsl_path = nil
      view_paths.each do |view_path|
        xsl_path = "#{view_path}/#{controller_name}/#{action_name}.xsl"
        if File.exist?(xsl_path)
          @xsl_path = xsl_path
          return
        end
      end
      raise 'No XSL template found' unless @xsl_path
    end

    def cleanup_xml
      FileUtils.rm_rf(@xml_path)
    end
  end
end