mirror of
https://github.com/flarum/core.git
synced 2025-08-05 16:07:34 +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 & {
|
export type AdminData = ApplicationData & {
|
||||||
mysqlVersion: string;
|
mysqlVersion: string;
|
||||||
phpVersion: string;
|
phpVersion: string;
|
||||||
|
permissions: {
|
||||||
|
[key: string]: string[];
|
||||||
|
};
|
||||||
settings: {
|
settings: {
|
||||||
[key: string]: string;
|
[key: string]: string;
|
||||||
};
|
};
|
||||||
|
@@ -50,12 +50,15 @@ export default class AdminNav<T> extends Component<T> {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
// items.add('permissions', AdminLinkButton.component({
|
items.add(
|
||||||
// href: app.route('permissions'),
|
'permissions',
|
||||||
// icon: 'fas fa-key',
|
AdminLinkButton.component({
|
||||||
// children: app.translator.trans('core.admin.nav.permissions_button'),
|
href: app.route('permissions'),
|
||||||
// description: app.translator.trans('core.admin.nav.permissions_text')
|
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({
|
// items.add('appearance', AdminLinkButton.component({
|
||||||
// href: app.route('appearance'),
|
// 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];
|
const prop = this.values[name];
|
||||||
|
|
||||||
if (prop == undefined) {
|
if (prop == undefined) {
|
||||||
console.log(field)
|
|
||||||
console.log(this.values)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof field === 'string') {
|
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 BasicsPage from './components/BasicsPage';
|
||||||
import DashboardPage from './components/DashboardPage';
|
import DashboardPage from './components/DashboardPage';
|
||||||
import MailPage from './components/MailPage';
|
import MailPage from './components/MailPage';
|
||||||
|
import PermissionsPage from './components/PermissionsPage';
|
||||||
|
|
||||||
export default (app) => {
|
export default (app) => {
|
||||||
app.routes = {
|
app.routes = {
|
||||||
dashboard: { path: '/', component: DashboardPage },
|
dashboard: { path: '/', component: DashboardPage },
|
||||||
basics: { path: '/basics', component: BasicsPage },
|
basics: { path: '/basics', component: BasicsPage },
|
||||||
mail: { path: '/mail', component: MailPage },
|
mail: { path: '/mail', component: MailPage },
|
||||||
|
permissions: { path: '/permissions', component: PermissionsPage },
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@@ -113,7 +113,7 @@ export default abstract class Application {
|
|||||||
}
|
}
|
||||||
|
|
||||||
boot() {
|
boot() {
|
||||||
this.initializers.toArray().forEach((initializer) => initializer(this));
|
//this.initializers.toArray().forEach((initializer) => initializer(this));
|
||||||
|
|
||||||
this.store.pushPayload({ data: this.data.resources });
|
this.store.pushPayload({ data: this.data.resources });
|
||||||
|
|
||||||
|
@@ -5,7 +5,7 @@ import listItems from '../helpers/listItems';
|
|||||||
export interface DropdownProps extends ComponentProps {
|
export interface DropdownProps extends ComponentProps {
|
||||||
buttonClassName?: string;
|
buttonClassName?: string;
|
||||||
menuClassName?: string;
|
menuClassName?: string;
|
||||||
label?: string;
|
label?: string | any[];
|
||||||
icon?: string;
|
icon?: string;
|
||||||
caretIcon?: undefined | 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.
|
* Get the title of the modal dialog.
|
||||||
*/
|
*/
|
||||||
abstract title(): string;
|
abstract title();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the content of the modal.
|
* Get the content of the modal.
|
||||||
|
Reference in New Issue
Block a user