From 45a63eaecdcdb4d3a6093284adfa6dd3f2316c73 Mon Sep 17 00:00:00 2001 From: John Livingston Date: Fri, 21 Jun 2024 18:18:11 +0200 Subject: [PATCH] Terms&Conditions (#18): * new settings for instance's terms * new channel option for channel's terms --- CHANGELOG.md | 1 + client/@types/global.d.ts | 3 ++ .../elements/channel-configuration.ts | 12 ++++++++ .../templates/channel-configuration.ts | 30 +++++++++++++++++++ .../configuration/services/channel-details.ts | 5 ++++ .../common/lib/elements/dynamic-table-form.ts | 4 +++ client/common/lib/models/validation.ts | 3 +- languages/en.yml | 9 ++++++ server/lib/configuration/channel/sanitize.ts | 14 +++++++++ server/lib/configuration/channel/storage.ts | 3 +- server/lib/settings.ts | 8 +++++ shared/lib/constants.ts | 1 + shared/lib/types.ts | 3 +- 13 files changed, 93 insertions(+), 3 deletions(-) create mode 100644 shared/lib/constants.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 39f1ca7e..e7c54f17 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ ### New features * #233: new option to [mute anonymous users](https://livingston.frama.io/peertube-plugin-livechat/fr/documentation/user/streamers/moderation/). +* #18: terms & conditions. You can configure terms&conditions on your instance that will be shown to each joining users. Streamers can also add terms&conditions in their channels options. ## 10.1.2 diff --git a/client/@types/global.d.ts b/client/@types/global.d.ts index d398383c..1398da00 100644 --- a/client/@types/global.d.ts +++ b/client/@types/global.d.ts @@ -83,6 +83,8 @@ declare const LOC_LIVECHAT_CONFIGURATION_CHANNEL_BOT_NICKNAME: string declare const LOC_LIVECHAT_CONFIGURATION_CHANNEL_FOR_MORE_INFO: string declare const LOC_LIVECHAT_CONFIGURATION_CHANNEL_MUTE_ANONYMOUS_LABEL: string declare const LOC_LIVECHAT_CONFIGURATION_CHANNEL_MUTE_ANONYMOUS_DESC: string +declare const LOC_LIVECHAT_CONFIGURATION_CHANNEL_TERMS_LABEL: string +declare const LOC_LIVECHAT_CONFIGURATION_CHANNEL_TERMS_DESC: string declare const LOC_VALIDATION_ERROR: string declare const LOC_TOO_MANY_ENTRIES: string @@ -93,6 +95,7 @@ declare const LOC_INVALID_VALUE_WRONG_FORMAT: string declare const LOC_INVALID_VALUE_NOT_IN_RANGE: string declare const LOC_INVALID_VALUE_FILE_TOO_BIG: string declare const LOC_INVALID_VALUE_DUPLICATE: string +declare const LOC_INVALID_VALUE_TOO_LONG: string declare const LOC_CHATROOM_NOT_ACCESSIBLE: string diff --git a/client/common/configuration/elements/channel-configuration.ts b/client/common/configuration/elements/channel-configuration.ts index a5e5bb14..7e6edd66 100644 --- a/client/common/configuration/elements/channel-configuration.ts +++ b/client/common/configuration/elements/channel-configuration.ts @@ -14,6 +14,7 @@ import { customElement, property, state } from 'lit/decorators.js' import { ptTr } from '../../lib/directives/translation' import { Task } from '@lit/task' import { provide } from '@lit/context' +import { channelTermsMaxLength } from 'shared/lib/constants' @customElement('livechat-channel-configuration') export class ChannelConfigurationElement extends LivechatElement { @@ -51,6 +52,10 @@ export class ChannelConfigurationElement extends LivechatElement { }) } + public termsMaxLength (): number { + return channelTermsMaxLength + } + /** * Resets the form by reloading data from backend. */ @@ -120,7 +125,11 @@ export class ChannelConfigurationElement extends LivechatElement { const validationErrorTypes: ValidationErrorType[] | undefined = this.validationError?.properties[`${propertyName}`] ?? undefined + // FIXME: this code is duplicated in dymamic table form if (validationErrorTypes && validationErrorTypes.length !== 0) { + if (validationErrorTypes.includes(ValidationErrorType.Missing)) { + errorMessages.push(html`${ptTr(LOC_INVALID_VALUE_MISSING)}`) + } if (validationErrorTypes.includes(ValidationErrorType.WrongType)) { errorMessages.push(html`${ptTr(LOC_INVALID_VALUE_WRONG_TYPE)}`) } @@ -130,6 +139,9 @@ export class ChannelConfigurationElement extends LivechatElement { if (validationErrorTypes.includes(ValidationErrorType.NotInRange)) { errorMessages.push(html`${ptTr(LOC_INVALID_VALUE_NOT_IN_RANGE)}`) } + if (validationErrorTypes.includes(ValidationErrorType.TooLong)) { + errorMessages.push(html`${ptTr(LOC_INVALID_VALUE_TOO_LONG)}`) + } return html`
${errorMessages}
` } else { diff --git a/client/common/configuration/elements/templates/channel-configuration.ts b/client/common/configuration/elements/templates/channel-configuration.ts index 9459e0fc..118e05bc 100644 --- a/client/common/configuration/elements/templates/channel-configuration.ts +++ b/client/common/configuration/elements/templates/channel-configuration.ts @@ -128,6 +128,36 @@ export function tplChannelConfiguration (el: ChannelConfigurationElement): Templ
+ + +
+ + ${el.renderFeedback('peertube-livechat-terms-feedback', 'terms')} +
+ => { const propertiesError: ValidationError['properties'] = {} + if (channelConfigurationOptions.terms && channelConfigurationOptions.terms.length > channelTermsMaxLength) { + propertiesError.terms = [ValidationErrorType.TooLong] + } + const botConf = channelConfigurationOptions.bot const slowModeDuration = channelConfigurationOptions.slowMode.duration diff --git a/client/common/lib/elements/dynamic-table-form.ts b/client/common/lib/elements/dynamic-table-form.ts index 5170b79d..bc0099e5 100644 --- a/client/common/lib/elements/dynamic-table-form.ts +++ b/client/common/lib/elements/dynamic-table-form.ts @@ -672,6 +672,7 @@ export class DynamicTableFormElement extends LivechatElement { const validationErrorTypes: ValidationErrorType[] | undefined = this.validation?.[`${this.validationPrefix}.${originalIndex}.${propertyName}`] + // FIXME: this code is duplicated in channel-configuration if (validationErrorTypes !== undefined && validationErrorTypes.length !== 0) { if (validationErrorTypes.includes(ValidationErrorType.Missing)) { errorMessages.push(html`${ptTr(LOC_INVALID_VALUE_MISSING)}`) @@ -688,6 +689,9 @@ export class DynamicTableFormElement extends LivechatElement { if (validationErrorTypes.includes(ValidationErrorType.Duplicate)) { errorMessages.push(html`${ptTr(LOC_INVALID_VALUE_DUPLICATE)}`) } + if (validationErrorTypes.includes(ValidationErrorType.TooLong)) { + errorMessages.push(html`${ptTr(LOC_INVALID_VALUE_TOO_LONG)}`) + } return html`
${errorMessages}
` } else { diff --git a/client/common/lib/models/validation.ts b/client/common/lib/models/validation.ts index da146ab0..b034b693 100644 --- a/client/common/lib/models/validation.ts +++ b/client/common/lib/models/validation.ts @@ -7,7 +7,8 @@ export enum ValidationErrorType { WrongType, WrongFormat, NotInRange, - Duplicate + Duplicate, + TooLong } export class ValidationError extends Error { diff --git a/languages/en.yml b/languages/en.yml index 7ba36fa3..b70ec513 100644 --- a/languages/en.yml +++ b/languages/en.yml @@ -45,6 +45,11 @@ diagnostic: | chat_title: "

Chat

" +chat_terms_label: "Terms & Conditions" +chat_terms_description: | + These terms & conditions will be shown to all users when then join chatrooms. + Streamers can also configure terms & conditions for their channels, that will be shown right after these global terms & conditions. + list_rooms_label: "List existing rooms" list_rooms_description: | List rooms @@ -455,6 +460,7 @@ invalid_value_wrong_format: "Value is in the wrong format." invalid_value_not_in_range: "Value is not in authorized range." invalid_value_file_too_big: "File size is too big (max size: %s)." invalid_value_duplicate: "Duplicate value" +invalid_value_too_long: "Value too long" too_many_entries: "Too many entries" slow_mode_info: "Slow mode is enabled, users can send a message every %1$s seconds." @@ -553,3 +559,6 @@ livechat_configuration_channel_mute_anonymous_desc: | Default value for new chatrooms. For existing chatrooms, you can change the feature in the room configuration form. When this feature is enabled, anonymous users can only read the chat, and not send messages. +livechat_configuration_channel_terms_label: "Channel's chat terms & conditions" +livechat_configuration_channel_terms_desc: | + You can configure a "terms & conditions" message that will be shown to users joining your chatrooms. diff --git a/server/lib/configuration/channel/sanitize.ts b/server/lib/configuration/channel/sanitize.ts index 2067e463..1167263f 100644 --- a/server/lib/configuration/channel/sanitize.ts +++ b/server/lib/configuration/channel/sanitize.ts @@ -4,6 +4,7 @@ import type { RegisterServerOptions } from '@peertube/peertube-types' import type { ChannelConfigurationOptions } from '../../../../shared/lib/types' +import { channelTermsMaxLength } from '../../../../shared/lib/constants' /** * Sanitize data so that they can safely be used/stored for channel configuration configuration. @@ -43,6 +44,16 @@ async function sanitizeChannelConfigurationOptions ( throw new Error('Invalid data.mute data type') } + // terms not present in livechat <= 10.2.0 + let terms = data.terms + if (terms !== undefined && (typeof terms !== 'string')) { + throw new Error('Invalid data.terms data type') + } + if (terms && terms.length > channelTermsMaxLength) { + throw new Error('data.terms value too long') + } + if (terms === '') { terms = undefined } + const result: ChannelConfigurationOptions = { bot: { enabled: _readBoolean(botData, 'enabled'), @@ -59,6 +70,9 @@ async function sanitizeChannelConfigurationOptions ( anonymous: _readBoolean(mute, 'anonymous') } } + if (terms !== undefined) { + result.terms = terms + } return result } diff --git a/server/lib/configuration/channel/storage.ts b/server/lib/configuration/channel/storage.ts index 0836db46..1c6c4ce3 100644 --- a/server/lib/configuration/channel/storage.ts +++ b/server/lib/configuration/channel/storage.ts @@ -52,7 +52,8 @@ function getDefaultChannelConfigurationOptions (_options: RegisterServerOptions) }, mute: { anonymous: false - } + }, + terms: undefined } } diff --git a/server/lib/settings.ts b/server/lib/settings.ts index 8e8502d5..71bf1981 100644 --- a/server/lib/settings.ts +++ b/server/lib/settings.ts @@ -148,6 +148,14 @@ function initChatSettings ({ registerSetting }: RegisterServerOptions): void { private: true, descriptionHTML: loc('chat_title') }) + registerSetting({ + name: 'chat-terms', + private: true, + label: loc('chat_terms_label'), + type: 'input-textarea', + default: '', + descriptionHTML: loc('chat_terms_description') + }) registerSetting({ name: 'prosody-list-rooms', label: loc('list_rooms_label'), diff --git a/shared/lib/constants.ts b/shared/lib/constants.ts new file mode 100644 index 00000000..23231517 --- /dev/null +++ b/shared/lib/constants.ts @@ -0,0 +1 @@ +export const channelTermsMaxLength = 400 diff --git a/shared/lib/types.ts b/shared/lib/types.ts index ad9cdf4f..20ff605e 100644 --- a/shared/lib/types.ts +++ b/shared/lib/types.ts @@ -101,11 +101,12 @@ interface ChannelConfigurationOptions { slowMode: { duration: number } - mute: { + mute: { // comes with Livechat 10.2.0 anonymous: boolean // TODO: https://github.com/JohnXLivingston/peertube-plugin-livechat/issues/127 // nonFollowers: boolean (or a number of seconds?) } + terms?: string // comes with Livechat 10.2.0 } interface ChannelForbiddenWords {