RoR Prawn PDF back of sheet - ruby-on-rails

I want to be able to put a default back of sheet on my generated PDF,
has someone already did this ?
I was wondering if there is something in Prawn to do that, something like an event which is triggered when a new page has been started ?
I made many search about this and cannot see any way to do it, your help will be precious.
Here some code to show you the way I generate my PDF :
Prawn::Document.generate(invoice_file, :print_scaling => :none, :top_margin => margin_top) do |pdf|
pdf.font_size=11
pdf.define_grid(:columns => 12,:rows => 16, :gutter => 10)
pdf.font "Helvetica"
#some code to fill my pdf
#And what I was trying to deal with it :
pdf.repeat(:all, :dynamic => true) do
pdf.start_new_page
#some code to fill my new page
end
end
This is almost working, I get the new created page but it's generating another blank page that I don't need, because of the new one I think. If someone has an idea, I take !
Thanks ! Flo.

Related

Can't add parameters to the picture model with the use of Ckeditor, Paperclip and Ruby on Rails

I have a CKeditor picture model like this:
class Ckeditor::Picture < Ckeditor::Asset
before_save :set_vars
has_attached_file :data,
:url => "/ckeditor_assets/pictures/:id/:style_:basename.:extension",
:path => ":rails_root/public/ckeditor_assets/pictures/:id/:style_:basename.:extension",
:styles => { :content => '600>',:medium => '300x300', :quintet => '150x150', :thumb => '118x100#' }
validates_attachment_size :data, :less_than => 2.megabytes
validates_attachment_presence :data
def url_content
url(:content)
end
protected
def set_vars
#self.assetable_id = id
#self.assetable_type = controller_name
end
end
What I want is that the 'assetable_id' and 'assetable_type' are being filled during the creation of a new picture (because there are columns for those in the database table). And I want to pass them variables. Like the id from the post/event/user that the picture is linked to. And ofcourse the type of 'model' the picture is assigned to - again post/event/user.
I don't know if this is the right solution but I don't know how else to fix it. Documentation online about the CKeditor gem, the config settings and Rails is horrible - I'm searching for hours and hours and I can't find a single thing that closely resembles what I want - so please help.
I know how to adjust the parameters for the upload but none of them seem to do something that I want:
Started POST "/ckeditor/pictures?CKEditor=post%5B14%5D&CKEditorFuncNum=1&
langCode=en&%3Aassetable-id=0&assetable_type=post&
authenticity_token=fsKA68sxkzQpiSMmtcP782i4oI%2FA6KSIsSZuwO5zDWA%3D" for 127.0.0.1 at
2013-04-15 10:05:06 +0200
Processing by Ckeditor::PicturesController#create as HTML
Parameters: {"upload"=>#<ActionDispatch::Http::UploadedFile:0x007f94f80beef8
#original_filename="kb_new.png", #content_type="image/png", #headers="Content-
Disposition: form-data; name=\"upload\"; filename=\"kb_new.png\"\r\nContent-Type:
image/png\r\n", #tempfile=#<File:/tmp/RackMultipart20130415-3130-1ls814r>>,
"CKEditor"=>"post[14]", "CKEditorFuncNum"=>"1", "langCode"=>"en", ":assetable-id"=>"0",
"assetable_type"=>"post",
"authenticity_token"=>"fsKA68sxkzQpiSMmtcP782i4oI/A6KSIsSZuwO5zDWA="}
User Load (0.2ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = 1 LIMIT 1
[ALL THE FORMATING STUFF]
SQL (0.6ms) INSERT INTO `ckeditor_assets` (`assetable_id`, `assetable_type`, `created_at`, `data_content_type`, `data_file_name`, `data_file_size`, `height`, `type`, `updated_at`, `width`) VALUES (NULL, '0', '2013-04-15 08:05:07', 'image/png', 'kb_new.png', 291770, 419, 'Ckeditor::Picture', '2013-04-15 08:05:07', 450)
[paperclip] Saving attachments.
(62.5ms) COMMIT
Rendered text template (0.0ms)
Completed 200 OK in 929ms (Views: 0.5ms | ActiveRecord: 63.6ms)
Also check out this question:
How to set the size of an image within CKeditor in Ruby On Rails according to the picture model?
I actually figured this out and have a solution that works. I too tried googling everything and found literally ZERO information about how to add custom params to a Ckeditor upload within Rails.
Here's what I did:
Let's start with the form itself. With Ckeditor, everything is driven by the URL. So when you click the image icon, and the pop-up window appears, and then you click the BROWSE SERVER button, you'll notice it opens a new pop-up which has a URL. This is the 1st URL you need to modify. Then, when you actually upload an image, it will send a post request to another URL. That's the 2nd URL you need to modify.
By default, these URL's are set to the "index" and "create" actions on the Ckeditor::PicturesController. Here's a link to this controller file in the gem's repo, you will need this later - https://github.com/galetahub/ckeditor/blob/2dd963fae117726976e5bf1dc264e27d3741fea8/app/controllers/ckeditor/pictures_controller.rb
So logically, what we need to do is modify these URL's so that we can add our own custom code to control what happens. So here's what I did to do that (notice the "ckeditor" hash):
<%= cktext_area_tag 'column1_body', #section,
:ckeditor => {
filebrowserImageUploadUrl: "/ckeditor/pictures?website_id=#{#website.id}",
filebrowserImageBrowseUrl: "/picture_overrides"
} %>
cool.
2.So first I modified the filebrowserImageBrowseUrl with '/picture_overrides'. This means that when someone clicks the BROWSE SERVER button, it will call the 'index' action on my PictureOverridesController. So I added the route, created the controller file, and views.
For the views, I copied the view file exactly from the gem, which is here - https://github.com/galetahub/ckeditor/blob/master/app/views/ckeditor/pictures/index.html.erb . Note: there is a partial in there, so you'll need to grab that too.
Now on my new 'PictureOverridesController' file, I edited the 'index' action as follows:
def index
# I need to find the website
#website = Website.find_by_subdomain(request.subdomain)
# I can scope #pictures to only contain the images the user should see.
#pictures = Ckeditor::Picture.where(website_id: #website.id)
end
Then, some other private methods are called during this process, so you need to add the following code to this controller file as well (just cut & paste). You'll notice I grabbed this code from the same controller on the gem file.
protected
def find_asset
#picture = Ckeditor.picture_adapter.get!(params[:id])
end
def authorize_resource
model = (#picture || Ckeditor.picture_model)
#authorization_adapter.try(:authorize, params[:action], model)
end
With this, now my users will only see the images that have the correct website_id.
Now, I need to make sure the website_id is set when a user uploads an image through Ckeditor.
3.While you could certainly modify the create action on this controller file, and set the URL to this, I chose a different way. I simply added the website_id to the URL, as you could see above. Unfortunately, the Ckeditor::Picture instance wont save this automatically. So I decided to accomplish this with an after_filter in the ApplicationController, like so:
after_filter :ckeditor_add_website_id
def ckeditor_add_website_id
if params[:controller] == 'ckeditor/pictures' && params[:action] == 'create'
#website = find_website_by_domain_subdomain(request)
#picture.update_attributes(website_id: #website.id)
end
end
It just works.
Lastly -- DONT FORGET to add the custom URL's to any form where you are utilizing Ckeditor.
In my opinion the simplest solution:
#controllers/application_controller.rb
protected
def ckeditor_pictures_scope(options = { :assetable_id => "#{current_page.id}" ,:assetable_type => "Page" })
ckeditor_filebrowser_scope(options)
end
def ckeditor_attachment_files_scope(options = { :assetable_id => "#{current_page.id}" ,:assetable_type => "Page" })
ckeditor_filebrowser_scope(options)
end
def ckeditor_before_create_asset(asset)
asset.assetable = current_page if current_page
return true
end
Thank you to nfriend21 and to wacaw for their solutions.
This problem have distressed me for a week!
The solution of wacaw is the simplest and cleanest, but I am inspired by nfriend21 to complete it. It can be useful for inexperienced like me.
I have a page with many editors, each with a different ID (named column_id).
I want that the column_id is saved as asset_id of type Column (object defined in my application).
1) I change the urls for all the editors in the page:
for (instance in CKEDITOR.instances) {
var editor = CKEDITOR.instances[instance];
var id = editor.name.substr(6);
if(editor) {
editor.config.filebrowserImageBrowseUrl = "/ckeditor/pictures?column_id=" + id;
editor.config.filebrowserImageUploadUrl = "/ckeditor/pictures?column_id=" + id;
}
}
note that I extract the id of the editor as a substring of the ID of the div containing it... i.e. editor134, editor245, etc.: i remove first 6 characters.
Note also I use the original PicturesController of ckeditor's gem.
2) now, modifying a bit the wacav solution, in ApplicationController:
#controllers/application_controller.rb
protected
def ckeditor_pictures_scope(options = { :assetable_id => params[:column_id], :assetable_type => "Column"})
ckeditor_filebrowser_scope(options)
end
# to be modified: at this moment I'm interested in pictures only
def ckeditor_attachment_files_scope(options = { :assetable_id => "#{current_page.id}" ,:assetable_type => "Page" })
ckeditor_filebrowser_scope(options)
end
def ckeditor_before_create_asset(asset)
asset.assetable = Column.find(params[:column_id])
return true
end
I hope it can be helpful to others.
What you are searching for is using polymorphic Picture model, which will belong to many other models.
Please, read down here http://rails-bestpractices.com/posts/45-use-sti-and-polymorphic-model-for-multiple-uploads , as it's pretty well described.

