metadata_presenter 3.0.15 → 3.2.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 +4 -4
- data/app/controllers/metadata_presenter/answers_controller.rb +107 -5
- data/app/controllers/metadata_presenter/file_controller.rb +9 -0
- data/app/controllers/metadata_presenter/pages_controller.rb +1 -2
- data/app/helpers/metadata_presenter/application_helper.rb +56 -0
- data/app/models/metadata_presenter/component.rb +10 -1
- data/app/models/metadata_presenter/file_uploader.rb +4 -0
- data/app/models/metadata_presenter/multi_upload_answer.rb +33 -0
- data/app/models/metadata_presenter/page.rb +4 -0
- data/app/models/metadata_presenter/page_answers.rb +60 -1
- data/app/presenters/metadata_presenter/page_answers_presenter.rb +4 -0
- data/app/validators/metadata_presenter/accept_validator.rb +11 -0
- data/app/validators/metadata_presenter/max_files_validator.rb +9 -0
- data/app/validators/metadata_presenter/multiupload_validator.rb +24 -0
- data/app/validators/metadata_presenter/required_validator.rb +4 -0
- data/app/validators/metadata_presenter/validate_answers.rb +15 -6
- data/app/views/metadata_presenter/component/_multiupload.html.erb +51 -0
- data/app/views/metadata_presenter/page/confirmation.html.erb +2 -0
- data/app/views/metadata_presenter/page/content.html.erb +4 -1
- data/config/initializers/supported_components.rb +1 -1
- data/config/locales/en.yml +12 -0
- data/config/routes.rb +1 -0
- data/default_metadata/component/multiupload.json +30 -0
- data/default_metadata/page/content.json +0 -1
- data/default_metadata/string/error.max_files.json +6 -0
- data/default_metadata/string/error.multiupload.json +6 -0
- data/default_metadata/validations/max_files.json +1 -0
- data/fixtures/version.json +73 -0
- data/lib/metadata_presenter/version.rb +1 -1
- data/schemas/component/content.json +29 -0
- data/schemas/component/multiupload.json +42 -0
- data/schemas/definition/expression_item.json +36 -0
- data/schemas/definition/expressions.json +15 -0
- data/schemas/flow/branch.json +1 -27
- data/schemas/page/confirmation.json +0 -3
- data/schemas/validations/validations.json +38 -0
- metadata +27 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e690363ac373962c0478469b6b501d6ecc255473d6bbd04aff6cf012aa098843
|
4
|
+
data.tar.gz: 907eced7e4a09a2b247130deb082ef5c05766dd3d995d18483c69d5dcb8babf7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a061114f4db7608516af2da01234bedd45b11708c47c9f693ebffa0b13d7f1fe2814313ce79bfc4bf555b2db3526d63743193789297e1bf2f3d8a87ec6611a40
|
7
|
+
data.tar.gz: 78b39e5035fe137a52b83c2b3f6a42edcc72c2d921247a67f6958608acf2a5df8ba15795986e56f5d75e602c530a1d83005c30a89d51c0ce1362f2ce9984be91
|
@@ -4,30 +4,85 @@ module MetadataPresenter
|
|
4
4
|
|
5
5
|
def create
|
6
6
|
@previous_answers = reload_user_data.deep_dup
|
7
|
-
|
7
|
+
|
8
|
+
@page_answers = PageAnswers.new(page, incoming_answer, autocomplete_items(page.components))
|
8
9
|
|
9
10
|
if params[:save_for_later].present?
|
10
|
-
save_user_data
|
11
|
-
|
11
|
+
save_user_data unless upload? || multiupload?
|
12
|
+
|
12
13
|
redirect_to save_path(page_slug: params[:page_slug]) and return
|
13
14
|
end
|
14
15
|
|
15
16
|
upload_files if upload?
|
17
|
+
upload_multiupload_new_files if multiupload? && answers_params.present?
|
16
18
|
|
17
19
|
if @page_answers.validate_answers
|
18
20
|
save_user_data # method signature
|
21
|
+
|
22
|
+
# if adding another file in multi upload, redirect back to referrer
|
23
|
+
if about_to_render_multiupload?
|
24
|
+
redirect_back(fallback_location: root_path) and return
|
25
|
+
end
|
26
|
+
|
19
27
|
redirect_to_next_page
|
20
28
|
else
|
29
|
+
# can't render error in the same way for the multiupload component
|
30
|
+
if about_to_render_multiupload?
|
31
|
+
@user_data = @previous_answers
|
32
|
+
|
33
|
+
render template: @page.template, status: :unprocessable_entity and return
|
34
|
+
end
|
21
35
|
render_validation_error
|
22
36
|
end
|
23
37
|
end
|
24
38
|
|
39
|
+
def about_to_render_multiupload?
|
40
|
+
answers_params.present? && multiupload?
|
41
|
+
end
|
42
|
+
|
25
43
|
def update_count_matching_filenames(original_filename, user_data)
|
26
44
|
extname = File.extname(original_filename)
|
27
45
|
basename = File.basename(original_filename, extname)
|
28
46
|
filename_regex = /^#{Regexp.quote(basename)}(?>-\((\d)\))?#{Regexp.quote(extname)}/
|
29
47
|
|
30
|
-
user_data.select { |_k, v|
|
48
|
+
user_data.select { |_k, v|
|
49
|
+
if v.is_a?(Array)
|
50
|
+
v.any? { |e| e['original_filename'] =~ filename_regex }
|
51
|
+
else
|
52
|
+
v['original_filename'] =~ filename_regex
|
53
|
+
end
|
54
|
+
}.count
|
55
|
+
end
|
56
|
+
|
57
|
+
def upload_multiupload_new_files
|
58
|
+
user_data = load_user_data
|
59
|
+
@page_answers.page.multiupload_components.each do |component|
|
60
|
+
previous_answers = user_data[component.id]
|
61
|
+
incoming_filename = @page_answers.send(component.id)[component.id].last['original_filename']
|
62
|
+
|
63
|
+
if editor_preview?
|
64
|
+
@page_answers.uploaded_files.push(multiuploaded_file(previous_answers, component))
|
65
|
+
else
|
66
|
+
|
67
|
+
if incoming_filename.present?
|
68
|
+
# determine if duplicate filename from any other user answer
|
69
|
+
@page_answers.count = update_count_matching_filenames(incoming_filename, user_data)
|
70
|
+
end
|
71
|
+
|
72
|
+
if previous_answers.present? && previous_answers.any? { |answer| answer['original_filename'] == incoming_answer.incoming_answer.values.first.original_filename }
|
73
|
+
@page_answers.count = nil # ensure we don't also try to suffix this filename as we will reject it anyway
|
74
|
+
file = MetadataPresenter::UploadedFile.new(
|
75
|
+
file: @page_answers.send(component.id)[component.id].last,
|
76
|
+
component:
|
77
|
+
)
|
78
|
+
|
79
|
+
file.errors.add('invalid.multiupload')
|
80
|
+
@page_answers.uploaded_files.push(file)
|
81
|
+
else
|
82
|
+
@page_answers.uploaded_files.push(multiuploaded_file(previous_answers, component))
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
31
86
|
end
|
32
87
|
|
33
88
|
def show_save_and_return
|
@@ -67,6 +122,16 @@ module MetadataPresenter
|
|
67
122
|
render template: page.template, status: :unprocessable_entity
|
68
123
|
end
|
69
124
|
|
125
|
+
def incoming_answer
|
126
|
+
if multiupload?
|
127
|
+
multiupload_answer = MultiUploadAnswer.new
|
128
|
+
multiupload_answer.key = Array(page.components).first.id
|
129
|
+
multiupload_answer.previous_answers = @previous_answers[Array(page.components).first.id]
|
130
|
+
multiupload_answer.incoming_answer = answers_params
|
131
|
+
end
|
132
|
+
multiupload_answer || answers_params
|
133
|
+
end
|
134
|
+
|
70
135
|
def answers_params
|
71
136
|
params.permit(:page_slug, :save_for_later)
|
72
137
|
params[:answers] ? params[:answers].permit! : {}
|
@@ -84,7 +149,6 @@ module MetadataPresenter
|
|
84
149
|
user_data = load_user_data
|
85
150
|
@page_answers.page.upload_components.each do |component|
|
86
151
|
answer = user_data[component.id]
|
87
|
-
|
88
152
|
original_filename = answer.nil? ? @page_answers.send(component.id)['original_filename'] : answer['original_filename']
|
89
153
|
|
90
154
|
if original_filename.present?
|
@@ -112,6 +176,40 @@ module MetadataPresenter
|
|
112
176
|
end
|
113
177
|
end
|
114
178
|
|
179
|
+
def multiuploaded_file(answer, component)
|
180
|
+
if answer.present?
|
181
|
+
if @page_answers.answers.is_a?(MetadataPresenter::MultiUploadAnswer)
|
182
|
+
if @page_answers.answers.incoming_answer.present?
|
183
|
+
FileUploader.new(
|
184
|
+
session:,
|
185
|
+
page_answers: @page_answers,
|
186
|
+
component:,
|
187
|
+
adapter: upload_adapter
|
188
|
+
).upload
|
189
|
+
else
|
190
|
+
MetadataPresenter::UploadedFile.new(
|
191
|
+
file: @page_answers.answers.previous_answers.last,
|
192
|
+
component:
|
193
|
+
)
|
194
|
+
end
|
195
|
+
else
|
196
|
+
FileUploader.new(
|
197
|
+
session:,
|
198
|
+
page_answers: @page_answers,
|
199
|
+
component:,
|
200
|
+
adapter: upload_adapter
|
201
|
+
).upload
|
202
|
+
end
|
203
|
+
else
|
204
|
+
FileUploader.new(
|
205
|
+
session:,
|
206
|
+
page_answers: @page_answers,
|
207
|
+
component:,
|
208
|
+
adapter: upload_adapter
|
209
|
+
).upload
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
115
213
|
def upload_adapter
|
116
214
|
super if defined?(super)
|
117
215
|
end
|
@@ -119,5 +217,9 @@ module MetadataPresenter
|
|
119
217
|
def upload?
|
120
218
|
Array(page.components).any?(&:upload?)
|
121
219
|
end
|
220
|
+
|
221
|
+
def multiupload?
|
222
|
+
Array(page.components).any?(&:multiupload?)
|
223
|
+
end
|
122
224
|
end
|
123
225
|
end
|
@@ -5,8 +5,17 @@ module MetadataPresenter
|
|
5
5
|
redirect_back(fallback_location: root_path)
|
6
6
|
end
|
7
7
|
|
8
|
+
def remove_multifile
|
9
|
+
remove_file_from_data(params[:component_id], params[:file_uuid])
|
10
|
+
redirect_back(fallback_location: root_path)
|
11
|
+
end
|
12
|
+
|
8
13
|
def remove_user_data(component_id)
|
9
14
|
super(component_id) if defined?(super)
|
10
15
|
end
|
16
|
+
|
17
|
+
def remove_file_from_data(component_id, file_id)
|
18
|
+
super(component_id, file_id) if defined?(super)
|
19
|
+
end
|
11
20
|
end
|
12
21
|
end
|
@@ -4,13 +4,12 @@ module MetadataPresenter
|
|
4
4
|
|
5
5
|
def show
|
6
6
|
@user_data = load_user_data # method signature
|
7
|
-
@page ||= service.find_page_by_url(request.env['PATH_INFO'])
|
8
7
|
|
8
|
+
@page ||= service.find_page_by_url(request.env['PATH_INFO'])
|
9
9
|
if @page
|
10
10
|
load_autocomplete_items
|
11
11
|
|
12
12
|
@page_answers = PageAnswers.new(@page, @user_data)
|
13
|
-
|
14
13
|
render template: @page.template
|
15
14
|
else
|
16
15
|
not_found
|
@@ -35,5 +35,61 @@ module MetadataPresenter
|
|
35
35
|
def default_page_title(type)
|
36
36
|
MetadataPresenter::DefaultMetadata[type.to_s]&.[]('heading')
|
37
37
|
end
|
38
|
+
|
39
|
+
def multiupload_files_remaining
|
40
|
+
component = page_multiupload_component
|
41
|
+
answers = @user_data.keys.include?(component.id) ? @user_data.find(component.id).first : []
|
42
|
+
max_files = component.validation['max_files'].to_i
|
43
|
+
|
44
|
+
if uploads_remaining.zero?
|
45
|
+
I18n.t('presenter.questions.multiupload.none')
|
46
|
+
elsif max_files == 1
|
47
|
+
I18n.t('presenter.questions.multiupload.single_upload')
|
48
|
+
elsif uploads_remaining == 1
|
49
|
+
if answers.present?
|
50
|
+
I18n.t('presenter.questions.multiupload.answered_singular')
|
51
|
+
else
|
52
|
+
I18n.t('presenter.questions.multiupload.singular')
|
53
|
+
end
|
54
|
+
elsif answers.present?
|
55
|
+
I18n.t('presenter.questions.multiupload.answered_plural', num: uploads_remaining)
|
56
|
+
else
|
57
|
+
I18n.t('presenter.questions.multiupload.plural', num: uploads_remaining)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def uploads_remaining
|
62
|
+
component = page_multiupload_component
|
63
|
+
max_files = component.validation['max_files'].to_i
|
64
|
+
answers = @user_data.keys.include?(component.id) ? @user_data[component.id] : []
|
65
|
+
return 0 if answers.is_a?(ActionDispatch::Http::UploadedFile)
|
66
|
+
|
67
|
+
max_files - answers.count
|
68
|
+
end
|
69
|
+
|
70
|
+
def uploads_count
|
71
|
+
component = page_multiupload_component
|
72
|
+
answers = @user_data.keys.include?(component.id) ? @user_data[component.id] : []
|
73
|
+
|
74
|
+
return 0 if answers.is_a?(ActionDispatch::Http::UploadedFile)
|
75
|
+
|
76
|
+
answers.count == 1 ? I18n.t('presenter.questions.multiupload.answered_count_singular') : I18n.t('presenter.questions.multiupload.answered_count_plural', num: answers.count)
|
77
|
+
end
|
78
|
+
|
79
|
+
def files_to_render
|
80
|
+
component = page_multiupload_component
|
81
|
+
|
82
|
+
error_file = @page_answers.uploaded_files.select { |file| file.errors.any? }.first
|
83
|
+
|
84
|
+
if error_file.present?
|
85
|
+
@page_answers.send(component.id)[component.id].compact.reject { |file| file[error_file.file['original_filename'] == 'original_filename'] }
|
86
|
+
else
|
87
|
+
@page_answers.send(component.id)[component.id].compact
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def page_multiupload_component
|
92
|
+
@page.components.select { |c| c.type == 'multiupload' }.first
|
93
|
+
end
|
38
94
|
end
|
39
95
|
end
|
@@ -3,7 +3,8 @@ class MetadataPresenter::Component < MetadataPresenter::Metadata
|
|
3
3
|
'date' => 'date',
|
4
4
|
'number' => 'number',
|
5
5
|
'text' => 'string',
|
6
|
-
'textarea' => 'string'
|
6
|
+
'textarea' => 'string',
|
7
|
+
'multiupload' => 'file'
|
7
8
|
}.freeze
|
8
9
|
|
9
10
|
# Used for max_length and max_word validations.
|
@@ -57,6 +58,10 @@ class MetadataPresenter::Component < MetadataPresenter::Metadata
|
|
57
58
|
type == 'upload'
|
58
59
|
end
|
59
60
|
|
61
|
+
def multiupload?
|
62
|
+
type == 'multiupload'
|
63
|
+
end
|
64
|
+
|
60
65
|
def find_item_by_uuid(uuid)
|
61
66
|
items.find { |item| item.uuid == uuid }
|
62
67
|
end
|
@@ -73,6 +78,10 @@ class MetadataPresenter::Component < MetadataPresenter::Metadata
|
|
73
78
|
VALIDATION_STRING_LENGTH_THRESHOLD
|
74
79
|
end
|
75
80
|
|
81
|
+
def max_files
|
82
|
+
metadata.max_files.presence || '0'
|
83
|
+
end
|
84
|
+
|
76
85
|
private
|
77
86
|
|
78
87
|
def validation_bundle_key
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module MetadataPresenter
|
2
|
+
class MultiUploadAnswer
|
3
|
+
attr_accessor :previous_answers, :incoming_answer, :key
|
4
|
+
|
5
|
+
def to_h
|
6
|
+
{
|
7
|
+
key => previous_answers_value.present? ? previous_answers_value.reject(&:blank?) : []
|
8
|
+
}
|
9
|
+
end
|
10
|
+
|
11
|
+
def previous_answers_value
|
12
|
+
return nil if previous_answers.nil? && incoming_answer.nil?
|
13
|
+
return [incoming_answer] if previous_answers.nil? && incoming_answer.present?
|
14
|
+
|
15
|
+
if previous_answers.is_a?(Array)
|
16
|
+
return previous_answers.reject(&:blank?) if incoming_answer.nil? || previous_answers.find { |answer|
|
17
|
+
answer['original_filename'] == incoming_answer['original_filename']
|
18
|
+
}.present?
|
19
|
+
|
20
|
+
previous_answers.reject(&:blank?).push(incoming_answer)
|
21
|
+
else
|
22
|
+
return [previous_answers] if incoming_answer.nil?
|
23
|
+
|
24
|
+
[previous_answers, incoming_answer]
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def from_h(input)
|
29
|
+
self.key = input.keys[0]
|
30
|
+
self.previous_answers = input[key]
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -25,11 +25,13 @@ module MetadataPresenter
|
|
25
25
|
|
26
26
|
def method_missing(method_name, *_args)
|
27
27
|
component = components.find { |c| c.id == method_name.to_s }
|
28
|
-
|
29
28
|
if component && component.type == 'date'
|
30
29
|
date_answer(component.id)
|
31
30
|
elsif component && component.type == 'upload'
|
32
31
|
upload_answer(component.id, count)
|
32
|
+
elsif component && component.type == 'multiupload'
|
33
|
+
answer_object = multiupload_answer(component.id, count)
|
34
|
+
answer_object.to_h if answer_object.present?
|
33
35
|
elsif component && component.type == 'checkboxes'
|
34
36
|
answers[method_name.to_s].to_a
|
35
37
|
else
|
@@ -53,6 +55,62 @@ module MetadataPresenter
|
|
53
55
|
end
|
54
56
|
end
|
55
57
|
|
58
|
+
def multiupload_answer(component_id, _count)
|
59
|
+
file_details = answers[component_id.to_s] unless answers.is_a?(MetadataPresenter::MultiUploadAnswer)
|
60
|
+
return nil if file_details.nil? && answers.nil?
|
61
|
+
|
62
|
+
if file_details.is_a?(Hash)
|
63
|
+
# when referencing a single previous answer but no incoming new answer
|
64
|
+
presentable = MetadataPresenter::MultiUploadAnswer.new
|
65
|
+
presentable.key = component_id.to_s
|
66
|
+
presentable.previous_answers = [file_details]
|
67
|
+
return presentable
|
68
|
+
end
|
69
|
+
|
70
|
+
if file_details.is_a?(Array)
|
71
|
+
# when referencing multiple previous answers but no incoming new answer
|
72
|
+
presentable = MetadataPresenter::MultiUploadAnswer.new
|
73
|
+
presentable.key = component_id.to_s
|
74
|
+
presentable.previous_answers = file_details.reject { |f| f['original_filename'].blank? }
|
75
|
+
return presentable
|
76
|
+
end
|
77
|
+
|
78
|
+
if answers.blank?
|
79
|
+
return nil
|
80
|
+
end
|
81
|
+
|
82
|
+
if answers.is_a?(Hash) # rendering only existing answers
|
83
|
+
return if answers[component_id].blank?
|
84
|
+
|
85
|
+
if answers[component_id].is_a?(Array)
|
86
|
+
answers[component_id].each { |answer| answer['original_filename'] = sanitize(filename(update_filename(answer['original_filename']))) }
|
87
|
+
end
|
88
|
+
|
89
|
+
answers[component_id] = answers[component_id].reject { |a| a['original_filename'].blank? }
|
90
|
+
return answers
|
91
|
+
end
|
92
|
+
|
93
|
+
# uploading a new answer, this method will be called during multiple render operations
|
94
|
+
if answers.incoming_answer.present? && answers.incoming_answer.is_a?(ActionController::Parameters)
|
95
|
+
answers.incoming_answer[component_id].original_filename = sanitize(filename(update_filename(answers.incoming_answer[component_id].original_filename)))
|
96
|
+
end
|
97
|
+
|
98
|
+
if answers.incoming_answer.present? && answers.incoming_answer.is_a?(Hash)
|
99
|
+
answers.incoming_answer['original_filename'] = sanitize(filename(update_filename(answers.incoming_answer['original_filename'])))
|
100
|
+
end
|
101
|
+
|
102
|
+
if answers.incoming_answer.present? && answers.incoming_answer[component_id].is_a?(ActionDispatch::Http::UploadedFile)
|
103
|
+
answers.incoming_answer = {
|
104
|
+
'original_filename' => sanitize(filename(update_filename(answers.incoming_answer[component_id].original_filename))),
|
105
|
+
'content_type' => answers.incoming_answer[component_id].content_type,
|
106
|
+
'tempfile' => answers.incoming_answer[component_id].tempfile.path.to_s,
|
107
|
+
'uuid' => SecureRandom.uuid
|
108
|
+
}
|
109
|
+
end
|
110
|
+
|
111
|
+
answers
|
112
|
+
end
|
113
|
+
|
56
114
|
def date_answer(component_id)
|
57
115
|
date = raw_date_answer(component_id)
|
58
116
|
|
@@ -81,6 +139,7 @@ module MetadataPresenter
|
|
81
139
|
basename = File.basename(filename, extname)
|
82
140
|
|
83
141
|
filename = "#{basename}-(#{count})#{extname}"
|
142
|
+
@count = nil # this is called multiple times for multiupload components so ensure we apply suffix to filename only once
|
84
143
|
end
|
85
144
|
|
86
145
|
filename
|
@@ -84,6 +84,10 @@ module MetadataPresenter
|
|
84
84
|
file_hash['original_filename']
|
85
85
|
end
|
86
86
|
|
87
|
+
def multiupload(multifile_hash)
|
88
|
+
multifile_hash[component.id].map { |i| i['original_filename'] }.join('<br>').html_safe
|
89
|
+
end
|
90
|
+
|
87
91
|
def autocomplete(value)
|
88
92
|
JSON.parse(value)['text']
|
89
93
|
end
|
@@ -3,5 +3,16 @@ module MetadataPresenter
|
|
3
3
|
def error_name
|
4
4
|
'accept'
|
5
5
|
end
|
6
|
+
|
7
|
+
def error_message_hash
|
8
|
+
if component.type == 'multiupload'
|
9
|
+
{
|
10
|
+
control: page_answers.send(component.id)[component.id].last['original_filename'],
|
11
|
+
schema_key.to_sym => component.validation[schema_key]
|
12
|
+
}
|
13
|
+
else
|
14
|
+
super
|
15
|
+
end
|
16
|
+
end
|
6
17
|
end
|
7
18
|
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module MetadataPresenter
|
2
|
+
class MultiuploadValidator < BaseValidator
|
3
|
+
def invalid_answer?
|
4
|
+
user_answer.errors.any? { |error| error.attribute.to_s == error_name }
|
5
|
+
end
|
6
|
+
|
7
|
+
def user_answer
|
8
|
+
page_answers.uploaded_files.find do |uploaded_file|
|
9
|
+
component.id == uploaded_file.component.id
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def error_message_hash
|
14
|
+
{
|
15
|
+
control: page_answers.send(component.id)[component.id].last['original_filename'],
|
16
|
+
schema_key.to_sym => component.validation[schema_key]
|
17
|
+
}
|
18
|
+
end
|
19
|
+
|
20
|
+
def error_name
|
21
|
+
'invalid.multiupload'
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -21,12 +21,21 @@ module MetadataPresenter
|
|
21
21
|
def validators
|
22
22
|
components.map { |component|
|
23
23
|
component_validations(component).map do |key|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
24
|
+
if key == 'max_files'
|
25
|
+
'MetadataPresenter::MaxFilesValidator'.constantize.new(
|
26
|
+
**{
|
27
|
+
page_answers:,
|
28
|
+
component:
|
29
|
+
}.merge(autocomplete_param(key))
|
30
|
+
)
|
31
|
+
else
|
32
|
+
"MetadataPresenter::#{key.classify}Validator".constantize.new(
|
33
|
+
**{
|
34
|
+
page_answers:,
|
35
|
+
component:
|
36
|
+
}.merge(autocomplete_param(key))
|
37
|
+
)
|
38
|
+
end
|
30
39
|
end
|
31
40
|
}.compact.flatten
|
32
41
|
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
<legend class="govuk-heading-xl"><%= input_title %></legend>
|
2
|
+
|
3
|
+
<% if answered?(component.id) && @page_answers.send(component.id)[component.id].compact.count.positive? %>
|
4
|
+
<label id="uploaded-file-summary-list-label"><p class="govuk-heading-s"><%= uploads_count %></p></label>
|
5
|
+
|
6
|
+
<dl id="uploaded-file-summary-list" class="fb-block fb-block-answers govuk-summary-list" aria-labelled-by="uploaded-file-summary-list-label">
|
7
|
+
<% files_to_render.each do |previous_file| %>
|
8
|
+
<div class="govuk-summary-list__row">
|
9
|
+
<dt class="govuk-summary-list__value">
|
10
|
+
<%= previous_file['original_filename'] %>
|
11
|
+
</dt>
|
12
|
+
<dd class="govuk-summary-list__actions">
|
13
|
+
<%= link_to "#{t('presenter.questions.multiupload.remove_file')}<span class=\"govuk-visually-hidden\">#{previous_file['original_filename']}</span>".html_safe, remove_multifile_path(component.id, previous_file['uuid']), class: 'govuk-link' %>
|
14
|
+
</dd>
|
15
|
+
</div>
|
16
|
+
<% end %>
|
17
|
+
</dl>
|
18
|
+
<% end %>
|
19
|
+
|
20
|
+
<% if uploads_remaining.positive? || editable? %>
|
21
|
+
<div data-multiupload-element="upload-another-file" <%= answered?(component.id) && @page_answers.send(component.id)[component.id].compact.count.positive? ? 'hidden' : '' %>>
|
22
|
+
<%= f.govuk_file_field component.id.to_sym,
|
23
|
+
hint: {
|
24
|
+
data: { "fb-default-text" => default_text('option_hint') },
|
25
|
+
text: component.hint.present? ? component.hint : ''
|
26
|
+
},
|
27
|
+
accept: component.validation['accept'],
|
28
|
+
disabled: editable?,
|
29
|
+
label: -> do %>
|
30
|
+
<% if answered?(component.id) && @page_answers.send(component.id)[component.id].compact.count.positive? && !editable? %>
|
31
|
+
<h3 class="govuk-heading-s govuk-!-margin-top-8"><%= t('presenter.questions.multiupload.add_another') %></h3>
|
32
|
+
<% else %>
|
33
|
+
<h3 class="govuk-visually-hidden"><%= t('presenter.questions.multiupload.add_another') %></h3>
|
34
|
+
<% end %>
|
35
|
+
<% end %>
|
36
|
+
</div>
|
37
|
+
<% end %>
|
38
|
+
|
39
|
+
<% if editable? %>
|
40
|
+
<% if editor_preview? && answered?(component.id) %>
|
41
|
+
<p class="govuk-!-margin-bottom-8"><%= t('presenter.questions.multiupload.none') %></p>
|
42
|
+
<% else %>
|
43
|
+
<p class="govuk-!-margin-bottom-8"><%= component.validation['max_files'].to_i > 1 ? t('presenter.questions.multiupload.plural', num: component.validation['max_files']) : t('presenter.questions.multiupload.single_upload') %></p>
|
44
|
+
<% end %>
|
45
|
+
<% else %>
|
46
|
+
<p class="govuk-!-margin-bottom-8"><%= multiupload_files_remaining %></p>
|
47
|
+
<% end %>
|
48
|
+
|
49
|
+
<% if answered?(component.id) && uploads_remaining.positive? && @page_answers.send(component.id)[component.id].compact.count.positive? %>
|
50
|
+
<button class="govuk-button govuk-!-margin-bottom-8 govuk-button--secondary" data-multiupload-element="add-another-file"><%= t('presenter.questions.multiupload.add_another') %></button>
|
51
|
+
<% end %>
|
@@ -37,7 +37,9 @@
|
|
37
37
|
|
38
38
|
<div class="govuk-grid-row">
|
39
39
|
<div class="govuk-grid-column-two-thirds">
|
40
|
+
<% unless @page.body == default_text('body') %>
|
40
41
|
<%= render 'metadata_presenter/attribute/body' %>
|
42
|
+
<% end %>
|
41
43
|
|
42
44
|
<%= render partial: 'metadata_presenter/component/components',
|
43
45
|
locals: {
|
@@ -12,7 +12,10 @@
|
|
12
12
|
</h1>
|
13
13
|
|
14
14
|
<%= render 'metadata_presenter/attribute/lede' %>
|
15
|
-
|
15
|
+
|
16
|
+
<% unless @page.body == default_text('body') %>
|
17
|
+
<%= render 'metadata_presenter/attribute/body' %>
|
18
|
+
<% end %>
|
16
19
|
|
17
20
|
<%= form_for @page_answers, as: :answers, url: @page.url, method: :post do |f| %>
|
18
21
|
|
@@ -21,7 +21,7 @@ Rails.application.config.supported_components =
|
|
21
21
|
content: %w(content)
|
22
22
|
},
|
23
23
|
singlequestion: {
|
24
|
-
input: %w(text textarea number date radios checkboxes email upload autocomplete),
|
24
|
+
input: %w(text textarea number date radios checkboxes email upload multiupload autocomplete),
|
25
25
|
content: %w()
|
26
26
|
}
|
27
27
|
})
|
data/config/locales/en.yml
CHANGED
@@ -41,6 +41,18 @@ en:
|
|
41
41
|
payment_enabled: You still need to pay
|
42
42
|
continue_to_pay_button: Continue to pay
|
43
43
|
application_complete: 'Application complete'
|
44
|
+
questions:
|
45
|
+
multiupload:
|
46
|
+
remove_file: 'Delete'
|
47
|
+
add_another: 'Add another file'
|
48
|
+
single_upload: 'Maximum file size 7MB'
|
49
|
+
answered_singular: 'You can add 1 more file (maximum 7MB per file)'
|
50
|
+
answered_plural: 'You can add %{num} more files (maximum 7MB per file)'
|
51
|
+
answered_count_singular: 'You have added 1 file'
|
52
|
+
answered_count_plural: 'You have added %{num} files'
|
53
|
+
singular: 'You can add 1 more file (maximum 7MB)'
|
54
|
+
plural: 'You can add up to %{num} files (maximum 7MB per file)'
|
55
|
+
none: "You can't add any more files. Remove a file if you need to add a different one."
|
44
56
|
save_and_return:
|
45
57
|
save: 'Save for later'
|
46
58
|
show:
|
data/config/routes.rb
CHANGED
@@ -7,6 +7,7 @@ MetadataPresenter::Engine.routes.draw do
|
|
7
7
|
# We are not adding rails ujs to the editor app so we need to make it
|
8
8
|
# as get verb.
|
9
9
|
get '/reserved/file/:component_id', to: 'file#destroy', as: :remove_file
|
10
|
+
get '/reserved/file/:component_id/:file_uuid', to: 'file#remove_multifile', as: :remove_multifile
|
10
11
|
|
11
12
|
get 'session/expired', to: 'session#expired'
|
12
13
|
get 'session/complete', to: 'session#complete'
|
@@ -0,0 +1,30 @@
|
|
1
|
+
{
|
2
|
+
"_id": "component.multiupload",
|
3
|
+
"_type": "multiupload",
|
4
|
+
"errors": {},
|
5
|
+
"legend": "Question",
|
6
|
+
"hint": "",
|
7
|
+
"name": "component-name",
|
8
|
+
"validation": {
|
9
|
+
"required": true,
|
10
|
+
"max_size": "7340032",
|
11
|
+
"virus_scan": true,
|
12
|
+
"max_files": "1",
|
13
|
+
"multiupload": true,
|
14
|
+
"accept": [
|
15
|
+
"text/csv",
|
16
|
+
"text/plain",
|
17
|
+
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
18
|
+
"application/msword",
|
19
|
+
"application/vnd.oasis.opendocument.spreadsheet",
|
20
|
+
"application/vnd.oasis.opendocument.text",
|
21
|
+
"application/pdf",
|
22
|
+
"application/rtf",
|
23
|
+
"application/csv",
|
24
|
+
"image/jpeg",
|
25
|
+
"image/png",
|
26
|
+
"application/vnd.ms-excel",
|
27
|
+
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
|
28
|
+
]
|
29
|
+
}
|
30
|
+
}
|
@@ -0,0 +1,6 @@
|
|
1
|
+
{
|
2
|
+
"_id": "error.multiupload",
|
3
|
+
"_type": "string.error",
|
4
|
+
"description": "Cannot upload files with duplicate filenames",
|
5
|
+
"value": "The selected file cannot have the same name as a file you have already selected. Please check you aren't uploading the same file again."
|
6
|
+
}
|
@@ -0,0 +1 @@
|
|
1
|
+
{ "max_files": "10" }
|
data/fixtures/version.json
CHANGED
@@ -69,6 +69,12 @@
|
|
69
69
|
}
|
70
70
|
},
|
71
71
|
"2ef7d11e-0307-49e9-9fe2-345dc528dd66": {
|
72
|
+
"_type": "flow.page",
|
73
|
+
"next": {
|
74
|
+
"default": "2ef7d11e-0307-49e9-9fe2-345dc528dd67"
|
75
|
+
}
|
76
|
+
},
|
77
|
+
"2ef7d11e-0307-49e9-9fe2-345dc528dd67": {
|
72
78
|
"_type": "flow.page",
|
73
79
|
"next": {
|
74
80
|
"default": "c7755991-436b-4495-afa6-803db58cefbc"
|
@@ -574,6 +580,73 @@
|
|
574
580
|
}
|
575
581
|
]
|
576
582
|
},
|
583
|
+
{
|
584
|
+
"_id": "page.dog-picture-2",
|
585
|
+
"url": "dog-picture-2",
|
586
|
+
"_type": "page.singlequestion",
|
587
|
+
"_uuid": "2ef7d11e-0307-49e9-9fe2-345dc528dd67",
|
588
|
+
"heading": "Multiupload",
|
589
|
+
"components": [
|
590
|
+
{
|
591
|
+
"_id": "dog-picture_upload_2",
|
592
|
+
"name": "dog-picture_upload_2",
|
593
|
+
"_type": "multiupload",
|
594
|
+
"_uuid": "f056a76e-ec3f-47ae-b625-1bba92220ad2",
|
595
|
+
"hint": "",
|
596
|
+
"legend": "Upload your best dog photos",
|
597
|
+
"validation": {
|
598
|
+
"required": true,
|
599
|
+
"max_files": "3",
|
600
|
+
"multiupload": true,
|
601
|
+
"accept": [
|
602
|
+
"audio/*",
|
603
|
+
"image/bmp",
|
604
|
+
"text/csv",
|
605
|
+
"application/vnd.ms-excel",
|
606
|
+
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
607
|
+
"application/vnd.openxmlformats-officedocument.spreadsheetml.template",
|
608
|
+
"application/vnd.ms-excel.sheet.macroEnabled.12",
|
609
|
+
"application/vnd.ms-excel.template.macroEnabled.12",
|
610
|
+
"application/vnd.ms-excel.addin.macroEnabled.12",
|
611
|
+
"application/vnd.ms-excel.sheet.binary.macroEnabled.12",
|
612
|
+
"image/gif",
|
613
|
+
"image/*",
|
614
|
+
"application/x-iwork-pages-sffpages",
|
615
|
+
"image/jpeg",
|
616
|
+
"application/pdf",
|
617
|
+
"text/plain",
|
618
|
+
"image/png",
|
619
|
+
"application/vnd.ms-powerpoint",
|
620
|
+
"application/vnd.openxmlformats-officedocument.presentationml.presentation",
|
621
|
+
"application/vnd.openxmlformats-officedocument.presentationml.template",
|
622
|
+
"application/vnd.openxmlformats-officedocument.presentationml.slideshow",
|
623
|
+
"application/vnd.ms-powerpoint.addin.macroEnabled.12",
|
624
|
+
"application/vnd.ms-powerpoint.presentation.macroEnabled.12",
|
625
|
+
"application/vnd.ms-powerpoint.template.macroEnabled.12",
|
626
|
+
"application/vnd.ms-powerpoint.slideshow.macroEnabled.12",
|
627
|
+
"text/rtf",
|
628
|
+
"excel",
|
629
|
+
"csv",
|
630
|
+
"image/svg+xml",
|
631
|
+
"pdf",
|
632
|
+
"word",
|
633
|
+
"rtf",
|
634
|
+
"plaintext",
|
635
|
+
"image/tiff",
|
636
|
+
"video/*",
|
637
|
+
"application/msword",
|
638
|
+
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
639
|
+
"application/vnd.openxmlformats-officedocument.wordprocessingml.template",
|
640
|
+
"application/vnd.ms-word.document.macroEnabled.12",
|
641
|
+
"application/vnd.ms-word.template.macroEnabled.12",
|
642
|
+
"application/csv"
|
643
|
+
],
|
644
|
+
"max_size": 7340032,
|
645
|
+
"virus_scan": true
|
646
|
+
}
|
647
|
+
}
|
648
|
+
]
|
649
|
+
},
|
577
650
|
{
|
578
651
|
"_id": "page.countries",
|
579
652
|
"url": "countries",
|
@@ -17,6 +17,35 @@
|
|
17
17
|
"type": "string",
|
18
18
|
"content": true,
|
19
19
|
"multiline": true
|
20
|
+
},
|
21
|
+
"conditionals": {
|
22
|
+
"$ref": "#/definitions/conditionals"
|
23
|
+
}
|
24
|
+
},
|
25
|
+
"definitions": {
|
26
|
+
"conditionals": {
|
27
|
+
"type": "array",
|
28
|
+
"items": {
|
29
|
+
"type": "object",
|
30
|
+
"properties": {
|
31
|
+
"_uuid": {
|
32
|
+
"type": "string",
|
33
|
+
"title": "Unique identifier of the conditional",
|
34
|
+
"description": "Used internally in the editor and the runner"
|
35
|
+
},
|
36
|
+
"_type": {
|
37
|
+
"type": "string",
|
38
|
+
"enum": [
|
39
|
+
"if",
|
40
|
+
"and",
|
41
|
+
"or"
|
42
|
+
]
|
43
|
+
},
|
44
|
+
"expressions": {
|
45
|
+
"$ref": "definition.expressions"
|
46
|
+
}
|
47
|
+
}
|
48
|
+
}
|
20
49
|
}
|
21
50
|
},
|
22
51
|
"allOf": [
|
@@ -0,0 +1,42 @@
|
|
1
|
+
{
|
2
|
+
"$id": "http://gov.uk/schema/v1.0.0/multiupload",
|
3
|
+
"_name": "component.multiupload",
|
4
|
+
"title": "Multiupload",
|
5
|
+
"description": "Let users select and upload one or more files",
|
6
|
+
"type": "object",
|
7
|
+
"properties": {
|
8
|
+
"_type": {
|
9
|
+
"const": "multiupload"
|
10
|
+
},
|
11
|
+
"max_files": {
|
12
|
+
"title": "Maximum number of files",
|
13
|
+
"description": "Maximum number of files a user can upload",
|
14
|
+
"type": "number"
|
15
|
+
},
|
16
|
+
"min_files": {
|
17
|
+
"title": "Minimum number of files",
|
18
|
+
"description": "Minimum number of files a user can upload - 1 if required, 0 if not required",
|
19
|
+
"type": "number"
|
20
|
+
}
|
21
|
+
},
|
22
|
+
"allOf": [
|
23
|
+
{
|
24
|
+
"$ref": "definition.field"
|
25
|
+
},
|
26
|
+
{
|
27
|
+
"$ref": "definition.width_class.input"
|
28
|
+
},
|
29
|
+
{
|
30
|
+
"$ref": "validations#/definitions/errors_accept"
|
31
|
+
},
|
32
|
+
{
|
33
|
+
"$ref": "validations#/definitions/errors_max_size"
|
34
|
+
},
|
35
|
+
{
|
36
|
+
"$ref": "validations#/definitions/errors_virus_scan"
|
37
|
+
},
|
38
|
+
{
|
39
|
+
"$ref": "validations#/definitions/file_bundle"
|
40
|
+
}
|
41
|
+
]
|
42
|
+
}
|
@@ -0,0 +1,36 @@
|
|
1
|
+
{
|
2
|
+
"$id": "http://gov.uk/schema/v1.0.0/definition/expression_item",
|
3
|
+
"_name": "definition.expression_item",
|
4
|
+
"title": "Expression item",
|
5
|
+
"description": "Component that provides an expression item",
|
6
|
+
"type": "object",
|
7
|
+
"properties": {
|
8
|
+
"operator": {
|
9
|
+
"type": "string",
|
10
|
+
"enum": [
|
11
|
+
"is",
|
12
|
+
"is_not",
|
13
|
+
"is_answered",
|
14
|
+
"is_not_answered"
|
15
|
+
],
|
16
|
+
"page": {
|
17
|
+
"type": "string"
|
18
|
+
},
|
19
|
+
"component": {
|
20
|
+
"type": "string"
|
21
|
+
},
|
22
|
+
"field": {
|
23
|
+
"type": "string"
|
24
|
+
}
|
25
|
+
}
|
26
|
+
},
|
27
|
+
"required": [
|
28
|
+
"operator",
|
29
|
+
"page",
|
30
|
+
"component",
|
31
|
+
"field"
|
32
|
+
],
|
33
|
+
"category": [
|
34
|
+
"expression_item"
|
35
|
+
]
|
36
|
+
}
|
@@ -0,0 +1,15 @@
|
|
1
|
+
{
|
2
|
+
"$id": "http://gov.uk/schema/v1.0.0/definition/expressions",
|
3
|
+
"_name": "definition.expressions",
|
4
|
+
"title": "Expressions",
|
5
|
+
"type": "array",
|
6
|
+
"items": {
|
7
|
+
"$ref": "definition.expression_item"
|
8
|
+
},
|
9
|
+
"required": [
|
10
|
+
"items"
|
11
|
+
],
|
12
|
+
"category": [
|
13
|
+
"expression"
|
14
|
+
]
|
15
|
+
}
|
data/schemas/flow/branch.json
CHANGED
@@ -48,33 +48,7 @@
|
|
48
48
|
"type": "string"
|
49
49
|
},
|
50
50
|
"expressions": {
|
51
|
-
"$ref": "
|
52
|
-
}
|
53
|
-
}
|
54
|
-
}
|
55
|
-
},
|
56
|
-
"expressions": {
|
57
|
-
"type": "array",
|
58
|
-
"items": {
|
59
|
-
"type": "object",
|
60
|
-
"properties": {
|
61
|
-
"operator": {
|
62
|
-
"type": "string",
|
63
|
-
"enum": [
|
64
|
-
"is",
|
65
|
-
"is_not",
|
66
|
-
"is_answered",
|
67
|
-
"is_not_answered"
|
68
|
-
],
|
69
|
-
"page": {
|
70
|
-
"type": "string"
|
71
|
-
},
|
72
|
-
"component": {
|
73
|
-
"type": "string"
|
74
|
-
},
|
75
|
-
"field": {
|
76
|
-
"type": "string"
|
77
|
-
}
|
51
|
+
"$ref": "definition.expressions"
|
78
52
|
}
|
79
53
|
}
|
80
54
|
}
|
@@ -118,6 +118,20 @@
|
|
118
118
|
}
|
119
119
|
]
|
120
120
|
},
|
121
|
+
"max_files": {
|
122
|
+
"title": "Maximum files",
|
123
|
+
"description": "The maximum number of fiels a user can upload",
|
124
|
+
"type": "number",
|
125
|
+
"minimum": 0
|
126
|
+
},
|
127
|
+
"errors_max_files": {
|
128
|
+
"title": "Error messages for 'Maximum files'",
|
129
|
+
"allOf": [
|
130
|
+
{
|
131
|
+
"$ref": "#/definitions/error_strings"
|
132
|
+
}
|
133
|
+
]
|
134
|
+
},
|
121
135
|
"pattern": {
|
122
136
|
"title": "Pattern to match string against",
|
123
137
|
"description": "A regular expression for validating users’ answers",
|
@@ -450,6 +464,9 @@
|
|
450
464
|
},
|
451
465
|
"exclusive_minimum": {
|
452
466
|
"$ref": "#/definitions/exclusive_minimum"
|
467
|
+
},
|
468
|
+
"max_files": {
|
469
|
+
"$ref": "#/definitions/max_files"
|
453
470
|
}
|
454
471
|
}
|
455
472
|
},
|
@@ -469,6 +486,9 @@
|
|
469
486
|
},
|
470
487
|
"exclusive_minimum": {
|
471
488
|
"$ref": "#/definitions/errors_exclusive_minimum"
|
489
|
+
},
|
490
|
+
"max_files": {
|
491
|
+
"$ref": "#/definitions/max_files"
|
472
492
|
}
|
473
493
|
}
|
474
494
|
}
|
@@ -509,6 +529,24 @@
|
|
509
529
|
}
|
510
530
|
}
|
511
531
|
}
|
532
|
+
},
|
533
|
+
"file_bundle": {
|
534
|
+
"properties": {
|
535
|
+
"validation": {
|
536
|
+
"properties": {
|
537
|
+
"max_files": {
|
538
|
+
"$ref": "#/definitions/max_files"
|
539
|
+
}
|
540
|
+
}
|
541
|
+
},
|
542
|
+
"errors": {
|
543
|
+
"properties": {
|
544
|
+
"max_files": {
|
545
|
+
"$ref": "#/definitions/max_files"
|
546
|
+
}
|
547
|
+
}
|
548
|
+
}
|
549
|
+
}
|
512
550
|
}
|
513
551
|
},
|
514
552
|
"category": [
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: metadata_presenter
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.0
|
4
|
+
version: 3.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- MoJ Forms
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-07-
|
11
|
+
date: 2023-07-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: govuk_design_system_formbuilder
|
@@ -178,6 +178,20 @@ dependencies:
|
|
178
178
|
- - ">="
|
179
179
|
- !ruby/object:Gem::Version
|
180
180
|
version: '0'
|
181
|
+
- !ruby/object:Gem::Dependency
|
182
|
+
name: hashie
|
183
|
+
requirement: !ruby/object:Gem::Requirement
|
184
|
+
requirements:
|
185
|
+
- - ">="
|
186
|
+
- !ruby/object:Gem::Version
|
187
|
+
version: '0'
|
188
|
+
type: :development
|
189
|
+
prerelease: false
|
190
|
+
version_requirements: !ruby/object:Gem::Requirement
|
191
|
+
requirements:
|
192
|
+
- - ">="
|
193
|
+
- !ruby/object:Gem::Version
|
194
|
+
version: '0'
|
181
195
|
- !ruby/object:Gem::Dependency
|
182
196
|
name: rspec-rails
|
183
197
|
requirement: !ruby/object:Gem::Requirement
|
@@ -332,6 +346,7 @@ files:
|
|
332
346
|
- app/models/metadata_presenter/meta.rb
|
333
347
|
- app/models/metadata_presenter/meta_item.rb
|
334
348
|
- app/models/metadata_presenter/metadata.rb
|
349
|
+
- app/models/metadata_presenter/multi_upload_answer.rb
|
335
350
|
- app/models/metadata_presenter/next_page.rb
|
336
351
|
- app/models/metadata_presenter/offline_upload_adapter.rb
|
337
352
|
- app/models/metadata_presenter/page.rb
|
@@ -362,6 +377,7 @@ files:
|
|
362
377
|
- app/validators/metadata_presenter/date_before_validator.rb
|
363
378
|
- app/validators/metadata_presenter/date_validator.rb
|
364
379
|
- app/validators/metadata_presenter/email_validator.rb
|
380
|
+
- app/validators/metadata_presenter/max_files_validator.rb
|
365
381
|
- app/validators/metadata_presenter/max_length_validator.rb
|
366
382
|
- app/validators/metadata_presenter/max_size_validator.rb
|
367
383
|
- app/validators/metadata_presenter/max_word_validator.rb
|
@@ -369,6 +385,7 @@ files:
|
|
369
385
|
- app/validators/metadata_presenter/min_length_validator.rb
|
370
386
|
- app/validators/metadata_presenter/min_word_validator.rb
|
371
387
|
- app/validators/metadata_presenter/minimum_validator.rb
|
388
|
+
- app/validators/metadata_presenter/multiupload_validator.rb
|
372
389
|
- app/validators/metadata_presenter/number_validator.rb
|
373
390
|
- app/validators/metadata_presenter/required_validator.rb
|
374
391
|
- app/validators/metadata_presenter/upload_validator.rb
|
@@ -400,6 +417,7 @@ files:
|
|
400
417
|
- app/views/metadata_presenter/component/_content.html.erb
|
401
418
|
- app/views/metadata_presenter/component/_date.html.erb
|
402
419
|
- app/views/metadata_presenter/component/_email.html.erb
|
420
|
+
- app/views/metadata_presenter/component/_multiupload.html.erb
|
403
421
|
- app/views/metadata_presenter/component/_number.html.erb
|
404
422
|
- app/views/metadata_presenter/component/_radios.html.erb
|
405
423
|
- app/views/metadata_presenter/component/_text.html.erb
|
@@ -445,6 +463,7 @@ files:
|
|
445
463
|
- default_metadata/component/content.json
|
446
464
|
- default_metadata/component/date.json
|
447
465
|
- default_metadata/component/email.json
|
466
|
+
- default_metadata/component/multiupload.json
|
448
467
|
- default_metadata/component/number.json
|
449
468
|
- default_metadata/component/radios.json
|
450
469
|
- default_metadata/component/text.json
|
@@ -473,6 +492,7 @@ files:
|
|
473
492
|
- default_metadata/string/error.date_after.json
|
474
493
|
- default_metadata/string/error.date_before.json
|
475
494
|
- default_metadata/string/error.email.json
|
495
|
+
- default_metadata/string/error.max_files.json
|
476
496
|
- default_metadata/string/error.max_length.json
|
477
497
|
- default_metadata/string/error.max_size.json
|
478
498
|
- default_metadata/string/error.max_word.json
|
@@ -480,11 +500,13 @@ files:
|
|
480
500
|
- default_metadata/string/error.min_length.json
|
481
501
|
- default_metadata/string/error.min_word.json
|
482
502
|
- default_metadata/string/error.minimum.json
|
503
|
+
- default_metadata/string/error.multiupload.json
|
483
504
|
- default_metadata/string/error.number.json
|
484
505
|
- default_metadata/string/error.required.json
|
485
506
|
- default_metadata/string/error.virus_scan.json
|
486
507
|
- default_metadata/validations/date_after.json
|
487
508
|
- default_metadata/validations/date_before.json
|
509
|
+
- default_metadata/validations/max_files.json
|
488
510
|
- default_metadata/validations/max_length.json
|
489
511
|
- default_metadata/validations/max_word.json
|
490
512
|
- default_metadata/validations/maximum.json
|
@@ -531,6 +553,7 @@ files:
|
|
531
553
|
- schemas/component/content.json
|
532
554
|
- schemas/component/date.json
|
533
555
|
- schemas/component/email.json
|
556
|
+
- schemas/component/multiupload.json
|
534
557
|
- schemas/component/number.json
|
535
558
|
- schemas/component/radios.json
|
536
559
|
- schemas/component/text.json
|
@@ -557,6 +580,8 @@ files:
|
|
557
580
|
- schemas/definition/conditions.exactly.json
|
558
581
|
- schemas/definition/control.json
|
559
582
|
- schemas/definition/data.json
|
583
|
+
- schemas/definition/expression_item.json
|
584
|
+
- schemas/definition/expressions.json
|
560
585
|
- schemas/definition/field.json
|
561
586
|
- schemas/definition/fieldset.json
|
562
587
|
- schemas/definition/grouping.json
|