mirror of
https://github.com/discourse/discourse.git
synced 2024-11-23 02:19:27 +08:00
UX: File types site setting (#24976)
New file types site setting with buttons allowing to easily add image/video/audio/document extensions to the list.
This commit is contained in:
parent
fe8b1a1074
commit
7cad69e6ef
|
@ -33,6 +33,7 @@
|
|||
preview=this.preview
|
||||
isSecret=this.isSecret
|
||||
allowAny=this.allowAny
|
||||
changeValueCallback=this.changeValueCallback
|
||||
}}
|
||||
</div>
|
||||
|
||||
|
|
|
@ -0,0 +1,152 @@
|
|||
import Component from "@glimmer/component";
|
||||
import { tracked } from "@glimmer/tracking";
|
||||
import { fn, hash } from "@ember/helper";
|
||||
import { action } from "@ember/object";
|
||||
import { inject as service } from "@ember/service";
|
||||
import { isEmpty } from "@ember/utils";
|
||||
import DButton from "discourse/components/d-button";
|
||||
import htmlSafe from "discourse-common/helpers/html-safe";
|
||||
import i18n from "discourse-common/helpers/i18n";
|
||||
import { makeArray } from "discourse-common/lib/helpers";
|
||||
import I18n from "discourse-i18n";
|
||||
import SettingValidationMessage from "admin/components/setting-validation-message";
|
||||
import ListSetting from "select-kit/components/list-setting";
|
||||
|
||||
const IMAGE_TYPES = [
|
||||
"gif",
|
||||
"png",
|
||||
"jpeg",
|
||||
"jpg",
|
||||
"heic",
|
||||
"heif",
|
||||
"webp",
|
||||
"avif",
|
||||
"svg",
|
||||
];
|
||||
const VIDEO_TYPES = ["mov", "mp4", "webm", "m4v", "3gp", "ogv", "avi", "mpeg"];
|
||||
const AUDIO_TYPES = ["mp3", "ogg", "m4a", "wav", "aac", "flac"];
|
||||
const DOCUMENT_TYPES = ["txt", "pdf", "doc", "docx", "csv"];
|
||||
const TOKEN_SEPARATOR = "|";
|
||||
const IMAGE_TYPES_STRING = IMAGE_TYPES.join(", ");
|
||||
const VIDEO_TYPES_STRING = VIDEO_TYPES.join(", ");
|
||||
const AUDIO_TYPES_STRING = AUDIO_TYPES.join(", ");
|
||||
const DOCUMENT_TYPES_STRING = DOCUMENT_TYPES.join(", ");
|
||||
|
||||
export default class FileTypesList extends Component {
|
||||
@service toasts;
|
||||
|
||||
@tracked createdChoices = null;
|
||||
|
||||
get settingValue() {
|
||||
return this.args.value.toString().split(TOKEN_SEPARATOR).filter(Boolean);
|
||||
}
|
||||
|
||||
get settingChoices() {
|
||||
return [
|
||||
...new Set([
|
||||
...makeArray(this.settingValue),
|
||||
...makeArray(this.args.setting.choices),
|
||||
...makeArray(this.createdChoices),
|
||||
]),
|
||||
];
|
||||
}
|
||||
|
||||
@action
|
||||
onChangeListSetting(value) {
|
||||
this.args.changeValueCallback(value.join(TOKEN_SEPARATOR));
|
||||
}
|
||||
|
||||
@action
|
||||
onChangeChoices(choices) {
|
||||
this.createdChoices = [
|
||||
...new Set([...makeArray(this.createdChoices), ...makeArray(choices)]),
|
||||
];
|
||||
}
|
||||
|
||||
@action
|
||||
insertDefaultTypes(category) {
|
||||
let types;
|
||||
switch (category) {
|
||||
case "image":
|
||||
types = IMAGE_TYPES;
|
||||
break;
|
||||
case "video":
|
||||
types = VIDEO_TYPES;
|
||||
break;
|
||||
case "audio":
|
||||
types = AUDIO_TYPES;
|
||||
break;
|
||||
case "document":
|
||||
types = DOCUMENT_TYPES;
|
||||
break;
|
||||
}
|
||||
|
||||
const oldTypes = this.args.value.split(TOKEN_SEPARATOR);
|
||||
const newTypes = [...new Set([...oldTypes, ...types])];
|
||||
const diffTypes = newTypes.filter((type) => !oldTypes.includes(type));
|
||||
|
||||
if (isEmpty(diffTypes)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.toasts.success({
|
||||
data: {
|
||||
message: I18n.t("admin.site_settings.file_types_list.add_types_toast", {
|
||||
types: diffTypes.join(", "),
|
||||
}),
|
||||
},
|
||||
});
|
||||
|
||||
this.args.changeValueCallback(newTypes.join(TOKEN_SEPARATOR));
|
||||
}
|
||||
<template>
|
||||
<ListSetting
|
||||
@value={{this.settingValue}}
|
||||
@settingName={{@setting.setting}}
|
||||
@choices={{this.settingChoices}}
|
||||
@onChange={{this.onChangeListSetting}}
|
||||
@onChangeChoices={{this.onChangeChoices}}
|
||||
@options={{hash allowAny=@allowAny}}
|
||||
/>
|
||||
|
||||
<DButton
|
||||
@action={{(fn this.insertDefaultTypes "image")}}
|
||||
@label="admin.site_settings.file_types_list.add_image_types"
|
||||
@translatedTitle={{i18n
|
||||
"admin.site_settings.file_types_list.add_types_title"
|
||||
types=IMAGE_TYPES_STRING
|
||||
}}
|
||||
class="btn file-types-list__button image"
|
||||
/>
|
||||
<DButton
|
||||
@action={{(fn this.insertDefaultTypes "video")}}
|
||||
@label="admin.site_settings.file_types_list.add_video_types"
|
||||
@translatedTitle={{i18n
|
||||
"admin.site_settings.file_types_list.add_types_title"
|
||||
types=VIDEO_TYPES_STRING
|
||||
}}
|
||||
class="btn file-types-list__button video"
|
||||
/>
|
||||
<DButton
|
||||
@action={{(fn this.insertDefaultTypes "audio")}}
|
||||
@label="admin.site_settings.file_types_list.add_audio_types"
|
||||
@translatedTitle={{i18n
|
||||
"admin.site_settings.file_types_list.add_types_title audio"
|
||||
types=AUDIO_TYPES_STRING
|
||||
}}
|
||||
class="btn file-types-list__button"
|
||||
/>
|
||||
<DButton
|
||||
@action={{(fn this.insertDefaultTypes "document")}}
|
||||
@label="admin.site_settings.file_types_list.add_document_types"
|
||||
@translatedTitle={{i18n
|
||||
"admin.site_settings.file_types_list.add_types_title"
|
||||
types=DOCUMENT_TYPES_STRING
|
||||
}}
|
||||
class="btn file-types-list__button document"
|
||||
/>
|
||||
|
||||
<SettingValidationMessage @message={{this.validationMessage}} />
|
||||
<div class="desc">{{htmlSafe @setting.description}}</div>
|
||||
</template>
|
||||
}
|
|
@ -34,6 +34,7 @@ const CUSTOM_TYPES = [
|
|||
"emoji_list",
|
||||
"named_list",
|
||||
"file_size_restriction",
|
||||
"file_types_list",
|
||||
];
|
||||
|
||||
const AUTO_REFRESH_ON_SAVE = ["logo", "logo_small", "large_icon"];
|
||||
|
@ -230,6 +231,11 @@ export default Mixin.create({
|
|||
}
|
||||
},
|
||||
|
||||
@action
|
||||
changeValueCallback(value) {
|
||||
this.set("buffered.value", value);
|
||||
},
|
||||
|
||||
@action
|
||||
cancel() {
|
||||
this.rollbackBuffer();
|
||||
|
|
|
@ -59,4 +59,33 @@ module("Integration | Component | site-setting", function (hooks) {
|
|||
|
||||
assert.strictEqual(query(".validation-error h1"), null);
|
||||
});
|
||||
|
||||
test("displays file types list setting", async function (assert) {
|
||||
this.set("setting", {
|
||||
setting: "theme_authorized_extensions",
|
||||
value: "jpg|jpeg|png",
|
||||
type: "file_types_list",
|
||||
});
|
||||
|
||||
await render(hbs`<SiteSetting @setting={{this.setting}} />`);
|
||||
|
||||
assert.strictEqual(
|
||||
query(".formatted-selection").innerText,
|
||||
"jpg, jpeg, png"
|
||||
);
|
||||
|
||||
await click(query(".file-types-list__button.image"));
|
||||
|
||||
assert.strictEqual(
|
||||
query(".formatted-selection").innerText,
|
||||
"jpg, jpeg, png, gif, heic, heif, webp, avif, svg"
|
||||
);
|
||||
|
||||
await click(query(".file-types-list__button.image"));
|
||||
|
||||
assert.strictEqual(
|
||||
query(".formatted-selection").innerText,
|
||||
"jpg, jpeg, png, gif, heic, heif, webp, avif, svg"
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -137,4 +137,11 @@
|
|||
.warning {
|
||||
color: var(--danger);
|
||||
}
|
||||
.file-types-list__button {
|
||||
margin-top: 0.5em;
|
||||
margin-bottom: 0.2em;
|
||||
background: var(--primary-medium);
|
||||
color: var(--secondary);
|
||||
font-size: var(--font-down-2);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6373,6 +6373,13 @@ en:
|
|||
json_schema:
|
||||
edit: Launch Editor
|
||||
modal_title: "Edit %{name}"
|
||||
file_types_list:
|
||||
add_image_types: "Images"
|
||||
add_video_types: "Videos"
|
||||
add_audio_types: "Audio"
|
||||
add_document_types: "Documents"
|
||||
add_types_title: "Allow extensions %{types}"
|
||||
add_types_toast: "%{types} file types added"
|
||||
|
||||
badges:
|
||||
title: Badges
|
||||
|
|
|
@ -2023,9 +2023,9 @@ en:
|
|||
|
||||
max_image_size_kb: "The maximum image upload size. This must be configured in nginx (client_max_body_size) / apache or proxy as well. Images larger than this and smaller than client_max_body_size will be resized to fit on upload."
|
||||
max_attachment_size_kb: "The maximum attachment files upload size. This must be configured in nginx (client_max_body_size) / apache or proxy as well."
|
||||
authorized_extensions: "A list of file extensions allowed for upload (use '*' to enable all file types)"
|
||||
authorized_extensions_for_staff: "A list of file extensions allowed for upload for staff users in addition to the list defined in the `authorized_extensions` site setting. (use '*' to enable all file types)"
|
||||
theme_authorized_extensions: "A list of file extensions allowed for theme uploads (use '*' to enable all file types)"
|
||||
authorized_extensions: "A list of file extensions allowed for upload"
|
||||
authorized_extensions_for_staff: "A list of file extensions allowed for upload for staff users in addition to the list defined in the `authorized_extensions` site setting."
|
||||
theme_authorized_extensions: "A list of file extensions allowed for theme uploads"
|
||||
max_similar_results: "How many similar topics to show above the editor when composing a new topic. Comparison is based on title and body."
|
||||
|
||||
max_image_megapixels: "Maximum number of megapixels allowed for an image. Images with a higher number of megapixels will be rejected."
|
||||
|
|
|
@ -1435,19 +1435,19 @@ files:
|
|||
theme_authorized_extensions:
|
||||
default: "wasm|jpg|jpeg|png|woff|woff2|svg|eot|ttf|otf|gif|webp|avif|js"
|
||||
type: list
|
||||
list_type: compact
|
||||
list_type: file_types
|
||||
authorized_extensions:
|
||||
client: true
|
||||
default: "jpg|jpeg|png|gif|heic|heif|webp|avif"
|
||||
refresh: true
|
||||
type: list
|
||||
list_type: compact
|
||||
list_type: file_types
|
||||
authorized_extensions_for_staff:
|
||||
client: true
|
||||
default: ""
|
||||
refresh: true
|
||||
type: list
|
||||
list_type: compact
|
||||
list_type: file_types
|
||||
export_authorized_extensions:
|
||||
hidden: true
|
||||
default: "zip"
|
||||
|
|
Loading…
Reference in New Issue
Block a user