feat: admin - manage groups + permissions + page rules
This commit is contained in:
@@ -9,13 +9,13 @@
|
||||
v-list-tile-title {{ $t('admin:dashboard.title') }}
|
||||
v-divider.my-2
|
||||
v-subheader.pl-4 {{ $t('admin:nav.site') }}
|
||||
v-list-tile(to='/general')
|
||||
v-list-tile(to='/general', v-if='hasPermission(`manage:system`)')
|
||||
v-list-tile-avatar: v-icon widgets
|
||||
v-list-tile-title {{ $t('admin:general.title') }}
|
||||
v-list-tile(to='/locale')
|
||||
v-list-tile(to='/locale', v-if='hasPermission(`manage:system`)')
|
||||
v-list-tile-avatar: v-icon language
|
||||
v-list-tile-title {{ $t('admin:locale.title') }}
|
||||
v-list-tile(to='/navigation')
|
||||
v-list-tile(to='/navigation', v-if='hasPermission([`manage:system`, `manage:navigation`])')
|
||||
v-list-tile-avatar: v-icon near_me
|
||||
v-list-tile-title {{ $t('admin:navigation.title') }}
|
||||
v-list-tile(to='/pages')
|
||||
@@ -24,7 +24,7 @@
|
||||
v-list-tile-action
|
||||
v-chip(small, disabled, :color='darkMode ? `grey darken-3-d4` : `grey lighten-4`')
|
||||
.caption.grey--text {{ info.pagesTotal }}
|
||||
v-list-tile(to='/theme')
|
||||
v-list-tile(to='/theme', v-if='hasPermission([`manage:system`, `manage:theme`])')
|
||||
v-list-tile-avatar: v-icon palette
|
||||
v-list-tile-title {{ $t('admin:theme.title') }}
|
||||
v-divider.my-2
|
||||
@@ -41,44 +41,46 @@
|
||||
v-list-tile-action
|
||||
v-chip(small, disabled, :color='darkMode ? `grey darken-3-d4` : `grey lighten-4`')
|
||||
.caption.grey--text {{ info.usersTotal }}
|
||||
v-divider.my-2
|
||||
v-subheader.pl-4 {{ $t('admin:nav.modules') }}
|
||||
v-list-tile(to='/auth')
|
||||
v-list-tile-avatar: v-icon lock_outline
|
||||
v-list-tile-title {{ $t('admin:auth.title') }}
|
||||
v-list-tile(to='/editor')
|
||||
v-list-tile-avatar: v-icon transform
|
||||
v-list-tile-title {{ $t('admin:editor.title') }}
|
||||
v-list-tile(to='/logging')
|
||||
v-list-tile-avatar: v-icon graphic_eq
|
||||
v-list-tile-title {{ $t('admin:logging.title') }}
|
||||
v-list-tile(to='/rendering')
|
||||
v-list-tile-avatar: v-icon system_update_alt
|
||||
v-list-tile-title {{ $t('admin:rendering.title') }}
|
||||
v-list-tile(to='/search')
|
||||
v-list-tile-avatar: v-icon search
|
||||
v-list-tile-title {{ $t('admin:search.title') }}
|
||||
v-list-tile(to='/storage')
|
||||
v-list-tile-avatar: v-icon storage
|
||||
v-list-tile-title {{ $t('admin:storage.title') }}
|
||||
v-divider.my-2
|
||||
v-subheader.pl-4 {{ $t('admin:nav.system') }}
|
||||
v-list-tile(to='/api')
|
||||
v-list-tile-avatar: v-icon call_split
|
||||
v-list-tile-title {{ $t('admin:api.title') }}
|
||||
v-list-tile(to='/mail')
|
||||
v-list-tile-avatar: v-icon email
|
||||
v-list-tile-title {{ $t('admin:mail.title') }}
|
||||
v-list-tile(to='/system')
|
||||
v-list-tile-avatar: v-icon tune
|
||||
v-list-tile-title {{ $t('admin:system.title') }}
|
||||
v-list-tile(to='/utilities')
|
||||
v-list-tile-avatar: v-icon build
|
||||
v-list-tile-title {{ $t('admin:utilities.title') }}
|
||||
v-list-tile(to='/dev')
|
||||
v-list-tile-avatar: v-icon weekend
|
||||
v-list-tile-title {{ $t('admin:dev.title') }}
|
||||
v-divider.my-2
|
||||
template(v-if='hasPermission(`manage:system`)')
|
||||
v-divider.my-2
|
||||
v-subheader.pl-4 {{ $t('admin:nav.modules') }}
|
||||
v-list-tile(to='/auth')
|
||||
v-list-tile-avatar: v-icon lock_outline
|
||||
v-list-tile-title {{ $t('admin:auth.title') }}
|
||||
v-list-tile(to='/editor')
|
||||
v-list-tile-avatar: v-icon transform
|
||||
v-list-tile-title {{ $t('admin:editor.title') }}
|
||||
v-list-tile(to='/logging')
|
||||
v-list-tile-avatar: v-icon graphic_eq
|
||||
v-list-tile-title {{ $t('admin:logging.title') }}
|
||||
v-list-tile(to='/rendering')
|
||||
v-list-tile-avatar: v-icon system_update_alt
|
||||
v-list-tile-title {{ $t('admin:rendering.title') }}
|
||||
v-list-tile(to='/search')
|
||||
v-list-tile-avatar: v-icon search
|
||||
v-list-tile-title {{ $t('admin:search.title') }}
|
||||
v-list-tile(to='/storage')
|
||||
v-list-tile-avatar: v-icon storage
|
||||
v-list-tile-title {{ $t('admin:storage.title') }}
|
||||
v-divider.my-2
|
||||
template(v-if='hasPermission([`manage:system`, `manage:api`])')
|
||||
v-subheader.pl-4 {{ $t('admin:nav.system') }}
|
||||
v-list-tile(to='/api', v-if='hasPermission([`manage:system`, `manage:api`])')
|
||||
v-list-tile-avatar: v-icon call_split
|
||||
v-list-tile-title {{ $t('admin:api.title') }}
|
||||
v-list-tile(to='/mail', v-if='hasPermission(`manage:system`)')
|
||||
v-list-tile-avatar: v-icon email
|
||||
v-list-tile-title {{ $t('admin:mail.title') }}
|
||||
v-list-tile(to='/system', v-if='hasPermission(`manage:system`)')
|
||||
v-list-tile-avatar: v-icon tune
|
||||
v-list-tile-title {{ $t('admin:system.title') }}
|
||||
v-list-tile(to='/utilities', v-if='hasPermission(`manage:system`)')
|
||||
v-list-tile-avatar: v-icon build
|
||||
v-list-tile-title {{ $t('admin:utilities.title') }}
|
||||
v-list-tile(to='/dev', v-if='hasPermission([`manage:system`, `manage:api`])')
|
||||
v-list-tile-avatar: v-icon weekend
|
||||
v-list-tile-title {{ $t('admin:dev.title') }}
|
||||
v-divider.my-2
|
||||
v-list-tile(to='/contribute')
|
||||
v-list-tile-avatar: v-icon favorite
|
||||
v-list-tile-title {{ $t('admin:contribute.title') }}
|
||||
@@ -91,6 +93,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import _ from 'lodash'
|
||||
import VueRouter from 'vue-router'
|
||||
import { get, sync } from 'vuex-pathify'
|
||||
|
||||
@@ -161,12 +164,24 @@ export default {
|
||||
},
|
||||
computed: {
|
||||
darkMode: get('site/dark'),
|
||||
info: sync('admin/info')
|
||||
info: sync('admin/info'),
|
||||
permissions: get('user/permissions')
|
||||
},
|
||||
router,
|
||||
created() {
|
||||
this.$store.commit('page/SET_MODE', 'admin')
|
||||
},
|
||||
methods: {
|
||||
hasPermission(prm) {
|
||||
if (_.isArray(prm)) {
|
||||
return _.some(prm, p => {
|
||||
return _.includes(this.permissions, p)
|
||||
})
|
||||
} else {
|
||||
return _.includes(this.permissions, prm)
|
||||
}
|
||||
}
|
||||
},
|
||||
apollo: {
|
||||
info: {
|
||||
query: statsQuery,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"'
|
||||
|
||||
203
client/components/admin/admin-groups-edit-permissions.vue
Normal file
203
client/components/admin/admin-groups-edit-permissions.vue
Normal 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>
|
||||
302
client/components/admin/admin-groups-edit-rules.vue
Normal file
302
client/components/admin/admin-groups-edit-rules.vue
Normal 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>
|
||||
147
client/components/admin/admin-groups-edit-users.vue
Normal file
147
client/components/admin/admin-groups-edit-users.vue
Normal 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>
|
||||
@@ -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')
|
||||
}
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -1,211 +0,0 @@
|
||||
<template lang="pug">
|
||||
.criterias-item
|
||||
//- Type
|
||||
v-select(
|
||||
solo
|
||||
:items='filteredCriteriaTypes'
|
||||
v-model='item.type'
|
||||
placeholder='Rule Type'
|
||||
ref='typeSelect'
|
||||
hide-details
|
||||
)
|
||||
template(slot='item', slot-scope='data')
|
||||
v-list-tile-avatar
|
||||
v-avatar(:color='data.item.color', size='40', tile): v-icon(color='white') {{ data.item.icon }}
|
||||
v-list-tile-content
|
||||
v-list-tile-title(v-html='data.item.text')
|
||||
v-list-tile-sub-title.caption(v-html='data.item.description')
|
||||
|
||||
//- Operator
|
||||
v-select(
|
||||
solo
|
||||
:items='filteredCriteriaOperators'
|
||||
v-model='item.operator'
|
||||
placeholder='Operator'
|
||||
:disabled='!item.type'
|
||||
:class='!item.type ? "blue-grey lighten-4" : ""'
|
||||
hide-details
|
||||
)
|
||||
template(slot='item', slot-scope='data')
|
||||
v-list-tile-avatar
|
||||
v-avatar.white--text(color='blue', size='30', tile) {{ data.item.icon }}
|
||||
v-list-tile-content
|
||||
v-list-tile-title(v-html='data.item.text')
|
||||
|
||||
//- Value
|
||||
v-select(
|
||||
v-if='item.type === "country"'
|
||||
solo
|
||||
:items='countries'
|
||||
v-model='item.value'
|
||||
placeholder='Countries...'
|
||||
multiple
|
||||
item-text='name'
|
||||
item-value='code'
|
||||
hide-details
|
||||
)
|
||||
v-text-field(
|
||||
v-else-if='item.type === "path"'
|
||||
solo
|
||||
v-model='item.value'
|
||||
label='Path (e.g. /section)'
|
||||
hide-details
|
||||
)
|
||||
v-text-field(
|
||||
v-else-if='item.type === "date"'
|
||||
solo
|
||||
@click.native.stop='dateActivator = true'
|
||||
v-model='item.value'
|
||||
label='YYYY-MM-DD'
|
||||
readonly
|
||||
hide-details
|
||||
)
|
||||
v-text-field(
|
||||
v-else-if='item.type === "time"'
|
||||
solo
|
||||
@click.native.stop='timeActivator = true'
|
||||
v-model='item.value'
|
||||
label='HH:MM'
|
||||
readonly
|
||||
hide-details
|
||||
)
|
||||
v-select(
|
||||
v-else-if='item.type === "group"'
|
||||
solo
|
||||
:items='groups'
|
||||
v-model='item.value'
|
||||
placeholder='Group...'
|
||||
item-text='name'
|
||||
item-value='id'
|
||||
hide-details
|
||||
)
|
||||
v-text-field.blue-grey.lighten-4(
|
||||
v-else
|
||||
solo
|
||||
disabled
|
||||
hide-details
|
||||
)
|
||||
|
||||
v-dialog(lazy, v-model='dateActivator', width='290px', ref='dateDialog')
|
||||
v-date-picker(v-model='item.value', scrollable, color='primary')
|
||||
v-btn(flat, color='primary' @click='$refs.dateDialog.save(date)', block) ok
|
||||
|
||||
v-dialog(lazy, v-model='timeActivator', width='300px', ref='timeDialog')
|
||||
v-time-picker(v-model='item.value', scrollable, color='primary')
|
||||
v-btn(flat, color='primary' @click='$refs.timeDialog.save(time)', block) ok
|
||||
|
||||
v-btn(icon, @click='remove'): v-icon(color='blue-grey') clear
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import _ from 'lodash'
|
||||
|
||||
// import countriesQuery from 'gql/upsells-query-countries.gql'
|
||||
|
||||
export default {
|
||||
inject: ['allowedCriteriaTypes'],
|
||||
props: {
|
||||
value: {
|
||||
type: Object,
|
||||
default() { return {} }
|
||||
},
|
||||
groupIndex: {
|
||||
type: Number,
|
||||
default() { return 0 }
|
||||
},
|
||||
itemIndex: {
|
||||
type: Number,
|
||||
default() { return 0 }
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
item: {
|
||||
operator: '',
|
||||
type: '',
|
||||
value: ''
|
||||
},
|
||||
dateActivator: false,
|
||||
dateDialog: false,
|
||||
timeActivator: false,
|
||||
timeDialog: false,
|
||||
countries: [],
|
||||
groups: [],
|
||||
criteriaTypes: [
|
||||
{ text: 'Path', value: 'path', icon: 'space_bar', color: 'blue', description: 'Match the path of the document being viewed.' },
|
||||
{ text: 'Date', value: 'date', icon: 'date_range', color: 'blue', description: 'Match the current calendar day.' },
|
||||
{ text: 'Time', value: 'time', icon: 'access_time', color: 'blue', description: 'Match the current time of day.' },
|
||||
{ text: 'User Country', value: 'country', icon: 'public', color: 'red', description: `Match the user's country.` },
|
||||
{ text: 'User Group', value: 'group', icon: 'group', color: 'orange', description: 'Match the user group assignments.' }
|
||||
],
|
||||
criteriaOperators: {
|
||||
country: [
|
||||
{ text: 'In', value: 'in', icon: '[...]' },
|
||||
{ text: 'Not In', value: 'notIn', icon: '[ x ]' }
|
||||
],
|
||||
path: [
|
||||
{ text: 'Matches Exactly', value: 'eq', icon: '=' },
|
||||
{ text: 'NOT Matches Exactly', value: 'ne', icon: '!=' },
|
||||
{ text: 'Starts With', value: 'sw', icon: 'x...' },
|
||||
{ text: 'NOT Starts With', value: 'nsw', icon: '!x...' },
|
||||
{ text: 'Ends With', value: 'ew', icon: '...x' },
|
||||
{ text: 'NOT Ends With', value: 'new', icon: '!...x' },
|
||||
{ text: 'Matches Regex', value: 'regexp', icon: '^x$' }
|
||||
],
|
||||
date: [
|
||||
{ text: 'On or After', value: 'gte', icon: '>=' },
|
||||
{ text: 'On or Before', value: 'lte', icon: '<=' }
|
||||
],
|
||||
time: [
|
||||
{ text: 'At or Later Than', value: 'gte', icon: '>=' },
|
||||
{ text: 'At or Before', value: 'lte', icon: '<=' }
|
||||
],
|
||||
group: [
|
||||
{ text: 'Is Part Of', value: 'in', icon: '[...]' },
|
||||
{ text: 'Is Not Part Of', value: 'notIn', icon: '[ x ]' }
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
filteredCriteriaOperators() {
|
||||
return _.get(this.criteriaOperators, this.item.type, [])
|
||||
},
|
||||
filteredCriteriaTypes() {
|
||||
console.info(this.allowedCriteriaTypes)
|
||||
return _.filter(this.criteriaTypes, c => _.includes(this.allowedCriteriaTypes, c.value))
|
||||
},
|
||||
itemType() {
|
||||
return this.item.type
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
itemType(newValue, oldValue) {
|
||||
this.item.operator = _.head(this.criteriaOperators[newValue]).value
|
||||
this.item.value = ''
|
||||
},
|
||||
item: {
|
||||
handler(newValue, oldValue) {
|
||||
this.$emit('update', this.groupIndex, this.itemIndex, this.item)
|
||||
},
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
if (!this.item.type) {
|
||||
this.$refs.typeSelect.showMenu()
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
remove() {
|
||||
this.$emit('remove', this.groupIndex, this.itemIndex)
|
||||
}
|
||||
}
|
||||
// apollo: {
|
||||
// countries: {
|
||||
// query: countriesQuery,
|
||||
// update: (data) => data.location.countries
|
||||
// }
|
||||
// }
|
||||
}
|
||||
</script>
|
||||
@@ -1,173 +0,0 @@
|
||||
<template lang="pug">
|
||||
.criterias
|
||||
transition-group(name='criterias-group', tag='div')
|
||||
.criterias-group(v-for='(group, g) in groups', :key='g')
|
||||
transition-group(name='criterias-item', tag='div')
|
||||
criterias-item(v-for='(item, i) in group', :key='i', :item='item', :group-index='g', :item-index='i', @update='updateItem', @remove='removeItem')
|
||||
.criterias-item-more
|
||||
v-btn.ml-0(@click='addItem(group)', small, color='blue-grey lighten-2', dark, depressed)
|
||||
v-icon(color='white', left) add
|
||||
| Add condition
|
||||
.criterias-group-more
|
||||
v-btn(@click='addGroup', small, color='blue-grey lighten-1', dark, depressed)
|
||||
v-icon(color='white', left) add
|
||||
| Add condition group
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import CriteriasItem from './criterias-item.vue'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
CriteriasItem
|
||||
},
|
||||
props: {
|
||||
value: {
|
||||
type: Array,
|
||||
default() { return [] }
|
||||
},
|
||||
types: {
|
||||
type: Array,
|
||||
default() {
|
||||
return ['country', 'path', 'date', 'time', 'group']
|
||||
}
|
||||
}
|
||||
},
|
||||
provide () {
|
||||
return {
|
||||
allowedCriteriaTypes: this.types
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
dataGroups: this.value || []
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
groups: {
|
||||
get() { return this.dataGroups },
|
||||
set(grp) {
|
||||
this.dataGroups = grp
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
dataGroups(newValue, oldValue) {
|
||||
if (newValue !== oldValue) {
|
||||
this.$emit('input', newValue)
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
addGroup() {
|
||||
this.dataGroups.push([{}])
|
||||
},
|
||||
addItem(group) {
|
||||
group.push({})
|
||||
},
|
||||
updateItem(groupIndex, itemIndex, item) {
|
||||
console.info(item)
|
||||
this.$set(this.dataGroups[groupIndex], itemIndex, item)
|
||||
},
|
||||
removeItem(groupIndex, itemIndex) {
|
||||
this.dataGroups[groupIndex].splice(itemIndex, 1)
|
||||
if (this.dataGroups[groupIndex].length < 1) {
|
||||
this.dataGroups.splice(groupIndex, 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.criterias {
|
||||
&-group {
|
||||
background-color: mc('blue-grey', '100');
|
||||
border-radius: 4px;
|
||||
padding: 1rem;
|
||||
position: relative;
|
||||
|
||||
&-enter-active, &-leave-active {
|
||||
transition: all .5s ease;
|
||||
}
|
||||
&-enter, &-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
& + .criterias-group {
|
||||
margin-top: 1rem;
|
||||
|
||||
&::before {
|
||||
content: 'OR';
|
||||
position: absolute;
|
||||
display: inline-flex;
|
||||
padding: 0 2rem;
|
||||
top: -1.25rem;
|
||||
left: 2rem;
|
||||
background-color: mc('blue-grey', '100');
|
||||
color: mc('blue-grey', '700');
|
||||
font-weight: 600;
|
||||
font-size: .9rem;
|
||||
}
|
||||
}
|
||||
|
||||
&-more {
|
||||
margin: .5rem 0 0 .4rem;
|
||||
}
|
||||
}
|
||||
|
||||
&-item {
|
||||
display: flex;
|
||||
background-color: mc('blue-grey', '200');
|
||||
border-radius: 4px;
|
||||
padding: .5rem;
|
||||
|
||||
&-enter-active, &-leave-active {
|
||||
transition: all .5s ease;
|
||||
}
|
||||
&-enter, &-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
& + .criterias-item {
|
||||
margin-top: .5rem;
|
||||
position: relative;
|
||||
|
||||
&::before {
|
||||
content: 'AND';
|
||||
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: .7rem;
|
||||
background-color: mc('blue-grey', '100');
|
||||
left: -2rem;
|
||||
top: -1.3rem;
|
||||
}
|
||||
}
|
||||
|
||||
.input-group {
|
||||
&:nth-child(1) {
|
||||
flex: 0 1 350px;
|
||||
}
|
||||
|
||||
&:nth-child(2) {
|
||||
flex: 0 1 250px;
|
||||
}
|
||||
|
||||
& + * {
|
||||
margin-left: .5rem;
|
||||
}
|
||||
}
|
||||
|
||||
&-more {
|
||||
margin-top: .15rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -16,117 +16,123 @@
|
||||
:loading='searchIsLoading',
|
||||
@keyup.enter='searchEnter'
|
||||
)
|
||||
v-menu(open-on-hover, offset-y, bottom, left, min-width='250')
|
||||
v-toolbar-side-icon.btn-animate-app(slot='activator')
|
||||
v-icon view_module
|
||||
v-list(dense, :light='!$vuetify.dark', :dark='$vuetify.dark', :class='$vuetify.dark ? `grey darken-4` : ``').py-0
|
||||
v-list-tile(avatar, href='/')
|
||||
v-list-tile-avatar: v-icon(color='blue') home
|
||||
v-list-tile-content Home
|
||||
v-list-tile(avatar, @click='pageNew')
|
||||
v-list-tile-avatar: v-icon(color='green') add_box
|
||||
v-list-tile-content New Page
|
||||
template(v-if='path && path.length')
|
||||
v-divider.my-0
|
||||
v-subheader Current Page
|
||||
v-list-tile(avatar, @click='pageView', v-if='mode !== `view`')
|
||||
v-list-tile-avatar: v-icon(color='indigo') subject
|
||||
v-list-tile-content View
|
||||
v-list-tile(avatar, @click='pageEdit', v-if='mode !== `edit`')
|
||||
v-list-tile-avatar: v-icon(color='indigo') edit
|
||||
v-list-tile-content Edit
|
||||
v-list-tile(avatar, @click='pageHistory', v-if='mode !== `history`')
|
||||
v-list-tile-avatar: v-icon(color='indigo') history
|
||||
v-list-tile-content History
|
||||
v-list-tile(avatar, @click='pageSource', v-if='mode !== `source`')
|
||||
v-list-tile-avatar: v-icon(color='indigo') code
|
||||
v-list-tile-content View Source
|
||||
v-list-tile(avatar, @click='pageMove')
|
||||
v-list-tile-avatar: v-icon(color='indigo') forward
|
||||
v-list-tile-content Move / Rename
|
||||
v-list-tile(avatar, @click='pageDelete')
|
||||
v-list-tile-avatar: v-icon(color='red darken-2') delete
|
||||
v-list-tile-content Delete
|
||||
v-divider.my-0
|
||||
v-subheader Assets
|
||||
v-list-tile(avatar, @click='')
|
||||
v-list-tile-avatar: v-icon(color='blue-grey') burst_mode
|
||||
v-list-tile-content Images & Files
|
||||
v-toolbar-title(:class='{ "ml-2": $vuetify.breakpoint.mdAndUp, "ml-0": $vuetify.breakpoint.smAndDown }')
|
||||
span.subheading {{title}}
|
||||
v-spacer(v-if='searchIsShown && $vuetify.breakpoint.mdAndUp')
|
||||
transition(name='navHeaderSearch')
|
||||
v-text-field(
|
||||
ref='searchField',
|
||||
v-if='searchIsShown && $vuetify.breakpoint.mdAndUp',
|
||||
v-model='search',
|
||||
clearable,
|
||||
color='white',
|
||||
label='Search...',
|
||||
single-line,
|
||||
solo
|
||||
flat
|
||||
hide-details,
|
||||
prepend-inner-icon='search',
|
||||
:loading='searchIsLoading',
|
||||
@keyup.enter='searchEnter'
|
||||
)
|
||||
v-progress-linear(
|
||||
indeterminate,
|
||||
slot='progress',
|
||||
height='2',
|
||||
color='blue'
|
||||
)
|
||||
v-spacer
|
||||
.navHeaderLoading.mr-3
|
||||
v-progress-circular(indeterminate, color='blue', :size='22', :width='2' v-show='isLoading')
|
||||
slot(name='actions')
|
||||
v-btn(
|
||||
v-if='!hideSearch && $vuetify.breakpoint.smAndDown'
|
||||
@click='searchToggle'
|
||||
icon
|
||||
)
|
||||
v-icon(color='grey') search
|
||||
v-tooltip(bottom, v-if='isAuthenticated && isAdmin')
|
||||
v-btn.btn-animate-rotate(icon, href='/a', slot='activator')
|
||||
v-icon(color='grey') settings
|
||||
span Admin
|
||||
v-menu(offset-y, min-width='300')
|
||||
v-tooltip(bottom, slot='activator')
|
||||
v-btn.btn-animate-grow(icon, slot='activator', outline, :color='isAuthenticated ? `blue` : `grey darken-3`')
|
||||
v-icon(color='grey') account_circle
|
||||
span Account
|
||||
v-list.py-0
|
||||
template(v-if='isAuthenticated')
|
||||
v-list-tile.py-3.grey(avatar, :class='$vuetify.dark ? `darken-4-l5` : `lighten-5`')
|
||||
v-list-tile-avatar
|
||||
v-avatar.blue(v-if='picture.kind === `initials`', :size='40')
|
||||
span.white--text.subheading {{picture.initials}}
|
||||
v-avatar(v-else-if='picture.kind === `image`', :size='40')
|
||||
v-img(:src='picture.url')
|
||||
v-list-tile-content
|
||||
v-list-tile-title {{name}}
|
||||
v-list-tile-sub-title {{email}}
|
||||
v-divider.my-0
|
||||
v-list-tile(href='/w')
|
||||
v-list-tile-action: v-icon(color='blue') web
|
||||
v-list-tile-title My Wiki
|
||||
v-divider.my-0
|
||||
v-list-tile(href='/p')
|
||||
v-list-tile-action: v-icon(color='blue') person
|
||||
v-list-tile-title Profile
|
||||
v-divider.my-0
|
||||
v-list-tile(@click='logout')
|
||||
v-list-tile-action: v-icon(color='red') exit_to_app
|
||||
v-list-tile-title Logout
|
||||
template(v-else)
|
||||
v-list-tile(href='/login')
|
||||
v-list-tile-action: v-icon(color='grey') person
|
||||
v-list-tile-title Login
|
||||
v-divider.my-0
|
||||
v-list-tile(href='/register')
|
||||
v-list-tile-action: v-icon(color='grey') person_add
|
||||
v-list-tile-title Register
|
||||
v-layout(row)
|
||||
v-flex(xs6, :md4='searchIsShown', :md6='!searchIsShown')
|
||||
v-toolbar.nav-header-inner(color='black', dark, flat)
|
||||
v-menu(open-on-hover, offset-y, bottom, left, min-width='250')
|
||||
v-toolbar-side-icon.btn-animate-app(slot='activator')
|
||||
v-icon view_module
|
||||
v-list(dense, :light='!$vuetify.dark', :dark='$vuetify.dark', :class='$vuetify.dark ? `grey darken-4` : ``').py-0
|
||||
v-list-tile(avatar, href='/')
|
||||
v-list-tile-avatar: v-icon(color='blue') home
|
||||
v-list-tile-content Home
|
||||
v-list-tile(avatar, @click='pageNew')
|
||||
v-list-tile-avatar: v-icon(color='green') add_box
|
||||
v-list-tile-content New Page
|
||||
template(v-if='path && path.length')
|
||||
v-divider.my-0
|
||||
v-subheader Current Page
|
||||
v-list-tile(avatar, @click='pageView', v-if='mode !== `view`')
|
||||
v-list-tile-avatar: v-icon(color='indigo') subject
|
||||
v-list-tile-content View
|
||||
v-list-tile(avatar, @click='pageEdit', v-if='mode !== `edit`')
|
||||
v-list-tile-avatar: v-icon(color='indigo') edit
|
||||
v-list-tile-content Edit
|
||||
v-list-tile(avatar, @click='pageHistory', v-if='mode !== `history`')
|
||||
v-list-tile-avatar: v-icon(color='indigo') history
|
||||
v-list-tile-content History
|
||||
v-list-tile(avatar, @click='pageSource', v-if='mode !== `source`')
|
||||
v-list-tile-avatar: v-icon(color='indigo') code
|
||||
v-list-tile-content View Source
|
||||
v-list-tile(avatar, @click='pageMove')
|
||||
v-list-tile-avatar: v-icon(color='indigo') forward
|
||||
v-list-tile-content Move / Rename
|
||||
v-list-tile(avatar, @click='pageDelete')
|
||||
v-list-tile-avatar: v-icon(color='red darken-2') delete
|
||||
v-list-tile-content Delete
|
||||
v-divider.my-0
|
||||
v-subheader Assets
|
||||
v-list-tile(avatar, @click='')
|
||||
v-list-tile-avatar: v-icon(color='blue-grey') burst_mode
|
||||
v-list-tile-content Images & Files
|
||||
v-toolbar-title(:class='{ "ml-2": $vuetify.breakpoint.mdAndUp, "ml-0": $vuetify.breakpoint.smAndDown }')
|
||||
span.subheading {{title}}
|
||||
v-flex(md4, v-if='searchIsShown && $vuetify.breakpoint.mdAndUp')
|
||||
v-toolbar.nav-header-inner(color='black', dark, flat)
|
||||
transition(name='navHeaderSearch')
|
||||
v-text-field(
|
||||
ref='searchField',
|
||||
v-if='searchIsShown && $vuetify.breakpoint.mdAndUp',
|
||||
v-model='search',
|
||||
clearable,
|
||||
color='white',
|
||||
label='Search...',
|
||||
single-line,
|
||||
solo
|
||||
flat
|
||||
hide-details,
|
||||
prepend-inner-icon='search',
|
||||
:loading='searchIsLoading',
|
||||
@keyup.enter='searchEnter'
|
||||
)
|
||||
v-progress-linear(
|
||||
indeterminate,
|
||||
slot='progress',
|
||||
height='2',
|
||||
color='blue'
|
||||
)
|
||||
v-flex(xs6, :md4='searchIsShown', :md6='!searchIsShown')
|
||||
v-toolbar.nav-header-inner(color='black', dark, flat)
|
||||
v-spacer
|
||||
.navHeaderLoading.mr-3
|
||||
v-progress-circular(indeterminate, color='blue', :size='22', :width='2' v-show='isLoading')
|
||||
slot(name='actions')
|
||||
v-btn(
|
||||
v-if='!hideSearch && $vuetify.breakpoint.smAndDown'
|
||||
@click='searchToggle'
|
||||
icon
|
||||
)
|
||||
v-icon(color='grey') search
|
||||
v-tooltip(bottom, v-if='isAuthenticated && isAdmin')
|
||||
v-btn.btn-animate-rotate(icon, href='/a', slot='activator')
|
||||
v-icon(color='grey') settings
|
||||
span Admin
|
||||
v-menu(offset-y, min-width='300')
|
||||
v-tooltip(bottom, slot='activator')
|
||||
v-btn.btn-animate-grow(icon, slot='activator', outline, :color='isAuthenticated ? `blue` : `grey darken-3`')
|
||||
v-icon(color='grey') account_circle
|
||||
span Account
|
||||
v-list.py-0
|
||||
template(v-if='isAuthenticated')
|
||||
v-list-tile.py-3.grey(avatar, :class='$vuetify.dark ? `darken-4-l5` : `lighten-5`')
|
||||
v-list-tile-avatar
|
||||
v-avatar.blue(v-if='picture.kind === `initials`', :size='40')
|
||||
span.white--text.subheading {{picture.initials}}
|
||||
v-avatar(v-else-if='picture.kind === `image`', :size='40')
|
||||
v-img(:src='picture.url')
|
||||
v-list-tile-content
|
||||
v-list-tile-title {{name}}
|
||||
v-list-tile-sub-title {{email}}
|
||||
v-divider.my-0
|
||||
v-list-tile(href='/w')
|
||||
v-list-tile-action: v-icon(color='blue') web
|
||||
v-list-tile-title My Wiki
|
||||
v-divider.my-0
|
||||
v-list-tile(href='/p')
|
||||
v-list-tile-action: v-icon(color='blue') person
|
||||
v-list-tile-title Profile
|
||||
v-divider.my-0
|
||||
v-list-tile(@click='logout')
|
||||
v-list-tile-action: v-icon(color='red') exit_to_app
|
||||
v-list-tile-title Logout
|
||||
template(v-else)
|
||||
v-list-tile(href='/login')
|
||||
v-list-tile-action: v-icon(color='grey') person
|
||||
v-list-tile-title Login
|
||||
v-divider.my-0
|
||||
v-list-tile(href='/register')
|
||||
v-list-tile-action: v-icon(color='grey') person_add
|
||||
v-list-tile-title Register
|
||||
|
||||
page-selector(mode='create', v-model='newPageModal', :open-handler='pageNewCreate')
|
||||
</template>
|
||||
@@ -251,6 +257,12 @@ export default {
|
||||
padding-right: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
&-inner {
|
||||
.v-toolbar__content {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.navHeaderSearch {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
v-model='dialogOpen'
|
||||
max-width='650'
|
||||
)
|
||||
v-card
|
||||
v-card.wiki-form
|
||||
.dialog-header
|
||||
span Search User
|
||||
v-spacer
|
||||
@@ -16,23 +16,25 @@
|
||||
)
|
||||
v-card-text
|
||||
v-text-field(
|
||||
solo
|
||||
flat
|
||||
outline
|
||||
label='Search Users...'
|
||||
v-model='search'
|
||||
prepend-icon='search'
|
||||
:background-color='$vuetify.dark ? "grey darken-4" : "blue lighten-5"'
|
||||
prepend-inner-icon='search'
|
||||
color='primary'
|
||||
ref='searchIpt'
|
||||
hide-details
|
||||
)
|
||||
v-list(two-line)
|
||||
v-list.grey.mt-3.py-0.radius-7(
|
||||
:class='$vuetify.dark ? `darken-3-d5` : `lighten-3`'
|
||||
two-line
|
||||
dense
|
||||
)
|
||||
template(v-for='(usr, idx) in items')
|
||||
v-list-tile(:key='usr.id', @click='setUser(usr.id)')
|
||||
v-list-tile-avatar(size='40', color='primary')
|
||||
span.body-1.white--text {{usr.name | initials}}
|
||||
v-list-tile-content
|
||||
v-list-tile-title {{usr.name}}
|
||||
v-list-tile-title.body-2 {{usr.name}}
|
||||
v-list-tile-sub-title {{usr.email}}
|
||||
v-list-tile-action
|
||||
v-icon(color='primary') arrow_forward
|
||||
|
||||
@@ -243,6 +243,7 @@ export default {
|
||||
})
|
||||
this.$store.set('editor/id', _.get(resp, 'page.id'))
|
||||
this.$store.set('editor/mode', 'update')
|
||||
window.location.assign(`/${this.$store.get('page/path')}`)
|
||||
} else {
|
||||
throw new Error(_.get(resp, 'responseResult.message'))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user