Add ability to add custom css/javascript

This commit is contained in:
Chocobozzz 2018-02-22 10:22:53 +01:00
parent 6221f311de
commit 00b5556c18
No known key found for this signature in database
GPG Key ID: 583A612D890159BE
15 changed files with 143 additions and 14 deletions

View File

@ -128,5 +128,29 @@
</div>
</ng-template>
<div class="inner-form-title">Customizations</div>
<div class="form-group">
<label for="customizationJavascript">JavaScript</label>
<textarea
id="customizationJavascript" formControlName="customizationJavascript"
[ngClass]="{ 'input-error': formErrors['customizationJavascript'] }"
></textarea>
<div *ngIf="formErrors.customizationJavascript" class="form-error">
{{ formErrors.customizationJavascript }}
</div>
</div>
<div class="form-group">
<label for="customizationCSS">CSS</label>
<textarea
id="customizationCSS" formControlName="customizationCSS"
[ngClass]="{ 'input-error': formErrors['customizationCSS'] }"
></textarea>
<div *ngIf="formErrors.customizationCSS" class="form-error">
{{ formErrors.customizationCSS }}
</div>
</div>
<input type="submit" value="Update configuration" [disabled]="!form.valid">
</form>

View File

@ -29,3 +29,9 @@ input[type=submit] {
margin-top: 30px;
margin-bottom: 10px;
}
textarea {
@include peertube-textarea(500px, 150px);
display: block;
}

View File

