card-mod-carrierwave 0.11.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
@@ -0,0 +1,85 @@
|
|
1
|
+
# action id of the cached upload
|
2
|
+
attr_accessor :action_id_of_cached_upload
|
3
|
+
|
4
|
+
def actionable?
|
5
|
+
super || preliminary_upload?
|
6
|
+
end
|
7
|
+
|
8
|
+
event :prepare_attachment, :prepare_to_validate, on: :save, when: :preliminary_upload? do
|
9
|
+
save_original_filename # save original filename as comment in action
|
10
|
+
write_identifier # set db_content
|
11
|
+
# (needs original filename to determine extension)
|
12
|
+
store_attachment!
|
13
|
+
store_card_changes
|
14
|
+
# finalize_action # create Card::Change entry for db_content
|
15
|
+
|
16
|
+
card_id = new_card? ? upload_cache_card.id : id
|
17
|
+
@current_action.update! draft: true, card_id: card_id
|
18
|
+
success << {
|
19
|
+
target: (new_card? ? upload_cache_card : self),
|
20
|
+
type: type_name,
|
21
|
+
view: "preview_editor",
|
22
|
+
rev_id: current_action.id
|
23
|
+
}
|
24
|
+
abort :success
|
25
|
+
end
|
26
|
+
|
27
|
+
event :assign_attachment_on_create, :initialize,
|
28
|
+
after: :assign_action, on: :create, when: :save_preliminary_upload? do
|
29
|
+
return unless (action = Card::Action.fetch(@action_id_of_cached_upload))
|
30
|
+
upload_cache_card.selected_action_id = action.id
|
31
|
+
upload_cache_card.select_file_revision
|
32
|
+
assign_attachment upload_cache_card.attachment.file, action.comment
|
33
|
+
end
|
34
|
+
|
35
|
+
event :assign_attachment_on_update, :initialize,
|
36
|
+
after: :assign_action, on: :update, when: :save_preliminary_upload? do
|
37
|
+
return unless (action = Card::Action.fetch(@action_id_of_cached_upload))
|
38
|
+
uploaded_file = with_selected_action_id(action.id) { attachment.file }
|
39
|
+
assign_attachment uploaded_file, action.comment
|
40
|
+
end
|
41
|
+
|
42
|
+
def assign_attachment file, original_filename
|
43
|
+
send "#{attachment_name}=", file
|
44
|
+
write_identifier
|
45
|
+
@current_action&.update! comment: original_filename
|
46
|
+
end
|
47
|
+
|
48
|
+
event :delete_cached_upload_file_on_create, :integrate,
|
49
|
+
on: :create, when: :save_preliminary_upload? do
|
50
|
+
return unless (action = Card::Action.fetch(@action_id_of_cached_upload))
|
51
|
+
upload_cache_card.delete_files_for_action action
|
52
|
+
action.delete
|
53
|
+
end
|
54
|
+
|
55
|
+
# at some point uploaded files of canceled file card creation
|
56
|
+
# should be deleted. We do this when ever an new file is created.
|
57
|
+
event :clear_draft_files, :integrate_with_delay, priority: 100, on: :create do
|
58
|
+
Card.delete_tmp_files_of_cached_uploads
|
59
|
+
end
|
60
|
+
|
61
|
+
event :delete_cached_upload_file_on_update, :integrate,
|
62
|
+
on: :update, when: :save_preliminary_upload? do
|
63
|
+
return unless (action = Card::Action.fetch(@action_id_of_cached_upload))
|
64
|
+
delete_files_for_action action
|
65
|
+
action.delete
|
66
|
+
end
|
67
|
+
|
68
|
+
# used for uploads for new cards until the new card is created
|
69
|
+
def upload_cache_card
|
70
|
+
cache_card_codename = "new_#{attachment_name}"
|
71
|
+
@upload_cache_card ||= Card::Codename.card(cache_card_codename) { Card[:new_file] }
|
72
|
+
end
|
73
|
+
|
74
|
+
def preliminary_upload?
|
75
|
+
Card::Env && Card::Env.params[:attachment_upload]
|
76
|
+
end
|
77
|
+
|
78
|
+
def save_preliminary_upload?
|
79
|
+
@action_id_of_cached_upload.present?
|
80
|
+
end
|
81
|
+
|
82
|
+
# place for files if card doesn't have an id yet
|
83
|
+
def tmp_upload_dir _action_id=nil
|
84
|
+
"#{files_base_dir}/#{upload_cache_card.id}"
|
85
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module ClassMethods
|
2
|
+
def update_all_storage_locations
|
3
|
+
Card.search(type_id: ["in", Card::FileID, Card::ImageID])
|
4
|
+
.each(&:update_storage_location!)
|
5
|
+
end
|
6
|
+
|
7
|
+
def delete_tmp_files_of_cached_uploads
|
8
|
+
cards_with_disposable_attachments do |card, action|
|
9
|
+
card.delete_files_for_action action
|
10
|
+
action.delete
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def cards_with_disposable_attachments
|
15
|
+
draft_actions_with_attachment.each do |action|
|
16
|
+
# we don't want to delete uploads in progress
|
17
|
+
next unless old_enough?(action.created_at) && (card = action.card)
|
18
|
+
# we can't delete attachments we don't have write access to
|
19
|
+
next if card.read_only?
|
20
|
+
|
21
|
+
yield card, action
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def old_enough? time, expiration_time=5.day.to_i
|
26
|
+
Time.now - time > expiration_time
|
27
|
+
end
|
28
|
+
|
29
|
+
def draft_actions_with_attachment
|
30
|
+
Card::Action.find_by_sql(
|
31
|
+
"SELECT * FROM card_actions "\
|
32
|
+
"INNER JOIN cards ON card_actions.card_id = cards.id "\
|
33
|
+
"WHERE cards.type_id IN (#{Card::FileID}, #{Card::ImageID}) "\
|
34
|
+
"AND card_actions.draft = true"
|
35
|
+
)
|
36
|
+
end
|
37
|
+
|
38
|
+
def count_cards_with_attachment
|
39
|
+
Card.search type_id: ["in", Card::FileID, Card::ImageID], return: :count
|
40
|
+
end
|
41
|
+
end
|
data/set/self/admin.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
add_to_basket(
|
2
|
+
:tasks,
|
3
|
+
name: :update_file_storage_locations,
|
4
|
+
execute_policy: -> { Card.update_all_storage_locations },
|
5
|
+
stats: {
|
6
|
+
title: "cards with attachment",
|
7
|
+
count: -> { Card.count_cards_with_attachment }
|
8
|
+
# link_text: "update storage locations",
|
9
|
+
# task: "update_file_storage_locations"
|
10
|
+
}
|
11
|
+
)
|
12
|
+
|
13
|
+
add_to_basket(
|
14
|
+
:tasks,
|
15
|
+
name: :delete_upload_tmp_files,
|
16
|
+
execute_policy: -> { Card.delete_tmp_files_of_cached_uploads },
|
17
|
+
stats: {
|
18
|
+
title: "tmp files of canceled uploads",
|
19
|
+
count: -> { ::Card.draft_actions_with_attachment },
|
20
|
+
link_text: "delete tmp files",
|
21
|
+
task: "delete_tmp_files_of_cached_uploads"
|
22
|
+
}
|
23
|
+
)
|
data/set/self/favicon.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
format :html do
|
2
|
+
view :source do
|
3
|
+
source = card.type_id == Card::ImageID ? super() : nil
|
4
|
+
source.present? ? source : nest(:logo, view: :source, size: voo.size)
|
5
|
+
end
|
6
|
+
|
7
|
+
view :link_tag, perms: :none do
|
8
|
+
return unless (source = render :source, size: :small)
|
9
|
+
tag :link, rel: "shortcut icon", href: source
|
10
|
+
end
|
11
|
+
|
12
|
+
def raw_help_text
|
13
|
+
"A favicon (or shortcut icon) is a small image used by browsers to help identify "\
|
14
|
+
"your website. [[http://www.decko.org/favicon|How to customize your favicon]]"
|
15
|
+
end
|
16
|
+
end
|
data/set/type/file.rb
ADDED
@@ -0,0 +1,116 @@
|
|
1
|
+
attachment :file, uploader: CarrierWave::FileCardUploader
|
2
|
+
|
3
|
+
module SelectedAction
|
4
|
+
def select_action_by_params params
|
5
|
+
# skip action table lookups for current revision
|
6
|
+
rev_id = params[:rev_id]
|
7
|
+
super unless rev_id && rev_id == last_content_action_id
|
8
|
+
end
|
9
|
+
|
10
|
+
def last_content_action_id
|
11
|
+
return super if temporary_storage_type_change?
|
12
|
+
# find action id from content (saves lookups)
|
13
|
+
db_content.to_s.split(%r{[/\.]})[-2]
|
14
|
+
end
|
15
|
+
end
|
16
|
+
include SelectedAction
|
17
|
+
|
18
|
+
format do
|
19
|
+
view :source do
|
20
|
+
file = card.attachment
|
21
|
+
return "" unless file.valid?
|
22
|
+
contextualize_path file.url
|
23
|
+
end
|
24
|
+
|
25
|
+
view :core do
|
26
|
+
handle_source do |source|
|
27
|
+
card_url source
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def short_content
|
32
|
+
number_to_human_size card.attachment.size
|
33
|
+
end
|
34
|
+
|
35
|
+
def handle_source
|
36
|
+
source = _render_source
|
37
|
+
return "" if source.blank?
|
38
|
+
block_given? ? yield(source) : source
|
39
|
+
rescue => e
|
40
|
+
Rails.logger.info "Error with file source: #{e.message}"
|
41
|
+
tr :file_error
|
42
|
+
end
|
43
|
+
|
44
|
+
def selected_version
|
45
|
+
card.attachment
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
format :file do
|
50
|
+
# NOCACHE because returns send_file args. not in love with this...
|
51
|
+
view :core, cache: :never do
|
52
|
+
# this means we only support known formats. dislike.
|
53
|
+
attachment_format = card.attachment_format(params[:format])
|
54
|
+
return _render_not_found unless attachment_format
|
55
|
+
return card.format(:html).render_core if card.remote_storage?
|
56
|
+
set_response_headers
|
57
|
+
args_for_send_file
|
58
|
+
end
|
59
|
+
|
60
|
+
def args_for_send_file
|
61
|
+
file = selected_version
|
62
|
+
[file.path, { type: file.content_type,
|
63
|
+
filename: "#{card.name.safe_key}#{file.extension}",
|
64
|
+
x_sendfile: true,
|
65
|
+
disposition: (params[:format] == "file" ? "attachment" : "inline") }]
|
66
|
+
end
|
67
|
+
|
68
|
+
def set_response_headers
|
69
|
+
return unless params[:explicit_file] && (response = controller&.response)
|
70
|
+
response.headers["Expires"] = 1.year.from_now.httpdate
|
71
|
+
# currently using default "private", because proxy servers could block
|
72
|
+
# needed permission checks
|
73
|
+
# r.headers["Cache-Control"] = "public"
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
format :html do
|
78
|
+
view :core do
|
79
|
+
handle_source do |source|
|
80
|
+
"<a href=\"#{source}\">#{tr :download, title: title_in_context(voo.title)}</a>"
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
view :input do
|
85
|
+
if card.no_upload?
|
86
|
+
text_field :content, class: "d0-card-content"
|
87
|
+
else
|
88
|
+
haml :file_chooser, action_text: file_chooser_action_text
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
view :preview_editor, unknown: true, cache: :never do
|
93
|
+
haml :preview_editor
|
94
|
+
end
|
95
|
+
|
96
|
+
def file_chooser_action_text
|
97
|
+
action = card.new_card? ? "Add" : "Replace"
|
98
|
+
"#{action} #{humanized_attachment_name}..."
|
99
|
+
end
|
100
|
+
|
101
|
+
def humanized_attachment_name
|
102
|
+
card.attachment_name.to_s.humanize
|
103
|
+
end
|
104
|
+
|
105
|
+
def preview
|
106
|
+
""
|
107
|
+
end
|
108
|
+
|
109
|
+
def cached_upload_card_name
|
110
|
+
Card::Env.params[:attachment_upload].gsub(/\[\w+\]$/, "[action_id_of_cached_upload]")
|
111
|
+
end
|
112
|
+
|
113
|
+
def preview_editor_delete_text
|
114
|
+
tr :delete
|
115
|
+
end
|
116
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
.choose-file
|
2
|
+
= preview
|
3
|
+
%span.btn.btn-secondary.fileinput-button
|
4
|
+
= icon_tag "cloud_upload"
|
5
|
+
%span<
|
6
|
+
= action_text
|
7
|
+
%input.file-upload.slotter.form-control{ id: "card_#{card.type_code}",
|
8
|
+
name: "card[#{card.type_code}]",
|
9
|
+
type: "file" }
|
10
|
+
= hidden_field_tag "attachment_type_id", card.type_id
|
11
|
+
= hidden_field card.attachment_name, class: "attachment_card_name", value: ""
|
12
|
+
= hidden_field_tag 'file_card_name', card.name.url_key
|
13
|
+
#progress.progress.mb-2{ style: "display: none;" }
|
14
|
+
.progress-bar.progress-bar-success{ style: "width: 0%;" }
|
15
|
+
.chosen-file
|
@@ -0,0 +1,19 @@
|
|
1
|
+
.chosen-file
|
2
|
+
%input{ name: cached_upload_card_name, type: "hidden", value: card.selected_action_id }
|
3
|
+
%table.table.table-striped{ role: "presentation" }
|
4
|
+
%tbody.files
|
5
|
+
%tr.template-download.fade.show
|
6
|
+
%td
|
7
|
+
%span.preview
|
8
|
+
= preview
|
9
|
+
%td
|
10
|
+
%p.name
|
11
|
+
= card.original_filename
|
12
|
+
%td
|
13
|
+
%span.size
|
14
|
+
= number_to_human_size card.attachment.size
|
15
|
+
%td.float-right
|
16
|
+
%button.btn.btn-danger.delete.cancel-upload{ "data-type": "DELETE" }
|
17
|
+
= icon_tag :delete
|
18
|
+
%span
|
19
|
+
= preview_editor_delete_text
|
data/set/type/image.rb
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
attachment :image, uploader: CarrierWave::ImageCardUploader
|
2
|
+
|
3
|
+
include File::SelectedAction
|
4
|
+
|
5
|
+
def create_versions? new_file
|
6
|
+
new_file.extension != "svg"
|
7
|
+
end
|
8
|
+
|
9
|
+
def svg?
|
10
|
+
image&.extension == ".svg"
|
11
|
+
end
|
12
|
+
|
13
|
+
format do
|
14
|
+
include File::Format
|
15
|
+
|
16
|
+
view :one_line_content do
|
17
|
+
_render_core size: :icon
|
18
|
+
end
|
19
|
+
|
20
|
+
def short_content
|
21
|
+
render_core size: :icon
|
22
|
+
end
|
23
|
+
|
24
|
+
view :source do
|
25
|
+
return card.content if card.web?
|
26
|
+
image = selected_version
|
27
|
+
return "" unless image.valid?
|
28
|
+
contextualize_path image.url
|
29
|
+
end
|
30
|
+
|
31
|
+
def selected_version
|
32
|
+
size = determine_image_size
|
33
|
+
if size && size != :original
|
34
|
+
card.image.versions[size]
|
35
|
+
else
|
36
|
+
card.image
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def handle_source
|
41
|
+
super
|
42
|
+
end
|
43
|
+
|
44
|
+
def closed_size
|
45
|
+
:icon
|
46
|
+
end
|
47
|
+
|
48
|
+
def main_size
|
49
|
+
:large
|
50
|
+
end
|
51
|
+
|
52
|
+
def default_size
|
53
|
+
:medium
|
54
|
+
end
|
55
|
+
|
56
|
+
def determine_image_size
|
57
|
+
voo.size =
|
58
|
+
case
|
59
|
+
when nest_mode == :closed then closed_size
|
60
|
+
when voo.size.present? then voo.size.to_sym
|
61
|
+
when main? then main_size
|
62
|
+
else default_size
|
63
|
+
end
|
64
|
+
voo.size = :original if voo.size == :full
|
65
|
+
voo.size
|
66
|
+
end
|
67
|
+
|
68
|
+
view :inline do
|
69
|
+
_render_core
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
format :email_html do
|
74
|
+
view :inline, cache: :never do
|
75
|
+
handle_source do |source|
|
76
|
+
return source unless (mail = inherit :active_mail) &&
|
77
|
+
::File.exist?(path = selected_version.path)
|
78
|
+
url = attach_image mail, path
|
79
|
+
image_tag url
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def attach_image mail, path
|
84
|
+
mail.attachments.inline[path] = ::File.read path
|
85
|
+
mail.attachments[path].url
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
format :css do
|
90
|
+
view :core do
|
91
|
+
handle_source
|
92
|
+
end
|
93
|
+
|
94
|
+
view :content do # why is this necessary?
|
95
|
+
render_core
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
format :file do
|
100
|
+
include File::FileFormat
|
101
|
+
end
|