papermill 1.1.6 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/README.rdoc CHANGED
@@ -18,7 +18,7 @@
18
18
 
19
19
  * Formtastic # use :as => :[image|asset](s)_upload
20
20
  * JGrowl # for notifications (included)
21
- * FaceBox/Shadowbox # for popups (included)
21
+ * FaceBox # for popups (included)
22
22
  * Stringex # (or any String#to_url) for asset filename/url generation
23
23
 
24
24
  === Navigator minimal requirements:
@@ -183,7 +183,7 @@ Create the option file config/initializers/papermill.rb
183
183
 
184
184
  === Translations:
185
185
 
186
- Papermill is fully I18n-able.
186
+ Papermill is fully I18n-able, except for javascript error messages. (coming)
187
187
  Copy config/locales/papermill.yml to your root config/locale folder to modify any wording in a any locale.
188
188
 
189
189
  Copyright (c) 2009 Benoit Bénézech, released under the MIT license
data/TODO.txt CHANGED
@@ -1,4 +1,5 @@
1
1
  Edit views :
2
2
  * use Jcrop
3
3
  * try Picnic API
4
- * trad errors
4
+ * trad errors
5
+ * migration with/whithout url_key
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.1.6
1
+ 1.2.0
@@ -1,12 +1,12 @@
1
1
  class PapermillController < ApplicationController
2
- prepend_before_filter :load_asset, :only => [ "show", "destroy", "update", "edit", "crop" ]
3
- prepend_before_filter :load_assets, :only => [ "sort", "mass_delete", "mass_edit" ]
2
+ prepend_before_filter :load_asset, :only => [ "show", "destroy", "update", "edit", "crop" ]
3
+ prepend_before_filter :load_assets, :only => [ "sort", "mass_delete", "mass_edit", "mass_thumbnail_reset" ]
4
4
 
5
5
  def show
6
- if @asset.create_thumb_file(params[:style])
6
+ if @asset.has_valid_url_key?(params[:url_key], params[:style]) && @asset.create_thumb_file(params[:style])
7
7
  redirect_to @asset.url(params[:style])
8
8
  else
9
- render :nothing => true, :status => 500
9
+ render :nothing => true, :status => 404
10
10
  end
11
11
  end
12
12
 
@@ -29,6 +29,10 @@ class PapermillController < ApplicationController
29
29
  end
30
30
 
31
31
  def update
32
+ if params[:target]
33
+ @asset.create_thumb_file(params[:target], params[:papermill_asset].merge({ :geometry => "original#" }))
34
+ end
35
+
32
36
  render :update do |page|
33
37
  if @asset.update_attributes(params[:papermill_asset])
34
38
  page << %{ notify("#{@asset.name}", "#{ escape_javascript t("papermill.updated", :resource => @asset.name)}", "notice"); close_popup(self); }
@@ -67,6 +71,13 @@ class PapermillController < ApplicationController
67
71
  end
68
72
  end
69
73
 
74
+ def mass_thumbnail_reset
75
+ @assets.each &:destroy_thumbnails
76
+ render :update do |page|
77
+ page << %{ notify("", "#{ escape_javascript t("papermill.updated", :resource => @assets.map(&:name).to_sentence) }", "notice"); } unless @assets.blank?
78
+ end
79
+ end
80
+
70
81
  private
71
82
  def load_asset
72
83
  @asset = PapermillAsset.find(params[:id] || (params[:id0] + params[:id1] + params[:id2]).to_i)
@@ -2,7 +2,7 @@
2
2
  <input type="hidden" value="put" name="_method"/>
3
3
  <div id="error" style="display:none;"></div>
4
4
  <% fields_for :papermill_asset, @asset do |form| %>
5
- <% @asset.assetable_papermill_options[:editable_fields].each do |field| %>
5
+ <% @asset.papermill_options[:editable_fields].each do |field| %>
6
6
  <% key = field.keys.first %>
7
7
  <p>
8
8
  <%= form.label key, field[key][:label] || t("papermill.#{key}") %><br />
@@ -1,6 +1,5 @@
1
1
  <%= image_tag(@asset.url, :id => "cropbox") %>
2
2
 
3
- <!--
4
3
  <form onsubmit="jQuery.ajax({data:jQuery.param(jQuery(this).serializeArray()), dataType:'script', type:'post', url:'/papermill/<%= @asset.id %>'}); return false;" method="post">
5
4
  <input type="hidden" value="put" name="_method"/>
6
5
  <input type="hidden" value="<%= params[:target] %>" name="target"/>
@@ -8,14 +7,14 @@
8
7
  <% for attribute in [:crop_x, :crop_y, :crop_w, :crop_h] %>
9
8
  <%= f.hidden_field attribute, :id => attribute %>
10
9
  <% end %>
11
- <%= submit_tag t('papermill.save') %>
10
+ <%= submit_tag I18n.t('papermill.save') %>
12
11
  <% end -%>
13
12
  </form>
14
- -->
13
+
15
14
  <div style="margin:6px"></div>
16
15
  <%= link_to_function I18n.t("papermill.back"), "popup('#{edit_papermill_path(@asset)}')" %>
17
16
 
18
- <script type="text/javascript" charset="utf-8">/*
17
+ <script type="text/javascript" charset="utf-8">
19
18
  jQuery(function() {
20
19
  jQuery('#cropbox').Jcrop({
21
20
  onChange: update_crop,
@@ -30,5 +29,5 @@
30
29
  jQuery("#crop_y").val(coords.y);
31
30
  jQuery("#crop_w").val(coords.w);
32
31
  jQuery("#crop_h").val(coords.h);
33
- }*/
32
+ }
34
33
  </script>
@@ -1,8 +1,8 @@
1
1
  <div id="papermill-box">
2
2
  <div id="left">
3
3
  <% if @asset.image? %>
