Skip to content

Commit cae706a

Browse files
authored
feat: advanced admin registry extenders (#4209)
* feat: advanced admin registry extenders * fix: admin extender execution order
1 parent 2be1932 commit cae706a

File tree

5 files changed

+251
-34
lines changed

5 files changed

+251
-34
lines changed

framework/core/js/src/admin/AdminApplication.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,12 +129,14 @@ export default class AdminApplication extends Application {
129129
this.route = (Object.getPrototypeOf(Object.getPrototypeOf(this)) as Application).route.bind(this);
130130
}
131131

132-
protected beforeMount(): void {
132+
protected runBeforeMount(): void {
133133
BasicsPage.register();
134134
AppearancePage.register();
135135
MailPage.register();
136136
AdvancedPage.register();
137137
PermissionsPage.register();
138+
139+
super.runBeforeMount();
138140
}
139141

140142
/**

framework/core/js/src/admin/components/GeneralSearchSource.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import Icon from '../../common/components/Icon';
99
import PermissionGrid from './PermissionGrid';
1010
import escapeRegExp from '../../common/utils/escapeRegExp';
1111
import { GeneralIndexData, GeneralIndexItem } from '../states/GeneralSearchIndex';
12-
import { ExtensionConfig, SettingConfigInternal } from '../utils/AdminRegistry';
12+
import { ExtensionConfig, SettingConfigInput } from '../utils/AdminRegistry';
1313
import ItemList from '../../common/utils/ItemList';
1414

1515
export class GeneralSearchResult {
@@ -94,7 +94,7 @@ export default class GeneralSearchSource implements GlobalSearchSource {
9494
for (const extensionId in data) {
9595
// settings
9696
const settings = data[extensionId]!.settings;
97-
let normalizedSettings: GeneralIndexItem[] | SettingConfigInternal[] = [];
97+
let normalizedSettings: GeneralIndexItem[] | SettingConfigInput[] = [];
9898

9999
if (settings instanceof ItemList) {
100100
normalizedSettings = settings?.toArray();
@@ -113,7 +113,7 @@ export default class GeneralSearchSource implements GlobalSearchSource {
113113
const group = app.generalIndex.getGroup(extensionId);
114114

115115
if (this.itemHasQuery(label, query) || this.itemHasQuery(help, query)) {
116-
const id = extensionId + '-' + ('setting' in setting ? setting : setting.id);
116+
const id = extensionId + '-' + ('setting' in setting ? setting : 'id' in setting ? setting.id : '');
117117

118118
results.push(
119119
new GeneralSearchResult(

framework/core/js/src/admin/utils/AdminRegistry.ts

Lines changed: 117 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ export default class AdminRegistry {
7171
* label: app.translator.trans('flarum-flags.admin.settings.guidelines_url_label')
7272
* }, 15) // priority is optional (ItemList)
7373
*/
74-
registerSetting(content: SettingConfigInput, priority = 0): this {
74+
registerSetting(content: SettingConfigInput, priority = 0, key: string | null = null): this {
7575
if (this.state.currentExtension === null) {
7676
throw new Error(noActiveExtensionErrorMessage);
7777
}
@@ -83,7 +83,7 @@ export default class AdminRegistry {
8383
// To support multiple such items for one extension, we assign a random ID.
8484
// 36 is arbitrary length, but makes collisions very unlikely.
8585
if (tmpContent instanceof Function) {
86-
tmpContent.setting = Math.random().toString(36);
86+
tmpContent.setting = key || Math.random().toString(36);
8787
}
8888

8989
const settings = this.state.data[this.state.currentExtension].settings || new ItemList();
@@ -94,6 +94,62 @@ export default class AdminRegistry {
9494
return this;
9595
}
9696

97+
/**
98+
* This function allows you to change the configuration of a setting.
99+
*/
100+
setSetting(key: string, content: SettingConfigInput | ((original: SettingConfigInput) => SettingConfigInput)): this {
101+
if (this.state.currentExtension === null) {
102+
throw new Error(noActiveExtensionErrorMessage);
103+
}
104+
105+
const settings = this.state.data[this.state.currentExtension].settings || new ItemList();
106+
107+
if (settings.has(key)) {
108+
if (content instanceof Function) {
109+
const original = settings.get(key);
110+
content = content(original) as SettingConfigInternal;
111+
}
112+
113+
settings.setContent(key, content as SettingConfigInternal);
114+
}
115+
116+
return this;
117+
}
118+
119+
/**
120+
* This function allows you to change the priority of a setting.
121+
*/
122+
setSettingPriority(key: string, priority: number): this {
123+
if (this.state.currentExtension === null) {
124+
throw new Error(noActiveExtensionErrorMessage);
125+
}
126+
127+
const settings = this.state.data[this.state.currentExtension].settings || new ItemList();
128+
129+
if (settings.has(key)) {
130+
settings.setPriority(key, priority);
131+
}
132+
133+
return this;
134+
}
135+
136+
/**
137+
* This function allows you to remove a setting.
138+
*/
139+
removeSetting(key: string): this {
140+
if (this.state.currentExtension === null) {
141+
throw new Error(noActiveExtensionErrorMessage);
142+
}
143+
144+
const settings = this.state.data[this.state.currentExtension].settings || new ItemList();
145+
146+
if (settings.has(key)) {
147+
settings.remove(key);
148+
}
149+
150+
return this;
151+
}
152+
97153
/**
98154
* This function registers your permission with Flarum
99155
*
@@ -125,6 +181,65 @@ export default class AdminRegistry {
125181
return this;
126182
}
127183

184+
/**
185+
* This function allows you to change the configuration of a permission.
186+
*/
187+
setPermission(key: string, content: PermissionConfig | ((original: PermissionConfig) => PermissionConfig), permissionType: PermissionType): this {
188+
if (this.state.currentExtension === null) {
189+
throw new Error(noActiveExtensionErrorMessage);
190+
}
191+
192+
const permissions = this.state.data[this.state.currentExtension].permissions || {};
193+
const permissionsForType = permissions[permissionType] || new ItemList();
194+
195+
if (permissionsForType.has(key)) {
196+
if (content instanceof Function) {
197+
const original = permissionsForType.get(key);
198+
content = content(original) as PermissionConfig;
199+
}
200+
201+
permissionsForType.setContent(key, content);
202+
}
203+
204+
return this;
205+
}
206+
207+
/**
208+
* This function allows you to change the priority of a permission.
209+
*/
210+
setPermissionPriority(key: string, permissionType: PermissionType, priority: number): this {
211+
if (this.state.currentExtension === null) {
212+
throw new Error(noActiveExtensionErrorMessage);
213+
}
214+
215+
const permissions = this.state.data[this.state.currentExtension].permissions;
216+
const permissionsForType = permissions?.[permissionType] || new ItemList();
217+
218+
if (permissionsForType.has(key)) {
219+
permissionsForType.setPriority(key, priority);
220+
}
221+
222+
return this;
223+
}
224+
225+
/**
226+
* This function allows you to remove a permission.
227+
*/
228+
removePermission(key: string, permissionType: PermissionType): this {
229+
if (this.state.currentExtension === null) {
230+
throw new Error(noActiveExtensionErrorMessage);
231+
}
232+
233+
const permissions = this.state.data[this.state.currentExtension].permissions;
234+
const permissionsForType = permissions?.[permissionType] || new ItemList();
235+
236+
if (permissionsForType.has(key)) {
237+
permissionsForType.remove(key);
238+
}
239+
240+
return this;
241+
}
242+
128243
/**
129244
* Replace the default extension page with a custom component.
130245
* This component would typically extend ExtensionPage

framework/core/js/src/common/Application.tsx

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,8 @@ export default class Application {
286286

287287
private handledErrors: { extension: null | string; errorId: string; error: any }[] = [];
288288

289+
private beforeMounts: (() => void)[] = [];
290+
289291
public load(payload: Application['data']) {
290292
this.data = payload;
291293
this.translator.setLocale(payload.locale);
@@ -326,7 +328,7 @@ export default class Application {
326328

327329
this.session = new Session(this.store.getById<User>('users', String(this.data.session.userId)) ?? null, this.data.session.csrfToken);
328330

329-
this.beforeMount();
331+
this.runBeforeMount();
330332

331333
this.mount();
332334

@@ -335,8 +337,13 @@ export default class Application {
335337
caughtInitializationErrors.forEach((handler) => handler());
336338
}
337339

338-
protected beforeMount(): void {
339-
// ...
340+
public beforeMount(callback: () => void) {
341+
this.beforeMounts.push(callback);
342+
}
343+
344+
protected runBeforeMount(): void {
345+
this.beforeMounts.forEach((callback) => callback());
346+
this.beforeMounts = [];
340347
}
341348

342349
public bootExtensions(extensions: Record<string, { extend?: IExtender[] }>) {

0 commit comments

Comments
 (0)