papermill 1.4.3 → 2.0.0.pre

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.
@@ -1,130 +1,122 @@
1
1
  class ActionView::Helpers::FormBuilder
2
2
  include ActionView::Helpers::FormTagHelper
3
3
 
4
- def assets_upload(key = nil, options = {})
5
- papermill_upload_tag key, { :thumbnail => false }.update(options)
4
+ def assets_upload(method, options = {})
5
+ papermill_upload_tag method, { :thumbnail => false }.update(options)
6
6
  end
7
- def asset_upload(key = nil, options = {})
8
- papermill_upload_tag key, { :gallery => false, :thumbnail => false }.update(options)
7
+ def asset_upload(method, options = {})
8
+ papermill_upload_tag method, { :gallery => false, :thumbnail => false }.update(options)
9
9
  end
10
- def images_upload(key = nil, options = {})
11
- papermill_upload_tag key, options
10
+ def images_upload(method, options = {})
11
+ papermill_upload_tag method, options
12
12
  end
13
- def image_upload(key = nil, options = {})
14
- papermill_upload_tag key, { :gallery => false }.update(options)
13
+ def image_upload(method, options = {})
14
+ papermill_upload_tag method, { :gallery => false }.update(options)
15
15
  end
16
16
  end
17
17
 
18
18
  module ActionView::Helpers::FormTagHelper
19
19
 
20
- def assets_upload_tag(assetable, key = nil, options = {})
21
- papermill_upload_tag key, { :thumbnail => false, :assetable => assetable }.update(options)
20
+ def assets_upload_tag(assetable, method, options = {})
21
+ papermill_upload_tag method, { :thumbnail => false, :assetable => assetable }.update(options)
22
22
  end
23
23
 
24
- def asset_upload_tag(assetable, key = nil, options = {})
25
- papermill_upload_tag key, { :gallery => false, :thumbnail => false, :assetable => assetable }.update(options)
24
+ def asset_upload_tag(assetable, method, options = {})
25
+ papermill_upload_tag method, { :gallery => false, :thumbnail => false, :assetable => assetable }.update(options)
26
26
  end
27
27
 
28
- def images_upload_tag(assetable, key = nil, options = {})
29
- papermill_upload_tag key, { :assetable => assetable }.update(options)
28
+ def images_upload_tag(assetable, method, options = {})
29
+ papermill_upload_tag method, { :assetable => assetable }.update(options)
30
30
  end
31
31
 
32
- def image_upload_tag(assetable, key = nil, options = {})
33
- papermill_upload_tag key, { :gallery => false, :assetable => assetable }.update(options)
32
+ def image_upload_tag(assetable, method, options = {})
33
+ papermill_upload_tag method, { :gallery => false, :assetable => assetable }.update(options)
34
34
  end
35
35
 
36
36
  private
37
37
 
38
- def papermill_upload_tag(key, options)
38
+ def papermill_upload_tag(method, options)
39
+ assetable = options[:object] || options[:assetable] || @object || @template.instance_variable_get("@#{@object_name}")
39
40
 