How do I create a temp file and write to it then allow users to download it?

I'm working on my first application and I need some help with allowing my users to download a text file with certain variables that are being displayed on the page.
Take a shopping list for example.
Let's say you allow your users to create a shopping list of products, and then display the shopping list with the items on a shopping list page,
e.g. localhost:3000/list/my-list
Take a look at the example code below (which is probably incorrect):
File.open('shopping_list.txt', 'w') do |file|
file.puts 'Item 1: #{product_1.name}'
file.puts 'Item 2: #{product_2.name}'
file.puts 'Item 3: #{product_3.name}'
end
Which then creates a text file that has the following content:
Item 1: Eggs
Item 2: Butter
Item 3: Bread
Users should then be able to download this file (i don't want this file to be stored on the server) via a download link.
I have no idea how to achieve this, but I'm hoping you guys can guide me. :D
TL;DR
create text files populated with model data (perhaps create a method to achieve this?)
text files should not be stored on the server, but created as users click the download button (not sure if this is the rails way but perhaps someone could show me a better way)
I am assuming there is a resource for List with the attribute name as the name of the list and a list has_many Item which has an attribute description
First off, create a download path change your routes config/routes.rb
resources :lists do
member {get "download"}
end
Now if you run a rake routes in the console you should see a route like
/lists/:id/download
Whats more you should now have the helpers download_list_url & download_list_path to use in your view like
<ul>
<% #lists.each do |list| %>
<li> <%= list.name %> - <%= link_to 'Download List', download_list_path(list) %> </li>
<% end %>
</ul>
In your lists_controller add the action, and as you dont actually want to keep the file on the server disk just stream the data as a string
def download
list = List.find(params[:id])
send_data list.as_file,
:filename => "#{list.name}.txt",
:type => "text/plain"
end
Finally you see I have used a as_file method which you should add to the model (I prefer not to do this stuff in controllers, fat models, skinny controllers). So in the List model
def as_file
output = [self.name]
self.items.each {|item| output << item.description }
output.join("\n")
end
You say you don't want to store the file on the server, but "download" it on request; this sounds like you just want to generate and deliver a text document in response to the download link. There are several approaches, but you want to be sure of setting the mime-type so the browser sees it as a text file instead of an html document.
product_info = [
"Item 1: #{product_1.name}",
"Item 2: #{product_2.name}",
"Item 3: #{product_3.name}",
].join("\n")
render :text => product_info # implies :content_type => Mime::Type["text/plain"]
BTW, your example with open/puts above won't output what you think since single-quoted strings don't interpolate.
so, you wish to :
create text files populated with model data (perhaps create a method
to achieve this?)
text files should not be stored on the server, but
created as users click the download button (not sure if this is the
rails way but perhaps someone could show me a better way)
You have the right idea, here's what to do :
Create a method in your model to generate the text file contents. Let's say this method is called list_data
It seems like you have an existing controller action called my_list. Hence we can call our new method in the controller like so :
.
def my_list
# pre-existing code
respond_to do |format|
format.html # show html page as before
format.text do
send_data #list.list_data, :content_type => 'text/plain', :filename => 'my-shopping-list.txt'
end
end
end
To link to the download, just use link_to :action => my_list, :format => 'text'
See http://api.rubyonrails.org/classes/ActionController/DataStreaming.html#method-i-send_data for full docs on send_data
Caveat & explanations : Using the method above, there isn't really an explicit creation of files, Rails is streaming it for you. Hence this method is not suitable for very large files, or when the generation of the file content will take a while. Use a delayed method to generate the file and store it - the file contents somewhere if that's the case - but we can use send_data once it has been generated
You could try a combination of TempFile and send_file. In your controller action ..
file = Tempfile.new('foo')
file.write("hello world")
file.close
send_file file.path
At Rails 2.3 you can use Template Streaming. Working with Redmine I can remember something like that, you have to adapt for your case. Reference: Streaming and file downloads
require "prawn"
class ClientsController < ApplicationController
# Generate a PDF document with information on the client and return it.
# The user will get the PDF as a file download.
def download_pdf
client = Client.find(params[:id])
send_data(generate_pdf, :filename => "#{client.name}.pdf", :type => "application/pdf")
end
private
def generate_pdf(client)
Prawn::Document.new do
text client.name, :align => :center
text "Address: #{client.address}"
text "Email: #{client.email}"
end.render
end
end
Using the Thong Kuah you must just change the "content_type" param:
def my_list
# pre-existing code
respond_to do |format|
format.html # show html page as before
format.text do
send_data #list.list_data, :content_type => 'text/plain', :filename => 'my-shopping-list.txt'
end
end
end

Partial not updated on first click

I am having problems with a remotely executed action and a partial that doesn't update the first time I click the link.
Inside the view (a partial named books) I am creating a link:
link_to "⊗", read_book_path(book), :remote => true
The read_book_path is defined in routes.rb
There is also a conditional that displays a different text when that book is read.
Inside my controller, I have defined a new action:
def read
#books = Book.all
#book = Book.find(params[:id])
#book.read = !#book.read
#book.save
respond_to do |format|
format.html { redirect_to(books_url) }
format.js {render :layout => false, :locals => { :book => #book } }
end
end
This means I need a file read.js.erb, this file's content is:
$("#books").empty().html("<%= escape_javascript( render(:partial => "books") ) %>");
When I click the link, I can see in the terminal window that the database field is updated but the partial is not. Clicking the same link again updates the partial.
Changing the link to :remote => false also works but the page reloads (as expected).
I have tried to debug it with Safari and the Developer tools and I can see the server's response when clicking the link for the first time.
Something is wrong there, the HTML generated by <%= escape_javascript( render(:partial => "books") ) %> contains the wrong HTML with the old content of the partial. Only the second or third click shows the updated HTML.
I have integrated jquery-ujs - is that the reason the partial doesn't update the first time or am I missing something else?
This really gave me a headache, can you help me?
Edit:
If that helps: I created a listener in application.js to ajax:before and ajax:complete. The first one shows a little spinner, the second one hides it.
When I click the link, the spinner shows but it doesn't hide.
It looks like you have an ordering problem that's causing the trouble. You're capturing a complete set of books into the #books variable and then modifying a separate copy of a single book. This change will not be propagated back.
# Load and modify the one book by flipping the flag
#book = Book.find(params[:id])
#book.read = !#book.read
#book.save
# Load all books
#books = Book.all
As a note this is an extremely inefficient way of doing things, so I hope you're not working on a large amount of data. You might find it's easier to do this by simply toggling the one field with a simple UPDATE query:
Book.update_all({ :read => true }, { :id => params[:id] })
I'm not sure why you're calling $(...).empty().html(...) instead of simply $(...).html(...) since the html() method should replace the HTML wholesale with no need to clear it in advance.
One thing that might help is using .js.rjs where the equivalent would be:
page[:books].replace_html(:partial => 'books')
With simple JavaScript, RJS allows you to eliminate a lot of the drudgery. You can use JS in RJS as well for cases where there is no equivalent:
page << '$("#books").empty()'
page[:books].replace_html(:partial => 'books')
To make this more Rails friendly, you could call your partial _book which would make the local variables redundant. Each partial has a default variable with a name matching the template name:
render(:partial => 'book', :collection => #books)

How to save a rendered view as a static file?

My Rails 2.3 app generates a page in HTML/CSS or as a word doc. I'd like to save that html file to the filesystem as a static file (ie. filename.html or filename.doc). I plan to have a preview action w/ the fully rendered page and a 'save report' button. Our users will access those static files later. (I'll save the path to the db.)
Any suggestions for how to do this?
I'm as far as creating a file and saving it, but I'm not sure how to get my rendered view into it. Bonus points if anyone knows how to save it up to S3! Many thanks!
render_to_string is your friend. One you have it in a string, burn it to file in the usual way.
class FooController
def save_foo_to_disk
data = render_to_string( :action => :index )
File.open(file_path, "w"){|f| f << data }
flash[:notice] = "saved to #{file_path}"
end
end
As far as S3 goes, see the aws-s3 gem. It seem to do what you are after. Usage is a little like this.
AWS::S3::Base.establish_connection!(
:access_key_id => 'abc',
:secret_access_key => '123'
)
S3Object.store(file_name, data, 'bucket-name')
Have fun, and don't run with scissors.
Another way is adding an after_action to the controller, and in that action using response.body to access rendered content. In this way, your controller can respond to client as normal, save rendered content to database in meanwhile.

Problems with Prawnto options

I'm using Prawnto to generate PDFs in my Rails app. I want three specific options set for my PDFs:
I don't want it to start with a blank page
I want it to download directly (not inline)
I want to specify the filename
Here's my controller method:
def print
#purchase = Purchase.find(params[:id])
prawnto :prawn=>{:skip_page_creation=>true}, :inline=>false, :filename=>#purchase.deal.name + "-" + #purchase.customer.name+".pdf"
end
Without the :skip_page_creation option, the other two options (inline and filename) work fine. But when I add the skip_page_creation option, it goes inline with a default filename. And of course, if I remove skip_page_creation, I get a nice downloaded PDF with a first blank page.
The docs for this library leave something to be desired, but can anyone point me in the right direction?
Cheers!
Aaron.
I've just tried this by changing one of my inline examples which worked ok:
module SharedPdfs
def show
prawnto :prawn => {:skip_page_creation=>true}, :inline => false, :filename => "results_pdf.pdf"
render :template => '/results/show'
end
end
Had a quick look at the prawnto source and it should pickup your prawn options not sure why it isn't but at least you've got it working for now.

Resources