diff --git a/client/src/app/+about/about-instance/about-instance.component.html b/client/src/app/+about/about-instance/about-instance.component.html index 7c27ec760..0fd3626b7 100644 --- a/client/src/app/+about/about-instance/about-instance.component.html +++ b/client/src/app/+about/about-instance/about-instance.component.html @@ -1,7 +1,8 @@ <div class="row"> <div class="col-md-12 col-xl-6"> + <div class="about-instance-title"> - <div i18n>About {{ instanceName }} instance</div> + <div i18n class="title">About {{ instanceName }} instance</div> <div *ngIf="isContactFormEnabled" (click)="openContactModal()" i18n role="button" class="contact-admin">Contact administrator</div> </div> @@ -12,16 +13,58 @@ <div *ngIf="isNSFW" class="dedicated-to-nsfw">This instance is dedicated to sensitive/NSFW content.</div> </div> - <div class="description"> - <div i18n class="section-title">Description</div> - - <div [innerHTML]="descriptionHTML"></div> + <div class="middle-title" *ngIf="html.administrator || maintenanceLifetime || businessModel"> + Administrators & sustainability </div> - <div class="terms" id="terms-section"> + <div class="block administrator" *ngIf="html.administrator"> + <div i18n class="section-title">Instance administrators</div> + + <div [innerHTML]="html.administrator"></div> + </div> + + <div class="block maintenance-lifetime" *ngIf="maintenanceLifetime"> + <div i18n class="section-title">Maintenance lifetime</div> + + <p>{{ maintenanceLifetime }}</p> + </div> + + <div class="block business-model" *ngIf="businessModel"> + <div i18n class="section-title">Business model</div> + + <p>{{ businessModel }}</p> + </div> + + <div class="middle-title" *ngIf="html.description"> + Information + </div> + + <div class="block description"> + <div i18n class="section-title">Description</div> + + <div [innerHTML]="html.description"></div> + </div> + + <div class="middle-title" *ngIf="html.moderationInformation || html.codeOfConduct || html.terms"> + Moderation + </div> + + <div class="block moderation-information" *ngIf="html.moderationInformation"> + <div i18n class="section-title">Moderation information</div> + + <div [innerHTML]="html.moderationInformation"></div> + </div> + + <div class="block code-of-conduct" *ngIf="html.codeOfConduct"> + <div i18n class="section-title">Code of conduct</div> + + <div [innerHTML]="html.codeOfConduct"></div> + </div> + + <div class="block terms" id="terms-section"> <div i18n class="section-title">Terms</div> - <div [innerHTML]="termsHTML"></div> + <div [innerHTML]="html.terms"></div> </div> </div> diff --git a/client/src/app/+about/about-instance/about-instance.component.scss b/client/src/app/+about/about-instance/about-instance.component.scss index 0296ae8e9..0585ad5f3 100644 --- a/client/src/app/+about/about-instance/about-instance.component.scss +++ b/client/src/app/+about/about-instance/about-instance.component.scss @@ -5,13 +5,13 @@ display: flex; justify-content: space-between; - & > div { + .title { font-size: 20px; - font-weight: bold; margin-bottom: 15px; + font-weight: $font-semibold; } - & > .contact-admin { + .contact-admin { @include peertube-button; @include orange-button; @@ -21,11 +21,20 @@ .section-title { font-weight: $font-semibold; - font-size: 20px; + font-size: 16px; margin-bottom: 5px; + display: flex; + align-items: center; } -.short-description, .description, .terms, .signup { +.middle-title { + @include in-content-small-title; + + margin-top: 45px; + margin-bottom: 25px; +} + +.block { margin-bottom: 30px; } diff --git a/client/src/app/+about/about-instance/about-instance.component.ts b/client/src/app/+about/about-instance/about-instance.component.ts index a5204de27..b85a6be94 100644 --- a/client/src/app/+about/about-instance/about-instance.component.ts +++ b/client/src/app/+about/about-instance/about-instance.component.ts @@ -14,8 +14,20 @@ export class AboutInstanceComponent implements OnInit { @ViewChild('contactAdminModal', { static: true }) contactAdminModal: ContactAdminModalComponent shortDescription = '' - descriptionHTML = '' - termsHTML = '' + + html = { + description: '', + terms: '', + codeOfConduct: '', + moderationInformation: '', + administrator: '' + } + + maintenanceLifetime = '' + businessModel = '' + + languages: string[] = [] + categories: number[] = [] constructor ( private notifier: Notifier, @@ -43,8 +55,15 @@ export class AboutInstanceComponent implements OnInit { async res => { this.shortDescription = res.instance.shortDescription - this.descriptionHTML = await this.markdownService.textMarkdownToHTML(res.instance.description) - this.termsHTML = await this.markdownService.textMarkdownToHTML(res.instance.terms) + this.maintenanceLifetime = res.instance.maintenanceLifetime + this.businessModel = res.instance.businessModel + + for (const key of [ 'description', 'terms', 'codeOfConduct', 'moderationInformation', 'administrator' ]) { + this.html[key] = await this.markdownService.textMarkdownToHTML(res.instance[key]) + } + + this.languages = res.instance.languages + this.categories = res.instance.categories }, () => this.notifier.error(this.i18n('Cannot get about information from server')) diff --git a/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.html b/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.html index ec6f879d7..50df8a8ac 100644 --- a/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.html +++ b/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.html @@ -2,12 +2,13 @@ <ngb-tabset class="root-tabset bootstrap"> - <ngb-tab i18n-title title="Basic configuration"> + <ngb-tab i18n-title title="Instance information"> <ng-template ngbTabContent> - <div i18n class="inner-form-title">Instance</div> - <ng-container formGroupName="instance"> + + <div i18n class="inner-form-title">Instance</div> + <div class="form-group"> <label i18n for="instanceName">Name</label> <input @@ -36,36 +37,40 @@ </div> <div class="form-group"> - <label i18n for="instanceTerms">Terms</label><my-help helpType="markdownText"></my-help> - <my-markdown-textarea - id="instanceTerms" formControlName="terms" textareaWidth="500px" [previewColumn]="true" - [ngClass]="{ 'input-error': formErrors['instance.terms'] }" - ></my-markdown-textarea> - <div *ngIf="formErrors.instance.terms" class="form-error">{{ formErrors.instance.terms }}</div> + <label i18n for="instanceCategories">Main instance categories</label> + + <div> + <p-multiSelect + inputId="instanceCategories" [options]="categoryItems" formControlName="categories" showToggleAll="false" + [defaultLabel]="getDefaultCategoryLabel()" [selectedItemsLabel]="getSelectedCategoryLabel()" + emptyFilterMessage="No results found" i18n-emptyFilterMessage + ></p-multiSelect> + </div> </div> + <div class="form-group"> + <label i18n for="instanceLanguages">Main languages you/your moderators speak</label> + + <div> + <p-multiSelect + inputId="instanceLanguages" [options]="languageItems" formControlName="languages" showToggleAll="false" + [defaultLabel]="getDefaultLanguageLabel()" [selectedItemsLabel]="getSelectedLanguageLabel()" + emptyFilterMessage="No results found" i18n-emptyFilterMessage + ></p-multiSelect> + </div> + </div> + + <div i18n class="inner-form-title">Moderation & NSFW</div> + <div class="form-group"> <my-peertube-checkbox inputName="instanceIsNSFW" formControlName="isNSFW" - i18n-labelText labelText="Dedicated to sensitive or NSFW content" + i18n-labelText labelText="This instance is dedicated to sensitive or NSFW content" i18n-helpHtml helpHtml="Enabling it will allow other administrators to know that you are mainly federating sensitive content.<br /><br /> Moreover, the NSFW checkbox on video upload will be automatically checked by default." ></my-peertube-checkbox> </div> - <div class="form-group"> - <label i18n for="instanceDefaultClientRoute">Default client route</label> - <div class="peertube-select-container"> - <select id="instanceDefaultClientRoute" formControlName="defaultClientRoute"> - <option i18n value="/videos/overview">Videos Overview</option> - <option i18n value="/videos/trending">Videos Trending</option> - <option i18n value="/videos/recently-added">Videos Recently Added</option> - <option i18n value="/videos/local">Local videos</option> - </select> - </div> - <div *ngIf="formErrors.instance.defaultClientRoute" class="form-error">{{ formErrors.instance.defaultClientRoute }}</div> - </div> - <div class="form-group"> <label i18n for="instanceDefaultNSFWPolicy">Policy on videos containing sensitive content</label> <my-help @@ -82,10 +87,79 @@ </div> <div *ngIf="formErrors.instance.defaultNSFWPolicy" class="form-error">{{ formErrors.instance.defaultNSFWPolicy }}</div> </div> + + <div class="form-group"> + <label i18n for="instanceTerms">Terms</label><my-help helpType="markdownText"></my-help> + <my-markdown-textarea + id="instanceTerms" formControlName="terms" textareaWidth="500px" [previewColumn]="true" + [ngClass]="{ 'input-error': formErrors['instance.terms'] }" + ></my-markdown-textarea> + <div *ngIf="formErrors.instance.terms" class="form-error">{{ formErrors.instance.terms }}</div> + </div> + + <div class="form-group"> + <label i18n for="instanceCodeOfConduct">Code of conduct</label><my-help helpType="markdownText"></my-help> + <my-markdown-textarea + id="instanceCodeOfConduct" formControlName="codeOfConduct" textareaWidth="500px" [previewColumn]="true" + [ngClass]="{ 'input-error': formErrors['instance.codeOfConduct'] }" + ></my-markdown-textarea> + <div *ngIf="formErrors.instance.codeOfConduct" class="form-error">{{ formErrors.instance.codeOfConduct }}</div> + </div> + + <div class="form-group"> + <label i18n for="instanceModerationInformation">Moderation information</label><my-help helpType="markdownText"></my-help> + <div class="label-small-info">Who moderates the instance? What is the policy regarding NSFW videos? Political videos? etc</div> + + <my-markdown-textarea + id="instanceModerationInformation" formControlName="moderationInformation" textareaWidth="500px" [previewColumn]="true" + [ngClass]="{ 'input-error': formErrors['instance.moderationInformation'] }" + ></my-markdown-textarea> + <div *ngIf="formErrors.instance.moderationInformation" class="form-error">{{ formErrors.instance.moderationInformation }}</div> + </div> + + <div i18n class="inner-form-title">You and your instance</div> + + <div class="form-group"> + <label i18n for="instanceAdministrator">Who is behind the instance? </label> + <div class="label-small-info">A single person? A non profit? A company?</div> + + <textarea + id="instanceAdministrator" formControlName="administrator" + [ngClass]="{ 'input-error': formErrors['instance.administrator'] }" + ></textarea> + <div *ngIf="formErrors.instance.administrator" class="form-error">{{ formErrors.instance.administrator }}</div> + </div> + + <div class="form-group"> + <label i18n for="instanceMaintenanceLifetime">How long do you plan to maintain this instance?</label> + <div class="label-small-info">It's important to know for users who want to register on your instance</div> + + <textarea + id="instanceMaintenanceLifetime" formControlName="maintenanceLifetime" + [ngClass]="{ 'input-error': formErrors['instance.maintenanceLifetime'] }" + ></textarea> + <div *ngIf="formErrors.instance.maintenanceLifetime" class="form-error">{{ formErrors.instance.maintenanceLifetime }}</div> + </div> + + <div class="form-group"> + <label i18n for="instanceBusinessModel">How will you pay the PeerTube instance server?</label> + <div class="label-small-info">With you own funds? With users donations? Advertising?</div> + + <textarea + id="instanceBusinessModel" formControlName="businessModel" + [ngClass]="{ 'input-error': formErrors['instance.businessModel'] }" + ></textarea> + <div *ngIf="formErrors.instance.businessModel" class="form-error">{{ formErrors.instance.businessModel }}</div> + </div> + </ng-container> + </ng-template> + </ngb-tab> + <ngb-tab i18n-title title="Basic configuration"> + <ng-template ngbTabContent> - <div i18n class="inner-form-title">Theme</div> + <div i18n class="inner-form-title">Theme & Default route</div> <ng-container formGroupName="theme"> <div class="form-group"> @@ -102,6 +176,19 @@ </ng-container> + <div class="form-group" formGroupName="instance"> + <label i18n for="instanceDefaultClientRoute">Default client route</label> + <div class="peertube-select-container"> + <select id="instanceDefaultClientRoute" formControlName="defaultClientRoute"> + <option i18n value="/videos/overview">Videos Discover</option> + <option i18n value="/videos/trending">Videos Trending</option> + <option i18n value="/videos/recently-added">Videos Recently Added</option> + <option i18n value="/videos/local">Local videos</option> + </select> + </div> + <div *ngIf="formErrors.instance.defaultClientRoute" class="form-error">{{ formErrors.instance.defaultClientRoute }}</div> + </div> + <div i18n class="inner-form-title">Signup</div> <ng-container formGroupName="signup"> diff --git a/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.scss b/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.scss index c90bd5141..68f1b01b7 100644 --- a/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.scss +++ b/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.scss @@ -1,6 +1,10 @@ @import '_variables'; @import '_mixins'; +.form-group { + margin-bottom: 25px; +} + input[type=text] { @include peertube-input-text(340px); display: block; @@ -44,3 +48,8 @@ textarea { height: 100px; } } + +.label-small-info { + font-style: italic; + margin-bottom: 10px; +} diff --git a/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.ts b/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.ts index d51104569..3119ab040 100644 --- a/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.ts +++ b/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.ts @@ -6,6 +6,9 @@ import { Notifier } from '@app/core' import { CustomConfig } from '../../../../../../shared/models/server/custom-config.model' import { I18n } from '@ngx-translate/i18n-polyfill' import { FormValidatorService } from '@app/shared/forms/form-validators/form-validator.service' +import { SelectItem } from 'primeng/api' +import { forkJoin } from 'rxjs' +import { first } from 'rxjs/operators' @Component({ selector: 'my-edit-custom-config', @@ -18,6 +21,9 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit { resolutions: { id: string, label: string }[] = [] transcodingThreadOptions: { label: string, value: number }[] = [] + languageItems: SelectItem[] = [] + categoryItems: SelectItem[] = [] + constructor ( protected formValidatorService: FormValidatorService, private customConfigValidatorsService: CustomConfigValidatorsService, @@ -88,10 +94,22 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit { name: this.customConfigValidatorsService.INSTANCE_NAME, shortDescription: this.customConfigValidatorsService.INSTANCE_SHORT_DESCRIPTION, description: null, - terms: null, - defaultClientRoute: null, + isNSFW: false, defaultNSFWPolicy: null, + + terms: null, + codeOfConduct: null, + moderationInformation: null, + administrator: null, + maintenanceLifetime: null, + businessModel: null, + + categories: null, + languages: null, + + defaultClientRoute: null, + customizations: { javascript: null, css: null @@ -184,18 +202,27 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit { this.buildForm(formGroupData) - this.configService.getCustomConfig() - .subscribe( - res => { - this.customConfig = res + forkJoin([ + this.configService.getCustomConfig(), + this.serverService.videoLanguagesLoaded.pipe(first()), // First so the observable completes + this.serverService.videoCategoriesLoaded.pipe(first()) + ]).subscribe( + ([ config ]) => { + this.customConfig = config - this.updateForm() - // Force form validation - this.forceCheck() - }, + const languages = this.serverService.getVideoLanguages() + this.languageItems = languages.map(l => ({ label: l.label, value: l.id })) - err => this.notifier.error(err.message) - ) + const categories = this.serverService.getVideoCategories() + this.categoryItems = categories.map(l => ({ label: l.label, value: l.id })) + + this.updateForm() + // Force form validation + this.forceCheck() + }, + + err => this.notifier.error(err.message) + ) } isTranscodingEnabled () { @@ -224,8 +251,23 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit { ) } + getSelectedLanguageLabel () { + return this.i18n('{{\'{0} languages selected') + } + + getDefaultLanguageLabel () { + return this.i18n('No language') + } + + getSelectedCategoryLabel () { + return this.i18n('{{\'{0} categories selected') + } + + getDefaultCategoryLabel () { + return this.i18n('No category') + } + private updateForm () { this.form.patchValue(this.customConfig) } - } diff --git a/client/src/app/+my-account/my-account-settings/my-account-video-settings/my-account-video-settings.component.html b/client/src/app/+my-account/my-account-settings/my-account-video-settings/my-account-video-settings.component.html index 2796dd2db..caa032149 100644 --- a/client/src/app/+my-account/my-account-settings/my-account-video-settings/my-account-video-settings.component.html +++ b/client/src/app/+my-account/my-account-settings/my-account-video-settings/my-account-video-settings.component.html @@ -23,7 +23,7 @@ <div> <p-multiSelect - [options]="languageItems" formControlName="videoLanguages" showToggleAll="true" + inputId="videoLanguages" [options]="languageItems" formControlName="videoLanguages" showToggleAll="true" [defaultLabel]="getDefaultVideoLanguageLabel()" [selectedItemsLabel]="getSelectedVideoLanguageLabel()" emptyFilterMessage="No results found" i18n-emptyFilterMessage ></p-multiSelect> diff --git a/client/src/app/+my-account/my-account-settings/my-account-video-settings/my-account-video-settings.component.ts b/client/src/app/+my-account/my-account-settings/my-account-video-settings/my-account-video-settings.component.ts index 77febf179..4fb828082 100644 --- a/client/src/app/+my-account/my-account-settings/my-account-video-settings/my-account-video-settings.component.ts +++ b/client/src/app/+my-account/my-account-settings/my-account-video-settings/my-account-video-settings.component.ts @@ -5,9 +5,9 @@ import { AuthService } from '../../../core' import { FormReactive, User, UserService } from '../../../shared' import { I18n } from '@ngx-translate/i18n-polyfill' import { FormValidatorService } from '@app/shared/forms/form-validators/form-validator.service' -import { Subject } from 'rxjs' +import { forkJoin, Subject } from 'rxjs' import { SelectItem } from 'primeng/api' -import { switchMap } from 'rxjs/operators' +import { first } from 'rxjs/operators' @Component({ selector: 'my-account-video-settings', @@ -39,30 +39,31 @@ export class MyAccountVideoSettingsComponent extends FormReactive implements OnI videoLanguages: null }) - this.serverService.videoLanguagesLoaded - .pipe(switchMap(() => this.userInformationLoaded)) - .subscribe(() => { - const languages = this.serverService.getVideoLanguages() + forkJoin([ + this.serverService.videoLanguagesLoaded.pipe(first()), + this.userInformationLoaded.pipe(first()) + ]).subscribe(() => { + const languages = this.serverService.getVideoLanguages() - this.languageItems = [ { label: this.i18n('Unknown language'), value: '_unknown' } ] - this.languageItems = this.languageItems - .concat(languages.map(l => ({ label: l.label, value: l.id }))) + this.languageItems = [ { label: this.i18n('Unknown language'), value: '_unknown' } ] + this.languageItems = this.languageItems + .concat(languages.map(l => ({ label: l.label, value: l.id }))) - const videoLanguages = this.user.videoLanguages - ? this.user.videoLanguages - : this.languageItems.map(l => l.value) + const videoLanguages = this.user.videoLanguages + ? this.user.videoLanguages + : this.languageItems.map(l => l.value) - this.form.patchValue({ - nsfwPolicy: this.user.nsfwPolicy, - webTorrentEnabled: this.user.webTorrentEnabled, - autoPlayVideo: this.user.autoPlayVideo === true, - videoLanguages - }) - }) + this.form.patchValue({ + nsfwPolicy: this.user.nsfwPolicy, + webTorrentEnabled: this.user.webTorrentEnabled, + autoPlayVideo: this.user.autoPlayVideo === true, + videoLanguages + }) + }) } updateDetails () { - const nsfwPolicy = this.form.value['nsfwPolicy'] + const nsfwPolicy = this.form.value[ 'nsfwPolicy' ] const webTorrentEnabled = this.form.value['webTorrentEnabled'] const autoPlayVideo = this.form.value['autoPlayVideo'] diff --git a/client/src/app/+my-account/my-account.module.ts b/client/src/app/+my-account/my-account.module.ts index 571f46de9..6cf1499d3 100644 --- a/client/src/app/+my-account/my-account.module.ts +++ b/client/src/app/+my-account/my-account.module.ts @@ -37,7 +37,6 @@ import { } from '@app/+my-account/my-account-video-playlists/my-account-video-playlist-elements.component' import { DragDropModule } from '@angular/cdk/drag-drop' import { MyAccountChangeEmailComponent } from '@app/+my-account/my-account-settings/my-account-change-email' -import { MultiSelectModule } from 'primeng/multiselect' import { MyAccountInterfaceSettingsComponent } from '@app/+my-account/my-account-settings/my-account-interface' @NgModule({ @@ -48,8 +47,7 @@ import { MyAccountInterfaceSettingsComponent } from '@app/+my-account/my-account SharedModule, TableModule, InputSwitchModule, - DragDropModule, - MultiSelectModule + DragDropModule ], declarations: [ diff --git a/client/src/app/shared/shared.module.ts b/client/src/app/shared/shared.module.ts index eb57a2fff..d71f6357b 100644 --- a/client/src/app/shared/shared.module.ts +++ b/client/src/app/shared/shared.module.ts @@ -6,10 +6,8 @@ import { RouterModule } from '@angular/router' import { MarkdownTextareaComponent } from '@app/shared/forms/markdown-textarea.component' import { HelpComponent } from '@app/shared/misc/help.component' import { InfiniteScrollerDirective } from '@app/shared/video/infinite-scroller.directive' - import { BytesPipe, KeysPipe, NgPipesModule } from 'ngx-pipes' import { SharedModule as PrimeSharedModule } from 'primeng/components/common/shared' - import { AUTH_INTERCEPTOR_PROVIDER } from './auth' import { ButtonComponent } from './buttons/button.component' import { DeleteButtonComponent } from './buttons/delete-button.component' @@ -93,6 +91,7 @@ import { VideoDownloadComponent } from '@app/shared/video/modals/video-download. import { VideoReportComponent } from '@app/shared/video/modals/video-report.component' import { ClipboardModule } from 'ngx-clipboard' import { FollowService } from '@app/shared/instance/follow.service' +import { MultiSelectModule } from 'primeng/multiselect' @NgModule({ imports: [ @@ -113,7 +112,8 @@ import { FollowService } from '@app/shared/instance/follow.service' PrimeSharedModule, InputMaskModule, - NgPipesModule + NgPipesModule, + MultiSelectModule ], declarations: [ @@ -186,6 +186,7 @@ import { FollowService } from '@app/shared/instance/follow.service' InputMaskModule, BytesPipe, KeysPipe, + MultiSelectModule, LoaderComponent, SmallLoaderComponent, diff --git a/config/default.yaml b/config/default.yaml index 5a935fede..f84ecfcf9 100644 --- a/config/default.yaml +++ b/config/default.yaml @@ -238,7 +238,53 @@ instance: short_description: 'PeerTube, a federated (ActivityPub) video streaming platform using P2P (BitTorrent) directly in the web browser with WebTorrent and Angular.' description: 'Welcome to this PeerTube instance!' # Support markdown terms: 'No terms for now.' # Support markdown + code_of_conduct: '' # Supports markdown + + # Who moderates the instance? What is the policy regarding NSFW videos? Political videos? etc + moderation_information: '' # Supports markdown + + # Who is behind the instance? A single person? A non profit? + administrator: '' + + # How long do you plan to maintain this instance? + maintenance_lifetime: '' + + # How will you pay the PeerTube instance server? With you own funds? With users donations? Advertising? + business_model: '' + + # What are the main languages of your instance? To interact with your users for example + # Uncomment or add the languages you want + # List of supported languages: https://peertube.cpy.re/api/v1/videos/languages + languages: +# - en +# - es +# - fr + + # You can specify the main categories of your instance (dedicated to music, gaming or politics etc) + # Uncomment or add the category ids you want + # List of supported categories: https://peertube.cpy.re/api/v1/videos/categories + categories: +# - 1 # Music +# - 2 # Films +# - 3 # Vehicles +# - 4 # Art +# - 5 # Sports +# - 6 # Travels +# - 7 # Gaming +# - 8 # People +# - 9 # Comedy +# - 10 # Entertainment +# - 11 # News & Politics +# - 12 # How To +# - 13 # Education +# - 14 # Activism +# - 15 # Science & Technology +# - 16 # Animals +# - 17 # Kids +# - 18 # Food + default_client_route: '/videos/trending' + # Whether or not the instance is dedicated to NSFW content # Enabling it will allow other administrators to know that you are mainly federating sensitive content # Moreover, the NSFW checkbox on video upload will be automatically checked by default @@ -246,6 +292,7 @@ instance: # By default, "do_not_list" or "blur" or "display" NSFW videos # Could be overridden per user with a setting default_nsfw_policy: 'do_not_list' + customizations: javascript: '' # Directly your JavaScript code (without <script> tags). Will be eval at runtime css: '' # Directly your CSS code (without <style> tags). Will be injected at runtime diff --git a/server/controllers/api/config.ts b/server/controllers/api/config.ts index 0c52bfa7a..b5244756d 100644 --- a/server/controllers/api/config.ts +++ b/server/controllers/api/config.ts @@ -158,7 +158,16 @@ function getAbout (req: express.Request, res: express.Response) { name: CONFIG.INSTANCE.NAME, shortDescription: CONFIG.INSTANCE.SHORT_DESCRIPTION, description: CONFIG.INSTANCE.DESCRIPTION, - terms: CONFIG.INSTANCE.TERMS + terms: CONFIG.INSTANCE.TERMS, + codeOfConduct: CONFIG.INSTANCE.CODE_OF_CONDUCT, + + moderationInformation: CONFIG.INSTANCE.MODERATION_INFORMATION, + administrator: CONFIG.INSTANCE.ADMINISTRATOR, + maintenanceLifetime: CONFIG.INSTANCE.MAINTENANCE_LIFETIME, + businessModel: CONFIG.INSTANCE.BUSINESS_MODEL, + + languages: CONFIG.INSTANCE.LANGUAGES, + categories: CONFIG.INSTANCE.CATEGORIES } } @@ -221,6 +230,16 @@ function customConfig (): CustomConfig { shortDescription: CONFIG.INSTANCE.SHORT_DESCRIPTION, description: CONFIG.INSTANCE.DESCRIPTION, terms: CONFIG.INSTANCE.TERMS, + codeOfConduct: CONFIG.INSTANCE.CODE_OF_CONDUCT, + + moderationInformation: CONFIG.INSTANCE.MODERATION_INFORMATION, + administrator: CONFIG.INSTANCE.ADMINISTRATOR, + maintenanceLifetime: CONFIG.INSTANCE.MAINTENANCE_LIFETIME, + businessModel: CONFIG.INSTANCE.BUSINESS_MODEL, + + languages: CONFIG.INSTANCE.LANGUAGES, + categories: CONFIG.INSTANCE.CATEGORIES, + isNSFW: CONFIG.INSTANCE.IS_NSFW, defaultClientRoute: CONFIG.INSTANCE.DEFAULT_CLIENT_ROUTE, defaultNSFWPolicy: CONFIG.INSTANCE.DEFAULT_NSFW_POLICY, diff --git a/server/initializers/config.ts b/server/initializers/config.ts index 599f3f5ac..4e2b07e64 100644 --- a/server/initializers/config.ts +++ b/server/initializers/config.ts @@ -209,6 +209,16 @@ const CONFIG = { get SHORT_DESCRIPTION () { return config.get<string>('instance.short_description') }, get DESCRIPTION () { return config.get<string>('instance.description') }, get TERMS () { return config.get<string>('instance.terms') }, + get CODE_OF_CONDUCT () { return config.get<string>('instance.code_of_conduct') }, + + get MODERATION_INFORMATION () { return config.get<string>('instance.moderation_information') }, + get ADMINISTRATOR () { return config.get<string>('instance.administrator') }, + get MAINTENANCE_LIFETIME () { return config.get<string>('instance.maintenance_lifetime') }, + get BUSINESS_MODEL () { return config.get<string>('instance.business_model') }, + + get LANGUAGES () { return config.get<string[]>('instance.languages') || [] }, + get CATEGORIES () { return config.get<number[]>('instance.categories') || [] }, + get IS_NSFW () { return config.get<boolean>('instance.is_nsfw') }, get DEFAULT_CLIENT_ROUTE () { return config.get<string>('instance.default_client_route') }, get DEFAULT_NSFW_POLICY () { return config.get<NSFWPolicyType>('instance.default_nsfw_policy') }, diff --git a/server/tests/api/check-params/config.ts b/server/tests/api/check-params/config.ts index 1221735c5..f716dc673 100644 --- a/server/tests/api/check-params/config.ts +++ b/server/tests/api/check-params/config.ts @@ -27,6 +27,16 @@ describe('Test config API validators', function () { shortDescription: 'my short description', description: 'my super description', terms: 'my super terms', + codeOfConduct: 'my super coc', + + moderationInformation: 'my super moderation information', + administrator: 'Kuja', + maintenanceLifetime: 'forever', + businessModel: 'my super business model', + + languages: [ 'en', 'es' ], + categories: [ 1, 2 ], + isNSFW: true, defaultClientRoute: '/videos/recently-added', defaultNSFWPolicy: 'blur', diff --git a/server/tests/api/server/config.ts b/server/tests/api/server/config.ts index b2f1933d1..da75495a5 100644 --- a/server/tests/api/server/config.ts +++ b/server/tests/api/server/config.ts @@ -28,7 +28,17 @@ function checkInitialConfig (server: ServerInfo, data: CustomConfig) { 'with WebTorrent and Angular.' ) expect(data.instance.description).to.equal('Welcome to this PeerTube instance!') + expect(data.instance.terms).to.equal('No terms for now.') + expect(data.instance.codeOfConduct).to.be.empty + expect(data.instance.moderationInformation).to.be.empty + expect(data.instance.administrator).to.be.empty + expect(data.instance.maintenanceLifetime).to.be.empty + expect(data.instance.businessModel).to.be.empty + + expect(data.instance.languages).to.have.lengthOf(0) + expect(data.instance.categories).to.have.lengthOf(0) + expect(data.instance.defaultClientRoute).to.equal('/videos/trending') expect(data.instance.isNSFW).to.be.false expect(data.instance.defaultNSFWPolicy).to.equal('display') @@ -78,7 +88,17 @@ function checkUpdatedConfig (data: CustomConfig) { expect(data.instance.name).to.equal('PeerTube updated') expect(data.instance.shortDescription).to.equal('my short description') expect(data.instance.description).to.equal('my super description') + expect(data.instance.terms).to.equal('my super terms') + expect(data.instance.codeOfConduct).to.equal('my super coc') + expect(data.instance.moderationInformation).to.equal('my super moderation information') + expect(data.instance.administrator).to.equal('Kuja') + expect(data.instance.maintenanceLifetime).to.equal('forever') + expect(data.instance.businessModel).to.equal('my super business model') + + expect(data.instance.languages).to.deep.equal([ 'en', 'es' ]) + expect(data.instance.categories).to.deep.equal([ 1, 2 ]) + expect(data.instance.defaultClientRoute).to.equal('/videos/recently-added') expect(data.instance.isNSFW).to.be.true expect(data.instance.defaultNSFWPolicy).to.equal('blur') @@ -190,6 +210,16 @@ describe('Test config', function () { shortDescription: 'my short description', description: 'my super description', terms: 'my super terms', + codeOfConduct: 'my super coc', + + moderationInformation: 'my super moderation information', + administrator: 'Kuja', + maintenanceLifetime: 'forever', + businessModel: 'my super business model', + + languages: [ 'en', 'es' ], + categories: [ 1, 2 ], + defaultClientRoute: '/videos/recently-added', isNSFW: true, defaultNSFWPolicy: 'blur' as 'blur', diff --git a/shared/extra-utils/server/config.ts b/shared/extra-utils/server/config.ts index d784af9a9..785421c98 100644 --- a/shared/extra-utils/server/config.ts +++ b/shared/extra-utils/server/config.ts @@ -53,6 +53,16 @@ function updateCustomSubConfig (url: string, token: string, newConfig: DeepParti shortDescription: 'my short description', description: 'my super description', terms: 'my super terms', + codeOfConduct: 'my super coc', + + moderationInformation: 'my super moderation information', + administrator: 'Kuja', + maintenanceLifetime: 'forever', + businessModel: 'my super business model', + + languages: [ 'en', 'es' ], + categories: [ 1, 2 ], + defaultClientRoute: '/videos/recently-added', isNSFW: true, defaultNSFWPolicy: 'blur', diff --git a/shared/models/server/about.model.ts b/shared/models/server/about.model.ts index 10dff8b8f..e32ed26ee 100644 --- a/shared/models/server/about.model.ts +++ b/shared/models/server/about.model.ts @@ -4,5 +4,15 @@ export interface About { shortDescription: string description: string terms: string + + codeOfConduct: string + + moderationInformation: string + administrator: string + maintenanceLifetime: string + businessModel: string + + languages: string[] + categories: number[] } } diff --git a/shared/models/server/custom-config.model.ts b/shared/models/server/custom-config.model.ts index 1073ba32c..0c331a820 100644 --- a/shared/models/server/custom-config.model.ts +++ b/shared/models/server/custom-config.model.ts @@ -6,6 +6,16 @@ export interface CustomConfig { shortDescription: string description: string terms: string + codeOfConduct: string + + moderationInformation: string + administrator: string + maintenanceLifetime: string + businessModel: string + + languages: string[] + categories: number[] + isNSFW: boolean defaultClientRoute: string defaultNSFWPolicy: NSFWPolicyType