feat: admin - manage groups + permissions + page rules

This commit is contained in:
Nicolas Giard
2018-12-29 21:30:51 -05:00
parent 10940ca230
commit edb97b832d
36 changed files with 1116 additions and 958 deletions

View File

@@ -130,6 +130,7 @@
outline
background-color='grey lighten-2'
persistent-hint
small-chips
deletable-chips
clearable
multiple
@@ -145,6 +146,7 @@
v-model='strategy.autoEnrollGroups'
prepend-icon='people'
hint='Automatically assign new users to these groups.'
small-chips
persistent-hint
deletable-chips
clearable

View File

@@ -8,38 +8,53 @@
.headline.primary--text {{ $t('admin:dashboard.title') }}
.subheading.grey--text {{ $t('admin:dashboard.subtitle') }}
v-flex(xs12 md6 lg4 xl3 d-flex)
v-card.primary.dashboard-card(dark)
v-card-text
v-icon.dashboard-icon insert_drive_file
.subheading Pages
animated-number.display-1(
:value='info.pagesTotal'
:duration='2000'
:formatValue='round'
easing='easeOutQuint'
)
v-hover
v-card.primary.dashboard-card(
dark
slot-scope='{ hover }'
:class='hover ? `elevation-10` : `elevation-2`'
)
v-card-text
v-icon.dashboard-icon insert_drive_file
.subheading Pages
animated-number.display-1(
:value='info.pagesTotal'
:duration='2000'
:formatValue='round'
easing='easeOutQuint'
)
v-flex(xs12 md6 lg4 xl3 d-flex)
v-card.indigo.lighten-1.dashboard-card(dark)
v-card-text
v-icon.dashboard-icon person
.subheading Users
animated-number.display-1(
:value='info.usersTotal'
:duration='2000'
:formatValue='round'
easing='easeOutQuint'
)
v-hover
v-card.indigo.lighten-1.dashboard-card(
dark
slot-scope='{ hover }'
:class='hover ? `elevation-10` : `elevation-2`'
)
v-card-text
v-icon.dashboard-icon person
.subheading Users
animated-number.display-1(
:value='info.usersTotal'
:duration='2000'
:formatValue='round'
easing='easeOutQuint'
)
v-flex(xs12 md6 lg4 xl3 d-flex)
v-card.indigo.lighten-2.dashboard-card(dark)
v-card-text
v-icon.dashboard-icon people
.subheading Groups
animated-number.display-1(
:value='info.groupsTotal'
:duration='2000'
:formatValue='round'
easing='easeOutQuint'
)
v-hover
v-card.indigo.lighten-2.dashboard-card(
dark
slot-scope='{ hover }'
:class='hover ? `elevation-10` : `elevation-2`'
)
v-card-text
v-icon.dashboard-icon people
.subheading Groups
animated-number.display-1(
:value='info.groupsTotal'
:duration='2000'
:formatValue='round'
easing='easeOutQuint'
)
v-flex(xs12 md6 lg12 xl3 d-flex)
v-card.dashboard-card(
:class='isLatestVersion ? "teal lighten-2" : "red lighten-2"'

View File

@@ -0,0 +1,203 @@
<template lang="pug">
v-card.wiki-form(flat)
v-card-text
v-text-field(
outline
background-color='grey lighten-3'
v-model='group.name'
label='Group Name'
counter='255'
prepend-icon='people'
)
v-alert.radius-7(
v-if='group.isSystem'
color='orange darken-2'
:class='$vuetify.dark ? "grey darken-4" : "orange lighten-5"'
outline
:value='true'
icon='lock_outline'
) This is a system group. Some permissions cannot be modified.
v-container.px-3.pb-3.pt-0(fluid, grid-list-md)
v-layout(row, wrap)
v-flex(xs12, md6, lg4, v-for='pmGroup in permissions')
v-card.md2(flat, :class='$vuetify.dark ? "grey darken-3-d5" : "white"')
v-subheader {{pmGroup.category}}
v-card-text.pt-0
template(v-for='(pm, idx) in pmGroup.items')
v-checkbox.pt-0(
:key='pm.permission'
:label='pm.permission'
:hint='pm.hint'
persistent-hint
color='primary'
v-model='group.permissions'
:value='pm.permission'
:append-icon='pm.warning ? "warning" : null',
:disabled='(group.isSystem && pm.restrictedForSystem) || group.id === 1 || pm.disabled'
)
v-divider.mt-3(v-if='idx < pmGroup.items.length - 1')
</template>
<script>
export default {
props: {
value: {
type: Object
}
},
data() {
return {
permissions: [
{
category: 'Content',
items: [
{
permission: 'read:pages',
hint: 'Can view pages, as specified in the Page Rules',
warning: false,
restrictedForSystem: false,
disabled: false
},
{
permission: 'write:pages',
hint: 'Can view and create new pages, as specified in the Page Rules',
warning: false,
restrictedForSystem: false,
disabled: false
},
{
permission: 'manage:pages',
hint: 'Can view, create, edit and move existing pages as specified in the Page Rules',
warning: false,
restrictedForSystem: false,
disabled: false
},
{
permission: 'delete:pages',
hint: 'Can delete existing pages, as specified in the Page Rules',
warning: false,
restrictedForSystem: false,
disabled: false
},
{
permission: 'read:assets',
hint: 'Can view / use assets (such as images and files), as specified in the Page Rules',
warning: false,
restrictedForSystem: false,
disabled: false
},
{
permission: 'write:assets',
hint: 'Can upload new assets (such as images and files), as specified in the Page Rules',
warning: false,
restrictedForSystem: false,
disabled: false
},
{
permission: 'manage:assets',
hint: 'Can edit and delete assets (such as images and files), as specified in the Page Rules',
warning: false,
restrictedForSystem: false,
disabled: false
},
{
permission: 'read:comments',
hint: 'Can view comments, as specified in the Page Rules',
warning: false,
restrictedForSystem: false,
disabled: false
},
{
permission: 'write:comments',
hint: 'Can post new comments, as specified in the Page Rules',
warning: false,
restrictedForSystem: false,
disabled: false
},
{
permission: 'manage:comments',
hint: 'Can edit and delete comments, as specified in the Page Rules',
warning: false,
restrictedForSystem: false,
disabled: false
}
]
},
{
category: 'Users',
items: [
{
permission: 'write:users',
hint: 'Can create or authorize new users, but not modify existing ones',
warning: false,
restrictedForSystem: true,
disabled: false
},
{
permission: 'manage:users',
hint: 'Can manage all users (but not users with administrative permissions)',
warning: false,
restrictedForSystem: true,
disabled: false
},
{
permission: 'write:groups',
hint: 'Can manage groups and assign CONTENT permissions / page rules',
warning: false,
restrictedForSystem: true,
disabled: false
},
{
permission: 'manage:groups',
hint: 'Can manage groups and assign ANY permissions (but not manage:system) / page rules',
warning: true,
restrictedForSystem: true,
disabled: false
}
]
},
{
category: 'Administration',
items: [
{
permission: 'manage:navigation',
hint: 'Can manage the site navigation',
warning: false,
restrictedForSystem: true,
disabled: false
},
{
permission: 'manage:theme',
hint: 'Can manage and modify themes',
warning: false,
restrictedForSystem: true,
disabled: false
},
{
permission: 'manage:api',
hint: 'Can generate and revoke API keys',
warning: true,
restrictedForSystem: true,
disabled: false
},
{
permission: 'manage:system',
hint: 'Can manage and access everything. Root administrator.',
warning: true,
restrictedForSystem: true,
disabled: true
}
]
}
]
}
},
computed: {
group: {
get() { return this.value },
set(val) { this.$set('input', val) }
}
}
}
</script>

View File

@@ -0,0 +1,302 @@
<template lang="pug">
v-card.wiki-form
v-card-text(v-if='group.id === 1')
v-alert.radius-7(
:class='$vuetify.dark ? "grey darken-4" : "orange lighten-5"'
color='orange darken-2'
outline
:value='true'
icon='lock_outline'
) This group has access to everything.
template(v-else)
v-card-title(:class='$vuetify.dark ? `grey darken-3-d5` : `grey lighten-5`')
v-alert.radius-7(
:class='$vuetify.dark ? `grey darken-3-d3` : `white`'
:value='true'
color='grey'
outline
icon='info'
) You must enable global content permissions (under Permissions tab) for page rules to have any effect.
v-spacer
v-btn(depressed, color='primary', @click='addRule')
v-icon(left) add
| Add Rule
v-menu(
right
offset-y
nudge-left='115'
)
v-btn.is-icon(slot='activator', flat, outline, color='primary')
v-icon more_horiz
v-list(dense)
v-list-tile(@click='comingSoon')
v-list-tile-avatar
v-icon keyboard_capslock
v-list-tile-title Load Preset
v-divider
v-list-tile(@click='comingSoon')
v-list-tile-avatar
v-icon publish
v-list-tile-title Save As Preset
v-divider
v-list-tile(@click='comingSoon')
v-list-tile-avatar
v-icon cloud_upload
v-list-tile-title Import Rules
v-divider
v-list-tile(@click='comingSoon')
v-list-tile-avatar
v-icon cloud_download
v-list-tile-title Export Rules
v-card-text(:class='$vuetify.dark ? `grey darken-4-l5` : `white`')
.rules
.caption(v-if='group.pageRules.length === 0')
em(:class='$vuetify.dark ? `grey--text` : `blue-grey--text`') This group has no page rules yet.
.rule(v-for='rule of group.pageRules', :key='rule.id')
v-btn.ma-0.rule-deny-btn(
solo
:color='rule.deny ? "red" : "green"'
dark
@click='rule.deny = !rule.deny'
)
v-icon(v-if='rule.deny') block
v-icon(v-else) check_circle
//- Roles
v-select.ml-1(
solo
:items='roles'
v-model='rule.roles'
placeholder='Select Role(s)...'
hide-details
multiple
chips
deletable-chips
small-chips
style='flex: 0 1 440px;'
:menu-props='{ "maxHeight": 500 }'
clearable
dense
)
template(slot='selection', slot-scope='{ item, index }')
v-chip.white--text.ml-0(v-if='index <= 2', small, label, :color='rule.deny ? `red` : `green`').caption {{ item.value }}
v-chip.white--text.ml-0(v-if='index === 3', small, label, :color='rule.deny ? `red lighten-2` : `green lighten-2`').caption + {{ rule.roles.length - 3 }} more
template(slot='item', slot-scope='props')
v-list-tile-action(style='min-width: 30px;')
v-checkbox(
v-model='props.tile.props.value'
hide-details
color='primary'
)
v-icon.mr-2(:color='rule.deny ? `red` : `green`') {{props.item.icon}}
v-list-tile-content
v-list-tile-title.body-2 {{props.item.text}}
v-chip.mr-2.grey--text(label, small, :color='$vuetify.dark ? `grey darken-4` : `grey lighten-4`').caption {{props.item.value}}
//- Match
v-select.ml-1.mr-1(
solo
:items='matches'
v-model='rule.match'
placeholder='Match...'
hide-details
style='flex: 0 1 250px;'
dense
)
template(slot='selection', slot-scope='{ item, index }')
.body-1 {{item.text}}
template(slot='item', slot-scope='data')
v-list-tile-avatar
v-avatar.white--text.radius-4(color='blue', size='30', tile) {{ data.item.icon }}
v-list-tile-content
v-list-tile-title(v-html='data.item.text')
//- Locales
v-select.mr-1(
:background-color='$vuetify.dark ? `grey darken-3-d5` : `blue-grey lighten-5`'
solo
:items='locales'
v-model='rule.locales'
placeholder='Any Locale'
multiple
hide-details
dense
:menu-props='{ "minWidth": 250 }'
style='flex: 0 1 150px;'
)
template(slot='selection', slot-scope='{ item, index }')
v-chip.white--text.ml-0(v-if='rule.locales.length === 1', small, label, :color='rule.deny ? `red` : `green`').caption {{ item.value.toUpperCase() }}
v-chip.white--text.ml-0(v-else-if='index === 0', small, label, :color='rule.deny ? `red` : `green`').caption {{ rule.locales.length }} locales
v-list-tile(slot='prepend-item', @click='rule.locales = []')
v-list-tile-action(style='min-width: 30px;')
v-checkbox(
:input-value='rule.locales.length === 0'
hide-details
color='primary'
readonly
)
v-icon.mr-2(:color='rule.deny ? `red` : `green`') public
v-list-tile-content
v-list-tile-title.body-2 Any Locale
v-divider(slot='prepend-item')
template(slot='item', slot-scope='props')
v-list-tile-action(style='min-width: 30px;')
v-checkbox(
v-model='props.tile.props.value'
hide-details
color='primary'
)
v-icon.mr-2(:color='rule.deny ? `red` : `green`') language
v-list-tile-content
v-list-tile-title.body-2 {{props.item.text}}
v-chip.mr-2.grey--text(label, small, :color='$vuetify.dark ? `grey darken-4` : `grey lighten-4`').caption {{props.item.value.toUpperCase()}}
//- Path
v-text-field(
solo
v-model='rule.path'
label='Path'
:prefix='rule.match !== `END` ? `/` : null'
:placeholder='rule.match === `REGEX` ? `Regular Expression` : `Path`'
:suffix='rule.match === `REGEX` ? `/` : null'
hide-details
:color='$vuetify.dark ? `grey` : `blue-grey`'
)
v-btn(icon, @click='removeRule(rule.id)')
v-icon(:color='$vuetify.dark ? `grey` : `blue-grey`') clear
</template>
<script>
import _ from 'lodash'
import nanoid from 'nanoid/non-secure/generate'
export default {
props: {
value: {
type: Object
}
},
data() {
return {
roles: [
{ text: 'Read Pages', value: 'READ', icon: 'insert_drive_file' },
{ text: 'Create Pages', value: 'WRITE', icon: 'insert_drive_file' },
{ text: 'Edit + Move Pages', value: 'MANAGE', icon: 'insert_drive_file' },
{ text: 'Delete Pages', value: 'DELETE', icon: 'insert_drive_file' },
{ text: 'Read / Use Assets', value: 'AS_READ', icon: 'camera' },
{ text: 'Upload Assets', value: 'AS_WRITE', icon: 'camera' },
{ text: 'Edit + Delete Assets', value: 'AS_MANAGE', icon: 'camera' },
{ text: 'Read Comments', value: 'CM_READ', icon: 'insert_comment' },
{ text: 'Create Comments', value: 'CM_WRITE', icon: 'insert_comment' },
{ text: 'Edit + Delete Comments', value: 'CM_MANAGE', icon: 'insert_comment' }
],
matches: [
{ text: 'Path Starts With...', value: 'START', icon: '/...' },
{ text: 'Path is Exactly...', value: 'EXACT', icon: '=' },
{ text: 'Path Ends With...', value: 'END', icon: '.../' },
{ text: 'Path Matches Regex...', value: 'REGEX', icon: '$.*' }
],
locales: [
{ text: 'English', value: 'en' },
{ text: 'Français', value: 'fr' },
]
}
},
computed: {
group: {
get() { return this.value },
set(val) { this.$set('input', val) }
}
},
methods: {
addRule(group) {
this.group.pageRules.push({
id: nanoid('1234567890abcdef', 10),
path: '',
roles: [],
match: 'START',
deny: false,
locales: []
})
},
removeRule(rule) {
this.group.pageRules.splice(_.findIndex(this.group.pageRules, ['id', rule.id]), 1)
},
comingSoon() {
this.$store.commit('showNotification', {
style: 'indigo',
message: `Coming soon...`,
icon: 'directions_boat'
})
}
}
}
</script>
<style lang="scss">
.rules {
background-color: mc('blue-grey', '50');
border-radius: 4px;
padding: 1rem;
position: relative;
@at-root .theme--dark & {
background-color: mc('grey', '800');
}
}
.rule {
display: flex;
background-color: mc('blue-grey', '100');
border-radius: 4px;
padding: .5rem;
&-enter-active, &-leave-active {
transition: all .5s ease;
}
&-enter, &-leave-to {
opacity: 0;
}
@at-root .theme--dark & {
background-color: mc('grey', '700');
}
& + .rule {
margin-top: .5rem;
position: relative;
&::before {
content: '+';
position: absolute;
width: 2rem;
height: 2rem;
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
font-weight: 600;
color: mc('blue-grey', '700');
font-size: 1.25rem;
background-color: mc('blue-grey', '50');
left: -2rem;
top: -1.3rem;
@at-root .theme--dark & {
background-color: mc('grey', '800');
color: mc('grey', '600');
}
}
}
.input-group + * {
margin-left: .5rem;
}
&-deny-btn {
height: 48px;
border-radius: 2px 0 0 2px;
min-width: 0;
}
}
</style>

View File

@@ -0,0 +1,147 @@
<template lang="pug">
v-card.wiki-form
v-card-title(:class='$vuetify.dark ? `grey darken-3-d3` : `grey lighten-5`')
v-text-field(
outline
flat
prepend-inner-icon='search'
v-model='search'
label='Search Group Users...'
hide-details
)
v-spacer
v-btn(color='primary', depressed, @click='searchUserDialog = true', :disabled='group.id === 2')
v-icon(left) assignment_ind
| Assign User
v-data-table(
:items='group.users',
:headers='headers',
:search='search'
:pagination.sync='pagination',
:rows-per-page-items='[15]'
hide-actions
)
template(slot='items', slot-scope='props')
tr(:active='props.selected')
td.text-xs-right {{ props.item.id }}
td {{ props.item.name }}
td {{ props.item.email }}
td
v-menu(bottom, right, min-width='200')
v-btn(icon, slot='activator'): v-icon.grey--text.text--darken-1 more_horiz
v-list
v-list-tile(:to='`/users/` + props.item.id')
v-list-tile-action: v-icon(color='primary') person
v-list-tile-content
v-list-tile-title View User Profile
template(v-if='props.item.id !== 2')
v-divider
v-list-tile(@click='unassignUser(props.item.id)')
v-list-tile-action: v-icon(color='orange') highlight_off
v-list-tile-content
v-list-tile-title Unassign
template(slot='no-data')
v-alert.ma-3(icon='warning', :value='true', outline) No users to display.
.text-xs-center.py-2(v-if='group.users.length > 15')
v-pagination(v-model='pagination.page', :length='pages')
user-search(v-model='searchUserDialog', @select='assignUser')
</template>
<script>
import UserSearch from '../common/user-search.vue'
import assignUserMutation from 'gql/admin/groups/groups-mutation-assign.gql'
import unassignUserMutation from 'gql/admin/groups/groups-mutation-unassign.gql'
export default {
props: {
value: {
type: Object
}
},
components: {
UserSearch
},
data() {
return {
headers: [
{ text: 'ID', value: 'id', width: 50, align: 'right' },
{ text: 'Name', value: 'name' },
{ text: 'Email', value: 'email' },
{ text: '', value: 'actions', sortable: false, width: 50 }
],
searchUserDialog: false,
pagination: {},
search: ''
}
},
computed: {
group: {
get() { return this.value },
set(val) { this.$set('input', val) }
},
pages () {
if (this.pagination.rowsPerPage == null || this.pagination.totalItems == null) {
return 0
}
return Math.ceil(this.pagination.totalItems / this.pagination.rowsPerPage)
}
},
methods: {
async assignUser(id) {
try {
await this.$apollo.mutate({
mutation: assignUserMutation,
variables: {
groupId: this.group.id,
userId: id
},
watchLoading (isLoading) {
this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'admin-groups-assign')
}
})
this.$store.commit('showNotification', {
style: 'success',
message: `User has been assigned to ${this.group.name}.`,
icon: 'assignment_ind'
})
this.$emit('refresh')
} catch (err) {
this.$store.commit('showNotification', {
style: 'red',
message: err.message,
icon: 'warning'
})
}
},
async unassignUser(id) {
try {
await this.$apollo.mutate({
mutation: unassignUserMutation,
variables: {
groupId: this.group.id,
userId: id
},
watchLoading (isLoading) {
this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'admin-groups-unassign')
}
})
this.$store.commit('showNotification', {
style: 'success',
message: `User has been unassigned from ${this.group.name}.`,
icon: 'assignment_ind'
})
this.$emit('refresh')
} catch (err) {
this.$store.commit('showNotification', {
style: 'red',
message: err.message,
icon: 'warning'
})
}
}
}
}
</script>

