diff options
author | Chocobozzz <me@florianbigard.com> | 2020-06-23 14:10:17 +0200 |
---|---|---|
committer | Chocobozzz <chocobozzz@cpy.re> | 2020-06-23 16:00:49 +0200 |
commit | 67ed6552b831df66713bac9e672738796128d33f (patch) | |
tree | 59c97d41e0b49d75a90aa3de987968ab9b1ff447 /client/src/app/shared/shared-forms/form-validators | |
parent | 0c4bacbff53bc732f5a2677d62a6ead7752e2405 (diff) | |
download | PeerTube-67ed6552b831df66713bac9e672738796128d33f.tar.gz PeerTube-67ed6552b831df66713bac9e672738796128d33f.tar.zst PeerTube-67ed6552b831df66713bac9e672738796128d33f.zip |
Reorganize client shared modules
Diffstat (limited to 'client/src/app/shared/shared-forms/form-validators')
18 files changed, 915 insertions, 0 deletions
diff --git a/client/src/app/shared/shared-forms/form-validators/batch-domains-validators.service.ts b/client/src/app/shared/shared-forms/form-validators/batch-domains-validators.service.ts new file mode 100644 index 000000000..f270b602b --- /dev/null +++ b/client/src/app/shared/shared-forms/form-validators/batch-domains-validators.service.ts | |||
@@ -0,0 +1,69 @@ | |||
1 | import { Injectable } from '@angular/core' | ||
2 | import { ValidatorFn, Validators } from '@angular/forms' | ||
3 | import { I18n } from '@ngx-translate/i18n-polyfill' | ||
4 | import { BuildFormValidator } from './form-validator.service' | ||
5 | import { validateHost } from './host' | ||
6 | |||
7 | @Injectable() | ||
8 | export class BatchDomainsValidatorsService { | ||
9 | readonly DOMAINS: BuildFormValidator | ||
10 | |||
11 | constructor (private i18n: I18n) { | ||
12 | this.DOMAINS = { | ||
13 | VALIDATORS: [ Validators.required, this.validDomains, this.isHostsUnique ], | ||
14 | MESSAGES: { | ||
15 | 'required': this.i18n('Domain is required.'), | ||
16 | 'validDomains': this.i18n('Domains entered are invalid.'), | ||
17 | 'uniqueDomains': this.i18n('Domains entered contain duplicates.') | ||
18 | } | ||
19 | } | ||
20 | } | ||
21 | |||
22 | getNotEmptyHosts (hosts: string) { | ||
23 | return hosts | ||
24 | .split('\n') | ||
25 | .filter((host: string) => host && host.length !== 0) // Eject empty hosts | ||
26 | } | ||
27 | |||
28 | private validDomains: ValidatorFn = (control) => { | ||
29 | if (!control.value) return null | ||
30 | |||
31 | const newHostsErrors = [] | ||
32 | const hosts = this.getNotEmptyHosts(control.value) | ||
33 | |||
34 | for (const host of hosts) { | ||
35 | if (validateHost(host) === false) { | ||
36 | newHostsErrors.push(this.i18n('{{host}} is not valid', { host })) | ||
37 | } | ||
38 | } | ||
39 | |||
40 | /* Is not valid. */ | ||
41 | if (newHostsErrors.length !== 0) { | ||
42 | return { | ||
43 | 'validDomains': { | ||
44 | reason: 'invalid', | ||
45 | value: newHostsErrors.join('. ') + '.' | ||
46 | } | ||
47 | } | ||
48 | } | ||
49 | |||
50 | /* Is valid. */ | ||
51 | return null | ||
52 | } | ||
53 | |||
54 | private isHostsUnique: ValidatorFn = (control) => { | ||
55 | if (!control.value) return null | ||
56 | |||
57 | const hosts = this.getNotEmptyHosts(control.value) | ||
58 | |||
59 | if (hosts.every((host: string) => hosts.indexOf(host) === hosts.lastIndexOf(host))) { | ||
60 | return null | ||
61 | } else { | ||
62 | return { | ||
63 | 'uniqueDomains': { | ||
64 | reason: 'invalid' | ||
65 | } | ||
66 | } | ||
67 | } | ||
68 | } | ||
69 | } | ||
diff --git a/client/src/app/shared/shared-forms/form-validators/custom-config-validators.service.ts b/client/src/app/shared/shared-forms/form-validators/custom-config-validators.service.ts new file mode 100644 index 000000000..c77aba6a1 --- /dev/null +++ b/client/src/app/shared/shared-forms/form-validators/custom-config-validators.service.ts | |||
@@ -0,0 +1,98 @@ | |||
1 | import { Validators } from '@angular/forms' | ||
2 | import { I18n } from '@ngx-translate/i18n-polyfill' | ||
3 | import { BuildFormValidator } from './form-validator.service' | ||
4 | import { Injectable } from '@angular/core' | ||
5 | |||
6 | @Injectable() | ||
7 | export class CustomConfigValidatorsService { | ||
8 | readonly INSTANCE_NAME: BuildFormValidator | ||
9 | readonly INSTANCE_SHORT_DESCRIPTION: BuildFormValidator | ||
10 | readonly SERVICES_TWITTER_USERNAME: BuildFormValidator | ||
11 | readonly CACHE_PREVIEWS_SIZE: BuildFormValidator | ||
12 | readonly CACHE_CAPTIONS_SIZE: BuildFormValidator | ||
13 | readonly SIGNUP_LIMIT: BuildFormValidator | ||
14 | readonly ADMIN_EMAIL: BuildFormValidator | ||
15 | readonly TRANSCODING_THREADS: BuildFormValidator | ||
16 | readonly INDEX_URL: BuildFormValidator | ||
17 | readonly SEARCH_INDEX_URL: BuildFormValidator | ||
18 | |||
19 | constructor (private i18n: I18n) { | ||
20 | this.INSTANCE_NAME = { | ||
21 | VALIDATORS: [ Validators.required ], | ||
22 | MESSAGES: { | ||
23 | 'required': this.i18n('Instance name is required.') | ||
24 | } | ||
25 | } | ||
26 | |||
27 | this.INSTANCE_SHORT_DESCRIPTION = { | ||
28 | VALIDATORS: [ Validators.max(250) ], | ||
29 | MESSAGES: { | ||
30 | 'max': this.i18n('Short description should not be longer than 250 characters.') | ||
31 | } | ||
32 | } | ||
33 | |||
34 | this.SERVICES_TWITTER_USERNAME = { | ||
35 | VALIDATORS: [ Validators.required ], | ||
36 | MESSAGES: { | ||
37 | 'required': this.i18n('Twitter username is required.') | ||
38 | } | ||
39 | } | ||
40 | |||
41 | this.CACHE_PREVIEWS_SIZE = { | ||
42 | VALIDATORS: [ Validators.required, Validators.min(1), Validators.pattern('[0-9]+') ], | ||
43 | MESSAGES: { | ||
44 | 'required': this.i18n('Previews cache size is required.'), | ||
45 | 'min': this.i18n('Previews cache size must be greater than 1.'), | ||
46 | 'pattern': this.i18n('Previews cache size must be a number.') | ||
47 | } | ||
48 | } | ||
49 | |||
50 | this.CACHE_CAPTIONS_SIZE = { | ||
51 | VALIDATORS: [ Validators.required, Validators.min(1), Validators.pattern('[0-9]+') ], | ||
52 | MESSAGES: { | ||
53 | 'required': this.i18n('Captions cache size is required.'), | ||
54 | 'min': this.i18n('Captions cache size must be greater than 1.'), | ||
55 | 'pattern': this.i18n('Captions cache size must be a number.') | ||
56 | } | ||
57 | } | ||
58 | |||
59 | this.SIGNUP_LIMIT = { | ||
60 | VALIDATORS: [ Validators.required, Validators.min(-1), Validators.pattern('-?[0-9]+') ], | ||
61 | MESSAGES: { | ||
62 | 'required': this.i18n('Signup limit is required.'), | ||
63 | 'min': this.i18n('Signup limit must be greater than 1.'), | ||
64 | 'pattern': this.i18n('Signup limit must be a number.') | ||
65 | } | ||
66 | } | ||
67 | |||
68 | this.ADMIN_EMAIL = { | ||
69 | VALIDATORS: [ Validators.required, Validators.email ], | ||
70 | MESSAGES: { | ||
71 | 'required': this.i18n('Admin email is required.'), | ||
72 | 'email': this.i18n('Admin email must be valid.') | ||
73 | } | ||
74 | } | ||
75 | |||
76 | this.TRANSCODING_THREADS = { | ||
77 | VALIDATORS: [ Validators.required, Validators.min(0) ], | ||
78 | MESSAGES: { | ||
79 | 'required': this.i18n('Transcoding threads is required.'), | ||
80 | 'min': this.i18n('Transcoding threads must be greater or equal to 0.') | ||
81 | } | ||
82 | } | ||
83 | |||
84 | this.INDEX_URL = { | ||
85 | VALIDATORS: [ Validators.pattern(/^https:\/\//) ], | ||
86 | MESSAGES: { | ||
87 | 'pattern': this.i18n('Index URL should be a URL') | ||
88 | } | ||
89 | } | ||
90 | |||
91 | this.SEARCH_INDEX_URL = { | ||
92 | VALIDATORS: [ Validators.pattern(/^https?:\/\//) ], | ||
93 | MESSAGES: { | ||
94 | 'pattern': this.i18n('Search index URL should be a URL') | ||
95 | } | ||
96 | } | ||
97 | } | ||
98 | } | ||
diff --git a/client/src/app/shared/shared-forms/form-validators/form-validator.service.ts b/client/src/app/shared/shared-forms/form-validators/form-validator.service.ts new file mode 100644 index 000000000..dec7d8d9a --- /dev/null +++ b/client/src/app/shared/shared-forms/form-validators/form-validator.service.ts | |||
@@ -0,0 +1,87 @@ | |||
1 | import { FormBuilder, FormControl, FormGroup, ValidatorFn } from '@angular/forms' | ||
2 | import { Injectable } from '@angular/core' | ||
3 | import { FormReactiveErrors, FormReactiveValidationMessages } from '../form-reactive' | ||
4 | |||
5 | export type BuildFormValidator = { | ||
6 | VALIDATORS: ValidatorFn[], | ||
7 | MESSAGES: { [ name: string ]: string } | ||
8 | } | ||
9 | export type BuildFormArgument = { | ||
10 | [ id: string ]: BuildFormValidator | BuildFormArgument | ||
11 | } | ||
12 | export type BuildFormDefaultValues = { | ||
13 | [ name: string ]: string | string[] | BuildFormDefaultValues | ||
14 | } | ||
15 | |||
16 | @Injectable() | ||
17 | export class FormValidatorService { | ||
18 | |||
19 | constructor ( | ||
20 | private formBuilder: FormBuilder | ||
21 | ) {} | ||
22 | |||
23 | buildForm (obj: BuildFormArgument, defaultValues: BuildFormDefaultValues = {}) { | ||
24 | const formErrors: FormReactiveErrors = {} | ||
25 | const validationMessages: FormReactiveValidationMessages = {} | ||
26 | const group: { [key: string]: any } = {} | ||
27 | |||
28 | for (const name of Object.keys(obj)) { | ||
29 | formErrors[name] = '' | ||
30 | |||
31 | const field = obj[name] | ||
32 | if (this.isRecursiveField(field)) { | ||
33 | const result = this.buildForm(field as BuildFormArgument, defaultValues[name] as BuildFormDefaultValues) | ||
34 | group[name] = result.form | ||
35 | formErrors[name] = result.formErrors | ||
36 | validationMessages[name] = result.validationMessages | ||
37 | |||
38 | continue | ||
39 | } | ||
40 | |||
41 | if (field && field.MESSAGES) validationMessages[name] = field.MESSAGES as { [ name: string ]: string } | ||
42 | |||
43 | const defaultValue = defaultValues[name] || '' | ||
44 | |||
45 | if (field && field.VALIDATORS) group[name] = [ defaultValue, field.VALIDATORS ] | ||
46 | else group[name] = [ defaultValue ] | ||
47 | } | ||
48 | |||
49 | const form = this.formBuilder.group(group) | ||
50 | return { form, formErrors, validationMessages } | ||
51 | } | ||
52 | |||
53 | updateForm ( | ||
54 | form: FormGroup, | ||
55 | formErrors: FormReactiveErrors, | ||
56 | validationMessages: FormReactiveValidationMessages, | ||
57 | obj: BuildFormArgument, | ||
58 | defaultValues: BuildFormDefaultValues = {} | ||
59 | ) { | ||
60 | for (const name of Object.keys(obj)) { | ||
61 | formErrors[name] = '' | ||
62 | |||
63 | const field = obj[name] | ||
64 | if (this.isRecursiveField(field)) { | ||
65 | this.updateForm( | ||
66 | form[name], | ||
67 | formErrors[name] as FormReactiveErrors, | ||
68 | validationMessages[name] as FormReactiveValidationMessages, | ||
69 | obj[name] as BuildFormArgument, | ||
70 | defaultValues[name] as BuildFormDefaultValues | ||
71 | ) | ||
72 | continue | ||
73 | } | ||
74 | |||
75 | if (field && field.MESSAGES) validationMessages[name] = field.MESSAGES as { [ name: string ]: string } | ||
76 | |||
77 | const defaultValue = defaultValues[name] || '' | ||
78 | |||
79 | if (field && field.VALIDATORS) form.addControl(name, new FormControl(defaultValue, field.VALIDATORS as ValidatorFn[])) | ||
80 | else form.addControl(name, new FormControl(defaultValue)) | ||
81 | } | ||
82 | } | ||
83 | |||
84 | private isRecursiveField (field: any) { | ||
85 | return field && typeof field === 'object' && !field.MESSAGES && !field.VALIDATORS | ||
86 | } | ||
87 | } | ||
diff --git a/client/src/app/shared/shared-forms/form-validators/host.ts b/client/src/app/shared/shared-forms/form-validators/host.ts new file mode 100644 index 000000000..c18a35f9b --- /dev/null +++ b/client/src/app/shared/shared-forms/form-validators/host.ts | |||
@@ -0,0 +1,8 @@ | |||
1 | export function validateHost (value: string) { | ||
2 | // Thanks to http://stackoverflow.com/a/106223 | ||
3 | const HOST_REGEXP = new RegExp( | ||
4 | '^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$' | ||
5 | ) | ||
6 | |||
7 | return HOST_REGEXP.test(value) | ||
8 | } | ||
diff --git a/client/src/app/shared/shared-forms/form-validators/index.ts b/client/src/app/shared/shared-forms/form-validators/index.ts new file mode 100644 index 000000000..8b71841a9 --- /dev/null +++ b/client/src/app/shared/shared-forms/form-validators/index.ts | |||
@@ -0,0 +1,17 @@ | |||
1 | export * from './batch-domains-validators.service' | ||
2 | export * from './custom-config-validators.service' | ||
3 | export * from './form-validator.service' | ||
4 | export * from './host' | ||
5 | export * from './instance-validators.service' | ||
6 | export * from './login-validators.service' | ||
7 | export * from './reset-password-validators.service' | ||
8 | export * from './user-validators.service' | ||
9 | export * from './video-abuse-validators.service' | ||
10 | export * from './video-accept-ownership-validators.service' | ||
11 | export * from './video-block-validators.service' | ||
12 | export * from './video-captions-validators.service' | ||
13 | export * from './video-change-ownership-validators.service' | ||
14 | export * from './video-channel-validators.service' | ||
15 | export * from './video-comment-validators.service' | ||
16 | export * from './video-playlist-validators.service' | ||
17 | export * from './video-validators.service' | ||
diff --git a/client/src/app/shared/shared-forms/form-validators/instance-validators.service.ts b/client/src/app/shared/shared-forms/form-validators/instance-validators.service.ts new file mode 100644 index 000000000..96a35a48f --- /dev/null +++ b/client/src/app/shared/shared-forms/form-validators/instance-validators.service.ts | |||
@@ -0,0 +1,62 @@ | |||
1 | import { I18n } from '@ngx-translate/i18n-polyfill' | ||
2 | import { Validators } from '@angular/forms' | ||
3 | import { BuildFormValidator } from './form-validator.service' | ||
4 | import { Injectable } from '@angular/core' | ||
5 | |||
6 | @Injectable() | ||
7 | export class InstanceValidatorsService { | ||
8 | readonly FROM_EMAIL: BuildFormValidator | ||
9 | readonly FROM_NAME: BuildFormValidator | ||
10 | readonly SUBJECT: BuildFormValidator | ||
11 | readonly BODY: BuildFormValidator | ||
12 | |||
13 | constructor (private i18n: I18n) { | ||
14 | |||
15 | this.FROM_EMAIL = { | ||
16 | VALIDATORS: [ Validators.required, Validators.email ], | ||
17 | MESSAGES: { | ||
18 | 'required': this.i18n('Email is required.'), | ||
19 | 'email': this.i18n('Email must be valid.') | ||
20 | } | ||
21 | } | ||
22 | |||
23 | this.FROM_NAME = { | ||
24 | VALIDATORS: [ | ||
25 | Validators.required, | ||
26 | Validators.minLength(1), | ||
27 | Validators.maxLength(120) | ||
28 | ], | ||
29 | MESSAGES: { | ||
30 | 'required': this.i18n('Your name is required.'), | ||
31 | 'minlength': this.i18n('Your name must be at least 1 character long.'), | ||
32 | 'maxlength': this.i18n('Your name cannot be more than 120 characters long.') | ||
33 | } | ||
34 | } | ||
35 | |||
36 | this.SUBJECT = { | ||
37 | VALIDATORS: [ | ||
38 | Validators.required, | ||
39 | Validators.minLength(1), | ||
40 | Validators.maxLength(120) | ||
41 | ], | ||
42 | MESSAGES: { | ||
43 | 'required': this.i18n('A subject is required.'), | ||
44 | 'minlength': this.i18n('The subject must be at least 1 character long.'), | ||
45 | 'maxlength': this.i18n('The subject cannot be more than 120 characters long.') | ||
46 | } | ||
47 | } | ||
48 | |||
49 | this.BODY = { | ||
50 | VALIDATORS: [ | ||
51 | Validators.required, | ||
52 | Validators.minLength(3), | ||
53 | Validators.maxLength(5000) | ||
54 | ], | ||
55 | MESSAGES: { | ||
56 | 'required': this.i18n('A message is required.'), | ||
57 | 'minlength': this.i18n('The message must be at least 3 characters long.'), | ||
58 | 'maxlength': this.i18n('The message cannot be more than 5000 characters long.') | ||
59 | } | ||
60 | } | ||
61 | } | ||
62 | } | ||
diff --git a/client/src/app/shared/shared-forms/form-validators/login-validators.service.ts b/client/src/app/shared/shared-forms/form-validators/login-validators.service.ts new file mode 100644 index 000000000..a5837357e --- /dev/null +++ b/client/src/app/shared/shared-forms/form-validators/login-validators.service.ts | |||
@@ -0,0 +1,30 @@ | |||
1 | import { I18n } from '@ngx-translate/i18n-polyfill' | ||
2 | import { Validators } from '@angular/forms' | ||
3 | import { Injectable } from '@angular/core' | ||
4 | import { BuildFormValidator } from './form-validator.service' | ||
5 | |||
6 | @Injectable() | ||
7 | export class LoginValidatorsService { | ||
8 | readonly LOGIN_USERNAME: BuildFormValidator | ||
9 | readonly LOGIN_PASSWORD: BuildFormValidator | ||
10 | |||
11 | constructor (private i18n: I18n) { | ||
12 | this.LOGIN_USERNAME = { | ||
13 | VALIDATORS: [ | ||
14 | Validators.required | ||
15 | ], | ||
16 | MESSAGES: { | ||
17 | 'required': this.i18n('Username is required.') | ||
18 | } | ||
19 | } | ||
20 | |||
21 | this.LOGIN_PASSWORD = { | ||
22 | VALIDATORS: [ | ||
23 | Validators.required | ||
24 | ], | ||
25 | MESSAGES: { | ||
26 | 'required': this.i18n('Password is required.') | ||
27 | } | ||
28 | } | ||
29 | } | ||
30 | } | ||
diff --git a/client/src/app/shared/shared-forms/form-validators/reset-password-validators.service.ts b/client/src/app/shared/shared-forms/form-validators/reset-password-validators.service.ts new file mode 100644 index 000000000..d2085a309 --- /dev/null +++ b/client/src/app/shared/shared-forms/form-validators/reset-password-validators.service.ts | |||
@@ -0,0 +1,20 @@ | |||
1 | import { I18n } from '@ngx-translate/i18n-polyfill' | ||
2 | import { Validators } from '@angular/forms' | ||
3 | import { Injectable } from '@angular/core' | ||
4 | import { BuildFormValidator } from './form-validator.service' | ||
5 | |||
6 | @Injectable() | ||
7 | export class ResetPasswordValidatorsService { | ||
8 | readonly RESET_PASSWORD_CONFIRM: BuildFormValidator | ||
9 | |||
10 | constructor (private i18n: I18n) { | ||
11 | this.RESET_PASSWORD_CONFIRM = { | ||
12 | VALIDATORS: [ | ||
13 | Validators.required | ||
14 | ], | ||
15 | MESSAGES: { | ||
16 | 'required': this.i18n('Confirmation of the password is required.') | ||
17 | } | ||
18 | } | ||
19 | } | ||
20 | } | ||
diff --git a/client/src/app/shared/shared-forms/form-validators/user-validators.service.ts b/client/src/app/shared/shared-forms/form-validators/user-validators.service.ts new file mode 100644 index 000000000..bd3030a54 --- /dev/null +++ b/client/src/app/shared/shared-forms/form-validators/user-validators.service.ts | |||
@@ -0,0 +1,151 @@ | |||
1 | import { I18n } from '@ngx-translate/i18n-polyfill' | ||
2 | import { Validators } from '@angular/forms' | ||
3 | import { BuildFormValidator } from './form-validator.service' | ||
4 | import { Injectable } from '@angular/core' | ||
5 | |||
6 | @Injectable() | ||
7 | export class UserValidatorsService { | ||
8 | readonly USER_USERNAME: BuildFormValidator | ||
9 | readonly USER_EMAIL: BuildFormValidator | ||
10 | readonly USER_PASSWORD: BuildFormValidator | ||
11 | readonly USER_PASSWORD_OPTIONAL: BuildFormValidator | ||
12 | readonly USER_CONFIRM_PASSWORD: BuildFormValidator | ||
13 | readonly USER_VIDEO_QUOTA: BuildFormValidator | ||
14 | readonly USER_VIDEO_QUOTA_DAILY: BuildFormValidator | ||
15 | readonly USER_ROLE: BuildFormValidator | ||
16 | readonly USER_DISPLAY_NAME_REQUIRED: BuildFormValidator | ||
17 | readonly USER_DESCRIPTION: BuildFormValidator | ||
18 | readonly USER_TERMS: BuildFormValidator | ||
19 | |||
20 | readonly USER_BAN_REASON: BuildFormValidator | ||
21 | |||
22 | constructor (private i18n: I18n) { | ||
23 | |||
24 | this.USER_USERNAME = { | ||
25 | VALIDATORS: [ | ||
26 | Validators.required, | ||
27 | Validators.minLength(1), | ||
28 | Validators.maxLength(50), | ||
29 | Validators.pattern(/^[a-z0-9][a-z0-9._]*$/) | ||
30 | ], | ||
31 | MESSAGES: { | ||
32 | 'required': this.i18n('Username is required.'), | ||
33 | 'minlength': this.i18n('Username must be at least 1 character long.'), | ||
34 | 'maxlength': this.i18n('Username cannot be more than 50 characters long.'), | ||
35 | 'pattern': this.i18n('Username should be lowercase alphanumeric; dots and underscores are allowed.') | ||
36 | } | ||
37 | } | ||
38 | |||
39 | this.USER_EMAIL = { | ||
40 | VALIDATORS: [ Validators.required, Validators.email ], | ||
41 | MESSAGES: { | ||
42 | 'required': this.i18n('Email is required.'), | ||
43 | 'email': this.i18n('Email must be valid.') | ||
44 | } | ||
45 | } | ||
46 | |||
47 | this.USER_PASSWORD = { | ||
48 | VALIDATORS: [ | ||
49 | Validators.required, | ||
50 | Validators.minLength(6), | ||
51 | Validators.maxLength(255) | ||
52 | ], | ||
53 | MESSAGES: { | ||
54 | 'required': this.i18n('Password is required.'), | ||
55 | 'minlength': this.i18n('Password must be at least 6 characters long.'), | ||
56 | 'maxlength': this.i18n('Password cannot be more than 255 characters long.') | ||
57 | } | ||
58 | } | ||
59 | |||
60 | this.USER_PASSWORD_OPTIONAL = { | ||
61 | VALIDATORS: [ | ||
62 | Validators.minLength(6), | ||
63 | Validators.maxLength(255) | ||
64 | ], | ||
65 | MESSAGES: { | ||
66 | 'minlength': this.i18n('Password must be at least 6 characters long.'), | ||
67 | 'maxlength': this.i18n('Password cannot be more than 255 characters long.') | ||
68 | } | ||
69 | } | ||
70 | |||
71 | this.USER_CONFIRM_PASSWORD = { | ||
72 | VALIDATORS: [], | ||
73 | MESSAGES: { | ||
74 | 'matchPassword': this.i18n('The new password and the confirmed password do not correspond.') | ||
75 | } | ||
76 | } | ||
77 | |||
78 | this.USER_VIDEO_QUOTA = { | ||
79 | VALIDATORS: [ Validators.required, Validators.min(-1) ], | ||
80 | MESSAGES: { | ||
81 | 'required': this.i18n('Video quota is required.'), | ||
82 | 'min': this.i18n('Quota must be greater than -1.') | ||
83 | } | ||
84 | } | ||
85 | this.USER_VIDEO_QUOTA_DAILY = { | ||
86 | VALIDATORS: [ Validators.required, Validators.min(-1) ], | ||
87 | MESSAGES: { | ||
88 | 'required': this.i18n('Daily upload limit is required.'), | ||
89 | 'min': this.i18n('Daily upload limit must be greater than -1.') | ||
90 | } | ||
91 | } | ||
92 | |||
93 | this.USER_ROLE = { | ||
94 | VALIDATORS: [ Validators.required ], | ||
95 | MESSAGES: { | ||
96 | 'required': this.i18n('User role is required.') | ||
97 | } | ||
98 | } | ||
99 | |||
100 | this.USER_DISPLAY_NAME_REQUIRED = this.getDisplayName(true) | ||
101 | |||
102 | this.USER_DESCRIPTION = { | ||
103 | VALIDATORS: [ | ||
104 | Validators.minLength(3), | ||
105 | Validators.maxLength(1000) | ||
106 | ], | ||
107 | MESSAGES: { | ||
108 | 'minlength': this.i18n('Description must be at least 3 characters long.'), | ||
109 | 'maxlength': this.i18n('Description cannot be more than 1000 characters long.') | ||
110 | } | ||
111 | } | ||
112 | |||
113 | this.USER_TERMS = { | ||
114 | VALIDATORS: [ | ||
115 | Validators.requiredTrue | ||
116 | ], | ||
117 | MESSAGES: { | ||
118 | 'required': this.i18n('You must agree with the instance terms in order to register on it.') | ||
119 | } | ||
120 | } | ||
121 | |||
122 | this.USER_BAN_REASON = { | ||
123 | VALIDATORS: [ | ||
124 | Validators.minLength(3), | ||
125 | Validators.maxLength(250) | ||
126 | ], | ||
127 | MESSAGES: { | ||
128 | 'minlength': this.i18n('Ban reason must be at least 3 characters long.'), | ||
129 | 'maxlength': this.i18n('Ban reason cannot be more than 250 characters long.') | ||
130 | } | ||
131 | } | ||
132 | } | ||
133 | |||
134 | private getDisplayName (required: boolean) { | ||
135 | const control = { | ||
136 | VALIDATORS: [ | ||
137 | Validators.minLength(1), | ||
138 | Validators.maxLength(120) | ||
139 | ], | ||
140 | MESSAGES: { | ||
141 | 'required': this.i18n('Display name is required.'), | ||
142 | 'minlength': this.i18n('Display name must be at least 1 character long.'), | ||
143 | 'maxlength': this.i18n('Display name cannot be more than 50 characters long.') | ||
144 | } | ||
145 | } | ||
146 | |||
147 | if (required) control.VALIDATORS.push(Validators.required) | ||
148 | |||
149 | return control | ||
150 | } | ||
151 | } | ||
diff --git a/client/src/app/shared/shared-forms/form-validators/video-abuse-validators.service.ts b/client/src/app/shared/shared-forms/form-validators/video-abuse-validators.service.ts new file mode 100644 index 000000000..aae56d607 --- /dev/null +++ b/client/src/app/shared/shared-forms/form-validators/video-abuse-validators.service.ts | |||
@@ -0,0 +1,30 @@ | |||
1 | import { I18n } from '@ngx-translate/i18n-polyfill' | ||
2 | import { Validators } from '@angular/forms' | ||
3 | import { Injectable } from '@angular/core' | ||
4 | import { BuildFormValidator } from './form-validator.service' | ||
5 | |||
6 | @Injectable() | ||
7 | export class VideoAbuseValidatorsService { | ||
8 | readonly VIDEO_ABUSE_REASON: BuildFormValidator | ||
9 | readonly VIDEO_ABUSE_MODERATION_COMMENT: BuildFormValidator | ||
10 | |||
11 | constructor (private i18n: I18n) { | ||
12 | this.VIDEO_ABUSE_REASON = { | ||
13 | VALIDATORS: [ Validators.required, Validators.minLength(2), Validators.maxLength(3000) ], | ||
14 | MESSAGES: { | ||
15 | 'required': this.i18n('Report reason is required.'), | ||
16 | 'minlength': this.i18n('Report reason must be at least 2 characters long.'), | ||
17 | 'maxlength': this.i18n('Report reason cannot be more than 3000 characters long.') | ||
18 | } | ||
19 | } | ||
20 | |||
21 | this.VIDEO_ABUSE_MODERATION_COMMENT = { | ||
22 | VALIDATORS: [ Validators.required, Validators.minLength(2), Validators.maxLength(3000) ], | ||
23 | MESSAGES: { | ||
24 | 'required': this.i18n('Moderation comment is required.'), | ||
25 | 'minlength': this.i18n('Moderation comment must be at least 2 characters long.'), | ||
26 | 'maxlength': this.i18n('Moderation comment cannot be more than 3000 characters long.') | ||
27 | } | ||
28 | } | ||
29 | } | ||
30 | } | ||
diff --git a/client/src/app/shared/shared-forms/form-validators/video-accept-ownership-validators.service.ts b/client/src/app/shared/shared-forms/form-validators/video-accept-ownership-validators.service.ts new file mode 100644 index 000000000..998d616ec --- /dev/null +++ b/client/src/app/shared/shared-forms/form-validators/video-accept-ownership-validators.service.ts | |||
@@ -0,0 +1,18 @@ | |||
1 | import { I18n } from '@ngx-translate/i18n-polyfill' | ||
2 | import { Validators } from '@angular/forms' | ||
3 | import { Injectable } from '@angular/core' | ||
4 | import { BuildFormValidator } from './form-validator.service' | ||
5 | |||
6 | @Injectable() | ||
7 | export class VideoAcceptOwnershipValidatorsService { | ||
8 | readonly CHANNEL: BuildFormValidator | ||
9 | |||
10 | constructor (private i18n: I18n) { | ||
11 | this.CHANNEL = { | ||
12 | VALIDATORS: [ Validators.required ], | ||
13 | MESSAGES: { | ||
14 | 'required': this.i18n('The channel is required.') | ||
15 | } | ||
16 | } | ||
17 | } | ||
18 | } | ||
diff --git a/client/src/app/shared/shared-forms/form-validators/video-block-validators.service.ts b/client/src/app/shared/shared-forms/form-validators/video-block-validators.service.ts new file mode 100644 index 000000000..ddf0ab5eb --- /dev/null +++ b/client/src/app/shared/shared-forms/form-validators/video-block-validators.service.ts | |||
@@ -0,0 +1,19 @@ | |||
1 | import { I18n } from '@ngx-translate/i18n-polyfill' | ||
2 | import { Validators } from '@angular/forms' | ||
3 | import { Injectable } from '@angular/core' | ||
4 | import { BuildFormValidator } from './form-validator.service' | ||
5 | |||
6 | @Injectable() | ||
7 | export class VideoBlockValidatorsService { | ||
8 | readonly VIDEO_BLOCK_REASON: BuildFormValidator | ||
9 | |||
10 | constructor (private i18n: I18n) { | ||
11 | this.VIDEO_BLOCK_REASON = { | ||
12 | VALIDATORS: [ Validators.minLength(2), Validators.maxLength(300) ], | ||
13 | MESSAGES: { | ||
14 | 'minlength': this.i18n('Block reason must be at least 2 characters long.'), | ||
15 | 'maxlength': this.i18n('Block reason cannot be more than 300 characters long.') | ||
16 | } | ||
17 | } | ||
18 | } | ||
19 | } | ||
diff --git a/client/src/app/shared/shared-forms/form-validators/video-captions-validators.service.ts b/client/src/app/shared/shared-forms/form-validators/video-captions-validators.service.ts new file mode 100644 index 000000000..280d28414 --- /dev/null +++ b/client/src/app/shared/shared-forms/form-validators/video-captions-validators.service.ts | |||
@@ -0,0 +1,27 @@ | |||
1 | import { I18n } from '@ngx-translate/i18n-polyfill' | ||
2 | import { Validators } from '@angular/forms' | ||
3 | import { Injectable } from '@angular/core' | ||
4 | import { BuildFormValidator } from './form-validator.service' | ||
5 | |||
6 | @Injectable() | ||
7 | export class VideoCaptionsValidatorsService { | ||
8 | readonly VIDEO_CAPTION_LANGUAGE: BuildFormValidator | ||
9 | readonly VIDEO_CAPTION_FILE: BuildFormValidator | ||
10 | |||
11 | constructor (private i18n: I18n) { | ||
12 | |||
13 | this.VIDEO_CAPTION_LANGUAGE = { | ||
14 | VALIDATORS: [ Validators.required ], | ||
15 | MESSAGES: { | ||
16 | 'required': this.i18n('Video caption language is required.') | ||
17 | } | ||
18 | } | ||
19 | |||
20 | this.VIDEO_CAPTION_FILE = { | ||
21 | VALIDATORS: [ Validators.required ], | ||
22 | MESSAGES: { | ||
23 | 'required': this.i18n('Video caption file is required.') | ||
24 | } | ||
25 | } | ||
26 | } | ||
27 | } | ||
diff --git a/client/src/app/shared/shared-forms/form-validators/video-change-ownership-validators.service.ts b/client/src/app/shared/shared-forms/form-validators/video-change-ownership-validators.service.ts new file mode 100644 index 000000000..59659defd --- /dev/null +++ b/client/src/app/shared/shared-forms/form-validators/video-change-ownership-validators.service.ts | |||
@@ -0,0 +1,27 @@ | |||
1 | import { I18n } from '@ngx-translate/i18n-polyfill' | ||
2 | import { AbstractControl, ValidationErrors, Validators } from '@angular/forms' | ||
3 | import { Injectable } from '@angular/core' | ||
4 | import { BuildFormValidator } from './form-validator.service' | ||
5 | |||
6 | @Injectable() | ||
7 | export class VideoChangeOwnershipValidatorsService { | ||
8 | readonly USERNAME: BuildFormValidator | ||
9 | |||
10 | constructor (private i18n: I18n) { | ||
11 | this.USERNAME = { | ||
12 | VALIDATORS: [ Validators.required, this.localAccountValidator ], | ||
13 | MESSAGES: { | ||
14 | 'required': this.i18n('The username is required.'), | ||
15 | 'localAccountOnly': this.i18n('You can only transfer ownership to a local account') | ||
16 | } | ||
17 | } | ||
18 | } | ||
19 | |||
20 | localAccountValidator (control: AbstractControl): ValidationErrors { | ||
21 | if (control.value.includes('@')) { | ||
22 | return { 'localAccountOnly': true } | ||
23 | } | ||
24 | |||
25 | return null | ||
26 | } | ||
27 | } | ||
diff --git a/client/src/app/shared/shared-forms/form-validators/video-channel-validators.service.ts b/client/src/app/shared/shared-forms/form-validators/video-channel-validators.service.ts new file mode 100644 index 000000000..bb650b149 --- /dev/null +++ b/client/src/app/shared/shared-forms/form-validators/video-channel-validators.service.ts | |||
@@ -0,0 +1,64 @@ | |||
1 | import { I18n } from '@ngx-translate/i18n-polyfill' | ||
2 | import { Validators } from '@angular/forms' | ||
3 | import { Injectable } from '@angular/core' | ||
4 | import { BuildFormValidator } from './form-validator.service' | ||
5 | |||
6 | @Injectable() | ||
7 | export class VideoChannelValidatorsService { | ||
8 | readonly VIDEO_CHANNEL_NAME: BuildFormValidator | ||
9 | readonly VIDEO_CHANNEL_DISPLAY_NAME: BuildFormValidator | ||
10 | readonly VIDEO_CHANNEL_DESCRIPTION: BuildFormValidator | ||
11 | readonly VIDEO_CHANNEL_SUPPORT: BuildFormValidator | ||
12 | |||
13 | constructor (private i18n: I18n) { | ||
14 | this.VIDEO_CHANNEL_NAME = { | ||
15 | VALIDATORS: [ | ||
16 | Validators.required, | ||
17 | Validators.minLength(1), | ||
18 | Validators.maxLength(50), | ||
19 | Validators.pattern(/^[a-z0-9][a-z0-9._]*$/) | ||
20 | ], | ||
21 | MESSAGES: { | ||
22 | 'required': this.i18n('Name is required.'), | ||
23 | 'minlength': this.i18n('Name must be at least 1 character long.'), | ||
24 | 'maxlength': this.i18n('Name cannot be more than 50 characters long.'), | ||
25 | 'pattern': this.i18n('Name should be lowercase alphanumeric; dots and underscores are allowed.') | ||
26 | } | ||
27 | } | ||
28 | |||
29 | this.VIDEO_CHANNEL_DISPLAY_NAME = { | ||
30 | VALIDATORS: [ | ||
31 | Validators.required, | ||
32 | Validators.minLength(1), | ||
33 | Validators.maxLength(50) | ||
34 | ], | ||
35 | MESSAGES: { | ||
36 | 'required': i18n('Display name is required.'), | ||
37 | 'minlength': i18n('Display name must be at least 1 character long.'), | ||
38 | 'maxlength': i18n('Display name cannot be more than 50 characters long.') | ||
39 | } | ||
40 | } | ||
41 | |||
42 | this.VIDEO_CHANNEL_DESCRIPTION = { | ||
43 | VALIDATORS: [ | ||
44 | Validators.minLength(3), | ||
45 | Validators.maxLength(1000) | ||
46 | ], | ||
47 | MESSAGES: { | ||
48 | 'minlength': i18n('Description must be at least 3 characters long.'), | ||
49 | 'maxlength': i18n('Description cannot be more than 1000 characters long.') | ||
50 | } | ||
51 | } | ||
52 | |||
53 | this.VIDEO_CHANNEL_SUPPORT = { | ||
54 | VALIDATORS: [ | ||
55 | Validators.minLength(3), | ||
56 | Validators.maxLength(1000) | ||
57 | ], | ||
58 | MESSAGES: { | ||
59 | 'minlength': i18n('Support text must be at least 3 characters long.'), | ||
60 | 'maxlength': i18n('Support text cannot be more than 1000 characters long.') | ||
61 | } | ||
62 | } | ||
63 | } | ||
64 | } | ||
diff --git a/client/src/app/shared/shared-forms/form-validators/video-comment-validators.service.ts b/client/src/app/shared/shared-forms/form-validators/video-comment-validators.service.ts new file mode 100644 index 000000000..97c8e967e --- /dev/null +++ b/client/src/app/shared/shared-forms/form-validators/video-comment-validators.service.ts | |||
@@ -0,0 +1,20 @@ | |||
1 | import { I18n } from '@ngx-translate/i18n-polyfill' | ||
2 | import { Validators } from '@angular/forms' | ||
3 | import { Injectable } from '@angular/core' | ||
4 | import { BuildFormValidator } from './form-validator.service' | ||
5 | |||
6 | @Injectable() | ||
7 | export class VideoCommentValidatorsService { | ||
8 | readonly VIDEO_COMMENT_TEXT: BuildFormValidator | ||
9 | |||
10 | constructor (private i18n: I18n) { | ||
11 | this.VIDEO_COMMENT_TEXT = { | ||
12 | VALIDATORS: [ Validators.required, Validators.minLength(1), Validators.maxLength(3000) ], | ||
13 | MESSAGES: { | ||
14 | 'required': this.i18n('Comment is required.'), | ||
15 | 'minlength': this.i18n('Comment must be at least 2 characters long.'), | ||
16 | 'maxlength': this.i18n('Comment cannot be more than 3000 characters long.') | ||
17 | } | ||
18 | } | ||
19 | } | ||
20 | } | ||
diff --git a/client/src/app/shared/shared-forms/form-validators/video-playlist-validators.service.ts b/client/src/app/shared/shared-forms/form-validators/video-playlist-validators.service.ts new file mode 100644 index 000000000..ab9c43625 --- /dev/null +++ b/client/src/app/shared/shared-forms/form-validators/video-playlist-validators.service.ts | |||
@@ -0,0 +1,66 @@ | |||
1 | import { I18n } from '@ngx-translate/i18n-polyfill' | ||
2 | import { AbstractControl, FormControl, Validators } from '@angular/forms' | ||
3 | import { Injectable } from '@angular/core' | ||
4 | import { BuildFormValidator } from './form-validator.service' | ||
5 | import { VideoPlaylistPrivacy } from '@shared/models' | ||
6 | |||
7 | @Injectable() | ||
8 | export class VideoPlaylistValidatorsService { | ||
9 | readonly VIDEO_PLAYLIST_DISPLAY_NAME: BuildFormValidator | ||
10 | readonly VIDEO_PLAYLIST_PRIVACY: BuildFormValidator | ||
11 | readonly VIDEO_PLAYLIST_DESCRIPTION: BuildFormValidator | ||
12 | readonly VIDEO_PLAYLIST_CHANNEL_ID: BuildFormValidator | ||
13 | |||
14 | constructor (private i18n: I18n) { | ||
15 | this.VIDEO_PLAYLIST_DISPLAY_NAME = { | ||
16 | VALIDATORS: [ | ||
17 | Validators.required, | ||
18 | Validators.minLength(1), | ||
19 | Validators.maxLength(120) | ||
20 | ], | ||
21 | MESSAGES: { | ||
22 | 'required': this.i18n('Display name is required.'), | ||
23 | 'minlength': this.i18n('Display name must be at least 1 character long.'), | ||
24 | 'maxlength': this.i18n('Display name cannot be more than 120 characters long.') | ||
25 | } | ||
26 | } | ||
27 | |||
28 | this.VIDEO_PLAYLIST_PRIVACY = { | ||
29 | VALIDATORS: [ | ||
30 | Validators.required | ||
31 | ], | ||
32 | MESSAGES: { | ||
33 | 'required': this.i18n('Privacy is required.') | ||
34 | } | ||
35 | } | ||
36 | |||
37 | this.VIDEO_PLAYLIST_DESCRIPTION = { | ||
38 | VALIDATORS: [ | ||
39 | Validators.minLength(3), | ||
40 | Validators.maxLength(1000) | ||
41 | ], | ||
42 | MESSAGES: { | ||
43 | 'minlength': i18n('Description must be at least 3 characters long.'), | ||
44 | 'maxlength': i18n('Description cannot be more than 1000 characters long.') | ||
45 | } | ||
46 | } | ||
47 | |||
48 | this.VIDEO_PLAYLIST_CHANNEL_ID = { | ||
49 | VALIDATORS: [ ], | ||
50 | MESSAGES: { | ||
51 | 'required': this.i18n('The channel is required when the playlist is public.') | ||
52 | } | ||
53 | } | ||
54 | } | ||
55 | |||
56 | setChannelValidator (channelControl: AbstractControl, privacy: VideoPlaylistPrivacy) { | ||
57 | if (privacy.toString() === VideoPlaylistPrivacy.PUBLIC.toString()) { | ||
58 | channelControl.setValidators([ Validators.required ]) | ||
59 | } else { | ||
60 | channelControl.setValidators(null) | ||
61 | } | ||
62 | |||
63 | channelControl.markAsDirty() | ||
64 | channelControl.updateValueAndValidity() | ||
65 | } | ||
66 | } | ||
diff --git a/client/src/app/shared/shared-forms/form-validators/video-validators.service.ts b/client/src/app/shared/shared-forms/form-validators/video-validators.service.ts new file mode 100644 index 000000000..9b24e4f62 --- /dev/null +++ b/client/src/app/shared/shared-forms/form-validators/video-validators.service.ts | |||
@@ -0,0 +1,102 @@ | |||
1 | import { I18n } from '@ngx-translate/i18n-polyfill' | ||
2 | import { Validators } from '@angular/forms' | ||
3 | import { Injectable } from '@angular/core' | ||
4 | import { BuildFormValidator } from './form-validator.service' | ||
5 | |||
6 | @Injectable() | ||
7 | export class VideoValidatorsService { | ||
8 | readonly VIDEO_NAME: BuildFormValidator | ||
9 | readonly VIDEO_PRIVACY: BuildFormValidator | ||
10 | readonly VIDEO_CATEGORY: BuildFormValidator | ||
11 | readonly VIDEO_LICENCE: BuildFormValidator | ||
12 | readonly VIDEO_LANGUAGE: BuildFormValidator | ||
13 | readonly VIDEO_IMAGE: BuildFormValidator | ||
14 | readonly VIDEO_CHANNEL: BuildFormValidator | ||
15 | readonly VIDEO_DESCRIPTION: BuildFormValidator | ||
16 | readonly VIDEO_TAGS: BuildFormValidator | ||
17 | readonly VIDEO_SUPPORT: BuildFormValidator | ||
18 | readonly VIDEO_SCHEDULE_PUBLICATION_AT: BuildFormValidator | ||
19 | readonly VIDEO_ORIGINALLY_PUBLISHED_AT: BuildFormValidator | ||
20 | |||
21 | constructor (private i18n: I18n) { | ||
22 | |||
23 | this.VIDEO_NAME = { | ||
24 | VALIDATORS: [ Validators.required, Validators.minLength(3), Validators.maxLength(120) ], | ||
25 | MESSAGES: { | ||
26 | 'required': this.i18n('Video name is required.'), | ||
27 | 'minlength': this.i18n('Video name must be at least 3 characters long.'), | ||
28 | 'maxlength': this.i18n('Video name cannot be more than 120 characters long.') | ||
29 | } | ||
30 | } | ||
31 | |||
32 | this.VIDEO_PRIVACY = { | ||
33 | VALIDATORS: [ Validators.required ], | ||
34 | MESSAGES: { | ||
35 | 'required': this.i18n('Video privacy is required.') | ||
36 | } | ||
37 | } | ||
38 | |||
39 | this.VIDEO_CATEGORY = { | ||
40 | VALIDATORS: [ ], | ||
41 | MESSAGES: {} | ||
42 | } | ||
43 | |||
44 | this.VIDEO_LICENCE = { | ||
45 | VALIDATORS: [ ], | ||
46 | MESSAGES: {} | ||
47 | } | ||
48 | |||
49 | this.VIDEO_LANGUAGE = { | ||
50 | VALIDATORS: [ ], | ||
51 | MESSAGES: {} | ||
52 | } | ||
53 | |||
54 | this.VIDEO_IMAGE = { | ||
55 | VALIDATORS: [ ], | ||
56 | MESSAGES: {} | ||
57 | } | ||
58 | |||
59 | this.VIDEO_CHANNEL = { | ||
60 | VALIDATORS: [ Validators.required ], | ||
61 | MESSAGES: { | ||
62 | 'required': this.i18n('Video channel is required.') | ||
63 | } | ||
64 | } | ||
65 | |||
66 | this.VIDEO_DESCRIPTION = { | ||
67 | VALIDATORS: [ Validators.minLength(3), Validators.maxLength(10000) ], | ||
68 | MESSAGES: { | ||
69 | 'minlength': this.i18n('Video description must be at least 3 characters long.'), | ||
70 | 'maxlength': this.i18n('Video description cannot be more than 10000 characters long.') | ||
71 | } | ||
72 | } | ||
73 | |||
74 | this.VIDEO_TAGS = { | ||
75 | VALIDATORS: [ Validators.minLength(2), Validators.maxLength(30) ], | ||
76 | MESSAGES: { | ||
77 | 'minlength': this.i18n('A tag should be more than 2 characters long.'), | ||
78 | 'maxlength': this.i18n('A tag should be less than 30 characters long.') | ||
79 | } | ||
80 | } | ||
81 | |||
82 | this.VIDEO_SUPPORT = { | ||
83 | VALIDATORS: [ Validators.minLength(3), Validators.maxLength(1000) ], | ||
84 | MESSAGES: { | ||
85 | 'minlength': this.i18n('Video support must be at least 3 characters long.'), | ||
86 | 'maxlength': this.i18n('Video support cannot be more than 1000 characters long.') | ||
87 | } | ||
88 | } | ||
89 | |||
90 | this.VIDEO_SCHEDULE_PUBLICATION_AT = { | ||
91 | VALIDATORS: [ ], | ||
92 | MESSAGES: { | ||
93 | 'required': this.i18n('A date is required to schedule video update.') | ||
94 | } | ||
95 | } | ||
96 | |||
97 | this.VIDEO_ORIGINALLY_PUBLISHED_AT = { | ||
98 | VALIDATORS: [ ], | ||
99 | MESSAGES: {} | ||
100 | } | ||
101 | } | ||
102 | } | ||