mirror of
https://github.com/discourse/discourse.git
synced 2025-05-23 21:04:33 +08:00
DEV: Add support for uploads to form templates (#22232)
This commit is contained in:
@ -32,11 +32,10 @@ export default class FormTemplateForm extends Component {
|
|||||||
type: "dropdown",
|
type: "dropdown",
|
||||||
icon: "chevron-circle-down",
|
icon: "chevron-circle-down",
|
||||||
},
|
},
|
||||||
// TODO(@keegan): add support for uploads
|
{
|
||||||
// {
|
type: "upload",
|
||||||
// type: "upload",
|
icon: "cloud-upload-alt",
|
||||||
// icon: "cloud-upload-alt",
|
},
|
||||||
// },
|
|
||||||
{
|
{
|
||||||
type: "multiselect",
|
type: "multiselect",
|
||||||
icon: "bullseye",
|
icon: "bullseye",
|
||||||
|
@ -51,7 +51,7 @@ export const templateFormFields = [
|
|||||||
type: "upload",
|
type: "upload",
|
||||||
structure: `- type: upload
|
structure: `- type: upload
|
||||||
attributes:
|
attributes:
|
||||||
file_types: "jpg, png, gif"
|
file_types: ".jpg, .png, .gif"
|
||||||
allow_multiple: false
|
allow_multiple: false
|
||||||
label: "${I18n.t("admin.form_templates.field_placeholders.label")}"
|
label: "${I18n.t("admin.form_templates.field_placeholders.label")}"
|
||||||
validations:
|
validations:
|
||||||
|
@ -7,10 +7,35 @@
|
|||||||
{{/if}}
|
{{/if}}
|
||||||
</label>
|
</label>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
<input
|
|
||||||
type="file"
|
<input type="hidden" name={{@attributes.label}} value={{this.uploadValue}} />
|
||||||
accept={{@attributes.file_types}}
|
|
||||||
class="form-template-field__upload"
|
<PickFilesButton
|
||||||
multiple={{@attributes.allow_multiple}}
|
@fileInputClass="form-template-field__upload"
|
||||||
|
@fileInputId={{this.fileUploadElementId}}
|
||||||
|
@allowMultiple={{@attributes.allow_multiple}}
|
||||||
|
@showButton={{true}}
|
||||||
|
@onFilesPicked={{true}}
|
||||||
|
@icon="upload"
|
||||||
|
@label={{this.uploadStatus}}
|
||||||
|
@fileInputDisabled={{this.disabled}}
|
||||||
|
@acceptedFormatsOverride={{@attributes.file_types}}
|
||||||
|
@acceptedFileTypesString={{@attributes.file_types}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
{{#if this.uploadedFiles}}
|
||||||
|
<ul class="form-template-field__uploaded-files">
|
||||||
|
{{#each this.uploadedFiles as |file|}}
|
||||||
|
<li>
|
||||||
|
{{d-icon "file"}}
|
||||||
|
<a
|
||||||
|
href={{file.url}}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>{{file.file_name}}</a>
|
||||||
|
<span>{{file.human_filesize}}</span>
|
||||||
|
</li>
|
||||||
|
{{/each}}
|
||||||
|
</ul>
|
||||||
|
{{/if}}
|
||||||
</div>
|
</div>
|
@ -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 ``;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isAudio(upload.url)) {
|
||||||
|
return ``;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isVideo(upload.url)) {
|
||||||
|
return ``;
|
||||||
|
}
|
||||||
|
|
||||||
|
return `[${upload.file_name}|attachment](${upload.short_url}) (${upload.human_filesize})`;
|
||||||
|
}
|
||||||
|
}
|
@ -91,7 +91,6 @@ export default Component.extend({
|
|||||||
this.dialog.alert(message);
|
this.dialog.alert(message);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.onFilesPicked(files);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_haveAcceptedTypes(files) {
|
_haveAcceptedTypes(files) {
|
||||||
|
@ -20,4 +20,35 @@
|
|||||||
margin-left: 0.5em;
|
margin-left: 0.5em;
|
||||||
font-size: var(--font-down-4);
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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("textarea")
|
||||||
expect(form_template_page).to have_input_field("checkbox")
|
expect(form_template_page).to have_input_field("checkbox")
|
||||||
expect(form_template_page).to have_input_field("dropdown")
|
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")
|
expect(form_template_page).to have_input_field("multi-select")
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -176,13 +175,12 @@ describe "Admin Customize Form Templates", type: :system do
|
|||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
# TODO(@keegan): Unskip this test when Upload functionality is added
|
it "should allow quick insertion of upload field" do
|
||||||
xit "should allow quick insertion of upload field" do
|
|
||||||
quick_insertion_test(
|
quick_insertion_test(
|
||||||
"upload",
|
"upload",
|
||||||
'- type: upload
|
'- type: upload
|
||||||
attributes:
|
attributes:
|
||||||
file_types: "jpg, png, gif"
|
file_types: ".jpg, .png, .gif"
|
||||||
allow_multiple: false
|
allow_multiple: false
|
||||||
label: "Enter label here"
|
label: "Enter label here"
|
||||||
validations:
|
validations:
|
||||||
|
@ -24,6 +24,34 @@ describe "Composer Form Templates", type: :system do
|
|||||||
fab!(:form_template_4) do
|
fab!(:form_template_4) do
|
||||||
Fabricate(:form_template, name: "Biography", template: "- type: textarea")
|
Fabricate(:form_template, name: "Biography", template: "- type: textarea")
|
||||||
end
|
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
|
fab!(:category_with_template_1) do
|
||||||
Fabricate(
|
Fabricate(
|
||||||
:category,
|
:category,
|
||||||
@ -60,6 +88,15 @@ describe "Composer Form Templates", type: :system do
|
|||||||
form_template_ids: [form_template_3.id, form_template_4.id],
|
form_template_ids: [form_template_3.id, form_template_4.id],
|
||||||
)
|
)
|
||||||
end
|
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
|
fab!(:category_no_template) do
|
||||||
Fabricate(:category, name: "Staff", slug: "staff", topic_count: 2, form_template_ids: [])
|
Fabricate(:category, name: "Staff", slug: "staff", topic_count: 2, form_template_ids: [])
|
||||||
end
|
end
|
||||||
@ -73,6 +110,7 @@ describe "Composer Form Templates", type: :system do
|
|||||||
topic_template: "Testing",
|
topic_template: "Testing",
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
let(:category_page) { PageObjects::Pages::Category.new }
|
let(:category_page) { PageObjects::Pages::Category.new }
|
||||||
let(:composer) { PageObjects::Components::Composer.new }
|
let(:composer) { PageObjects::Components::Composer.new }
|
||||||
let(:form_template_chooser) { PageObjects::Components::SelectKit.new(".form-template-chooser") }
|
let(:form_template_chooser) { PageObjects::Components::SelectKit.new(".form-template-chooser") }
|
||||||
@ -80,6 +118,7 @@ describe "Composer Form Templates", type: :system do
|
|||||||
|
|
||||||
before do
|
before do
|
||||||
SiteSetting.experimental_form_templates = true
|
SiteSetting.experimental_form_templates = true
|
||||||
|
SiteSetting.authorized_extensions = "*"
|
||||||
sign_in user
|
sign_in user
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -195,4 +234,61 @@ describe "Composer Form Templates", type: :system do
|
|||||||
"Bruce Wayne",
|
"Bruce Wayne",
|
||||||
)
|
)
|
||||||
end
|
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
|
end
|
||||||
|
@ -62,7 +62,7 @@ module PageObjects
|
|||||||
end
|
end
|
||||||
|
|
||||||
def has_input_field?(type)
|
def has_input_field?(type)
|
||||||
find(".form-template-field__#{type}").present?
|
find(".form-template-field__#{type}", visible: :all).present?
|
||||||
end
|
end
|
||||||
|
|
||||||
def has_preview_modal?
|
def has_preview_modal?
|
||||||
|
Reference in New Issue
Block a user