diff options
Diffstat (limited to 'client/src')
5 files changed, 77 insertions, 24 deletions
diff --git a/client/src/app/+videos/+video-edit/shared/video-edit.component.html b/client/src/app/+videos/+video-edit/shared/video-edit.component.html index aa88d6c4c..f65cd8883 100644 --- a/client/src/app/+videos/+video-edit/shared/video-edit.component.html +++ b/client/src/app/+videos/+video-edit/shared/video-edit.component.html | |||
@@ -146,6 +146,13 @@ | |||
146 | </ng-template> | 146 | </ng-template> |
147 | </my-peertube-checkbox> | 147 | </my-peertube-checkbox> |
148 | 148 | ||
149 | <ng-container ngbNavItem *ngIf="getPluginsFields('main').length !== 0"> | ||
150 | |||
151 | <div *ngFor="let pluginSetting of getPluginsFields('main')" class="form-group" [hidden]="isPluginFieldHidden(pluginSetting)"> | ||
152 | <my-dynamic-form-field [form]="pluginDataFormGroup" [formErrors]="formErrors['pluginData']" [setting]="pluginSetting.commonOptions"></my-dynamic-form-field> | ||
153 | </div> | ||
154 | |||
155 | </ng-container> | ||
149 | </div> | 156 | </div> |
150 | </div> | 157 | </div> |
151 | </ng-template> | 158 | </ng-template> |
@@ -339,15 +346,15 @@ | |||
339 | </ng-template> | 346 | </ng-template> |
340 | </ng-container> | 347 | </ng-container> |
341 | 348 | ||
342 | <ng-container ngbNavItem *ngIf="pluginFields.length !== 0"> | 349 | <ng-container ngbNavItem *ngIf="getPluginsFields('plugin-settings').length !== 0"> |
343 | <a ngbNavLink i18n>Plugin settings</a> | 350 | <a ngbNavLink i18n>Plugin settings</a> |
344 | 351 | ||
345 | <ng-template ngbNavContent> | 352 | <ng-template ngbNavContent> |
346 | <div class="row plugin-settings"> | 353 | <div class="row plugin-settings"> |
347 | 354 | ||
348 | <div class="col-md-12 col-xl-8"> | 355 | <div class="col-md-12 col-xl-8"> |
349 | <div *ngFor="let pluginSetting of pluginFields" class="form-group" [hidden]="isPluginFieldHidden(pluginSetting)"> | 356 | <div *ngFor="let pluginSetting of getPluginsFields('plugin-settings')" class="form-group" [hidden]="isPluginFieldHidden(pluginSetting)"> |
350 | <my-dynamic-form-field [form]="pluginDataFormGroup" [formErrors]="formErrors" [setting]="pluginSetting.commonOptions"></my-dynamic-form-field> | 357 | <my-dynamic-form-field [form]="pluginDataFormGroup" [formErrors]="formErrors['pluginData']" [setting]="pluginSetting.commonOptions"></my-dynamic-form-field> |
351 | </div> | 358 | </div> |
352 | </div> | 359 | </div> |
353 | 360 | ||
diff --git a/client/src/app/+videos/+video-edit/shared/video-edit.component.ts b/client/src/app/+videos/+video-edit/shared/video-edit.component.ts index 2bec933e9..a03005bcb 100644 --- a/client/src/app/+videos/+video-edit/shared/video-edit.component.ts +++ b/client/src/app/+videos/+video-edit/shared/video-edit.component.ts | |||
@@ -1,10 +1,11 @@ | |||
1 | import { forkJoin } from 'rxjs' | 1 | import { forkJoin } from 'rxjs' |
2 | import { map } from 'rxjs/operators' | 2 | import { map } from 'rxjs/operators' |
3 | import { SelectChannelItem } from 'src/types/select-options-item.model' | 3 | import { SelectChannelItem } from 'src/types/select-options-item.model' |
4 | import { Component, EventEmitter, Input, NgZone, OnDestroy, OnInit, Output, ViewChild } from '@angular/core' | 4 | import { ChangeDetectorRef, Component, EventEmitter, Input, NgZone, OnDestroy, OnInit, Output, ViewChild } from '@angular/core' |
5 | import { FormArray, FormControl, FormGroup, Validators } from '@angular/forms' | 5 | import { AbstractControl, FormArray, FormControl, FormGroup, ValidationErrors, Validators } from '@angular/forms' |
6 | import { HooksService, PluginService, ServerService } from '@app/core' | 6 | import { HooksService, PluginService, ServerService } from '@app/core' |
7 | import { removeElementFromArray } from '@app/helpers' | 7 | import { removeElementFromArray } from '@app/helpers' |
8 | import { BuildFormValidator } from '@app/shared/form-validators' | ||
8 | import { | 9 | import { |
9 | VIDEO_CATEGORY_VALIDATOR, | 10 | VIDEO_CATEGORY_VALIDATOR, |
10 | VIDEO_CHANNEL_VALIDATOR, | 11 | VIDEO_CHANNEL_VALIDATOR, |
@@ -101,7 +102,8 @@ export class VideoEditComponent implements OnInit, OnDestroy { | |||
101 | private instanceService: InstanceService, | 102 | private instanceService: InstanceService, |
102 | private i18nPrimengCalendarService: I18nPrimengCalendarService, | 103 | private i18nPrimengCalendarService: I18nPrimengCalendarService, |
103 | private ngZone: NgZone, | 104 | private ngZone: NgZone, |
104 | private hooks: HooksService | 105 | private hooks: HooksService, |
106 | private cd: ChangeDetectorRef | ||
105 | ) { | 107 | ) { |
106 | this.calendarTimezone = this.i18nPrimengCalendarService.getTimezone() | 108 | this.calendarTimezone = this.i18nPrimengCalendarService.getTimezone() |
107 | this.calendarDateFormat = this.i18nPrimengCalendarService.getDateFormat() | 109 | this.calendarDateFormat = this.i18nPrimengCalendarService.getDateFormat() |
@@ -116,7 +118,7 @@ export class VideoEditComponent implements OnInit, OnDestroy { | |||
116 | licence: this.serverConfig.defaults.publish.licence, | 118 | licence: this.serverConfig.defaults.publish.licence, |
117 | tags: [] | 119 | tags: [] |
118 | } | 120 | } |
119 | const obj: any = { | 121 | const obj: { [ id: string ]: BuildFormValidator } = { |
120 | name: VIDEO_NAME_VALIDATOR, | 122 | name: VIDEO_NAME_VALIDATOR, |
121 | privacy: VIDEO_PRIVACY_VALIDATOR, | 123 | privacy: VIDEO_PRIVACY_VALIDATOR, |
122 | channelId: VIDEO_CHANNEL_VALIDATOR, | 124 | channelId: VIDEO_CHANNEL_VALIDATOR, |
@@ -138,7 +140,7 @@ export class VideoEditComponent implements OnInit, OnDestroy { | |||
138 | saveReplay: null | 140 | saveReplay: null |
139 | } | 141 | } |
140 | 142 | ||
141 | this.formValidatorService.updateForm( | 143 | this.formValidatorService.updateFormGroup( |
142 | this.form, | 144 | this.form, |
143 | this.formErrors, | 145 | this.formErrors, |
144 | this.validationMessages, | 146 | this.validationMessages, |
@@ -275,6 +277,14 @@ export class VideoEditComponent implements OnInit, OnDestroy { | |||
275 | }) | 277 | }) |
276 | } | 278 | } |
277 | 279 | ||
280 | getPluginsFields (tab: 'main' | 'plugin-settings') { | ||
281 | return this.pluginFields.filter(p => { | ||
282 | const wanted = p.videoFormOptions.tab ?? 'plugin-settings' | ||
283 | |||
284 | return wanted === tab | ||
285 | }) | ||
286 | } | ||
287 | |||
278 | private sortVideoCaptions () { | 288 | private sortVideoCaptions () { |
279 | this.videoCaptions.sort((v1, v2) => { | 289 | this.videoCaptions.sort((v1, v2) => { |
280 | if (v1.language.label < v2.language.label) return -1 | 290 | if (v1.language.label < v2.language.label) return -1 |
@@ -289,15 +299,44 @@ export class VideoEditComponent implements OnInit, OnDestroy { | |||
289 | 299 | ||
290 | if (this.pluginFields.length === 0) return | 300 | if (this.pluginFields.length === 0) return |
291 | 301 | ||
292 | const obj: any = {} | 302 | const pluginObj: { [ id: string ]: BuildFormValidator } = {} |
303 | const pluginValidationMessages: FormReactiveValidationMessages = {} | ||
304 | const pluginFormErrors: any = {} | ||
305 | const pluginDefaults: any = {} | ||
293 | 306 | ||
294 | for (const setting of this.pluginFields) { | 307 | for (const setting of this.pluginFields) { |
295 | obj[setting.commonOptions.name] = new FormControl(setting.commonOptions.default) | 308 | const validator = (control: AbstractControl): ValidationErrors | null => { |
309 | if (!setting.commonOptions.error) return null | ||
310 | |||
311 | const error = setting.commonOptions.error({ formValues: this.form.value, value: control.value }) | ||
312 | |||
313 | return error?.error ? { [setting.commonOptions.name]: error.text } : null | ||
314 | } | ||
315 | |||
316 | const name = setting.commonOptions.name | ||
317 | |||
318 | pluginObj[name] = { | ||
319 | VALIDATORS: [ validator ], | ||
320 | MESSAGES: {} | ||
321 | } | ||
322 | |||
323 | pluginDefaults[name] = setting.commonOptions.default | ||
296 | } | 324 | } |
297 | 325 | ||
298 | this.pluginDataFormGroup = new FormGroup(obj) | 326 | this.pluginDataFormGroup = new FormGroup({}) |
327 | this.formValidatorService.updateFormGroup( | ||
328 | this.pluginDataFormGroup, | ||
329 | pluginFormErrors, | ||
330 | pluginValidationMessages, | ||
331 | pluginObj, | ||
332 | pluginDefaults | ||
333 | ) | ||
334 | |||
299 | this.form.addControl('pluginData', this.pluginDataFormGroup) | 335 | this.form.addControl('pluginData', this.pluginDataFormGroup) |
336 | this.formErrors['pluginData'] = pluginFormErrors | ||
337 | this.validationMessages['pluginData'] = pluginValidationMessages | ||
300 | 338 | ||
339 | this.cd.detectChanges() | ||
301 | this.pluginFieldsAdded.emit() | 340 | this.pluginFieldsAdded.emit() |
302 | } | 341 | } |
303 | 342 | ||
diff --git a/client/src/app/+videos/+video-edit/video-add-components/video-upload.component.ts b/client/src/app/+videos/+video-edit/video-add-components/video-upload.component.ts index 76f154249..fa5800897 100644 --- a/client/src/app/+videos/+video-edit/video-add-components/video-upload.component.ts +++ b/client/src/app/+videos/+video-edit/video-add-components/video-upload.component.ts | |||
@@ -226,7 +226,7 @@ export class VideoUploadComponent extends VideoSend implements OnInit, OnDestroy | |||
226 | } | 226 | } |
227 | 227 | ||
228 | isPublishingButtonDisabled () { | 228 | isPublishingButtonDisabled () { |
229 | return !this.form.valid || | 229 | return !this.checkForm() || |
230 | this.isUpdatingVideo === true || | 230 | this.isUpdatingVideo === true || |
231 | this.videoUploaded !== true || | 231 | this.videoUploaded !== true || |
232 | !this.videoUploadedIds.id | 232 | !this.videoUploadedIds.id |
@@ -240,7 +240,7 @@ export class VideoUploadComponent extends VideoSend implements OnInit, OnDestroy | |||
240 | } | 240 | } |
241 | 241 | ||
242 | updateSecondStep () { | 242 | updateSecondStep () { |
243 | if (this.isPublishingButtonDisabled() || !this.checkForm()) { | 243 | if (this.isPublishingButtonDisabled()) { |
244 | return | 244 | return |
245 | } | 245 | } |
246 | 246 | ||
diff --git a/client/src/app/shared/shared-forms/form-reactive.ts b/client/src/app/shared/shared-forms/form-reactive.ts index f2ce82360..30b59c141 100644 --- a/client/src/app/shared/shared-forms/form-reactive.ts +++ b/client/src/app/shared/shared-forms/form-reactive.ts | |||
@@ -56,13 +56,18 @@ export abstract class FormReactive { | |||
56 | 56 | ||
57 | if (control.dirty) this.formChanged = true | 57 | if (control.dirty) this.formChanged = true |
58 | 58 | ||
59 | // Don't care if dirty on force check | 59 | if (forceCheck) control.updateValueAndValidity({ emitEvent: false }) |
60 | const isDirty = control.dirty || forceCheck === true | 60 | if (!control || !control.dirty || !control.enabled || control.valid) continue |
61 | if (control && isDirty && control.enabled && !control.valid) { | 61 | |
62 | const messages = validationMessages[field] | 62 | const staticMessages = validationMessages[field] |
63 | for (const key of Object.keys(control.errors)) { | 63 | for (const key of Object.keys(control.errors)) { |
64 | formErrors[field] += messages[key] + ' ' | 64 | const formErrorValue = control.errors[key] |
65 | } | 65 | |
66 | // Try to find error message in static validation messages first | ||
67 | // Then check if the validator returns a string that is the error | ||
68 | if (typeof formErrorValue === 'boolean') formErrors[field] += staticMessages[key] + ' ' | ||
69 | else if (typeof formErrorValue === 'string') formErrors[field] += control.errors[key] | ||
70 | else throw new Error('Form error value of ' + field + ' is invalid') | ||
66 | } | 71 | } |
67 | } | 72 | } |
68 | } | 73 | } |
diff --git a/client/src/app/shared/shared-forms/form-validator.service.ts b/client/src/app/shared/shared-forms/form-validator.service.ts index c0664de5f..055fbb2d9 100644 --- a/client/src/app/shared/shared-forms/form-validator.service.ts +++ b/client/src/app/shared/shared-forms/form-validator.service.ts | |||
@@ -40,7 +40,7 @@ export class FormValidatorService { | |||
40 | return { form, formErrors, validationMessages } | 40 | return { form, formErrors, validationMessages } |
41 | } | 41 | } |
42 | 42 | ||
43 | updateForm ( | 43 | updateFormGroup ( |
44 | form: FormGroup, | 44 | form: FormGroup, |
45 | formErrors: FormReactiveErrors, | 45 | formErrors: FormReactiveErrors, |
46 | validationMessages: FormReactiveValidationMessages, | 46 | validationMessages: FormReactiveValidationMessages, |
@@ -52,7 +52,7 @@ export class FormValidatorService { | |||
52 | 52 | ||
53 | const field = obj[name] | 53 | const field = obj[name] |
54 | if (this.isRecursiveField(field)) { | 54 | if (this.isRecursiveField(field)) { |
55 | this.updateForm( | 55 | this.updateFormGroup( |
56 | form[name], | 56 | form[name], |
57 | formErrors[name] as FormReactiveErrors, | 57 | formErrors[name] as FormReactiveErrors, |
58 | validationMessages[name] as FormReactiveValidationMessages, | 58 | validationMessages[name] as FormReactiveValidationMessages, |
@@ -66,8 +66,10 @@ export class FormValidatorService { | |||
66 | 66 | ||
67 | const defaultValue = defaultValues[name] || '' | 67 | const defaultValue = defaultValues[name] || '' |
68 | 68 | ||
69 | if (field?.VALIDATORS) form.addControl(name, new FormControl(defaultValue, field.VALIDATORS as ValidatorFn[])) | 69 | form.addControl( |
70 | else form.addControl(name, new FormControl(defaultValue)) | 70 | name, |
71 | new FormControl(defaultValue, field?.VALIDATORS as ValidatorFn[]) | ||
72 | ) | ||
71 | } | 73 | } |
72 | } | 74 | } |
73 | 75 | ||