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 i18n class="inner-form-title">Instance</div>
<div class="form-group"> <ng-container formGroupName="instance">
<label i18n for="instanceName">Name</label> <div class="form-group">
<input <label i18n for="instanceName">Name</label>
type="text" id="instanceName" <input
formControlName="instanceName" [ngClass]="{ 'input-error': formErrors['instanceName'] }" type="text" id="instanceName"
> formControlName="name" [ngClass]="{ 'input-error': formErrors.instance.name }"
<div *ngIf="formErrors.instanceName" class="form-error"> >
{{ formErrors.instanceName }} <div *ngIf="formErrors.instance.name" class="form-error">{{ formErrors.instance.name }}</div>
</div> </div>
</div>
<div class="form-group"> <div class="form-group">
<label i18n for="instanceShortDescription">Short description</label> <label i18n for="instanceShortDescription">Short description</label>
<textarea <textarea
id="instanceShortDescription" formControlName="instanceShortDescription" id="instanceShortDescription" formControlName="shortDescription"
[ngClass]="{ 'input-error': formErrors['instanceShortDescription'] }" [ngClass]="{ 'input-error': formErrors['instance.shortDescription'] }"
></textarea> ></textarea>
<div *ngIf="formErrors.instanceShortDescription" class="form-error"> <div *ngIf="formErrors.instance.shortDescription" class="form-error">{{ formErrors.instance.shortDescription }}</div>
{{ formErrors.instanceShortDescription }}
</div> </div>
</div>
<div class="form-group"> <div class="form-group">
<label i18n for="instanceDescription">Description</label><my-help helpType="markdownText"></my-help> <label i18n for="instanceDescription">Description</label><my-help helpType="markdownText"></my-help>
<my-markdown-textarea <my-markdown-textarea
id="instanceDescription" formControlName="instanceDescription" textareaWidth="500px" [previewColumn]="true" id="instanceDescription" formControlName="description" textareaWidth="500px" [previewColumn]="true"
[classes]="{ 'input-error': formErrors['instanceDescription'] }" [classes]="{ 'input-error': formErrors['instance.description'] }"
></my-markdown-textarea> ></my-markdown-textarea>
<div *ngIf="formErrors.instanceDescription" class="form-error"> <div *ngIf="formErrors.instance.description" class="form-error">{{ formErrors.instance.description }}</div>
{{ formErrors.instanceDescription }}
</div> </div>
</div>
<div class="form-group"> <div class="form-group">
<label i18n for="instanceTerms">Terms</label><my-help helpType="markdownText"></my-help> <label i18n for="instanceTerms">Terms</label><my-help helpType="markdownText"></my-help>
<my-markdown-textarea <my-markdown-textarea
id="instanceTerms" formControlName="instanceTerms" textareaWidth="500px" [previewColumn]="true" id="instanceTerms" formControlName="terms" textareaWidth="500px" [previewColumn]="true"
[ngClass]="{ 'input-error': formErrors['instanceTerms'] }" [ngClass]="{ 'input-error': formErrors['instance.terms'] }"
></my-markdown-textarea> ></my-markdown-textarea>
<div *ngIf="formErrors.instanceTerms" class="form-error"> <div *ngIf="formErrors.instance.terms" class="form-error">{{ formErrors.instance.terms }}</div>
{{ formErrors.instanceTerms }}
</div> </div>
</div>
<div class="form-group"> <div class="form-group">
<label i18n for="instanceDefaultClientRoute">Default client route</label> <label i18n for="instanceDefaultClientRoute">Default client route</label>
<div class="peertube-select-container"> <div class="peertube-select-container">
<select id="instanceDefaultClientRoute" formControlName="instanceDefaultClientRoute"> <select id="instanceDefaultClientRoute" formControlName="defaultClientRoute">
<option i18n value="/videos/overview">Videos Overview</option> <option i18n value="/videos/overview">Videos Overview</option>
<option i18n value="/videos/trending">Videos Trending</option> <option i18n value="/videos/trending">Videos Trending</option>
<option i18n value="/videos/recently-added">Videos Recently Added</option> <option i18n value="/videos/recently-added">Videos Recently Added</option>
<option i18n value="/videos/local">Local videos</option> <option i18n value="/videos/local">Local videos</option>
</select> </select>
</div>
<div *ngIf="formErrors.instance.defaultClientRoute" class="form-error">{{ formErrors.instance.defaultClientRoute }}</div>
</div> </div>
<div *ngIf="formErrors.instanceDefaultClientRoute" class="form-error">
{{ formErrors.instanceDefaultClientRoute }}
</div>
</div>
<div class="form-group"> <div class="form-group">
<label i18n for="instanceDefaultNSFWPolicy">Policy on videos containing sensitive content</label> <label i18n for="instanceDefaultNSFWPolicy">Policy on videos containing sensitive content</label>
<my-help <my-help
helpType="custom" i18n-customHtml 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." customHtml="With <strong>Do not list</strong> or <strong>Blur thumbnails</strong>, a confirmation will be requested to watch the video."
></my-help> ></my-help>
<div class="peertube-select-container"> <div class="peertube-select-container">
<select id="instanceDefaultNSFWPolicy" formControlName="instanceDefaultNSFWPolicy"> <select id="instanceDefaultNSFWPolicy" formControlName="defaultNSFWPolicy">
<option i18n value="do_not_list">Do not list</option> <option i18n value="do_not_list">Do not list</option>
<option i18n value="blur">Blur thumbnails</option> <option i18n value="blur">Blur thumbnails</option>
<option i18n value="display">Display</option> <option i18n value="display">Display</option>
</select> </select>
</div>
<div *ngIf="formErrors.instance.defaultNSFWPolicy" class="form-error">{{ formErrors.instance.defaultNSFWPolicy }}</div>
</div> </div>
<div *ngIf="formErrors.instanceDefaultNSFWPolicy" class="form-error"> </ng-container>
{{ formErrors.instanceDefaultNSFWPolicy }}
</div>
</div>
<div i18n class="inner-form-title">Signup</div> <div i18n class="inner-form-title">Signup</div>
<div class="form-group"> <ng-container formGroupName="signup">
<my-peertube-checkbox <div class="form-group">
inputName="signupEnabled" formControlName="signupEnabled" <my-peertube-checkbox
i18n-labelText labelText="Signup enabled" inputName="signupEnabled" formControlName="enabled"
></my-peertube-checkbox> i18n-labelText labelText="Signup enabled"
</div> ></my-peertube-checkbox>
<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 }}
</div> </div>
</div>
<div i18n class="inner-form-title">Import</div> <div class="form-group">
<my-peertube-checkbox *ngIf="isSignupEnabled()"
<div class="form-group"> inputName="signupRequiresEmailVerification" formControlName="requiresEmailVerification"
<my-peertube-checkbox i18n-labelText labelText="Signup requires email verification"
inputName="importVideosHttpEnabled" formControlName="importVideosHttpEnabled" ></my-peertube-checkbox>
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> </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 i18n class="inner-form-title">Users</div>
<div class="form-group"> <ng-container formGroupName="user">
<label i18n for="userVideoQuota">User default video quota</label> <div class="form-group">
<div class="peertube-select-container"> <label i18n for="userVideoQuota">User default video quota</label>
<select id="userVideoQuota" formControlName="userVideoQuota"> <div class="peertube-select-container">
<option *ngFor="let videoQuotaOption of videoQuotaOptions" [value]="videoQuotaOption.value"> <select id="userVideoQuota" formControlName="videoQuota">
{{ videoQuotaOption.label }} <option *ngFor="let videoQuotaOption of videoQuotaOptions" [value]="videoQuotaOption.value">
</option> {{ videoQuotaOption.label }}
</select> </option>
</select>
</div>
<div *ngIf="formErrors.user.videoQuota" class="form-error">{{ formErrors.user.videoQuota }}</div>
</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> </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>
<div class="form-group"> <div class="form-group" formGroupName="contactForm">
<label i18n for="userVideoQuotaDaily">User default daily upload limit</label> <my-peertube-checkbox
<div class="peertube-select-container"> inputName="enableContactForm" formControlName="enabled"
<select id="userVideoQuotaDaily" formControlName="userVideoQuotaDaily"> i18n-labelText labelText="Enable contact form"
<option *ngFor="let videoQuotaDailyOption of videoQuotaDailyOptions" [value]="videoQuotaDailyOption.value"> ></my-peertube-checkbox>
{{ videoQuotaDailyOption.label }}
</option>
</select>
</div>
<div *ngIf="formErrors.userVideoQuotaDaily" class="form-error">
{{ formErrors.userVideoQuotaDaily }}
</div>
</div> </div>
</ng-template> </ng-template>
</ngb-tab> </ngb-tab>
@ -177,30 +177,35 @@
<ng-template ngbTabContent> <ng-template ngbTabContent>
<div i18n class="inner-form-title">Twitter</div> <div i18n class="inner-form-title">Twitter</div>
<div class="form-group"> <ng-container formGroupName="services">
<label i18n for="signupLimit">Your Twitter username</label> <ng-container formGroupName="twitter">
<my-help
helpType="custom" i18n-customHtml <div class="form-group">
customHtml="Indicates the Twitter account for the website or platform on which the content was published." <label i18n for="signupLimit">Your Twitter username</label>
></my-help> <my-help
<input helpType="custom" i18n-customHtml
type="text" id="servicesTwitterUsername" customHtml="Indicates the Twitter account for the website or platform on which the content was published."
formControlName="servicesTwitterUsername" [ngClass]="{ 'input-error': formErrors['servicesTwitterUsername'] }" ></my-help>
> <input
<div *ngIf="formErrors.servicesTwitterUsername" class="form-error"> type="text" id="servicesTwitterUsername"
{{ formErrors.servicesTwitterUsername }} formControlName="username" [ngClass]="{ 'input-error': formErrors['services.twitter.username'] }"
</div> >
</div> <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> </ng-template>
</ngb-tab> </ngb-tab>
@ -209,45 +214,48 @@
<div i18n class="inner-form-title">Transcoding</div> <div i18n class="inner-form-title">Transcoding</div>
<div class="form-group"> <ng-container formGroupName="transcoding">
<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()">
<div class="form-group"> <div class="form-group">
<my-peertube-checkbox <my-peertube-checkbox
inputName="transcodingAllowAdditionalExtensions" formControlName="transcodingAllowAdditionalExtensions" inputName="transcodingEnabled" formControlName="enabled"
i18n-labelText labelText="Allow additional extensions" i18n-labelText labelText="Transcoding enabled"
i18n-helpHtml helpHtml="Allow your users to upload .mkv, .mov, .avi, .flv videos" i18n-helpHtml helpHtml="If you disable transcoding, many videos from your users will not work!"
></my-peertube-checkbox> ></my-peertube-checkbox>
</div> </div>
<div class="form-group"> <ng-container *ngIf="isTranscodingEnabled()">
<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>
<div class="form-group" *ngFor="let resolution of resolutions"> <div class="form-group">
<my-peertube-checkbox <my-peertube-checkbox
[inputName]="getResolutionKey(resolution)" [formControlName]="getResolutionKey(resolution)" inputName="transcodingAllowAdditionalExtensions" formControlName="allowAdditionalExtensions"
i18n-labelText labelText="Resolution {{resolution}} enabled" i18n-labelText labelText="Allow additional extensions"
></my-peertube-checkbox> i18n-helpHtml helpHtml="Allow your users to upload .mkv, .mov, .avi, .flv videos"
</div> ></my-peertube-checkbox>
</ng-template> </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"> <div i18n class="inner-form-title">
Cache Cache
@ -258,74 +266,73 @@
></my-help> ></my-help>
</div> </div>
<div class="form-group"> <ng-container formGroupName="cache">
<label i18n for="cachePreviewsSize">Previews cache size</label> <div class="form-group" formGroupName="previews">
<input <label i18n for="cachePreviewsSize">Previews cache size</label>
type="text" id="cachePreviewsSize" <input
formControlName="cachePreviewsSize" [ngClass]="{ 'input-error': formErrors['cachePreviewsSize'] }" type="text" id="cachePreviewsSize"
> formControlName="size" [ngClass]="{ 'input-error': formErrors['cache.previews.size'] }"
<div *ngIf="formErrors.cachePreviewsSize" class="form-error"> >
{{ formErrors.cachePreviewsSize }} <div *ngIf="formErrors.cache.previews.size" class="form-error">{{ formErrors.cache.previews.size }}</div>
</div> </div>
</div>
<div class="form-group"> <div class="form-group" formGroupName="captions">
<label i18n for="cachePreviewsSize">Video captions cache size</label> <label i18n for="cacheCaptionsSize">Video captions cache size</label>
<input <input
type="text" id="cacheCaptionsSize" type="text" id="cacheCaptionsSize"
formControlName="cacheCaptionsSize" [ngClass]="{ 'input-error': formErrors['cacheCaptionsSize'] }" formControlName="size" [ngClass]="{ 'input-error': formErrors['cache.captions.size'] }"
> >
<div *ngIf="formErrors.cacheCaptionsSize" class="form-error"> <div *ngIf="formErrors.cache.captions.size" class="form-error">{{ formErrors.cache.captions.size }}</div>
{{ formErrors.cacheCaptionsSize }}
</div> </div>
</div> </ng-container>
<div i18n class="inner-form-title">Customizations</div> <div i18n class="inner-form-title">Customizations</div>
<div class="form-group"> <ng-container formGroupName="instance">
<label i18n for="customizationJavascript">JavaScript</label> <ng-container formGroupName="customizations">
<my-help <div class="form-group">
helpType="custom" i18n-customHtml <label i18n for="customizationJavascript">JavaScript</label>
customHtml="Write directly JavaScript code.<br />Example: <pre>console.log('my instance is amazing');</pre>" <my-help
></my-help> helpType="custom" i18n-customHtml
<textarea customHtml="Write directly JavaScript code.<br />Example: <pre>console.log('my instance is amazing');</pre>"
id="customizationJavascript" formControlName="customizationJavascript" ></my-help>
[ngClass]="{ 'input-error': formErrors['customizationJavascript'] }" <textarea
></textarea> id="customizationJavascript" formControlName="javascript"
<div *ngIf="formErrors.customizationJavascript" class="form-error"> [ngClass]="{ 'input-error': formErrors['instance.customizations.javascript'] }"
{{ formErrors.customizationJavascript }} ></textarea>
</div> <div *ngIf="formErrors.instance.customizations.javascript" class="form-error">{{ formErrors.instance.customizations.javascript }}</div>
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="customizationCSS">CSS</label> <label for="customizationCSS">CSS</label>
<my-help <my-help
helpType="custom" helpType="custom"
i18n-customHtml i18n-customHtml
customHtml=" customHtml="
Write directly CSS code. Example:<br /> Write directly CSS code. Example:<br />
<pre> <pre>
body {{ '{' }} body {{ '{' }}
background-color: red; background-color: red;
{{ '}' }} {{ '}' }}
</pre> </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> </ng-template>
</ngb-tab> </ngb-tab>
</ngb-tabset> </ngb-tabset>