@ -49,7 +49,9 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit {
signupLimit: '',
adminEmail: '',
userVideoQuota: '',
transcodingThreads: ''
transcodingThreads: '',
customizationJavascript: '',
customizationCSS: ''
}
validationMessages = {
instanceName: INSTANCE_NAME.MESSAGES,
@ -84,7 +86,9 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit {
adminEmail: [ '', ADMIN_EMAIL.VALIDATORS ],
userVideoQuota: [ '', USER_VIDEO_QUOTA.VALIDATORS ],
transcodingThreads: [ '', TRANSCODING_THREADS.VALIDATORS ],
transcodingEnabled: [ ]
transcodingEnabled: [ ],
customizationJavascript: [ '' ],
customizationCSS: [ '' ]
}
for (const resolution of this.resolutions) {
@ -125,7 +129,11 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit {
instance: {
name: this.form.value['instanceName'],
description: this.form.value['instanceDescription'],
terms: this.form.value['instanceTerms']
terms: this.form.value['instanceTerms'],
customizations: {
javascript: this.form.value['customizationJavascript'],
css: this.form.value['customizationCSS']
}
},
cache: {
previews: {
@ -183,7 +191,9 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit {
adminEmail: this.customConfig.admin.email,
userVideoQuota: this.customConfig.user.videoQuota,
transcodingThreads: this.customConfig.transcoding.threads,
transcodingEnabled: this.customConfig.transcoding.enabled
transcodingEnabled: this.customConfig.transcoding.enabled,
customizationJavascript: this.customConfig.instance.customizations.javascript,
customizationCSS: this.customConfig.instance.customizations.css
}
for (const resolution of this.resolutions) {

View File

@ -1,3 +1,5 @@
<div *ngIf="customCSS" [innerHTML]="customCSS"></div>
<div>
<div class="header">

View File

@ -1,4 +1,5 @@
import { Component, OnInit } from '@angular/core'
import { DomSanitizer, SafeHtml } from '@angular/platform-browser'
import { GuardsCheckStart, Router } from '@angular/router'
import { AuthService, ServerService } from '@app/core'
import { isInSmallView } from '@app/shared/misc/utils'
@ -24,10 +25,13 @@ export class AppComponent implements OnInit {
isMenuDisplayed = true
customCSS: SafeHtml
constructor (
private router: Router,
private authService: AuthService,
private serverService: ServerService
private serverService: ServerService,
private domSanitizer: DomSanitizer
) {}
get serverVersion () {
@ -66,6 +70,26 @@ export class AppComponent implements OnInit {
}
}
)
this.serverService.configLoaded
.subscribe(() => {
const config = this.serverService.getConfig()
// We test customCSS in case or the admin removed the css
if (this.customCSS || config.instance.customizations.css) {
const styleTag = '<style>' + config.instance.customizations.css + '</style>'
this.customCSS = this.domSanitizer.bypassSecurityTrustHtml(styleTag)
}
if (config.instance.customizations.javascript) {
try {
// tslint:disable:no-eval
eval(config.instance.customizations.javascript)
} catch (err) {
console.error('Cannot eval custom JavaScript.', err)
}
}
})
}
toggleMenu () {

View File

@ -12,6 +12,7 @@ export class ServerService {
private static BASE_VIDEO_URL = environment.apiUrl + '/api/v1/videos/'
private static CONFIG_LOCAL_STORAGE_KEY = 'server-config'
configLoaded = new ReplaySubject<boolean>(1)
videoPrivaciesLoaded = new ReplaySubject<boolean>(1)
videoCategoriesLoaded = new ReplaySubject<boolean>(1)
videoLicencesLoaded = new ReplaySubject<boolean>(1)
@ -19,7 +20,11 @@ export class ServerService {
private config: ServerConfig = {
instance: {
name: 'PeerTube'
name: 'PeerTube',
customizations: {
javascript: '',
css: ''
}
},
serverVersion: 'Unknown',
signup: {
@ -56,7 +61,11 @@ export class ServerService {
loadConfig () {
this.http.get<ServerConfig>(ServerService.BASE_CONFIG_URL)
.do(this.saveConfigLocally)
.subscribe(data => this.config = data)
.subscribe(data => {
this.config = data
this.configLoaded.next(true)
})
}
loadVideoCategories () {

View File

@ -74,3 +74,6 @@ instance:
name: 'PeerTube'
description: 'Welcome to this PeerTube instance!' # Support markdown
terms: 'No terms for now.' # Support markdown
customizations:
javascript: '' # Directly your JavaScript code (without <script> tags). Will be eval at runtime
css: '' # Directly your CSS code (without <style> tags). Will be injected at runtime

View File

@ -74,3 +74,6 @@ instance:
name: 'PeerTube'
description: '' # Support markdown
terms: '' # Support markdown
customizations:
javascript: '' # Directly your JavaScript code (without <script> tags). Will be eval at runtime
css: '' # Directly your CSS code (without <style> tags). Will be injected at runtime

View File

@ -43,7 +43,11 @@ async function getConfig (req: express.Request, res: express.Response, next: exp
const json: ServerConfig = {
instance: {
name: CONFIG.INSTANCE.NAME
name: CONFIG.INSTANCE.NAME,
customizations: {
javascript: CONFIG.INSTANCE.CUSTOMIZATIONS.JAVASCRIPT,
css: CONFIG.INSTANCE.CUSTOMIZATIONS.CSS
}
},
serverVersion: packageJSON.version,
signup: {
@ -132,7 +136,11 @@ function customConfig (): CustomConfig {
instance: {
name: CONFIG.INSTANCE.NAME,
description: CONFIG.INSTANCE.DESCRIPTION,
terms: CONFIG.INSTANCE.TERMS
terms: CONFIG.INSTANCE.TERMS,
customizations: {
css: CONFIG.INSTANCE.CUSTOMIZATIONS.CSS,
javascript: CONFIG.INSTANCE.CUSTOMIZATIONS.JAVASCRIPT
}
},
cache: {
previews: {

View File

@ -158,7 +158,11 @@ const CONFIG = {
INSTANCE: {
get NAME () { return config.get<string>('instance.name') },
get DESCRIPTION () { return config.get<string>('instance.description') },
get TERMS () { return config.get<string>('instance.terms') }
get TERMS () { return config.get<string>('instance.terms') },
CUSTOMIZATIONS: {
get JAVASCRIPT () { return config.get<string>('instance.customizations.javascript') },
get CSS () { return config.get<string>('instance.customizations.css') }
}
}
}

View File

@ -17,7 +17,11 @@ describe('Test config API validators', function () {
instance: {
name: 'PeerTube updated',
description: 'my super description',
terms: 'my super terms'
terms: 'my super terms',
customizations: {
javascript: 'alert("coucou")',
css: 'body { background-color: red; }'
}
},
cache: {
previews: {

View File

@ -3,6 +3,7 @@
import 'mocha'
import * as chai from 'chai'
import { About } from '../../../../shared/models/config/about.model'
import { CustomConfig } from '../../../../shared/models/config/custom-config.model'
import { deleteCustomConfig, getAbout, killallServers, reRunServer } from '../../utils'
const expect = chai.expect
@ -48,11 +49,13 @@ describe('Test config', function () {
it('Should get the customized configuration', async function () {
const res = await getCustomConfig(server.url, server.accessToken)
const data = res.body
const data = res.body as CustomConfig
expect(data.instance.name).to.equal('PeerTube')
expect(data.instance.description).to.equal('Welcome to this PeerTube instance!')
expect(data.instance.terms).to.equal('No terms for now.')
expect(data.instance.customizations.css).to.be.empty
expect(data.instance.customizations.javascript).to.be.empty
expect(data.cache.previews.size).to.equal(1)
expect(data.signup.enabled).to.be.true
expect(data.signup.limit).to.equal(4)
@ -72,7 +75,11 @@ describe('Test config', function () {
instance: {
name: 'PeerTube updated',
description: 'my super description',
terms: 'my super terms'
terms: 'my super terms',
customizations: {
javascript: 'alert("coucou")',
css: 'body { background-color: red; }'
}
},
cache: {
previews: {
@ -109,6 +116,8 @@ describe('Test config', function () {
expect(data.instance.name).to.equal('PeerTube updated')
expect(data.instance.description).to.equal('my super description')
expect(data.instance.terms).to.equal('my super terms')
expect(data.instance.customizations.javascript).to.equal('alert("coucou")')
expect(data.instance.customizations.css).to.equal('body { background-color: red; }')
expect(data.cache.previews.size).to.equal(2)
expect(data.signup.enabled).to.be.false
expect(data.signup.limit).to.equal(5)
@ -136,6 +145,8 @@ describe('Test config', function () {
expect(data.instance.name).to.equal('PeerTube updated')
expect(data.instance.description).to.equal('my super description')
expect(data.instance.terms).to.equal('my super terms')
expect(data.instance.customizations.javascript).to.equal('alert("coucou")')
expect(data.instance.customizations.css).to.equal('body { background-color: red; }')
expect(data.cache.previews.size).to.equal(2)
expect(data.signup.enabled).to.be.false
expect(data.signup.limit).to.equal(5)
@ -167,6 +178,11 @@ describe('Test config', function () {
const res = await getCustomConfig(server.url, server.accessToken)
const data = res.body
expect(data.instance.name).to.equal('PeerTube')
expect(data.instance.description).to.equal('Welcome to this PeerTube instance!')
expect(data.instance.terms).to.equal('No terms for now.')
expect(data.instance.customizations.css).to.be.empty
expect(data.instance.customizations.javascript).to.be.empty
expect(data.cache.previews.size).to.equal(1)
expect(data.signup.enabled).to.be.true
expect(data.signup.limit).to.equal(4)

View File

@ -3,6 +3,10 @@ export interface CustomConfig {
name: string
description: string
terms: string
customizations: {
javascript?: string
css?: string
}
}
cache: {

View File

@ -0,0 +1,8 @@
export interface Customization {
instance: {
customization: {
javascript: string
css: string
}
}
}

View File

@ -2,7 +2,11 @@ export interface ServerConfig {
serverVersion: string
instance: {
name: string
name: string;
customizations: {
javascript: string
css: string
}
}
signup: {