diff --git a/app/assets/javascripts/admin/addon/components/form-template/form.js b/app/assets/javascripts/admin/addon/components/form-template/form.js
index 09a2cfc700b..f6655a7de4a 100644
--- a/app/assets/javascripts/admin/addon/components/form-template/form.js
+++ b/app/assets/javascripts/admin/addon/components/form-template/form.js
@@ -32,11 +32,10 @@ export default class FormTemplateForm extends Component {
type: "dropdown",
icon: "chevron-circle-down",
},
- // TODO(@keegan): add support for uploads
- // {
- // type: "upload",
- // icon: "cloud-upload-alt",
- // },
+ {
+ type: "upload",
+ icon: "cloud-upload-alt",
+ },
{
type: "multiselect",
icon: "bullseye",
diff --git a/app/assets/javascripts/admin/addon/lib/template-form-fields.js b/app/assets/javascripts/admin/addon/lib/template-form-fields.js
index 6a27b5c1d05..80bf3c42050 100644
--- a/app/assets/javascripts/admin/addon/lib/template-form-fields.js
+++ b/app/assets/javascripts/admin/addon/lib/template-form-fields.js
@@ -51,7 +51,7 @@ export const templateFormFields = [
type: "upload",
structure: `- type: upload
attributes:
- file_types: "jpg, png, gif"
+ file_types: ".jpg, .png, .gif"
allow_multiple: false
label: "${I18n.t("admin.form_templates.field_placeholders.label")}"
validations:
diff --git a/app/assets/javascripts/discourse/app/components/form-template-field/upload.hbs b/app/assets/javascripts/discourse/app/components/form-template-field/upload.hbs
index 5d8cc2698d8..4ce85aba49f 100644
--- a/app/assets/javascripts/discourse/app/components/form-template-field/upload.hbs
+++ b/app/assets/javascripts/discourse/app/components/form-template-field/upload.hbs
@@ -7,10 +7,35 @@
{{/if}}
{{/if}}
-
+
+
+
+ {{#if this.uploadedFiles}}
+
+ {{/if}}
\ No newline at end of file
diff --git a/app/assets/javascripts/discourse/app/components/form-template-field/upload.js b/app/assets/javascripts/discourse/app/components/form-template-field/upload.js
new file mode 100644
index 00000000000..91c609676ca
--- /dev/null
+++ b/app/assets/javascripts/discourse/app/components/form-template-field/upload.js
@@ -0,0 +1,69 @@
+import Component from "@ember/component";
+import UppyUploadMixin from "discourse/mixins/uppy-upload";
+import { computed } from "@ember/object";
+import { tracked } from "@glimmer/tracking";
+import { dasherize } from "@ember/string";
+import { isAudio, isImage, isVideo } from "discourse/lib/uploads";
+
+export default class FormTemplateFieldUpload extends Component.extend(
+ UppyUploadMixin
+) {
+ @tracked uploadValue;
+ @tracked uploadComplete = false;
+ @tracked uploadedFiles = [];
+ @tracked disabled = this.uploading;
+ @tracked fileUploadElementId = this.attributes?.label
+ ? `${dasherize(this.attributes.label)}-uploader`
+ : `${this.elementId}-uploader`;
+ @tracked fileInputSelector = `#${this.fileUploadElementId}`;
+ @tracked id = this.fileUploadElementId;
+
+ @computed("uploading", "uploadValue")
+ get uploadStatus() {
+ if (!this.uploading && !this.uploadValue) {
+ return "upload";
+ }
+
+ if (!this.uploading && this.uploadValue) {
+ this.uploadComplete = true;
+ return "upload";
+ }
+
+ return "uploading";
+ }
+
+ uploadDone(upload) {
+ // If reuploading, clear the existing file
+ if (this.uploadComplete) {
+ this.uploadedFiles = [];
+ this.uploadValue = "";
+ }
+
+ const uploadMarkdown = this.buildMarkdown(upload);
+ this.uploadedFiles.pushObject(upload);
+
+ if (this.uploadValue && this.allowMultipleFiles) {
+ // multiple file upload
+ this.uploadValue = `${this.uploadValue}\n${uploadMarkdown}`;
+ } else {
+ // single file upload
+ this.uploadValue = uploadMarkdown;
+ }
+ }
+
+ buildMarkdown(upload) {
+ if (isImage(upload.url)) {
+ return `![${upload.file_name}|${upload.width}x${upload.height}](${upload.short_url})`;
+ }
+
+ if (isAudio(upload.url)) {
+ return `![${upload.file_name}|audio](${upload.short_url})`;
+ }
+
+ if (isVideo(upload.url)) {
+ return `![${upload.file_name}|video](${upload.short_url})`;
+ }
+
+ return `[${upload.file_name}|attachment](${upload.short_url}) (${upload.human_filesize})`;
+ }
+}
diff --git a/app/assets/javascripts/discourse/app/components/pick-files-button.js b/app/assets/javascripts/discourse/app/components/pick-files-button.js
index 991ffe1592f..2ac80e2ba28 100644
--- a/app/assets/javascripts/discourse/app/components/pick-files-button.js
+++ b/app/assets/javascripts/discourse/app/components/pick-files-button.js
@@ -91,7 +91,6 @@ export default Component.extend({
this.dialog.alert(message);
return;
}
- this.onFilesPicked(files);
},
_haveAcceptedTypes(files) {
diff --git a/app/assets/stylesheets/common/components/form-template-field.scss b/app/assets/stylesheets/common/components/form-template-field.scss
index 2d7c1019da4..37350e96e68 100644
--- a/app/assets/stylesheets/common/components/form-template-field.scss
+++ b/app/assets/stylesheets/common/components/form-template-field.scss
@@ -20,4 +20,35 @@
margin-left: 0.5em;
font-size: var(--font-down-4);
}
+
+ &__uploaded-files {
+ list-style: none;
+ margin-left: 0;
+
+ li {
+ padding: 0.5rem;
+ margin-block: 0.25rem;
+ border: 1px solid var(--primary-low-mid);
+ background: var(--primary-low);
+ border-radius: var(--d-border-radius);
+ display: flex;
+ align-items: center;
+
+ a {
+ @include ellipsis;
+ width: 70%;
+ }
+
+ span {
+ color: var(--primary-high);
+ margin-left: auto;
+ font-size: var(--font-down-1);
+ }
+ }
+
+ .d-icon {
+ color: var(--tertiary);
+ margin-right: 0.5rem;
+ }
+ }
}
diff --git a/spec/system/admin_customize_form_templates_spec.rb b/spec/system/admin_customize_form_templates_spec.rb
index fb19dc301cd..847d7accefd 100644
--- a/spec/system/admin_customize_form_templates_spec.rb
+++ b/spec/system/admin_customize_form_templates_spec.rb
@@ -119,8 +119,7 @@ describe "Admin Customize Form Templates", type: :system do
expect(form_template_page).to have_input_field("textarea")
expect(form_template_page).to have_input_field("checkbox")
expect(form_template_page).to have_input_field("dropdown")
- # TODO(@keegan): Add this back when upload functionality is added
- # expect(form_template_page).to have_input_field("upload")
+ expect(form_template_page).to have_input_field("upload")
expect(form_template_page).to have_input_field("multi-select")
end
@@ -176,13 +175,12 @@ describe "Admin Customize Form Templates", type: :system do
)
end
- # TODO(@keegan): Unskip this test when Upload functionality is added
- xit "should allow quick insertion of upload field" do
+ it "should allow quick insertion of upload field" do
quick_insertion_test(
"upload",
'- type: upload
attributes:
- file_types: "jpg, png, gif"
+ file_types: ".jpg, .png, .gif"
allow_multiple: false
label: "Enter label here"
validations:
diff --git a/spec/system/composer/category_templates_spec.rb b/spec/system/composer/category_templates_spec.rb
index f930a483646..d8222e8df35 100644
--- a/spec/system/composer/category_templates_spec.rb
+++ b/spec/system/composer/category_templates_spec.rb
@@ -24,6 +24,34 @@ describe "Composer Form Templates", type: :system do
fab!(:form_template_4) do
Fabricate(:form_template, name: "Biography", template: "- type: textarea")
end
+ fab!(:form_template_5) do
+ Fabricate(
+ :form_template,
+ name: "Medication",
+ template:
+ %Q(
+ - type: input
+ attributes:
+ label: "What is your name?"
+ placeholder: "John Smith"
+ validations:
+ required: false
+ - type: upload
+ attributes:
+ file_types: ".jpg, .png"
+ allow_multiple: false
+ label: "Upload your prescription"
+ validations:
+ required: true
+ - type: upload
+ attributes:
+ file_types: ".jpg, .png, .pdf, .mp3, .mp4"
+ allow_multiple: true
+ label: "Any additional docs"
+ validations:
+ required: false"),
+ )
+ end
fab!(:category_with_template_1) do
Fabricate(
:category,
@@ -60,6 +88,15 @@ describe "Composer Form Templates", type: :system do
form_template_ids: [form_template_3.id, form_template_4.id],
)
end
+ fab!(:category_with_upload_template) do
+ Fabricate(
+ :category,
+ name: "Medical",
+ slug: "medical",
+ topic_count: 2,
+ form_template_ids: [form_template_5.id],
+ )
+ end
fab!(:category_no_template) do
Fabricate(:category, name: "Staff", slug: "staff", topic_count: 2, form_template_ids: [])
end
@@ -73,6 +110,7 @@ describe "Composer Form Templates", type: :system do
topic_template: "Testing",
)
end
+
let(:category_page) { PageObjects::Pages::Category.new }
let(:composer) { PageObjects::Components::Composer.new }
let(:form_template_chooser) { PageObjects::Components::SelectKit.new(".form-template-chooser") }
@@ -80,6 +118,7 @@ describe "Composer Form Templates", type: :system do
before do
SiteSetting.experimental_form_templates = true
+ SiteSetting.authorized_extensions = "*"
sign_in user
end
@@ -195,4 +234,61 @@ describe "Composer Form Templates", type: :system do
"Bruce Wayne",
)
end
+
+ it "creates a post with an upload field" do
+ topic_title = "Bruce Wayne's Medication"
+
+ category_page.visit(category_with_upload_template)
+ category_page.new_topic_button.click
+ attach_file "upload-your-prescription-uploader",
+ "#{Rails.root}/spec/fixtures/images/logo.png",
+ make_visible: true
+ composer.fill_title(topic_title)
+ composer.fill_form_template_field("input", "Bruce Wayne")
+ composer.create
+
+ expect(find("#{topic_page.post_by_number_selector(1)} .cooked")).to have_css(
+ "img[alt='logo.png']",
+ )
+ end
+
+ it "doesn't allow uploading an invalid file type" do
+ topic_title = "Bruce Wayne's Medication"
+
+ category_page.visit(category_with_upload_template)
+ category_page.new_topic_button.click
+ attach_file "upload-your-prescription-uploader",
+ "#{Rails.root}/spec/fixtures/images/animated.gif",
+ make_visible: true
+ expect(find("#dialog-holder .dialog-body p", visible: :all)).to have_content(
+ I18n.t("js.pick_files_button.unsupported_file_picked", { types: ".jpg, .png" }),
+ )
+ end
+
+ it "creates a post with multiple uploads" do
+ topic_title = "Peter Parker's Medication"
+
+ category_page.visit(category_with_upload_template)
+ category_page.new_topic_button.click
+ attach_file "upload-your-prescription-uploader",
+ "#{Rails.root}/spec/fixtures/images/logo.png",
+ make_visible: true
+ attach_file "any-additional-docs-uploader",
+ [
+ "#{Rails.root}/spec/fixtures/media/small.mp3",
+ "#{Rails.root}/spec/fixtures/media/small.mp4",
+ "#{Rails.root}/spec/fixtures/pdf/small.pdf",
+ ],
+ make_visible: true
+ composer.fill_title(topic_title)
+ composer.fill_form_template_field("input", "Peter Parker}")
+ composer.create
+
+ expect(find("#{topic_page.post_by_number_selector(1)} .cooked")).to have_css(
+ "img[alt='logo.png']",
+ )
+ expect(find("#{topic_page.post_by_number_selector(1)} .cooked")).to have_css("a.attachment")
+ expect(find("#{topic_page.post_by_number_selector(1)} .cooked")).to have_css("audio")
+ expect(find("#{topic_page.post_by_number_selector(1)} .cooked")).to have_css("video")
+ end
end
diff --git a/spec/system/page_objects/pages/form_template.rb b/spec/system/page_objects/pages/form_template.rb
index 67d4f16f26d..3e86ab80e53 100644
--- a/spec/system/page_objects/pages/form_template.rb
+++ b/spec/system/page_objects/pages/form_template.rb
@@ -62,7 +62,7 @@ module PageObjects
end
def has_input_field?(type)
- find(".form-template-field__#{type}").present?
+ find(".form-template-field__#{type}", visible: :all).present?
end
def has_preview_modal?