aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.html35
-rw-r--r--client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.ts25
-rw-r--r--client/src/app/shared/forms/form-validators/custom-config.ts7
-rw-r--r--client/src/app/shared/forms/markdown-textarea.component.html12
-rw-r--r--client/src/app/shared/forms/markdown-textarea.component.scss27
-rw-r--r--client/src/app/shared/forms/markdown-textarea.component.ts (renamed from client/src/app/videos/+video-edit/shared/video-description.component.ts)29
-rw-r--r--client/src/app/shared/shared.module.ts13
-rw-r--r--client/src/app/shared/video/video.service.ts8
-rw-r--r--client/src/app/videos/+video-edit/shared/video-description.component.html9
-rw-r--r--client/src/app/videos/+video-edit/shared/video-description.component.scss24
-rw-r--r--client/src/app/videos/+video-edit/shared/video-edit.component.html6
-rw-r--r--client/src/app/videos/+video-edit/shared/video-edit.module.ts13
-rw-r--r--client/src/app/videos/shared/markdown.service.ts18
-rw-r--r--config/default.yaml5
-rw-r--r--config/production.yaml.example5
-rw-r--r--server/controllers/api/config.ts5
-rw-r--r--server/initializers/checker.ts3
-rw-r--r--server/initializers/constants.ts5
-rw-r--r--server/tests/api/check-params/config.ts5
-rw-r--r--server/tests/api/server/config.ts12
-rw-r--r--server/tests/utils/videos/videos.ts2
-rw-r--r--shared/models/config/custom-config.model.ts6
22 files changed, 201 insertions, 73 deletions
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 @@
2 2
3<form role="form" (ngSubmit)="formValidated()" [formGroup]="form"> 3<form role="form" (ngSubmit)="formValidated()" [formGroup]="form">
4 4
5 <div class="inner-form-title">Instance</div>
6
7 <div class="form-group">
8 <label for="instanceName">Name</label>
9 <input
10 type="text" id="instanceName"
11 formControlName="instanceName" [ngClass]="{ 'input-error': formErrors['instanceName'] }"
12 >
13 <div *ngIf="formErrors.instanceName" class="form-error">
14 {{ formErrors.instanceName }}
15 </div>
16 </div>
17
18 <div class="form-group">
19 <label for="instanceDescription">Description (markdown)</label>
20 <my-markdown-textarea
21 id="instanceDescription" formControlName="instanceDescription" textareaWidth="500px" [previewColumn]="true"
22 [classes]="{ 'input-error': formErrors['instanceDescription'] }"
23 ></my-markdown-textarea>
24 <div *ngIf="formErrors.instanceDescription" class="form-error">
25 {{ formErrors.instanceDescription }}
26 </div>
27 </div>
28
29 <div class="form-group">
30 <label for="instanceTerms">Terms (markdown)</label>
31 <my-markdown-textarea
32 id="instanceTerms" formControlName="instanceTerms" textareaWidth="500px" [previewColumn]="true"
33 [ngClass]="{ 'input-error': formErrors['instanceTerms'] }"
34 ></my-markdown-textarea>
35 <div *ngIf="formErrors.instanceTerms" class="form-error">
36 {{ formErrors.instanceTerms }}
37 </div>
38 </div>
39
5 <div class="inner-form-title">Cache</div> 40 <div class="inner-form-title">Cache</div>
6 41
7 <div class="form-group"> 42 <div class="form-group">
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'
4import { ConfigService } from '@app/+admin/config/shared/config.service' 4import { ConfigService } from '@app/+admin/config/shared/config.service'
5import { ServerService } from '@app/core/server/server.service' 5import { ServerService } from '@app/core/server/server.service'
6import { FormReactive, USER_VIDEO_QUOTA } from '@app/shared' 6import { FormReactive, USER_VIDEO_QUOTA } from '@app/shared'
7import { ADMIN_EMAIL, CACHE_PREVIEWS_SIZE, SIGNUP_LIMIT, TRANSCODING_THREADS } from '@app/shared/forms/form-validators/custom-config' 7import {
8 ADMIN_EMAIL,
9 CACHE_PREVIEWS_SIZE,
10 INSTANCE_NAME,
11 SIGNUP_LIMIT,
12 TRANSCODING_THREADS
13} from '@app/shared/forms/form-validators/custom-config'
8import { NotificationsService } from 'angular2-notifications' 14import { NotificationsService } from 'angular2-notifications'
9import { CustomConfig } from '../../../../../../shared/models/config/custom-config.model' 15import { CustomConfig } from '../../../../../../shared/models/config/custom-config.model'
10 16
@@ -36,6 +42,9 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit {
36 42
37 form: FormGroup 43 form: FormGroup
38 formErrors = { 44 formErrors = {
45 instanceName: '',
46 instanceDescription: '',
47 instanceTerms: '',
39 cachePreviewsSize: '', 48 cachePreviewsSize: '',
40 signupLimit: '', 49 signupLimit: '',
41 adminEmail: '', 50 adminEmail: '',
@@ -43,6 +52,7 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit {
43 transcodingThreads: '' 52 transcodingThreads: ''
44 } 53 }
45 validationMessages = { 54 validationMessages = {
55 instanceName: INSTANCE_NAME.MESSAGES,
46 cachePreviewsSize: CACHE_PREVIEWS_SIZE.MESSAGES, 56 cachePreviewsSize: CACHE_PREVIEWS_SIZE.MESSAGES,
47 signupLimit: SIGNUP_LIMIT.MESSAGES, 57 signupLimit: SIGNUP_LIMIT.MESSAGES,
48 adminEmail: ADMIN_EMAIL.MESSAGES, 58 adminEmail: ADMIN_EMAIL.MESSAGES,
@@ -65,6 +75,9 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit {
65 75
66 buildForm () { 76 buildForm () {
67 const formGroupData = { 77 const formGroupData = {
78 instanceName: [ '', INSTANCE_NAME.VALIDATORS ],
79 instanceDescription: [ '' ],
80 instanceTerms: [ '' ],
68 cachePreviewsSize: [ '', CACHE_PREVIEWS_SIZE.VALIDATORS ], 81 cachePreviewsSize: [ '', CACHE_PREVIEWS_SIZE.VALIDATORS ],
69 signupEnabled: [ ], 82 signupEnabled: [ ],
70 signupLimit: [ '', SIGNUP_LIMIT.VALIDATORS ], 83 signupLimit: [ '', SIGNUP_LIMIT.VALIDATORS ],
@@ -109,6 +122,11 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit {
109 122
110 formValidated () { 123 formValidated () {
111 const data = { 124 const data = {
125 instance: {
126 name: this.form.value['instanceName'],
127 description: this.form.value['instanceDescription'],
128 terms: this.form.value['instanceTerms']
129 },
112 cache: { 130 cache: {
113 previews: { 131 previews: {
114 size: this.form.value['cachePreviewsSize'] 132 size: this.form.value['cachePreviewsSize']
@@ -146,6 +164,8 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit {
146 this.serverService.loadConfig() 164 this.serverService.loadConfig()
147 165
148 this.updateForm() 166 this.updateForm()
167
168 this.notificationsService.success('Success', 'Configuration updated.')
149 }, 169 },
150 170
151 err => this.notificationsService.error('Error', err.message) 171 err => this.notificationsService.error('Error', err.message)
@@ -154,6 +174,9 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit {
154 174
155 private updateForm () { 175 private updateForm () {
156 const data = { 176 const data = {
177 instanceName: this.customConfig.instance.name,
178 instanceDescription: this.customConfig.instance.description,
179 instanceTerms: this.customConfig.instance.terms,
157 cachePreviewsSize: this.customConfig.cache.previews.size, 180 cachePreviewsSize: this.customConfig.cache.previews.size,
158 signupEnabled: this.customConfig.signup.enabled, 181 signupEnabled: this.customConfig.signup.enabled,
159 signupLimit: this.customConfig.signup.limit, 182 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 @@
1import { Validators } from '@angular/forms' 1import { Validators } from '@angular/forms'
2 2
3export const INSTANCE_NAME = {
4 VALIDATORS: [ Validators.required ],
5 MESSAGES: {
6 'required': 'Instance name is required.',
7 }
8}
9
3export const CACHE_PREVIEWS_SIZE = { 10export const CACHE_PREVIEWS_SIZE = {
4 VALIDATORS: [ Validators.required, Validators.min(1), Validators.pattern('[0-9]+') ], 11 VALIDATORS: [ Validators.required, Validators.min(1), Validators.pattern('[0-9]+') ],
5 MESSAGES: { 12 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 @@
1<div class="root" [ngStyle]="{ 'flex-direction': flexDirection }">
2 <textarea
3 [(ngModel)]="description" (ngModelChange)="onModelChange()"
4 [ngClass]="classes" [ngStyle]="{ width: textareaWidth, height: textareaHeight, 'margin-right': textareaMarginRight }"
5 id="description" name="description">
6 </textarea>
7
8 <tabset *ngIf="arePreviewsDisplayed()" #staticTabs class="previews">
9 <tab *ngIf="truncate !== undefined" heading="Truncated description preview" [innerHTML]="truncatedDescriptionHTML"></tab>
10 <tab heading="Complete description preview" [innerHTML]="descriptionHTML"></tab>
11 </tabset>
12</div>
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 @@
1@import '_variables';
2@import '_mixins';
3
4.root {
5 display: flex;
6
7 textarea {
8 @include peertube-textarea(100%, 150px);
9
10 margin-bottom: 15px;
11 }
12
13 /deep/ {
14 .nav-link {
15 display: flex !important;
16 align-items: center;
17 height: 30px !important;
18 padding: 0 15px !important;
19 }
20
21 .tab-content {
22 min-height: 75px;
23 padding: 15px;
24 font-size: 15px;
25 }
26 }
27}
diff --git a/client/src/app/videos/+video-edit/shared/video-description.component.ts b/client/src/app/shared/forms/markdown-textarea.component.ts
index eba345412..20f13b28c 100644
--- a/client/src/app/videos/+video-edit/shared/video-description.component.ts
+++ b/client/src/app/shared/forms/markdown-textarea.component.ts
@@ -3,25 +3,33 @@ import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'
3import 'rxjs/add/operator/debounceTime' 3import 'rxjs/add/operator/debounceTime'
4import 'rxjs/add/operator/distinctUntilChanged' 4import 'rxjs/add/operator/distinctUntilChanged'
5import { isInMobileView } from '@app/shared/misc/utils' 5import { isInMobileView } from '@app/shared/misc/utils'
6import { MarkdownService } from '@app/videos/shared'
6import { Subject } from 'rxjs/Subject' 7import { Subject } from 'rxjs/Subject'
7import { MarkdownService } from '../../shared'
8import truncate from 'lodash-es/truncate' 8import truncate from 'lodash-es/truncate'
9 9
10@Component({ 10@Component({
11 selector: 'my-video-description', 11 selector: 'my-markdown-textarea',
12 templateUrl: './video-description.component.html', 12 templateUrl: './markdown-textarea.component.html',
13 styleUrls: [ './video-description.component.scss' ], 13 styleUrls: [ './markdown-textarea.component.scss' ],
14 providers: [ 14 providers: [
15 { 15 {
16 provide: NG_VALUE_ACCESSOR, 16 provide: NG_VALUE_ACCESSOR,
17 useExisting: forwardRef(() => VideoDescriptionComponent), 17 useExisting: forwardRef(() => MarkdownTextareaComponent),
18 multi: true 18 multi: true
19 } 19 }
20 ] 20 ]
21}) 21})
22 22
23export class VideoDescriptionComponent implements ControlValueAccessor, OnInit { 23export class MarkdownTextareaComponent implements ControlValueAccessor, OnInit {
24 @Input() description = '' 24 @Input() description = ''
25 @Input() classes: string[] = []
26 @Input() textareaWidth = '100%'
27 @Input() textareaHeight = '150px'
28 @Input() previewColumn = false
29 @Input() truncate: number
30
31 textareaMarginRight = '0'
32 flexDirection = 'column'
25 truncatedDescriptionHTML = '' 33 truncatedDescriptionHTML = ''
26 descriptionHTML = '' 34 descriptionHTML = ''
27 35
@@ -36,6 +44,11 @@ export class VideoDescriptionComponent implements ControlValueAccessor, OnInit {
36 .subscribe(() => this.updateDescriptionPreviews()) 44 .subscribe(() => this.updateDescriptionPreviews())
37 45
38 this.descriptionChanged.next(this.description) 46 this.descriptionChanged.next(this.description)
47
48 if (this.previewColumn) {
49 this.flexDirection = 'row'
50 this.textareaMarginRight = '15px'
51 }
39 } 52 }
40 53
41 propagateChange = (_: any) => { /* empty */ } 54 propagateChange = (_: any) => { /* empty */ }
@@ -65,9 +78,9 @@ export class VideoDescriptionComponent implements ControlValueAccessor, OnInit {
65 } 78 }
66 79
67 private updateDescriptionPreviews () { 80 private updateDescriptionPreviews () {
68 if (!this.description) return 81 if (this.description === null || this.description === undefined) return
69 82
70 this.truncatedDescriptionHTML = this.markdownService.markdownToHTML(truncate(this.description, { length: 250 })) 83 this.truncatedDescriptionHTML = this.markdownService.markdownToHTML(truncate(this.description, { length: this.truncate }))
71 this.descriptionHTML = this.markdownService.markdownToHTML(this.description) 84 this.descriptionHTML = this.markdownService.markdownToHTML(this.description)
72 } 85 }
73} 86}
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'
3import { NgModule } from '@angular/core' 3import { NgModule } from '@angular/core'
4import { FormsModule, ReactiveFormsModule } from '@angular/forms' 4import { FormsModule, ReactiveFormsModule } from '@angular/forms'
5import { RouterModule } from '@angular/router' 5import { RouterModule } from '@angular/router'
6import { MarkdownTextareaComponent } from '@app/shared/forms/markdown-textarea.component'
7import { MarkdownService } from '@app/videos/shared'
6import { LoadingBarHttpClientModule } from '@ngx-loading-bar/http-client' 8import { LoadingBarHttpClientModule } from '@ngx-loading-bar/http-client'
7 9
8import { BsDropdownModule } from 'ngx-bootstrap/dropdown' 10import { BsDropdownModule } from 'ngx-bootstrap/dropdown'
9import { ModalModule } from 'ngx-bootstrap/modal' 11import { ModalModule } from 'ngx-bootstrap/modal'
12import { TabsModule } from 'ngx-bootstrap/tabs'
10import { InfiniteScrollModule } from 'ngx-infinite-scroll' 13import { InfiniteScrollModule } from 'ngx-infinite-scroll'
11import { BytesPipe, KeysPipe, NgPipesModule } from 'ngx-pipes' 14import { BytesPipe, KeysPipe, NgPipesModule } from 'ngx-pipes'
12import { SharedModule as PrimeSharedModule } from 'primeng/components/common/shared' 15import { SharedModule as PrimeSharedModule } from 'primeng/components/common/shared'
@@ -40,7 +43,8 @@ import { VideoService } from './video/video.service'
40 43
41 PrimeSharedModule, 44 PrimeSharedModule,
42 InfiniteScrollModule, 45 InfiniteScrollModule,
43 NgPipesModule 46 NgPipesModule,
47 TabsModule.forRoot()
44 ], 48 ],
45 49
46 declarations: [ 50 declarations: [
@@ -50,7 +54,8 @@ import { VideoService } from './video/video.service'
50 DeleteButtonComponent, 54 DeleteButtonComponent,
51 EditButtonComponent, 55 EditButtonComponent,
52 NumberFormatterPipe, 56 NumberFormatterPipe,
53 FromNowPipe 57 FromNowPipe,
58 MarkdownTextareaComponent
54 ], 59 ],
55 60
56 exports: [ 61 exports: [
@@ -74,6 +79,7 @@ import { VideoService } from './video/video.service'
74 VideoMiniatureComponent, 79 VideoMiniatureComponent,
75 DeleteButtonComponent, 80 DeleteButtonComponent,
76 EditButtonComponent, 81 EditButtonComponent,
82 MarkdownTextareaComponent,
77 83
78 NumberFormatterPipe, 84 NumberFormatterPipe,
79 FromNowPipe 85 FromNowPipe
@@ -86,7 +92,8 @@ import { VideoService } from './video/video.service'
86 VideoAbuseService, 92 VideoAbuseService,
87 VideoBlacklistService, 93 VideoBlacklistService,
88 UserService, 94 UserService,
89 VideoService 95 VideoService,
96 MarkdownService
90 ] 97 ]
91}) 98})
92export class SharedModule { } 99export 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 {
42 } 42 }
43 43
44 updateVideo (video: VideoEdit) { 44 updateVideo (video: VideoEdit) {
45 const language = video.language || undefined 45 const language = video.language || null
46 const licence = video.licence || undefined 46 const licence = video.licence || null
47 const category = video.category || undefined 47 const category = video.category || null
48 const description = video.description || undefined 48 const description = video.description || null
49 49
50 const body: VideoUpdate = { 50 const body: VideoUpdate = {
51 name: video.name, 51 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 @@
1<textarea
2 [(ngModel)]="description" (ngModelChange)="onModelChange()"
3 id="description" name="description">
4</textarea>
5
6<tabset *ngIf="arePreviewsDisplayed()" #staticTabs class="previews">
7 <tab heading="Truncated description preview" [innerHTML]="truncatedDescriptionHTML"></tab>
8 <tab heading="Complete description preview" [innerHTML]="descriptionHTML"></tab>
9</tabset>
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 @@
1@import '_variables';
2@import '_mixins';
3
4textarea {
5 @include peertube-textarea(100%, 150px);
6
7 margin-bottom: 15px;
8}
9
10/deep/ {
11 .nav-link {
12 display: flex !important;
13 align-items: center;
14 height: 30px !important;
15 padding: 0 15px !important;
16 }
17
18 .tab-content {
19 min-height: 75px;
20 padding: 15px;
21 font-size: 15px;
22 }
23}
24
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 @@
12 <div class="form-group"> 12 <div class="form-group">
13 <label class="label-tags">Tags</label> <span>(press Enter to add)</span> 13 <label class="label-tags">Tags</label> <span>(press Enter to add)</span>
14 <tag-input 14 <tag-input
15 [ngModel]="tags" [validators]="tagValidators" [errorMessages]="tagValidatorsMessages" 15 [ngModel]="tags" [validators]="tagValidators" [errorMessages]="tagValidatorsMessages"
16 formControlName="tags" maxItems="5" modelAsStrings="true" 16 formControlName="tags" maxItems="5" modelAsStrings="true"
17 ></tag-input> 17 ></tag-input>
18 </div> 18 </div>
19 19
20 <div class="form-group"> 20 <div class="form-group">
21 <label for="description">Description</label> 21 <label for="description">Description</label>
22 <my-video-description formControlName="description"></my-video-description> 22 <my-markdown-textarea truncate="250" formControlName="description"></my-markdown-textarea>
23 23
24 <div *ngIf="formErrors.description" class="form-error"> 24 <div *ngIf="formErrors.description" class="form-error">
25 {{ formErrors.description }} 25 {{ 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 @@
1import { NgModule } from '@angular/core' 1import { NgModule } from '@angular/core'
2
3import { TagInputModule } from 'ngx-chips'
4import { TabsModule } from 'ngx-bootstrap/tabs' 2import { TabsModule } from 'ngx-bootstrap/tabs'
5 3import { TagInputModule } from 'ngx-chips'
6import { MarkdownService } from '../../shared'
7import { SharedModule } from '../../../shared' 4import { SharedModule } from '../../../shared'
8import { VideoDescriptionComponent } from './video-description.component'
9import { VideoEditComponent } from './video-edit.component' 5import { VideoEditComponent } from './video-edit.component'
10 6
11@NgModule({ 7@NgModule({
12 imports: [ 8 imports: [
13 TagInputModule, 9 TagInputModule,
14 TabsModule.forRoot(),
15 10
16 SharedModule 11 SharedModule
17 ], 12 ],
18 13
19 declarations: [ 14 declarations: [
20 VideoDescriptionComponent,
21 VideoEditComponent 15 VideoEditComponent
22 ], 16 ],
23 17
@@ -25,12 +19,9 @@ import { VideoEditComponent } from './video-edit.component'
25 TagInputModule, 19 TagInputModule,
26 TabsModule, 20 TabsModule,
27 21
28 VideoDescriptionComponent,
29 VideoEditComponent 22 VideoEditComponent
30 ], 23 ],
31 24
32 providers: [ 25 providers: []
33 MarkdownService
34 ]
35}) 26})
36export class VideoEditModule { } 27export 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 {
14 .enable('link') 14 .enable('link')
15 .enable('newline') 15 .enable('newline')
16 16
17 this.setTargetToLinks()
18 }
19
20 markdownToHTML (markdown: string) {
21 const html = this.markdownIt.render(markdown)
22
23 // Avoid linkify truncated links
24 return html.replace(/<a[^>]+>([^<]+)<\/a>\s*...(<\/p>)?$/mi, '$1...')
25 }
26
27 private setTargetToLinks () {
17 // Snippet from markdown-it documentation: https://github.com/markdown-it/markdown-it/blob/master/docs/architecture.md#renderer 28 // Snippet from markdown-it documentation: https://github.com/markdown-it/markdown-it/blob/master/docs/architecture.md#renderer
18 const defaultRender = this.markdownIt.renderer.rules.link_open || function (tokens, idx, options, env, self) { 29 const defaultRender = this.markdownIt.renderer.rules.link_open || function (tokens, idx, options, env, self) {
19 return self.renderToken(tokens, idx, options) 30 return self.renderToken(tokens, idx, options)
@@ -33,11 +44,4 @@ export class MarkdownService {
33 return defaultRender(tokens, idx, options, env, self) 44 return defaultRender(tokens, idx, options, env, self)
34 } 45 }
35 } 46 }
36
37 markdownToHTML (markdown: string) {
38 const html = this.markdownIt.render(markdown)
39
40 // Avoid linkify truncated links
41 return html.replace(/<a[^>]+>([^<]+)<\/a>\s*...(<\/p>)?$/mi, '$1...')
42 }
43} 47}
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:
69 480p: true 69 480p: true
70 720p: true 70 720p: true
71 1080p: true 71 1080p: true
72
73instance:
74 name: 'PeerTube'
75 description: '' # Support markdown
76 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:
69 480p: true 69 480p: true
70 720p: true 70 720p: true
71 1080p: true 71 1080p: true
72
73instance:
74 name: 'PeerTube'
75 description: '' # Support markdown
76 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 {
105 105
106function customConfig (): CustomConfig { 106function customConfig (): CustomConfig {
107 return { 107 return {
108 instance: {
109 name: CONFIG.INSTANCE.NAME,
110 description: CONFIG.INSTANCE.DESCRIPTION,
111 terms: CONFIG.INSTANCE.TERMS
112 },
108 cache: { 113 cache: {
109 previews: { 114 previews: {
110 size: CONFIG.CACHE.PREVIEWS.SIZE 115 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 () {
23 'database.hostname', 'database.port', 'database.suffix', 'database.username', 'database.password', 23 'database.hostname', 'database.port', 'database.suffix', 'database.username', 'database.password',
24 'storage.videos', 'storage.logs', 'storage.thumbnails', 'storage.previews', 'storage.torrents', 'storage.cache', 'log.level', 24 'storage.videos', 'storage.logs', 'storage.thumbnails', 'storage.previews', 'storage.torrents', 'storage.cache', 'log.level',
25 'cache.previews.size', 'admin.email', 'signup.enabled', 'signup.limit', 'transcoding.enabled', 'transcoding.threads', 25 'cache.previews.size', 'admin.email', 'signup.enabled', 'signup.limit', 'transcoding.enabled', 'transcoding.threads',
26 'user.video_quota', 'smtp.hostname', 'smtp.port', 'smtp.username', 'smtp.password', 'smtp.tls', 'smtp.from_address' 26 'user.video_quota', 'smtp.hostname', 'smtp.port', 'smtp.username', 'smtp.password', 'smtp.tls', 'smtp.from_address',
27 'instance.name', 'instance.description', 'instance.terms'
27 ] 28 ]
28 const miss: string[] = [] 29 const miss: string[] = []
29 30
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 = {
154 PREVIEWS: { 154 PREVIEWS: {
155 get SIZE () { return config.get<number>('cache.previews.size') } 155 get SIZE () { return config.get<number>('cache.previews.size') }
156 } 156 }
157 },
158 INSTANCE: {
159 get NAME () { return config.get<string>('instance.name') },
160 get DESCRIPTION () { return config.get<string>('instance.description') },
161 get TERMS () { return config.get<string>('instance.terms') }
157 } 162 }
158} 163}
159 164
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 () {
14 let server: ServerInfo 14 let server: ServerInfo
15 let userAccessToken: string 15 let userAccessToken: string
16 const updateParams: CustomConfig = { 16 const updateParams: CustomConfig = {
17 instance: {
18 name: 'PeerTube updated',
19 description: 'my super description',
20 terms: 'my super terms'
21 },
17 cache: { 22 cache: {
18 previews: { 23 previews: {
19 size: 2 24 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 () {
49 const res = await getCustomConfig(server.url, server.accessToken) 49 const res = await getCustomConfig(server.url, server.accessToken)
50 const data = res.body 50 const data = res.body
51 51
52 expect(data.instance.name).to.equal('PeerTube')
53 expect(data.instance.description).to.be.empty
54 expect(data.instance.terms).to.be.empty
52 expect(data.cache.previews.size).to.equal(1) 55 expect(data.cache.previews.size).to.equal(1)
53 expect(data.signup.enabled).to.be.true 56 expect(data.signup.enabled).to.be.true
54 expect(data.signup.limit).to.equal(4) 57 expect(data.signup.limit).to.equal(4)
@@ -65,6 +68,11 @@ describe('Test config', function () {
65 68
66 it('Should update the customized configuration', async function () { 69 it('Should update the customized configuration', async function () {
67 const newCustomConfig = { 70 const newCustomConfig = {
71 instance: {
72 name: 'PeerTube updated',
73 description: 'my super description',
74 terms: 'my super terms'
75 },
68 cache: { 76 cache: {
69 previews: { 77 previews: {
70 size: 2 78 size: 2
@@ -97,7 +105,9 @@ describe('Test config', function () {
97 const res = await getCustomConfig(server.url, server.accessToken) 105 const res = await getCustomConfig(server.url, server.accessToken)
98 const data = res.body 106 const data = res.body
99 107
100 expect(data.cache.previews.size).to.equal(2) 108 expect(data.instance.name).to.equal('PeerTube updated')
109 expect(data.instance.description).to.equal('my super description')
110 expect(data.instance.terms).to.equal('my super terms')
101 expect(data.signup.enabled).to.be.false 111 expect(data.signup.enabled).to.be.false
102 expect(data.signup.limit).to.equal(5) 112 expect(data.signup.limit).to.equal(5)
103 expect(data.admin.email).to.equal('superadmin1@example.com') 113 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 (
373 expect(dateIsValid(video.createdAt)).to.be.true 373 expect(dateIsValid(video.createdAt)).to.be.true
374 expect(dateIsValid(video.updatedAt)).to.be.true 374 expect(dateIsValid(video.updatedAt)).to.be.true
375 375
376 const res = await getVideo(url, video.id) 376 const res = await getVideo(url, video.uuid)
377 const videoDetails = res.body 377 const videoDetails = res.body
378 378
379 expect(videoDetails.files).to.have.lengthOf(attributes.files.length) 379 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 @@
1export interface CustomConfig { 1export interface CustomConfig {
2 instance: {
3 name: string
4 description: string
5 terms: string
6 }
7
2 cache: { 8 cache: {
3 previews: { 9 previews: {
4 size: number 10 size: number