4
- <%= link_to image_tag(@asset.url("400x400>")), @asset.url, :popup => true %>
5
- <%# image_tag(@asset.url("400x400>"), :onDblClick => "popup('#{crop_papermill_path(@asset, :target => "original")}'); return false;", :title => I18n.t("papermill.thumbnail-edit-title", :resource => @asset.name)) %>
4
+ <%# link_to image_tag(@asset.url("400x400>")), @asset.url, :popup => true %>
5
+ <%= image_tag(@asset.url("400x400>"), :onDblClick => "popup('#{crop_papermill_path(@asset, :target => "original")}'); return false;", :title => I18n.t("papermill.thumbnail-edit-title", :resource => @asset.name)) %>
6
6
  <div style="clear:both;"></div>
7
7
  <% else %>
8
8
  <%= link_to t("file_type", :type => @asset.content_type, :scope => 'papermill'), @asset.url, :popup => true %>
@@ -15,8 +15,8 @@
15
15
  <tr><td class="left-cell"><%= t("papermill.content_type") %></td><td><%= @asset.content_type %></td></tr>
16
16
  <tr><td class="left-cell"><%= t("papermill.size") %> </td><td><%= number_to_human_size(@asset.size.to_i) %></td></tr>
17
17
  <% if @asset.image? %>
18
- <tr><td class="left-cell"><%= t("papermill.width") %> </td><td><%= @asset.width.to_i %>px</td></tr>
19
- <tr><td class="left-cell"><%= t("papermill.height") %> </td><td><%= @asset.height.to_i %>px</td></tr>
18
+ <tr><td class="left-cell"><%= t("papermill.width") %> </td><td><%= @asset.width.to_i %>px</td></tr>
19
+ <tr><td class="left-cell"><%= t("papermill.height") %> </td><td><%= @asset.height.to_i %>px</td></tr>
20
20
  <% end %>
21
21
  <tr><td class="left-cell"><%= t("papermill.created_at") %> </td><td><%= I18n.l(@asset.created_at) %></td></tr>
22
22
  <tr><td class="left-cell"><%= t("papermill.updated_at") %> </td><td><%= I18n.l(@asset.updated_at) %></td></tr>
@@ -29,7 +29,6 @@
29
29
 
30
30
  <div style="clear:both;"></div>
31
31
  <p id="footer">
32
- <%= t("papermill.location") + @asset.file.path %><br />
33
- <%= t("papermill.url") + @asset.url %>
32
+ <%= t("papermill.url") + link_to(@asset.url, @asset.url, :popup => true) %>
34
33
  </p>
35
34
  </div>
@@ -2,7 +2,7 @@ en:
2
2
  papermill:
3
3
  not-processed: "Error/resource not processed"
4
4
  updated: "'{{resource}}' updated"
5
- not-found: "Asset #{{resource}} not found"
5
+ not-found: "'#{{resource}}' not found"
6
6
  edit-title: "Click to edit '{{resource}}'"
7
7
  thumbnail-edit-title: "Double-click to edit '{{resource}}'"
8
8
  upload-button-wording: "Upload..."
@@ -25,16 +25,17 @@ en:
25
25
  height: "Height"
26
26
  save: "Save"
27
27
  back: "Back"
28
- location: "File : "
29
28
  url: "URL : "
30
29
  modify-all: "Modify all: "
31
30
  delete-all: "Delete all"
32
31
  delete-all-confirmation: "Are you sure?"
32
+ mass-thumbnail-reset: "Delete all thumbnails"
33
+ mass-thumbnail-reset-confirmation: "Are you sure?"
33
34
  fr:
34
35
  papermill:
35
36
  not-processed: "Erreur/resource non trouvée"
36
- updated: "{{resource}} modifiés(s)"
37
- not-found: "Asset #{{resource}} non trouvé"
37
+ updated: "'{{resource}}' modifiés(s)"
38
+ not-found: "'{{resource}}' non trouvé"
38
39
  edit-title: "Cliquer pour éditer '{{resource}}'"
39
40
  thumbnail-edit-title: "Double-cliquer pour éditer '{{resource}}'"
40
41
  upload-button-wording: "Charger.."
@@ -57,8 +58,9 @@ fr:
57
58
  height: "Hauteur"
58
59
  save: "Modifier"
59
60
  back: "Retour"
60
- location: "Fichier : "
61
61
  url: "Lien : "
62
62
  modify-all: "Modifier tout : "
63
63
  delete-all: "Supprimer tout"
64
64
  delete-all-confirmation: "Êtes-vous sûr ?"
65
+ mass-thumbnail-reset: "Supprimer tous les thumbnails"
66
+ mass-thumbnail-reset-confirmation: "Êtes-vous sûr ?"
data/config/routes.rb CHANGED
@@ -1,4 +1,4 @@
1
1
  ActionController::Routing::Routes.draw do |map|
2
- map.resources :papermill, :collection => { :sort => :post, :mass_edit => :post, :mass_delete => :post }, :member => { :crop => :get }
3
- map.connect "#{Papermill::options[:papermill_prefix]}/#{Papermill::PAPERCLIP_INTERPOLATION_STRING.gsub(":id_partition", ":id0/:id1/:id2")}", :controller => "papermill", :action => "show"
2
+ map.resources :papermill, :collection => { :sort => :post, :mass_edit => :post, :mass_delete => :post, :mass_thumbnail_reset => :post }, :member => { :crop => :get }
3
+ map.connect "#{Papermill::options[:papermill_url_prefix]}/#{Papermill::compute_paperclip_path.gsub(":id_partition", ":id0/:id1/:id2")}", :controller => "papermill", :action => "show", :requirements => { :style => /.*/ }
4
4
  end
@@ -55,4 +55,4 @@ module StringToUrlNotFound
55
55
  def to_url
56
56
  gsub(/[^a-zA-Z0-9]/, "-").gsub(/-+/, "-").gsub(/^-|-$/, "").downcase
57
57
  end
58
- end
58
+ end
@@ -45,7 +45,7 @@ module ActionView::Helpers::FormTagHelper
45
45
  assetable = options[:assetable] || @template.instance_variable_get("@#{@object_name}")
46
46
  assetable_id = assetable && (assetable.id || assetable.timestamp) || nil
47
47
  assetable_type = assetable && assetable.class.base_class.name || nil
48
- options = PapermillAsset.assetable_papermill_options(assetable && assetable.class.name, key).deep_merge(options)
48
+ options = PapermillAsset.papermill_options(assetable && assetable.class.name, key).deep_merge(options)
49
49
  dom_id = "papermill_#{assetable_type}_#{assetable_id}_#{key}"