View File

@@ -6,130 +6,57 @@
img(src='/svg/icon-social-group.svg', alt='Edit Group', style='width: 80px;')
.admin-header-title
.headline.blue--text.text--darken-2 Edit Group
.subheading.grey--text {{name}}
.subheading.grey--text {{group.name}}
v-spacer
.caption.grey--text ID #[strong {{group.id}}]
v-divider.mx-3(vertical)
v-btn(color='indigo', large, outline, to='/groups')
v-btn(color='grey', large, outline, to='/groups')
v-icon arrow_back
v-dialog(v-model='deleteGroupDialog', max-width='500', v-if='!group.isSystem')
v-btn(color='red', large, outline, slot='activator')
v-icon(color='red') delete
v-card
.dialog-header.is-red Delete Group?
v-card-text Are you sure you want to delete group #[strong {{ name }}]? All users will be unassigned from this group.
v-card-text Are you sure you want to delete group #[strong {{ group.name }}]? All users will be unassigned from this group.
v-card-actions
v-spacer
v-btn(flat, @click='deleteGroupDialog = false') Cancel
v-btn(color='red', dark, @click='deleteGroup') Delete
v-btn(color='primary', large, depressed, @click='updateGroup')
v-btn(color='success', large, depressed, @click='updateGroup')
v-icon(left) check
span Update Group
v-card.mt-3
v-tabs(v-model='tab', :color='$vuetify.dark ? "primary" : "grey darken-2"', fixed-tabs, slider-color='white', show-arrows, dark)
v-tab(key='properties') Properties
v-tab(key='permissions') Permissions
v-tab(key='rules') Page Rules
v-tab(key='users') Users
v-tab-item(key='properties', :transition='false', :reverse-transition='false')
v-card
v-card-text
v-text-field(
outline
background-color='grey lighten-3'
v-model='name'
label='Group Name'
counter='255'
prepend-icon='people'
)
v-tab-item(key='permissions', :transition='false', :reverse-transition='false')
v-container.pa-3(fluid, grid-list-md)
v-layout(row, wrap)
v-flex(xs12, md6, lg4, v-for='pmGroup in permissions')
v-card.md2.grey(flat, :class='$vuetify.dark ? "darken-4" : "lighten-5"')
v-subheader {{pmGroup.category}}
v-card-text.pt-0
template(v-for='(pm, idx) in pmGroup.items')
v-checkbox.pt-0(
:key='pm.permission'
:label='pm.permission'
:hint='pm.hint'
persistent-hint
color='primary'
v-model='group.permissions'
:value='pm.permission'
:append-icon='pm.warning ? "warning" : null',
:disabled='(group.isSystem && pm.restrictedForSystem) || group.id === 1 || pm.disabled'
)
v-divider.mt-3(v-if='idx < pmGroup.items.length - 1')
group-permissions(v-model='group', @refresh='refresh')
v-tab-item(key='rules', :transition='false', :reverse-transition='false')
v-card
v-card-title.pb-0
v-spacer
v-btn(flat, outline)
v-icon(left) arrow_drop_down
| Load Preset
v-btn(flat, outline)
v-icon(left) vertical_align_bottom
| Import Rules
.pa-3.pl-4
criterias
group-rules(v-model='group', @refresh='refresh')
v-tab-item(key='users', :transition='false', :reverse-transition='false')
v-card
v-card-title.pb-0
v-spacer
v-btn(color='primary', outline, flat, @click='searchUserDialog = true')
v-icon(left) assignment_ind
| Assign User
v-data-table(
:items='group.users',
:headers='headers',
:search='search',
:pagination.sync='pagination',
:rows-per-page-items='[15]'
hide-actions
)
template(slot='items', slot-scope='props')
tr(:active='props.selected')
td.text-xs-right {{ props.item.id }}
td {{ props.item.name }}
td {{ props.item.email }}
td
v-menu(bottom, right, min-width='200')
v-btn(icon, slot='activator'): v-icon.grey--text.text--darken-1 more_horiz
v-list
v-list-tile(@click='unassignUser(props.item.id)')
v-list-tile-action: v-icon(color='orange') highlight_off
v-list-tile-content
v-list-tile-title Unassign
template(slot='no-data')
v-alert.ma-3(icon='warning', :value='true', outline) No users to display.
.text-xs-center.py-2(v-if='users.length > 15')
v-pagination(v-model='pagination.page', :length='pages')
user-search(v-model='searchUserDialog', @select='assignUser')
group-users(v-model='group', @refresh='refresh')
</template>
<script>
import _ from 'lodash'
import Criterias from '../common/criterias.vue'
import UserSearch from '../common/user-search.vue'
import GroupPermissions from './admin-groups-edit-permissions.vue'
import GroupRules from './admin-groups-edit-rules.vue'
import GroupUsers from './admin-groups-edit-users.vue'
import groupQuery from 'gql/admin/groups/groups-query-single.gql'
import assignUserMutation from 'gql/admin/groups/groups-mutation-assign.gql'
import deleteGroupMutation from 'gql/admin/groups/groups-mutation-delete.gql'
import unassignUserMutation from 'gql/admin/groups/groups-mutation-unassign.gql'
import updateGroupMutation from 'gql/admin/groups/groups-mutation-update.gql'
export default {
components: {
Criterias,
UserSearch
GroupPermissions,
GroupRules,
GroupUsers
},
data() {
return {
@@ -141,158 +68,10 @@ export default {
pageRules: [],
users: []
},
name: '',
deleteGroupDialog: false,
searchUserDialog: false,
pagination: {},
permissions: [
{
category: 'Content',
items: [
{
permission: 'read:pages',
hint: 'Can view pages, as specified in the Page Rules',
warning: false,
restrictedForSystem: false,
disabled: false
},
{
permission: 'write:pages',
hint: 'Can view and create new pages, as specified in the Page Rules',
warning: false,
restrictedForSystem: false,
disabled: false
},
{
permission: 'manage:pages',
hint: 'Can view, create, edit and move existing pages as specified in the Page Rules',
warning: false,
restrictedForSystem: false,
disabled: false
},
{
permission: 'delete:pages',
hint: 'Can delete existing pages, as specified in the Page Rules',
warning: false,
restrictedForSystem: false,
disabled: false
},
{
permission: 'write:assets',
hint: 'Can upload assets (such as images and files), as specified in the Page Rules',
warning: false,
restrictedForSystem: false,
disabled: false
},
{
permission: 'read:comments',
hint: 'Can view comments, as specified in the Page Rules',
warning: false,
restrictedForSystem: false,
disabled: false
},
{
permission: 'write:comments',
hint: 'Can post new comments, as specified in the Page Rules',
warning: false,
restrictedForSystem: false,
disabled: false
}
]
},
{
category: 'Users',
items: [
{
permission: 'write:users',
hint: 'Can create or authorize new users, but not modify existing ones',
warning: false,
restrictedForSystem: true,
disabled: false
},
{
permission: 'manage:users',
hint: 'Can manage all users (but not users with administrative permissions)',
warning: false,
restrictedForSystem: true,
disabled: false
},
{
permission: 'write:groups',
hint: 'Can manage groups and assign CONTENT permissions / page rules',
warning: false,
restrictedForSystem: true,
disabled: false
},
{
permission: 'manage:groups',
hint: 'Can manage groups and assign ANY permissions (but not manage:system) / page rules',
warning: true,
restrictedForSystem: true,
disabled: false
}
]
},
{
category: 'Administration',
items: [
{
permission: 'manage:navigation',
hint: 'Can manage the site navigation',
warning: false,
restrictedForSystem: true,
disabled: false
},
{
permission: 'manage:theme',
hint: 'Can manage and modify themes',
warning: false,
restrictedForSystem: true,
disabled: false
},
{
permission: 'manage:api',
hint: 'Can generate and revoke API keys',
warning: true,
restrictedForSystem: true,
disabled: false
},
{
permission: 'manage:system',
hint: 'Can manage and access everything. Root administrator.',
warning: true,
restrictedForSystem: true,
disabled: true
}
]
}
],
users: [],
headers: [
{ text: 'ID', value: 'id', width: 50, align: 'right' },
{ text: 'Name', value: 'name' },
{ text: 'Email', value: 'email' },
{ text: '', value: 'actions', sortable: false, width: 50 }
],
search: '',
tab: '1'
}
},
computed: {
pages () {
if (this.pagination.rowsPerPage == null || this.pagination.totalItems == null) {
return 0
}
return Math.ceil(this.pagination.totalItems / this.pagination.rowsPerPage)
}
},
watch: {
group(newValue, oldValue) {
this.name = newValue.name
}
},
methods: {
async updateGroup() {
try {
@@ -300,7 +79,9 @@ export default {
mutation: updateGroupMutation,
variables: {
id: this.group.id,
name: this.name
name: this.group.name,
permissions: this.group.permissions,
pageRules: this.group.pageRules
},
watchLoading (isLoading) {
this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'admin-groups-update')
@@ -345,57 +126,8 @@ export default {
})
}
},
async assignUser(id) {
try {
await this.$apollo.mutate({
mutation: assignUserMutation,
variables: {
groupId: this.group.id,
userId: id
},
watchLoading (isLoading) {
this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'admin-groups-assign')
}
})
this.$store.commit('showNotification', {
style: 'success',
message: `User has been assigned to ${this.group.name}.`,
icon: 'assignment_ind'
})
this.$apollo.queries.group.refetch()
} catch (err) {
this.$store.commit('showNotification', {
style: 'red',
message: err.message,
icon: 'warning'
})
}
},
async unassignUser(id) {
try {
await this.$apollo.mutate({
mutation: unassignUserMutation,
variables: {
groupId: this.group.id,
userId: id
},
watchLoading (isLoading) {
this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'admin-groups-unassign')
}
})
this.$store.commit('showNotification', {
style: 'success',
message: `User has been unassigned from ${this.group.name}.`,
icon: 'assignment_ind'
})
this.$apollo.queries.group.refetch()
} catch (err) {
this.$store.commit('showNotification', {
style: 'red',
message: err.message,
icon: 'warning'
})
}
async refresh() {
return this.$apollo.queries.group.refetch()
}
},
apollo: {
@@ -407,7 +139,7 @@ export default {
}
},
fetchPolicy: 'network-only',
update: (data) => data.groups.single,
update: (data) => _.cloneDeep(data.groups.single),
watchLoading (isLoading) {
this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'admin-groups-refresh')
}

