aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2019-07-26 13:13:39 +0200
committerChocobozzz <me@florianbigard.com>2019-07-26 15:18:30 +0200
commitee286591a5b740702bad66c55cc900740f749e9a (patch)
tree16503d1299a107c5972ba16f95228b1ebce20f79
parent16d54696294d15f8ab6ba3a6bcfac21528fec2f2 (diff)
downloadPeerTube-ee286591a5b740702bad66c55cc900740f749e9a.tar.gz
PeerTube-ee286591a5b740702bad66c55cc900740f749e9a.tar.zst
PeerTube-ee286591a5b740702bad66c55cc900740f749e9a.zip
Plugins can update video constants
Categories, licences and languages
-rw-r--r--server/lib/plugins/plugin-manager.ts122
-rw-r--r--server/tests/fixtures/peertube-plugin-test-three/main.js39
-rw-r--r--server/tests/fixtures/peertube-plugin-test-three/package.json19
-rw-r--r--server/tests/plugins/index.ts1
-rw-r--r--server/tests/plugins/video-constants.ts140
-rw-r--r--server/typings/plugins/register-server-option.model.ts7
-rw-r--r--shared/models/plugins/plugin-video-category-manager.model.ts5
-rw-r--r--shared/models/plugins/plugin-video-language-manager.model.ts5
-rw-r--r--shared/models/plugins/plugin-video-licence-manager.model.ts5
9 files changed, 342 insertions, 1 deletions
diff --git a/server/lib/plugins/plugin-manager.ts b/server/lib/plugins/plugin-manager.ts
index 78e8d758f..81554a09e 100644
--- a/server/lib/plugins/plugin-manager.ts
+++ b/server/lib/plugins/plugin-manager.ts
@@ -5,7 +5,7 @@ import { CONFIG } from '../../initializers/config'
5import { isLibraryCodeValid, isPackageJSONValid } from '../../helpers/custom-validators/plugins' 5import { isLibraryCodeValid, isPackageJSONValid } from '../../helpers/custom-validators/plugins'
6import { ClientScript, PluginPackageJson } from '../../../shared/models/plugins/plugin-package-json.model' 6import { ClientScript, PluginPackageJson } from '../../../shared/models/plugins/plugin-package-json.model'
7import { createReadStream, createWriteStream } from 'fs' 7import { createReadStream, createWriteStream } from 'fs'
8import { PLUGIN_GLOBAL_CSS_PATH } from '../../initializers/constants' 8import { PLUGIN_GLOBAL_CSS_PATH, VIDEO_CATEGORIES, VIDEO_LANGUAGES, VIDEO_LICENCES } from '../../initializers/constants'
9import { PluginType } from '../../../shared/models/plugins/plugin.type' 9import { PluginType } from '../../../shared/models/plugins/plugin.type'
10import { installNpmPlugin, installNpmPluginFromDisk, removeNpmPlugin } from './yarn' 10import { installNpmPlugin, installNpmPluginFromDisk, removeNpmPlugin } from './yarn'
11import { outputFile, readJSON } from 'fs-extra' 11import { outputFile, readJSON } from 'fs-extra'
@@ -18,6 +18,9 @@ import { PluginLibrary } from '../../typings/plugins'
18import { ClientHtml } from '../client-html' 18import { ClientHtml } from '../client-html'
19import { RegisterServerHookOptions } from '../../../shared/models/plugins/register-server-hook.model' 19import { RegisterServerHookOptions } from '../../../shared/models/plugins/register-server-hook.model'
20import { RegisterServerSettingOptions } from '../../../shared/models/plugins/register-server-setting.model' 20import { RegisterServerSettingOptions } from '../../../shared/models/plugins/register-server-setting.model'
21import { PluginVideoLanguageManager } from '../../../shared/models/plugins/plugin-video-language-manager.model'
22import { PluginVideoCategoryManager } from '../../../shared/models/plugins/plugin-video-category-manager.model'
23import { PluginVideoLicenceManager } from '../../../shared/models/plugins/plugin-video-licence-manager.model'
21 24
22export interface RegisteredPlugin { 25export interface RegisteredPlugin {
23 npmName: string 26 npmName: string
@@ -46,6 +49,17 @@ export interface HookInformationValue {
46 priority: number 49 priority: number
47} 50}
48 51
52type AlterableVideoConstant = 'language' | 'licence' | 'category'
53type VideoConstant = { [ key in number | string ]: string }
54type UpdatedVideoConstant = {
55 [ name in AlterableVideoConstant ]: {
56 [ npmName: string ]: {
57 added: { key: number | string, label: string }[],
58 deleted: { key: number | string, label: string }[]
59 }
60 }
61}
62
49export class PluginManager implements ServerHook { 63export class PluginManager implements ServerHook {
50 64
51 private static instance: PluginManager 65 private static instance: PluginManager
@@ -54,6 +68,12 @@ export class PluginManager implements ServerHook {
54 private settings: { [ name: string ]: RegisterServerSettingOptions[] } = {} 68 private settings: { [ name: string ]: RegisterServerSettingOptions[] } = {}
55 private hooks: { [ name: string ]: HookInformationValue[] } = {} 69 private hooks: { [ name: string ]: HookInformationValue[] } = {}
56 70
71 private updatedVideoConstants: UpdatedVideoConstant = {
72 language: {},
73 licence: {},
74 category: {}
75 }
76
57 private constructor () { 77 private constructor () {
58 } 78 }
59 79
@@ -161,6 +181,8 @@ export class PluginManager implements ServerHook {
161 this.hooks[key] = this.hooks[key].filter(h => h.pluginName !== npmName) 181 this.hooks[key] = this.hooks[key].filter(h => h.pluginName !== npmName)
162 } 182 }
163 183
184 this.reinitVideoConstants(plugin.npmName)
185
164 logger.info('Regenerating registered plugin CSS to global file.') 186 logger.info('Regenerating registered plugin CSS to global file.')
165 await this.regeneratePluginGlobalCSS() 187 await this.regeneratePluginGlobalCSS()
166 } 188 }
@@ -427,6 +449,24 @@ export class PluginManager implements ServerHook {
427 storeData: (key: string, data: any) => PluginModel.storeData(plugin.name, plugin.type, key, data) 449 storeData: (key: string, data: any) => PluginModel.storeData(plugin.name, plugin.type, key, data)
428 } 450 }
429 451
452 const videoLanguageManager: PluginVideoLanguageManager = {
453 addLanguage: (key: string, label: string) => this.addConstant({ npmName, type: 'language', obj: VIDEO_LANGUAGES, key, label }),
454
455 deleteLanguage: (key: string) => this.deleteConstant({ npmName, type: 'language', obj: VIDEO_LANGUAGES, key })
456 }
457
458 const videoCategoryManager: PluginVideoCategoryManager= {
459 addCategory: (key: number, label: string) => this.addConstant({ npmName, type: 'category', obj: VIDEO_CATEGORIES, key, label }),
460
461 deleteCategory: (key: number) => this.deleteConstant({ npmName, type: 'category', obj: VIDEO_CATEGORIES, key })
462 }
463
464 const videoLicenceManager: PluginVideoLicenceManager = {
465 addLicence: (key: number, label: string) => this.addConstant({ npmName, type: 'licence', obj: VIDEO_LICENCES, key, label }),
466
467 deleteLicence: (key: number) => this.deleteConstant({ npmName, type: 'licence', obj: VIDEO_LICENCES, key })
468 }
469
430 const peertubeHelpers = { 470 const peertubeHelpers = {
431 logger 471 logger
432 } 472 }
@@ -436,10 +476,90 @@ export class PluginManager implements ServerHook {
436 registerSetting, 476 registerSetting,
437 settingsManager, 477 settingsManager,
438 storageManager, 478 storageManager,
479 videoLanguageManager,
480 videoCategoryManager,
481 videoLicenceManager,
439 peertubeHelpers 482 peertubeHelpers
440 } 483 }
441 } 484 }
442 485
486 private addConstant <T extends string | number> (parameters: {
487 npmName: string,
488 type: AlterableVideoConstant,
489 obj: VideoConstant,
490 key: T,
491 label: string
492 }) {
493 const { npmName, type, obj, key, label } = parameters
494
495 if (obj[key]) {
496 logger.warn('Cannot add %s %s by plugin %s: key already exists.', type, npmName, key)
497 return false
498 }
499
500 if (!this.updatedVideoConstants[type][npmName]) {
501 this.updatedVideoConstants[type][npmName] = {
502 added: [],
503 deleted: []
504 }
505 }
506
507 this.updatedVideoConstants[type][npmName].added.push({ key, label })
508 obj[key] = label
509
510 return true
511 }
512
513 private deleteConstant <T extends string | number> (parameters: {
514 npmName: string,
515 type: AlterableVideoConstant,
516 obj: VideoConstant,
517 key: T
518 }) {
519 const { npmName, type, obj, key } = parameters
520
521 if (!obj[key]) {
522 logger.warn('Cannot delete %s %s by plugin %s: key does not exist.', type, npmName, key)
523 return false
524 }
525
526 if (!this.updatedVideoConstants[type][npmName]) {
527 this.updatedVideoConstants[type][npmName] = {
528 added: [],
529 deleted: []
530 }
531 }
532
533 this.updatedVideoConstants[type][npmName].deleted.push({ key, label: obj[key] })
534 delete obj[key]
535
536 return true
537 }
538
539 private reinitVideoConstants (npmName: string) {
540 const hash = {
541 language: VIDEO_LANGUAGES,
542 licence: VIDEO_LICENCES,
543 category: VIDEO_CATEGORIES
544 }
545 const types: AlterableVideoConstant[] = [ 'language', 'licence', 'category' ]
546
547 for (const type of types) {
548 const updatedConstants = this.updatedVideoConstants[type][npmName]
549 if (!updatedConstants) continue
550
551 for (const added of updatedConstants.added) {
552 delete hash[type][added.key]
553 }
554
555 for (const deleted of updatedConstants.deleted) {
556 hash[type][deleted.key] = deleted.label
557 }
558
559 delete this.updatedVideoConstants[type][npmName]
560 }
561 }
562
443 static get Instance () { 563 static get Instance () {
444 return this.instance || (this.instance = new this()) 564 return this.instance || (this.instance = new this())
445 } 565 }
diff --git a/server/tests/fixtures/peertube-plugin-test-three/main.js b/server/tests/fixtures/peertube-plugin-test-three/main.js
new file mode 100644
index 000000000..4945feb55
--- /dev/null
+++ b/server/tests/fixtures/peertube-plugin-test-three/main.js
@@ -0,0 +1,39 @@
1async function register ({
2 registerHook,
3 registerSetting,
4 settingsManager,
5 storageManager,
6 videoCategoryManager,
7 videoLicenceManager,
8 videoLanguageManager
9}) {
10 videoLanguageManager.addLanguage('al_bhed', 'Al Bhed')
11 videoLanguageManager.addLanguage('al_bhed2', 'Al Bhed 2')
12 videoLanguageManager.deleteLanguage('en')
13 videoLanguageManager.deleteLanguage('fr')
14
15 videoCategoryManager.addCategory(42, 'Best category')
16 videoCategoryManager.addCategory(43, 'High best category')
17 videoCategoryManager.deleteCategory(1) // Music
18 videoCategoryManager.deleteCategory(2) // Films
19
20 videoLicenceManager.addLicence(42, 'Best licence')
21 videoLicenceManager.addLicence(43, 'High best licence')
22 videoLicenceManager.deleteLicence(1) // Attribution
23 videoLicenceManager.deleteLicence(7) // Public domain
24}
25
26async function unregister () {
27 return
28}
29
30module.exports = {
31 register,
32 unregister
33}
34
35// ############################################################################
36
37function addToCount (obj) {
38 return Object.assign({}, obj, { count: obj.count + 1 })
39}
diff --git a/server/tests/fixtures/peertube-plugin-test-three/package.json b/server/tests/fixtures/peertube-plugin-test-three/package.json
new file mode 100644
index 000000000..3f7819db3
--- /dev/null
+++ b/server/tests/fixtures/peertube-plugin-test-three/package.json
@@ -0,0 +1,19 @@
1{
2 "name": "peertube-plugin-test-three",
3 "version": "0.0.1",
4 "description": "Plugin test 3",
5 "engine": {
6 "peertube": ">=1.3.0"
7 },
8 "keywords": [
9 "peertube",
10 "plugin"
11 ],
12 "homepage": "https://github.com/Chocobozzz/PeerTube",
13 "author": "Chocobozzz",
14 "bugs": "https://github.com/Chocobozzz/PeerTube/issues",
15 "library": "./main.js",
16 "staticDirs": {},
17 "css": [],
18 "clientScripts": []
19}
diff --git a/server/tests/plugins/index.ts b/server/tests/plugins/index.ts
index d97ca1515..95e358732 100644
--- a/server/tests/plugins/index.ts
+++ b/server/tests/plugins/index.ts
@@ -1,2 +1,3 @@
1import './action-hooks' 1import './action-hooks'
2import './filter-hooks' 2import './filter-hooks'
3import './video-constants'
diff --git a/server/tests/plugins/video-constants.ts b/server/tests/plugins/video-constants.ts
new file mode 100644
index 000000000..6562e2b45
--- /dev/null
+++ b/server/tests/plugins/video-constants.ts
@@ -0,0 +1,140 @@
1/* tslint:disable:no-unused-expression */
2
3import * as chai from 'chai'
4import 'mocha'
5import {
6 cleanupTests,
7 flushAndRunMultipleServers,
8 flushAndRunServer, killallServers, reRunServer,
9 ServerInfo,
10 waitUntilLog
11} from '../../../shared/extra-utils/server/servers'
12import {
13 addVideoCommentReply,
14 addVideoCommentThread,
15 deleteVideoComment,
16 getPluginTestPath,
17 getVideosList,
18 installPlugin,
19 removeVideo,
20 setAccessTokensToServers,
21 updateVideo,
22 uploadVideo,
23 viewVideo,
24 getVideosListPagination,
25 getVideo,
26 getVideoCommentThreads,
27 getVideoThreadComments,
28 getVideoWithToken,
29 setDefaultVideoChannel,
30 waitJobs,
31 doubleFollow, getVideoLanguages, getVideoLicences, getVideoCategories, uninstallPlugin
32} from '../../../shared/extra-utils'
33import { VideoCommentThreadTree } from '../../../shared/models/videos/video-comment.model'
34import { VideoDetails } from '../../../shared/models/videos'
35import { getYoutubeVideoUrl, importVideo } from '../../../shared/extra-utils/videos/video-imports'
36
37const expect = chai.expect
38
39describe('Test plugin altering video constants', function () {
40 let server: ServerInfo
41
42 before(async function () {
43 this.timeout(30000)
44
45 server = await flushAndRunServer(1)
46 await setAccessTokensToServers([ server ])
47
48 await installPlugin({
49 url: server.url,
50 accessToken: server.accessToken,
51 path: getPluginTestPath('-three')
52 })
53 })
54
55 it('Should have updated languages', async function () {
56 const res = await getVideoLanguages(server.url)
57 const languages = res.body
58
59 expect(languages['en']).to.not.exist
60 expect(languages['fr']).to.not.exist
61
62 expect(languages['al_bhed']).to.equal('Al Bhed')
63 expect(languages['al_bhed2']).to.equal('Al Bhed 2')
64 })
65
66 it('Should have updated categories', async function () {
67 const res = await getVideoCategories(server.url)
68 const categories = res.body
69
70 expect(categories[1]).to.not.exist
71 expect(categories[2]).to.not.exist
72
73 expect(categories[42]).to.equal('Best category')
74 expect(categories[43]).to.equal('High best category')
75 })
76
77 it('Should have updated licences', async function () {
78 const res = await getVideoLicences(server.url)
79 const licences = res.body
80
81 expect(licences[1]).to.not.exist
82 expect(licences[7]).to.not.exist
83
84 expect(licences[42]).to.equal('Best licence')
85 expect(licences[43]).to.equal('High best licence')
86 })
87
88 it('Should be able to upload a video with these values', async function () {
89 const attrs = { name: 'video', category: 42, licence: 42, language: 'al_bhed2' }
90 const resUpload = await uploadVideo(server.url, server.accessToken, attrs)
91
92 const res = await getVideo(server.url, resUpload.body.video.uuid)
93
94 const video: VideoDetails = res.body
95 expect(video.language.label).to.equal('Al Bhed 2')
96 expect(video.licence.label).to.equal('Best licence')
97 expect(video.category.label).to.equal('Best category')
98 })
99
100 it('Should uninstall the plugin and reset languages, categories and licences', async function () {
101 await uninstallPlugin({ url: server.url, accessToken: server.accessToken, npmName: 'peertube-plugin-test-three' })
102
103 {
104 const res = await getVideoLanguages(server.url)
105 const languages = res.body
106
107 expect(languages[ 'en' ]).to.equal('English')
108 expect(languages[ 'fr' ]).to.equal('French')
109
110 expect(languages[ 'al_bhed' ]).to.not.exist
111 expect(languages[ 'al_bhed2' ]).to.not.exist
112 }
113
114 {
115 const res = await getVideoCategories(server.url)
116 const categories = res.body
117
118 expect(categories[ 1 ]).to.equal('Music')
119 expect(categories[ 2 ]).to.equal('Films')
120
121 expect(categories[ 42 ]).to.not.exist
122 expect(categories[ 43 ]).to.not.exist
123 }
124
125 {
126 const res = await getVideoLicences(server.url)
127 const licences = res.body
128
129 expect(licences[ 1 ]).to.equal('Attribution')
130 expect(licences[ 7 ]).to.equal('Public Domain Dedication')
131
132 expect(licences[ 42 ]).to.not.exist
133 expect(licences[ 43 ]).to.not.exist
134 }
135 })
136
137 after(async function () {
138 await cleanupTests([ server ])
139 })
140})
diff --git a/server/typings/plugins/register-server-option.model.ts b/server/typings/plugins/register-server-option.model.ts
index 91a06a7c5..54753cc01 100644
--- a/server/typings/plugins/register-server-option.model.ts
+++ b/server/typings/plugins/register-server-option.model.ts
@@ -3,6 +3,9 @@ import { PluginSettingsManager } from '../../../shared/models/plugins/plugin-set
3import { PluginStorageManager } from '../../../shared/models/plugins/plugin-storage-manager.model' 3import { PluginStorageManager } from '../../../shared/models/plugins/plugin-storage-manager.model'
4import { RegisterServerHookOptions } from '../../../shared/models/plugins/register-server-hook.model' 4import { RegisterServerHookOptions } from '../../../shared/models/plugins/register-server-hook.model'
5import { RegisterServerSettingOptions } from '../../../shared/models/plugins/register-server-setting.model' 5import { RegisterServerSettingOptions } from '../../../shared/models/plugins/register-server-setting.model'
6import { PluginVideoCategoryManager } from '../../../shared/models/plugins/plugin-video-category-manager.model'
7import { PluginVideoLanguageManager } from '../../../shared/models/plugins/plugin-video-language-manager.model'
8import { PluginVideoLicenceManager } from '../../../shared/models/plugins/plugin-video-licence-manager.model'
6 9
7export type RegisterServerOptions = { 10export type RegisterServerOptions = {
8 registerHook: (options: RegisterServerHookOptions) => void 11 registerHook: (options: RegisterServerHookOptions) => void
@@ -13,6 +16,10 @@ export type RegisterServerOptions = {
13 16
14 storageManager: PluginStorageManager 17 storageManager: PluginStorageManager
15 18
19 videoCategoryManager: PluginVideoCategoryManager
20 videoLanguageManager: PluginVideoLanguageManager
21 videoLicenceManager: PluginVideoLicenceManager
22
16 peertubeHelpers: { 23 peertubeHelpers: {
17 logger: typeof logger 24 logger: typeof logger
18 } 25 }
diff --git a/shared/models/plugins/plugin-video-category-manager.model.ts b/shared/models/plugins/plugin-video-category-manager.model.ts
new file mode 100644
index 000000000..201bfa979
--- /dev/null
+++ b/shared/models/plugins/plugin-video-category-manager.model.ts
@@ -0,0 +1,5 @@
1export interface PluginVideoCategoryManager {
2 addCategory: (categoryKey: number, categoryLabel: string) => boolean
3
4 deleteCategory: (categoryKey: number) => boolean
5}
diff --git a/shared/models/plugins/plugin-video-language-manager.model.ts b/shared/models/plugins/plugin-video-language-manager.model.ts
new file mode 100644
index 000000000..3fd577a79
--- /dev/null
+++ b/shared/models/plugins/plugin-video-language-manager.model.ts
@@ -0,0 +1,5 @@
1export interface PluginVideoLanguageManager {
2 addLanguage: (languageKey: string, languageLabel: string) => boolean
3
4 deleteLanguage: (languageKey: string) => boolean
5}
diff --git a/shared/models/plugins/plugin-video-licence-manager.model.ts b/shared/models/plugins/plugin-video-licence-manager.model.ts
new file mode 100644
index 000000000..82a634d3a
--- /dev/null
+++ b/shared/models/plugins/plugin-video-licence-manager.model.ts
@@ -0,0 +1,5 @@
1export interface PluginVideoLicenceManager {
2 addLicence: (licenceKey: number, licenceLabel: string) => boolean
3
4 deleteLicence: (licenceKey: number) => boolean
5}