Add contact form checkbox in admin form

This commit is contained in:
Chocobozzz 2019-01-10 09:58:08 +01:00
parent a4101923e6
commit 3866f1a02f
No known key found for this signature in database
GPG Key ID: 583A612D890159BE
8 changed files with 396 additions and 408 deletions

View File

@ -7,169 +7,169 @@
<div i18n class="inner-form-title">Instance</div>
<div class="form-group">
<label i18n 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 }}
<ng-container formGroupName="instance">
<div class="form-group">
<label i18n for="instanceName">Name</label>
<input
type="text" id="instanceName"
formControlName="name" [ngClass]="{ 'input-error': formErrors.instance.name }"
>
<div *ngIf="formErrors.instance.name" class="form-error">{{ formErrors.instance.name }}</div>
</div>
</div>
<div class="form-group">
<label i18n for="instanceShortDescription">Short description</label>
<textarea
id="instanceShortDescription" formControlName="instanceShortDescription"
[ngClass]="{ 'input-error': formErrors['instanceShortDescription'] }"
></textarea>
<div *ngIf="formErrors.instanceShortDescription" class="form-error">
{{ formErrors.instanceShortDescription }}
<div class="form-group">
<label i18n for="instanceShortDescription">Short description</label>
<textarea
id="instanceShortDescription" formControlName="shortDescription"
[ngClass]="{ 'input-error': formErrors['instance.shortDescription'] }"
></textarea>
<div *ngIf="formErrors.instance.shortDescription" class="form-error">{{ formErrors.instance.shortDescription }}</div>
</div>
</div>
<div class="form-group">
<label i18n for="instanceDescription">Description</label><my-help helpType="markdownText"></my-help>
<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 class="form-group">
<label i18n for="instanceDescription">Description</label><my-help helpType="markdownText"></my-help>
<my-markdown-textarea
id="instanceDescription" formControlName="description" textareaWidth="500px" [previewColumn]="true"
[classes]="{ 'input-error': formErrors['instance.description'] }"
></my-markdown-textarea>
<div *ngIf="formErrors.instance.description" class="form-error">{{ formErrors.instance.description }}</div>
</div>
</div>
<div class="form-group">
<label i18n for="instanceTerms">Terms</label><my-help helpType="markdownText"></my-help>
<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 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>
<div class="form-group">
<label i18n for="instanceDefaultClientRoute">Default client route</label>
<div class="peertube-select-container">
<select id="instanceDefaultClientRoute" formControlName="instanceDefaultClientRoute">
<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 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 *ngIf="formErrors.instanceDefaultClientRoute" class="form-error">
{{ formErrors.instanceDefaultClientRoute }}
</div>
</div>
<div class="form-group">
<label i18n for="instanceDefaultNSFWPolicy">Policy on videos containing sensitive content</label>
<my-help
helpType="custom" i18n-customHtml
customHtml="With <strong>Do not list</strong> or <strong>Blur thumbnails</strong>, a confirmation will be requested to watch the video."
></my-help>
<div class="form-group">
<label i18n for="instanceDefaultNSFWPolicy">Policy on videos containing sensitive content</label>
<my-help
helpType="custom" i18n-customHtml
customHtml="With <strong>Do not list</strong> or <strong>Blur thumbnails</strong>, a confirmation will be requested to watch the video."
></my-help>
<div class="peertube-select-container">
<select id="instanceDefaultNSFWPolicy" formControlName="instanceDefaultNSFWPolicy">
<option i18n value="do_not_list">Do not list</option>
<option i18n value="blur">Blur thumbnails</option>
<option i18n value="display">Display</option>
</select>
<div class="peertube-select-container">
<select id="instanceDefaultNSFWPolicy" formControlName="defaultNSFWPolicy">
<option i18n value="do_not_list">Do not list</option>
<option i18n value="blur">Blur thumbnails</option>
<option i18n value="display">Display</option>
</select>
</div>
<div *ngIf="formErrors.instance.defaultNSFWPolicy" class="form-error">{{ formErrors.instance.defaultNSFWPolicy }}</div>
</div>
<div *ngIf="formErrors.instanceDefaultNSFWPolicy" class="form-error">
{{ formErrors.instanceDefaultNSFWPolicy }}
</div>
</div>
</ng-container>
<div i18n class="inner-form-title">Signup</div>
<div class="form-group">
<my-peertube-checkbox
inputName="signupEnabled" formControlName="signupEnabled"
i18n-labelText labelText="Signup enabled"
></my-peertube-checkbox>
</div>
<div class="form-group">
<my-peertube-checkbox *ngIf="isSignupEnabled()"
inputName="signupRequiresEmailVerification" formControlName="signupRequiresEmailVerification"
i18n-labelText labelText="Signup requires email verification"
></my-peertube-checkbox>
</div>
<div *ngIf="isSignupEnabled()" class="form-group">
<label i18n for="signupLimit">Signup limit</label>
<input
type="text" id="signupLimit"
formControlName="signupLimit" [ngClass]="{ 'input-error': formErrors['signupLimit'] }"
>
<div *ngIf="formErrors.signupLimit" class="form-error">
{{ formErrors.signupLimit }}
<ng-container formGroupName="signup">
<div class="form-group">
<my-peertube-checkbox
inputName="signupEnabled" formControlName="enabled"
i18n-labelText labelText="Signup enabled"
></my-peertube-checkbox>
</div>
</div>
<div i18n class="inner-form-title">Import</div>
<div class="form-group">
<my-peertube-checkbox
inputName="importVideosHttpEnabled" formControlName="importVideosHttpEnabled"
i18n-labelText labelText="Video import with HTTP URL (i.e. YouTube) enabled"
></my-peertube-checkbox>
</div>
<div class="form-group">
<my-peertube-checkbox
inputName="importVideosTorrentEnabled" formControlName="importVideosTorrentEnabled"
i18n-labelText labelText="Video import with a torrent file or a magnet URI enabled"
></my-peertube-checkbox>
</div>
<div i18n class="inner-form-title">Administrator</div>
<div class="form-group">
<label i18n for="adminEmail">Admin email</label>
<input
type="text" id="adminEmail"
formControlName="adminEmail" [ngClass]="{ 'input-error': formErrors['adminEmail'] }"
>
<div *ngIf="formErrors.adminEmail" class="form-error">
{{ formErrors.adminEmail }}
<div class="form-group">
<my-peertube-checkbox *ngIf="isSignupEnabled()"
inputName="signupRequiresEmailVerification" formControlName="requiresEmailVerification"
i18n-labelText labelText="Signup requires email verification"
></my-peertube-checkbox>
</div>
</div>
<div *ngIf="isSignupEnabled()" class="form-group">
<label i18n for="signupLimit">Signup limit</label>
<input
type="text" id="signupLimit"
formControlName="limit" [ngClass]="{ 'input-error': formErrors['signup.limit'] }"
>
<div *ngIf="formErrors.signup.limit" class="form-error">{{ formErrors.signup.limit }}</div>
</div>
</ng-container>
<div i18n class="inner-form-title">Users</div>
<div class="form-group">
<label i18n for="userVideoQuota">User default video quota</label>
<div class="peertube-select-container">
<select id="userVideoQuota" formControlName="userVideoQuota">
<option *ngFor="let videoQuotaOption of videoQuotaOptions" [value]="videoQuotaOption.value">
{{ videoQuotaOption.label }}
</option>
</select>
<ng-container formGroupName="user">
<div class="form-group">
<label i18n for="userVideoQuota">User default video quota</label>
<div class="peertube-select-container">
<select id="userVideoQuota" formControlName="videoQuota">
<option *ngFor="let videoQuotaOption of videoQuotaOptions" [value]="videoQuotaOption.value">
{{ videoQuotaOption.label }}
</option>
</select>
</div>
<div *ngIf="formErrors.user.videoQuota" class="form-error">{{ formErrors.user.videoQuota }}</div>
</div>
<div *ngIf="formErrors.userVideoQuota" class="form-error">
{{ formErrors.userVideoQuota }}
<div class="form-group">
<label i18n for="userVideoQuotaDaily">User default daily upload limit</label>
<div class="peertube-select-container">
<select id="userVideoQuotaDaily" formControlName="videoQuotaDaily">
<option *ngFor="let videoQuotaDailyOption of videoQuotaDailyOptions" [value]="videoQuotaDailyOption.value">
{{ videoQuotaDailyOption.label }}
</option>
</select>
</div>
<div *ngIf="formErrors.user.videoQuotaDaily" class="form-error">{{ formErrors.user.videoQuotaDaily }}</div>
</div>
</ng-container>
<div i18n class="inner-form-title">Import</div>
<ng-container formGroupName="import">
<ng-container formGroupName="videos">
<div class="form-group" formGroupName="http">
<my-peertube-checkbox
inputName="importVideosHttpEnabled" formControlName="enabled"
i18n-labelText labelText="Video import with HTTP URL (i.e. YouTube) enabled"
></my-peertube-checkbox>
</div>
<div class="form-group" formGroupName="torrent">
<my-peertube-checkbox
inputName="importVideosTorrentEnabled" formControlName="enabled"
i18n-labelText labelText="Video import with a torrent file or a magnet URI enabled"
></my-peertube-checkbox>
</div>
</ng-container>
</ng-container>
<div i18n class="inner-form-title">Administrator</div>
<div class="form-group" formGroupName="admin">
<label i18n for="adminEmail">Admin email</label>
<input
type="text" id="adminEmail"
formControlName="email" [ngClass]="{ 'input-error': formErrors['admin.email'] }"
>
<div *ngIf="formErrors.admin.email" class="form-error">{{ formErrors.admin.email }}</div>
</div>
<div class="form-group">
<label i18n for="userVideoQuotaDaily">User default daily upload limit</label>
<div class="peertube-select-container">
<select id="userVideoQuotaDaily" formControlName="userVideoQuotaDaily">
<option *ngFor="let videoQuotaDailyOption of videoQuotaDailyOptions" [value]="videoQuotaDailyOption.value">
{{ videoQuotaDailyOption.label }}
</option>
</select>
</div>
<div *ngIf="formErrors.userVideoQuotaDaily" class="form-error">
{{ formErrors.userVideoQuotaDaily }}
</div>
<div class="form-group" formGroupName="contactForm">
<my-peertube-checkbox
inputName="enableContactForm" formControlName="enabled"
i18n-labelText labelText="Enable contact form"
></my-peertube-checkbox>
</div>
</ng-template>
</ngb-tab>
@ -177,30 +177,35 @@
<ng-template ngbTabContent>
<div i18n class="inner-form-title">Twitter</div>
<div class="form-group">
<label i18n for="signupLimit">Your Twitter username</label>
<my-help
helpType="custom" i18n-customHtml
customHtml="Indicates the Twitter account for the website or platform on which the content was published."
></my-help>
<input
type="text" id="servicesTwitterUsername"
formControlName="servicesTwitterUsername" [ngClass]="{ 'input-error': formErrors['servicesTwitterUsername'] }"
>
<div *ngIf="formErrors.servicesTwitterUsername" class="form-error">
{{ formErrors.servicesTwitterUsername }}
</div>
</div>
<ng-container formGroupName="services">
<ng-container formGroupName="twitter">
<div class="form-group">
<label i18n for="signupLimit">Your Twitter username</label>
<my-help
helpType="custom" i18n-customHtml
customHtml="Indicates the Twitter account for the website or platform on which the content was published."
></my-help>
<input
type="text" id="servicesTwitterUsername"
formControlName="username" [ngClass]="{ 'input-error': formErrors['services.twitter.username'] }"
>
<div *ngIf="formErrors.services.twitter.username" class="form-error">{{ formErrors.services.twitter.username }}</div>
</div>
<div class="form-group">
<my-peertube-checkbox
inputName="servicesTwitterWhitelisted" formControlName="whitelisted"
i18n-labelText labelText="Instance whitelisted by Twitter"
i18n-helpHtml helpHtml="If your instance is whitelisted by Twitter, a video player will be embedded in the Twitter feed on PeerTube video share.<br />
If the instance is not whitelisted, we use an image link card that will redirect on your PeerTube instance.<br /><br />
Check this checkbox, save the configuration and test with a video URL of your instance (https://example.com/videos/watch/blabla) on <a target='_blank' rel='noopener noreferrer' href='https://cards-dev.twitter.com/validator'>https://cards-dev.twitter.com/validator</a> to see if you instance is whitelisted."
></my-peertube-checkbox>
</div>
</ng-container>
</ng-container>
<div class="form-group">
<my-peertube-checkbox
inputName="servicesTwitterWhitelisted" formControlName="servicesTwitterWhitelisted"
i18n-labelText labelText="Instance whitelisted by Twitter"
i18n-helpHtml helpHtml="If your instance is whitelisted by Twitter, a video player will be embedded in the Twitter feed on PeerTube video share.<br />
If the instance is not whitelisted, we use an image link card that will redirect on your PeerTube instance.<br /><br />
Check this checkbox, save the configuration and test with a video URL of your instance (https://example.com/videos/watch/blabla) on <a target='_blank' rel='noopener noreferrer' href='https://cards-dev.twitter.com/validator'>https://cards-dev.twitter.com/validator</a> to see if you instance is whitelisted."
></my-peertube-checkbox>
</div>
</ng-template>
</ngb-tab>
@ -209,45 +214,48 @@
<div i18n class="inner-form-title">Transcoding</div>
<div class="form-group">
<my-peertube-checkbox
inputName="transcodingEnabled" formControlName="transcodingEnabled"
i18n-labelText labelText="Transcoding enabled"
i18n-helpHtml helpHtml="If you disable transcoding, many videos from your users will not work!"
></my-peertube-checkbox>
</div>
<ng-template [ngIf]="isTranscodingEnabled()">
<ng-container formGroupName="transcoding">
<div class="form-group">
<my-peertube-checkbox
inputName="transcodingAllowAdditionalExtensions" formControlName="transcodingAllowAdditionalExtensions"
i18n-labelText labelText="Allow additional extensions"
i18n-helpHtml helpHtml="Allow your users to upload .mkv, .mov, .avi, .flv videos"
inputName="transcodingEnabled" formControlName="enabled"
i18n-labelText labelText="Transcoding enabled"
i18n-helpHtml helpHtml="If you disable transcoding, many videos from your users will not work!"
></my-peertube-checkbox>
</div>
<div class="form-group">
<label i18n for="transcodingThreads">Transcoding threads</label>
<div class="peertube-select-container">
<select id="transcodingThreads" formControlName="transcodingThreads">
<option *ngFor="let transcodingThreadOption of transcodingThreadOptions" [value]="transcodingThreadOption.value">
{{ transcodingThreadOption.label }}
</option>
</select>
</div>
<div *ngIf="formErrors.transcodingThreads" class="form-error">
{{ formErrors.transcodingThreads }}
</div>
</div>
<ng-container *ngIf="isTranscodingEnabled()">
<div class="form-group" *ngFor="let resolution of resolutions">
<my-peertube-checkbox
[inputName]="getResolutionKey(resolution)" [formControlName]="getResolutionKey(resolution)"
i18n-labelText labelText="Resolution {{resolution}} enabled"
></my-peertube-checkbox>
</div>
</ng-template>
<div class="form-group">
<my-peertube-checkbox
inputName="transcodingAllowAdditionalExtensions" formControlName="allowAdditionalExtensions"
i18n-labelText labelText="Allow additional extensions"
i18n-helpHtml helpHtml="Allow your users to upload .mkv, .mov, .avi, .flv videos"
></my-peertube-checkbox>
</div>
<div class="form-group">
<label i18n for="transcodingThreads">Transcoding threads</label>
<div class="peertube-select-container">
<select id="transcodingThreads" formControlName="threads">
<option *ngFor="let transcodingThreadOption of transcodingThreadOptions" [value]="transcodingThreadOption.value">
{{ transcodingThreadOption.label }}
</option>
</select>
</div>
<div *ngIf="formErrors.transcoding.threads" class="form-error">{{ formErrors.transcoding.threads }}</div>
</div>
<ng-container formGroupName="resolutions">
<div class="form-group" *ngFor="let resolution of resolutions">
<my-peertube-checkbox
[inputName]="getResolutionKey(resolution)" [formControlName]="resolution"
i18n-labelText labelText="Resolution {{resolution}} enabled"
></my-peertube-checkbox>
</div>
</ng-container>
</ng-container>
</ng-container>
<div i18n class="inner-form-title">
Cache
@ -258,74 +266,73 @@
></my-help>
</div>
<div class="form-group">
<label i18n for="cachePreviewsSize">Previews cache size</label>
<input
type="text" id="cachePreviewsSize"
formControlName="cachePreviewsSize" [ngClass]="{ 'input-error': formErrors['cachePreviewsSize'] }"
>
<div *ngIf="formErrors.cachePreviewsSize" class="form-error">
{{ formErrors.cachePreviewsSize }}
<ng-container formGroupName="cache">
<div class="form-group" formGroupName="previews">
<label i18n for="cachePreviewsSize">Previews cache size</label>
<input
type="text" id="cachePreviewsSize"
formControlName="size" [ngClass]="{ 'input-error': formErrors['cache.previews.size'] }"
>
<div *ngIf="formErrors.cache.previews.size" class="form-error">{{ formErrors.cache.previews.size }}</div>
</div>
</div>
<div class="form-group">
<label i18n for="cachePreviewsSize">Video captions cache size</label>
<input
type="text" id="cacheCaptionsSize"
formControlName="cacheCaptionsSize" [ngClass]="{ 'input-error': formErrors['cacheCaptionsSize'] }"
>
<div *ngIf="formErrors.cacheCaptionsSize" class="form-error">
{{ formErrors.cacheCaptionsSize }}
<div class="form-group" formGroupName="captions">
<label i18n for="cacheCaptionsSize">Video captions cache size</label>
<input
type="text" id="cacheCaptionsSize"
formControlName="size" [ngClass]="{ 'input-error': formErrors['cache.captions.size'] }"
>
<div *ngIf="formErrors.cache.captions.size" class="form-error">{{ formErrors.cache.captions.size }}</div>
</div>
</div>
</ng-container>
<div i18n class="inner-form-title">Customizations</div>
<div class="form-group">
<label i18n for="customizationJavascript">JavaScript</label>
<my-help
helpType="custom" i18n-customHtml
customHtml="Write directly JavaScript code.<br />Example: <pre>console.log('my instance is amazing');</pre>"
></my-help>
<textarea
id="customizationJavascript" formControlName="customizationJavascript"
[ngClass]="{ 'input-error': formErrors['customizationJavascript'] }"
></textarea>
<div *ngIf="formErrors.customizationJavascript" class="form-error">
{{ formErrors.customizationJavascript }}
</div>
</div>
<ng-container formGroupName="instance">
<ng-container formGroupName="customizations">
<div class="form-group">
<label i18n for="customizationJavascript">JavaScript</label>
<my-help
helpType="custom" i18n-customHtml
customHtml="Write directly JavaScript code.<br />Example: <pre>console.log('my instance is amazing');</pre>"
></my-help>
<textarea
id="customizationJavascript" formControlName="javascript"
[ngClass]="{ 'input-error': formErrors['instance.customizations.javascript'] }"
></textarea>
<div *ngIf="formErrors.instance.customizations.javascript" class="form-error">{{ formErrors.instance.customizations.javascript }}</div>
</div>
<div class="form-group">
<label for="customizationCSS">CSS</label>
<my-help
helpType="custom"
i18n-customHtml
customHtml="
Write directly CSS code. Example:<br />
<pre>
body {{ '{' }}
background-color: red;
{{ '}' }}
</pre>
<div class="form-group">
<label for="customizationCSS">CSS</label>
<my-help
helpType="custom"
i18n-customHtml
customHtml="
Write directly CSS code. Example:<br />
<pre>
body {{ '{' }}
background-color: red;
{{ '}' }}
</pre>
Prepend with <em>#custom-css</em> to override styles. Example:
<pre>
#custom-css .logged-in-email {{ '{' }}
color: red;
{{ '}' }}
</pre>
"
></my-help>
<textarea
id="customizationCSS" formControlName="css"
[ngClass]="{ 'input-error': formErrors['instance.customizations.css'] }"
></textarea>
<div *ngIf="formErrors.instance.customizations.css" class="form-error">{{ formErrors.instance.customizations.css }}</div>
</div>
</ng-container>
</ng-container>
Prepend with <em>#custom-css</em> to override styles. Example:
<pre>
#custom-css .logged-in-email {{ '{' }}
color: red;
{{ '}' }}
</pre>
"
></my-help>
<textarea
id="customizationCSS" formControlName="customizationCSS"
[ngClass]="{ 'input-error': formErrors['customizationCSS'] }"
></textarea>
<div *ngIf="formErrors.customizationCSS" class="form-error">
{{ formErrors.customizationCSS }}
</div>
</div>
</ng-template>
</ngb-tab>
</ngb-tabset>

View File

@ -18,9 +18,6 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit {
resolutions: string[] = []
transcodingThreadOptions: { label: string, value: number }[] = []
private oldCustomJavascript: string
private oldCustomCSS: string
constructor (
protected formValidatorService: FormValidatorService,
private customConfigValidatorsService: CustomConfigValidatorsService,
@ -58,41 +55,78 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit {
}
getResolutionKey (resolution: string) {
return 'transcodingResolution' + resolution
return 'transcoding.resolutions.' + resolution
}
ngOnInit () {
const formGroupData: { [key: string]: any } = {
instanceName: this.customConfigValidatorsService.INSTANCE_NAME,
instanceShortDescription: this.customConfigValidatorsService.INSTANCE_SHORT_DESCRIPTION,
instanceDescription: null,
instanceTerms: null,
instanceDefaultClientRoute: null,
instanceDefaultNSFWPolicy: null,
servicesTwitterUsername: this.customConfigValidatorsService.SERVICES_TWITTER_USERNAME,
servicesTwitterWhitelisted: null,
cachePreviewsSize: this.customConfigValidatorsService.CACHE_PREVIEWS_SIZE,
cacheCaptionsSize: this.customConfigValidatorsService.CACHE_CAPTIONS_SIZE,
signupEnabled: null,
signupLimit: this.customConfigValidatorsService.SIGNUP_LIMIT,
signupRequiresEmailVerification: null,
importVideosHttpEnabled: null,
importVideosTorrentEnabled: null,
adminEmail: this.customConfigValidatorsService.ADMIN_EMAIL,
userVideoQuota: this.userValidatorsService.USER_VIDEO_QUOTA,
userVideoQuotaDaily: this.userValidatorsService.USER_VIDEO_QUOTA_DAILY,
transcodingThreads: this.customConfigValidatorsService.TRANSCODING_THREADS,
transcodingAllowAdditionalExtensions: null,
transcodingEnabled: null,
customizationJavascript: null,
customizationCSS: null
const formGroupData: { [key in keyof CustomConfig ]: any } = {
instance: {
name: this.customConfigValidatorsService.INSTANCE_NAME,
shortDescription: this.customConfigValidatorsService.INSTANCE_SHORT_DESCRIPTION,
description: null,
terms: null,
defaultClientRoute: null,
defaultNSFWPolicy: null,
customizations: {
javascript: null,
css: null
}
},
services: {
twitter: {
username: this.customConfigValidatorsService.SERVICES_TWITTER_USERNAME,
whitelisted: null
}
},
cache: {
previews: {
size: this.customConfigValidatorsService.CACHE_PREVIEWS_SIZE
},
captions: {
size: this.customConfigValidatorsService.CACHE_CAPTIONS_SIZE
}
},
signup: {
enabled: null,
limit: this.customConfigValidatorsService.SIGNUP_LIMIT,
requiresEmailVerification: null
},
import: {
videos: {
http: {
enabled: null
},
torrent: {
enabled: null
}
}
},
admin: {
email: this.customConfigValidatorsService.ADMIN_EMAIL
},
contactForm: {
enabled: null
},
user: {
videoQuota: this.userValidatorsService.USER_VIDEO_QUOTA,
videoQuotaDaily: this.userValidatorsService.USER_VIDEO_QUOTA_DAILY
},
transcoding: {
enabled: null,
threads: this.customConfigValidatorsService.TRANSCODING_THREADS,
allowAdditionalExtensions: null,
resolutions: {}
}
}
const defaultValues: BuildFormDefaultValues = {}
const defaultValues = {
transcoding: {
resolutions: {}
}
}
for (const resolution of this.resolutions) {
const key = this.getResolutionKey(resolution)
defaultValues[key] = 'false'
formGroupData[key] = null
defaultValues.transcoding.resolutions[resolution] = 'false'
formGroupData.transcoding.resolutions[resolution] = null
}
this.buildForm(formGroupData)
@ -102,9 +136,6 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit {
res => {
this.customConfig = res
this.oldCustomCSS = this.customConfig.instance.customizations.css
this.oldCustomJavascript = this.customConfig.instance.customizations.javascript
this.updateForm()
// Force form validation
this.forceCheck()
@ -115,78 +146,15 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit {
}
isTranscodingEnabled () {
return this.form.value['transcodingEnabled'] === true
return this.form.value['transcoding']['enabled'] === true
}
isSignupEnabled () {
return this.form.value['signupEnabled'] === true
return this.form.value['signup']['enabled'] === true
}
async formValidated () {
const data: CustomConfig = {
instance: {
name: this.form.value['instanceName'],
shortDescription: this.form.value['instanceShortDescription'],
description: this.form.value['instanceDescription'],
terms: this.form.value['instanceTerms'],
defaultClientRoute: this.form.value['instanceDefaultClientRoute'],
defaultNSFWPolicy: this.form.value['instanceDefaultNSFWPolicy'],
customizations: {
javascript: this.form.value['customizationJavascript'],
css: this.form.value['customizationCSS']
}
},
services: {
twitter: {
username: this.form.value['servicesTwitterUsername'],
whitelisted: this.form.value['servicesTwitterWhitelisted']
}
},
cache: {
previews: {
size: this.form.value['cachePreviewsSize']
},
captions: {
size: this.form.value['cacheCaptionsSize']
}
},
signup: {
enabled: this.form.value['signupEnabled'],
limit: this.form.value['signupLimit'],
requiresEmailVerification: this.form.value['signupRequiresEmailVerification']
},
admin: {
email: this.form.value['adminEmail']
},
user: {
videoQuota: this.form.value['userVideoQuota'],
videoQuotaDaily: this.form.value['userVideoQuotaDaily']
},
transcoding: {
enabled: this.form.value['transcodingEnabled'],
allowAdditionalExtensions: this.form.value['transcodingAllowAdditionalExtensions'],
threads: this.form.value['transcodingThreads'],
resolutions: {
'240p': this.form.value[this.getResolutionKey('240p')],
'360p': this.form.value[this.getResolutionKey('360p')],
'480p': this.form.value[this.getResolutionKey('480p')],
'720p': this.form.value[this.getResolutionKey('720p')],
'1080p': this.form.value[this.getResolutionKey('1080p')]
}
},
import: {
videos: {
http: {
enabled: this.form.value['importVideosHttpEnabled']
},
torrent: {
enabled: this.form.value['importVideosTorrentEnabled']
}
}
}
}
this.configService.updateCustomConfig(data)
this.configService.updateCustomConfig(this.form.value)
.subscribe(
res => {
this.customConfig = res
@ -204,38 +172,7 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit {
}
private updateForm () {
const data: { [key: string]: any } = {
instanceName: this.customConfig.instance.name,
instanceShortDescription: this.customConfig.instance.shortDescription,
instanceDescription: this.customConfig.instance.description,
instanceTerms: this.customConfig.instance.terms,
instanceDefaultClientRoute: this.customConfig.instance.defaultClientRoute,
instanceDefaultNSFWPolicy: this.customConfig.instance.defaultNSFWPolicy,
servicesTwitterUsername: this.customConfig.services.twitter.username,
servicesTwitterWhitelisted: this.customConfig.services.twitter.whitelisted,
cachePreviewsSize: this.customConfig.cache.previews.size,
cacheCaptionsSize: this.customConfig.cache.captions.size,
signupEnabled: this.customConfig.signup.enabled,
signupLimit: this.customConfig.signup.limit,
signupRequiresEmailVerification: this.customConfig.signup.requiresEmailVerification,
adminEmail: this.customConfig.admin.email,
userVideoQuota: this.customConfig.user.videoQuota,
userVideoQuotaDaily: this.customConfig.user.videoQuotaDaily,
transcodingThreads: this.customConfig.transcoding.threads,
transcodingEnabled: this.customConfig.transcoding.enabled,
transcodingAllowAdditionalExtensions: this.customConfig.transcoding.allowAdditionalExtensions,
customizationJavascript: this.customConfig.instance.customizations.javascript,
customizationCSS: this.customConfig.instance.customizations.css,
importVideosHttpEnabled: this.customConfig.import.videos.http.enabled,
importVideosTorrentEnabled: this.customConfig.import.videos.torrent.enabled
}
for (const resolution of this.resolutions) {
const key = this.getResolutionKey(resolution)
data[key] = this.customConfig.transcoding.resolutions[resolution]
}
this.form.patchValue(data)
this.form.patchValue(this.customConfig)
}
}

View File

@ -40,6 +40,9 @@ export class ServerService {
email: {
enabled: false
},
contactForm: {
enabled: false
},
serverVersion: 'Unknown',
signup: {
allowed: false,

View File

@ -1,11 +1,9 @@
import { FormGroup } from '@angular/forms'
import { BuildFormArgument, BuildFormDefaultValues, FormValidatorService } from '@app/shared/forms/form-validators/form-validator.service'
export type FormReactiveErrors = { [ id: string ]: string }
export type FormReactiveErrors = { [ id: string ]: string | FormReactiveErrors }
export type FormReactiveValidationMessages = {
[ id: string ]: {
[ name: string ]: string
}
[ id: string ]: { [ name: string ]: string } | FormReactiveValidationMessages
}
export abstract class FormReactive {
@ -23,29 +21,49 @@ export abstract class FormReactive {
this.formErrors = formErrors
this.validationMessages = validationMessages
this.form.valueChanges.subscribe(() => this.onValueChanged(false))
this.form.valueChanges.subscribe(() => this.onValueChanged(this.form, this.formErrors, this.validationMessages, false))
}
protected onValueChanged (forceCheck = false) {
for (const field in this.formErrors) {
protected forceCheck () {
return this.onValueChanged(this.form, this.formErrors, this.validationMessages, true)
}
protected check () {
return this.onValueChanged(this.form, this.formErrors, this.validationMessages, false)
}
private onValueChanged (
form: FormGroup,
formErrors: FormReactiveErrors,
validationMessages: FormReactiveValidationMessages,
forceCheck = false
) {
for (const field of Object.keys(formErrors)) {
if (formErrors[field] && typeof formErrors[field] === 'object') {
this.onValueChanged(
form.controls[field] as FormGroup,
formErrors[field] as FormReactiveErrors,
validationMessages[field] as FormReactiveValidationMessages,
forceCheck
)
continue
}
// clear previous error message (if any)
this.formErrors[ field ] = ''
const control = this.form.get(field)
formErrors[ field ] = ''
const control = form.get(field)
if (control.dirty) this.formChanged = true
// Don't care if dirty on force check
const isDirty = control.dirty || forceCheck === true
if (control && isDirty && !control.valid) {
const messages = this.validationMessages[ field ]
const messages = validationMessages[ field ]
for (const key in control.errors) {
this.formErrors[ field ] += messages[ key ] + ' '
formErrors[ field ] += messages[ key ] + ' '
}
}
}
}
protected forceCheck () {
return this.onValueChanged(true)
}
}

View File

@ -7,10 +7,10 @@ export type BuildFormValidator = {
MESSAGES: { [ name: string ]: string }
}
export type BuildFormArgument = {
[ id: string ]: BuildFormValidator
[ id: string ]: BuildFormValidator | BuildFormArgument
}
export type BuildFormDefaultValues = {
[ name: string ]: string | string[]
[ name: string ]: string | string[] | BuildFormDefaultValues
}
@Injectable()
@ -29,7 +29,16 @@ export class FormValidatorService {
formErrors[name] = ''
const field = obj[name]
if (field && field.MESSAGES) validationMessages[name] = field.MESSAGES
if (this.isRecursiveField(field)) {
const result = this.buildForm(field as BuildFormArgument, defaultValues[name] as BuildFormDefaultValues)
group[name] = result.form
formErrors[name] = result.formErrors
validationMessages[name] = result.validationMessages
continue
}
if (field && field.MESSAGES) validationMessages[name] = field.MESSAGES as { [ name: string ]: string }
const defaultValue = defaultValues[name] || ''
@ -52,13 +61,27 @@ export class FormValidatorService {
formErrors[name] = ''
const field = obj[name]
if (field && field.MESSAGES) validationMessages[name] = field.MESSAGES
if (this.isRecursiveField(field)) {
this.updateForm(
form[name],
formErrors[name] as FormReactiveErrors,
validationMessages[name] as FormReactiveValidationMessages,
obj[name] as BuildFormArgument,
defaultValues[name] as BuildFormDefaultValues
)
continue
}
if (field && field.MESSAGES) validationMessages[name] = field.MESSAGES as { [ name: string ]: string }
const defaultValue = defaultValues[name] || ''
if (field && field.VALIDATORS) form.addControl(name, new FormControl(defaultValue, field.VALIDATORS))
if (field && field.VALIDATORS) form.addControl(name, new FormControl(defaultValue, field.VALIDATORS as ValidatorFn[]))
else form.addControl(name, new FormControl(defaultValue))
}
}
private isRecursiveField (field: any) {
return field && typeof field === 'object' && !field.MESSAGES && !field.VALIDATORS
}
}

View File

@ -12,7 +12,7 @@
}
/deep/ {
.popover-help.popover {
.help-popover {
max-width: 300px;
.popover-body {

View File

@ -29,7 +29,7 @@ export class RemoteSubscribeComponent extends FormReactive implements OnInit {
}
onValidKey () {
this.onValueChanged()
this.check()
if (!this.form.valid) return
this.formValidated()

View File

@ -70,7 +70,7 @@ export class VideoCommentAddComponent extends FormReactive implements OnInit {
}
onValidKey () {
this.onValueChanged()
this.check()
if (!this.form.valid) return
this.formValidated()