Add new name/terms/description config options
This commit is contained in:
parent
81ebea48bf
commit
66b16cafb3
22 changed files with 201 additions and 73 deletions
|
@ -2,6 +2,41 @@
|
||||||
|
|
||||||
<form role="form" (ngSubmit)="formValidated()" [formGroup]="form">
|
<form role="form" (ngSubmit)="formValidated()" [formGroup]="form">
|
||||||
|
|
||||||
|
<div class="inner-form-title">Instance</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="instanceName">Name</label>
|
||||||
|
<input
|
||||||
|
type="text" id="instanceName"
|
||||||
|
formControlName="instanceName" [ngClass]="{ 'input-error': formErrors['instanceName'] }"
|
||||||
|
>
|
||||||
|
<div *ngIf="formErrors.instanceName" class="form-error">
|
||||||
|
{{ formErrors.instanceName }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="instanceDescription">Description (markdown)</label>
|
||||||
|
<my-markdown-textarea
|
||||||
|
id="instanceDescription" formControlName="instanceDescription" textareaWidth="500px" [previewColumn]="true"
|
||||||
|
[classes]="{ 'input-error': formErrors['instanceDescription'] }"
|
||||||
|
></my-markdown-textarea>
|
||||||
|
<div *ngIf="formErrors.instanceDescription" class="form-error">
|
||||||
|
{{ formErrors.instanceDescription }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="instanceTerms">Terms (markdown)</label>
|
||||||
|
<my-markdown-textarea
|
||||||
|
id="instanceTerms" formControlName="instanceTerms" textareaWidth="500px" [previewColumn]="true"
|
||||||
|
[ngClass]="{ 'input-error': formErrors['instanceTerms'] }"
|
||||||
|
></my-markdown-textarea>
|
||||||
|
<div *ngIf="formErrors.instanceTerms" class="form-error">
|
||||||
|
{{ formErrors.instanceTerms }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="inner-form-title">Cache</div>
|
<div class="inner-form-title">Cache</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
|
|
|
@ -4,7 +4,13 @@ import { Router } from '@angular/router'
|
||||||
import { ConfigService } from '@app/+admin/config/shared/config.service'
|
import { ConfigService } from '@app/+admin/config/shared/config.service'
|
||||||
import { ServerService } from '@app/core/server/server.service'
|
import { ServerService } from '@app/core/server/server.service'
|
||||||
import { FormReactive, USER_VIDEO_QUOTA } from '@app/shared'
|
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 { NotificationsService } from 'angular2-notifications'
|
||||||
import { CustomConfig } from '../../../../../../shared/models/config/custom-config.model'
|
import { CustomConfig } from '../../../../../../shared/models/config/custom-config.model'
|
||||||
|
|
||||||
|
@ -36,6 +42,9 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit {
|
||||||
|
|
||||||
form: FormGroup
|
form: FormGroup
|
||||||
formErrors = {
|
formErrors = {
|
||||||
|
instanceName: '',
|
||||||
|
instanceDescription: '',
|
||||||
|
instanceTerms: '',
|
||||||
cachePreviewsSize: '',
|
cachePreviewsSize: '',
|
||||||
signupLimit: '',
|
signupLimit: '',
|
||||||
adminEmail: '',
|
adminEmail: '',
|
||||||
|
@ -43,6 +52,7 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit {
|
||||||
transcodingThreads: ''
|
transcodingThreads: ''
|
||||||
}
|
}
|
||||||
validationMessages = {
|
validationMessages = {
|
||||||
|
instanceName: INSTANCE_NAME.MESSAGES,
|
||||||
cachePreviewsSize: CACHE_PREVIEWS_SIZE.MESSAGES,
|
cachePreviewsSize: CACHE_PREVIEWS_SIZE.MESSAGES,
|
||||||
signupLimit: SIGNUP_LIMIT.MESSAGES,
|
signupLimit: SIGNUP_LIMIT.MESSAGES,
|
||||||
adminEmail: ADMIN_EMAIL.MESSAGES,
|
adminEmail: ADMIN_EMAIL.MESSAGES,
|
||||||
|
@ -65,6 +75,9 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit {
|
||||||
|
|
||||||
buildForm () {
|
buildForm () {
|
||||||
const formGroupData = {
|
const formGroupData = {
|
||||||
|
instanceName: [ '', INSTANCE_NAME.VALIDATORS ],
|
||||||
|
instanceDescription: [ '' ],
|
||||||
|
instanceTerms: [ '' ],
|
||||||
cachePreviewsSize: [ '', CACHE_PREVIEWS_SIZE.VALIDATORS ],
|
cachePreviewsSize: [ '', CACHE_PREVIEWS_SIZE.VALIDATORS ],
|
||||||
signupEnabled: [ ],
|
signupEnabled: [ ],
|
||||||
signupLimit: [ '', SIGNUP_LIMIT.VALIDATORS ],
|
signupLimit: [ '', SIGNUP_LIMIT.VALIDATORS ],
|
||||||
|
@ -109,6 +122,11 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit {
|
||||||
|
|
||||||
formValidated () {
|
formValidated () {
|
||||||
const data = {
|
const data = {
|
||||||
|
instance: {
|
||||||
|
name: this.form.value['instanceName'],
|
||||||
|
description: this.form.value['instanceDescription'],
|
||||||
|
terms: this.form.value['instanceTerms']
|
||||||
|
},
|
||||||
cache: {
|
cache: {
|
||||||
previews: {
|
previews: {
|
||||||
size: this.form.value['cachePreviewsSize']
|
size: this.form.value['cachePreviewsSize']
|
||||||
|
@ -146,6 +164,8 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit {
|
||||||
this.serverService.loadConfig()
|
this.serverService.loadConfig()
|
||||||
|
|
||||||
this.updateForm()
|
this.updateForm()
|
||||||
|
|
||||||
|
this.notificationsService.success('Success', 'Configuration updated.')
|
||||||
},
|
},
|
||||||
|
|
||||||
err => this.notificationsService.error('Error', err.message)
|
err => this.notificationsService.error('Error', err.message)
|
||||||
|
@ -154,6 +174,9 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit {
|
||||||
|
|
||||||
private updateForm () {
|
private updateForm () {
|
||||||
const data = {
|
const data = {
|
||||||
|
instanceName: this.customConfig.instance.name,
|
||||||
|
instanceDescription: this.customConfig.instance.description,
|
||||||
|
instanceTerms: this.customConfig.instance.terms,
|
||||||
cachePreviewsSize: this.customConfig.cache.previews.size,
|
cachePreviewsSize: this.customConfig.cache.previews.size,
|
||||||
signupEnabled: this.customConfig.signup.enabled,
|
signupEnabled: this.customConfig.signup.enabled,
|
||||||
signupLimit: this.customConfig.signup.limit,
|
signupLimit: this.customConfig.signup.limit,
|
||||||
|
|
|
@ -1,5 +1,12 @@
|
||||||
import { Validators } from '@angular/forms'
|
import { Validators } from '@angular/forms'
|
||||||
|
|
||||||
|
export const INSTANCE_NAME = {
|
||||||
|
VALIDATORS: [ Validators.required ],
|
||||||
|
MESSAGES: {
|
||||||
|
'required': 'Instance name is required.',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export const CACHE_PREVIEWS_SIZE = {
|
export const CACHE_PREVIEWS_SIZE = {
|
||||||
VALIDATORS: [ Validators.required, Validators.min(1), Validators.pattern('[0-9]+') ],
|
VALIDATORS: [ Validators.required, Validators.min(1), Validators.pattern('[0-9]+') ],
|
||||||
MESSAGES: {
|
MESSAGES: {
|
||||||
|
|
12
client/src/app/shared/forms/markdown-textarea.component.html
Normal file
12
client/src/app/shared/forms/markdown-textarea.component.html
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
<div class="root" [ngStyle]="{ 'flex-direction': flexDirection }">
|
||||||
|
<textarea
|
||||||
|
[(ngModel)]="description" (ngModelChange)="onModelChange()"
|
||||||
|
[ngClass]="classes" [ngStyle]="{ width: textareaWidth, height: textareaHeight, 'margin-right': textareaMarginRight }"
|
||||||
|
id="description" name="description">
|
||||||
|
</textarea>
|
||||||
|
|
||||||
|
<tabset *ngIf="arePreviewsDisplayed()" #staticTabs class="previews">
|
||||||
|
<tab *ngIf="truncate !== undefined" heading="Truncated description preview" [innerHTML]="truncatedDescriptionHTML"></tab>
|
||||||
|
<tab heading="Complete description preview" [innerHTML]="descriptionHTML"></tab>
|
||||||
|
</tabset>
|
||||||
|
</div>
|
27
client/src/app/shared/forms/markdown-textarea.component.scss
Normal file
27
client/src/app/shared/forms/markdown-textarea.component.scss
Normal file
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,25 +3,33 @@ import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'
|
||||||
import 'rxjs/add/operator/debounceTime'
|
import 'rxjs/add/operator/debounceTime'
|
||||||
import 'rxjs/add/operator/distinctUntilChanged'
|
import 'rxjs/add/operator/distinctUntilChanged'
|
||||||
import { isInMobileView } from '@app/shared/misc/utils'
|
import { isInMobileView } from '@app/shared/misc/utils'
|
||||||
|
import { MarkdownService } from '@app/videos/shared'
|
||||||
import { Subject } from 'rxjs/Subject'
|
import { Subject } from 'rxjs/Subject'
|
||||||
import { MarkdownService } from '../../shared'
|
|
||||||
import truncate from 'lodash-es/truncate'
|
import truncate from 'lodash-es/truncate'
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'my-video-description',
|
selector: 'my-markdown-textarea',
|
||||||
templateUrl: './video-description.component.html',
|
templateUrl: './markdown-textarea.component.html',
|
||||||
styleUrls: [ './video-description.component.scss' ],
|
styleUrls: [ './markdown-textarea.component.scss' ],
|
||||||
providers: [
|
providers: [
|
||||||
{
|
{
|
||||||
provide: NG_VALUE_ACCESSOR,
|
provide: NG_VALUE_ACCESSOR,
|
||||||
useExisting: forwardRef(() => VideoDescriptionComponent),
|
useExisting: forwardRef(() => MarkdownTextareaComponent),
|
||||||
multi: true
|
multi: true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
|
||||||
export class VideoDescriptionComponent implements ControlValueAccessor, OnInit {
|
export class MarkdownTextareaComponent implements ControlValueAccessor, OnInit {
|
||||||
@Input() description = ''
|
@Input() description = ''
|
||||||
|
@Input() classes: string[] = []
|
||||||
|
@Input() textareaWidth = '100%'
|
||||||
|
@Input() textareaHeight = '150px'
|
||||||
|
@Input() previewColumn = false
|
||||||
|
@Input() truncate: number
|
||||||
|
|
||||||
|
textareaMarginRight = '0'
|
||||||
|
flexDirection = 'column'
|
||||||
truncatedDescriptionHTML = ''
|
truncatedDescriptionHTML = ''
|
||||||
descriptionHTML = ''
|
descriptionHTML = ''
|
||||||
|
|
||||||
|
@ -36,6 +44,11 @@ export class VideoDescriptionComponent implements ControlValueAccessor, OnInit {
|
||||||
.subscribe(() => this.updateDescriptionPreviews())
|
.subscribe(() => this.updateDescriptionPreviews())
|
||||||
|
|
||||||
this.descriptionChanged.next(this.description)
|
this.descriptionChanged.next(this.description)
|
||||||
|
|
||||||
|
if (this.previewColumn) {
|
||||||
|
this.flexDirection = 'row'
|
||||||
|
this.textareaMarginRight = '15px'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
propagateChange = (_: any) => { /* empty */ }
|
propagateChange = (_: any) => { /* empty */ }
|
||||||
|
@ -65,9 +78,9 @@ export class VideoDescriptionComponent implements ControlValueAccessor, OnInit {
|
||||||
}
|
}
|
||||||
|
|
||||||
private updateDescriptionPreviews () {
|
private updateDescriptionPreviews () {
|
||||||
if (!this.description) return
|
if (this.description === null || this.description === undefined) return
|
||||||
|
|
||||||
this.truncatedDescriptionHTML = this.markdownService.markdownToHTML(truncate(this.description, { length: 250 }))
|
this.truncatedDescriptionHTML = this.markdownService.markdownToHTML(truncate(this.description, { length: this.truncate }))
|
||||||
this.descriptionHTML = this.markdownService.markdownToHTML(this.description)
|
this.descriptionHTML = this.markdownService.markdownToHTML(this.description)
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -3,10 +3,13 @@ import { HttpClientModule } from '@angular/common/http'
|
||||||
import { NgModule } from '@angular/core'
|
import { NgModule } from '@angular/core'
|
||||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
|
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
|
||||||
import { RouterModule } from '@angular/router'
|
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 { LoadingBarHttpClientModule } from '@ngx-loading-bar/http-client'
|
||||||
|
|
||||||
import { BsDropdownModule } from 'ngx-bootstrap/dropdown'
|
import { BsDropdownModule } from 'ngx-bootstrap/dropdown'
|
||||||
import { ModalModule } from 'ngx-bootstrap/modal'
|
import { ModalModule } from 'ngx-bootstrap/modal'
|
||||||
|
import { TabsModule } from 'ngx-bootstrap/tabs'
|
||||||
import { InfiniteScrollModule } from 'ngx-infinite-scroll'
|
import { InfiniteScrollModule } from 'ngx-infinite-scroll'
|
||||||
import { BytesPipe, KeysPipe, NgPipesModule } from 'ngx-pipes'
|
import { BytesPipe, KeysPipe, NgPipesModule } from 'ngx-pipes'
|
||||||
import { SharedModule as PrimeSharedModule } from 'primeng/components/common/shared'
|
import { SharedModule as PrimeSharedModule } from 'primeng/components/common/shared'
|
||||||
|
@ -40,7 +43,8 @@ import { VideoService } from './video/video.service'
|
||||||
|
|
||||||
PrimeSharedModule,
|
PrimeSharedModule,
|
||||||
InfiniteScrollModule,
|
InfiniteScrollModule,
|
||||||
NgPipesModule
|
NgPipesModule,
|
||||||
|
TabsModule.forRoot()
|
||||||
],
|
],
|
||||||
|
|
||||||
declarations: [
|
declarations: [
|
||||||
|
@ -50,7 +54,8 @@ import { VideoService } from './video/video.service'
|
||||||
DeleteButtonComponent,
|
DeleteButtonComponent,
|
||||||
EditButtonComponent,
|
EditButtonComponent,
|
||||||
NumberFormatterPipe,
|
NumberFormatterPipe,
|
||||||
FromNowPipe
|
FromNowPipe,
|
||||||
|
MarkdownTextareaComponent
|
||||||
],
|
],
|
||||||
|
|
||||||
exports: [
|
exports: [
|
||||||
|
@ -74,6 +79,7 @@ import { VideoService } from './video/video.service'
|
||||||
VideoMiniatureComponent,
|
VideoMiniatureComponent,
|
||||||
DeleteButtonComponent,
|
DeleteButtonComponent,
|
||||||
EditButtonComponent,
|
EditButtonComponent,
|
||||||
|
MarkdownTextareaComponent,
|
||||||
|
|
||||||
NumberFormatterPipe,
|
NumberFormatterPipe,
|
||||||
FromNowPipe
|
FromNowPipe
|
||||||
|
@ -86,7 +92,8 @@ import { VideoService } from './video/video.service'
|
||||||
VideoAbuseService,
|
VideoAbuseService,
|
||||||
VideoBlacklistService,
|
VideoBlacklistService,
|
||||||
UserService,
|
UserService,
|
||||||
VideoService
|
VideoService,
|
||||||
|
MarkdownService
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class SharedModule { }
|
export class SharedModule { }
|
||||||
|
|
|
@ -42,10 +42,10 @@ export class VideoService {
|
||||||
}
|
}
|
||||||
|
|
||||||
updateVideo (video: VideoEdit) {
|
updateVideo (video: VideoEdit) {
|
||||||
const language = video.language || undefined
|
const language = video.language || null
|
||||||
const licence = video.licence || undefined
|
const licence = video.licence || null
|
||||||
const category = video.category || undefined
|
const category = video.category || null
|
||||||
const description = video.description || undefined
|
const description = video.description || null
|
||||||
|
|
||||||
const body: VideoUpdate = {
|
const body: VideoUpdate = {
|
||||||
name: video.name,
|
name: video.name,
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
<textarea
|
|
||||||
[(ngModel)]="description" (ngModelChange)="onModelChange()"
|
|
||||||
id="description" name="description">
|
|
||||||
</textarea>
|
|
||||||
|
|
||||||
<tabset *ngIf="arePreviewsDisplayed()" #staticTabs class="previews">
|
|
||||||
<tab heading="Truncated description preview" [innerHTML]="truncatedDescriptionHTML"></tab>
|
|
||||||
<tab heading="Complete description preview" [innerHTML]="descriptionHTML"></tab>
|
|
||||||
</tabset>
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -12,14 +12,14 @@
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="label-tags">Tags</label> <span>(press Enter to add)</span>
|
<label class="label-tags">Tags</label> <span>(press Enter to add)</span>
|
||||||
<tag-input
|
<tag-input
|
||||||
[ngModel]="tags" [validators]="tagValidators" [errorMessages]="tagValidatorsMessages"
|
[ngModel]="tags" [validators]="tagValidators" [errorMessages]="tagValidatorsMessages"
|
||||||
formControlName="tags" maxItems="5" modelAsStrings="true"
|
formControlName="tags" maxItems="5" modelAsStrings="true"
|
||||||
></tag-input>
|
></tag-input>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="description">Description</label>
|
<label for="description">Description</label>
|
||||||
<my-video-description formControlName="description"></my-video-description>
|
<my-markdown-textarea truncate="250" formControlName="description"></my-markdown-textarea>
|
||||||
|
|
||||||
<div *ngIf="formErrors.description" class="form-error">
|
<div *ngIf="formErrors.description" class="form-error">
|
||||||
{{ formErrors.description }}
|
{{ formErrors.description }}
|
||||||
|
|
|
@ -1,23 +1,17 @@
|
||||||
import { NgModule } from '@angular/core'
|
import { NgModule } from '@angular/core'
|
||||||
|
|
||||||
import { TagInputModule } from 'ngx-chips'
|
|
||||||
import { TabsModule } from 'ngx-bootstrap/tabs'
|
import { TabsModule } from 'ngx-bootstrap/tabs'
|
||||||
|
import { TagInputModule } from 'ngx-chips'
|
||||||
import { MarkdownService } from '../../shared'
|
|
||||||
import { SharedModule } from '../../../shared'
|
import { SharedModule } from '../../../shared'
|
||||||
import { VideoDescriptionComponent } from './video-description.component'
|
|
||||||
import { VideoEditComponent } from './video-edit.component'
|
import { VideoEditComponent } from './video-edit.component'
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
TagInputModule,
|
TagInputModule,
|
||||||
TabsModule.forRoot(),
|
|
||||||
|
|
||||||
SharedModule
|
SharedModule
|
||||||
],
|
],
|
||||||
|
|
||||||
declarations: [
|
declarations: [
|
||||||
VideoDescriptionComponent,
|
|
||||||
VideoEditComponent
|
VideoEditComponent
|
||||||
],
|
],
|
||||||
|
|
||||||
|
@ -25,12 +19,9 @@ import { VideoEditComponent } from './video-edit.component'
|
||||||
TagInputModule,
|
TagInputModule,
|
||||||
TabsModule,
|
TabsModule,
|
||||||
|
|
||||||
VideoDescriptionComponent,
|
|
||||||
VideoEditComponent
|
VideoEditComponent
|
||||||
],
|
],
|
||||||
|
|
||||||
providers: [
|
providers: []
|
||||||
MarkdownService
|
|
||||||
]
|
|
||||||
})
|
})
|
||||||
export class VideoEditModule { }
|
export class VideoEditModule { }
|
||||||
|
|
|
@ -14,6 +14,17 @@ export class MarkdownService {
|
||||||
.enable('link')
|
.enable('link')
|
||||||
.enable('newline')
|
.enable('newline')
|
||||||
|
|
||||||
|
this.setTargetToLinks()
|
||||||
|
}
|
||||||
|
|
||||||
|
markdownToHTML (markdown: string) {
|
||||||
|
const html = this.markdownIt.render(markdown)
|
||||||
|
|
||||||
|
// Avoid linkify truncated links
|
||||||
|
return html.replace(/<a[^>]+>([^<]+)<\/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
|
// 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) {
|
const defaultRender = this.markdownIt.renderer.rules.link_open || function (tokens, idx, options, env, self) {
|
||||||
return self.renderToken(tokens, idx, options)
|
return self.renderToken(tokens, idx, options)
|
||||||
|
@ -33,11 +44,4 @@ export class MarkdownService {
|
||||||
return defaultRender(tokens, idx, options, env, self)
|
return defaultRender(tokens, idx, options, env, self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
markdownToHTML (markdown: string) {
|
|
||||||
const html = this.markdownIt.render(markdown)
|
|
||||||
|
|
||||||
// Avoid linkify truncated links
|
|
||||||
return html.replace(/<a[^>]+>([^<]+)<\/a>\s*...(<\/p>)?$/mi, '$1...')
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,3 +69,8 @@ transcoding:
|
||||||
480p: true
|
480p: true
|
||||||
720p: true
|
720p: true
|
||||||
1080p: true
|
1080p: true
|
||||||
|
|
||||||
|
instance:
|
||||||
|
name: 'PeerTube'
|
||||||
|
description: '' # Support markdown
|
||||||
|
terms: '' # Support markdown
|
||||||
|
|
|
@ -69,3 +69,8 @@ transcoding:
|
||||||
480p: true
|
480p: true
|
||||||
720p: true
|
720p: true
|
||||||
1080p: true
|
1080p: true
|
||||||
|
|
||||||
|
instance:
|
||||||
|
name: 'PeerTube'
|
||||||
|
description: '' # Support markdown
|
||||||
|
terms: '' # Support markdown
|
||||||
|
|
|
@ -105,6 +105,11 @@ export {
|
||||||
|
|
||||||
function customConfig (): CustomConfig {
|
function customConfig (): CustomConfig {
|
||||||
return {
|
return {
|
||||||
|
instance: {
|
||||||
|
name: CONFIG.INSTANCE.NAME,
|
||||||
|
description: CONFIG.INSTANCE.DESCRIPTION,
|
||||||
|
terms: CONFIG.INSTANCE.TERMS
|
||||||
|
},
|
||||||
cache: {
|
cache: {
|
||||||
previews: {
|
previews: {
|
||||||
size: CONFIG.CACHE.PREVIEWS.SIZE
|
size: CONFIG.CACHE.PREVIEWS.SIZE
|
||||||
|
|
|
@ -23,7 +23,8 @@ function checkMissedConfig () {
|
||||||
'database.hostname', 'database.port', 'database.suffix', 'database.username', 'database.password',
|
'database.hostname', 'database.port', 'database.suffix', 'database.username', 'database.password',
|
||||||
'storage.videos', 'storage.logs', 'storage.thumbnails', 'storage.previews', 'storage.torrents', 'storage.cache', 'log.level',
|
'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',
|
'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[] = []
|
const miss: string[] = []
|
||||||
|
|
||||||
|
|
|
@ -154,6 +154,11 @@ const CONFIG = {
|
||||||
PREVIEWS: {
|
PREVIEWS: {
|
||||||
get SIZE () { return config.get<number>('cache.previews.size') }
|
get SIZE () { return config.get<number>('cache.previews.size') }
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
INSTANCE: {
|
||||||
|
get NAME () { return config.get<string>('instance.name') },
|
||||||
|
get DESCRIPTION () { return config.get<string>('instance.description') },
|
||||||
|
get TERMS () { return config.get<string>('instance.terms') }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,11 @@ describe('Test config API validators', function () {
|
||||||
let server: ServerInfo
|
let server: ServerInfo
|
||||||
let userAccessToken: string
|
let userAccessToken: string
|
||||||
const updateParams: CustomConfig = {
|
const updateParams: CustomConfig = {
|
||||||
|
instance: {
|
||||||
|
name: 'PeerTube updated',
|
||||||
|
description: 'my super description',
|
||||||
|
terms: 'my super terms'
|
||||||
|
},
|
||||||
cache: {
|
cache: {
|
||||||
previews: {
|
previews: {
|
||||||
size: 2
|
size: 2
|
||||||
|
|
|
@ -49,6 +49,9 @@ describe('Test config', function () {
|
||||||
const res = await getCustomConfig(server.url, server.accessToken)
|
const res = await getCustomConfig(server.url, server.accessToken)
|
||||||
const data = res.body
|
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.cache.previews.size).to.equal(1)
|
||||||
expect(data.signup.enabled).to.be.true
|
expect(data.signup.enabled).to.be.true
|
||||||
expect(data.signup.limit).to.equal(4)
|
expect(data.signup.limit).to.equal(4)
|
||||||
|
@ -65,6 +68,11 @@ describe('Test config', function () {
|
||||||
|
|
||||||
it('Should update the customized configuration', async function () {
|
it('Should update the customized configuration', async function () {
|
||||||
const newCustomConfig = {
|
const newCustomConfig = {
|
||||||
|
instance: {
|
||||||
|
name: 'PeerTube updated',
|
||||||
|
description: 'my super description',
|
||||||
|
terms: 'my super terms'
|
||||||
|
},
|
||||||
cache: {
|
cache: {
|
||||||
previews: {
|
previews: {
|
||||||
size: 2
|
size: 2
|
||||||
|
@ -97,7 +105,9 @@ describe('Test config', function () {
|
||||||
const res = await getCustomConfig(server.url, server.accessToken)
|
const res = await getCustomConfig(server.url, server.accessToken)
|
||||||
const data = res.body
|
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.enabled).to.be.false
|
||||||
expect(data.signup.limit).to.equal(5)
|
expect(data.signup.limit).to.equal(5)
|
||||||
expect(data.admin.email).to.equal('superadmin1@example.com')
|
expect(data.admin.email).to.equal('superadmin1@example.com')
|
||||||
|
|
|
@ -373,7 +373,7 @@ async function completeVideoCheck (
|
||||||
expect(dateIsValid(video.createdAt)).to.be.true
|
expect(dateIsValid(video.createdAt)).to.be.true
|
||||||
expect(dateIsValid(video.updatedAt)).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
|
const videoDetails = res.body
|
||||||
|
|
||||||
expect(videoDetails.files).to.have.lengthOf(attributes.files.length)
|
expect(videoDetails.files).to.have.lengthOf(attributes.files.length)
|
||||||
|
|
|
@ -1,4 +1,10 @@
|
||||||
export interface CustomConfig {
|
export interface CustomConfig {
|
||||||
|
instance: {
|
||||||
|
name: string
|
||||||
|
description: string
|
||||||
|
terms: string
|
||||||
|
}
|
||||||
|
|
||||||
cache: {
|
cache: {
|
||||||
previews: {
|
previews: {
|
||||||
size: number
|
size: number
|
||||||
|
|
Loading…
Reference in a new issue