Papermill
-
Asset management made easy, 10 minutes integration.
-
All-you-can-eat glue around Polymorphic Paperclip table, SWFUpload & JQuery.
-
Associate any image or list of images with any model and any key.
Install the gem
sudo gem install papermill
Try the demo
rails -m http://github.com/bbenezech/papermill/raw/master/demo.txt papermill-example
Out-of-the-box compatibility with :
-
Formtastic # use :as => :[image|asset](s)_upload
-
JGrowl # for notifications (included)
-
FaceBox # for popups (included)
-
Stringex # (or any String#to_url) for asset filename/url generation
Navigator minimal requirements:
-
IE6+
-
Flash 9+
-
Javascript ON
Check your audience.
Server requirements:
-
Rails 2.3.
-
Paperclip 2.3.1.1 (loaded with gem dependency)
-
Front web server serving static assets if present, and forwarding demand to rails if not. Any classic installation will do that by default.
-
NOT compatible with Heroku/S3
Installation
Once gem is installed
Generate the migration
./script/generate papermill_table PapermillMigration
Edit it and migrate
rake db:migrate
Copy static assets to your public directory
./script/generate papermill_assets
Create the option file config/initializers/papermill.rb
./script/generate papermill_initializer
Go have a look at config/initializers/papermill.rb
In environment.rb
...
Rails::Initializer.run do |config|
...
config.gem papermill
end
In your layout
Quick version
Inside <head></head>
<%= papermill_stylesheet_tag %>
Before </body> (best practice for javascript loading)
<%= papermill_javascript_tag :with_jquery => "no_conflict" %>
You don’t need :with_jquery if load it by yourself. Pass “no_conflict” if you use the default Prototype library, or some other ‘$’ library (mootools..)
In a real-world production application, you could use something like this, and adapt it to your own needs
Inside <head></head>
<% unless @content_for_papermill_inline_js.blank? %>
<%= javascript_include_tag "/facebox/facebox.js", "/jgrowl/jquery.jgrowl_minimized.js", "/papermill/jquery.Jcrop.min.js", "/swfupload/swfupload.js", "/papermill/papermill.js", :cache => "papermill" %>
<script type="text/javascript">
jQuery(document).ready(function() {
<%= yield :content_for_papermill_inline_js %>
}
</script>
<% end %>
Before </body>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js" type="text/javascript"></script>
<script src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.2/jquery-ui.min.js" type="text/javascript"></script>
<% unless @content_for_papermill_inline_js.blank? %>
<%= stylesheet_link_tag("/facebox/facebox.css", "/jgrowl/jquery.jgrowl.css", "/Jcrop/jquery.Jcrop.css", "/papermill/papermill.css", :cache => "papermill") %>
<style type="text/css">
<%= yield :papermill_inline_css %>
</style>
<% end %>
Security
URL-hacking
Maybe you don’t want users to use your application as a thumbnailing farm for their own uploaded images, or you have protected members areas and you don’t want users to ‘browse’ others members file.
-
Brute solution: pass :use_url_key to true in the options (config/initializers/papermill.rb). A crypted hash unique to your application and to each asset and to the requested style will be added to the URL. No more happy-guessing of anything. Do that first before going live, or you’ll have to migrate all assets…
-
pass :alias_only to true. This will disable the possibility to generate thumbnails with a papermill string in the url, but won’t do anything for the member area thing. Plus you will have to use aliases only, form helpers included (pass :thumbnail => { :style => :some_alias })
Usage
Assetable is the class that has_many papermill_assets (i.e. the class with the papermill declaration)
Assetable declaration
You can have one :default association (his settings will be used for unfound associations) and as many other associations as you want in your model. You can define a papermill relationship dynamically: just do smtg like Assetable.papermill(:dynamic_key, {}) when you need to. Perfect for CMS where associations are created by users. Then you’ll be able to use assetable.dynamic_key to retrieve the associated assets. If you don’t send the {}, default options from default association will be used, which may or may not be what you want.
Actually, the form helper leverages this when you use a :key that doesn’t exist: it will create a new Papermill relationship whith :key as the name and options from the :default declaration if any found on the model.
If you don’t need dynamic keys, just declare your associations in the model, like this :
class Article
papermill :default
papermill :images
papermill :pdf_version
papermill :cover_image
papermill :illustrations
end
Form helpers
Example form:
form_for @assetable do
# I need a simple asset upload field :
f.asset_upload :pdf_version
# Now I need to be able to upload as many documents as I need, and sort them at will
# no document should be bigger than 1MB (respect the quoting!)
# and I don't want the mass_edit feature
f.assets_upload :documentation, :swfupload => { :file_size_limit => "'1 MB'" }, :mass_edit => false
# I need to display *one* cover *image*, format will be 200x200
# targetted_size will give the uploader hints when cropping the image after upload : desired display size and wanted aspect-ratio.
# Better than cropping automatically in the center if the character's head is in the upper-left corner..
# :thumbnail => { :width & :height } set the dimensions of the preview thumbnail
# And finally, I need a 200x200# crop for preview, not the default 200x200> that would be generated by default ("#{:width}x#{:heigth}>")
f.image_upload :cover_image, :targetted_size => "200x200", :thumbnail => { :width => 200, :height => 200, :style => "200x200#" }
# Now the image gallery, sortable.
# I use :gallery => { :lines & :columns } to give the number of lines/columns,
# and some CSS will be generated to size the gallery perfectly,
# according to the thumb size inside the gallery and their padding/margin/border sizes.
# the number of lines will increase if needed when uploading
f.images_upload :illustrations, {
:thumbnail => {
:width => 100,
:height => 70
},
:gallery => {
:columns => 8, # number of columns
:lines => 2, # number of lines
:vpadding => 2, # vertical padding around each thumb
:hpadding => 2, # horizontal one
:vmargin => 3, # vertical margin
:hmargin => 1, # horizontal one
:border_thickness => 2 # border size around each thumb
}
}
end
With Formtastic, pass
:as => (:image_upload | :images_upload | :asset_upload | :assets_upload)
And add your options as you would with the normal helpers.
With FormTagHelpers, use (image_upload_tag | images_upload_tag | asset_upload_tag | assets_upload_tag) @assetable, :key, options
image_upload_tag @article, :cover_image, :targetted_size => "200x200"
Asset editing
-
double-click on any uploaded asset in any form-helper to access & edit his properties
-
then double-click image to crop it if it’s an image. You’ll then access a Jcrop window. Pass :targetted_size => “widthxheigth” to lock aspect-ratio and default the selection size to widthxheigth.
Thumbnails
On-the-fly request time processing:
PapermillAsset#url(papermill string (see 1.)) # path and url behave the same way
PapermillAsset#url(papermill alias (see 2.))
Pros: fast. Nothing done upon page rendering. If asset isn’t found by Apache/NGinx, then request is passed to rails, which will create it, once.
Cons: need to setup an alias in the options if you want to define use a hash instead of a papermill string (for custom watermark)
Render time processing:
PapermillAsset#url!(papermill string (see 1.)) # path! and url! behave the same way
PapermillAsset#url!(papermill alias (see 2.))
PapermillAsset#url!(papermill hash (see 3.))
Pros: can use a hash directly in the url call.
Cons: needs a thumbnail presence check at each render.
1. Papermill String
Consist of:
-
an ImageMagick geometry string (ex: “100x100>”, “original”, “100x#”, etc.)
-
an optional watermark (-wm) flag # will use option for URI
-
an optional copyright (©) flag # will use copyright text after the “©” or options
Examples:
image_tag @article.covers.first.url("100x100")
image_tag @article.covers.first.url("original©")
image_tag @article.covers.first.url("100x100#-wm©")
image_tag @article.covers.first.url("100x200#©papermill")
2. Papermill Alias
Those are application-wide, set them in the options
Consist of:
:geometry => "ImageMagick-geometry-string"
:copyright => true | "copyright" # If true, the asset copyright field will be used. Edit the asset.
:watermark => true | URI # If true, will use options[:watemark]
Examples:
#config/initilializers/papermill.rb
# snip
:aliases => {
:thumb_copyrighted => {
:geometry => "100x100",
:copyright => "papermill",
},
:thumb_copyrighted_dynamically => {
:geometry => "100x100",
:copyright => true
},
:thumb_watermarked_with_rails => {
:width => "100",
:height => "100",
:watermark => "/images/rails.png"
}
}
Then in your views, simply do
image_tag @article.covers.first.url(:thumb_copyrighted)
3. Papermill Hash
Same as aliases, but defined directly in #url!() Plus you can add a :name that will be used for style-name (defaults to a md5 of the hash)
Example:
image_tag @article.covers.first.url(
:geometry => "100x100",
:watermark => "/images/rails.png",
:copyright => "papermill",
:name => "thumbnail_watermarked_and_copyrighted"
)
Resource access
Papermill generates an #<association_key> association
@entry.mug_shots.first
@entry.diaporamas.each do |image| ..
# etc.
Using PapermillAsset
@asset = @entry.mug_shots.first
image_tag @asset.url # original
image_tag @asset.url("100x>") # assuming asset is an image
image_tag @asset.url(:big) # assuming you have a :big alias
@asset.name
@asset.content_type
@asset.path
@asset.path("100x>")
# etc.
Translations:
Papermill is fully I18n-able. Copy config/locales/papermill.yml to your root config/locale folder to modify any wording in a any locale.
Copyright © 2009 Benoit Bénézech, released under the MIT license