diff options
Diffstat (limited to 'client/src/app')
6 files changed, 82 insertions, 7 deletions
diff --git a/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.html b/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.html index 0fe2aa203..8dca9bc04 100644 --- a/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.html +++ b/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.html | |||
@@ -128,5 +128,29 @@ | |||
128 | </div> | 128 | </div> |
129 | </ng-template> | 129 | </ng-template> |
130 | 130 | ||
131 | <div class="inner-form-title">Customizations</div> | ||
132 | |||
133 | <div class="form-group"> | ||
134 | <label for="customizationJavascript">JavaScript</label> | ||
135 | <textarea | ||
136 | id="customizationJavascript" formControlName="customizationJavascript" | ||
137 | [ngClass]="{ 'input-error': formErrors['customizationJavascript'] }" | ||
138 | ></textarea> | ||
139 | <div *ngIf="formErrors.customizationJavascript" class="form-error"> | ||
140 | {{ formErrors.customizationJavascript }} | ||
141 | </div> | ||
142 | </div> | ||
143 | |||
144 | <div class="form-group"> | ||
145 | <label for="customizationCSS">CSS</label> | ||
146 | <textarea | ||
147 | id="customizationCSS" formControlName="customizationCSS" | ||
148 | [ngClass]="{ 'input-error': formErrors['customizationCSS'] }" | ||
149 | ></textarea> | ||
150 | <div *ngIf="formErrors.customizationCSS" class="form-error"> | ||
151 | {{ formErrors.customizationCSS }} | ||
152 | </div> | ||
153 | </div> | ||
154 | |||
131 | <input type="submit" value="Update configuration" [disabled]="!form.valid"> | 155 | <input type="submit" value="Update configuration" [disabled]="!form.valid"> |
132 | </form> | 156 | </form> |
diff --git a/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.scss b/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.scss index 0195f44eb..e72f30c69 100644 --- a/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.scss +++ b/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.scss | |||
@@ -29,3 +29,9 @@ input[type=submit] { | |||
29 | margin-top: 30px; | 29 | margin-top: 30px; |
30 | margin-bottom: 10px; | 30 | margin-bottom: 10px; |
31 | } | 31 | } |
32 | |||
33 | textarea { | ||
34 | @include peertube-textarea(500px, 150px); | ||
35 | |||
36 | display: block; | ||
37 | } | ||
diff --git a/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.ts b/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.ts index cd8c926f7..027268536 100644 --- a/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.ts +++ b/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.ts | |||
@@ -49,7 +49,9 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit { | |||
49 | signupLimit: '', | 49 | signupLimit: '', |
50 | adminEmail: '', | 50 | adminEmail: '', |
51 | userVideoQuota: '', | 51 | userVideoQuota: '', |
52 | transcodingThreads: '' | 52 | transcodingThreads: '', |
53 | customizationJavascript: '', | ||
54 | customizationCSS: '' | ||
53 | } | 55 | } |
54 | validationMessages = { | 56 | validationMessages = { |
55 | instanceName: INSTANCE_NAME.MESSAGES, | 57 | instanceName: INSTANCE_NAME.MESSAGES, |
@@ -84,7 +86,9 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit { | |||
84 | adminEmail: [ '', ADMIN_EMAIL.VALIDATORS ], | 86 | adminEmail: [ '', ADMIN_EMAIL.VALIDATORS ], |
85 | userVideoQuota: [ '', USER_VIDEO_QUOTA.VALIDATORS ], | 87 | userVideoQuota: [ '', USER_VIDEO_QUOTA.VALIDATORS ], |
86 | transcodingThreads: [ '', TRANSCODING_THREADS.VALIDATORS ], | 88 | transcodingThreads: [ '', TRANSCODING_THREADS.VALIDATORS ], |
87 | transcodingEnabled: [ ] | 89 | transcodingEnabled: [ ], |
90 | customizationJavascript: [ '' ], | ||
91 | customizationCSS: [ '' ] | ||
88 | } | 92 | } |
89 | 93 | ||
90 | for (const resolution of this.resolutions) { | 94 | for (const resolution of this.resolutions) { |
@@ -125,7 +129,11 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit { | |||
125 | instance: { | 129 | instance: { |
126 | name: this.form.value['instanceName'], | 130 | name: this.form.value['instanceName'], |
127 | description: this.form.value['instanceDescription'], | 131 | description: this.form.value['instanceDescription'], |
128 | terms: this.form.value['instanceTerms'] | 132 | terms: this.form.value['instanceTerms'], |
133 | customizations: { | ||
134 | javascript: this.form.value['customizationJavascript'], | ||
135 | css: this.form.value['customizationCSS'] | ||
136 | } | ||
129 | }, | 137 | }, |
130 | cache: { | 138 | cache: { |
131 | previews: { | 139 | previews: { |
@@ -183,7 +191,9 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit { | |||
183 | adminEmail: this.customConfig.admin.email, | 191 | adminEmail: this.customConfig.admin.email, |
184 | userVideoQuota: this.customConfig.user.videoQuota, | 192 | userVideoQuota: this.customConfig.user.videoQuota, |
185 | transcodingThreads: this.customConfig.transcoding.threads, | 193 | transcodingThreads: this.customConfig.transcoding.threads, |
186 | transcodingEnabled: this.customConfig.transcoding.enabled | 194 | transcodingEnabled: this.customConfig.transcoding.enabled, |
195 | customizationJavascript: this.customConfig.instance.customizations.javascript, | ||
196 | customizationCSS: this.customConfig.instance.customizations.css | ||
187 | } | 197 | } |
188 | 198 | ||
189 | for (const resolution of this.resolutions) { | 199 | for (const resolution of this.resolutions) { |
diff --git a/client/src/app/app.component.html b/client/src/app/app.component.html index dafc45266..0e1882ad3 100644 --- a/client/src/app/app.component.html +++ b/client/src/app/app.component.html | |||
@@ -1,3 +1,5 @@ | |||
1 | <div *ngIf="customCSS" [innerHTML]="customCSS"></div> | ||
2 | |||
1 | <div> | 3 | <div> |
2 | <div class="header"> | 4 | <div class="header"> |
3 | 5 | ||
diff --git a/client/src/app/app.component.ts b/client/src/app/app.component.ts index 3af33ba2b..25936146c 100644 --- a/client/src/app/app.component.ts +++ b/client/src/app/app.component.ts | |||
@@ -1,4 +1,5 @@ | |||
1 | import { Component, OnInit } from '@angular/core' | 1 | import { Component, OnInit } from '@angular/core' |
2 | import { DomSanitizer, SafeHtml } from '@angular/platform-browser' | ||
2 | import { GuardsCheckStart, Router } from '@angular/router' | 3 | import { GuardsCheckStart, Router } from '@angular/router' |
3 | import { AuthService, ServerService } from '@app/core' | 4 | import { AuthService, ServerService } from '@app/core' |
4 | import { isInSmallView } from '@app/shared/misc/utils' | 5 | import { isInSmallView } from '@app/shared/misc/utils' |
@@ -24,10 +25,13 @@ export class AppComponent implements OnInit { | |||
24 | 25 | ||
25 | isMenuDisplayed = true | 26 | isMenuDisplayed = true |
26 | 27 | ||
28 | customCSS: SafeHtml | ||
29 | |||
27 | constructor ( | 30 | constructor ( |
28 | private router: Router, | 31 | private router: Router, |
29 | private authService: AuthService, | 32 | private authService: AuthService, |
30 | private serverService: ServerService | 33 | private serverService: ServerService, |
34 | private domSanitizer: DomSanitizer | ||
31 | ) {} | 35 | ) {} |
32 | 36 | ||
33 | get serverVersion () { | 37 | get serverVersion () { |
@@ -66,6 +70,26 @@ export class AppComponent implements OnInit { | |||
66 | } | 70 | } |
67 | } | 71 | } |
68 | ) | 72 | ) |
73 | |||
74 | this.serverService.configLoaded | ||
75 | .subscribe(() => { | ||
76 | const config = this.serverService.getConfig() | ||
77 | |||
78 | // We test customCSS in case or the admin removed the css | ||
79 | if (this.customCSS || config.instance.customizations.css) { | ||
80 | const styleTag = '<style>' + config.instance.customizations.css + '</style>' | ||
81 | this.customCSS = this.domSanitizer.bypassSecurityTrustHtml(styleTag) | ||
82 | } | ||
83 | |||
84 | if (config.instance.customizations.javascript) { | ||
85 | try { | ||
86 | // tslint:disable:no-eval | ||
87 | eval(config.instance.customizations.javascript) | ||
88 | } catch (err) { | ||
89 | console.error('Cannot eval custom JavaScript.', err) | ||
90 | } | ||
91 | } | ||
92 | }) | ||
69 | } | 93 | } |
70 | 94 | ||
71 | toggleMenu () { | 95 | toggleMenu () { |
diff --git a/client/src/app/core/server/server.service.ts b/client/src/app/core/server/server.service.ts index f54e63efd..3c94f09c6 100644 --- a/client/src/app/core/server/server.service.ts +++ b/client/src/app/core/server/server.service.ts | |||
@@ -12,6 +12,7 @@ export class ServerService { | |||
12 | private static BASE_VIDEO_URL = environment.apiUrl + '/api/v1/videos/' | 12 | private static BASE_VIDEO_URL = environment.apiUrl + '/api/v1/videos/' |
13 | private static CONFIG_LOCAL_STORAGE_KEY = 'server-config' | 13 | private static CONFIG_LOCAL_STORAGE_KEY = 'server-config' |
14 | 14 | ||
15 | configLoaded = new ReplaySubject<boolean>(1) | ||
15 | videoPrivaciesLoaded = new ReplaySubject<boolean>(1) | 16 | videoPrivaciesLoaded = new ReplaySubject<boolean>(1) |
16 | videoCategoriesLoaded = new ReplaySubject<boolean>(1) | 17 | videoCategoriesLoaded = new ReplaySubject<boolean>(1) |
17 | videoLicencesLoaded = new ReplaySubject<boolean>(1) | 18 | videoLicencesLoaded = new ReplaySubject<boolean>(1) |
@@ -19,7 +20,11 @@ export class ServerService { | |||
19 | 20 | ||
20 | private config: ServerConfig = { | 21 | private config: ServerConfig = { |
21 | instance: { | 22 | instance: { |
22 | name: 'PeerTube' | 23 | name: 'PeerTube', |
24 | customizations: { | ||
25 | javascript: '', | ||
26 | css: '' | ||
27 | } | ||
23 | }, | 28 | }, |
24 | serverVersion: 'Unknown', | 29 | serverVersion: 'Unknown', |
25 | signup: { | 30 | signup: { |
@@ -56,7 +61,11 @@ export class ServerService { | |||
56 | loadConfig () { | 61 | loadConfig () { |
57 | this.http.get<ServerConfig>(ServerService.BASE_CONFIG_URL) | 62 | this.http.get<ServerConfig>(ServerService.BASE_CONFIG_URL) |
58 | .do(this.saveConfigLocally) | 63 | .do(this.saveConfigLocally) |
59 | .subscribe(data => this.config = data) | 64 | .subscribe(data => { |
65 | this.config = data | ||
66 | |||
67 | this.configLoaded.next(true) | ||
68 | }) | ||
60 | } | 69 | } |
61 | 70 | ||
62 | loadVideoCategories () { | 71 | loadVideoCategories () { |