Terms&Conditions (#18):

* new settings for instance's terms
* new channel option for channel's terms
This commit is contained in:
John Livingston 2024-06-21 18:18:11 +02:00
parent 4b49037f68
commit 45a63eaecd
No known key found for this signature in database
GPG Key ID: B17B5640CE66CDBC
13 changed files with 93 additions and 3 deletions

View File

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

View File

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

View File

@ -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`<div id=${feedbackId} class="invalid-feedback">${errorMessages}</div>`
} else {

View File

@ -128,6 +128,36 @@ export function tplChannelConfiguration (el: ChannelConfigurationElement): Templ
<form livechat-configuration-channel-options role="form" @submit=${el.saveConfig} @change=${el.resetValidation}>
<livechat-configuration-section-header
.label=${ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_TERMS_LABEL)}
.description=${ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_TERMS_DESC, true)}
.helpPage=${'documentation/user/streamers/channel'}>
</livechat-configuration-section-header>
<div class="form-group">
<textarea
name="terms"
id="peertube-livechat-terms"
.value=${el.channelConfiguration?.configuration.terms ?? ''}
maxlength=${el.termsMaxLength()}
class=${classMap(
Object.assign(
{ 'form-control': true },
el.getInputValidationClass('terms')
)
)}
@change=${(event: Event) => {
if (event?.target && el.channelConfiguration) {
let value: string | undefined = (event.target as HTMLTextAreaElement).value
if (value === '') { value = undefined }
el.channelConfiguration.configuration.terms = value
}
el.requestUpdate('channelConfiguration')
}
}
></textarea>
${el.renderFeedback('peertube-livechat-terms-feedback', 'terms')}
</div>
<livechat-configuration-section-header
.label=${ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_MUTE_ANONYMOUS_LABEL)}
.description=${ptTr(LOC_LIVECHAT_CONFIGURATION_CHANNEL_MUTE_ANONYMOUS_DESC, true)}

View File

@ -11,6 +11,7 @@ import type {
import { ValidationError, ValidationErrorType } from '../../lib/models/validation'
import { getBaseRoute } from '../../../utils/uri'
import { maxEmojisPerChannel } from 'shared/lib/emojis'
import { channelTermsMaxLength } from 'shared/lib/constants'
export class ChannelDetailsService {
public _registerClientOptions: RegisterClientOptions
@ -27,6 +28,10 @@ export class ChannelDetailsService {
validateOptions = async (channelConfigurationOptions: ChannelConfigurationOptions): Promise<boolean> => {
const propertiesError: ValidationError['properties'] = {}
if (channelConfigurationOptions.terms && channelConfigurationOptions.terms.length > channelTermsMaxLength) {
propertiesError.terms = [ValidationErrorType.TooLong]
}
const botConf = channelConfigurationOptions.bot
const slowModeDuration = channelConfigurationOptions.slowMode.duration

View File

@ -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`<div id="${inputId}-feedback" class="invalid-feedback">${errorMessages}</div>`
} else {

View File

@ -7,7 +7,8 @@ export enum ValidationErrorType {
WrongType,
WrongFormat,
NotInRange,
Duplicate
Duplicate,
TooLong
}
export class ValidationError extends Error {

View File

@ -45,6 +45,11 @@ diagnostic: |
chat_title: "<h3>Chat</h3>"
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: |
<a class="peertube-plugin-livechat-prosody-list-rooms-btn">List rooms</a>
@ -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.

View File

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

View File

@ -52,7 +52,8 @@ function getDefaultChannelConfigurationOptions (_options: RegisterServerOptions)
},
mute: {
anonymous: false
}
},
terms: undefined
}
}

View File

@ -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'),

1
shared/lib/constants.ts Normal file
View File

@ -0,0 +1 @@
export const channelTermsMaxLength = 400

View File

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