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:
Krzysztof Kotlarek 2023-12-22 11:23:42 +11:00 committed by GitHub
parent fe8b1a1074
commit 7cad69e6ef
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 208 additions and 6 deletions

View File

@ -33,6 +33,7 @@
preview=this.preview
isSecret=this.isSecret
allowAny=this.allowAny
changeValueCallback=this.changeValueCallback
}}
</div>

View File

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

View File

@ -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();

View File

@ -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"
);
});
});

View File

@ -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);
}
}

View File

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

View File

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

View File

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