50
50
 
51
51
  if ot = options[:thumbnail]
@@ -71,6 +71,7 @@ module ActionView::Helpers::FormTagHelper
71
71
  %{<option value="#{field.to_s}">#{I18n.t("papermill.#{field.to_s}", :default => field.to_s)}</option>}
72
72
  end.join("\n")}</select>}
73
73
  html[:dashboard][:mass_delete] = %{<a onclick="Papermill.mass_delete('#{dom_id}', '#{@template.escape_javascript I18n.t("papermill.delete-all-confirmation")}'); return false;" style="cursor:pointer">#{I18n.t("papermill.delete-all")}</a>}
74
+ html[:dashboard][:mass_thumbnail_reset] = %{<a onclick="Papermill.mass_thumbnail_reset('#{dom_id}', '#{@template.escape_javascript I18n.t("papermill.mass-thumbnail-reset-confirmation")}'); return false;" style="cursor:pointer">#{I18n.t("papermill.mass-thumbnail-reset")}</a>}
74
75
  html[:dashboard] = @template.content_tag(:ul, options[:dashboard].map{|action| @template.content_tag(:li, html[:dashboard][action], :class => action.to_s) }.join("\n"), :class => "dashboard")
75
76
  end
76
77
 
@@ -131,7 +132,7 @@ module ActionView::Helpers::FormTagHelper
131
132
  upload_success_handler: Papermill.upload_success,
132
133
  upload_complete_handler: Papermill.upload_complete,
133
134
  button_placeholder_id: "browse_for_#{dom_id}",
134
- #{ options[:swfupload].map { |key, value| "#{key}: #{(value.is_a?(String) ? "\"#{@template.escape_javascript(value)}\"" : @template.escape_javascript(value.to_s))}" if value }.compact.join(",\n") }
135
+ #{ options[:swfupload].map { |key, value| "#{key}: #{value}" if value }.compact.join(",\n") }
135
136
  });
136
137
  }
137
138
  end
@@ -1,5 +1,4 @@
1
1
  module Papermill
2
- PAPERCLIP_INTERPOLATION_STRING = ":id_partition/:style/:basename.:extension"
3
2
 
4
3
  def self.included(base)
5
4
  base.extend(ClassMethods)
@@ -9,6 +8,14 @@ module Papermill
9
8
  @options ||= BASE_OPTIONS.deep_merge(defined?(OPTIONS) ? OPTIONS : {})
10
9
  end
11
10
 
11
+ def self.compute_paperclip_path
12
+ path = []
13
+ path << (options[:use_id_partition] ? ":id_partition" : ":id")
14
+ path << (":url_key" if options[:use_url_key])
15
+ path << ":style"
16
+ path << ":basename.:extension"
17
+ path.compact.join("/")
18
+ end
12
19
 
13
20
  module ClassMethods
14
21
  attr_reader :papermill_associations
@@ -36,8 +43,8 @@ module Papermill
36
43
  scope
37
44
  end
38
45
  end
39
- ActionController::Dispatcher.middleware.delete(FlashSessionCookieMiddleware) rescue nil
40
- ActionController::Dispatcher.middleware.insert_before(ActionController::Base.session_store, FlashSessionCookieMiddleware, ActionController::Base.session_options[:key]) rescue nil
46
+ ActionController::Dispatcher.middleware.delete(FlashSessionCookieMiddleware) rescue true
47
+ ActionController::Dispatcher.middleware.insert_before(ActionController::Base.session_store, FlashSessionCookieMiddleware, ActionController::Base.session_options[:key]) rescue true
41
48
  end
42
49
 
43
50
  def inherited(subclass)
@@ -5,8 +5,8 @@ class PapermillAsset < ActiveRecord::Base
5
5
 
6
6
  has_attached_file :file,
7
7
  :processors => [:papermill_paperclip_processor],
8
- :path => "#{Papermill::options[:public_root]}/#{Papermill::options[:papermill_prefix]}/#{Papermill::PAPERCLIP_INTERPOLATION_STRING}",
9
- :url => "/#{Papermill::options[:papermill_prefix]}/#{Papermill::PAPERCLIP_INTERPOLATION_STRING}"
8
+ :url => "/#{Papermill::options[:papermill_url_prefix]}/#{Papermill::compute_paperclip_path.gsub(':style', ':escaped_style')}",
9
+ :path => "#{Papermill::options[:public_root]}/#{Papermill::options[:papermill_path_prefix]}/#{Papermill::compute_paperclip_path}"
10
10
 
11
11
  before_post_process :set_file_name
12
12
 
@@ -17,6 +17,14 @@ class PapermillAsset < ActiveRecord::Base
17
17
 
18
18
  named_scope :key, lambda { |assetable_key| { :conditions => ['assetable_key = ?', assetable_key.to_s] }}
19
19
 
20
+ Paperclip.interpolates :url_key do |attachment, style|
21
+ attachment.instance.compute_url_key((style || "original").to_s)
22
+ end
23
+
24
+ Paperclip.interpolates :escaped_style do |attachment, style|
25
+ CGI::escape((style || "original").to_s)
26
+ end
27
+
20
28
  attr_accessor :crop_h, :crop_w, :crop_x, :crop_y
21
29
 
22
30
  def Filedata=(data)
@@ -27,12 +35,7 @@ class PapermillAsset < ActiveRecord::Base
27
35
  def Filename=(name)
28
36
  @real_file_name = name
29
37
  end
30
-
31
- def create_thumb_file(style_name)
32
- FileUtils.mkdir_p File.dirname(file.path(style_name))
33
- FileUtils.mv(Paperclip::PapermillPaperclipProcessor.make(file, self.class.compute_style(style_name)).path, file.path(style_name))
34
- end
35
-
38
+
36
39
  def id_partition
37
40
  ("%09d" % self.id).scan(/\d{3}/).join("/")
38
41
  end
@@ -50,11 +53,11 @@ class PapermillAsset < ActiveRecord::Base
50
53
  end
51
54
 
52
55
  def width
