papermill 0.14.3 → 0.16.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -1,6 +1,8 @@
1
1
  .DS_Store
2
+ test/*.log
2
3
  log/*.log
3
4
  tmp/**/*
4
5
  config/database.yml
5
6
  db/*.sqlite3
7
+ test.sqlte3
6
8
  papermill.gemspec
data/README.rdoc CHANGED
@@ -1,125 +1,171 @@
1
1
  = Papermill
2
2
 
3
- Asset management made easy.
3
+ Asset management made easy. Now in pre-1.0.0 release!
4
4
 
5
- == Install the gem
5
+ == Install the gems
6
6
 
7
- $ gem source -a http://gemcutter.org # Needed for paperclip (installed as a dependency) : Rubyforge's version is too old..
7
+ $ gem source -a http://gemcutter.org
8
8
  $ sudo gem install papermill
9
9
 
10
10
  == Try the demo
11
11
 
12
12
  $ sudo gem install sqlite3-ruby
13
13
  $ rails -m http://github.com/bbenezech/papermill/raw/master/installation-template.txt papermill-example
14
- $ cd papermill-example
15
- $ ./script/server
16
- Open localhost:3000 in your browser and try to create an article with assets but without title
17
-
14
+
15
+ == Features
16
+
17
+ Loads of them
18
+
19
+ === Ajax uploading form helpers through SWFUpload:
20
+
21
+ * image_upload => unique image upload field, with preview
22
+ * images_upload => sortable image gallery upload field
23
+ * asset_upload => simple one asset field
24
+ * assets_upload => sortable asset list field
25
+
26
+ === Choose thumbnail size for images previews :
27
+
28
+ * {:thumbnail => {:width => 100, :height => 100}}
29
+ * {:thumbnail => {:style => "100x100>"}}
30
+ * {:thumbnail => {:width => 100, :aspect_ratio => 4.0/3.0 }}
31
+
32
+ === Asset edit form:
33
+
34
+ * double-click on any asset in any helper to access&edit his properties
35
+ * with pop-up/shadowbox/facebox, out of the box (or use your own pop-up system, dead-easy)
36
+
37
+ === Lazy created thumbnails
38
+
39
+ * thumbnails are generated the first time they are asked-for, and only in the requested size.
40
+ * no need to register thumbnail size anywhere: my_asset.url("100x100>")
41
+
42
+ === Alias handling, declaration application-wide
43
+
44
+ * :big_alias => {:geometry => "1000x>"}
45
+ * :other_alias => "100x>"
46
+ * :third_alias => {:geometry => '100:122', :my_other_keys => 'blblabla'} # if you have a customed Paperclip::Thumbnail processor, you can pass any values you need.
47
+ * and use them when you need them : my_asset.url(:big_alias)
48
+
18
49
  == Papermill comes in 2 flavors:
19
50
 
20
51
  === Generic catch-all declaration
21
52
 
22
- papermill my_option_hash # in your papermilled assetable model
23
- assets_upload(:my_key, my_option_hash) # form helper call
24
- f.input :my_key, :as => :assets_upload, my_option_hash # if you are using formtastic
25
- @assetable.assets(:my_key) # data access in your view
53
+ papermill {options} # in your papermilled assetable model
54
+ @article.assets(:any_key, options_hash) # data access
26
55
 
27
56
  === Association specific declaration
28
57
 
29
- papermill :my_association, my_option_hash # in your papermilled assetable model
30
- assets_upload(:my_association, my_option_hash) # form helper call
31
- @assetable.my_association # data access in your view
58
+ papermill :my_association, options_hash # in your papermilled assetable model
59
+ @article.my_association # data access
60
+
61
+ == Usage:
32
62
 
63
+ === Model declaration
33
64
 
34
- In both case, you can specify a PapermillAsset subclass to use with :class_name => MyPapermillAssetSubclass in the option hash.
65
+ You can have a generic association and as many declarative associations as you want in your model. Papermill will always use specific if found.
66
+
67
+ article.rb
68
+ class Article < ActiveRecord::Base
69
+ papermill :class_name => ColorAsset, other_options..
70
+ end
71
+
72
+ entry.rb
73
+ class Entry < ActiveRecord::Base
74
+ papermill :mug_shot, other_options.. # default class_name is built-in PapermillAsset
75
+ papermill :diaporama, :class_name => ColorAsset, other_options..
76
+ end
35
77
 
36
- You can have a catch-all declaration and as many specific association as you want in your model (as long as they use different keys).
78
+ color_asset.rb # You should add columns to papermill_assets and use STI on PapermillAsset to extend defaults capabilities (or re-open PapermillAsset and monkey-patch it..)
79
+ class ColorAsset < PapermillAsset
80
+ named_scope :red, :conditions => {:color => 'red'}
81
+ end
37
82
 
38
- It's up to you. You can use the first one only, the second only or both.
83
+ === Form helpers
39
84
 
40
- See papermill_module.rb for the complete list of options.
85
+ FormHelpers
86
+ form_for @article do
87
+ f.image_upload :cover_image, options_hash
88
+ f.images_upload :illustrations, options_hash
89
+ f.asset_upload :pdf, options_hash
90
+ f.image_upload :other_ressources, options_hash
91
+ end
92
+
93
+ Or with formtastic :
94
+ semantic_form_for @article do |f|
95
+ f.input @article, :cover_image, options_hash, :as => :image_upload
96
+ f.input @article, :illustrations, options_hash, :as => :images_upload
97
+ f.input @article, :pdf, options_hash, :as => :asset_upload
98
+ f.input @article, :other_ressources, options_hash, :as => :image_upload
99
+ end
41
100
 
101
+ FormTagHelpers
102
+ image_upload_tag @article, :cover_image, options_hash
103
+ images_upload_tag @article, :illustrations, options_hash
104
+ asset_upload_tag @article, :pdf, options_hash
105
+ image_upload_tag @article, :other_ressources, options_hash
106
+ # For resources not linked to any assetable model :
107
+ image_upload_tag #{current_organization.name}_logo
108
+
109
+ === Resources access
110
+
111
+ With generic papermill association, Papermill generates an #assets(:key, *args) named_scope
112
+ @article.assets(:illustrations)
113
+ @article.assets(:illustrations, :order => "created_at DESC")
114
+ @article.illustrations.red.first
115
+ @article.assets(:illustrations, :order => "created_at DESC").red.first
116
+ # etc.
117
+
118
+ With declarative papermill associations, Papermill generates an #<association_key>(*args) named_scope
119
+ @entry.mug_shot.first
120
+ @entry.diaporama
121
+ @entry.diaporama(:order => "created_at DESC")
122
+ @entry.diaporama.red
123
+ # === @entry.diaporama(:conditions => {:color => "red"})
124
+ # etc.
125
+
126
+ Or for non-assetable resources :
127
+ PapermillAsset.all(:conditions => { :assetable_key => "#{current_organization.name}_logo" }).first
128
+ ColorAsset.all.red
129
+
130
+ === Using PapermillAsset
131
+
132
+ @asset = @entry.mug_shot.first
133
+ image_tag @asset.url # original
134
+ image_tag @asset.url("100x>")
135
+ image_tag @asset.url(:big) # assuming you have a :big alias in your environment.rb
136
+ @asset.name
137
+ @asset.content_type
138
+ @asset.path # original
139
+ @asset.path("100x>")
140
+ # etc.
141
+
42
142
  == Installation
43
143
 
44
- === Once you've installed the gem, generate a migration and copy a couple of static assets:
144
+ === Once gem is installed :
45
145
 
46
- # Generate the migration and migrate:
146
+ # Generate migration :
47
147
  $ ./script/generate papermill_table PapermillMigration
48
148
  $ rake db:migrate
49
- # copy some needed static assets to your public directory:
149
+ # copy static assets to your public directory:
50
150
  $ ./script/generate papermill_assets
151
+ # create the option hash in config/initializers/papermill.rb
152
+ $ ./script/generate papermill_initializer
51
153
 
52
154
  === Then in environment.rb:
53
155
 
54
156
  ...
55
-
56
157
  Rails::Initializer.run do |config|
57
158
  ...
58
159
  config.gem papermill
59
-
60
- # You can set application-wide options inside or before Rails::Initializer :
61
- module Papermill
62
- OPTIONS = {
63
- :thumbnail => {
64
- :width => 150,
65
- :height => 100
66
- },
67
- :aliases => {
68
- :big => "500x500>",
69
- :small => "100x100>"
70
- },
71
- :public_root => ":rails_root/public", # already a default
72
- :papermill_prefix => "system/papermill" # already a default
73
- }
74
- end
75
- # see lib/papermill/papermill_module.rb
76
-
77
- # You can use stringex's String#to_url (papermill will use its own String#to_url if none exists)
78
- config.gem 'stringex'
79
- ...
80
- # You can use Mime-Type to get the correct mime type from upload - flash garbles it.. (default is a UNIX call to "file --mime")
81
- # needed for Windows OS
160
+ # Needed for Windows OS (mime type from file extension):
82
161
  config.gem "mime-types", :lib => "mime/types"
83
162
  end
84
-
85
- === In your assetable model:
86
-
87
- # You can set a catch-all papermill association :
88
- papermill :class_name => MyAssetClass
89
-
90
- # or create an association for the specific :my_gallery key
91
- papermill :my_gallery_assets, :class_name => MyGalleryAsset
92
163
 
93
164
  === In your layout:
94
165
 
95
166
  <%= papermill_stylesheet_tag %>
96
167
  <%= papermill_javascript_tag :with_jquery => "no_conflict" %>
97
- # you won't need :with_jquery if you have it already.
98
-
99
- === In your edit form:
100
-
101
- f.images_upload(:my_gallery) # use specific papermill :my_gallery declaration
102
- f.assets_upload(:my_assets) # use catch-all
103
- f.asset_upload(:my_other_asset) # use catch-all
104
-
105
- === Access them with:
106
-
107
- @assetable.my_gallery_assets.each{ |image| image_tag image.url("100x100") }
108
- # equivalent to:
109
- @assetable.assets(:my_gallery_assets).each{ |image| image_tag image.url("100x100") }
110
- # also equivalent to:
111
- @assetable.assets(:conditions => {:assetable_key => 'my_gallery_assets'}).each{ |image| image_tag image.url("100x100") }
112
-
113
- @assetable.assets(:my_assets).each{ |asset| asset.url }
114
- # if your association name is singularizable, you can do smtg like :
115
- @assetable.asset(:my_other_asset).try(:url)
116
- # equivalent to:
117
- @assetable.assets(:my_other_asset).first.try(:url)
118
-
119
- # You can change assets/asset with :base_association_name in Papermill::OPTIONS (choose a plural word and you'll get singular association for free)
120
-
121
- Also see http://github.com/bbenezech/papermill/raw/master/installation-template.txt
122
- Have a look at the API here http://rdoc.info/projects/bbenezech/papermill
168
+ # you don't need :with_jquery if you already had it loaded.
123
169
 
124
170
  === Translations:
125
171
 
data/TODO.txt CHANGED
@@ -1,5 +1,3 @@
1
- * non-regression tests
2
- * write rake task to delete all forgotten images.
3
1
  Edit views :
4
2
  * use Jcrop
5
3
  * try Picnic API
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.14.3
1
+ 0.16.0
@@ -4,72 +4,48 @@ class PapermillController < ApplicationController
4
4
  skip_before_filter :verify_authenticity_token, :only => [:create]
5
5
 
6
6
  def show
7
- begin
8
- complete_id = (params[:id0] + params[:id1] + params[:id2]).to_i
9
- asset = PapermillAsset.find(complete_id)
10
- raise if asset.nil? || params[:style] == "original"
11
- style = Papermill::PAPERMILL_DEFAULTS[:aliases][params[:style]] || !Papermill::PAPERMILL_DEFAULTS[:alias_only] && params[:style]
12
- raise unless style
13
- style = {:geometry => style} unless style.is_a? Hash
14
-
15
- if asset.image?
16
- temp_thumbnail = Paperclip::Thumbnail.make(asset_file = asset.file, style)
17
- new_parent_folder_path = File.dirname(new_image_path = asset_file.path(params[:style]))
18
- FileUtils.mkdir_p new_parent_folder_path unless File.exists? new_parent_folder_path
19
- FileUtils.cp temp_thumbnail.path, new_image_path
20
- redirect_to asset.url(params[:style])
21
- else
22
- redirect_to asset.url
23
- end
24
- rescue
25
- render :text => t('papermill.not-found'), :status => "404"
7
+ @asset = PapermillAsset.find_by_id_partition params
8
+ if @asset.create_thumb_file(params[:style])
9
+ redirect_to @asset.url(params[:style])
10
+ else
11
+ render :nothing => true, :status => 500
26
12
  end
27
13
  end
28
14
 
29
15
  def destroy
30
- @asset = PapermillAsset.find_by_id(params[:id])
16
+ @asset = PapermillAsset.find params[:id]
31
17
  render :update do |page|
32
- if @asset && @asset.destroy
18
+ if @asset.destroy
33
19
  page << "jQuery('#papermill_asset_#{params[:id]}').remove()"
34
20
  else
35
21
  page << "jQuery('#papermill_asset_#{params[:id]}').show()"
36
- page << %{ notify("#{t((@asset && "papermill.not-deleted" || "papermill.not-found"), :ressource => @asset.name)}", "error") }
22
+ page << %{ notify("#{ escape_javascript t("papermill.not-deleted", :ressource => @asset.name) }", "error") }
37
23
  end
38
24
  end
39
25
  end
40
26
 
41
27
  def update
42
- @asset = PapermillAsset.find_by_id(params[:id])
28
+ @asset = PapermillAsset.find params[:id]
43
29
  render :update do |page|
44
- if @asset && @asset.update_attributes(params[:papermill_asset])
45
- page << %{ notify("#{t("papermill.updated", :ressource => @asset.name)}", "notice") }
30
+ if @asset.update_attributes(params[:papermill_asset])
31
+ page << %{ notify("#{ escape_javascript t("papermill.updated", :ressource => @asset.name)}", "notice") }
46
32
  else
47
- page << %{ notify("#{@asset && @asset.errors.full_messages.to_sentence || t("papermill.not-found", :ressource => params[:id].to_s)}", "warning") }
33
+ page << %{ notify("#{ escape_javascript @asset.errors.full_messages.to_sentence }", "warning") }
48
34
  end
49
35
  end
50
36
  end
51
37
 
52
38
  def edit
53
39
  @asset = PapermillAsset.find params[:id]
40
+ render :action => "edit", :layout => (params[:layout] || "none")
54
41
  end
55
42
 
56
43
  def create
57
- asset_class = params[:asset_class].constantize
58
- params[:assetable_id] = params[:assetable_id].try :to_i
59
- params[:assetable_type] = params[:assetable_type].try :camelize
60
- params[:assetable_key] = params[:assetable_key].try :to_s
61
- params[:swfupload_file] = params.delete(:Filedata)
62
- unless params[:gallery]
63
- @old_asset = asset_class.find(:first, :conditions => params.reject{|k, v| !["assetable_key", "assetable_type", "assetable_id"].include?(k)})
64
- end
65
- @asset = asset_class.new(params.reject{|k, v| !(PapermillAsset.columns.map(&:name)+["swfupload_file"]).include?(k)})
66
- @asset.position = asset_class.find(:first, :conditions => params.reject{|k, v| !["assetable_key", "assetable_type", "assetable_id"].include?(k)}, :order => "position DESC" ).try(:position).to_i + 1
67
-
68
- if @asset.save
69
- @old_asset.destroy if @old_asset
44
+ @asset = params[:asset_class].constantize.new(params.reject{|k, v| !(PapermillAsset.columns.map(&:name)+["Filedata", "Filename"]).include?(k)})
45
+ if @asset.save(:unique => !params[:gallery])
70
46
  render :partial => "papermill/asset", :object => @asset, :locals => {:gallery => params[:gallery], :thumbnail_style => params[:thumbnail_style]}
71
47
  else
72
- render :text => @asset.errors.full_messages.join('<br />'), :status => "500"
48
+ render :text => @asset.errors.full_messages.join('<br />'), :status => 500
73
49
  end
74
50
  end
75
51
 
@@ -79,4 +55,29 @@ class PapermillController < ApplicationController
79
55
  end
80
56
  render :nothing => true
81
57
  end
58
+
59
+ def mass_delete
60
+ render :update do |page|
61
+ (params[:papermill_asset] || []).each do |id|
62
+ @asset = PapermillAsset.find(id)
63
+ if @asset.destroy
64
+ page << "jQuery('#papermill_asset_#{id}').remove()"
65
+ else
66
+ page << %{ notify('#{ escape_javascript t("papermill.not-deleted", :ressource => @asset.name)}', 'error') }
67
+ end
68
+ end
69
+ end
70
+ end
71
+
72
+ def mass_edit
73
+ message = []
74
+ (params[:papermill_asset] || []).each do |id|
75
+ @asset = PapermillAsset.find(id)
76
+ @asset.update_attribute(params[:attribute], params[:value])
77
+ message << t("papermill.updated", :ressource => @asset.name)
78
+ end
79
+ render :update do |page|
80
+ page << %{ notify('#{ escape_javascript message.join("<br />")}', "notice") } unless message.empty?
81
+ end
82
+ end
82
83
  end
@@ -1,5 +1,5 @@
1
1
  <%- dom_id = "papermill_asset_#{asset.id}" -%>
2
- <%- delete_link = %{<a onclick="if(confirm('#{escape_javascript t("papermill.delete-confirmation", :resource => asset.name)}')){ $.ajax({async:true, beforeSend:function(request){$('##{dom_id}').hide();}, dataType:'script', error:function(request){$('##{dom_id}').show();}, type:'delete', url:'#{papermill_url(asset)}'})}; return false;" href="#" class="delete"><img title="#{escape_javascript t("papermill.delete", :ressource => asset.name)}" src="/papermill/images/delete.png" alt="delete"/></a>} %>
2
+ <%- delete_link = %{<a onclick="if(confirm('#{escape_javascript t("papermill.delete-confirmation", :resource => asset.name)}')){ jQuery.ajax({async:true, beforeSend:function(request){jQuery('##{dom_id}').hide();}, dataType:'script', error:function(request){jQuery('##{dom_id}').show();}, type:'delete', url:'#{papermill_url(asset)}'})}; return false;" href="#" class="delete"><img title="#{escape_javascript t("papermill.delete", :ressource => asset.name)}" src="/papermill/images/delete.png" alt="delete"/></a>} %>
3
3
 
4
4
  <li id="<%= dom_id %>" title="<%= t("papermill.#{thumbnail_style ? "thumbnail-" : ""}edit-title", :ressource => asset.name) %>" onDblClick="popup(jQuery(this).attr('rel')); return false;" rel="<%= edit_papermill_url(asset) %>">
5
5
  <%= delete_link %>
@@ -1,13 +1,20 @@
1
- <p>
2
- <%= form.label :title, t("papermill.title") %><br />
3
- <%= form.text_field :title, :class => "text_field" %>
4
- </p>
5
- <p>
6
- <%= form.label :copyright, t("papermill.copyright") %><br />
7
- <%= form.text_field :copyright, :class => "text_field" %>
8
- </p>
9
- <p>
10
- <%= form.label :description, t("papermill.description") %><br />
11
- <%= form.text_area :description %>
12
- </p>
13
- <%= submit_tag t('papermill.save') %>
1
+ <form onsubmit="jQuery.ajax({data:jQuery.param(jQuery(this).serializeArray()), dataType:'script', type:'post', url:'/papermill/<%= @asset.id %>'}); try {jQuery(document).trigger('close.facebox');} catch (e) {}; try{Shadowbox.close();} catch (e) {}; try{self.close();} catch (e) {}; return false;" method="post" action="/papermill/<%= @asset.id %>">
2
+ <input type="hidden" value="put" name="_method"/>
3
+
4
+ <% fields_for :papermill_asset, @asset do |form| %>
5
+ <p>
6
+ <%= form.label :title, t("papermill.title") %><br />
7
+ <%= form.text_field :title, :class => "text_field" %>
8
+ </p>
9
+ <p>
10
+ <%= form.label :copyright, t("papermill.copyright") %><br />
11
+ <%= form.text_field :copyright, :class => "text_field" %>
12
+ </p>
13
+ <p>
14
+ <%= form.label :description, t("papermill.description") %><br />
15
+ <%= form.text_area :description %>
16
+ </p>
17
+ <%= submit_tag t('papermill.save') %>
18
+ <% end %>
19
+ </form>
20
+ <%= javascript_include_tag("http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js") if params[:with_jquery] %>
@@ -1,6 +1,6 @@
1
1
  <div id="papermill-box">
2
2
  <div id="left">
3
- <%= link_to(@asset.image? ? image_tag(@asset.url("400x#{Papermill::PAPERMILL_DEFAULTS[:max_height]}>")) : t("file_type", :type => @asset.content_type, :scope => 'papermill'), @asset.url, :popup => true) %>
3
+ <%= link_to(@asset.image? ? image_tag(@asset.url("400x>")) : t("file_type", :type => @asset.content_type, :scope => 'papermill'), @asset.url, :popup => true) %>
4
4
  </div>
5
5
  <div id="right">
6
6
  <div id="read-only">
@@ -17,12 +17,7 @@
17
17
  </table>
18
18
  </div>
19
19
  <div id="read-write">
20
- <form onsubmit="jQuery.ajax({data:jQuery.param(jQuery(this).serializeArray()), dataType:'script', type:'post', url:'/papermill/<%= @asset.id %>'}); return false;" method="post" action="/papermill/<%= @asset.id %>">
21
- <input type="hidden" value="put" name="_method"/>
22
- <% fields_for :papermill_asset, @asset do |form| %>
23
- <%= render :partial => 'form', :object => form %>
24
- <% end %>
25
- </form>
20
+ <%= render :partial => 'form' %>
26
21
  </div>
27
22
  </div>
28
23
  <div style="clear:both;"></div>