40
- if key.nil? && [String, Symbol].include?(options[:assetable].class)
41
- key = options[:assetable].to_s
42
- options[:assetable] = nil
43
- end
44
-
45
- assetable = @object || options[:object] || options[:assetable] || @template.instance_variable_get("@#{@object_name}")
46
-
47
- raise PapermillException.new("Your form instance object is not @#{@object_name}, and therefor cannot be found. \nPlease provide your object name in your form_for initialization. \nform_for :my_object_name, @my_object_name, :url => { :action => 'create'/'update'}") if @object_name && !assetable
41
+ raise PapermillException.new("Form object not found. Please provide it with :object => @assetable with the Papermill helper call") unless assetable
42
+ assetable_name = @object_name && "#{@object_name}#{(i = @options[:index]) ? "[#{i}]" : ""}"
48
43
 
49
- assetable_id = assetable && (assetable.id || assetable.timestamp) || nil
50
- assetable_type = assetable && assetable.class.base_class.name || nil
51
-
52
- options = PapermillAsset.papermill_options(assetable && assetable.class.name, key).deep_merge(options)
53
-
54
- dom_id = "papermill_#{assetable_type}_#{assetable_id}_#{key}"
44
+ sanitized_method = method.to_s.gsub(/[\?\/\-]$/, '')
45
+ sanitized_object_name = @object_name.to_s.gsub(/\]\[|[^-a-zA-Z0-9:.]/, "_").sub(/_$/, "")
46
+ field_id = @object_name && "#{sanitized_object_name}#{(i = @options[:index]) ? "_#{i}" : ""}_#{sanitized_method}"
47
+ assetable.class.papermill(method)
48
+ association_options = assetable.class.papermill_options[method.to_sym]
49
+
50
+ raise PapermillException.new("Papermill association #{method} failed to be generated dynamically for #{assetable.class.name}") unless association_options
51
+ options = association_options.deep_merge(options)
52
+ field_name = "#{assetable_name}[papermill_#{method}_ids][]"
55
53
 
56
54
  if ot = options[:thumbnail]
57
55
  w = ot[:width] || ot[:height] && ot[:aspect_ratio] && (ot[:height] * ot[:aspect_ratio]).to_i || nil
58
- h = ot[:height] || ot[:width] && ot[:aspect_ratio] && (ot[:width] / ot[:aspect_ratio]).to_i || nil
59
-
56
+ h = ot[:height] || ot[:width] && ot[:aspect_ratio] && (ot[:width] / ot[:aspect_ratio]).to_i || nil
60
57
  computed_style = ot[:style] || (w || h) && "#{w}x#{h}>" || "original"
61
- set_papermill_inline_css(dom_id, w, h, options)
58
+ set_papermill_inline_css(field_id, w, h, options)
62
59
  end
63
60
 
64
- set_papermill_inline_js(dom_id, compute_papermill_create_url(assetable_id, assetable_type, key, computed_style, options), options)
61
+ set_papermill_inline_js(field_id, compute_papermill_url(:create, computed_style, field_name, field_id, options), options)
65
62
 
66
63
  html = {}
67
- html[:upload_button] = %{<div id="#{dom_id}-button-wrapper" class="papermill-button-wrapper" style="height: #{options[:swfupload][:button_height]}px;"><span id="browse_for_#{dom_id}" class="swf_button"></span></div>}
68
- html[:container] = @template.content_tag(:div, :id => dom_id, :class => "papermill-#{key.to_s} #{(options[:thumbnail] ? "papermill-thumb-container" : "papermill-asset-container")} #{(options[:gallery] ? "papermill-multiple-items" : "papermill-unique-item")}") do
69
- conditions = {:assetable_type => assetable_type, :assetable_id => assetable_id}
70
- conditions.merge!({:assetable_key => key.to_s}) if key
71
- @template.render :partial => "papermill/asset", :collection => PapermillAsset.all(:conditions => conditions), :locals => { :thumbnail_style => computed_style, :targetted_size => options[:targetted_size] }
72
- end
64
+ html[:upload_button] = %{\
65
+ <div id="#{field_id}-button-wrapper" class="papermill-button-wrapper" style="height: #{options[:swfupload][:button_height]}px;">
66
+ <span id="browse_for_#{field_id}" class="swf_button"></span>
67
+ </div>}
73
68
 
74
- if options[:gallery]
75
- html[:dashboard] = {}
76
- html[:dashboard][:mass_edit] = %{<a onclick="Papermill.modify_all('#{dom_id}'); return false;" style="cursor:pointer">#{I18n.t("papermill.modify-all")}</a><select id="batch_#{dom_id}">#{options[:mass_editable_fields].map do |field|
77
- %{<option value="#{field.to_s}">#{I18n.t("papermill.#{field.to_s}", :default => field.to_s)}</option>}
78
- end.join("\n")}</select>}
79
- 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>}
80
- 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>}
81
- 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")
69
+ # I don't use the full :through association that is not updated if assetable.new_record?.
70
+ collection = association_options[:through] ? assetable.send("#{method}_associations").map(&:papermill_asset) : assetable.send(method)
71
+ locals = { :thumbnail_style => computed_style, :targetted_size => options[:targetted_size], :field_name => field_name, :field_id => field_id }
72
+ html[:container] = @template.content_tag(:div, :id => field_id, :class => "papermill-#{method.to_s} #{(options[:thumbnail] ? "papermill-thumb-container" : "papermill-asset-container")} #{(options[:gallery] ? "papermill-multiple-items" : "papermill-unique-item")}") do
73
+ @template.render(:partial => "papermill/asset",
74
+ :collection => collection,
75
+ :locals => locals)
82
76
  end