53
- Paperclip::Geometry.from_file(file).width
56
+ @width ||= Paperclip::Geometry.from_file(file).width
54
57
  end
55
58
 
56
59
  def height
57
- Paperclip::Geometry.from_file(file).height
60
+ @height ||= Paperclip::Geometry.from_file(file).height
58
61
  end
59
62
 
60
63
  def size
@@ -62,18 +65,34 @@ class PapermillAsset < ActiveRecord::Base
62
65
  end
63
66
 
64
67
  def url(style = nil)
65
- file.url(style && CGI::escape(style.to_s))
68
+ return url!(style) if style.is_a?(Hash)
69
+ file.url(style)
66
70
  end
67
71
 
68
72
  def path(style = nil)
73
+ return path!(style) if style.is_a?(Hash)
69
74
  file.path(style)
70
75
  end
71
76
 
77
+ def url!(style = nil)
78
+ create_thumb_file(style_name = style_name(style), style) unless File.exists?(self.path(style_name))
79
+ file.url(style_name)
80
+ end
81
+
82
+ def path!(style = nil)
83
+ create_thumb_file(style_name = style_name(style), style) unless File.exists?(self.path(style_name))
84
+ file.path(style_name)
85
+ end
86
+
72
87
  def content_type
73
88
  file_content_type
74
89
  end
75
90
 
76
- def self.assetable_papermill_options(assetable_class, assetable_key)
91
+ def style_name(style)
92
+ style.is_a?(Hash) ? (style[:name] || style.hash).to_s : (style || "original").to_s
93
+ end
94
+
95
+ def self.papermill_options(assetable_class, assetable_key)
77
96
  if assetable_class
78
97
  assoc = assetable_class.constantize.papermill_associations
79
98
  assoc[assetable_key.try(:to_sym)] || assoc[Papermill::options[:base_association_name]]
@@ -82,19 +101,50 @@ class PapermillAsset < ActiveRecord::Base
82
101
  end
83
102
  end
84
103
 
85
- def assetable_papermill_options
86
- self.class.assetable_papermill_options(assetable_type, assetable_key)
104
+ def papermill_options
105
+ self.class.papermill_options(assetable_type, assetable_key)
87
106
  end
88
107
 
89
108
  def image?
90
109
  content_type.split("/")[0] == "image"
91
110
  end
92
111
 
93
- def self.cleanup
112
+ def create_thumb_file(style_name, style = nil)
113
+ destroy_thumbnails if style_name.to_s == "original"
114
+ style = self.class.compute_style(style_name) unless style.is_a?(Hash)
115
+ FileUtils.mkdir_p File.dirname(file.path(style_name))
116
+ FileUtils.mv(Paperclip::PapermillPaperclipProcessor.make(file, style).path, file.path(style_name))
117
+ end
118
+
119
+ def destroy_thumbnails
120
+ thumbnail_folder_mask = Papermill::options[:use_url_key] ? "*/*/" : "*/"
121
+ original_folder = "#{File.dirname(file.path)}/"
122
+ Dir.glob("#{root_directory}/#{thumbnail_folder_mask}").each do |f|
123
+ FileUtils.rm_r(f) unless f == original_folder
124
+ end
125
+ Dir.glob("#{root_directory}/*/").each do |f|
126
+ FileUtils.rm_r(f) if Dir.entries(f) == [".", ".."]
127
+ end
128
+ end
129
+
130
+ def self.destroy_orphans
94
131
  self.all(:conditions => ["id < 0 AND created_at < ?", DateTime.now.yesterday]).each &:destroy
95
132
  end
96
133
 
134
+ def compute_url_key(style)
135
+ Papermill::options[:url_key_generator].call(style, self)
136
+ end
137
+
138
+ def has_valid_url_key?(key, style)
139
+ !Papermill::options[:use_url_key] || compute_url_key(style) == key
140
+ end
141
+
97
142
  private
143
+
144
+ def root_directory
145
+ deepness_to_root = Papermill::options[:use_url_key] ? -3 : -2
146
+ @root_directory ||= File.dirname(path).split('/')[0..deepness_to_root].join('/')
147
+ end
98
148
 
99
149
  def set_file_name
100
150
  return if @real_file_name.blank?
@@ -107,7 +157,7 @@ class PapermillAsset < ActiveRecord::Base
107
157
  end
108
158
 
109
159
  def destroy_files
110
- FileUtils.rm_r(File.dirname(path).chomp("original")) rescue true
160
+ FileUtils.rm_r(root_directory) rescue true
111
161
  end
112
162
 
113
163
  def self.compute_style(style)
@@ -1,39 +1,29 @@
1
- # DO NOT MOVE OR RENAME THIS FILE.
2
- # It must stand in your RAILS_ROOT/config/initializer folder and be named papermill.rb, because it is explicitely early-loaded by Papermill.
1
+ # DO NOT MOVE OR RENAME THIS FILE
2
+ # It must stand in your RAILS_ROOT/config/initializer folder and be named papermill.rb, because it is explicitely early-loaded by Papermill
3
+
3
4
  module Papermill
4
5
 
5
- # All the options here already are papermill defaults. You can set them :
6
- #
7
- # * here
8
- #
9
- # * in your association. Ex :
10
- # class Article < ActiveRecord::Base
11
- # papermill :diaporama, {
12
- # :class_name => "MyAssetClass",
13
- # :inline_css => false,
14
- # :thumbnail => {:width => 150},
15
- # ...
16
- # }
17
- # end
18
- #
19
- # * in your form helper call. Ex :
20
- # form.image_upload :diaporama, :class_name => "MyAssetClass", :thumbnail => {:width => 150}, :inline_css => false
21
- #
22
- #
23
- # FormHelper options-hash merges with model papermill declaration option-hash, that merges with this Papermill::OPTIONS, that merge with Papermill::DEFAULT_OPTIONS hash.
24
- # Don't freak-out, there's a 99% chance that it is exactly what you expect it to do.
25
- # Merges are recursive (for :gallery, :thumbnail and :swfupload sub-hashs)
26
-
6
+ # All options here already are defaults.
7
+
27
8
  unless defined?(OPTIONS)
28
9
 
