diff --git a/framework/core/js/admin/src/components/PermissionsPage.js b/framework/core/js/admin/src/components/PermissionsPage.js
new file mode 100644
index 000000000..e594df3a8
--- /dev/null
+++ b/framework/core/js/admin/src/components/PermissionsPage.js
@@ -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 => (
+
+ {scope.render(permission)}
+ |
+ ));
+ };
+
+ return (
+
+
+
+ {this.groups.map(group => (
+
+ ))}
+
+
+
+
+
+
+
+
+
+ |
+ {this.scopes.map(scope => {scope.label} | )}
+ {this.scopeControls} |
+
+
+ {this.permissions.map(section => (
+
+
+ {section.label} |
+ {permissionCells(section)}
+ |
+
+ {section.children.map(child => (
+
+ {child.label} |
+ {permissionCells(child)}
+ |
+
+ ))}
+
+ ))}
+
+
+
+
+ );
+ }
+
+ 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 (
+
+ );
+ };
+
+ 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;
+ }
+}
diff --git a/framework/core/js/lib/components/Badge.js b/framework/core/js/lib/components/Badge.js
index 929e22e2b..6e071a6bf 100644
--- a/framework/core/js/lib/components/Badge.js
+++ b/framework/core/js/lib/components/Badge.js
@@ -38,6 +38,6 @@ export default class Badge extends Component {
config(isInitialized) {
if (isInitialized) return;
- this.$().tooltip();
+ if (this.props.label) this.$().tooltip();
}
}
diff --git a/framework/core/js/lib/components/Select.js b/framework/core/js/lib/components/Select.js
index 0974f857e..ab6ef9fea 100644
--- a/framework/core/js/lib/components/Select.js
+++ b/framework/core/js/lib/components/Select.js
@@ -15,7 +15,7 @@ export default class Select extends Component {
return (
-