mirror of
https://github.com/flarum/framework.git
synced 2024-12-12 14:13:37 +08:00
Begin implementing permissions page
This commit is contained in:
parent
5f1db93e3d
commit
6873f77012
269
framework/core/js/admin/src/components/PermissionsPage.js
Normal file
269
framework/core/js/admin/src/components/PermissionsPage.js
Normal file
|
@ -0,0 +1,269 @@
|
||||||
|
import Component from 'flarum/Component';
|
||||||
|
import Badge from 'flarum/components/Badge';
|
||||||
|
import Select from 'flarum/components/Select';
|
||||||
|
import Button from 'flarum/components/Button';
|
||||||
|
import Group from 'flarum/models/Group';
|
||||||
|
import icon from 'flarum/helpers/icon';
|
||||||
|
import ItemList from 'flarum/utils/ItemList';
|
||||||
|
|
||||||
|
export default class PermissionsPage extends Component {
|
||||||
|
constructor(...args) {
|
||||||
|
super(...args);
|
||||||
|
|
||||||
|
this.groups = app.store.all('groups')
|
||||||
|
.filter(group => [Group.GUEST_ID, Group.MEMBER_ID].indexOf(Number(group.id())) === -1);
|
||||||
|
|
||||||
|
this.permissions = this.permissionItems().toArray();
|
||||||
|
this.scopes = this.scopeItems().toArray();
|
||||||
|
this.scopeControls = this.scopeControlItems().toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
view() {
|
||||||
|
const permissionCells = permission => {
|
||||||
|
return this.scopes.map(scope => (
|
||||||
|
<td>
|
||||||
|
{scope.render(permission)}
|
||||||
|
</td>
|
||||||
|
));
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="PermissionsPage">
|
||||||
|
<div className="PermissionsPage-groups">
|
||||||
|
<div className="container">
|
||||||
|
{this.groups.map(group => (
|
||||||
|
<button className="Button Group">
|
||||||
|
{Badge.component({
|
||||||
|
className: 'Group-icon',
|
||||||
|
icon: group.icon(),
|
||||||
|
style: {backgroundColor: group.color()}
|
||||||
|
})}
|
||||||
|
<span className="Group-name">{group.namePlural()}</span>
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
<button className="Button Group Group--add">
|
||||||
|
{icon('plus', {className: 'Group-icon'})}
|
||||||
|
<span className="Group-name">New Group</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="PermissionsPage-permissions">
|
||||||
|
<div className="container">
|
||||||
|
<table className="PermissionGrid">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<td></td>
|
||||||
|
{this.scopes.map(scope => <th>{scope.label}</th>)}
|
||||||
|
<th>{this.scopeControls}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
{this.permissions.map(section => (
|
||||||
|
<tbody>
|
||||||
|
<tr className="PermissionGrid-section">
|
||||||
|
<th>{section.label}</th>
|
||||||
|
{permissionCells(section)}
|
||||||
|
<td/>
|
||||||
|
</tr>
|
||||||
|
{section.children.map(child => (
|
||||||
|
<tr className="PermissionGrid-child">
|
||||||
|
<th>{child.label}</th>
|
||||||
|
{permissionCells(child)}
|
||||||
|
<td/>
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
))}
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
permissionItems() {
|
||||||
|
const items = new ItemList();
|
||||||
|
|
||||||
|
items.add('view', {
|
||||||
|
label: 'View the forum',
|
||||||
|
children: this.viewItems().toArray()
|
||||||
|
});
|
||||||
|
|
||||||
|
items.add('start', {
|
||||||
|
label: 'Start discussions',
|
||||||
|
children: this.startItems().toArray()
|
||||||
|
});
|
||||||
|
|
||||||
|
items.add('reply', {
|
||||||
|
label: 'Reply to discussions',
|
||||||
|
children: this.replyItems().toArray()
|
||||||
|
});
|
||||||
|
|
||||||
|
items.add('moderate', {
|
||||||
|
label: 'Moderate',
|
||||||
|
children: this.moderateItems().toArray()
|
||||||
|
});
|
||||||
|
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
viewItems() {
|
||||||
|
const items = new ItemList();
|
||||||
|
|
||||||
|
items.add('view', {
|
||||||
|
label: 'View discussions',
|
||||||
|
permission: 'forum.view',
|
||||||
|
allowGuest: true
|
||||||
|
});
|
||||||
|
|
||||||
|
items.add('signUp', {
|
||||||
|
label: 'Sign up',
|
||||||
|
setting: Select.component({options: ['Open']})
|
||||||
|
});
|
||||||
|
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
startItems() {
|
||||||
|
const items = new ItemList();
|
||||||
|
|
||||||
|
items.add('start', {
|
||||||
|
label: 'Start discussions',
|
||||||
|
permission: 'forum.startDiscussion'
|
||||||
|
});
|
||||||
|
|
||||||
|
items.add('allowRenaming', {
|
||||||
|
label: 'Allow renaming',
|
||||||
|
setting: Select.component({options: ['Indefinitely']})
|
||||||
|
});
|
||||||
|
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
replyItems() {
|
||||||
|
const items = new ItemList();
|
||||||
|
|
||||||
|
items.add('reply', {
|
||||||
|
label: 'Reply to discussions',
|
||||||
|
permission: 'discussion.reply'
|
||||||
|
});
|
||||||
|
|
||||||
|
items.add('allowPostEditing', {
|
||||||
|
label: 'Allow post editing',
|
||||||
|
setting: Select.component({options: ['Indefinitely']})
|
||||||
|
});
|
||||||
|
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
moderateItems() {
|
||||||
|
const items = new ItemList();
|
||||||
|
|
||||||
|
items.add('editPosts', {
|
||||||
|
label: 'Edit posts',
|
||||||
|
permission: 'discussion.editPosts'
|
||||||
|
});
|
||||||
|
|
||||||
|
items.add('deletePosts', {
|
||||||
|
label: 'Delete posts',
|
||||||
|
permission: 'discussion.deletePosts'
|
||||||
|
});
|
||||||
|
|
||||||
|
items.add('renameDiscussions', {
|
||||||
|
label: 'Rename discussions',
|
||||||
|
permission: 'discussion.rename'
|
||||||
|
});
|
||||||
|
|
||||||
|
items.add('deleteDiscussions', {
|
||||||
|
label: 'Delete discussions',
|
||||||
|
permission: 'discussion.delete'
|
||||||
|
});
|
||||||
|
|
||||||
|
items.add('suspendUsers', {
|
||||||
|
label: 'Suspend users',
|
||||||
|
permission: 'user.suspend'
|
||||||
|
});
|
||||||
|
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
scopeItems() {
|
||||||
|
const items = new ItemList();
|
||||||
|
|
||||||
|
const groupBadge = id => {
|
||||||
|
const group = app.store.getById('groups', id);
|
||||||
|
|
||||||
|
return Badge.component({
|
||||||
|
icon: group.icon(),
|
||||||
|
style: {backgroundColor: group.color()},
|
||||||
|
label: group.namePlural()
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const groupBadges = groupIds => {
|
||||||
|
let content;
|
||||||
|
|
||||||
|
if (groupIds.indexOf(String(Group.GUEST_ID)) !== -1) {
|
||||||
|
content = 'Everyone';
|
||||||
|
} else if (groupIds.indexOf(String(Group.MEMBER_ID)) !== -1) {
|
||||||
|
content = 'Members';
|
||||||
|
} else {
|
||||||
|
content = [
|
||||||
|
groupBadge(Group.ADMINISTRATOR_ID),
|
||||||
|
groupIds.map(groupBadge)
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<button className="Button Button--text">
|
||||||
|
{content}
|
||||||
|
{icon('sort', {className: 'GroupsButton-caret'})}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
items.add('global', {
|
||||||
|
label: 'Global',
|
||||||
|
render: permission => {
|
||||||
|
if (permission.setting) {
|
||||||
|
return permission.setting;
|
||||||
|
} else if (permission.permission) {
|
||||||
|
const groupIds = app.forum.attribute('permissions')[permission.permission] || [];
|
||||||
|
|
||||||
|
return groupBadges(groupIds);
|
||||||
|
}
|
||||||
|
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
items.add('tag1', {
|
||||||
|
label: 'Blog',
|
||||||
|
render: permission => {
|
||||||
|
if (permission.setting) {
|
||||||
|
return '';
|
||||||
|
} else if (permission.permission) {
|
||||||
|
const groupIds = app.forum.attribute('permissions')[permission.permission] || [];
|
||||||
|
|
||||||
|
return groupBadges(groupIds);
|
||||||
|
}
|
||||||
|
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
scopeControlItems() {
|
||||||
|
const items = new ItemList();
|
||||||
|
|
||||||
|
items.add('addTag', Button.component({
|
||||||
|
children: 'Restrict by Tag',
|
||||||
|
icon: 'plus',
|
||||||
|
className: 'Button Button--text'
|
||||||
|
}))
|
||||||
|
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
}
|
|
@ -38,6 +38,6 @@ export default class Badge extends Component {
|
||||||
config(isInitialized) {
|
config(isInitialized) {
|
||||||
if (isInitialized) return;
|
if (isInitialized) return;
|
||||||
|
|
||||||
this.$().tooltip();
|
if (this.props.label) this.$().tooltip();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@ export default class Select extends Component {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<span className="Select">
|
<span className="Select">
|
||||||
<select className="Select-input FormControl" onchange={m.withAttr('value', onchange.bind(this))} value={value}>
|
<select className="Select-input FormControl" onchange={onchange ? m.withAttr('value', onchange.bind(this)) : undefined} value={value}>
|
||||||
{Object.keys(options).map(key => <option value={key}>{options[key]}</option>)}
|
{Object.keys(options).map(key => <option value={key}>{options[key]}</option>)}
|
||||||
</select>
|
</select>
|
||||||
{icon('sort', {className: 'Select-caret'})}
|
{icon('sort', {className: 'Select-caret'})}
|
||||||
|
|
86
framework/core/less/admin/PermissionsPage.less
Normal file
86
framework/core/less/admin/PermissionsPage.less
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
.PermissionsPage-groups {
|
||||||
|
background: @control-bg;
|
||||||
|
padding: 30px 0;
|
||||||
|
}
|
||||||
|
.Group {
|
||||||
|
width: 90px;
|
||||||
|
display: inline-block;
|
||||||
|
text-align: center;
|
||||||
|
color: @text-color;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
.Group-name {
|
||||||
|
display: block;
|
||||||
|
margin-top: 5px;
|
||||||
|
}
|
||||||
|
.Group-icon {
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
.Group--add {
|
||||||
|
border: 1px dashed @muted-color;
|
||||||
|
color: @muted-color;
|
||||||
|
width: auto;
|
||||||
|
margin-left: 10px;
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.PermissionsPage-permissions {
|
||||||
|
padding: 30px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.PermissionGrid {
|
||||||
|
td, th {
|
||||||
|
padding: 10px 0;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
td {
|
||||||
|
color: @muted-color;
|
||||||
|
}
|
||||||
|
thead th {
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 12px;
|
||||||
|
color: @muted-color;
|
||||||
|
width: 140px;
|
||||||
|
}
|
||||||
|
tbody {
|
||||||
|
th {
|
||||||
|
padding-right: 50px;
|
||||||
|
}
|
||||||
|
tr:last-child {
|
||||||
|
td, th {
|
||||||
|
padding-bottom: 15px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
select {
|
||||||
|
background: transparent;
|
||||||
|
padding: 0 30px 0 0;
|
||||||
|
height: auto;
|
||||||
|
border: 0;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.Button {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
td:not(:hover) {
|
||||||
|
.Select-caret, .GroupsButton-caret {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.PermissionGrid-section {
|
||||||
|
td, th {
|
||||||
|
padding-top: 15px;
|
||||||
|
border-top: 1px solid @control-bg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.PermissionGrid-child {
|
||||||
|
td, th {
|
||||||
|
padding: 5px 0;
|
||||||
|
}
|
||||||
|
th {
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
}
|
|
@ -156,7 +156,7 @@
|
||||||
.Button--text {
|
.Button--text {
|
||||||
background: transparent !important;
|
background: transparent !important;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
color: @muted-color !important;
|
color: inherit !important;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
|
|
|
@ -13,11 +13,18 @@ class GroupSerializer extends Serializer
|
||||||
protected function getDefaultAttributes($group)
|
protected function getDefaultAttributes($group)
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'id' => (int) $group->id,
|
|
||||||
'nameSingular' => $group->name_singular,
|
'nameSingular' => $group->name_singular,
|
||||||
'namePlural' => $group->name_plural,
|
'namePlural' => $group->name_plural,
|
||||||
'color' => $group->color,
|
'color' => $group->color,
|
||||||
'icon' => $group->icon,
|
'icon' => $group->icon,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return callable
|
||||||
|
*/
|
||||||
|
protected function permissions()
|
||||||
|
{
|
||||||
|
return $this->hasMany('Flarum\Api\Serializers\PermissionSerializer');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,4 +36,14 @@ class Group extends Model
|
||||||
{
|
{
|
||||||
return $this->belongsToMany('Flarum\Core\Users\User', 'users_groups');
|
return $this->belongsToMany('Flarum\Core\Users\User', 'users_groups');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define the relationship with the group's permissions.
|
||||||
|
*
|
||||||
|
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
|
||||||
|
*/
|
||||||
|
public function permissions()
|
||||||
|
{
|
||||||
|
return $this->hasMany('Flarum\Core\Groups\Permission');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
55
framework/core/src/Core/Groups/Permission.php
Executable file
55
framework/core/src/Core/Groups/Permission.php
Executable file
|
@ -0,0 +1,55 @@
|
||||||
|
<?php namespace Flarum\Core\Groups;
|
||||||
|
|
||||||
|
use Flarum\Core\Model;
|
||||||
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @todo document database columns with @property
|
||||||
|
*/
|
||||||
|
class Permission extends Model
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected $table = 'permissions';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define the relationship with the group that this permission is for.
|
||||||
|
*
|
||||||
|
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||||
|
*/
|
||||||
|
public function group()
|
||||||
|
{
|
||||||
|
return $this->belongsTo('Flarum\Core\Groups\Group', 'group_id');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the keys for a save update query.
|
||||||
|
*
|
||||||
|
* @param Builder $query
|
||||||
|
* @return Builder
|
||||||
|
*/
|
||||||
|
protected function setKeysForSaveQuery(Builder $query)
|
||||||
|
{
|
||||||
|
$query->where('group_id', $this->group_id)
|
||||||
|
->where('permission', $this->permission);
|
||||||
|
|
||||||
|
return $query;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a map of permissions to the group IDs that have them.
|
||||||
|
*
|
||||||
|
* @return array[]
|
||||||
|
*/
|
||||||
|
public static function map()
|
||||||
|
{
|
||||||
|
$permissions = [];
|
||||||
|
|
||||||
|
foreach (static::get() as $permission) {
|
||||||
|
$permissions[$permission->permission][] = $permission->group_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $permissions;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user