card-mod-carrierwave 0.11.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 29840e46e626a7121a84faa9b4b0f9889694ab7653031bd9cf97ccb896d46263
4
+ data.tar.gz: 503d37c6471c6fd8d0e6cba3c94c65097663ab0854fc24861673c80f913e1e86
5
+ SHA512:
6
+ metadata.gz: 65f55c9a919ea38e364f5863030d35b5674569dab60454ee157c3e78c762afbc427b3e258246d8389714e00eb752bf8076b948e8a5c70b4d202a2dd63ff01695
7
+ data.tar.gz: 69c7e3184ca375b4342ad3a4f5dd2ae9f8654666e0e6cefded79f5b3c2823dfd8a074ad906acdb28b749f51413c49ebba315c7a790aa17907b1da063baf07791
@@ -0,0 +1,5 @@
1
+ require "carrierwave"
2
+
3
+ ActiveSupport.on_load :card do
4
+ Card.extend CarrierWave::Mount
5
+ end
@@ -0,0 +1,134 @@
1
+ require "carrierwave"
2
+
3
+ module CarrierWave
4
+ # adapt carrierwave mount to cards
5
+ module CardMount
6
+ include CarrierWave::Mount
7
+
8
+ def uploaders
9
+ Card.uploaders ||= {}
10
+ end
11
+
12
+ def uploader_options
13
+ Card.uploader_options ||= {}
14
+ end
15
+
16
+ def mount_uploader column, uploader=nil, options={}, &block
17
+ options[:mount_on] ||= :db_content
18
+ super
19
+
20
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
21
+ event :store_#{column}_event, :finalize,
22
+ on: :save, when: :store_#{column}_event? do
23
+ store_#{column}!
24
+ end
25
+
26
+ # remove files only if card has no history
27
+ event :remove_#{column}_event, :finalize,
28
+ on: :delete, when: proc { |c| !c.history? } do
29
+ remove_#{column}!
30
+ end
31
+ event :mark_remove_#{column}_false_event, :finalize,
32
+ on: :update do
33
+ mark_remove_#{column}_false
34
+ end
35
+ event :store_previous_changes_for_#{column}_event, :store,
36
+ on: :update, when: proc { |c| !c.history? } do
37
+ store_previous_changes_for_#{column}
38
+ end
39
+ event :remove_previously_stored_#{column}_event, :finalize,
40
+ on: :update, when: proc { |c| !c.history?} do
41
+ remove_previously_stored_#{column}
42
+ end
43
+
44
+ # don't attempt to store coded images unless ENV specifies it
45
+ def store_#{column}_event?
46
+ !coded? || ENV["STORE_CODED_FILES"]
47
+ end
48
+
49
+ def attachment
50
+ #{column}
51
+ end
52
+
53
+ def store_attachment!
54
+ store_#{column}!
55
+ end
56
+
57
+ def attachment_name
58
+ "#{column}".to_sym
59
+ end
60
+
61
+ def read_uploader *args
62
+ read_attribute *args
63
+ end
64
+
65
+ def write_uploader *args
66
+ write_attribute *args
67
+ end
68
+
69
+ def #{column}=(new_file)
70
+ return if new_file.blank?
71
+ assign_file(new_file) { super }
72
+ end
73
+
74
+ def remote_#{column}_url=(url)
75
+ assign_file(url) { super }
76
+ end
77
+
78
+ def assign_file file
79
+ db_column = _mounter(:#{column}).serialization_column
80
+ send(:"\#{db_column}_will_change!") # unless attribute_is_changing? db_column
81
+ if web?
82
+ self.content = file
83
+ else
84
+ send(:"#{column}_will_change!")
85
+ yield
86
+ end
87
+ end
88
+
89
+ def remove_#{column}=(value)
90
+ column = _mounter(:#{column}).serialization_column
91
+ send(:"\#{column}_will_change!")
92
+ super
93
+ end
94
+
95
+ def remove_#{column}!
96
+ self.remove_#{column} = true
97
+ write_#{column}_identifier
98
+ self.remove_#{column} = false
99
+ super
100
+ end
101
+
102
+ def #{column}_will_change!
103
+ @#{column}_changed = true
104
+ @#{column}_is_changing = true
105
+ end
106
+
107
+ def #{column}_is_changing?
108
+ @#{column}_is_changing
109
+ end
110
+
111
+ def #{column}_changed?
112
+ @#{column}_changed
113
+ end
114
+
115
+ def serializable_hash(options=nil)
116
+ hash = {}
117
+
118
+ except = options && options[:except] &&
119
+ Array.wrap(options[:except]).map(&:to_s)
120
+ only = options && options[:only] &&
121
+ Array.wrap(options[:only]).map(&:to_s)
122
+
123
+ self.class.uploaders.each do |column, uploader|
124
+ if (!only && !except) || (only && only.include?(column.to_s)) ||
125
+ (!only && except && !except.include?(column.to_s))
126
+ hash[column.to_s] = _mounter(column).uploader.serializable_hash
127
+ end
128
+ end
129
+ super(options).merge(hash)
130
+ end
131
+ RUBY
132
+ end
133
+ end
134
+ end
@@ -0,0 +1,320 @@
1
+ module CarrierWave
2
+ class << self
3
+ def tmp_path
4
+ @tmp_path ||= Card.paths["tmp"].existent.first
5
+ end
6
+ end
7
+
8
+ class SanitizedFile
9
+ def content_type
10
+ # the original content_type method doesn't seem to be very reliable
11
+ # It uses mime_magic_content_type - which returns invalid/invalid for css files
12
+ # that start with a comment - as the second option. (we switch the order and
13
+ # use it as the third option)
14
+ @content_type ||=
15
+ existing_content_type ||
16
+ mini_mime_content_type ||
17
+ mime_magic_content_type
18
+ end
19
+ end
20
+
21
+ module Uploader
22
+ # Implements a different name pattern for versions than CarrierWave's
23
+ # default: we expect the version name at the end of the filename separated
24
+ # by a dash
25
+ module Versions
26
+ private
27
+
28
+ # put version at the end of the filename
29
+ def full_filename for_file
30
+ name = super(for_file)
31
+ parts = name.split "."
32
+ basename = [parts.shift, version_name].compact.join("-")
33
+ "#{basename}.#{parts.join('.')}"
34
+ end
35
+ end
36
+ end
37
+
38
+ # Takes care of the file upload for cards with attached files.
39
+ # Most of the upload behaviour depends on the card itself.
40
+ # (e.g. card type and storage option chosen for the card). So in contrary
41
+ # to CarrierWave's default uploader we depend very much on the model
42
+ # (= card object) to get the correct paths for retrieving and storing
43
+ # the file.
44
+ #
45
+ # Cards that support attachments (by default those are cards of type "file"
46
+ # and "image") accept a file handle as a card attribute.
47
+ #
48
+ # @example Attaching a file to a file card
49
+ # Card.create name: "file card", type: :file,
50
+ # file: File.new(path_to_file)
51
+ #
52
+ # @example Attaching a image to a image card
53
+ # Card.create name: "file card", type: :image,
54
+ # image: File.new(path_to_image)
55
+ #
56
+ # It's possible to upload files using a url. The card attribute for that is
57
+ # remote_<attachment_type>_url
58
+ #
59
+ # @example Create a file card using a remote url
60
+ # Card.create name: "file_card", type: :file,
61
+ # remote_file_url: "http://a.file.in/the.web"
62
+ #
63
+ # @example Updating a image card using a remote url
64
+ # card.update remote_image_url: "http://a.image/somewhere.png"
65
+ #
66
+ # ## Storage types
67
+ # You can choose between four different storage options
68
+ # - coded: These files are in the codebase, like the default logo.
69
+ # Every view is a decko request.
70
+ # - local: Uploaded files which are stored in a local upload directory
71
+ # (upload path is configurable via config.paths["files"]).
72
+ # If read permissions are set such that "Anyone" can read, then there is
73
+ # a symlink from the public directory. Otherwise every view is a decko
74
+ # request.
75
+ # - cloud: You can configure buckets that refer to an external storage
76
+ # service. Link is rendered as absolute url
77
+ # - web: A fixed url (to external source). No upload or other file
78
+ # processing. Link is just the saved url.
79
+ #
80
+ # Currently, there is no web interface that let's a user or administrator
81
+ # choose a storage option for a specific card or set of cards.
82
+ # There is only a global config option to set the storage type for all new
83
+ # uploads (config.storage_type). On the *admin card it's possible to
84
+ # update all existing file cards according to the current global config.
85
+ #
86
+ # Storage types for single cards can be changed by developers using
87
+ # the card attributes "storage_type", "bucket", and "mod".
88
+ #
89
+ # @example Creating a hard-coded file
90
+ # Card.create name: "file card", type_id: Card::FileID,
91
+ # file: File.new(path),
92
+ # storage_type: :coded, mod: "account"
93
+ #
94
+ # @example Moving a file to a cloud service
95
+ # # my_deck/config/application.rb:
96
+ # config.file_buckets = {
97
+ # aws_bucket: {
98
+ # provider: "fog/aws",
99
+ # directory: "bucket-name",
100
+ # subdirectory: "files",
101
+ # credentials: {
102
+ # provider: 'AWS' # required
103
+ # aws_access_key_id: 'key' # required
104
+ # aws_secret_access_key: 'secret-key' # required
105
+ # public: true,
106
+ # }
107
+ # }
108
+ #
109
+ # # decko console or rake task:
110
+ # card.update storage_type: :cloud, bucket: :aws_bucket
111
+ #
112
+ # @example Creating a file card with fixed external link
113
+ # Card.create name: "file card", type_id: Card::FileID,
114
+ # content: "http://animals.org/cat.png"
115
+ # storage_type: :web
116
+ #
117
+ # Card.create name: "file card", type_id: Card::FileID,
118
+ # file: "http://animals.org/cat.png"
119
+ # storage_type: :web
120
+ #
121
+ # Depending on the storage type the uploader uses the following paths
122
+ # and identifiers.
123
+ # ### Identifier (stored in the database as db_content)
124
+ # - coded: :codename/mod_name.ext
125
+ # - local: ~card_id/action_id.ext
126
+ # - cloud: (bucket)/card_id/action_id.ext
127
+ # - web: http://url
128
+ #
129
+ # ### Storage path
130
+ # - coded:
131
+ # mod_dir/file/codename/type_code(-variant).ext (no colon on codename!)
132
+ # - local:
133
+ # files_dir/card_id/action_id(-variant).ext (no tilde on id!)
134
+ # - cloud:
135
+ # bucket/bucket_subdir/id/action_id(-variant).ext
136
+ # - web: no storage
137
+ #
138
+ # Variants are only used for images. Possible options are
139
+ # icon|small|medium|large|original.
140
+ # files_dir, bucket, and bucket_subdir can be changed via config options.
141
+ #
142
+ # ### Supported url patterns
143
+ # mark.ext
144
+ # mark/revision.ext
145
+ # mark/revision-variant.ext
146
+ # /files/mark/revision-variant.ext # <- public symlink if readable by
147
+ # # "Anyone"
148
+ #
149
+ # <mark> can be one of the following options
150
+ # - <card name>
151
+ # - ~<card id>
152
+ # - :<code name>
153
+ #
154
+ # <revision> is the mod name if the file is coded or and action_id in any
155
+ # case
156
+ #
157
+ # Examples:
158
+ # *logo.png
159
+ # ~22/33-medium.png # local
160
+ # :yeti_skin/standard-large.png # coded
161
+ #
162
+ class FileCardUploader < Uploader::Base
163
+ attr_accessor :mod
164
+ include Card::Env::Location
165
+
166
+ STORAGE_TYPES = [:cloud, :web, :coded, :local].freeze
167
+ CONFIG_OPTIONS = [:provider, :attributes, :directory, :public, :credentials,
168
+ :authenticated_url_expiration, :use_ssl_for_aws].freeze
169
+ CONFIG_CREDENTIAL_OPTIONS = [
170
+ :provider,
171
+ :aws_access_key_id, :aws_secret_access_key, :region, :host, :endpoint,
172
+ :google_access_key_id, :google_secret_access_key
173
+ ].freeze
174
+ delegate :store_dir, :retrieve_dir, :file_dir, :mod, :bucket, to: :model
175
+
176
+ def valid?
177
+ extension.present?
178
+ end
179
+
180
+ def filename
181
+ if model.coded?
182
+ "#{model.type_code}#{extension}"
183
+ else
184
+ "#{action_id}#{extension}"
185
+ end
186
+ end
187
+
188
+ def extension
189
+ case
190
+ when file&.extension.present? then ".#{file.extension}"
191
+ when card_content = model.content then File.extname(card_content)
192
+ when orig = original_filename then File.extname(orig)
193
+ else ""
194
+ end.downcase
195
+ end
196
+
197
+ # generate identifier that gets stored in the card's db_content field
198
+ # @param opts [Hash] generate an identifier using the given storage options
199
+ # instead of the storage options derived from the model and
200
+ # the global configuration
201
+ # @option opts [Symbol] storage_type
202
+ # @option opts [String] mod
203
+ # @option opts [Symbol] bucket
204
+ def db_content opts={}
205
+ model.with_storage_options opts do
206
+ return model.content if model.web?
207
+ return "" unless file.present?
208
+ "%s/%s" % [file_dir, url_filename]
209
+ end
210
+ end
211
+
212
+ def url_filename opts={}
213
+ model.with_storage_options opts do
214
+ if model.coded?
215
+ "#{model.mod}#{extension}"
216
+ else
217
+ "#{action_id}#{extension}"
218
+ end
219
+ end
220
+ end
221
+
222
+ # @option opts [Symbol] :absolute - return absolute url
223
+ def url opts={}
224
+ if model.cloud?
225
+ file&.url
226
+ elsif model.web?
227
+ model.content
228
+ else
229
+ local_url opts
230
+ end
231
+ end
232
+
233
+ def local_url opts={}
234
+ "%s/%s/%s" % [local_url_base(opts), file_dir, full_filename(url_filename(opts))]
235
+ end
236
+
237
+ def local_url_base opts={}
238
+ web_path = Card.config.files_web_path
239
+ opts.delete(:absolute) ? card_url(web_path) : card_path(web_path)
240
+ end
241
+
242
+ def public_path
243
+ File.join Cardio.paths["public"].existent.first, url
244
+ end
245
+
246
+ def cache_dir
247
+ @model.files_base_dir + "/cache"
248
+ end
249
+
250
+ # Carrierwave calls store_path without argument when it stores the file
251
+ # and with the identifier from the db when it retrieves the file.
252
+ # In our case the first part of our identifier is not part of the path
253
+ # but we can construct the filename from db data. So we don't need the
254
+ # identifier.
255
+ def store_path for_file=nil
256
+ if for_file
257
+ retrieve_path
258
+ else
259
+ File.join([store_dir, full_filename(filename)].compact)
260
+ end
261
+ end
262
+
263
+ def retrieve_path
264
+ File.join([retrieve_dir, full_filename(filename)].compact)
265
+ end
266
+
267
+ def tmp_path
268
+ Dir.mkdir model.tmp_upload_dir unless Dir.exist? model.tmp_upload_dir
269
+ File.join model.tmp_upload_dir, filename
270
+ end
271
+
272
+ def create_versions? new_file
273
+ model.create_versions? new_file
274
+ end
275
+
276
+ # paperclip compatibility used in type/file.rb#core (base format)
277
+ def path version=nil
278
+ version ? versions[version].path : super()
279
+ end
280
+
281
+ def original_filename
282
+ @original_filename ||= model.selected_action &&
283
+ model.selected_action.comment
284
+ end
285
+
286
+ def action_id
287
+ model.selected_content_action_id || action_id_stand_in
288
+ end
289
+
290
+ # delegate carrierwave's fog config methods to bucket configuration
291
+ ::CarrierWave::FileCardUploader::CONFIG_OPTIONS.each do |name|
292
+ define_method("fog_#{name}") { bucket_config name }
293
+ end
294
+
295
+ def bucket_config option
296
+ @model.bucket_config[option]
297
+ end
298
+
299
+ def asset_host
300
+ bucket_config(:asset_host) || super
301
+ end
302
+
303
+ private
304
+
305
+ # used as action_id in the filename
306
+ # if card is not #actionable?
307
+ def action_id_stand_in
308
+ Time.now.to_i
309
+ end
310
+
311
+ def storage
312
+ case @model.storage_type
313
+ when :cloud
314
+ ::CarrierWave::Storage::Fog.new(self)
315
+ else
316
+ ::CarrierWave::Storage::File.new(self)
317
+ end
318
+ end
319
+ end
320
+ end