/
ExtensionData.ts
186 lines (153 loc) · 5.87 KB
/
ExtensionData.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
import type Mithril from 'mithril';
import ItemList from '../../common/utils/ItemList';
import { SettingsComponentOptions } from '../components/AdminPage';
import ExtensionPage, { ExtensionPageAttrs } from '../components/ExtensionPage';
import { PermissionConfig, PermissionType } from '../components/PermissionGrid';
type SettingConfigInput = SettingsComponentOptions | (() => Mithril.Children);
type SettingConfigInternal = SettingsComponentOptions | ((() => Mithril.Children) & { setting: string });
export type CustomExtensionPage<Attrs extends ExtensionPageAttrs = ExtensionPageAttrs> = new () => ExtensionPage<Attrs>;
type ExtensionConfig = {
settings?: ItemList<SettingConfigInternal>;
permissions?: {
view?: ItemList<PermissionConfig>;
start?: ItemList<PermissionConfig>;
reply?: ItemList<PermissionConfig>;
moderate?: ItemList<PermissionConfig>;
};
page?: CustomExtensionPage;
};
type InnerDataNoActiveExtension = {
currentExtension: null;
data: {
[key: string]: ExtensionConfig | undefined;
};
};
type InnerDataActiveExtension = {
currentExtension: string;
data: {
[key: string]: ExtensionConfig;
};
};
const noActiveExtensionErrorMessage = 'You must select an active extension via `.for()` before using extensionData.';
export default class ExtensionData {
protected state: InnerDataActiveExtension | InnerDataNoActiveExtension = {
currentExtension: null,
data: {},
};
/**
* This function simply takes the extension id
*
* @example
* app.extensionData.for('flarum-tags')
*
* flarum/flags -> flarum-flags | acme/extension -> acme-extension
*/
for(extension: string) {
this.state.currentExtension = extension;
this.state.data[extension] = this.state.data[extension] || {};
return this;
}
/**
* This function registers your settings with Flarum
*
* It takes either a settings object or a callback.
*
* @example
*
* .registerSetting({
* setting: 'flarum-flags.guidelines_url',
* type: 'text', // This will be inputted into the input tag for the setting (text/number/etc)
* label: app.translator.trans('flarum-flags.admin.settings.guidelines_url_label')
* }, 15) // priority is optional (ItemList)
*/
registerSetting(content: SettingConfigInput, priority = 0): this {
if (this.state.currentExtension === null) {
throw new Error(noActiveExtensionErrorMessage);
}
const tmpContent = content as SettingConfigInternal;
// Callbacks can be passed in instead of settings to display custom content.
// By default, they will be added with the `null` key, since they don't have a `.setting` attr.
// To support multiple such items for one extension, we assign a random ID.
// 36 is arbitrary length, but makes collisions very unlikely.
if (tmpContent instanceof Function) {
tmpContent.setting = Math.random().toString(36);
}
const settings = this.state.data[this.state.currentExtension].settings || new ItemList();
settings.add(tmpContent.setting, tmpContent, priority);
this.state.data[this.state.currentExtension].settings = settings;
return this;
}
/**
* This function registers your permission with Flarum
*
* @example
*
* .registerPermission('permissions', {
* icon: 'fas fa-flag',
* label: app.translator.trans('flarum-flags.admin.permissions.view_flags_label'),
* permission: 'discussion.viewFlags'
* }, 'moderate', 65)
*/
registerPermission(content: PermissionConfig, permissionType: PermissionType, priority = 0): this {
if (this.state.currentExtension === null) {
throw new Error(noActiveExtensionErrorMessage);
}
const permissions = this.state.data[this.state.currentExtension].permissions || {};
const permissionsForType = permissions[permissionType] || new ItemList();
permissionsForType.add(content.permission, content, priority);
this.state.data[this.state.currentExtension].permissions = { ...permissions, [permissionType]: permissionsForType };
return this;
}
/**
* Replace the default extension page with a custom component.
* This component would typically extend ExtensionPage
*/
registerPage(component: CustomExtensionPage): this {
if (this.state.currentExtension === null) {
throw new Error(noActiveExtensionErrorMessage);
}
this.state.data[this.state.currentExtension].page = component;
return this;
}
/**
* Get an extension's registered settings
*/
getSettings(extensionId: string): SettingConfigInternal[] | undefined {
return this.state.data[extensionId]?.settings?.toArray();
}
/**
* Get an ItemList of all extensions' registered permissions
*/
getAllExtensionPermissions(type: PermissionType): ItemList<PermissionConfig> {
const items = new ItemList<PermissionConfig>();
Object.keys(this.state.data).map((extension) => {
const extPerms = this.state.data[extension]?.permissions?.[type];
if (this.extensionHasPermissions(extension) && extPerms !== undefined) {
items.merge(extPerms);
}
});
return items;
}
/**
* Get a singular extension's registered permissions
*/
getExtensionPermissions(extension: string, type: PermissionType): ItemList<PermissionConfig> {
const extPerms = this.state.data[extension]?.permissions?.[type];
if (this.extensionHasPermissions(extension) && extPerms != null) {
return extPerms;
}
return new ItemList();
}
/**
* Checks whether a given extension has registered permissions.
*/
extensionHasPermissions(extension: string) {
return this.state.data[extension]?.permissions !== undefined;
}
/**
* Returns an extension's custom page component if it exists.
*/
getPage<Attrs extends ExtensionPageAttrs = ExtensionPageAttrs>(extension: string): CustomExtensionPage<Attrs> | undefined {
return this.state.data[extension]?.page as CustomExtensionPage<Attrs> | undefined;
}
}