aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--client/src/app/+videos/+video-watch/shared/information/privacy-concerns.component.scss7
-rw-r--r--client/src/sass/bootstrap.scss7
-rw-r--r--client/src/sass/include/_variables.scss5
-rwxr-xr-xscripts/ci.sh3
-rw-r--r--server/lib/plugins/register-helpers.ts222
-rw-r--r--server/lib/plugins/video-constant-manager-factory.ts139
-rw-r--r--server/tests/fixtures/peertube-plugin-test-video-constants/main.js46
-rw-r--r--server/tests/index.ts1
-rw-r--r--server/tests/lib/index.ts1
-rw-r--r--server/tests/lib/video-constant-registry-factory.ts155
-rw-r--r--server/tests/plugins/video-constants.ts40
-rw-r--r--shared/models/plugins/server/managers/plugin-playlist-privacy-manager.model.ts12
-rw-r--r--shared/models/plugins/server/managers/plugin-video-category-manager.model.ts10
-rw-r--r--shared/models/plugins/server/managers/plugin-video-language-manager.model.ts10
-rw-r--r--shared/models/plugins/server/managers/plugin-video-licence-manager.model.ts10
-rw-r--r--shared/models/plugins/server/managers/plugin-video-privacy-manager.model.ts14
-rw-r--r--shared/models/plugins/server/plugin-constant-manager.model.ts7
-rw-r--r--support/doc/plugins/guide.md28
18 files changed, 486 insertions, 231 deletions
diff --git a/client/src/app/+videos/+video-watch/shared/information/privacy-concerns.component.scss b/client/src/app/+videos/+video-watch/shared/information/privacy-concerns.component.scss
index b42be318f..a6479c7ec 100644
--- a/client/src/app/+videos/+video-watch/shared/information/privacy-concerns.component.scss
+++ b/client/src/app/+videos/+video-watch/shared/information/privacy-concerns.component.scss
@@ -25,6 +25,13 @@
25 } 25 }
26} 26}
27 27
28// Avoid higher z-index when overlay on touchscreens
29:host-context(.menu-open) {
30 .privacy-concerns {
31 z-index: z(overlay) - 1;
32 }
33}
34
28// Or if we are in the small view 35// Or if we are in the small view
29@media screen and (max-width: $small-view) { 36@media screen and (max-width: $small-view) {
30 .privacy-concerns { 37 .privacy-concerns {
diff --git a/client/src/sass/bootstrap.scss b/client/src/sass/bootstrap.scss
index 058033166..4f6e08c1b 100644
--- a/client/src/sass/bootstrap.scss
+++ b/client/src/sass/bootstrap.scss
@@ -59,13 +59,14 @@ $icon-font-path: '~@neos21/bootstrap3-glyphicons/assets/fonts/';
59} 59}
60 60
61/* rules for dropdowns excepts when in button group, to avoid impacting the dropdown-toggle */ 61/* rules for dropdowns excepts when in button group, to avoid impacting the dropdown-toggle */
62.dropdown { 62.dropdown,
63.dropup {
63 z-index: z(dropdown) !important; 64 z-index: z(dropdown) !important;
64} 65}
65 66
66.list-overflow-menu, 67.list-overflow-menu,
67.parent-entry { 68.parent-entry {
68 z-index: z(header) - 1 !important; 69 z-index: z(menu) - 1 !important;
69} 70}
70 71
71.btn-group, 72.btn-group,
@@ -213,7 +214,7 @@ $icon-font-path: '~@neos21/bootstrap3-glyphicons/assets/fonts/';
213 content: ''; 214 content: '';
214 display: block; 215 display: block;
215 position: fixed; 216 position: fixed;
216 z-index: z('menu') - 1; 217 z-index: z(overlay);
217 } 218 }
218 } 219 }
219 } 220 }
diff --git a/client/src/sass/include/_variables.scss b/client/src/sass/include/_variables.scss
index 39e81f270..e0a4c7d3f 100644
--- a/client/src/sass/include/_variables.scss
+++ b/client/src/sass/include/_variables.scss
@@ -159,17 +159,18 @@ $variables: (
159 159
160$zindex: ( 160$zindex: (
161 miniature : 10, 161 miniature : 10,
162 privacymsg : 20,
163 sub-menu : 12500, 162 sub-menu : 12500,
163 overlay : 12550,
164 menu : 12600, 164 menu : 12600,
165 search-typeahead: 12650, 165 search-typeahead: 12650,
166 header : 12700,
167 popover : 13000, 166 popover : 13000,
168 tooltip : 14000, 167 tooltip : 14000,
169 loadbar : 15000, 168 loadbar : 15000,
170 modal : 16000, 169 modal : 16000,
171 dropdown : 17000, 170 dropdown : 17000,
172 help-popover : 17000, 171 help-popover : 17000,
172 privacymsg : 17500,
173 header : 17500,
173 notification : 18000, 174 notification : 18000,
174 hotkeys : 19000 175 hotkeys : 19000
175); 176);
diff --git a/scripts/ci.sh b/scripts/ci.sh
index 07e37e0ee..7862888b8 100755
--- a/scripts/ci.sh
+++ b/scripts/ci.sh
@@ -47,11 +47,12 @@ if [ "$1" = "client" ]; then
47 47
48 feedsFiles=$(findTestFiles ./dist/server/tests/feeds) 48 feedsFiles=$(findTestFiles ./dist/server/tests/feeds)
49 helperFiles=$(findTestFiles ./dist/server/tests/helpers) 49 helperFiles=$(findTestFiles ./dist/server/tests/helpers)
50 libFiles=$(findTestFiles ./dist/server/tests/lib)
50 miscFiles="./dist/server/tests/client.js ./dist/server/tests/misc-endpoints.js" 51 miscFiles="./dist/server/tests/client.js ./dist/server/tests/misc-endpoints.js"
51 # Not in plugin task, it needs an index.html 52 # Not in plugin task, it needs an index.html
52 pluginFiles="./dist/server/tests/plugins/html-injection.js" 53 pluginFiles="./dist/server/tests/plugins/html-injection.js"
53 54
54 MOCHA_PARALLEL=true runTest "$1" 2 $feedsFiles $helperFiles $miscFiles $pluginFiles 55 MOCHA_PARALLEL=true runTest "$1" 2 $feedsFiles $helperFiles $miscFiles $pluginFiles $libFiles
55elif [ "$1" = "cli-plugin" ]; then 56elif [ "$1" = "cli-plugin" ]; then
56 npm run build:server 57 npm run build:server
57 npm run setup:cli 58 npm run setup:cli
diff --git a/server/lib/plugins/register-helpers.ts b/server/lib/plugins/register-helpers.ts
index 09275f9ba..af533effd 100644
--- a/server/lib/plugins/register-helpers.ts
+++ b/server/lib/plugins/register-helpers.ts
@@ -1,13 +1,7 @@
1import * as express from 'express' 1import * as express from 'express'
2import { logger } from '@server/helpers/logger' 2import { logger } from '@server/helpers/logger'
3import {
4 VIDEO_CATEGORIES,
5 VIDEO_LANGUAGES,
6 VIDEO_LICENCES,
7 VIDEO_PLAYLIST_PRIVACIES,
8 VIDEO_PRIVACIES
9} from '@server/initializers/constants'
10import { onExternalUserAuthenticated } from '@server/lib/auth/external-auth' 3import { onExternalUserAuthenticated } from '@server/lib/auth/external-auth'
4import { VideoConstantManagerFactory } from '@server/lib/plugins/video-constant-manager-factory'
11import { PluginModel } from '@server/models/server/plugin' 5import { PluginModel } from '@server/models/server/plugin'
12import { 6import {
13 RegisterServerAuthExternalOptions, 7 RegisterServerAuthExternalOptions,
@@ -18,41 +12,18 @@ import {
18} from '@server/types/plugins' 12} from '@server/types/plugins'
19import { 13import {
20 EncoderOptionsBuilder, 14 EncoderOptionsBuilder,
21 PluginPlaylistPrivacyManager,
22 PluginSettingsManager, 15 PluginSettingsManager,
23 PluginStorageManager, 16 PluginStorageManager,
24 PluginVideoCategoryManager,
25 PluginVideoLanguageManager,
26 PluginVideoLicenceManager,
27 PluginVideoPrivacyManager,
28 RegisterServerHookOptions, 17 RegisterServerHookOptions,
29 RegisterServerSettingOptions, 18 RegisterServerSettingOptions,
30 serverHookObject 19 serverHookObject,
20 VideoPlaylistPrivacy,
21 VideoPrivacy
31} from '@shared/models' 22} from '@shared/models'
32import { VideoTranscodingProfilesManager } from '../transcoding/video-transcoding-profiles' 23import { VideoTranscodingProfilesManager } from '../transcoding/video-transcoding-profiles'
33import { buildPluginHelpers } from './plugin-helpers-builder' 24import { buildPluginHelpers } from './plugin-helpers-builder'
34 25
35type AlterableVideoConstant = 'language' | 'licence' | 'category' | 'privacy' | 'playlistPrivacy'
36type VideoConstant = { [key in number | string]: string }
37
38type UpdatedVideoConstant = {
39 [name in AlterableVideoConstant]: {
40 [ npmName: string]: {
41 added: { key: number | string, label: string }[]
42 deleted: { key: number | string, label: string }[]
43 }
44 }
45}
46
47export class RegisterHelpers { 26export class RegisterHelpers {
48 private readonly updatedVideoConstants: UpdatedVideoConstant = {
49 playlistPrivacy: { },
50 privacy: { },
51 language: { },
52 licence: { },
53 category: { }
54 }
55
56 private readonly transcodingProfiles: { 27 private readonly transcodingProfiles: {
57 [ npmName: string ]: { 28 [ npmName: string ]: {
58 type: 'vod' | 'live' 29 type: 'vod' | 'live'
@@ -78,6 +49,7 @@ export class RegisterHelpers {
78 private readonly onSettingsChangeCallbacks: ((settings: any) => Promise<any>)[] = [] 49 private readonly onSettingsChangeCallbacks: ((settings: any) => Promise<any>)[] = []
79 50
80 private readonly router: express.Router 51 private readonly router: express.Router
52 private readonly videoConstantManagerFactory: VideoConstantManagerFactory
81 53
82 constructor ( 54 constructor (
83 private readonly npmName: string, 55 private readonly npmName: string,
@@ -85,6 +57,7 @@ export class RegisterHelpers {
85 private readonly onHookAdded: (options: RegisterServerHookOptions) => void 57 private readonly onHookAdded: (options: RegisterServerHookOptions) => void
86 ) { 58 ) {
87 this.router = express.Router() 59 this.router = express.Router()
60 this.videoConstantManagerFactory = new VideoConstantManagerFactory(this.npmName)
88 } 61 }
89 62
90 buildRegisterHelpers (): RegisterServerOptions { 63 buildRegisterHelpers (): RegisterServerOptions {
@@ -96,13 +69,13 @@ export class RegisterHelpers {
96 const settingsManager = this.buildSettingsManager() 69 const settingsManager = this.buildSettingsManager()
97 const storageManager = this.buildStorageManager() 70 const storageManager = this.buildStorageManager()
98 71
99 const videoLanguageManager = this.buildVideoLanguageManager() 72 const videoLanguageManager = this.videoConstantManagerFactory.createVideoConstantManager<string>('language')
100 73
101 const videoLicenceManager = this.buildVideoLicenceManager() 74 const videoLicenceManager = this.videoConstantManagerFactory.createVideoConstantManager<number>('licence')
102 const videoCategoryManager = this.buildVideoCategoryManager() 75 const videoCategoryManager = this.videoConstantManagerFactory.createVideoConstantManager<number>('category')
103 76
104 const videoPrivacyManager = this.buildVideoPrivacyManager() 77 const videoPrivacyManager = this.videoConstantManagerFactory.createVideoConstantManager<VideoPrivacy>('privacy')
105 const playlistPrivacyManager = this.buildPlaylistPrivacyManager() 78 const playlistPrivacyManager = this.videoConstantManagerFactory.createVideoConstantManager<VideoPlaylistPrivacy>('playlistPrivacy')
106 79
107 const transcodingManager = this.buildTranscodingManager() 80 const transcodingManager = this.buildTranscodingManager()
108 81
@@ -122,12 +95,38 @@ export class RegisterHelpers {
122 settingsManager, 95 settingsManager,
123 storageManager, 96 storageManager,
124 97
125 videoLanguageManager, 98 videoLanguageManager: {
126 videoCategoryManager, 99 ...videoLanguageManager,
127 videoLicenceManager, 100 /** @deprecated use `addConstant` instead **/
101 addLanguage: videoLanguageManager.addConstant,
102 /** @deprecated use `deleteConstant` instead **/
103 deleteLanguage: videoLanguageManager.deleteConstant
104 },
105 videoCategoryManager: {
106 ...videoCategoryManager,
107 /** @deprecated use `addConstant` instead **/
108 addCategory: videoCategoryManager.addConstant,
109 /** @deprecated use `deleteConstant` instead **/
110 deleteCategory: videoCategoryManager.deleteConstant
111 },
112 videoLicenceManager: {
113 ...videoLicenceManager,
114 /** @deprecated use `addConstant` instead **/
115 addLicence: videoLicenceManager.addConstant,
116 /** @deprecated use `deleteConstant` instead **/
117 deleteLicence: videoLicenceManager.deleteConstant
118 },
128 119
129 videoPrivacyManager, 120 videoPrivacyManager: {
130 playlistPrivacyManager, 121 ...videoPrivacyManager,
122 /** @deprecated use `deleteConstant` instead **/
123 deletePrivacy: videoPrivacyManager.deleteConstant
124 },
125 playlistPrivacyManager: {
126 ...playlistPrivacyManager,
127 /** @deprecated use `deleteConstant` instead **/
128 deletePlaylistPrivacy: playlistPrivacyManager.deleteConstant
129 },
131 130
132 transcodingManager, 131 transcodingManager,
133 132
@@ -141,29 +140,7 @@ export class RegisterHelpers {
141 } 140 }
142 141
143 reinitVideoConstants (npmName: string) { 142 reinitVideoConstants (npmName: string) {
144 const hash = { 143 this.videoConstantManagerFactory.resetVideoConstants(npmName)
145 language: VIDEO_LANGUAGES,
146 licence: VIDEO_LICENCES,
147 category: VIDEO_CATEGORIES,
148 privacy: VIDEO_PRIVACIES,
149 playlistPrivacy: VIDEO_PLAYLIST_PRIVACIES
150 }
151 const types: AlterableVideoConstant[] = [ 'language', 'licence', 'category', 'privacy', 'playlistPrivacy' ]
152
153 for (const type of types) {
154 const updatedConstants = this.updatedVideoConstants[type][npmName]
155 if (!updatedConstants) continue
156
157 for (const added of updatedConstants.added) {
158 delete hash[type][added.key]
159 }
160
161 for (const deleted of updatedConstants.deleted) {
162 hash[type][deleted.key] = deleted.label
163 }
164
165 delete this.updatedVideoConstants[type][npmName]
166 }
167 } 144 }
168 145
169 reinitTranscodingProfilesAndEncoders (npmName: string) { 146 reinitTranscodingProfilesAndEncoders (npmName: string) {
@@ -291,119 +268,6 @@ export class RegisterHelpers {
291 } 268 }
292 } 269 }
293 270
294 private buildVideoLanguageManager (): PluginVideoLanguageManager {
295 return {
296 addLanguage: (key: string, label: string) => {
297 return this.addConstant({ npmName: this.npmName, type: 'language', obj: VIDEO_LANGUAGES, key, label })
298 },
299
300 deleteLanguage: (key: string) => {
301 return this.deleteConstant({ npmName: this.npmName, type: 'language', obj: VIDEO_LANGUAGES, key })
302 }
303 }
304 }
305
306 private buildVideoCategoryManager (): PluginVideoCategoryManager {
307 return {
308 addCategory: (key: number, label: string) => {
309 return this.addConstant({ npmName: this.npmName, type: 'category', obj: VIDEO_CATEGORIES, key, label })
310 },
311
312 deleteCategory: (key: number) => {
313 return this.deleteConstant({ npmName: this.npmName, type: 'category', obj: VIDEO_CATEGORIES, key })
314 }
315 }
316 }
317
318 private buildVideoPrivacyManager (): PluginVideoPrivacyManager {
319 return {
320 deletePrivacy: (key: number) => {
321 return this.deleteConstant({ npmName: this.npmName, type: 'privacy', obj: VIDEO_PRIVACIES, key })
322 }
323 }
324 }
325
326 private buildPlaylistPrivacyManager (): PluginPlaylistPrivacyManager {
327 return {
328 deletePlaylistPrivacy: (key: number) => {
329 return this.deleteConstant({ npmName: this.npmName, type: 'playlistPrivacy', obj: VIDEO_PLAYLIST_PRIVACIES, key })
330 }
331 }
332 }
333
334 private buildVideoLicenceManager (): PluginVideoLicenceManager {
335 return {
336 addLicence: (key: number, label: string) => {
337 return this.addConstant({ npmName: this.npmName, type: 'licence', obj: VIDEO_LICENCES, key, label })
338 },
339
340 deleteLicence: (key: number) => {
341 return this.deleteConstant({ npmName: this.npmName, type: 'licence', obj: VIDEO_LICENCES, key })
342 }
343 }
344 }
345
346 private addConstant<T extends string | number> (parameters: {
347 npmName: string
348 type: AlterableVideoConstant
349 obj: VideoConstant
350 key: T
351 label: string
352 }) {
353 const { npmName, type, obj, key, label } = parameters
354
355 if (obj[key]) {
356 logger.warn('Cannot add %s %s by plugin %s: key already exists.', type, npmName, key)
357 return false
358 }
359
360 if (!this.updatedVideoConstants[type][npmName]) {
361 this.updatedVideoConstants[type][npmName] = {
362 added: [],
363 deleted: []
364 }
365 }
366
367 this.updatedVideoConstants[type][npmName].added.push({ key, label })
368 obj[key] = label
369
370 return true
371 }
372
373 private deleteConstant<T extends string | number> (parameters: {
374 npmName: string
375 type: AlterableVideoConstant
376 obj: VideoConstant
377 key: T
378 }) {
379 const { npmName, type, obj, key } = parameters
380
381 if (!obj[key]) {
382 logger.warn('Cannot delete %s by plugin %s: key %s does not exist.', type, npmName, key)
383 return false
384 }
385
386 if (!this.updatedVideoConstants[type][npmName]) {
387 this.updatedVideoConstants[type][npmName] = {
388 added: [],
389 deleted: []
390 }
391 }
392
393 const updatedConstants = this.updatedVideoConstants[type][npmName]
394
395 const alreadyAdded = updatedConstants.added.find(a => a.key === key)
396 if (alreadyAdded) {
397 updatedConstants.added.filter(a => a.key !== key)
398 } else if (obj[key]) {
399 updatedConstants.deleted.push({ key, label: obj[key] })
400 }
401
402 delete obj[key]
403
404 return true
405 }
406
407 private buildTranscodingManager () { 271 private buildTranscodingManager () {
408 const self = this 272 const self = this
409 273
diff --git a/server/lib/plugins/video-constant-manager-factory.ts b/server/lib/plugins/video-constant-manager-factory.ts
new file mode 100644
index 000000000..f04dde29f
--- /dev/null
+++ b/server/lib/plugins/video-constant-manager-factory.ts
@@ -0,0 +1,139 @@
1import { logger } from '@server/helpers/logger'
2import {
3 VIDEO_CATEGORIES,
4 VIDEO_LANGUAGES,
5 VIDEO_LICENCES,
6 VIDEO_PLAYLIST_PRIVACIES,
7 VIDEO_PRIVACIES
8} from '@server/initializers/constants'
9import { ConstantManager } from '@shared/models/plugins/server/plugin-constant-manager.model'
10
11type AlterableVideoConstant = 'language' | 'licence' | 'category' | 'privacy' | 'playlistPrivacy'
12type VideoConstant = Record<number | string, string>
13
14type UpdatedVideoConstant = {
15 [name in AlterableVideoConstant]: {
16 [ npmName: string]: {
17 added: VideoConstant[]
18 deleted: VideoConstant[]
19 }
20 }
21}
22
23const constantsHash: { [key in AlterableVideoConstant]: VideoConstant } = {
24 language: VIDEO_LANGUAGES,
25 licence: VIDEO_LICENCES,
26 category: VIDEO_CATEGORIES,
27 privacy: VIDEO_PRIVACIES,
28 playlistPrivacy: VIDEO_PLAYLIST_PRIVACIES
29}
30
31export class VideoConstantManagerFactory {
32 private readonly updatedVideoConstants: UpdatedVideoConstant = {
33 playlistPrivacy: { },
34 privacy: { },
35 language: { },
36 licence: { },
37 category: { }
38 }
39
40 constructor (
41 private readonly npmName: string
42 ) {}
43
44 public resetVideoConstants (npmName: string) {
45 const types: AlterableVideoConstant[] = [ 'language', 'licence', 'category', 'privacy', 'playlistPrivacy' ]
46 for (const type of types) {
47 this.resetConstants({ npmName, type })
48 }
49 }
50
51 private resetConstants (parameters: { npmName: string, type: AlterableVideoConstant }) {
52 const { npmName, type } = parameters
53 const updatedConstants = this.updatedVideoConstants[type][npmName]
54
55 if (!updatedConstants) return
56
57 for (const added of updatedConstants.added) {
58 delete constantsHash[type][added.key]
59 }
60
61 for (const deleted of updatedConstants.deleted) {
62 constantsHash[type][deleted.key] = deleted.label
63 }
64
65 delete this.updatedVideoConstants[type][npmName]
66 }
67
68 public createVideoConstantManager<K extends number | string>(type: AlterableVideoConstant): ConstantManager<K> {
69 const { npmName } = this
70 return {
71 addConstant: (key: K, label: string) => this.addConstant({ npmName, type, key, label }),
72 deleteConstant: (key: K) => this.deleteConstant({ npmName, type, key }),
73 getConstantValue: (key: K) => constantsHash[type][key],
74 getConstants: () => constantsHash[type] as Record<K, string>,
75 resetConstants: () => this.resetConstants({ npmName, type })
76 }
77 }
78
79 private addConstant<T extends string | number> (parameters: {
80 npmName: string
81 type: AlterableVideoConstant
82 key: T
83 label: string
84 }) {
85 const { npmName, type, key, label } = parameters
86 const obj = constantsHash[type]
87
88 if (obj[key]) {
89 logger.warn('Cannot add %s %s by plugin %s: key already exists.', type, npmName, key)
90 return false
91 }
92
93 if (!this.updatedVideoConstants[type][npmName]) {
94 this.updatedVideoConstants[type][npmName] = {
95 added: [],
96 deleted: []
97 }
98 }
99
100 this.updatedVideoConstants[type][npmName].added.push({ key: key, label } as VideoConstant)
101 obj[key] = label
102
103 return true
104 }
105
106 private deleteConstant<T extends string | number> (parameters: {
107 npmName: string
108 type: AlterableVideoConstant
109 key: T
110 }) {
111 const { npmName, type, key } = parameters
112 const obj = constantsHash[type]
113
114 if (!obj[key]) {
115 logger.warn('Cannot delete %s by plugin %s: key %s does not exist.', type, npmName, key)
116 return false
117 }
118
119 if (!this.updatedVideoConstants[type][npmName]) {
120 this.updatedVideoConstants[type][npmName] = {
121 added: [],
122 deleted: []
123 }
124 }
125
126 const updatedConstants = this.updatedVideoConstants[type][npmName]
127
128 const alreadyAdded = updatedConstants.added.find(a => a.key === key)
129 if (alreadyAdded) {
130 updatedConstants.added.filter(a => a.key !== key)
131 } else if (obj[key]) {
132 updatedConstants.deleted.push({ key, label: obj[key] } as VideoConstant)
133 }
134
135 delete obj[key]
136
137 return true
138 }
139}
diff --git a/server/tests/fixtures/peertube-plugin-test-video-constants/main.js b/server/tests/fixtures/peertube-plugin-test-video-constants/main.js
index 3e650e0a1..06527bd35 100644
--- a/server/tests/fixtures/peertube-plugin-test-video-constants/main.js
+++ b/server/tests/fixtures/peertube-plugin-test-video-constants/main.js
@@ -1,46 +1,46 @@
1async function register ({ 1async function register ({
2 registerHook,
3 registerSetting,
4 settingsManager,
5 storageManager,
6 videoCategoryManager, 2 videoCategoryManager,
7 videoLicenceManager, 3 videoLicenceManager,
8 videoLanguageManager, 4 videoLanguageManager,
9 videoPrivacyManager, 5 videoPrivacyManager,
10 playlistPrivacyManager 6 playlistPrivacyManager,
7 getRouter
11}) { 8}) {
12 videoLanguageManager.addLanguage('al_bhed', 'Al Bhed') 9 videoLanguageManager.addConstant('al_bhed', 'Al Bhed')
13 videoLanguageManager.addLanguage('al_bhed2', 'Al Bhed 2') 10 videoLanguageManager.addLanguage('al_bhed2', 'Al Bhed 2')
14 videoLanguageManager.addLanguage('al_bhed3', 'Al Bhed 3') 11 videoLanguageManager.addConstant('al_bhed3', 'Al Bhed 3')
15 videoLanguageManager.deleteLanguage('en') 12 videoLanguageManager.deleteConstant('en')
16 videoLanguageManager.deleteLanguage('fr') 13 videoLanguageManager.deleteLanguage('fr')
17 videoLanguageManager.deleteLanguage('al_bhed3') 14 videoLanguageManager.deleteConstant('al_bhed3')
18 15
19 videoCategoryManager.addCategory(42, 'Best category') 16 videoCategoryManager.addCategory(42, 'Best category')
20 videoCategoryManager.addCategory(43, 'High best category') 17 videoCategoryManager.addConstant(43, 'High best category')
21 videoCategoryManager.deleteCategory(1) // Music 18 videoCategoryManager.deleteConstant(1) // Music
22 videoCategoryManager.deleteCategory(2) // Films 19 videoCategoryManager.deleteCategory(2) // Films
23 20
24 videoLicenceManager.addLicence(42, 'Best licence') 21 videoLicenceManager.addLicence(42, 'Best licence')
25 videoLicenceManager.addLicence(43, 'High best licence') 22 videoLicenceManager.addConstant(43, 'High best licence')
26 videoLicenceManager.deleteLicence(1) // Attribution 23 videoLicenceManager.deleteConstant(1) // Attribution
27 videoLicenceManager.deleteLicence(7) // Public domain 24 videoLicenceManager.deleteConstant(7) // Public domain
28 25
26 videoPrivacyManager.deleteConstant(2)
29 videoPrivacyManager.deletePrivacy(2) 27 videoPrivacyManager.deletePrivacy(2)
28 playlistPrivacyManager.deleteConstant(3)
30 playlistPrivacyManager.deletePlaylistPrivacy(3) 29 playlistPrivacyManager.deletePlaylistPrivacy(3)
31}
32 30
33async function unregister () { 31 {
34 return 32 const router = getRouter()
33 router.get('/reset-categories', (req, res) => {
34 videoCategoryManager.resetConstants()
35
36 res.sendStatus(204)
37 })
38 }
35} 39}
36 40
41async function unregister () {}
42
37module.exports = { 43module.exports = {
38 register, 44 register,
39 unregister 45 unregister
40} 46}
41
42// ############################################################################
43
44function addToCount (obj) {
45 return Object.assign({}, obj, { count: obj.count + 1 })
46}
diff --git a/server/tests/index.ts b/server/tests/index.ts
index 3fbd0ebbd..1718ac424 100644
--- a/server/tests/index.ts
+++ b/server/tests/index.ts
@@ -6,3 +6,4 @@ import './cli/'
6import './api/' 6import './api/'
7import './plugins/' 7import './plugins/'
8import './helpers/' 8import './helpers/'
9import './lib/'
diff --git a/server/tests/lib/index.ts b/server/tests/lib/index.ts
new file mode 100644
index 000000000..a40df35fd
--- /dev/null
+++ b/server/tests/lib/index.ts
@@ -0,0 +1 @@
export * from './video-constant-registry-factory'
diff --git a/server/tests/lib/video-constant-registry-factory.ts b/server/tests/lib/video-constant-registry-factory.ts
new file mode 100644
index 000000000..e26b286e1
--- /dev/null
+++ b/server/tests/lib/video-constant-registry-factory.ts
@@ -0,0 +1,155 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions */
2import 'mocha'
3import { expect } from 'chai'
4import { VideoConstantManagerFactory } from '@server/lib/plugins/video-constant-manager-factory'
5import {
6 VIDEO_CATEGORIES,
7 VIDEO_LANGUAGES,
8 VIDEO_LICENCES,
9 VIDEO_PLAYLIST_PRIVACIES,
10 VIDEO_PRIVACIES
11} from '@server/initializers/constants'
12import {
13 VideoPlaylistPrivacy,
14 VideoPrivacy
15} from '@shared/models'
16
17describe('VideoConstantManagerFactory', function () {
18 const factory = new VideoConstantManagerFactory('peertube-plugin-constants')
19
20 afterEach(() => {
21 factory.resetVideoConstants('peertube-plugin-constants')
22 })
23
24 describe('VideoCategoryManager', () => {
25 const videoCategoryManager = factory.createVideoConstantManager<number>('category')
26
27 it('Should be able to list all video category constants', () => {
28 const constants = videoCategoryManager.getConstants()
29 expect(constants).to.deep.equal(VIDEO_CATEGORIES)
30 })
31
32 it('Should be able to delete a video category constant', () => {
33 const successfullyDeleted = videoCategoryManager.deleteConstant(1)
34 expect(successfullyDeleted).to.be.true
35 expect(videoCategoryManager.getConstantValue(1)).to.be.undefined
36 })
37
38 it('Should be able to add a video category constant', () => {
39 const successfullyAdded = videoCategoryManager.addConstant(42, 'The meaning of life')
40 expect(successfullyAdded).to.be.true
41 expect(videoCategoryManager.getConstantValue(42)).to.equal('The meaning of life')
42 })
43
44 it('Should be able to reset video category constants', () => {
45 videoCategoryManager.deleteConstant(1)
46 videoCategoryManager.resetConstants()
47 expect(videoCategoryManager.getConstantValue(1)).not.be.undefined
48 })
49 })
50
51 describe('VideoLicenceManager', () => {
52 const videoLicenceManager = factory.createVideoConstantManager<number>('licence')
53 it('Should be able to list all video licence constants', () => {
54 const constants = videoLicenceManager.getConstants()
55 expect(constants).to.deep.equal(VIDEO_LICENCES)
56 })
57
58 it('Should be able to delete a video licence constant', () => {
59 const successfullyDeleted = videoLicenceManager.deleteConstant(1)
60 expect(successfullyDeleted).to.be.true
61 expect(videoLicenceManager.getConstantValue(1)).to.be.undefined
62 })
63
64 it('Should be able to add a video licence constant', () => {
65 const successfullyAdded = videoLicenceManager.addConstant(42, 'European Union Public Licence')
66 expect(successfullyAdded).to.be.true
67 expect(videoLicenceManager.getConstantValue(42)).to.equal('European Union Public Licence')
68 })
69
70 it('Should be able to reset video licence constants', () => {
71 videoLicenceManager.deleteConstant(1)
72 videoLicenceManager.resetConstants()
73 expect(videoLicenceManager.getConstantValue(1)).not.be.undefined
74 })
75 })
76
77 describe('PlaylistPrivacyManager', () => {
78 const playlistPrivacyManager = factory.createVideoConstantManager<VideoPlaylistPrivacy>('playlistPrivacy')
79 it('Should be able to list all video playlist privacy constants', () => {
80 const constants = playlistPrivacyManager.getConstants()
81 expect(constants).to.deep.equal(VIDEO_PLAYLIST_PRIVACIES)
82 })
83
84 it('Should be able to delete a video playlist privacy constant', () => {
85 const successfullyDeleted = playlistPrivacyManager.deleteConstant(1)
86 expect(successfullyDeleted).to.be.true
87 expect(playlistPrivacyManager.getConstantValue(1)).to.be.undefined
88 })
89
90 it('Should be able to add a video playlist privacy constant', () => {
91 const successfullyAdded = playlistPrivacyManager.addConstant(42, 'Friends only')
92 expect(successfullyAdded).to.be.true
93 expect(playlistPrivacyManager.getConstantValue(42)).to.equal('Friends only')
94 })
95
96 it('Should be able to reset video playlist privacy constants', () => {
97 playlistPrivacyManager.deleteConstant(1)
98 playlistPrivacyManager.resetConstants()
99 expect(playlistPrivacyManager.getConstantValue(1)).not.be.undefined
100 })
101 })
102
103 describe('VideoPrivacyManager', () => {
104 const videoPrivacyManager = factory.createVideoConstantManager<VideoPrivacy>('privacy')
105 it('Should be able to list all video privacy constants', () => {
106 const constants = videoPrivacyManager.getConstants()
107 expect(constants).to.deep.equal(VIDEO_PRIVACIES)
108 })
109
110 it('Should be able to delete a video privacy constant', () => {
111 const successfullyDeleted = videoPrivacyManager.deleteConstant(1)
112 expect(successfullyDeleted).to.be.true
113 expect(videoPrivacyManager.getConstantValue(1)).to.be.undefined
114 })
115
116 it('Should be able to add a video privacy constant', () => {
117 const successfullyAdded = videoPrivacyManager.addConstant(42, 'Friends only')
118 expect(successfullyAdded).to.be.true
119 expect(videoPrivacyManager.getConstantValue(42)).to.equal('Friends only')
120 })
121
122 it('Should be able to reset video privacy constants', () => {
123 videoPrivacyManager.deleteConstant(1)
124 videoPrivacyManager.resetConstants()
125 expect(videoPrivacyManager.getConstantValue(1)).not.be.undefined
126 })
127 })
128
129 describe('VideoLanguageManager', () => {
130 const videoLanguageManager = factory.createVideoConstantManager<string>('language')
131 it('Should be able to list all video language constants', () => {
132 const constants = videoLanguageManager.getConstants()
133 expect(constants).to.deep.equal(VIDEO_LANGUAGES)
134 })
135
136 it('Should be able to add a video language constant', () => {
137 const successfullyAdded = videoLanguageManager.addConstant('fr', 'Fr occitan')
138 expect(successfullyAdded).to.be.true
139 expect(videoLanguageManager.getConstantValue('fr')).to.equal('Fr occitan')
140 })
141
142 it('Should be able to delete a video language constant', () => {
143 videoLanguageManager.addConstant('fr', 'Fr occitan')
144 const successfullyDeleted = videoLanguageManager.deleteConstant('fr')
145 expect(successfullyDeleted).to.be.true
146 expect(videoLanguageManager.getConstantValue('fr')).to.be.undefined
147 })
148
149 it('Should be able to reset video language constants', () => {
150 videoLanguageManager.addConstant('fr', 'Fr occitan')
151 videoLanguageManager.resetConstants()
152 expect(videoLanguageManager.getConstantValue('fr')).to.be.undefined
153 })
154 })
155})
diff --git a/server/tests/plugins/video-constants.ts b/server/tests/plugins/video-constants.ts
index f527a2b30..19cba6c2c 100644
--- a/server/tests/plugins/video-constants.ts
+++ b/server/tests/plugins/video-constants.ts
@@ -2,7 +2,14 @@
2 2
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { cleanupTests, createSingleServer, PeerTubeServer, PluginsCommand, setAccessTokensToServers } from '@shared/extra-utils' 5import {
6 cleanupTests,
7 createSingleServer,
8 makeGetRequest,
9 PeerTubeServer,
10 PluginsCommand,
11 setAccessTokensToServers
12} from '@shared/extra-utils'
6import { HttpStatusCode, VideoPlaylistPrivacy } from '@shared/models' 13import { HttpStatusCode, VideoPlaylistPrivacy } from '@shared/models'
7 14
8const expect = chai.expect 15const expect = chai.expect
@@ -139,6 +146,37 @@ describe('Test plugin altering video constants', function () {
139 } 146 }
140 }) 147 })
141 148
149 it('Should be able to reset categories', async function () {
150 await server.plugins.install({ path: PluginsCommand.getPluginTestPath('-video-constants') })
151
152 {
153 const categories = await server.videos.getCategories()
154
155 expect(categories[1]).to.not.exist
156 expect(categories[2]).to.not.exist
157
158 expect(categories[42]).to.exist
159 expect(categories[43]).to.exist
160 }
161
162 await makeGetRequest({
163 url: server.url,
164 token: server.accessToken,
165 path: '/plugins/test-video-constants/router/reset-categories',
166 expectedStatus: HttpStatusCode.NO_CONTENT_204
167 })
168
169 {
170 const categories = await server.videos.getCategories()
171
172 expect(categories[1]).to.exist
173 expect(categories[2]).to.exist
174
175 expect(categories[42]).to.not.exist
176 expect(categories[43]).to.not.exist
177 }
178 })
179
142 after(async function () { 180 after(async function () {
143 await cleanupTests([ server ]) 181 await cleanupTests([ server ])
144 }) 182 })
diff --git a/shared/models/plugins/server/managers/plugin-playlist-privacy-manager.model.ts b/shared/models/plugins/server/managers/plugin-playlist-privacy-manager.model.ts
index 4703c0a8b..5b3b37752 100644
--- a/shared/models/plugins/server/managers/plugin-playlist-privacy-manager.model.ts
+++ b/shared/models/plugins/server/managers/plugin-playlist-privacy-manager.model.ts
@@ -1,8 +1,12 @@
1import { VideoPlaylistPrivacy } from '../../../videos/playlist/video-playlist-privacy.model' 1import { VideoPlaylistPrivacy } from '../../../videos/playlist/video-playlist-privacy.model'
2import { ConstantManager } from '@shared/models/plugins/server/plugin-constant-manager.model'
2 3
3export interface PluginPlaylistPrivacyManager { 4export interface PluginPlaylistPrivacyManager extends ConstantManager<VideoPlaylistPrivacy> {
4 // PUBLIC = 1, 5 /**
5 // UNLISTED = 2, 6 * PUBLIC = 1,
6 // PRIVATE = 3 7 * UNLISTED = 2,
8 * PRIVATE = 3
9 * @deprecated use `deleteConstant` instead
10 */
7 deletePlaylistPrivacy: (privacyKey: VideoPlaylistPrivacy) => boolean 11 deletePlaylistPrivacy: (privacyKey: VideoPlaylistPrivacy) => boolean
8} 12}
diff --git a/shared/models/plugins/server/managers/plugin-video-category-manager.model.ts b/shared/models/plugins/server/managers/plugin-video-category-manager.model.ts
index 201bfa979..069ad1476 100644
--- a/shared/models/plugins/server/managers/plugin-video-category-manager.model.ts
+++ b/shared/models/plugins/server/managers/plugin-video-category-manager.model.ts
@@ -1,5 +1,13 @@
1export interface PluginVideoCategoryManager { 1import { ConstantManager } from '@shared/models/plugins/server/plugin-constant-manager.model'
2
3export interface PluginVideoCategoryManager extends ConstantManager<number> {
4 /**
5 * @deprecated use `addConstant` instead
6 */
2 addCategory: (categoryKey: number, categoryLabel: string) => boolean 7 addCategory: (categoryKey: number, categoryLabel: string) => boolean
3 8
9 /**
10 * @deprecated use `deleteConstant` instead
11 */
4 deleteCategory: (categoryKey: number) => boolean 12 deleteCategory: (categoryKey: number) => boolean
5} 13}
diff --git a/shared/models/plugins/server/managers/plugin-video-language-manager.model.ts b/shared/models/plugins/server/managers/plugin-video-language-manager.model.ts
index 3fd577a79..969c6c670 100644
--- a/shared/models/plugins/server/managers/plugin-video-language-manager.model.ts
+++ b/shared/models/plugins/server/managers/plugin-video-language-manager.model.ts
@@ -1,5 +1,13 @@
1export interface PluginVideoLanguageManager { 1import { ConstantManager } from '@shared/models/plugins/server/plugin-constant-manager.model'
2
3export interface PluginVideoLanguageManager extends ConstantManager<string> {
4 /**
5 * @deprecated use `addConstant` instead
6 */
2 addLanguage: (languageKey: string, languageLabel: string) => boolean 7 addLanguage: (languageKey: string, languageLabel: string) => boolean
3 8
9 /**
10 * @deprecated use `deleteConstant` instead
11 */
4 deleteLanguage: (languageKey: string) => boolean 12 deleteLanguage: (languageKey: string) => boolean
5} 13}
diff --git a/shared/models/plugins/server/managers/plugin-video-licence-manager.model.ts b/shared/models/plugins/server/managers/plugin-video-licence-manager.model.ts
index 82a634d3a..900a49661 100644
--- a/shared/models/plugins/server/managers/plugin-video-licence-manager.model.ts
+++ b/shared/models/plugins/server/managers/plugin-video-licence-manager.model.ts
@@ -1,5 +1,13 @@
1export interface PluginVideoLicenceManager { 1import { ConstantManager } from '@shared/models/plugins/server/plugin-constant-manager.model'
2
3export interface PluginVideoLicenceManager extends ConstantManager<number> {
4 /**
5 * @deprecated use `addLicence` instead
6 */
2 addLicence: (licenceKey: number, licenceLabel: string) => boolean 7 addLicence: (licenceKey: number, licenceLabel: string) => boolean
3 8
9 /**
10 * @deprecated use `deleteLicence` instead
11 */
4 deleteLicence: (licenceKey: number) => boolean 12 deleteLicence: (licenceKey: number) => boolean
5} 13}
diff --git a/shared/models/plugins/server/managers/plugin-video-privacy-manager.model.ts b/shared/models/plugins/server/managers/plugin-video-privacy-manager.model.ts
index 7717115e3..e26e48a53 100644
--- a/shared/models/plugins/server/managers/plugin-video-privacy-manager.model.ts
+++ b/shared/models/plugins/server/managers/plugin-video-privacy-manager.model.ts
@@ -1,9 +1,13 @@
1import { VideoPrivacy } from '../../../videos/video-privacy.enum' 1import { VideoPrivacy } from '../../../videos/video-privacy.enum'
2import { ConstantManager } from '@shared/models/plugins/server/plugin-constant-manager.model'
2 3
3export interface PluginVideoPrivacyManager { 4export interface PluginVideoPrivacyManager extends ConstantManager<VideoPrivacy> {
4 // PUBLIC = 1 5 /**
5 // UNLISTED = 2 6 * PUBLIC = 1,
6 // PRIVATE = 3 7 * UNLISTED = 2,
7 // INTERNAL = 4 8 * PRIVATE = 3
9 * INTERNAL = 4
10 * @deprecated use `deleteConstant` instead
11 */
8 deletePrivacy: (privacyKey: VideoPrivacy) => boolean 12 deletePrivacy: (privacyKey: VideoPrivacy) => boolean
9} 13}
diff --git a/shared/models/plugins/server/plugin-constant-manager.model.ts b/shared/models/plugins/server/plugin-constant-manager.model.ts
new file mode 100644
index 000000000..4de3ce38f
--- /dev/null
+++ b/shared/models/plugins/server/plugin-constant-manager.model.ts
@@ -0,0 +1,7 @@
1export interface ConstantManager <K extends string | number> {
2 addConstant: (key: K, label: string) => boolean
3 deleteConstant: (key: K) => boolean
4 getConstantValue: (key: K) => string
5 getConstants: () => Record<K, string>
6 resetConstants: () => void
7}
diff --git a/support/doc/plugins/guide.md b/support/doc/plugins/guide.md
index 568c0662f..85aaf9f02 100644
--- a/support/doc/plugins/guide.md
+++ b/support/doc/plugins/guide.md
@@ -234,21 +234,29 @@ function register ({
234 234
235#### Update video constants 235#### Update video constants
236 236
237You can add/delete video categories, licences or languages using the appropriate managers: 237You can add/delete video categories, licences or languages using the appropriate constant managers:
238 238
239```js 239```js
240function register (...) { 240function register ({
241 videoLanguageManager.addLanguage('al_bhed', 'Al Bhed') 241 videoLanguageManager,
242 videoLanguageManager.deleteLanguage('fr') 242 videoCategoryManager,
243 videoLicenceManager,
244 videoPrivacyManager,
245 playlistPrivacyManager
246}) {
247 videoLanguageManager.addConstant('al_bhed', 'Al Bhed')
248 videoLanguageManager.deleteConstant('fr')
243 249
244 videoCategoryManager.addCategory(42, 'Best category') 250 videoCategoryManager.addConstant(42, 'Best category')
245 videoCategoryManager.deleteCategory(1) // Music 251 videoCategoryManager.deleteConstant(1) // Music
252 videoCategoryManager.resetConstants() // Reset to initial categories
253 videoCategoryManager.getConstants() // Retrieve all category constants
246 254
247 videoLicenceManager.addLicence(42, 'Best licence') 255 videoLicenceManager.addConstant(42, 'Best licence')
248 videoLicenceManager.deleteLicence(7) // Public domain 256 videoLicenceManager.deleteConstant(7) // Public domain
249 257
250 videoPrivacyManager.deletePrivacy(2) // Remove Unlisted video privacy 258 videoPrivacyManager.deleteConstant(2) // Remove Unlisted video privacy
251 playlistPrivacyManager.deletePlaylistPrivacy(3) // Remove Private video playlist privacy 259 playlistPrivacyManager.deleteConstant(3) // Remove Private video playlist privacy
252} 260}
253``` 261```
254 262