View File

@@ -14,19 +14,18 @@
v-btn(color='primary', depressed, slot='activator', large)
v-icon(left) add
span New Group
v-card
v-card.wiki-form
.dialog-header.is-short New Group
v-card-text
v-text-field.md2(
solo,
flat,
background-color='grey lighten-4'
outline
background-color='grey lighten-3'
prepend-icon='people'
v-model='newGroupName'
label='Group Name'
counter='255'
@keyup.enter='createGroup'
ref='groupNameInput'
ref='groupNameIpt'
)
v-card-chin
v-spacer
@@ -44,7 +43,7 @@
template(slot='items', slot-scope='props')
tr.is-clickable(:active='props.selected', @click='$router.push("/groups/" + props.item.id)')
td.text-xs-right {{ props.item.id }}
td {{ props.item.name }}
td: strong {{ props.item.name }}
td {{ props.item.userCount }}
td {{ props.item.createdAt | moment('calendar') }}
td {{ props.item.updatedAt | moment('calendar') }}
@@ -93,6 +92,15 @@ export default {
return Math.ceil(this.pagination.totalItems / this.pagination.rowsPerPage)
}
},
watch: {
newGroupDialog(newValue, oldValue) {
if (newValue) {
this.$nextTick(() => {
this.$refs.groupNameIpt.focus()
})
}
}
},
methods: {
async refresh() {
await this.$apollo.queries.groups.refetch()
@@ -103,6 +111,14 @@ export default {
})
},
async createGroup() {
if (_.trim(this.newGroupName).length < 1) {
this.$store.commit('showNotification', {
style: 'red',
message: 'Enter a group name.',
icon: 'warning'
})
return
}
this.newGroupDialog = false
try {
await this.$apollo.mutate({
@@ -138,36 +154,6 @@ export default {
icon: 'warning'
})
}
},
async deleteGroupConfirm(group) {
this.deleteGroupDialog = true
this.selectedGroup = group
},
async deleteGroup() {
this.deleteGroupDialog = false
try {
await this.$apollo.mutate({
mutation: deleteGroupMutation,
variables: {
id: this.selectedGroup.id
},
watchLoading (isLoading) {
this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'admin-groups-delete')
}
})
await this.$apollo.queries.groups.refetch()
this.$store.commit('showNotification', {
style: 'success',
message: `Group ${this.selectedGroup.name} has been deleted.`,
icon: 'delete'
})
} catch (err) {
this.$store.commit('showNotification', {
style: 'red',
message: err.message,
icon: 'warning'
})
}
}
},
apollo: {

View File

@@ -33,10 +33,11 @@
v-subheader {{ $t('admin:system.hostInfo') }}
v-list-tile(avatar)
v-list-tile-avatar
v-icon.blue-grey.white--text bubble_chart
v-avatar.blue-grey(size='40')
img(:src='`/svg/icon-` + platformLogo + `-logo.svg`', alt='Platform', style='width: 24px;')
v-list-tile-content
v-list-tile-title {{ $t('admin:system.os') }}
v-list-tile-sub-title {{ info.operatingSystem }}
v-list-tile-sub-title {{ (info.platform === 'docker') ? 'Docker Container (Linux)' : info.operatingSystem }}
v-list-tile(avatar)
v-list-tile-avatar
v-icon.blue-grey.white--text computer
@@ -127,6 +128,20 @@ export default {
computed: {
dbVersion() {
return _.get(this.info, 'dbVersion', '').replace(/(?:\r\n|\r|\n)/g, '<br />')
},
platformLogo() {
switch (this.info.platform) {
case 'docker':
return 'docker'
case 'darwin':
return 'apple'
case 'linux':
return 'linux'
case 'win32':
return 'windows'
default:
return ''
}
}
},
methods: {

View File

@@ -1,29 +1,23 @@
<template lang="pug">
v-dialog(v-model='isShown', max-width='550')
v-card
v-card.wiki-form
.dialog-header.is-short New Local User
v-card-text
v-text-field.md2(
solo
flat
background-color='grey lighten-4'
outline
prepend-icon='email'
v-model='email'
label='Email Address'
ref='emailInput'
)
v-text-field.md2(
solo
flat
background-color='grey lighten-4'
outline
prepend-icon='person'
v-model='name'
label='Name'
)
v-text-field.md2(
solo
flat
background-color='grey lighten-4'
outline
prepend-icon='lock'
append-icon='casino'
v-model='password'
@@ -31,32 +25,10 @@
counter='255'
@click:append='generatePwd'
)
v-text-field.md2(
solo
flat
background-color='grey lighten-4'
prepend-icon='title'
v-model='jobTitle'
label='Job Title'
counter='255'
hint='Optional'
persistent-hint
)
v-text-field.md2(
solo
flat
background-color='grey lighten-4'
prepend-icon='public'
v-model='location'
label='Location'
counter='255'
hint='Optional'
persistent-hint
)
v-card-chin
v-spacer
v-btn(flat, @click='isShown = false') Cancel
v-btn(color='primary', @click='createUser') Create
v-btn(color='primary', @click='createUser') Create User
</template>
<script>