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