83
-
84
- if assetable && assetable.new_record? && !@timestamped
85
- @timestamp_field = @template.hidden_field(@object_name && "#{@object_name}#{(i = @options[:index]) ? "[#{i}]" : ""}" || assetable_type.underscore, :timestamp, :value => assetable.timestamp)
86
- @timestamped = true
77
+
78
+ if options[:gallery] && options[:mass_edit]
79
+ html[:mass_edit] = %{\
80
+ <a onclick="Papermill.modify_all('#{field_id}'); return false;" style="cursor:pointer">#{I18n.t("papermill.modify-all")}</a>
81
+ <select id="batch_#{field_id}">#{options[:mass_editable_fields].map do |field|
82
+ %{<option value="#{field.to_s}">#{I18n.t("papermill.#{field.to_s}", :default => field.to_s)}</option>}
83
+ end.join("\n")}</select>}
87
84
  end
88
85
 
89
- %{<div class="papermill">#{@timestamp_field.to_s + options[:form_helper_elements].map{|element| html[element] || ""}.join("\n")}</div>}
86
+ if options[:through]
87
+ browser_url = compute_papermill_url(:browser, computed_style, field_name, field_id, options)
88
+ html[:browser] = %{<a onclick="popup('#{@template.escape_javascript(browser_url)}&list_id=#{field_id}&' + jQuery('##{field_id}').sortable('serialize')); return false;" style="cursor:pointer">Ajouter...</a>}
89
+ end
90
+
91
+ # hidden_field needed to empty a list.
92
+ %{<div class="papermill">
93
+ #{@template.hidden_field("#{assetable_name}[papermill_#{method}_ids]", nil)}
94
+ #{options[:form_helper_elements].map{|element| html[element] || ""}.join("\n")}
95
+ </div>}
90
96
  end
91
97
 
92
98
 
93
- def compute_papermill_create_url(assetable_id, assetable_type, key, computed_style, options)
94
- create_url_options = {
95
- :escape => false, :controller => "/papermill", :action => "create",
99
+ def compute_papermill_url(action, computed_style, field_name, field_id, options)
100
+ @template.url_for({
101
+ :escape => false, :controller => "/papermill", :action => action,
96
102
  :asset_class => (options[:class_name] || PapermillAsset).to_s,
97
- :gallery => !!options[:gallery], :thumbnail_style => computed_style, :targetted_size => options[:targetted_size]
98
- }
99
- create_url_options.merge!({ :assetable_id => assetable_id, :assetable_type => assetable_type }) if assetable_id
100
- create_url_options.merge!({ :assetable_key => key }) if key
101
- @template.url_for(create_url_options)
103
+ :gallery => !!options[:gallery], :thumbnail_style => computed_style, :targetted_size => options[:targetted_size],
104
+ :field_name => field_name, :field_id => field_id
105
+ })
102
106
  end
103
107
 
104
- def set_papermill_inline_js(dom_id, create_url, options)
108
+ def set_papermill_inline_js(field_id, create_url, options)
105
109
  return unless options[:inline_css]
106
110
  @template.content_for :papermill_inline_js do
107
- %{
108
- jQuery("##{dom_id}").sortable({
109
- update:function(){
110
- jQuery.ajax({
111
- async: true,
112
- data: jQuery(this).sortable('serialize'),
113
- dataType: 'script',
114
- type: 'post',
115
- url: '#{@template.controller.send("sort_papermill_path")}'
116
- })
117
- }
118
- })
119
- } if options[:gallery]
111
+ %{ jQuery("##{field_id}").sortable() } if options[:gallery]
120
112
  end
121
113
  @template.content_for :papermill_inline_js do
122
114
  %{
123
115
  new SWFUpload({
124
- post_params: {
116
+ post_params: {
125
117
  "#{ ActionController::Base.session_options[:key] }": "#{ @template.cookies[ActionController::Base.session_options[:key]] }"
126
118
  },
127
- upload_id: "#{ dom_id }",
119
+ upload_id: "#{ field_id }",
128
120
  upload_url: "#{ @template.escape_javascript create_url }",
129
121
  file_types: "#{ options[:images_only] ? '*.jpg;*.jpeg;*.png;*.gif' : '' }",
130
122
  file_queue_limit: "#{ !options[:gallery] ? '1' : '0' }",
@@ -136,26 +128,26 @@ module ActionView::Helpers::FormTagHelper
136
128
  upload_error_handler: Papermill.upload_error,
137
129
  upload_success_handler: Papermill.upload_success,
138
130
  upload_complete_handler: Papermill.upload_complete,
139
- button_placeholder_id: "browse_for_#{dom_id}",
131
+ button_placeholder_id: "browse_for_#{field_id}",
140
132
  #{ options[:swfupload].map { |key, value| "#{key}: #{value}" if value }.compact.join(",\n") }
141
133
  });
142
134
  }
143
135
  end
144
136
  end
145
137
 
146
- def set_papermill_inline_css(dom_id, width, height, options)
138
+ def set_papermill_inline_css(field_id, width, height, options)
147
139
  html = ["\n"]
148
140
  size = [width && "width:#{width}px", height && "height:#{height}px"].compact.join("; ")
149
141
  if og = options[:gallery]
150
142
  vp, hp, vm, hm, b = [og[:vpadding], og[:hpadding], og[:vmargin], og[:hmargin], og[:border_thickness]].map &:to_i
151
143
  gallery_width = (og[:width] || width) && "width:#{og[:width] || og[:columns]*(width.to_i+(hp+hm+b)*2)}px;" || ""
152
144
  gallery_height = (og[:height] || height) && "min-height:#{og[:height] || og[:lines]*(height.to_i+(vp+vm+b)*2)}px;" || ""
153
- html << %{##{dom_id} { #{gallery_width} #{gallery_height} }}
154
- html << %{##{dom_id} .asset { margin:#{vm}px #{hm}px; border-width:#{b}px; padding:#{vp}px #{hp}px; #{size}; }}
145
+ html << %{##{field_id} { #{gallery_width} #{gallery_height} }}
146
+ html << %{##{field_id} .asset { margin:#{vm}px #{hm}px; border-width:#{b}px; padding:#{vp}px #{hp}px; #{size}; }}
155
147
  else
156
- html << %{##{dom_id}, ##{dom_id} .asset { #{size} }}
148
+ html << %{##{field_id}, ##{field_id} .asset { #{size} }}
157
149
  end
158
- html << %{##{dom_id} .name { width:#{width || "100"}px; }}
150
+ html << %{##{field_id} .name { width:#{width || "100"}px; }}
159
151
  @template.content_for :papermill_inline_css do
160
152
  html.join("\n")
161
153
  end
@@ -1,15 +1,10 @@
1
1
  module Papermill
2
-
3
- def self.included(base)
4
- base.extend(ClassMethods)
5
- end
2
+ MSWIN = (Config::CONFIG['host_os'] =~ /mswin|mingw/)
6
3
 
7
4
  def self.options
8
5
  @options ||= BASE_OPTIONS.deep_merge(defined?(OPTIONS) ? OPTIONS : {})
9
6
  end
10
7
 
11
- MSWIN = (Config::CONFIG['host_os'] =~ /mswin|mingw/)
12
-
13
8
  def self.compute_paperclip_path
14
9
  path = []
15
10
  path << (options[:use_id_partition] ? ":id_partition" : ":id")
@@ -19,52 +14,69 @@ module Papermill
19
14
  path.compact.join("/")
20
15
  end
21
16
 
22
- module ClassMethods
23
- attr_reader :papermill_associations
24
-
25
- def papermill(*args)
26
- assoc_name = (!args.first.is_a?(Hash) && args.shift || Papermill::options[:base_association_name]).to_sym
27
- local_options = args.first || {}
28
-
29
- (@papermill_associations ||= {}).merge!( assoc_name => Papermill::options.deep_merge(local_options) )
17
+ def self.included(base)
18
+ base.extend(ClassMethods)
19
+ base.class_eval do
20
+ def papermill(key, through = ((po = self.class.papermill_options) && (po[key.to_sym] || po[:default]) || Papermill::options)[:through])
21
+ PapermillAsset.papermill(self.class.base_class.name, self.id, key.to_s, through)
22
+ end
30
23
 
31
- include Papermill::InstanceMethods
32
- after_create :rebase_assets
33
- has_many :papermill_assets, :as => "assetable", :dependent => :destroy
34
-
35
- [assoc_name, Papermill::options[:base_association_name].to_sym].uniq.each do |assoc|
36
- define_method assoc do |*options|
37
- scope = PapermillAsset.scoped(:conditions => {:assetable_id => self.id, :assetable_type => self.class.base_class.name})
38
- if assoc != Papermill::options[:base_association_name]
39
- scope = scope.scoped(:conditions => { :assetable_key => assoc.to_s })
40
- elsif options.first && !options.first.is_a?(Hash)
41
- scope = scope.scoped(:conditions => { :assetable_key => options.shift.to_s.nie })
42
- end
43
- scope = scope.scoped(options.shift) if options.first
44
- scope
24
+ def respond_to_with_papermill?(method, *args, &block)
25
+ respond_to_without_papermill?(method, *args, &block) || (method.to_s =~ /^papermill_.+_ids=$/) == 0
26
+ end
27
+
28
+ def method_missing_with_papermill(method, *args, &block)
29
+ if method.to_s =~ /^papermill_.+_ids=$/
30
+ self.class.papermill(method.to_s[10..-6])
31
+ self.send(method, *args, &block)
32
+ else
33
+ method_missing_without_papermill(method, *args, &block)
45
34
  end
46
35
  end
47
- ActionController::Dispatcher.middleware.delete(FlashSessionCookieMiddleware) rescue true
48
- ActionController::Dispatcher.middleware.insert_before(ActionController::Base.session_store, FlashSessionCookieMiddleware, ActionController::Base.session_options[:key]) rescue true
49
36
  end
37
+ base.send :alias_method_chain, :method_missing, :papermill
38
+ base.send :alias_method_chain, :respond_to?, :papermill
39
+ ActionController::Dispatcher.middleware.insert_before(ActionController::Base.session_store, FlashSessionCookieMiddleware, ActionController::Base.session_options[:key]) unless ActionController::Dispatcher.middleware.include?(FlashSessionCookieMiddleware)
40
+ end
41
+
42
+ module ClassMethods
43
+ attr_reader :papermill_options
50
44
 
51
45
  def inherited(subclass)
52
- subclass.instance_variable_set("@papermill_associations", @papermill_associations)
46
+ subclass.instance_variable_set("@papermill_options", @papermill_options)
53
47
  super
54
48
  end
55
- end
56
-
57
- module InstanceMethods
58
- attr_writer :timestamp
59
- def timestamp
60
- @timestamp ||= "-#{(Time.now.to_f * 1000).to_i.to_s[4..-1]}"
61
- end
62
49
 
63
- private
64
-
65
- def rebase_assets
66
- PapermillAsset.all(:conditions => { :assetable_id => self.timestamp, :assetable_type => self.class.base_class.name }).each do |asset|
67
- asset.created_at < 2.hours.ago ? asset.destroy : asset.update_attribute(:assetable_id, self.id)
50
+ def papermill(assoc_key, assoc_options = (@papermill_options && @papermill_options[:default] || {}))
51
+ return if @papermill_options && @papermill_options[assoc_key.to_sym] # already defined
52
+ raise PapermillException.new("Can't use '#{assoc_key}' association : #{self.name} instances already responds to it !") if self.new.respond_to?(assoc_key)
53
+ (@papermill_options ||= {}).merge!( { assoc_key.to_sym => Papermill::options.deep_merge(assoc_options) } )
54
+ return if assoc_key.to_sym == :default
55
+ unless papermill_options[assoc_key.to_sym][:through]
56
+ self.class_eval %{
57
+ has_many :#{assoc_key}, :as => "assetable", :dependent => :delete_all, :order => :position, :class_name => "PapermillAsset", :conditions => {:assetable_key => '#{assoc_key}'}, :before_add => Proc.new{|a, asset| asset.assetable_key = '#{assoc_key}'}
58
+ def papermill_#{assoc_key}_ids=(ids)
59
+ unless (assets_ids = ids.map(&:to_i).select{|i|i>0}) == self.#{assoc_key}.map(&:id)
60
+ assets = PapermillAsset.find(assets_ids)
61
+ self.#{assoc_key} = assets_ids.map{|asset_id| assets.select{|asset|asset.id==asset_id}.first}
62
+ PapermillAsset.update_all("position = CASE id " + assets_ids.map_with_index{|asset_id, index| " WHEN " + asset_id.to_s + " THEN " + (index+1).to_s }.join + " END",
63
+ :id => assets_ids) unless assets_ids.empty?
64
+ end
65
+ end
66
+ }
67
+ else
68
+ self.class_eval %{
69
+ has_many(:#{assoc_key}_associations, :as => "assetable", :class_name => "PapermillAssociation", :include => :papermill_asset, :dependent => :delete_all, :order => :position, :conditions => {:assetable_key => '#{assoc_key}'}, :before_add => Proc.new{|a, assoc| assoc.assetable_key = '#{assoc_key}'})
70
+ has_many(:#{assoc_key}, :through => :#{assoc_key}_associations, :source => :papermill_asset)
71
+ def papermill_#{assoc_key}_ids=(ids)
72
+ unless (assets_ids = ids.map(&:to_i).select{|i|i>0}) == self.#{assoc_key}_associations.map(&:papermill_asset_id)
73
+ self.#{assoc_key}_associations.delete_all
74
+ self.#{assoc_key}_associations = assets_ids.map_with_index do |asset_id, index|
75
+ PapermillAssociation.new(:papermill_asset_id => asset_id, :position => (index+1))
76
+ end
77
+ end
78
+ end
79
+ }
68
80
  end
69
81
  end
70
82
  end
@@ -1,8 +1,6 @@
1
1
  class PapermillAsset < ActiveRecord::Base
2
-
3
2
  before_destroy :destroy_files
4
- before_create :set_position
5
-
3
+
6
4
  has_attached_file :file,
7
5
  :processors => [:papermill_paperclip_processor],
8
6
  :url => "#{Papermill::options[:papermill_url_prefix]}/#{Papermill::compute_paperclip_path.gsub(':style', ':escape_style_in_url')}",
@@ -12,10 +10,26 @@ class PapermillAsset < ActiveRecord::Base
12
10
 
13
11
  validates_attachment_presence :file
14
12
 
15
- belongs_to :assetable, :polymorphic => true, :touch => Papermill::options[:touch]
16
- default_scope :order => 'position'
17
-
18
- named_scope :key, lambda { |assetable_key| { :conditions => ['assetable_key = ?', assetable_key.to_s] }}
13
+ belongs_to :assetable, :polymorphic => true
14
+ has_many :papermill_associations, :dependent => :delete_all
15
+
16
+ named_scope :papermill, lambda { |assetable_type, assetable_id, assetable_key, through|
17
+ through ?
18
+ { :joins => "INNER JOIN papermill_associations ON papermill_assets.id = papermill_associations.papermill_asset_id \
19
+ AND papermill_associations.assetable_type = #{connection.quote assetable_type} \
20
+ AND papermill_associations.assetable_id = #{assetable_id.to_i} \
21
+ AND papermill_associations.assetable_key = #{connection.quote assetable_key}",
22
+ :order => "papermill_associations.position" } :
23
+ { :conditions => {
24
+ :assetable_type => assetable_type,
25
+ :assetable_id => assetable_id,
26
+ :assetable_key => assetable_key.to_s },
27
+ :order => "papermill_assets.position" }
28
+ }
29
+
30
+ def assetable_type=(sType)
31
+ super(sType.to_s.classify.constantize.base_class.to_s)
32
+ end
19
33
 
20
34
  Paperclip.interpolates :url_key do |attachment, style|
21
35
  attachment.instance.compute_url_key((style || "original").to_s)
@@ -103,10 +117,10 @@ class PapermillAsset < ActiveRecord::Base
103
117
  style.is_a?(Hash) ? (style[:name] || style.hash).to_s : (style || "original").to_s
104
118
  end
105
119
 
106
- def self.papermill_options(assetable_class, assetable_key)
107
- if assetable_class
108
- assoc = assetable_class.constantize.papermill_associations
109
- assoc[assetable_key.try(:to_sym)] || assoc[Papermill::options[:base_association_name]]
120
+ def self.papermill_options(assetable_type, assetable_key)
121
+ if assetable_type
122
+ assoc = assetable_type.constantize.papermill_options
123
+ assoc[assetable_key.try(:to_sym)] || assoc[:default] || Papermill::options
110
124
  else
111
125
  Papermill::options
112
126
  end
@@ -121,12 +135,14 @@ class PapermillAsset < ActiveRecord::Base
121
135
  end
122
136
 
123
137
  def create_thumb_file(style_name, style = nil)
138
+ return false unless self.image?
124
139
  destroy_thumbnails if style_name.to_s == "original"
125
140
  style = self.class.compute_style(style_name) unless style.is_a?(Hash)
126
141
  FileUtils.mkdir_p File.dirname(new_path = file.path(style_name))
127
142
  FileUtils.cp((tmp_path = Paperclip::PapermillPaperclipProcessor.make(file, style).path), new_path)
128
143
  FileUtils.chmod(0644, new_path) unless Papermill::MSWIN
129
144
  File.delete(tmp_path)
145
+ return true
130
146
  end
131
147
 
132
148
  def destroy_thumbnails
@@ -141,7 +157,9 @@ class PapermillAsset < ActiveRecord::Base
141
157
  end
142
158
 
143
159
  def self.destroy_orphans
144
- self.all(:conditions => ["id < 0 AND created_at < ?", DateTime.now.yesterday]).each &:destroy
160
+ self.name != self.base_class.name ?
161
+ PapermillAsset.delete_all(["created_at < ? AND assetable_id IS NULL AND type = ?", 1.hour.ago, self.name]) :
162
+ PapermillAsset.delete_all(["created_at < ? AND assetable_id IS NULL AND type IS NULL", 1.hour.ago])
145
163
  end
146
164
 
147
165
  def compute_url_key(style)
@@ -153,7 +171,7 @@ class PapermillAsset < ActiveRecord::Base
153
171
  end
154
172
 
155
173
  private
156
-
174
+
157
175
  def root_directory
158
176
  deepness_to_root = Papermill::options[:use_url_key] ? -3 : -2
159
177
  @root_directory ||= File.dirname(path).split('/')[0..deepness_to_root].join('/')
@@ -165,10 +183,6 @@ class PapermillAsset < ActiveRecord::Base
165
183
  self.file.instance_write(:file_name, "#{basename.to_url}#{extension}")
166
184
  end
167
185
 
168
- def set_position
169
- self.position ||= PapermillAsset.maximum(:position, :conditions => { :assetable_type => assetable_type, :assetable_id => assetable_id, :assetable_key => assetable_key } ).to_i + 1
170
- end
171
-
172
186
  def destroy_files
173
187
  FileUtils.rm_r(root_directory) rescue true
174
188
  end
@@ -0,0 +1,8 @@
1
+ class PapermillAssociation < ActiveRecord::Base
2
+ belongs_to :papermill_asset
3
+ belongs_to :assetable, :polymorphic => true
4
+
5
+ def assetable_type=(sType)
6
+ super(sType.to_s.classify.constantize.base_class.to_s)
7
+ end
8
+ end
@@ -1,10 +1,11 @@
1
1
  module Papermill
2
2
  BASE_OPTIONS = {
3
3
  :class_name => "PapermillAsset",
4
+ :through => false,
4
5
  :inline_css => true,
5
6
  :images_only => false,
6
- :form_helper_elements => [:upload_button, :container, :dashboard],
7
- :dashboard => [:mass_edit, :mass_delete],
7
+ :form_helper_elements => [:upload_button, :container, :browser, :mass_edit],
8
+ :mass_edit => true,
8
9
  :mass_editable_fields => ["title", "copyright", "description"],
9
10
  :editable_fields => [
10
11
  {:title => {:type => "string"}},
@@ -48,8 +49,6 @@ module Papermill
48
49
  :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},
49
50
  :watermark => "/images/rails.png",
50
51
  :watermark_im_command => %{- | composite \\( %s -resize 100% \\) - -dissolve 20% -gravity center -geometry +0+0 },
51
- :base_association_name => :assets,
52
- :touch => false,
53
52
  :alias_only => false,
54
53
  :aliases => {},
55
54
  :use_url_key => false,
@@ -57,9 +56,6 @@ module Papermill
57
56
  :url_key_generator => Proc.new { |style, asset| Digest::SHA512.hexdigest("#{style}#{asset.id}#{Papermill::options[:url_key_salt]}")[0..10] },
58
57
  :use_id_partition => true,
59
58
  :papermill_url_prefix => "/system/papermill",
60
- :papermill_path_prefix => ":rails_root/public/system/papermill",
61
- :authorize_create => "true",
62
- :authorize_update_and_destroy => "true",
63
- :authorize_multiple_modification => "true",
59
+ :papermill_path_prefix => ":rails_root/public/system/papermill"
64
60
  }
65
61
  end
data/lib/papermill.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require "rbconfig"
2
+ require "acts_as_list"
2
3
  begin
3
4
  require "mime/types"
4
5
  MIME_TYPE_LOADED = true
@@ -12,6 +13,7 @@ require 'papermill/flash_session_cookie_middleware.rb'
12
13
 
13
14
  Object.send :include, PapermillObjectExtensions
14
15
  Hash.send :include, PapermillHashExtensions
16
+ Array.send :include, PapermillArrayExtensions
15
17
  String.send :include, StringToUrlNotFound unless String.instance_methods.include? "to_url"
16
18
  Formtastic::SemanticFormBuilder.send(:include, PapermillFormtasticExtensions) rescue NameError
17
19
 
@@ -23,6 +25,7 @@ end
23
25
  require 'paperclip' unless defined?(Paperclip)
24
26
  require 'papermill/papermill_paperclip_processor'
25
27
  require 'papermill/papermill'
28
+ require 'papermill/papermill_association'
26
29
  require 'papermill/papermill_asset'
27
30
  require 'papermill/form_builder'
28
31
  require 'papermill/papermill_helper'
@@ -23,8 +23,6 @@
23
23
 
24
24
  .papermill .dashboard li a { display:inline; padding-left:20px }
25
25
  .papermill .dashboard li.mass_edit a { background:transparent url(/papermill/images/mass-edit.png) no-repeat top left; }
26
- .papermill .dashboard li.mass_delete a { background:transparent url(/papermill/images/mass-delete.png) no-repeat top left; }
27
- .papermill .dashboard li.mass_thumbnail_reset a { background:transparent url(/papermill/images/mass-thumbnail-reset.png) no-repeat top left; }
28
26
 
29
27
  .papermill-thumb-container { position:relative; border:5px solid #EEE; padding:4px; overflow:hidden; }
30
28
  .papermill-thumb-container .asset { border:0px solid transparent; min-height:25px; min-width:25px; display:block; float:left; position:relative; }
@@ -6,14 +6,15 @@ If you have your own popup solution, override popup and close_popup in your appl
6
6
  popup = function(url) {
7
7
  jQuery.facebox(function() {
8
8
  jQuery.get(url, function(data) {
9
- jQuery.facebox(data)
9
+ jQuery.facebox(data);
10
10
  })
11
11
  })
12
12
  return false;
13
- }
13
+ };
14
+
14
15
  close_popup = function(source) {
15
16
  jQuery(document).trigger('close.facebox');
16
- }
17
+ };
17
18
 
18
19
  jQuery(document).ajaxError(function(){
19
20
  switch (arguments[1].status) {
@@ -37,10 +38,11 @@ If you have your own notification solution, override notify
37
38
  */
38
39
 
39
40
  notify = function(title, message, type) {
40
- if(type == "notice") { jQuery.jGrowl(message, { header: title, life: 4000, theme: type }) }
41
- if(type == "warning") { jQuery.jGrowl(message, { header: title, life: 15000, theme: type }) }
42
- if(type == "error") { jQuery.jGrowl(message, { header: title, sticky: true, theme: type }) }
43
- }
41
+ if(type == "notice") { jQuery.jGrowl(message, { header: title, life: 4000, theme: type }) };
42
+ if(type == "warning") { jQuery.jGrowl(message, { header: title, life: 15000, theme: type }) };
43
+ if(type == "error") { jQuery.jGrowl(message, { header: title, sticky: true, theme: type }) };
44
+ };
45
+
44
46
  var Papermill = {
45
47
  files_queued: 0,
46
48
  file_dialog_complete: function(num_selected, num_queued)
@@ -62,7 +64,7 @@ var Papermill = {
62
64
  } while (file != null);
63
65
  this.sorted_queue = file_queue.sort(function(a,b){
64
66
  if(b.name < a.name) { return (1) } else { return (-1) }
65
- })
67
+ });
66
68
  var self = this;
67
69
  jQuery(this.sorted_queue).each( function(index, file) {
68
70
  div = jQuery('<div></div>').attr({ 'id': file.id, 'class': 'swfupload asset' });
@@ -76,7 +78,7 @@ var Papermill = {
76
78
  }
77
79
  }
78
80
  jQuery("#" + self.settings.upload_id).append(div);
79
- })
81
+ });
80
82
  this.startUpload(this.sorted_queue[this.index++].id);
81
83
  }
82
84
  },
@@ -84,6 +86,12 @@ var Papermill = {
84
86
  {
85
87
  jQuery('#' + file.id + ' .status').html(SWFUPLOAD_LOADING);
86
88
  this.addFileParam(file.id, "Fileid", file.id);
89
+ if(this.settings.file_queue_limit == 1) {
90
+ old_asset = jQuery("#" + this.settings.upload_id).children()[0];
91
+ if (old_asset && old_asset.id != file.id) {
92
+ this.addFileParam(file.id, "Oldfileid", old_asset.id);
93
+ }
94
+ }
87
95
  },
88
96
  upload_progress: function(file, bytes, total)
89
97
  {
@@ -107,7 +115,7 @@ var Papermill = {
107
115
  {
108
116
  Papermill.files_queued -= 1;
109
117
  if(this.sorted_queue[this.index]) {
110
- this.startUpload(this.sorted_queue[this.index++].id)
118
+ this.startUpload(this.sorted_queue[this.index++].id);
111
119
  }
112
120
  },
113
121
  file_queue_error: function(file, errorCode, message) {
@@ -127,23 +135,12 @@ var Papermill = {
127
135
  },
128
136
  modify_all: function(papermill_id) {
129
137
  container = jQuery("#" + papermill_id)[0];
130
- attribute_name = jQuery("#batch_" + papermill_id)[0].value
138
+ attribute_name = jQuery("#batch_" + papermill_id)[0].value;
131
139
  attribute_wording = jQuery("#batch_" + papermill_id + " option:selected").text();
132
- value = prompt(attribute_wording + ":")
140
+ value = prompt(attribute_wording + ":");
133
141
  if(value != null) {
134
- jQuery.ajax({async:true, data:jQuery(container).sortable('serialize'), dataType:'script', type:'post', url:'/papermill/mass_edit?attribute=' + attribute_name + "&value=" + value})
142
+ jQuery.ajax({async:true, data:jQuery(container).sortable('serialize') + "&list_id=" + container.id, dataType:'script', type:'post', url:'/papermill/mass_edit?attribute=' + attribute_name + "&value=" + value});
135
143
  }
136
- },
137
- mass_delete: function(papermill_id, wording) {
138
- if(confirm(wording)){
139
- jQuery.ajax({async:true, data:jQuery('#' + papermill_id).sortable('serialize'), dataType:'script', type:'post', url:'/papermill/mass_delete'})
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
- }
147
144
  }
148
- }
145
+ };
149
146