View File

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

View File

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

View File

@ -1,11 +1,9 @@
import { FormGroup } from '@angular/forms' import { FormGroup } from '@angular/forms'
import { BuildFormArgument, BuildFormDefaultValues, FormValidatorService } from '@app/shared/forms/form-validators/form-validator.service' 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 = { export type FormReactiveValidationMessages = {
[ id: string ]: { [ id: string ]: { [ name: string ]: string } | FormReactiveValidationMessages
[ name: string ]: string
}
} }
export abstract class FormReactive { export abstract class FormReactive {
@ -23,29 +21,49 @@ export abstract class FormReactive {
this.formErrors = formErrors this.formErrors = formErrors
this.validationMessages = validationMessages 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) { protected forceCheck () {
for (const field in this.formErrors) { 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) // clear previous error message (if any)
this.formErrors[ field ] = '' formErrors[ field ] = ''
const control = this.form.get(field) const control = form.get(field)
if (control.dirty) this.formChanged = true if (control.dirty) this.formChanged = true
// Don't care if dirty on force check // Don't care if dirty on force check
const isDirty = control.dirty || forceCheck === true const isDirty = control.dirty || forceCheck === true
if (control && isDirty && !control.valid) { if (control && isDirty && !control.valid) {
const messages = this.validationMessages[ field ] const messages = validationMessages[ field ]
for (const key in control.errors) { 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 } MESSAGES: { [ name: string ]: string }
} }
export type BuildFormArgument = { export type BuildFormArgument = {
[ id: string ]: BuildFormValidator [ id: string ]: BuildFormValidator | BuildFormArgument
} }
export type BuildFormDefaultValues = { export type BuildFormDefaultValues = {
[ name: string ]: string | string[] [ name: string ]: string | string[] | BuildFormDefaultValues
} }
@Injectable() @Injectable()
@ -29,7 +29,16 @@ export class FormValidatorService {
formErrors[name] = '' formErrors[name] = ''
const field = obj[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] || '' const defaultValue = defaultValues[name] || ''
@ -52,13 +61,27 @@ export class FormValidatorService {
formErrors[name] = '' formErrors[name] = ''
const field = obj[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] || '' 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)) 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/ { /deep/ {
.popover-help.popover { .help-popover {
max-width: 300px; max-width: 300px;
.popover-body { .popover-body {

View File

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

View File

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