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.
- checksums.yaml +7 -0
- data/config/core_initializers/carrierwave.rb +5 -0
- data/lib/carrier_wave/card_mount.rb +134 -0
- data/lib/carrier_wave/file_card_uploader.rb +320 -0
- data/lib/carrier_wave/image_card_uploader.rb +59 -0
- data/set/abstract/attachment.rb +112 -0
- data/set/abstract/attachment/cloud.rb +133 -0
- data/set/abstract/attachment/coded.rb +22 -0
- data/set/abstract/attachment/local.rb +40 -0
- data/set/abstract/attachment/paths.rb +58 -0
- data/set/abstract/attachment/storage_type.rb +170 -0
- data/set/abstract/attachment/upload_cache.rb +85 -0
- data/set/abstract/attachment/web.rb +3 -0
- data/set/all/file_utils.rb +41 -0
- data/set/self/admin.rb +23 -0
- data/set/self/favicon.rb +16 -0
- data/set/self/new_file.rb +13 -0
- data/set/self/new_image.rb +13 -0
- data/set/type/file.rb +116 -0
- data/set/type/file/file_chooser.haml +15 -0
- data/set/type/file/preview_editor.haml +19 -0
- data/set/type/image.rb +101 -0
- data/set/type/image/html_views.rb +69 -0
- metadata +138 -0
checksums.yaml
ADDED
@@ -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,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
|