card-mod-carrierwave 0.11.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.
@@ -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