mirror of
https://github.com/flarum/core.git
synced 2025-08-03 06:57:54 +02:00
Added permissions page
This commit is contained in:
committed by
David Sevilla Martín
parent
77eefc85c6
commit
9a3aec6079
@@ -8,6 +8,9 @@ import AdminNav from './components/AdminNav';
|
||||
export type AdminData = ApplicationData & {
|
||||
mysqlVersion: string;
|
||||
phpVersion: string;
|
||||
permissions: {
|
||||
[key: string]: string[];
|
||||
};
|
||||
settings: {
|
||||
[key: string]: string;
|
||||
};
|
||||
|
@@ -50,12 +50,15 @@ export default class AdminNav<T> extends Component<T> {
|
||||
})
|
||||
);
|
||||
|
||||
// items.add('permissions', AdminLinkButton.component({
|
||||
// href: app.route('permissions'),
|
||||
// icon: 'fas fa-key',
|
||||
// children: app.translator.trans('core.admin.nav.permissions_button'),
|
||||
// description: app.translator.trans('core.admin.nav.permissions_text')
|
||||
// }));
|
||||
items.add(
|
||||
'permissions',
|
||||
AdminLinkButton.component({
|
||||
href: app.route('permissions'),
|
||||
icon: 'fas fa-key',
|
||||
children: app.translator.trans('core.admin.nav.permissions_button'),
|
||||
description: app.translator.trans('core.admin.nav.permissions_text'),
|
||||
})
|
||||
);
|
||||
|
||||
// items.add('appearance', AdminLinkButton.component({
|
||||
// href: app.route('appearance'),
|
||||
|
157
js/src/admin/components/EditGroupModal.tsx
Normal file
157
js/src/admin/components/EditGroupModal.tsx
Normal file
@@ -0,0 +1,157 @@
|
||||
import app from '../app';
|
||||
import { ComponentProps } from '../../common/Component';
|
||||
import Modal from '../../common/components/Modal';
|
||||
import Button from '../../common/components/Button';
|
||||
import Badge from '../../common/components/Badge';
|
||||
import Group from '../../common/models/Group';
|
||||
import ItemList from '../../common/utils/ItemList';
|
||||
|
||||
import Stream from 'mithril/stream';
|
||||
|
||||
/**
|
||||
* The `EditGroupModal` component shows a modal dialog which allows the user
|
||||
* to create or edit a group.
|
||||
*/
|
||||
export default class EditGroupModal extends Modal<ComponentProps> {
|
||||
group: Group;
|
||||
|
||||
nameSingular: Stream<string>;
|
||||
namePlural: Stream<string>;
|
||||
icon: Stream<string>;
|
||||
color: Stream<string>;
|
||||
|
||||
oninit(vnode) {
|
||||
super.oninit(vnode);
|
||||
|
||||
this.group = this.props.group || app.store.createRecord('groups');
|
||||
|
||||
this.nameSingular = m.prop(this.group.nameSingular() || '');
|
||||
this.namePlural = m.prop(this.group.namePlural() || '');
|
||||
this.icon = m.prop(this.group.icon() || '');
|
||||
this.color = m.prop(this.group.color() || '');
|
||||
}
|
||||
|
||||
className() {
|
||||
return 'EditGroupModal Modal--small';
|
||||
}
|
||||
|
||||
title() {
|
||||
return [
|
||||
this.color() || this.icon()
|
||||
? Badge.component({
|
||||
icon: this.icon(),
|
||||
style: { backgroundColor: this.color() },
|
||||
})
|
||||
: '',
|
||||
' ',
|
||||
this.namePlural() || app.translator.trans('core.admin.edit_group.title'),
|
||||
];
|
||||
}
|
||||
|
||||
content() {
|
||||
return (
|
||||
<div className="Modal-body">
|
||||
<div className="Form">{this.fields().toArray()}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
fields() {
|
||||
const items = new ItemList();
|
||||
|
||||
items.add(
|
||||
'name',
|
||||
<div className="Form-group">
|
||||
<label>{app.translator.trans('core.admin.edit_group.name_label')}</label>
|
||||
<div className="EditGroupModal-name-input">
|
||||
<input
|
||||
className="FormControl"
|
||||
placeholder={app.translator.transText('core.admin.edit_group.singular_placeholder')}
|
||||
value={this.nameSingular()}
|
||||
oninput={m.withAttr('value', this.nameSingular)}
|
||||
/>
|
||||
<input
|
||||
className="FormControl"
|
||||
placeholder={app.translator.transText('core.admin.edit_group.plural_placeholder')}
|
||||
value={this.namePlural()}
|
||||
oninput={m.withAttr('value', this.namePlural)}
|
||||
/>
|
||||
</div>
|
||||
</div>,
|
||||
30
|
||||
);
|
||||
|
||||
items.add(
|
||||
'color',
|
||||
<div className="Form-group">
|
||||
<label>{app.translator.trans('core.admin.edit_group.color_label')}</label>
|
||||
<input className="FormControl" placeholder="#aaaaaa" value={this.color()} oninput={m.withAttr('value', this.color)} />
|
||||
</div>,
|
||||
20
|
||||
);
|
||||
|
||||
items.add(
|
||||
'icon',
|
||||
<div className="Form-group">
|
||||
<label>{app.translator.trans('core.admin.edit_group.icon_label')}</label>
|
||||
<div className="helpText">
|
||||
{app.translator.trans('core.admin.edit_group.icon_text', { a: <a href="https://fontawesome.com/icons?m=free" tabindex="-1" /> })}
|
||||
</div>
|
||||
<input className="FormControl" placeholder="fas fa-bolt" value={this.icon()} oninput={m.withAttr('value', this.icon)} />
|
||||
</div>,
|
||||
10
|
||||
);
|
||||
|
||||
items.add(
|
||||
'submit',
|
||||
<div className="Form-group">
|
||||
{Button.component({
|
||||
type: 'submit',
|
||||
className: 'Button Button--primary EditGroupModal-save',
|
||||
loading: this.loading,
|
||||
children: app.translator.trans('core.admin.edit_group.submit_button'),
|
||||
})}
|
||||
{this.group.exists && this.group.id() !== Group.ADMINISTRATOR_ID ? (
|
||||
<button type="button" className="Button EditGroupModal-delete" onclick={this.deleteGroup.bind(this)}>
|
||||
{app.translator.trans('core.admin.edit_group.delete_button')}
|
||||
</button>
|
||||
) : (
|
||||
''
|
||||
)}
|
||||
</div>,
|
||||
-10
|
||||
);
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
submitData() {
|
||||
return {
|
||||
nameSingular: this.nameSingular(),
|
||||
namePlural: this.namePlural(),
|
||||
color: this.color(),
|
||||
icon: this.icon(),
|
||||
};
|
||||
}
|
||||
|
||||
onsubmit(e) {
|
||||
e.preventDefault();
|
||||
|
||||
this.loading = true;
|
||||
|
||||
this.group
|
||||
.save(this.submitData(), { errorHandler: this.onerror.bind(this) })
|
||||
.then(this.hide.bind(this))
|
||||
.catch(() => {
|
||||
this.loading = false;
|
||||
m.redraw();
|
||||
});
|
||||
}
|
||||
|
||||
deleteGroup() {
|
||||
if (confirm(app.translator.transText('core.admin.edit_group.delete_confirmation'))) {
|
||||
this.group.delete().then(() => m.redraw());
|
||||
this.hide();
|
||||
}
|
||||
}
|
||||
}
|
@@ -152,8 +152,6 @@ export default class MailPage extends Page {
|
||||
const prop = this.values[name];
|
||||
|
||||
if (prop == undefined) {
|
||||
console.log(field)
|
||||
console.log(this.values)
|
||||
}
|
||||
|
||||
if (typeof field === 'string') {
|
||||
|
159
js/src/admin/components/PermissionDropdown.tsx
Normal file
159
js/src/admin/components/PermissionDropdown.tsx
Normal file
@@ -0,0 +1,159 @@
|
||||
import app from '../app';
|
||||
|
||||
import Dropdown, {DropdownProps} from '../../common/components/Dropdown';
|
||||
import Button from '../../common/components/Button';
|
||||
import Separator from '../../common/components/Separator';
|
||||
import Group from '../../common/models/Group';
|
||||
import Badge from '../../common/components/Badge';
|
||||
import GroupBadge from '../../common/components/GroupBadge';
|
||||
|
||||
function badgeForId(id) {
|
||||
const group = app.store.getById('groups', id);
|
||||
|
||||
return group ? GroupBadge.component({ group, label: null }) : '';
|
||||
}
|
||||
|
||||
function filterByRequiredPermissions(groupIds, permission) {
|
||||
app.getRequiredPermissions(permission).forEach((required) => {
|
||||
const restrictToGroupIds = app.data.permissions[required] || [];
|
||||
|
||||
if (restrictToGroupIds.indexOf(Group.GUEST_ID) !== -1) {
|
||||
// do nothing
|
||||
} else if (restrictToGroupIds.indexOf(Group.MEMBER_ID) !== -1) {
|
||||
groupIds = groupIds.filter((id) => id !== Group.GUEST_ID);
|
||||
} else if (groupIds.indexOf(Group.MEMBER_ID) !== -1) {
|
||||
groupIds = restrictToGroupIds;
|
||||
} else {
|
||||
groupIds = restrictToGroupIds.filter((id) => groupIds.indexOf(id) !== -1);
|
||||
}
|
||||
|
||||
groupIds = filterByRequiredPermissions(groupIds, required);
|
||||
});
|
||||
|
||||
return groupIds;
|
||||
}
|
||||
|
||||
export interface PermissionDropdownProps extends DropdownProps {
|
||||
label?: Badge[];
|
||||
}
|
||||
|
||||
export default class PermissionDropdown<T extends PermissionDropdownProps = PermissionDropdownProps> extends Dropdown<T> {
|
||||
static initProps(props) {
|
||||
super.initProps(props);
|
||||
|
||||
props.className = 'PermissionDropdown';
|
||||
props.buttonClassName = 'Button Button--text';
|
||||
}
|
||||
|
||||
view() {
|
||||
this.props.children = [];
|
||||
|
||||
let groupIds = app.data.permissions[this.props.permission] || [];
|
||||
|
||||
groupIds = filterByRequiredPermissions(groupIds, this.props.permission);
|
||||
|
||||
const everyone = groupIds.indexOf(Group.GUEST_ID) !== -1;
|
||||
const members = groupIds.indexOf(Group.MEMBER_ID) !== -1;
|
||||
const adminGroup: Group = app.store.getById('groups', Group.ADMINISTRATOR_ID);
|
||||
|
||||
if (everyone) {
|
||||
this.props.label = Badge.component({ icon: 'fas fa-globe' });
|
||||
} else if (members) {
|
||||
this.props.label = Badge.component({ icon: 'fas fa-user' });
|
||||
} else {
|
||||
this.props.label = [badgeForId(Group.ADMINISTRATOR_ID), groupIds.map(badgeForId)];
|
||||
}
|
||||
|
||||
if (this.showing) {
|
||||
if (this.props.allowGuest) {
|
||||
this.props.children.push(
|
||||
Button.component({
|
||||
children: [
|
||||
Badge.component({ icon: 'fas fa-globe' }),
|
||||
' ',
|
||||
app.translator.trans('core.admin.permissions_controls.everyone_button'),
|
||||
],
|
||||
icon: everyone ? 'fas fa-check' : true,
|
||||
onclick: () => this.save([Group.GUEST_ID]),
|
||||
disabled: this.isGroupDisabled(Group.GUEST_ID),
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
this.props.children.push(
|
||||
Button.component({
|
||||
children: [Badge.component({ icon: 'fas fa-user' }), ' ', app.translator.trans('core.admin.permissions_controls.members_button')],
|
||||
icon: members ? 'fas fa-check' : true,
|
||||
onclick: () => this.save([Group.MEMBER_ID]),
|
||||
disabled: this.isGroupDisabled(Group.MEMBER_ID),
|
||||
}),
|
||||
|
||||
Separator.component(),
|
||||
|
||||
Button.component({
|
||||
children: [badgeForId(adminGroup.id()), ' ', adminGroup.namePlural()],
|
||||
icon: !everyone && !members ? 'fas fa-check' : true,
|
||||
disabled: !everyone && !members,
|
||||
onclick: (e) => {
|
||||
if (e.shiftKey) e.stopPropagation();
|
||||
this.save([]);
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
[].push.apply(
|
||||
this.props.children,
|
||||
app.store
|
||||
.all('groups')
|
||||
.filter((group) => [Group.ADMINISTRATOR_ID, Group.GUEST_ID, Group.MEMBER_ID].indexOf(group.id()) === -1)
|
||||
.map((group: Group) =>
|
||||
Button.component({
|
||||
children: [badgeForId(group.id()), ' ', group.namePlural()],
|
||||
icon: groupIds.indexOf(group.id()) !== -1 ? 'fas fa-check' : true,
|
||||
onclick: (e) => {
|
||||
if (e.shiftKey) e.stopPropagation();
|
||||
this.toggle(group.id());
|
||||
},
|
||||
disabled:
|
||||
this.isGroupDisabled(group.id()) && this.isGroupDisabled(Group.MEMBER_ID) && this.isGroupDisabled(Group.GUEST_ID),
|
||||
})
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return super.view();
|
||||
}
|
||||
|
||||
save(groupIds) {
|
||||
const permission = this.props.permission;
|
||||
|
||||
app.data.permissions[permission] = groupIds;
|
||||
|
||||
app.request({
|
||||
method: 'POST',
|
||||
url: app.forum.attribute('apiUrl') + '/permission',
|
||||
body: { permission, groupIds },
|
||||
});
|
||||
}
|
||||
|
||||
toggle(groupId) {
|
||||
const permission = this.props.permission;
|
||||
|
||||
let groupIds = app.data.permissions[permission] || [];
|
||||
|
||||
const index = groupIds.indexOf(groupId);
|
||||
|
||||
if (index !== -1) {
|
||||
groupIds.splice(index, 1);
|
||||
} else {
|
||||
groupIds.push(groupId);
|
||||
groupIds = groupIds.filter((id) => [Group.GUEST_ID, Group.MEMBER_ID].indexOf(id) === -1);
|
||||
}
|
||||
|
||||
this.save(groupIds);
|
||||
}
|
||||
|
||||
isGroupDisabled(id) {
|
||||
return filterByRequiredPermissions([id], this.props.permission).indexOf(id) === -1;
|
||||
}
|
||||
}
|
361
js/src/admin/components/PermissionGrid.tsx
Normal file
361
js/src/admin/components/PermissionGrid.tsx
Normal file
@@ -0,0 +1,361 @@
|
||||
import app from '../app';
|
||||
|
||||
import Component from '../../common/Component';
|
||||
import PermissionDropdown from './PermissionDropdown';
|
||||
import SettingDropdown from './SettingDropdown';
|
||||
import Button from '../../common/components/Button';
|
||||
import ItemList from '../../common/utils/ItemList';
|
||||
import icon from '../../common/helpers/icon';
|
||||
|
||||
export default class PermissionGrid extends Component {
|
||||
view() {
|
||||
const scopes = this.scopeItems().toArray();
|
||||
|
||||
const permissionCells = (permission) => {
|
||||
return scopes.map((scope) => <td>{scope.render(permission)}</td>);
|
||||
};
|
||||
|
||||
return (
|
||||
<table className="PermissionGrid">
|
||||
<thead>
|
||||
<tr>
|
||||
<td></td>
|
||||
{scopes.map((scope) => (
|
||||
<th>
|
||||
{scope.label}{' '}
|
||||
{scope.onremove
|
||||
? Button.component({
|
||||
icon: 'fas fa-times',
|
||||
className: 'Button Button--text PermissionGrid-removeScope',
|
||||
onclick: scope.onremove,
|
||||
})
|
||||
: ''}
|
||||
</th>
|
||||
))}
|
||||
<th>{this.scopeControlItems().toArray()}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
{this.permissionItems()
|
||||
.toArray()
|
||||
.map((section) => (
|
||||
<tbody>
|
||||
<tr className="PermissionGrid-section">
|
||||
<th>{section.label}</th>
|
||||
{permissionCells(section)}
|
||||
<td />
|
||||
</tr>
|
||||
{section.children.map((child) => (
|
||||
<tr className="PermissionGrid-child">
|
||||
<th>
|
||||
{icon(child.icon)}
|
||||
{child.label}
|
||||
</th>
|
||||
{permissionCells(child)}
|
||||
<td />
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
))}
|
||||
</table>
|
||||
);
|
||||
}
|
||||
|
||||
permissionItems() {
|
||||
const items = new ItemList();
|
||||
|
||||
items.add(
|
||||
'view',
|
||||
{
|
||||
label: app.translator.trans('core.admin.permissions.read_heading'),
|
||||
children: this.viewItems().toArray(),
|
||||
},
|
||||
100
|
||||
);
|
||||
|
||||
items.add(
|
||||
'start',
|
||||
{
|
||||
label: app.translator.trans('core.admin.permissions.create_heading'),
|
||||
children: this.startItems().toArray(),
|
||||
},
|
||||
90
|
||||
);
|
||||
|
||||
items.add(
|
||||
'reply',
|
||||
{
|
||||
label: app.translator.trans('core.admin.permissions.participate_heading'),
|
||||
children: this.replyItems().toArray(),
|
||||
},
|
||||
80
|
||||
);
|
||||
|
||||
items.add(
|
||||
'moderate',
|
||||
{
|
||||
label: app.translator.trans('core.admin.permissions.moderate_heading'),
|
||||
children: this.moderateItems().toArray(),
|
||||
},
|
||||
70
|
||||
);
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
viewItems() {
|
||||
const items = new ItemList();
|
||||
|
||||
items.add(
|
||||
'viewDiscussions',
|
||||
{
|
||||
icon: 'fas fa-eye',
|
||||
label: app.translator.trans('core.admin.permissions.view_discussions_label'),
|
||||
permission: 'viewDiscussions',
|
||||
allowGuest: true,
|
||||
},
|
||||
100
|
||||
);
|
||||
|
||||
items.add(
|
||||
'viewUserList',
|
||||
{
|
||||
icon: 'fas fa-users',
|
||||
label: app.translator.trans('core.admin.permissions.view_user_list_label'),
|
||||
permission: 'viewUserList',
|
||||
allowGuest: true,
|
||||
},
|
||||
100
|
||||
);
|
||||
|
||||
items.add(
|
||||
'signUp',
|
||||
{
|
||||
icon: 'fas fa-user-plus',
|
||||
label: app.translator.trans('core.admin.permissions.sign_up_label'),
|
||||
setting: () =>
|
||||
SettingDropdown.component({
|
||||
key: 'allow_sign_up',
|
||||
options: [
|
||||
{ value: '1', label: app.translator.transText('core.admin.permissions_controls.signup_open_button') },
|
||||
{ value: '0', label: app.translator.transText('core.admin.permissions_controls.signup_closed_button') },
|
||||
],
|
||||
}),
|
||||
},
|
||||
90
|
||||
);
|
||||
|
||||
items.add('viewLastSeenAt', {
|
||||
icon: 'far fa-clock',
|
||||
label: app.translator.trans('core.admin.permissions.view_last_seen_at_label'),
|
||||
permission: 'user.viewLastSeenAt',
|
||||
});
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
startItems() {
|
||||
const items = new ItemList();
|
||||
|
||||
items.add(
|
||||
'start',
|
||||
{
|
||||
icon: 'fas fa-edit',
|
||||
label: app.translator.trans('core.admin.permissions.start_discussions_label'),
|
||||
permission: 'startDiscussion',
|
||||
},
|
||||
100
|
||||
);
|
||||
|
||||
items.add(
|
||||
'allowRenaming',
|
||||
{
|
||||
icon: 'fas fa-i-cursor',
|
||||
label: app.translator.trans('core.admin.permissions.allow_renaming_label'),
|
||||
setting: () => {
|
||||
const minutes = parseInt(app.data.settings.allow_renaming, 10);
|
||||
|
||||
return SettingDropdown.component({
|
||||
defaultLabel: minutes
|
||||
? app.translator.transChoice('core.admin.permissions_controls.allow_some_minutes_button', minutes, { count: minutes })
|
||||
: app.translator.trans('core.admin.permissions_controls.allow_indefinitely_button'),
|
||||
key: 'allow_renaming',
|
||||
options: [
|
||||
{ value: '-1', label: app.translator.transText('core.admin.permissions_controls.allow_indefinitely_button') },
|
||||
{ value: '10', label: app.translator.transText('core.admin.permissions_controls.allow_ten_minutes_button') },
|
||||
{ value: 'reply', label: app.translator.transText('core.admin.permissions_controls.allow_until_reply_button') },
|
||||
],
|
||||
});
|
||||
},
|
||||
},
|
||||
90
|
||||
);
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
replyItems() {
|
||||
const items = new ItemList();
|
||||
|
||||
items.add(
|
||||
'reply',
|
||||
{
|
||||
icon: 'fas fa-reply',
|
||||
label: app.translator.trans('core.admin.permissions.reply_to_discussions_label'),
|
||||
permission: 'discussion.reply',
|
||||
},
|
||||
100
|
||||
);
|
||||
|
||||
items.add(
|
||||
'allowPostEditing',
|
||||
{
|
||||
icon: 'fas fa-pencil-alt',
|
||||
label: app.translator.trans('core.admin.permissions.allow_post_editing_label'),
|
||||
setting: () => {
|
||||
const minutes = parseInt(app.data.settings.allow_post_editing, 10);
|
||||
|
||||
return SettingDropdown.component({
|
||||
defaultLabel: minutes
|
||||
? app.translator.transChoice('core.admin.permissions_controls.allow_some_minutes_button', minutes, { count: minutes })
|
||||
: app.translator.trans('core.admin.permissions_controls.allow_indefinitely_button'),
|
||||
key: 'allow_post_editing',
|
||||
options: [
|
||||
{ value: '-1', label: app.translator.transText('core.admin.permissions_controls.allow_indefinitely_button') },
|
||||
{ value: '10', label: app.translator.transText('core.admin.permissions_controls.allow_ten_minutes_button') },
|
||||
{ value: 'reply', label: app.translator.transText('core.admin.permissions_controls.allow_until_reply_button') },
|
||||
],
|
||||
});
|
||||
},
|
||||
},
|
||||
90
|
||||
);
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
moderateItems() {
|
||||
const items = new ItemList();
|
||||
|
||||
items.add(
|
||||
'viewIpsPosts',
|
||||
{
|
||||
icon: 'fas fa-bullseye',
|
||||
label: app.translator.trans('core.admin.permissions.view_post_ips_label'),
|
||||
permission: 'discussion.viewIpsPosts',
|
||||
},
|
||||
110
|
||||
);
|
||||
|
||||
items.add(
|
||||
'renameDiscussions',
|
||||
{
|
||||
icon: 'fas fa-i-cursor',
|
||||
label: app.translator.trans('core.admin.permissions.rename_discussions_label'),
|
||||
permission: 'discussion.rename',
|
||||
},
|
||||
100
|
||||
);
|
||||
|
||||
items.add(
|
||||
'hideDiscussions',
|
||||
{
|
||||
icon: 'far fa-trash-alt',
|
||||
label: app.translator.trans('core.admin.permissions.delete_discussions_label'),
|
||||
permission: 'discussion.hide',
|
||||
},
|
||||
90
|
||||
);
|
||||
|
||||
items.add(
|
||||
'deleteDiscussions',
|
||||
{
|
||||
icon: 'fas fa-times',
|
||||
label: app.translator.trans('core.admin.permissions.delete_discussions_forever_label'),
|
||||
permission: 'discussion.delete',
|
||||
},
|
||||
80
|
||||
);
|
||||
|
||||
items.add(
|
||||
'postWithoutThrottle',
|
||||
{
|
||||
icon: 'fas fa-swimmer',
|
||||
label: app.translator.trans('core.admin.permissions.post_without_throttle_label'),
|
||||
permission: 'postWithoutThrottle',
|
||||
},
|
||||
70
|
||||
);
|
||||
|
||||
items.add(
|
||||
'editPosts',
|
||||
{
|
||||
icon: 'fas fa-pencil-alt',
|
||||
label: app.translator.trans('core.admin.permissions.edit_posts_label'),
|
||||
permission: 'discussion.editPosts',
|
||||
},
|
||||
70
|
||||
);
|
||||
|
||||
items.add(
|
||||
'hidePosts',
|
||||
{
|
||||
icon: 'far fa-trash-alt',
|
||||
label: app.translator.trans('core.admin.permissions.delete_posts_label'),
|
||||
permission: 'discussion.hidePosts',
|
||||
},
|
||||
60
|
||||
);
|
||||
|
||||
items.add(
|
||||
'deletePosts',
|
||||
{
|
||||
icon: 'fas fa-times',
|
||||
label: app.translator.trans('core.admin.permissions.delete_posts_forever_label'),
|
||||
permission: 'discussion.deletePosts',
|
||||
},
|
||||
60
|
||||
);
|
||||
|
||||
items.add(
|
||||
'userEdit',
|
||||
{
|
||||
icon: 'fas fa-user-cog',
|
||||
label: app.translator.trans('core.admin.permissions.edit_users_label'),
|
||||
permission: 'user.edit',
|
||||
},
|
||||
60
|
||||
);
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
scopeItems() {
|
||||
const items = new ItemList();
|
||||
|
||||
items.add(
|
||||
'global',
|
||||
{
|
||||
label: app.translator.trans('core.admin.permissions.global_heading'),
|
||||
render: (item) => {
|
||||
if (item.setting) {
|
||||
return item.setting();
|
||||
} else if (item.permission) {
|
||||
return PermissionDropdown.component({
|
||||
permission: item.permission,
|
||||
allowGuest: item.allowGuest,
|
||||
});
|
||||
}
|
||||
|
||||
return '';
|
||||
},
|
||||
},
|
||||
100
|
||||
);
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
scopeControlItems() {
|
||||
return new ItemList();
|
||||
}
|
||||
}
|
42
js/src/admin/components/PermissionsPage.tsx
Normal file
42
js/src/admin/components/PermissionsPage.tsx
Normal file
@@ -0,0 +1,42 @@
|
||||
import app from '../app';
|
||||
|
||||
import Page from './Page';
|
||||
import GroupBadge from '../../common/components/GroupBadge';
|
||||
import EditGroupModal from './EditGroupModal';
|
||||
import Group from '../../common/models/Group';
|
||||
import icon from '../../common/helpers/icon';
|
||||
import PermissionGrid from './PermissionGrid';
|
||||
|
||||
export default class PermissionsPage extends Page {
|
||||
view() {
|
||||
return (
|
||||
<div className="PermissionsPage">
|
||||
<div className="PermissionsPage-groups">
|
||||
<div className="container">
|
||||
{app.store
|
||||
.all('groups')
|
||||
.filter((group) => [Group.GUEST_ID, Group.MEMBER_ID].indexOf(group.id()) === -1)
|
||||
.map((group: Group) => (
|
||||
<button className="Button Group" onclick={() => app.modal.show(EditGroupModal, { group })}>
|
||||
{GroupBadge.component({
|
||||
group,
|
||||
className: 'Group-icon',
|
||||
label: null,
|
||||
})}
|
||||
<span className="Group-name">{group.namePlural()}</span>
|
||||
</button>
|
||||
))}
|
||||
<button className="Button Group Group--add" onclick={() => app.modal.show(EditGroupModal)}>
|
||||
{icon('fas fa-plus', { className: 'Group-icon' })}
|
||||
<span className="Group-name">{app.translator.trans('core.admin.permissions.new_group_button')}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="PermissionsPage-permissions">
|
||||
<div className="container">{PermissionGrid.component()}</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
@@ -1,11 +1,13 @@
|
||||
import BasicsPage from './components/BasicsPage';
|
||||
import DashboardPage from './components/DashboardPage';
|
||||
import MailPage from './components/MailPage';
|
||||
import PermissionsPage from './components/PermissionsPage';
|
||||
|
||||
export default (app) => {
|
||||
app.routes = {
|
||||
dashboard: { path: '/', component: DashboardPage },
|
||||
basics: { path: '/basics', component: BasicsPage },
|
||||
mail: { path: '/mail', component: MailPage },
|
||||
permissions: { path: '/permissions', component: PermissionsPage },
|
||||
};
|
||||
};
|
||||
|
@@ -113,7 +113,7 @@ export default abstract class Application {
|
||||
}
|
||||
|
||||
boot() {
|
||||
this.initializers.toArray().forEach((initializer) => initializer(this));
|
||||
//this.initializers.toArray().forEach((initializer) => initializer(this));
|
||||
|
||||
this.store.pushPayload({ data: this.data.resources });
|
||||
|
||||
|
@@ -5,7 +5,7 @@ import listItems from '../helpers/listItems';
|
||||
export interface DropdownProps extends ComponentProps {
|
||||
buttonClassName?: string;
|
||||
menuClassName?: string;
|
||||
label?: string;
|
||||
label?: string | any[];
|
||||
icon?: string;
|
||||
caretIcon?: undefined | string;
|
||||
|
||||
|
@@ -65,7 +65,7 @@ export default abstract class Modal<T extends ComponentProps = ComponentProps> e
|
||||
/**
|
||||
* Get the title of the modal dialog.
|
||||
*/
|
||||
abstract title(): string;
|
||||
abstract title();
|
||||
|
||||
/**
|
||||
* Get the content of the modal.
|
||||
|
Reference in New Issue
Block a user