29
10
  OPTIONS = {
30
- # Associated PapermillAsset subclass
11
+
12
+ #@@@@@@@@@@@@@@@@@@@ papermill association parameters @@@@@@@@@@@@@@@@@@@@@@@
13
+
14
+ # You can override these parameters here, or in your papermill associations definition.
15
+
16
+ # Associated PapermillAsset subclass (must be STI subclass of PapermillAsset)
31
17
  # :class_name => "PapermillAsset",
32
-
33
- # Helper will generates some inline css styling. You can use it to scaffold, then copy the lines you need in your application css and set it to false.
18
+
19
+ #@@@@@@@@@@@@@@@@@@@ form-helper parameters @@@@@@@@@@@@@@@@@@@@@@@
20
+
21
+ # You can override all these parameters here, or in your papermill associations definition, or in form-helper calls.
22
+
23
+ # Helper can generates inline css styling that adapt to your gallery/images placeholder. You can use it to scaffold, then copy the lines you need in your application css and set it to false.
34
24
  # :inline_css => true,
35
25
 
36
- # SwfUpload will only let the user upload images.
26
+ # SwfUpload will only let the user upload images.
37
27
  # :images_only => false,
38
28
 
39
29
  # Dashboard is only for galleries
@@ -43,12 +33,13 @@ module Papermill
43
33
 
44
34
  # Dashboard elements
45
35
  # You can remove/change order of HTML elements.
46
- # :dashboard => [:mass_edit, :mass_delete],
36
+ # :dashboard => [:mass_edit, :mass_thumbnail_reset, :mass_delete ],
47
37
 
48
38
  # Attributes editable at once for all assets in a gallery
49
39
  # :mass_editable_fields => ["title", "copyright", "description"],
50
40
 
51
- # Attributes you can edit in the form. You can use :type and :label
41
+ # Attributes you can edit in the form. You can use :type (string or text) and :label (any string)
42
+ # if you have more complex needs, you should override app/views/papermill/_form.html.erb in your application.
52
43
  # :editable_fields => [
53
44
  # {:title => {:type => "string"}},
54
45
  # {:alt => {:type => "string"}},
@@ -61,19 +52,19 @@ module Papermill
61
52
  # Great for quick admin scaffolding.
62
53
 
63
54
  :gallery => {
64
- # override calculated gallery width. Ex: "auto"
55
+ # override calculated gallery width. Ex: "auto"
65
56
  # :width => nil,
66
- # override calculated gallery height
57
+ # override calculated gallery height
67
58
  # :height => nil,
68
- # Number of columns and lines in a gallery
59
+ # Number of columns and lines in a gallery
69
60
  # :columns => 8,
70
61
  # :lines => 2,
71
- # vertical/horizontal padding/margin around each thumbnails
62
+ # vertical/horizontal padding/margin around each thumbnails
72
63
  # :vpadding => 0,
73
64
  # :hpadding => 0,
74
65
  # :vmargin => 1,
75
66
  # :hmargin => 1,
76
- # border around thumbnails
67
+ # border around thumbnails
77
68
  # :border_thickness => 2
78
69
  },
79
70
 
@@ -93,40 +84,101 @@ module Papermill
93
84
  # To remove an option when overriding, set it to nil.
94
85
 
95
86
  :swfupload => {
96
- # :flash_url => '/papermill/swfupload.swf',
97
- # :button_image_url => '/papermill/images/upload-blank.png',
87
+ # :flash_url => "'/papermill/swfupload.swf'",
88
+ # :button_image_url => "'/papermill/images/upload-blank.png'",
98
89
  # :button_width => 61,
99
90
  # :button_height => 22,
100
- # :button_text => %{<span class="button-text">#{I18n.t("papermill.upload-button-wording")}</span>},
101
- # :button_text_style => %{.button-text { font-size: 12pt; font-weight: bold; }},
91
+ # :button_text => %{'<span class="button-text">#{I18n.t("papermill.upload-button-wording")}</span>'},
92
+ # :button_text_style => %{'.button-text { font-size: 12pt; font-weight: bold; }'},
102
93
  # :button_text_top_padding => 4,
103
94
  # :button_text_left_padding => 4,
104
95
  # :debug => false,
105
96
  # :prevent_swf_caching => true,
106
- # :file_size_limit => "10 MB"
97
+ # :file_size_limit => "'10 MB'"
107
98
  },
108
99
 
109
- # APPLICATION WIDE PARAMETERS
110
- # Do not change these in your model declaration or form helper call.
100
+ #@@@@@@@@@@@@@@@@@@@ thumbnails style parameters @@@@@@@@@@@@@@@@@@@@@@@
101
+
102
+ # You can override all these parameters here, or in your papermill associations definition, or in thumbnail styling hashes.
103
+
104
+ # 1. COPYRIGHT WATERMARKING
105
+
106
+ # Activate with '©' at the end of your geometry string or pass :copyright => "my_copyright" in alias definition
107
+ # Papermill will use, in that order of priority :
108
+ # * copyright found in geometry string AFTER the @
109
+ # * alternatively :copyright in alias/inline definition hash
110
+ # * asset's copyright column (if found)
111
+ # * associated :copyright definition in your Assetable association definition
112
+ # * below :copyright definition
113
+
114
+ # Set this definition to nil if you don't want a global copyright string (likely)
115
+ # :copyright => "Example Copyright",
116
+
117
+ # Textilize, truncate, transform... your copyright before integration
118
+ # :copyright_text_transform => Proc.new {|c| c },
119
+
120
+ # Watermark ImageMagick command string.
121
+ # * %s gets interpolated with above transformed copyright string
122
+ # * DO NOT change the background color!, change the bordercolor instead. (because background color adds to bordercolor I set it to totally transparent)
123
+ # * for both fill (=foreground color) and bordercolor (=background color), the last two octals control alpha (transparency). FF is opaque, 00 is transparent.
124
+ # * remove -bordercolor if you don't want any background
125
+ # * +antialias to REMOVE antialiasing
126
+ # * font-size is pointsize
127
+ # * type 'identify -list font' to get a list of the fonts you can use (ImageMagick will default to Arial/Times if it can't find it)
128
+ # * use -gravity and -geometry for positionning, -geometry +x+y is relative to -gravity's corner/zone
129
+ # * parenthesis are there to isolate the label creation from the compositing (blending) part.
130
+ # * don't touch things you don't understand, you'll save yourself some time
131
+
132
+ # :copyright_im_command => %{ \\( -font Arial-Bold -pointsize 9 -fill '#FFFFFFE0' -border 3 -bordercolor '#50550080' -background '#00000000' label:' %s ' \\) -gravity South-West -geometry +0+0 -composite },
133
+
134
+ # 2. IMAGE WATERMARKING
135
+
136
+ # Activate with "-wm" at the end of your geometry string BEFORE copyright ('©'), or alternatively with :watermark => <image_path|true> in your alias/inline hash
137
+ # If you pass an image_path to :watermark, it will override below :
138
+
139
+ # you can use a relative path from your public directory (see :public_root), a complete path, or an URI
140
+ # :watermark => "/images/rails.png",
141
+
142
+ # default :watermarking command for image_magick. %s gets interpolated with above image path.
143
+ # :watermark_im_command => %{- | composite \\( %s -resize 100% \\) - -dissolve 20% -gravity center -geometry +0+0 },
144
+
145
+ #@@@@@@@@@@@@@@@@@@@@ Application-wide parameters @@@@@@@@@@@@@@@@@@@@@@@@@
111
146
 
112
147
  # Default named_scope name for catch-all :papermill declaration
113
- # :base_association_name => :assets,
148
+ # :base_association_name => :assets,
114
149
 
115
150
  # Set to true to require aliases in all url/path
116
151
  # Don't forget to give an alias value to options[:thumbnail][:style] if true!
117
- # :alias_only => false,
152
+ # :alias_only => false,
118
153
 
119
154
  # Needed if :alias_only
120
155
  :aliases => {
121
- # 'example' => "100x100#",
122
- # 'example2' => {:geometry => "100x100#"}
156
+ # :mini_crop => "100x100#",
157
+ # :cant_touch_this => {
158
+ # :geometry => "400x>",
159
+ # :copyright => "You sire, can't touch this",
160
+ # :watermark => "http://westsidewill.com/newblog/wp-content/uploads/2009/09/MC-Hammer.jpg"
161
+ # }
123
162
  },
124
-
125
- # path to the root of your public directory (from NGINX/Apache pov)
126
- # :public_root => ":rails_root/public",
163
+
164
+ # To prevent generation of thumbnails and guessing of assets location through URL hacking
165
+ # e.g. if you want to protect access to non-copyrighted original files,
166
+ # or don't want users to browse images by guessing the sequence of ids,
167
+ # an encrypted hash can be generated for each geometry string/alias and added to path/url.
168
+ # Please note that all previous assets paths will be lost if you add/remove or change the :url_key generation.
169
+ # :use_url_key => false,
170
+ # :url_key_salt => "change-me-to-your-favorite-pet-name",
171
+ # :url_key_generator => Proc.new { |style, asset| Digest::SHA512.hexdigest("#{style}#{asset.id}#{Papermill::options[:url_key_salt]}")[0..10] },
127
172
 
128
- # added to :public_root as the root folder for all papermill assets
129
- # :papermill_prefix => "system/papermill"
173
+ # added to :public_root as the root folder for all papermill assets (system is a default for static assets with capistrano)
174
+ # :papermill_url_prefix => "system/papermill",
175
+ # :papermill_path_prefix => ":rails_root/public/system/papermill",
176
+
177
+ # you can set it to false if you don't plan to have too many assets. (dangerous)
178
+ # :use_id_partition => true,
179
+
180
+ # If you use those defaults, the first asset will end-up in RAILS_ROOT/public/system/papermill/000/000/001/original/my_first_asset_name.ext
181
+ # You'll access it with my_domain.com/system/papermill/000/000/001/original/my_first_asset_name.ext
130
182
  }
