diff options
Diffstat (limited to 'client/src')
6 files changed, 69 insertions, 34 deletions
diff --git a/client/src/app/+admin/plugins/plugin-list-installed/plugin-list-installed.component.ts b/client/src/app/+admin/plugins/plugin-list-installed/plugin-list-installed.component.ts index 9809759db..dced14dee 100644 --- a/client/src/app/+admin/plugins/plugin-list-installed/plugin-list-installed.component.ts +++ b/client/src/app/+admin/plugins/plugin-list-installed/plugin-list-installed.component.ts | |||
@@ -7,6 +7,7 @@ import { ConfirmService, Notifier } from '@app/core' | |||
7 | import { PeerTubePlugin } from '@shared/models/plugins/peertube-plugin.model' | 7 | import { PeerTubePlugin } from '@shared/models/plugins/peertube-plugin.model' |
8 | import { ActivatedRoute, Router } from '@angular/router' | 8 | import { ActivatedRoute, Router } from '@angular/router' |
9 | import { compareSemVer } from '@shared/core-utils/miscs/miscs' | 9 | import { compareSemVer } from '@shared/core-utils/miscs/miscs' |
10 | import { PluginService } from '@app/core/plugins/plugin.service' | ||
10 | 11 | ||
11 | @Component({ | 12 | @Component({ |
12 | selector: 'my-plugin-list-installed', | 13 | selector: 'my-plugin-list-installed', |
@@ -34,13 +35,14 @@ export class PluginListInstalledComponent implements OnInit { | |||
34 | 35 | ||
35 | constructor ( | 36 | constructor ( |
36 | private i18n: I18n, | 37 | private i18n: I18n, |
37 | private pluginService: PluginApiService, | 38 | private pluginService: PluginService, |
39 | private pluginApiService: PluginApiService, | ||
38 | private notifier: Notifier, | 40 | private notifier: Notifier, |
39 | private confirmService: ConfirmService, | 41 | private confirmService: ConfirmService, |
40 | private router: Router, | 42 | private router: Router, |
41 | private route: ActivatedRoute | 43 | private route: ActivatedRoute |
42 | ) { | 44 | ) { |
43 | this.pluginTypeOptions = this.pluginService.getPluginTypeOptions() | 45 | this.pluginTypeOptions = this.pluginApiService.getPluginTypeOptions() |
44 | } | 46 | } |
45 | 47 | ||
46 | ngOnInit () { | 48 | ngOnInit () { |
@@ -60,7 +62,7 @@ export class PluginListInstalledComponent implements OnInit { | |||
60 | } | 62 | } |
61 | 63 | ||
62 | loadMorePlugins () { | 64 | loadMorePlugins () { |
63 | this.pluginService.getPlugins(this.pluginType, this.pagination, this.sort) | 65 | this.pluginApiService.getPlugins(this.pluginType, this.pagination, this.sort) |
64 | .subscribe( | 66 | .subscribe( |
65 | res => { | 67 | res => { |
66 | this.plugins = this.plugins.concat(res.data) | 68 | this.plugins = this.plugins.concat(res.data) |
@@ -106,7 +108,7 @@ export class PluginListInstalledComponent implements OnInit { | |||
106 | ) | 108 | ) |
107 | if (res === false) return | 109 | if (res === false) return |
108 | 110 | ||
109 | this.pluginService.uninstall(plugin.name, plugin.type) | 111 | this.pluginApiService.uninstall(plugin.name, plugin.type) |
110 | .subscribe( | 112 | .subscribe( |
111 | () => { | 113 | () => { |
112 | this.notifier.success(this.i18n('{{pluginName}} uninstalled.', { pluginName: plugin.name })) | 114 | this.notifier.success(this.i18n('{{pluginName}} uninstalled.', { pluginName: plugin.name })) |
@@ -125,7 +127,7 @@ export class PluginListInstalledComponent implements OnInit { | |||
125 | 127 | ||
126 | this.updating[updatingKey] = true | 128 | this.updating[updatingKey] = true |
127 | 129 | ||
128 | this.pluginService.update(plugin.name, plugin.type) | 130 | this.pluginApiService.update(plugin.name, plugin.type) |
129 | .pipe() | 131 | .pipe() |
130 | .subscribe( | 132 | .subscribe( |
131 | res => { | 133 | res => { |
diff --git a/client/src/app/+admin/plugins/shared/plugin-api.service.ts b/client/src/app/+admin/plugins/shared/plugin-api.service.ts index bfcaec011..343eb57b2 100644 --- a/client/src/app/+admin/plugins/shared/plugin-api.service.ts +++ b/client/src/app/+admin/plugins/shared/plugin-api.service.ts | |||
@@ -12,16 +12,18 @@ import { ManagePlugin } from '@shared/models/plugins/manage-plugin.model' | |||
12 | import { InstallOrUpdatePlugin } from '@shared/models/plugins/install-plugin.model' | 12 | import { InstallOrUpdatePlugin } from '@shared/models/plugins/install-plugin.model' |
13 | import { PeerTubePluginIndex } from '@shared/models/plugins/peertube-plugin-index.model' | 13 | import { PeerTubePluginIndex } from '@shared/models/plugins/peertube-plugin-index.model' |
14 | import { RegisterServerSettingOptions } from '@shared/models/plugins/register-server-setting.model' | 14 | import { RegisterServerSettingOptions } from '@shared/models/plugins/register-server-setting.model' |
15 | import { PluginService } from '@app/core/plugins/plugin.service' | ||
15 | 16 | ||
16 | @Injectable() | 17 | @Injectable() |
17 | export class PluginApiService { | 18 | export class PluginApiService { |
18 | private static BASE_APPLICATION_URL = environment.apiUrl + '/api/v1/plugins' | 19 | private static BASE_PLUGIN_URL = environment.apiUrl + '/api/v1/plugins' |
19 | 20 | ||
20 | constructor ( | 21 | constructor ( |
21 | private authHttp: HttpClient, | 22 | private authHttp: HttpClient, |
22 | private restExtractor: RestExtractor, | 23 | private restExtractor: RestExtractor, |
23 | private restService: RestService, | 24 | private restService: RestService, |
24 | private i18n: I18n | 25 | private i18n: I18n, |
26 | private pluginService: PluginService | ||
25 | ) { } | 27 | ) { } |
26 | 28 | ||
27 | getPluginTypeOptions () { | 29 | getPluginTypeOptions () { |
@@ -56,7 +58,7 @@ export class PluginApiService { | |||
56 | params = this.restService.addRestGetParams(params, pagination, sort) | 58 | params = this.restService.addRestGetParams(params, pagination, sort) |
57 | params = params.append('pluginType', pluginType.toString()) | 59 | params = params.append('pluginType', pluginType.toString()) |
58 | 60 | ||
59 | return this.authHttp.get<ResultList<PeerTubePlugin>>(PluginApiService.BASE_APPLICATION_URL, { params }) | 61 | return this.authHttp.get<ResultList<PeerTubePlugin>>(PluginApiService.BASE_PLUGIN_URL, { params }) |
60 | .pipe(catchError(res => this.restExtractor.handleError(res))) | 62 | .pipe(catchError(res => this.restExtractor.handleError(res))) |
61 | } | 63 | } |
62 | 64 | ||
@@ -74,26 +76,28 @@ export class PluginApiService { | |||
74 | 76 | ||
75 | if (search) params = params.append('search', search) | 77 | if (search) params = params.append('search', search) |
76 | 78 | ||
77 | return this.authHttp.get<ResultList<PeerTubePluginIndex>>(PluginApiService.BASE_APPLICATION_URL + '/available', { params }) | 79 | return this.authHttp.get<ResultList<PeerTubePluginIndex>>(PluginApiService.BASE_PLUGIN_URL + '/available', { params }) |
78 | .pipe(catchError(res => this.restExtractor.handleError(res))) | 80 | .pipe(catchError(res => this.restExtractor.handleError(res))) |
79 | } | 81 | } |
80 | 82 | ||
81 | getPlugin (npmName: string) { | 83 | getPlugin (npmName: string) { |
82 | const path = PluginApiService.BASE_APPLICATION_URL + '/' + npmName | 84 | const path = PluginApiService.BASE_PLUGIN_URL + '/' + npmName |
83 | 85 | ||
84 | return this.authHttp.get<PeerTubePlugin>(path) | 86 | return this.authHttp.get<PeerTubePlugin>(path) |
85 | .pipe(catchError(res => this.restExtractor.handleError(res))) | 87 | .pipe(catchError(res => this.restExtractor.handleError(res))) |
86 | } | 88 | } |
87 | 89 | ||
88 | getPluginRegisteredSettings (pluginName: string, pluginType: PluginType) { | 90 | getPluginRegisteredSettings (pluginName: string, pluginType: PluginType) { |
89 | const path = PluginApiService.BASE_APPLICATION_URL + '/' + this.nameToNpmName(pluginName, pluginType) + '/registered-settings' | 91 | const npmName = this.pluginService.nameToNpmName(pluginName, pluginType) |
92 | const path = PluginApiService.BASE_PLUGIN_URL + '/' + npmName + '/registered-settings' | ||
90 | 93 | ||
91 | return this.authHttp.get<{ settings: RegisterServerSettingOptions[] }>(path) | 94 | return this.authHttp.get<{ settings: RegisterServerSettingOptions[] }>(path) |
92 | .pipe(catchError(res => this.restExtractor.handleError(res))) | 95 | .pipe(catchError(res => this.restExtractor.handleError(res))) |
93 | } | 96 | } |
94 | 97 | ||
95 | updatePluginSettings (pluginName: string, pluginType: PluginType, settings: any) { | 98 | updatePluginSettings (pluginName: string, pluginType: PluginType, settings: any) { |
96 | const path = PluginApiService.BASE_APPLICATION_URL + '/' + this.nameToNpmName(pluginName, pluginType) + '/settings' | 99 | const npmName = this.pluginService.nameToNpmName(pluginName, pluginType) |
100 | const path = PluginApiService.BASE_PLUGIN_URL + '/' + npmName + '/settings' | ||
97 | 101 | ||
98 | return this.authHttp.put(path, { settings }) | 102 | return this.authHttp.put(path, { settings }) |
99 | .pipe(catchError(res => this.restExtractor.handleError(res))) | 103 | .pipe(catchError(res => this.restExtractor.handleError(res))) |
@@ -101,19 +105,19 @@ export class PluginApiService { | |||
101 | 105 | ||
102 | uninstall (pluginName: string, pluginType: PluginType) { | 106 | uninstall (pluginName: string, pluginType: PluginType) { |
103 | const body: ManagePlugin = { | 107 | const body: ManagePlugin = { |
104 | npmName: this.nameToNpmName(pluginName, pluginType) | 108 | npmName: this.pluginService.nameToNpmName(pluginName, pluginType) |
105 | } | 109 | } |
106 | 110 | ||
107 | return this.authHttp.post(PluginApiService.BASE_APPLICATION_URL + '/uninstall', body) | 111 | return this.authHttp.post(PluginApiService.BASE_PLUGIN_URL + '/uninstall', body) |
108 | .pipe(catchError(res => this.restExtractor.handleError(res))) | 112 | .pipe(catchError(res => this.restExtractor.handleError(res))) |
109 | } | 113 | } |
110 | 114 | ||
111 | update (pluginName: string, pluginType: PluginType) { | 115 | update (pluginName: string, pluginType: PluginType) { |
112 | const body: ManagePlugin = { | 116 | const body: ManagePlugin = { |
113 | npmName: this.nameToNpmName(pluginName, pluginType) | 117 | npmName: this.pluginService.nameToNpmName(pluginName, pluginType) |
114 | } | 118 | } |
115 | 119 | ||
116 | return this.authHttp.post(PluginApiService.BASE_APPLICATION_URL + '/update', body) | 120 | return this.authHttp.post(PluginApiService.BASE_PLUGIN_URL + '/update', body) |
117 | .pipe(catchError(res => this.restExtractor.handleError(res))) | 121 | .pipe(catchError(res => this.restExtractor.handleError(res))) |
118 | } | 122 | } |
119 | 123 | ||
@@ -122,21 +126,7 @@ export class PluginApiService { | |||
122 | npmName | 126 | npmName |
123 | } | 127 | } |
124 | 128 | ||
125 | return this.authHttp.post(PluginApiService.BASE_APPLICATION_URL + '/install', body) | 129 | return this.authHttp.post(PluginApiService.BASE_PLUGIN_URL + '/install', body) |
126 | .pipe(catchError(res => this.restExtractor.handleError(res))) | 130 | .pipe(catchError(res => this.restExtractor.handleError(res))) |
127 | } | 131 | } |
128 | |||
129 | nameToNpmName (name: string, type: PluginType) { | ||
130 | const prefix = type === PluginType.PLUGIN | ||
131 | ? 'peertube-plugin-' | ||
132 | : 'peertube-theme-' | ||
133 | |||
134 | return prefix + name | ||
135 | } | ||
136 | |||
137 | pluginTypeFromNpmName (npmName: string) { | ||
138 | return npmName.startsWith('peertube-plugin-') | ||
139 | ? PluginType.PLUGIN | ||
140 | : PluginType.THEME | ||
141 | } | ||
142 | } | 132 | } |
diff --git a/client/src/app/app.component.ts b/client/src/app/app.component.ts index 14fd27784..db1f91f8c 100644 --- a/client/src/app/app.component.ts +++ b/client/src/app/app.component.ts | |||
@@ -162,6 +162,10 @@ export class AppComponent implements OnInit { | |||
162 | filter(pathname => !pathname || pathname === '/' || is18nPath(pathname)) | 162 | filter(pathname => !pathname || pathname === '/' || is18nPath(pathname)) |
163 | ).subscribe(() => this.redirectService.redirectToHomepage(true)) | 163 | ).subscribe(() => this.redirectService.redirectToHomepage(true)) |
164 | 164 | ||
165 | navigationEndEvent.subscribe(e => { | ||
166 | this.hooks.runAction('action:router.navigation-end', 'common', { path: e.url }) | ||
167 | }) | ||
168 | |||
165 | eventsObs.pipe( | 169 | eventsObs.pipe( |
166 | filter((e: Event): e is GuardsCheckStart => e instanceof GuardsCheckStart), | 170 | filter((e: Event): e is GuardsCheckStart => e instanceof GuardsCheckStart), |
167 | filter(() => this.screenService.isInSmallView()) | 171 | filter(() => this.screenService.isInSmallView()) |
diff --git a/client/src/app/core/plugins/hooks.service.ts b/client/src/app/core/plugins/hooks.service.ts index 257e27e6b..93dac1167 100644 --- a/client/src/app/core/plugins/hooks.service.ts +++ b/client/src/app/core/plugins/hooks.service.ts | |||
@@ -39,7 +39,7 @@ export class HooksService { | |||
39 | 39 | ||
40 | runAction<T, U extends ClientActionHookName> (hookName: U, scope: PluginClientScope, params?: T) { | 40 | runAction<T, U extends ClientActionHookName> (hookName: U, scope: PluginClientScope, params?: T) { |
41 | this.pluginService.ensurePluginsAreLoaded(scope) | 41 | this.pluginService.ensurePluginsAreLoaded(scope) |
42 | .then(() => this.pluginService.runHook(hookName, params)) | 42 | .then(() => this.pluginService.runHook(hookName, undefined, params)) |
43 | .catch((err: any) => console.error('Fatal hook error.', { err })) | 43 | .catch((err: any) => console.error('Fatal hook error.', { err })) |
44 | } | 44 | } |
45 | } | 45 | } |
diff --git a/client/src/app/core/plugins/plugin.service.ts b/client/src/app/core/plugins/plugin.service.ts index 1294edd7d..45d8088a4 100644 --- a/client/src/app/core/plugins/plugin.service.ts +++ b/client/src/app/core/plugins/plugin.service.ts | |||
@@ -6,11 +6,15 @@ import { ClientScript } from '@shared/models/plugins/plugin-package-json.model' | |||
6 | import { ClientScript as ClientScriptModule } from '../../../types/client-script.model' | 6 | import { ClientScript as ClientScriptModule } from '../../../types/client-script.model' |
7 | import { environment } from '../../../environments/environment' | 7 | import { environment } from '../../../environments/environment' |
8 | import { ReplaySubject } from 'rxjs' | 8 | import { ReplaySubject } from 'rxjs' |
9 | import { first, shareReplay } from 'rxjs/operators' | 9 | import { catchError, first, map, shareReplay } from 'rxjs/operators' |
10 | import { getHookType, internalRunHook } from '@shared/core-utils/plugins/hooks' | 10 | import { getHookType, internalRunHook } from '@shared/core-utils/plugins/hooks' |
11 | import { ClientHook, ClientHookName, clientHookObject } from '@shared/models/plugins/client-hook.model' | 11 | import { ClientHook, ClientHookName, clientHookObject } from '@shared/models/plugins/client-hook.model' |
12 | import { PluginClientScope } from '@shared/models/plugins/plugin-client-scope.type' | 12 | import { PluginClientScope } from '@shared/models/plugins/plugin-client-scope.type' |
13 | import { RegisterClientHookOptions } from '@shared/models/plugins/register-client-hook.model' | 13 | import { RegisterClientHookOptions } from '@shared/models/plugins/register-client-hook.model' |
14 | import { PeerTubePlugin } from '@shared/models/plugins/peertube-plugin.model' | ||
15 | import { HttpClient } from '@angular/common/http' | ||
16 | import { RestExtractor } from '@app/shared/rest' | ||
17 | import { PluginType } from '@shared/models/plugins/plugin.type' | ||
14 | 18 | ||
15 | interface HookStructValue extends RegisterClientHookOptions { | 19 | interface HookStructValue extends RegisterClientHookOptions { |
16 | plugin: ServerConfigPlugin | 20 | plugin: ServerConfigPlugin |
@@ -20,11 +24,14 @@ interface HookStructValue extends RegisterClientHookOptions { | |||
20 | type PluginInfo = { | 24 | type PluginInfo = { |
21 | plugin: ServerConfigPlugin | 25 | plugin: ServerConfigPlugin |
22 | clientScript: ClientScript | 26 | clientScript: ClientScript |
27 | pluginType: PluginType | ||
23 | isTheme: boolean | 28 | isTheme: boolean |
24 | } | 29 | } |
25 | 30 | ||
26 | @Injectable() | 31 | @Injectable() |
27 | export class PluginService implements ClientHook { | 32 | export class PluginService implements ClientHook { |
33 | private static BASE_PLUGIN_URL = environment.apiUrl + '/api/v1/plugins' | ||
34 | |||
28 | pluginsBuilt = new ReplaySubject<boolean>(1) | 35 | pluginsBuilt = new ReplaySubject<boolean>(1) |
29 | 36 | ||
30 | pluginsLoaded: { [ scope in PluginClientScope ]: ReplaySubject<boolean> } = { | 37 | pluginsLoaded: { [ scope in PluginClientScope ]: ReplaySubject<boolean> } = { |
@@ -43,7 +50,9 @@ export class PluginService implements ClientHook { | |||
43 | 50 | ||
44 | constructor ( | 51 | constructor ( |
45 | private router: Router, | 52 | private router: Router, |
46 | private server: ServerService | 53 | private server: ServerService, |
54 | private authHttp: HttpClient, | ||
55 | private restExtractor: RestExtractor | ||
47 | ) { | 56 | ) { |
48 | } | 57 | } |
49 | 58 | ||
@@ -87,6 +96,7 @@ export class PluginService implements ClientHook { | |||
87 | script: environment.apiUrl + `${pathPrefix}/${plugin.name}/${plugin.version}/client-scripts/${clientScript.script}`, | 96 | script: environment.apiUrl + `${pathPrefix}/${plugin.name}/${plugin.version}/client-scripts/${clientScript.script}`, |
88 | scopes: clientScript.scopes | 97 | scopes: clientScript.scopes |
89 | }, | 98 | }, |
99 | pluginType: isTheme ? PluginType.THEME : PluginType.PLUGIN, | ||
90 | isTheme | 100 | isTheme |
91 | }) | 101 | }) |
92 | 102 | ||
@@ -162,6 +172,20 @@ export class PluginService implements ClientHook { | |||
162 | return result | 172 | return result |
163 | } | 173 | } |
164 | 174 | ||
175 | nameToNpmName (name: string, type: PluginType) { | ||
176 | const prefix = type === PluginType.PLUGIN | ||
177 | ? 'peertube-plugin-' | ||
178 | : 'peertube-theme-' | ||
179 | |||
180 | return prefix + name | ||
181 | } | ||
182 | |||
183 | pluginTypeFromNpmName (npmName: string) { | ||
184 | return npmName.startsWith('peertube-plugin-') | ||
185 | ? PluginType.PLUGIN | ||
186 | : PluginType.THEME | ||
187 | } | ||
188 | |||
165 | private loadPlugin (pluginInfo: PluginInfo) { | 189 | private loadPlugin (pluginInfo: PluginInfo) { |
166 | const { plugin, clientScript } = pluginInfo | 190 | const { plugin, clientScript } = pluginInfo |
167 | 191 | ||
@@ -189,6 +213,7 @@ export class PluginService implements ClientHook { | |||
189 | return import(/* webpackIgnore: true */ clientScript.script) | 213 | return import(/* webpackIgnore: true */ clientScript.script) |
190 | .then((script: ClientScriptModule) => script.register({ registerHook, peertubeHelpers })) | 214 | .then((script: ClientScriptModule) => script.register({ registerHook, peertubeHelpers })) |
191 | .then(() => this.sortHooksByPriority()) | 215 | .then(() => this.sortHooksByPriority()) |
216 | .catch(err => console.error('Cannot import or register plugin %s.', pluginInfo.plugin.name, err)) | ||
192 | } | 217 | } |
193 | 218 | ||
194 | private buildScopeStruct () { | 219 | private buildScopeStruct () { |
@@ -212,6 +237,18 @@ export class PluginService implements ClientHook { | |||
212 | getBaseStaticRoute: () => { | 237 | getBaseStaticRoute: () => { |
213 | const pathPrefix = this.getPluginPathPrefix(pluginInfo.isTheme) | 238 | const pathPrefix = this.getPluginPathPrefix(pluginInfo.isTheme) |
214 | return environment.apiUrl + `${pathPrefix}/${plugin.name}/${plugin.version}/static` | 239 | return environment.apiUrl + `${pathPrefix}/${plugin.name}/${plugin.version}/static` |
240 | }, | ||
241 | |||
242 | getSettings: () => { | ||
243 | const npmName = this.nameToNpmName(pluginInfo.plugin.name, pluginInfo.pluginType) | ||
244 | const path = PluginService.BASE_PLUGIN_URL + '/' + npmName | ||
245 | |||
246 | return this.authHttp.get<PeerTubePlugin>(path) | ||
247 | .pipe( | ||
248 | map(p => p.settings), | ||
249 | catchError(res => this.restExtractor.handleError(res)) | ||
250 | ) | ||
251 | .toPromise() | ||
215 | } | 252 | } |
216 | } | 253 | } |
217 | } | 254 | } |
diff --git a/client/src/types/register-client-option.model.ts b/client/src/types/register-client-option.model.ts index 42d689403..473c2500f 100644 --- a/client/src/types/register-client-option.model.ts +++ b/client/src/types/register-client-option.model.ts | |||
@@ -5,5 +5,7 @@ export type RegisterClientOptions = { | |||
5 | 5 | ||
6 | peertubeHelpers: { | 6 | peertubeHelpers: { |
7 | getBaseStaticRoute: () => string | 7 | getBaseStaticRoute: () => string |
8 | |||
9 | getSettings: () => Promise<{ [ name: string ]: string }> | ||
8 | } | 10 | } |
9 | } | 11 | } |