From: Chocobozzz Date: Wed, 31 Jan 2018 15:42:40 +0000 (+0100) Subject: Add new name/terms/description config options X-Git-Tag: v0.0.20-alpha~4 X-Git-Url: https://git.librecmc.org/?a=commitdiff_plain;h=66b16cafb380012d3eca14e524d86f2450e04069;p=oweals%2Fpeertube.git Add new name/terms/description config options --- 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 c568a43b4..0fe2aa203 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,6 +2,41 @@
+
Instance
+ +
+ + +
+ {{ formErrors.instanceName }} +
+
+ +
+ + +
+ {{ formErrors.instanceDescription }} +
+
+ +
+ + +
+ {{ formErrors.instanceTerms }} +
+
+
Cache
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 1b3522786..cd8c926f7 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 @@ -4,7 +4,13 @@ import { Router } from '@angular/router' import { ConfigService } from '@app/+admin/config/shared/config.service' import { ServerService } from '@app/core/server/server.service' import { FormReactive, USER_VIDEO_QUOTA } from '@app/shared' -import { ADMIN_EMAIL, CACHE_PREVIEWS_SIZE, SIGNUP_LIMIT, TRANSCODING_THREADS } from '@app/shared/forms/form-validators/custom-config' +import { + ADMIN_EMAIL, + CACHE_PREVIEWS_SIZE, + INSTANCE_NAME, + SIGNUP_LIMIT, + TRANSCODING_THREADS +} from '@app/shared/forms/form-validators/custom-config' import { NotificationsService } from 'angular2-notifications' import { CustomConfig } from '../../../../../../shared/models/config/custom-config.model' @@ -36,6 +42,9 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit { form: FormGroup formErrors = { + instanceName: '', + instanceDescription: '', + instanceTerms: '', cachePreviewsSize: '', signupLimit: '', adminEmail: '', @@ -43,6 +52,7 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit { transcodingThreads: '' } validationMessages = { + instanceName: INSTANCE_NAME.MESSAGES, cachePreviewsSize: CACHE_PREVIEWS_SIZE.MESSAGES, signupLimit: SIGNUP_LIMIT.MESSAGES, adminEmail: ADMIN_EMAIL.MESSAGES, @@ -65,6 +75,9 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit { buildForm () { const formGroupData = { + instanceName: [ '', INSTANCE_NAME.VALIDATORS ], + instanceDescription: [ '' ], + instanceTerms: [ '' ], cachePreviewsSize: [ '', CACHE_PREVIEWS_SIZE.VALIDATORS ], signupEnabled: [ ], signupLimit: [ '', SIGNUP_LIMIT.VALIDATORS ], @@ -109,6 +122,11 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit { formValidated () { const data = { + instance: { + name: this.form.value['instanceName'], + description: this.form.value['instanceDescription'], + terms: this.form.value['instanceTerms'] + }, cache: { previews: { size: this.form.value['cachePreviewsSize'] @@ -146,6 +164,8 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit { this.serverService.loadConfig() this.updateForm() + + this.notificationsService.success('Success', 'Configuration updated.') }, err => this.notificationsService.error('Error', err.message) @@ -154,6 +174,9 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit { private updateForm () { const data = { + instanceName: this.customConfig.instance.name, + instanceDescription: this.customConfig.instance.description, + instanceTerms: this.customConfig.instance.terms, cachePreviewsSize: this.customConfig.cache.previews.size, signupEnabled: this.customConfig.signup.enabled, signupLimit: this.customConfig.signup.limit, diff --git a/client/src/app/shared/forms/form-validators/custom-config.ts b/client/src/app/shared/forms/form-validators/custom-config.ts index 17ae0e75c..9e3fa98d8 100644 --- a/client/src/app/shared/forms/form-validators/custom-config.ts +++ b/client/src/app/shared/forms/form-validators/custom-config.ts @@ -1,5 +1,12 @@ import { Validators } from '@angular/forms' +export const INSTANCE_NAME = { + VALIDATORS: [ Validators.required ], + MESSAGES: { + 'required': 'Instance name is required.', + } +} + export const CACHE_PREVIEWS_SIZE = { VALIDATORS: [ Validators.required, Validators.min(1), Validators.pattern('[0-9]+') ], MESSAGES: { diff --git a/client/src/app/shared/forms/markdown-textarea.component.html b/client/src/app/shared/forms/markdown-textarea.component.html new file mode 100644 index 000000000..d2d4cf95c --- /dev/null +++ b/client/src/app/shared/forms/markdown-textarea.component.html @@ -0,0 +1,12 @@ +
+ + + + + + +
diff --git a/client/src/app/shared/forms/markdown-textarea.component.scss b/client/src/app/shared/forms/markdown-textarea.component.scss new file mode 100644 index 000000000..82aff541d --- /dev/null +++ b/client/src/app/shared/forms/markdown-textarea.component.scss @@ -0,0 +1,27 @@ +@import '_variables'; +@import '_mixins'; + +.root { + display: flex; + + textarea { + @include peertube-textarea(100%, 150px); + + margin-bottom: 15px; + } + + /deep/ { + .nav-link { + display: flex !important; + align-items: center; + height: 30px !important; + padding: 0 15px !important; + } + + .tab-content { + min-height: 75px; + padding: 15px; + font-size: 15px; + } + } +} diff --git a/client/src/app/shared/forms/markdown-textarea.component.ts b/client/src/app/shared/forms/markdown-textarea.component.ts new file mode 100644 index 000000000..20f13b28c --- /dev/null +++ b/client/src/app/shared/forms/markdown-textarea.component.ts @@ -0,0 +1,86 @@ +import { Component, forwardRef, Input, OnInit } from '@angular/core' +import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms' +import 'rxjs/add/operator/debounceTime' +import 'rxjs/add/operator/distinctUntilChanged' +import { isInMobileView } from '@app/shared/misc/utils' +import { MarkdownService } from '@app/videos/shared' +import { Subject } from 'rxjs/Subject' +import truncate from 'lodash-es/truncate' + +@Component({ + selector: 'my-markdown-textarea', + templateUrl: './markdown-textarea.component.html', + styleUrls: [ './markdown-textarea.component.scss' ], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => MarkdownTextareaComponent), + multi: true + } + ] +}) + +export class MarkdownTextareaComponent implements ControlValueAccessor, OnInit { + @Input() description = '' + @Input() classes: string[] = [] + @Input() textareaWidth = '100%' + @Input() textareaHeight = '150px' + @Input() previewColumn = false + @Input() truncate: number + + textareaMarginRight = '0' + flexDirection = 'column' + truncatedDescriptionHTML = '' + descriptionHTML = '' + + private descriptionChanged = new Subject() + + constructor (private markdownService: MarkdownService) {} + + ngOnInit () { + this.descriptionChanged + .debounceTime(150) + .distinctUntilChanged() + .subscribe(() => this.updateDescriptionPreviews()) + + this.descriptionChanged.next(this.description) + + if (this.previewColumn) { + this.flexDirection = 'row' + this.textareaMarginRight = '15px' + } + } + + propagateChange = (_: any) => { /* empty */ } + + writeValue (description: string) { + this.description = description + + this.descriptionChanged.next(this.description) + } + + registerOnChange (fn: (_: any) => void) { + this.propagateChange = fn + } + + registerOnTouched () { + // Unused + } + + onModelChange () { + this.propagateChange(this.description) + + this.descriptionChanged.next(this.description) + } + + arePreviewsDisplayed () { + return isInMobileView() === false + } + + private updateDescriptionPreviews () { + if (this.description === null || this.description === undefined) return + + this.truncatedDescriptionHTML = this.markdownService.markdownToHTML(truncate(this.description, { length: this.truncate })) + this.descriptionHTML = this.markdownService.markdownToHTML(this.description) + } +} diff --git a/client/src/app/shared/shared.module.ts b/client/src/app/shared/shared.module.ts index a5c56cb46..d8f98bdf6 100644 --- a/client/src/app/shared/shared.module.ts +++ b/client/src/app/shared/shared.module.ts @@ -3,10 +3,13 @@ import { HttpClientModule } from '@angular/common/http' import { NgModule } from '@angular/core' import { FormsModule, ReactiveFormsModule } from '@angular/forms' import { RouterModule } from '@angular/router' +import { MarkdownTextareaComponent } from '@app/shared/forms/markdown-textarea.component' +import { MarkdownService } from '@app/videos/shared' import { LoadingBarHttpClientModule } from '@ngx-loading-bar/http-client' import { BsDropdownModule } from 'ngx-bootstrap/dropdown' import { ModalModule } from 'ngx-bootstrap/modal' +import { TabsModule } from 'ngx-bootstrap/tabs' import { InfiniteScrollModule } from 'ngx-infinite-scroll' import { BytesPipe, KeysPipe, NgPipesModule } from 'ngx-pipes' import { SharedModule as PrimeSharedModule } from 'primeng/components/common/shared' @@ -40,7 +43,8 @@ import { VideoService } from './video/video.service' PrimeSharedModule, InfiniteScrollModule, - NgPipesModule + NgPipesModule, + TabsModule.forRoot() ], declarations: [ @@ -50,7 +54,8 @@ import { VideoService } from './video/video.service' DeleteButtonComponent, EditButtonComponent, NumberFormatterPipe, - FromNowPipe + FromNowPipe, + MarkdownTextareaComponent ], exports: [ @@ -74,6 +79,7 @@ import { VideoService } from './video/video.service' VideoMiniatureComponent, DeleteButtonComponent, EditButtonComponent, + MarkdownTextareaComponent, NumberFormatterPipe, FromNowPipe @@ -86,7 +92,8 @@ import { VideoService } from './video/video.service' VideoAbuseService, VideoBlacklistService, UserService, - VideoService + VideoService, + MarkdownService ] }) export class SharedModule { } diff --git a/client/src/app/shared/video/video.service.ts b/client/src/app/shared/video/video.service.ts index 50761ca0c..d4f5e258f 100644 --- a/client/src/app/shared/video/video.service.ts +++ b/client/src/app/shared/video/video.service.ts @@ -42,10 +42,10 @@ export class VideoService { } updateVideo (video: VideoEdit) { - const language = video.language || undefined - const licence = video.licence || undefined - const category = video.category || undefined - const description = video.description || undefined + const language = video.language || null + const licence = video.licence || null + const category = video.category || null + const description = video.description || null const body: VideoUpdate = { name: video.name, diff --git a/client/src/app/videos/+video-edit/shared/video-description.component.html b/client/src/app/videos/+video-edit/shared/video-description.component.html deleted file mode 100644 index 989292c47..000000000 --- a/client/src/app/videos/+video-edit/shared/video-description.component.html +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - diff --git a/client/src/app/videos/+video-edit/shared/video-description.component.scss b/client/src/app/videos/+video-edit/shared/video-description.component.scss deleted file mode 100644 index 2c731bee3..000000000 --- a/client/src/app/videos/+video-edit/shared/video-description.component.scss +++ /dev/null @@ -1,24 +0,0 @@ -@import '_variables'; -@import '_mixins'; - -textarea { - @include peertube-textarea(100%, 150px); - - margin-bottom: 15px; -} - -/deep/ { - .nav-link { - display: flex !important; - align-items: center; - height: 30px !important; - padding: 0 15px !important; - } - - .tab-content { - min-height: 75px; - padding: 15px; - font-size: 15px; - } -} - diff --git a/client/src/app/videos/+video-edit/shared/video-description.component.ts b/client/src/app/videos/+video-edit/shared/video-description.component.ts deleted file mode 100644 index eba345412..000000000 --- a/client/src/app/videos/+video-edit/shared/video-description.component.ts +++ /dev/null @@ -1,73 +0,0 @@ -import { Component, forwardRef, Input, OnInit } from '@angular/core' -import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms' -import 'rxjs/add/operator/debounceTime' -import 'rxjs/add/operator/distinctUntilChanged' -import { isInMobileView } from '@app/shared/misc/utils' -import { Subject } from 'rxjs/Subject' -import { MarkdownService } from '../../shared' -import truncate from 'lodash-es/truncate' - -@Component({ - selector: 'my-video-description', - templateUrl: './video-description.component.html', - styleUrls: [ './video-description.component.scss' ], - providers: [ - { - provide: NG_VALUE_ACCESSOR, - useExisting: forwardRef(() => VideoDescriptionComponent), - multi: true - } - ] -}) - -export class VideoDescriptionComponent implements ControlValueAccessor, OnInit { - @Input() description = '' - truncatedDescriptionHTML = '' - descriptionHTML = '' - - private descriptionChanged = new Subject() - - constructor (private markdownService: MarkdownService) {} - - ngOnInit () { - this.descriptionChanged - .debounceTime(150) - .distinctUntilChanged() - .subscribe(() => this.updateDescriptionPreviews()) - - this.descriptionChanged.next(this.description) - } - - propagateChange = (_: any) => { /* empty */ } - - writeValue (description: string) { - this.description = description - - this.descriptionChanged.next(this.description) - } - - registerOnChange (fn: (_: any) => void) { - this.propagateChange = fn - } - - registerOnTouched () { - // Unused - } - - onModelChange () { - this.propagateChange(this.description) - - this.descriptionChanged.next(this.description) - } - - arePreviewsDisplayed () { - return isInMobileView() === false - } - - private updateDescriptionPreviews () { - if (!this.description) return - - this.truncatedDescriptionHTML = this.markdownService.markdownToHTML(truncate(this.description, { length: 250 })) - this.descriptionHTML = this.markdownService.markdownToHTML(this.description) - } -} diff --git a/client/src/app/videos/+video-edit/shared/video-edit.component.html b/client/src/app/videos/+video-edit/shared/video-edit.component.html index 80377933e..d031825bd 100644 --- a/client/src/app/videos/+video-edit/shared/video-edit.component.html +++ b/client/src/app/videos/+video-edit/shared/video-edit.component.html @@ -12,14 +12,14 @@
(press Enter to add)
- +
{{ formErrors.description }} diff --git a/client/src/app/videos/+video-edit/shared/video-edit.module.ts b/client/src/app/videos/+video-edit/shared/video-edit.module.ts index ce106d82f..098a71ae6 100644 --- a/client/src/app/videos/+video-edit/shared/video-edit.module.ts +++ b/client/src/app/videos/+video-edit/shared/video-edit.module.ts @@ -1,23 +1,17 @@ import { NgModule } from '@angular/core' - -import { TagInputModule } from 'ngx-chips' import { TabsModule } from 'ngx-bootstrap/tabs' - -import { MarkdownService } from '../../shared' +import { TagInputModule } from 'ngx-chips' import { SharedModule } from '../../../shared' -import { VideoDescriptionComponent } from './video-description.component' import { VideoEditComponent } from './video-edit.component' @NgModule({ imports: [ TagInputModule, - TabsModule.forRoot(), SharedModule ], declarations: [ - VideoDescriptionComponent, VideoEditComponent ], @@ -25,12 +19,9 @@ import { VideoEditComponent } from './video-edit.component' TagInputModule, TabsModule, - VideoDescriptionComponent, VideoEditComponent ], - providers: [ - MarkdownService - ] + providers: [] }) export class VideoEditModule { } diff --git a/client/src/app/videos/shared/markdown.service.ts b/client/src/app/videos/shared/markdown.service.ts index 82745f0c6..fd0330f9b 100644 --- a/client/src/app/videos/shared/markdown.service.ts +++ b/client/src/app/videos/shared/markdown.service.ts @@ -14,6 +14,17 @@ export class MarkdownService { .enable('link') .enable('newline') + this.setTargetToLinks() + } + + markdownToHTML (markdown: string) { + const html = this.markdownIt.render(markdown) + + // Avoid linkify truncated links + return html.replace(/]+>([^<]+)<\/a>\s*...(<\/p>)?$/mi, '$1...') + } + + private setTargetToLinks () { // Snippet from markdown-it documentation: https://github.com/markdown-it/markdown-it/blob/master/docs/architecture.md#renderer const defaultRender = this.markdownIt.renderer.rules.link_open || function (tokens, idx, options, env, self) { return self.renderToken(tokens, idx, options) @@ -33,11 +44,4 @@ export class MarkdownService { return defaultRender(tokens, idx, options, env, self) } } - - markdownToHTML (markdown: string) { - const html = this.markdownIt.render(markdown) - - // Avoid linkify truncated links - return html.replace(/]+>([^<]+)<\/a>\s*...(<\/p>)?$/mi, '$1...') - } } diff --git a/config/default.yaml b/config/default.yaml index 691c9e00b..6c73c5fea 100644 --- a/config/default.yaml +++ b/config/default.yaml @@ -69,3 +69,8 @@ transcoding: 480p: true 720p: true 1080p: true + +instance: + name: 'PeerTube' + description: '' # Support markdown + terms: '' # Support markdown diff --git a/config/production.yaml.example b/config/production.yaml.example index 04354b75d..e476bbcfa 100644 --- a/config/production.yaml.example +++ b/config/production.yaml.example @@ -69,3 +69,8 @@ transcoding: 480p: true 720p: true 1080p: true + +instance: + name: 'PeerTube' + description: '' # Support markdown + terms: '' # Support markdown diff --git a/server/controllers/api/config.ts b/server/controllers/api/config.ts index bc804d9ba..e4cb02820 100644 --- a/server/controllers/api/config.ts +++ b/server/controllers/api/config.ts @@ -105,6 +105,11 @@ export { function customConfig (): CustomConfig { return { + instance: { + name: CONFIG.INSTANCE.NAME, + description: CONFIG.INSTANCE.DESCRIPTION, + terms: CONFIG.INSTANCE.TERMS + }, cache: { previews: { size: CONFIG.CACHE.PREVIEWS.SIZE diff --git a/server/initializers/checker.ts b/server/initializers/checker.ts index d550fd23f..e5cc1b7be 100644 --- a/server/initializers/checker.ts +++ b/server/initializers/checker.ts @@ -23,7 +23,8 @@ function checkMissedConfig () { 'database.hostname', 'database.port', 'database.suffix', 'database.username', 'database.password', 'storage.videos', 'storage.logs', 'storage.thumbnails', 'storage.previews', 'storage.torrents', 'storage.cache', 'log.level', 'cache.previews.size', 'admin.email', 'signup.enabled', 'signup.limit', 'transcoding.enabled', 'transcoding.threads', - 'user.video_quota', 'smtp.hostname', 'smtp.port', 'smtp.username', 'smtp.password', 'smtp.tls', 'smtp.from_address' + 'user.video_quota', 'smtp.hostname', 'smtp.port', 'smtp.username', 'smtp.password', 'smtp.tls', 'smtp.from_address', + 'instance.name', 'instance.description', 'instance.terms' ] const miss: string[] = [] diff --git a/server/initializers/constants.ts b/server/initializers/constants.ts index e7b1656e2..e531c4c39 100644 --- a/server/initializers/constants.ts +++ b/server/initializers/constants.ts @@ -154,6 +154,11 @@ const CONFIG = { PREVIEWS: { get SIZE () { return config.get('cache.previews.size') } } + }, + INSTANCE: { + get NAME () { return config.get('instance.name') }, + get DESCRIPTION () { return config.get('instance.description') }, + get TERMS () { return config.get('instance.terms') } } } diff --git a/server/tests/api/check-params/config.ts b/server/tests/api/check-params/config.ts index a2a404702..efc1e4e09 100644 --- a/server/tests/api/check-params/config.ts +++ b/server/tests/api/check-params/config.ts @@ -14,6 +14,11 @@ describe('Test config API validators', function () { let server: ServerInfo let userAccessToken: string const updateParams: CustomConfig = { + instance: { + name: 'PeerTube updated', + description: 'my super description', + terms: 'my super terms' + }, cache: { previews: { size: 2 diff --git a/server/tests/api/server/config.ts b/server/tests/api/server/config.ts index a1f8212bb..f83e21e82 100644 --- a/server/tests/api/server/config.ts +++ b/server/tests/api/server/config.ts @@ -49,6 +49,9 @@ describe('Test config', function () { const res = await getCustomConfig(server.url, server.accessToken) const data = res.body + expect(data.instance.name).to.equal('PeerTube') + expect(data.instance.description).to.be.empty + expect(data.instance.terms).to.be.empty expect(data.cache.previews.size).to.equal(1) expect(data.signup.enabled).to.be.true expect(data.signup.limit).to.equal(4) @@ -65,6 +68,11 @@ describe('Test config', function () { it('Should update the customized configuration', async function () { const newCustomConfig = { + instance: { + name: 'PeerTube updated', + description: 'my super description', + terms: 'my super terms' + }, cache: { previews: { size: 2 @@ -97,7 +105,9 @@ describe('Test config', function () { const res = await getCustomConfig(server.url, server.accessToken) const data = res.body - expect(data.cache.previews.size).to.equal(2) + expect(data.instance.name).to.equal('PeerTube updated') + expect(data.instance.description).to.equal('my super description') + expect(data.instance.terms).to.equal('my super terms') expect(data.signup.enabled).to.be.false expect(data.signup.limit).to.equal(5) expect(data.admin.email).to.equal('superadmin1@example.com') diff --git a/server/tests/utils/videos/videos.ts b/server/tests/utils/videos/videos.ts index 860f04fd8..0b28edd48 100644 --- a/server/tests/utils/videos/videos.ts +++ b/server/tests/utils/videos/videos.ts @@ -373,7 +373,7 @@ async function completeVideoCheck ( expect(dateIsValid(video.createdAt)).to.be.true expect(dateIsValid(video.updatedAt)).to.be.true - const res = await getVideo(url, video.id) + const res = await getVideo(url, video.uuid) const videoDetails = res.body expect(videoDetails.files).to.have.lengthOf(attributes.files.length) diff --git a/shared/models/config/custom-config.model.ts b/shared/models/config/custom-config.model.ts index 73b5b6a72..6ef0fc5a2 100644 --- a/shared/models/config/custom-config.model.ts +++ b/shared/models/config/custom-config.model.ts @@ -1,4 +1,10 @@ export interface CustomConfig { + instance: { + name: string + description: string + terms: string + } + cache: { previews: { size: number