131
183
  end
132
184
  end
@@ -2,8 +2,9 @@ module Papermill
2
2
  BASE_OPTIONS = {
3
3
  :class_name => "PapermillAsset",
4
4
  :inline_css => true,
5
+ :images_only => false,
5
6
  :form_helper_elements => [:upload_button, :container, :dashboard],
6
- :dashboard => [:mass_edit, :mass_delete],
7
+ :dashboard => [:mass_edit, :mass_thumbnail_reset, :mass_delete],
7
8
  :mass_editable_fields => ["title", "copyright", "description"],
8
9
  :editable_fields => [
9
10
  {:title => {:type => "string"}},
@@ -29,23 +30,31 @@ module Papermill
29
30
  :style => nil
30
31
  },
31
32
  :swfupload => {
32
- :flash_url => '/papermill/swfupload.swf',
33
- :button_image_url => '/papermill/images/upload-blank.png',
33
+ :flash_url => "'/papermill/swfupload.swf'",
34
+ :button_image_url => "'/papermill/images/upload-blank.png'",
34
35
  :button_width => 61,
35
36
  :button_height => 22,
36
- :button_text => %{<span class="button-text">#{I18n.t("papermill.upload-button-wording")}</span>},
37
- :button_text_style => %{.button-text { font-size: 12pt; font-weight: bold; }},
37
+ :button_text => %{'<span class="button-text">#{I18n.t("papermill.upload-button-wording")}</span>'},
38
+ :button_text_style => %{'.button-text { font-size: 12pt; font-weight: bold; }'},
38
39
  :button_text_top_padding => 4,
39
- :button_text_left_padding => 4,
40
- :debug => false,
41
- :prevent_swf_caching => true,
42
- :file_size_limit => "10 MB"
40
+ :button_text_left_padding => 4,
41
+ :debug => false,
42
+ :prevent_swf_caching => true,
43
+ :file_size_limit => "'10 MB'"
43
44
  },
44
- :images_only => false,
45
+ :copyright => "Example Copyright",
46
+ :copyright_text_transform => Proc.new {|c| c },
47
+ :copyright_im_command => %{\\( -font Arial-Bold -pointsize 9 -fill '#FFFFFFE0' -border 3 -bordercolor '#50550080' -background '#00000000' label:' %s ' \\) -gravity South-West -geometry +0+0 -composite},
48
+ :watermark => "/images/rails.png",
49
+ :watermark_im_command => %{- | composite \\( %s -resize 100% \\) - -dissolve 20% -gravity center -geometry +0+0 },
45
50
  :base_association_name => :assets,
46
51
  :alias_only => false,
47
52
  :aliases => {},
48
- :public_root => ":rails_root/public",
49
- :papermill_prefix => "system/papermill"
53
+ :use_url_key => false,
54
+ :url_key_salt => "change-me-please",
55
+ :url_key_generator => Proc.new { |style, asset| Digest::SHA512.hexdigest("#{style}#{asset.id}#{Papermill::options[:url_key_salt]}")[0..10] },
56
+ :use_id_partition => true,
57
+ :papermill_url_prefix => "system/papermill",
58
+ :papermill_path_prefix => ":rails_root/public/system/papermill",
50
59
  }
51
60
  end
@@ -3,33 +3,41 @@ module Paperclip
3
3
  # Handles thumbnailing images that are uploaded.
4
4
  class PapermillPaperclipProcessor < Thumbnail
5
5
 
6
- attr_accessor :crop_h, :crop_w, :crop_x, :crop_y, :copyright
6
+ attr_accessor :crop_h, :crop_w, :crop_x, :crop_y, :copyright, :watermark_path
7
7
 
8
8
  def initialize(file, options = {}, attachment = nil)
9
9
  @crop_h, @crop_w, @crop_x, @crop_y = options[:crop_h], options[:crop_w], options[:crop_x], options[:crop_y]
10
10
 
11
- if options[:geometry] =~ /©/
12
- options[:geometry], *@copyright = options[:geometry].split("©")
13
- @copyright = @copyright.join("©").nie || options[:copyright] || @attachment.instance.respond_to?(:copyright) && @attachment.instance.copyright
11
+ # copyright extraction
12
+ if options[:geometry] =~ /©/ || options[:copyright]
13
+ options[:geometry], *@copyright = options[:geometry].split("©", -1)
14
+ @copyright = options[:copyright] || @copyright.join("©").nie || file.instance.respond_to?(:copyright) && file.instance.copyright.nie || file.instance.papermill_options[:copyright].nie
15
+ @copyright = (options[:copyright_text_transform] || file.instance.papermill_options[:copyright_text_transform]).try(:call, @copyright) || @copyright if @copyright
14
16
  end
15
17
 
18
+ # watermark extraction
19
+ if options[:watermark] || options[:geometry] =~ /\-wm/
20
+ options[:geometry] = options[:geometry].chomp("-wm")
21
+ @watermark_path = options[:watermark].is_a?(String) && options[:watermark] || file.instance.papermill_options[:watermark]
22
+ @watermark_path = file.instance.papermill_options[:public_root].sub(":rails_root", RAILS_ROOT) + @watermark_path if @watermark_path.starts_with?("/")
23
+ end
24
+
25
+
16
26
  if options[:geometry] =~ /#.+/
17
-
18
- # <@target_geometry.width>x<@target_geometry.height>#<@crop_w>x<@crop_h>:<@crop_x>:<@crop_y>
19
- # <@target_geometry.width>x<@target_geometry.height>#<@crop_w>x<@crop_h>
20
- # <@target_geometry.width>x<@target_geometry.height>#<@crop_x>:<@crop_y>
27
+ # let's parse :
28
+ # <width>x<height>#<crop_w>x<crop_h>:<crop_x>:<crop_y>
29
+ # <width>x<height>#<crop_w>x<crop_h>
30
+ # <width>x<height>#<crop_x>:<crop_y>
21
31
 
22
32
  options[:geometry], manual_crop = options[:geometry].split("#")
23
-
24
33
  crop_dimensions, @crop_x, @crop_y = manual_crop.split("+")
25
-
34
+
26
35
  if crop_dimensions =~ /x/
27
36
  @crop_w, @crop_h = crop_dimensions.split("x")
28
37
  else
29
38
  @crop_x, @crop_y = crop_dimensions, @crop_x
30
39
  end
31
-
32
-
40
+
33
41
  options[:geometry] = (options[:geometry].nie || "#{@crop_x}x#{@crop_y}") + "#"
34
42
 
35
43
  unless @crop_w && @crop_h
@@ -42,9 +50,15 @@ module Paperclip
42
50
  end
43
51
 
44
52
  def transformation_command
45
- #puts "crop_command= #{crop_command ? super.sub(/ -crop \S+/, crop_command) : super}"
46
-
47
- crop_command ? super.sub(/ -crop \S+/, crop_command) : super
53
+ "#{(crop_command ? super.sub(/ -crop \S+/, crop_command) : super)} #{copyright_command} #{watermark_command}".sub(%{-resize "0x" }, "")
54
+ end
55
+
56
+ def copyright_command
57
+ (options[:copyright_im_command] || @file.instance.papermill_options[:copyright_im_command]).gsub(/%s/, @copyright.gsub(/'/, %{'"'"'}).sub("-slash-", "/").sub("-backslash-", %{\\\\\\})) if @copyright
58
+ end
59
+
60
+ def watermark_command
61
+ (options[:watermark_im_command] || @file.instance.papermill_options[:watermark_im_command]).gsub(/%s/, @watermark_path) if @watermark_path
48
62
  end
49
63
 
50
64
  def crop_command
data/lib/papermill.rb CHANGED
@@ -1,5 +1,6 @@
1
+ #require "digest/sha2"
1
2
  I18n.load_path = [File.join(File.dirname(__FILE__), "../config/locales/papermill.yml")] + I18n.load_path
2
- require 'extensions'
3
+ require 'papermill/extensions'
3
4
  require 'papermill/flash_session_cookie_middleware.rb'
4
5
 
5
6
  Object.send :include, PapermillObjectExtensions
data/public/.DS_Store CHANGED
Binary file
Binary file
@@ -5,7 +5,7 @@
5
5
  {
6
6
  font-size: 0;
7
7
  position: absolute;
8
- background: white url('Jcrop.gif') top left repeat;
8
+ background: white url('images/Jcrop.gif') top left repeat;
9
9
  }
10
10
  .jcrop-vline { height: 100%; width: 1px !important; }
11
11
  .jcrop-hline { width: 100%; height: 1px !important; }
@@ -32,9 +32,10 @@ background: red;
32
32
  .papermill .progress { display:block; border:1px solid #C2E3EF; text-align:left; height:6px; }
33
33
  .papermill .progress span { background:#7BB963; height:6px; width:0; display:block; }
34
34
 
35
- .papermill .dashboard li a { display:inline; padding-left:20px; }
35
+ .papermill .dashboard li a { display:inline; padding-left:20px }
36
36
  .papermill .dashboard li.mass_edit a { background:transparent url(/papermill/images/mass-edit.png) no-repeat top left; }
37
37
  .papermill .dashboard li.mass_delete a { background:transparent url(/papermill/images/mass-delete.png) no-repeat top left; }
38
+ .papermill .dashboard li.mass_thumbnail_reset a { background:transparent url(/papermill/images/mass-thumbnail-reset.png) no-repeat top left; }
38
39
 
39
40
  .papermill-thumb-container { position:relative; border:5px solid #EEE; padding:4px; overflow:hidden; }
40
41
  .papermill-thumb-container .asset { border:0px solid transparent; min-height:25px; min-width:25px; display:block; float:left; position:relative; }
@@ -138,6 +138,12 @@ var Papermill = {
138
138
  if(confirm(wording)){
139
139
  jQuery.ajax({async:true, data:jQuery('#' + papermill_id).sortable('serialize'), dataType:'script', type:'post', url:'/papermill/mass_delete'})
140
140
  }
141
+ },
142
+
143
+ mass_thumbnail_reset: function(papermill_id, wording) {
144
+ if(confirm(wording)){
145
+ jQuery.ajax({async:true, data:jQuery('#' + papermill_id).sortable('serialize'), dataType:'script', type:'post', url:'/papermill/mass_thumbnail_reset'})
146
+ }
141
147
  }
142
148
  }
143
149
 
@@ -91,7 +91,7 @@ class PapermillTest < Test::Unit::TestCase
91
91
  assert_equal @article.papermill_assets.key(:asset1).all(:order => "position DESC").map(&:id), [1,2]
92
92
  assert_equal @article.papermill_assets.key(:asset1).all(:order => "position DESC", :limit => 1).map(&:id), [1]
93
93
  end
94
-
94
+
95
95
  def test_id_partition
96
96
  assert_equal @asset1.id_partition, "000/000/001"
97
97
  end
@@ -111,17 +111,7 @@ class PapermillTest < Test::Unit::TestCase
111
111
  def test_size
112
112
  assert_equal @asset1.size, 4456
113
113
  end
114
-
115
- def test_url
116
- assert_equal @asset1.url, "/system/papermill/000/000/001/original/5k.png"
117
- assert_equal @asset1.url("400x300#"), "/system/papermill/000/000/001/400x300%23/5k.png"
118
- end
119
-
120
- def test_path
121
- assert_equal @asset1.path, "./test/../../../../public/system/papermill/000/000/001/original/5k.png"
122
- assert_equal @asset1.path("400x300#"), "./test/../../../../public/system/papermill/000/000/001/400x300#/5k.png"
123
- end
124
-
114
+
125
115
  def test_content_type
126
116
  assert_equal @file.get_content_type, "image/png"
127
117
  assert_equal @asset1.content_type, "image/png"
@@ -142,4 +132,22 @@ class PapermillTest < Test::Unit::TestCase
142
132
  Papermill::options[:alias_only] = true
143
133
  assert_equal PapermillAsset.compute_style("100x100"), false
144
134
  end
145
- end
135
+ end
136
+
137
+ # Filedata=(data)
138
+ # Filename=(name)
139
+ # create_thumb_file(style_name)
140
+ # basename
141
+ # extension
142
+ # url(style = nil)
143
+ # path(style = nil)
144
+ # self.papermill_options(assetable_class, assetable_key)
145
+ # papermill_options
146
+ # destroy_thumbnails
147
+ # self.destroy_orphans
148
+ # compute_url_key(style)
149
+ # has_valid_url_key?(key, style)
150
+ # root_directory
151
+ # set_file_name
152
+ # set_position
153
+ # destroy_files
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: papermill
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.6
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - "Benoit B\xC3\xA9n\xC3\xA9zech"
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-12-01 00:00:00 +01:00
12
+ date: 2009-12-08 00:00:00 +01:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -56,8 +56,8 @@ files:
56
56
  - generators/papermill_table/templates/migrate/papermill_migration.rb.erb
57
57
  - init.rb
58
58
  - install.rb
59
- - lib/extensions.rb
60
59
  - lib/papermill.rb
60
+ - lib/papermill/extensions.rb
61
61
  - lib/papermill/flash_session_cookie_middleware.rb
62
62
  - lib/papermill/form_builder.rb
63
63
  - lib/papermill/papermill.rb
@@ -74,20 +74,19 @@ files:
74
74
  - public/facebox/closelabel.gif
75
75
  - public/facebox/facebox.css
76
76
  - public/facebox/facebox.js
77
- - public/facebox/fbx-border-sprite.png
78
77
  - public/facebox/loading.gif
79
- - public/facebox/logo.png
80
- - public/facebox/shadow.gif
81
78
  - public/facebox/tl.png
82
79
  - public/facebox/tr.png
83
80
  - public/jgrowl/jquery.jgrowl.css
84
81
  - public/jgrowl/jquery.jgrowl_minimized.js
85
82
  - public/papermill/README
83
+ - public/papermill/images/Jcrop.gif
86
84
  - public/papermill/images/background.png
87
85
  - public/papermill/images/container-background.jpg
88
86
  - public/papermill/images/delete.png
89
87
  - public/papermill/images/mass-delete.png
90
88
  - public/papermill/images/mass-edit.png
89
+ - public/papermill/images/mass-thumbnail-reset.png
91
90
  - public/papermill/images/upload-blank.png
92
91
  - public/papermill/images/upload.png
93
92
  - public/papermill/jquery-1.3.2.min.js
Binary file
Binary file
Binary file