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
@@ -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
|