aboutsummaryrefslogtreecommitdiffhomepage
path: root/client/src/app/core/plugins/plugin.service.ts
diff options
context:
space:
mode:
Diffstat (limited to 'client/src/app/core/plugins/plugin.service.ts')
-rw-r--r--client/src/app/core/plugins/plugin.service.ts92
1 files changed, 22 insertions, 70 deletions
diff --git a/client/src/app/core/plugins/plugin.service.ts b/client/src/app/core/plugins/plugin.service.ts
index dc115c0e1..4e44a1865 100644
--- a/client/src/app/core/plugins/plugin.service.ts
+++ b/client/src/app/core/plugins/plugin.service.ts
@@ -7,38 +7,22 @@ import { Notifier } from '@app/core/notification'
7import { MarkdownService } from '@app/core/renderer' 7import { MarkdownService } from '@app/core/renderer'
8import { RestExtractor } from '@app/core/rest' 8import { RestExtractor } from '@app/core/rest'
9import { ServerService } from '@app/core/server/server.service' 9import { ServerService } from '@app/core/server/server.service'
10import { getDevLocale, importModule, isOnDevLocale } from '@app/helpers' 10import { getDevLocale, isOnDevLocale } from '@app/helpers'
11import { CustomModalComponent } from '@app/modal/custom-modal.component' 11import { CustomModalComponent } from '@app/modal/custom-modal.component'
12import { FormFields, Hooks, loadPlugin, PluginInfo, runHook } from '@root-helpers/plugins'
12import { getCompleteLocale, isDefaultLocale, peertubeTranslate } from '@shared/core-utils/i18n' 13import { getCompleteLocale, isDefaultLocale, peertubeTranslate } from '@shared/core-utils/i18n'
13import { getHookType, internalRunHook } from '@shared/core-utils/plugins/hooks'
14import { 14import {
15 ClientHook, 15 ClientHook,
16 ClientHookName, 16 ClientHookName,
17 clientHookObject,
18 ClientScript,
19 PluginClientScope, 17 PluginClientScope,
20 PluginTranslation, 18 PluginTranslation,
21 PluginType, 19 PluginType,
22 PublicServerSetting, 20 PublicServerSetting,
23 RegisterClientHookOptions,
24 ServerConfigPlugin 21 ServerConfigPlugin
25} from '@shared/models' 22} from '@shared/models'
26import { environment } from '../../../environments/environment' 23import { environment } from '../../../environments/environment'
27import { ClientScript as ClientScriptModule } from '../../../types/client-script.model'
28import { RegisterClientHelpers } from '../../../types/register-client-option.model' 24import { RegisterClientHelpers } from '../../../types/register-client-option.model'
29 25
30interface HookStructValue extends RegisterClientHookOptions {
31 plugin: ServerConfigPlugin
32 clientScript: ClientScript
33}
34
35type PluginInfo = {
36 plugin: ServerConfigPlugin
37 clientScript: ClientScript
38 pluginType: PluginType
39 isTheme: boolean
40}
41
42@Injectable() 26@Injectable()
43export class PluginService implements ClientHook { 27export class PluginService implements ClientHook {
44 private static BASE_PLUGIN_API_URL = environment.apiUrl + '/api/v1/plugins' 28 private static BASE_PLUGIN_API_URL = environment.apiUrl + '/api/v1/plugins'
@@ -51,7 +35,9 @@ export class PluginService implements ClientHook {
51 search: new ReplaySubject<boolean>(1), 35 search: new ReplaySubject<boolean>(1),
52 'video-watch': new ReplaySubject<boolean>(1), 36 'video-watch': new ReplaySubject<boolean>(1),
53 signup: new ReplaySubject<boolean>(1), 37 signup: new ReplaySubject<boolean>(1),
54 login: new ReplaySubject<boolean>(1) 38 login: new ReplaySubject<boolean>(1),
39 'video-edit': new ReplaySubject<boolean>(1),
40 embed: new ReplaySubject<boolean>(1)
55 } 41 }
56 42
57 translationsObservable: Observable<PluginTranslation> 43 translationsObservable: Observable<PluginTranslation>
@@ -64,7 +50,10 @@ export class PluginService implements ClientHook {
64 private loadedScopes: PluginClientScope[] = [] 50 private loadedScopes: PluginClientScope[] = []
65 private loadingScopes: { [id in PluginClientScope]?: boolean } = {} 51 private loadingScopes: { [id in PluginClientScope]?: boolean } = {}
66 52
67 private hooks: { [ name: string ]: HookStructValue[] } = {} 53 private hooks: Hooks = {}
54 private formFields: FormFields = {
55 video: []
56 }
68 57
69 constructor ( 58 constructor (
70 private authService: AuthService, 59 private authService: AuthService,
@@ -120,7 +109,7 @@ export class PluginService implements ClientHook {
120 this.scopes[scope].push({ 109 this.scopes[scope].push({
121 plugin, 110 plugin,
122 clientScript: { 111 clientScript: {
123 script: environment.apiUrl + `${pathPrefix}/${plugin.name}/${plugin.version}/client-scripts/${clientScript.script}`, 112 script: `${pathPrefix}/${plugin.name}/${plugin.version}/client-scripts/${clientScript.script}`,
124 scopes: clientScript.scopes 113 scopes: clientScript.scopes
125 }, 114 },
126 pluginType: isTheme ? PluginType.THEME : PluginType.PLUGIN, 115 pluginType: isTheme ? PluginType.THEME : PluginType.PLUGIN,
@@ -184,20 +173,8 @@ export class PluginService implements ClientHook {
184 } 173 }
185 174
186 runHook <T> (hookName: ClientHookName, result?: T, params?: any): Promise<T> { 175 runHook <T> (hookName: ClientHookName, result?: T, params?: any): Promise<T> {
187 return this.zone.runOutsideAngular(async () => { 176 return this.zone.runOutsideAngular(() => {
188 if (!this.hooks[ hookName ]) return result 177 return runHook(this.hooks, hookName, result, params)
189
190 const hookType = getHookType(hookName)
191
192 for (const hook of this.hooks[ hookName ]) {
193 console.log('Running hook %s of plugin %s.', hookName, hook.plugin.name)
194
195 result = await internalRunHook(hook.handler, hookType, result, params, err => {
196 console.error('Cannot run hook %s of script %s of plugin %s.', hookName, hook.clientScript.script, hook.plugin.name, err)
197 })
198 }
199
200 return result
201 }) 178 })
202 } 179 }
203 180
@@ -215,35 +192,18 @@ export class PluginService implements ClientHook {
215 : PluginType.THEME 192 : PluginType.THEME
216 } 193 }
217 194
218 private loadPlugin (pluginInfo: PluginInfo) { 195 getRegisteredVideoFormFields (type: 'import-url' | 'import-torrent' | 'upload' | 'update') {
219 const { plugin, clientScript } = pluginInfo 196 return this.formFields.video.filter(f => f.videoFormOptions.type === type)
220 197 }
221 const registerHook = (options: RegisterClientHookOptions) => {
222 if (clientHookObject[options.target] !== true) {
223 console.error('Unknown hook %s of plugin %s. Skipping.', options.target, plugin.name)
224 return
225 }
226
227 if (!this.hooks[options.target]) this.hooks[options.target] = []
228
229 this.hooks[options.target].push({
230 plugin,
231 clientScript,
232 target: options.target,
233 handler: options.handler,
234 priority: options.priority || 0
235 })
236 }
237
238 const peertubeHelpers = this.buildPeerTubeHelpers(pluginInfo)
239
240 console.log('Loading script %s of plugin %s.', clientScript.script, plugin.name)
241 198
199 private loadPlugin (pluginInfo: PluginInfo) {
242 return this.zone.runOutsideAngular(() => { 200 return this.zone.runOutsideAngular(() => {
243 return importModule(clientScript.script) 201 return loadPlugin({
244 .then((script: ClientScriptModule) => script.register({ registerHook, peertubeHelpers })) 202 hooks: this.hooks,
245 .then(() => this.sortHooksByPriority()) 203 formFields: this.formFields,
246 .catch(err => console.error('Cannot import or register plugin %s.', pluginInfo.plugin.name, err)) 204 pluginInfo,
205 peertubeHelpersFactory: pluginInfo => this.buildPeerTubeHelpers(pluginInfo)
206 })
247 }) 207 })
248 } 208 }
249 209
@@ -253,14 +213,6 @@ export class PluginService implements ClientHook {
253 } 213 }
254 } 214 }
255 215
256 private sortHooksByPriority () {
257 for (const hookName of Object.keys(this.hooks)) {
258 this.hooks[hookName].sort((a, b) => {
259 return b.priority - a.priority
260 })
261 }
262 }
263
264 private buildPeerTubeHelpers (pluginInfo: PluginInfo): RegisterClientHelpers { 216 private buildPeerTubeHelpers (pluginInfo: PluginInfo): RegisterClientHelpers {
265 const { plugin } = pluginInfo 217 const { plugin } = pluginInfo
266 const npmName = this.nameToNpmName(pluginInfo.plugin.name, pluginInfo.pluginType) 218 const npmName = this.nameToNpmName(pluginInfo.plugin.name, pluginInfo.pluginType)