diff options
Diffstat (limited to 'client/src/app/core/plugins/plugin.service.ts')
-rw-r--r-- | client/src/app/core/plugins/plugin.service.ts | 92 |
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' | |||
7 | import { MarkdownService } from '@app/core/renderer' | 7 | import { MarkdownService } from '@app/core/renderer' |
8 | import { RestExtractor } from '@app/core/rest' | 8 | import { RestExtractor } from '@app/core/rest' |
9 | import { ServerService } from '@app/core/server/server.service' | 9 | import { ServerService } from '@app/core/server/server.service' |
10 | import { getDevLocale, importModule, isOnDevLocale } from '@app/helpers' | 10 | import { getDevLocale, isOnDevLocale } from '@app/helpers' |
11 | import { CustomModalComponent } from '@app/modal/custom-modal.component' | 11 | import { CustomModalComponent } from '@app/modal/custom-modal.component' |
12 | import { FormFields, Hooks, loadPlugin, PluginInfo, runHook } from '@root-helpers/plugins' | ||
12 | import { getCompleteLocale, isDefaultLocale, peertubeTranslate } from '@shared/core-utils/i18n' | 13 | import { getCompleteLocale, isDefaultLocale, peertubeTranslate } from '@shared/core-utils/i18n' |
13 | import { getHookType, internalRunHook } from '@shared/core-utils/plugins/hooks' | ||
14 | import { | 14 | import { |
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' |
26 | import { environment } from '../../../environments/environment' | 23 | import { environment } from '../../../environments/environment' |
27 | import { ClientScript as ClientScriptModule } from '../../../types/client-script.model' | ||
28 | import { RegisterClientHelpers } from '../../../types/register-client-option.model' | 24 | import { RegisterClientHelpers } from '../../../types/register-client-option.model' |
29 | 25 | ||
30 | interface HookStructValue extends RegisterClientHookOptions { | ||
31 | plugin: ServerConfigPlugin | ||
32 | clientScript: ClientScript | ||
33 | } | ||
34 | |||
35 | type PluginInfo = { | ||
36 | plugin: ServerConfigPlugin | ||
37 | clientScript: ClientScript | ||
38 | pluginType: PluginType | ||
39 | isTheme: boolean | ||
40 | } | ||
41 | |||
42 | @Injectable() | 26 | @Injectable() |
43 | export class PluginService implements ClientHook { | 27 | export 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) |