mirror of https://github.com/requarks/wiki
1188 lines
34 KiB
1188 lines
34 KiB
<template lang="pug">
|
|
q-layout(view='hHh lpR fFf', container)
|
|
q-header.card-header.q-px-md.q-py-sm
|
|
q-icon(name='img:/_assets/icons/fluent-people.svg', left, size='md')
|
|
div
|
|
span {{t(`admin.groups.edit`)}}
|
|
.text-caption {{state.group.name}}
|
|
q-space
|
|
q-btn-group(push)
|
|
q-btn(
|
|
push
|
|
color='grey-6'
|
|
text-color='white'
|
|
:aria-label='t(`common.actions.refresh`)'
|
|
icon='las la-redo-alt'
|
|
@click='refresh'
|
|
)
|
|
q-tooltip(anchor='center left', self='center right') {{t(`common.actions.refresh`)}}
|
|
q-btn(
|
|
push
|
|
color='white'
|
|
text-color='grey-7'
|
|
:label='t(`common.actions.close`)'
|
|
icon='las la-times'
|
|
@click='close'
|
|
)
|
|
q-btn(
|
|
push
|
|
color='positive'
|
|
text-color='white'
|
|
:label='t(`common.actions.save`)'
|
|
icon='las la-check'
|
|
)
|
|
q-drawer.bg-dark-6(:model-value='true', :width='250', dark)
|
|
q-list(padding, v-show='!state.isLoading')
|
|
q-item(
|
|
v-for='sc of sections'
|
|
:key='`section-` + sc.key'
|
|
clickable
|
|
:to='{ params: { section: sc.key } }'
|
|
active-class='bg-primary text-white'
|
|
:disabled='sc.disabled'
|
|
)
|
|
q-item-section(side)
|
|
q-icon(:name='sc.icon', color='white')
|
|
q-item-section {{sc.text}}
|
|
q-item-section(side, v-if='sc.usersTotal')
|
|
q-badge(color='dark-3', :label='state.usersTotal')
|
|
q-item-section(side, v-if='sc.rulesTotal && state.group.rules')
|
|
q-badge(color='dark-3', :label='state.group.rules.length')
|
|
q-page-container
|
|
q-page(v-if='state.isLoading')
|
|
//- -----------------------------------------------------------------------
|
|
//- OVERVIEW
|
|
//- -----------------------------------------------------------------------
|
|
q-page(v-else-if='route.params.section === `overview`')
|
|
.q-pa-md
|
|
.row.q-col-gutter-md
|
|
.col-12.col-lg-8
|
|
q-card.shadow-1.q-pb-sm
|
|
q-card-section
|
|
.text-subtitle1 {{t('admin.groups.general')}}
|
|
q-item
|
|
blueprint-icon(icon='team')
|
|
q-item-section
|
|
q-item-label {{t(`admin.groups.name`)}}
|
|
q-item-label(caption) {{t(`admin.groups.nameHint`)}}
|
|
q-item-section
|
|
q-input(
|
|
outlined
|
|
v-model='state.group.name'
|
|
dense
|
|
:rules='groupNameValidation'
|
|
hide-bottom-space
|
|
:aria-label='t(`admin.groups.name`)'
|
|
)
|
|
|
|
q-card.shadow-1.q-pb-sm.q-mt-md
|
|
q-card-section
|
|
.text-subtitle1 {{t('admin.groups.authBehaviors')}}
|
|
q-item
|
|
blueprint-icon(icon='double-right')
|
|
q-item-section
|
|
q-item-label {{t(`admin.groups.redirectOnLogin`)}}
|
|
q-item-label(caption) {{t(`admin.groups.redirectOnLoginHint`)}}
|
|
q-item-section
|
|
q-input(
|
|
outlined
|
|
v-model='state.group.redirectOnLogin'
|
|
dense
|
|
:aria-label='t(`admin.groups.redirectOnLogin`)'
|
|
)
|
|
q-separator.q-my-sm(inset)
|
|
q-item
|
|
blueprint-icon(icon='chevron-right')
|
|
q-item-section
|
|
q-item-label {{t(`admin.groups.redirectOnFirstLogin`)}}
|
|
q-item-label(caption) {{t(`admin.groups.redirectOnFirstLoginHint`)}}
|
|
q-item-section
|
|
q-input(
|
|
outlined
|
|
v-model='state.group.redirectOnFirstLogin'
|
|
dense
|
|
:aria-label='t(`admin.groups.redirectOnLogin`)'
|
|
)
|
|
q-separator.q-my-sm(inset)
|
|
q-item
|
|
blueprint-icon(icon='exit')
|
|
q-item-section
|
|
q-item-label {{t(`admin.groups.redirectOnLogout`)}}
|
|
q-item-label(caption) {{t(`admin.groups.redirectOnLogoutHint`)}}
|
|
q-item-section
|
|
q-input(
|
|
outlined
|
|
v-model='state.group.redirectOnLogout'
|
|
dense
|
|
:aria-label='t(`admin.groups.redirectOnLogout`)'
|
|
)
|
|
|
|
.col-12.col-lg-4
|
|
q-card.shadow-1.q-pb-sm
|
|
q-card-section
|
|
.text-subtitle1 {{t('admin.groups.info')}}
|
|
q-item
|
|
blueprint-icon(icon='team', :hue-rotate='-45')
|
|
q-item-section
|
|
q-item-label {{t(`common.field.id`)}}
|
|
q-item-label: strong {{state.group.id}}
|
|
q-separator.q-my-sm(inset)
|
|
q-item
|
|
blueprint-icon(icon='calendar-plus', :hue-rotate='-45')
|
|
q-item-section
|
|
q-item-label {{t(`common.field.createdOn`)}}
|
|
q-item-label: strong {{humanizeDate(state.group.createdAt)}}
|
|
q-separator.q-my-sm(inset)
|
|
q-item
|
|
blueprint-icon(icon='summertime', :hue-rotate='-45')
|
|
q-item-section
|
|
q-item-label {{t(`common.field.lastUpdated`)}}
|
|
q-item-label: strong {{humanizeDate(state.group.updatedAt)}}
|
|
//- -----------------------------------------------------------------------
|
|
//- RULES
|
|
//- -----------------------------------------------------------------------
|
|
q-page(v-else-if='route.params.section === `rules`')
|
|
q-toolbar.q-pl-md(
|
|
:class='$q.dark.isActive ? `bg-dark-3` : `bg-white`'
|
|
)
|
|
.text-subtitle1 {{t('admin.groups.rules')}}
|
|
q-space
|
|
q-btn.acrylic-btn.q-mr-sm(
|
|
icon='las la-question-circle'
|
|
flat
|
|
color='grey'
|
|
type='a'
|
|
href='https://docs.js.wiki/admin/groups#rules'
|
|
target='_blank'
|
|
)
|
|
q-btn.acrylic-btn.q-mr-sm(
|
|
flat
|
|
color='indigo'
|
|
icon='las la-file-export'
|
|
@click='exportRules'
|
|
)
|
|
q-tooltip {{t('admin.groups.exportRules')}}
|
|
q-btn.acrylic-btn.q-mr-sm(
|
|
flat
|
|
color='indigo'
|
|
icon='las la-file-import'
|
|
@click='importRules'
|
|
)
|
|
q-tooltip {{t('admin.groups.importRules')}}
|
|
q-btn(
|
|
unelevated
|
|
color='primary'
|
|
icon='las la-plus'
|
|
label='New Rule'
|
|
@click='newRule'
|
|
)
|
|
q-separator
|
|
.q-pa-md
|
|
q-banner(
|
|
v-if='!state.group.rules || state.group.rules.length < 1'
|
|
rounded
|
|
:class='$q.dark.isActive ? `bg-negative text-white` : `bg-grey-4 text-grey-9`'
|
|
) {{t('admin.groups.rulesNone')}}
|
|
q-card.shadow-1.q-pb-sm(v-else)
|
|
q-card-section
|
|
.admin-groups-rule(
|
|
v-for='(rule, idx) of state.group.rules'
|
|
:key='rule.id'
|
|
)
|
|
.admin-groups-rule-icon(:class='getRuleModeColor(rule.mode)')
|
|
q-icon.cursor-pointer(
|
|
:name='getRuleModeIcon(rule.mode)'
|
|
color='white'
|
|
@click='rule.mode = getNextRuleMode(rule.mode)'
|
|
)
|
|
.admin-groups-rule-name
|
|
.admin-groups-rule-name-text: strong(:class='getRuleModeColor(rule.mode)') {{getRuleModeName(rule.mode)}}
|
|
q-separator.q-ml-sm.q-mr-xs(vertical)
|
|
input(
|
|
type='text'
|
|
v-model='rule.name'
|
|
placeholder='Rule Name'
|
|
)
|
|
q-card.admin-groups-rule-card.q-mt-md(flat)
|
|
q-card-section.admin-groups-rule-card-permissions(:class='getRuleModeClass(rule.mode)')
|
|
q-select.q-mt-xs(
|
|
standout
|
|
v-model='rule.roles'
|
|
emit-value
|
|
map-options
|
|
dense
|
|
:aria-label='t(`admin.groups.ruleSites`)'
|
|
:options='rules'
|
|
placeholder='Select permissions...'
|
|
option-value='permission'
|
|
option-label='title'
|
|
options-dense
|
|
multiple
|
|
use-chips
|
|
stack-label
|
|
)
|
|
template(v-slot:option='{ itemProps, itemEvents, opt, selected, toggleOption }')
|
|
q-item(v-bind='itemProps', v-on='itemEvents')
|
|
q-item-section(side)
|
|
q-toggle(
|
|
:value='selected'
|
|
@input='toggleOption(opt)'
|
|
color='primary'
|
|
checked-icon='las la-check'
|
|
unchecked-icon='las la-times'
|
|
:aria-label='opt.label'
|
|
)
|
|
//- q-item-section(side, style='flex-basis: 150px;')
|
|
//- q-chip.text-caption(
|
|
//- square
|
|
//- color='teal'
|
|
//- text-color='white'
|
|
//- dense
|
|
//- ) {{opt.permission}}
|
|
q-item-section
|
|
q-item-label {{opt.title}}
|
|
q-item-label(caption) {{opt.hint}}
|
|
q-btn.acrylic-btn.q-ml-md(
|
|
flat
|
|
icon='las la-trash'
|
|
color='negative'
|
|
padding='sm sm'
|
|
size='md',
|
|
@click='deleteRule(rule.id)'
|
|
)
|
|
q-card-section(horizontal)
|
|
q-card-section.admin-groups-rule-card-filters
|
|
.text-caption Applies to...
|
|
q-select.q-mt-xs(
|
|
standout
|
|
v-model='rule.sites'
|
|
emit-value
|
|
map-options
|
|
dense
|
|
:aria-label='t(`admin.groups.ruleSites`)'
|
|
:options='adminStore.sites'
|
|
option-value='id'
|
|
option-label='title'
|
|
multiple
|
|
behavior='dialog'
|
|
:display-value='t(`admin.groups.selectedSites`, rule.sites.length, { count: rule.sites.length })'
|
|
)
|
|
template(v-slot:option='{ itemProps, itemEvents, opt, selected, toggleOption }')
|
|
q-item(v-bind='itemProps', v-on='itemEvents')
|
|
q-item-section
|
|
q-item-label {{opt.title}}
|
|
q-item-section(side)
|
|
q-toggle(
|
|
:value='selected'
|
|
@input='toggleOption(opt)'
|
|
color='primary'
|
|
checked-icon='las la-check'
|
|
unchecked-icon='las la-times'
|
|
:aria-label='opt.label'
|
|
)
|
|
q-select.q-mt-sm(
|
|
standout
|
|
v-model='rule.locales'
|
|
emit-value
|
|
map-options
|
|
dense
|
|
:aria-label='t(`admin.groups.ruleLocales`)'
|
|
:options='adminStore.locales'
|
|
option-value='code'
|
|
option-label='name'
|
|
multiple
|
|
behavior='dialog'
|
|
:display-value='t(`admin.groups.selectedLocales`, rule.locales.length, { count: rule.locales.length, locale: rule.locales.length === 1 ? rule.locales[0].toUpperCase() : `` })'
|
|
)
|
|
template(v-slot:option='{ itemProps, opt, selected, toggleOption }')
|
|
q-item(v-bind='itemProps')
|
|
q-item-section
|
|
q-item-label {{opt.name}}
|
|
q-item-section(side)
|
|
q-toggle(
|
|
:model-value='selected'
|
|
@update:model-value='toggleOption(opt)'
|
|
color='primary'
|
|
checked-icon='las la-check'
|
|
unchecked-icon='las la-times'
|
|
:aria-label='opt.name'
|
|
)
|
|
q-card-section.admin-groups-rule-card-pattern
|
|
.text-caption Pattern
|
|
q-select.q-mt-xs(
|
|
standout
|
|
v-model='rule.match'
|
|
emit-value
|
|
map-options
|
|
dense
|
|
:aria-label='t(`admin.groups.ruleMatch`)'
|
|
:options=`[
|
|
{ label: t('admin.groups.ruleMatchStart'), value: 'START' },
|
|
{ label: t('admin.groups.ruleMatchEnd'), value: 'END' },
|
|
{ label: t('admin.groups.ruleMatchRegex'), value: 'REGEX' },
|
|
{ label: t('admin.groups.ruleMatchTag'), value: 'TAG' },
|
|
{ label: t('admin.groups.ruleMatchTagAll'), value: 'TAGALL' },
|
|
{ label: t('admin.groups.ruleMatchExact'), value: 'EXACT' }
|
|
]`
|
|
)
|
|
q-input.q-mt-sm(
|
|
standout
|
|
v-model='rule.path'
|
|
dense
|
|
:prefix='[`START`, `REGEX`, `EXACT`].includes(rule.match) ? `/` : null'
|
|
:suffix='rule.match === `REGEX` ? `/` : null'
|
|
:aria-label='t(`admin.groups.rulePath`)'
|
|
)
|
|
//- -----------------------------------------------------------------------
|
|
//- PERMISSIONS
|
|
//- -----------------------------------------------------------------------
|
|
q-page(v-else-if='route.params.section === `permissions`')
|
|
.q-pa-md
|
|
.row.q-col-gutter-md
|
|
.col-12.col-lg-6
|
|
q-card.shadow-1.q-pb-sm
|
|
.flex.justify-between
|
|
q-card-section
|
|
.text-subtitle1 {{t(`admin.groups.permissions`)}}
|
|
q-card-section
|
|
q-btn.acrylic-btn(
|
|
icon='las la-question-circle'
|
|
flat
|
|
color='grey'
|
|
type='a'
|
|
href='https://docs.js.wiki/admin/groups#permissions'
|
|
target='_blank'
|
|
)
|
|
template(v-for='(perm, idx) of permissions', :key='perm.permission')
|
|
q-item(tag='label', v-ripple)
|
|
q-item-section.items-center(style='flex: 0 0 40px;')
|
|
q-icon(
|
|
name='las la-comments'
|
|
color='primary'
|
|
size='sm'
|
|
)
|
|
q-item-section
|
|
q-item-label {{perm.permission}}
|
|
q-item-label(caption) {{perm.hint}}
|
|
q-item-section(avatar)
|
|
q-toggle(
|
|
v-model='state.group.permissions'
|
|
:val='perm.permission'
|
|
color='primary'
|
|
checked-icon='las la-check'
|
|
unchecked-icon='las la-times'
|
|
:aria-label='t(`admin.general.allowComments`)'
|
|
)
|
|
q-separator.q-my-sm(inset, v-if='idx < permissions.length - 1')
|
|
//- -----------------------------------------------------------------------
|
|
//- USERS
|
|
//- -----------------------------------------------------------------------
|
|
q-page(v-else-if='route.params.section === `users`')
|
|
q-toolbar(
|
|
:class='$q.dark.isActive ? `bg-dark-3` : `bg-white`'
|
|
)
|
|
.text-subtitle1 {{t('admin.groups.users')}}
|
|
q-space
|
|
q-btn.acrylic-btn.q-mr-sm(
|
|
icon='las la-question-circle'
|
|
flat
|
|
color='grey'
|
|
type='a'
|
|
href='https://docs.js.wiki/admin/groups#users'
|
|
target='_blank'
|
|
)
|
|
q-input.denser.fill-outline.q-mr-sm(
|
|
outlined
|
|
v-model='state.usersFilter'
|
|
:placeholder='t(`admin.groups.filterUsers`)'
|
|
dense
|
|
)
|
|
template(#prepend)
|
|
q-icon(name='las la-search')
|
|
q-btn.q-mr-sm.acrylic-btn(
|
|
icon='las la-redo-alt'
|
|
flat
|
|
color='secondary'
|
|
@click='refreshUsers'
|
|
)
|
|
q-btn.q-mr-xs(
|
|
unelevated
|
|
icon='las la-user-plus'
|
|
:label='t(`admin.groups.assignUser`)'
|
|
color='primary'
|
|
@click='assignUser'
|
|
)
|
|
q-separator
|
|
.q-pa-md
|
|
q-banner(
|
|
v-if='!state.users || state.users.length < 1'
|
|
rounded
|
|
:class='$q.dark.isActive ? `bg-negative text-white` : `bg-grey-4 text-grey-9`'
|
|
) {{t('admin.groups.usersNone')}}
|
|
q-card.shadow-1
|
|
q-table(
|
|
:rows='state.users'
|
|
:columns='usersHeaders'
|
|
row-key='id'
|
|
flat
|
|
hide-header
|
|
hide-bottom
|
|
:rows-per-page-options='[0]'
|
|
:loading='state.isLoadingUsers'
|
|
)
|
|
template(v-slot:body-cell-id='props')
|
|
q-td(:props='props')
|
|
q-icon(name='las la-user', color='primary', size='sm')
|
|
template(v-slot:body-cell-name='props')
|
|
q-td(:props='props')
|
|
.flex.items-center
|
|
strong {{props.value}}
|
|
q-icon.q-ml-sm(
|
|
v-if='props.row.isSystem'
|
|
name='las la-lock'
|
|
color='pink'
|
|
)
|
|
q-icon.q-ml-sm(
|
|
v-if='!props.row.isActive'
|
|
name='las la-ban'
|
|
color='pink'
|
|
)
|
|
template(v-slot:body-cell-email='props')
|
|
q-td(:props='props')
|
|
em {{ props.value }}
|
|
template(v-slot:body-cell-date='props')
|
|
q-td(:props='props')
|
|
i18n-t.text-caption(keypath='admin.users.createdAt', tag='div')
|
|
template(#date)
|
|
strong {{ humanizeDate(props.value) }}
|
|
i18n-t.text-caption(
|
|
v-if='props.row.lastLoginAt'
|
|
keypath='admin.users.lastLoginAt'
|
|
tag='div'
|
|
)
|
|
template(#date)
|
|
strong {{ humanizeDate(props.row.lastLoginAt) }}
|
|
template(v-slot:body-cell-edit='props')
|
|
q-td(:props='props')
|
|
q-btn.acrylic-btn.q-mr-sm(
|
|
v-if='!props.row.isSystem'
|
|
flat
|
|
:to='`/_admin/users/` + props.row.id'
|
|
icon='las la-pen'
|
|
color='indigo'
|
|
:label='t(`common.actions.edit`)'
|
|
no-caps
|
|
)
|
|
q-btn.acrylic-btn(
|
|
v-if='!props.row.isSystem'
|
|
flat
|
|
icon='las la-user-minus'
|
|
color='accent'
|
|
@click='unassignUser(props.row)'
|
|
)
|
|
|
|
.flex.flex-center.q-mt-md(v-if='usersTotalPages > 1')
|
|
q-pagination(
|
|
v-model='state.usersPage'
|
|
:max='usersTotalPages'
|
|
:max-pages='9'
|
|
boundary-numbers
|
|
direction-links
|
|
)
|
|
</template>
|
|
|
|
<script setup>
|
|
import gql from 'graphql-tag'
|
|
import { DateTime } from 'luxon'
|
|
import { v4 as uuid } from 'uuid'
|
|
import { cloneDeep, some } from 'lodash-es'
|
|
import { fileOpen } from 'browser-fs-access'
|
|
|
|
import { useI18n } from 'vue-i18n'
|
|
import { exportFile, useQuasar } from 'quasar'
|
|
import { computed, onMounted, reactive, watch } from 'vue'
|
|
import { useRouter, useRoute } from 'vue-router'
|
|
|
|
import { useAdminStore } from 'src/stores/admin'
|
|
|
|
// QUASAR
|
|
|
|
const $q = useQuasar()
|
|
|
|
// STORES
|
|
|
|
const adminStore = useAdminStore()
|
|
|
|
// ROUTER
|
|
|
|
const router = useRouter()
|
|
const route = useRoute()
|
|
|
|
// I18N
|
|
|
|
const { t } = useI18n()
|
|
|
|
// DATA
|
|
|
|
const state = reactive({
|
|
group: {
|
|
rules: []
|
|
},
|
|
isLoading: false,
|
|
users: [],
|
|
isLoadingUsers: false,
|
|
usersFilter: '',
|
|
usersPage: 1,
|
|
usersPageSize: 15,
|
|
usersTotal: 0
|
|
})
|
|
|
|
const sections = [
|
|
{ key: 'overview', text: t('admin.groups.overview'), icon: 'las la-users' },
|
|
{ key: 'rules', text: t('admin.groups.rules'), icon: 'las la-file-invoice', rulesTotal: true },
|
|
{ key: 'permissions', text: t('admin.groups.permissions'), icon: 'las la-list-alt' },
|
|
{ key: 'users', text: t('admin.groups.users'), icon: 'las la-user', usersTotal: true }
|
|
]
|
|
|
|
const usersHeaders = [
|
|
{
|
|
align: 'center',
|
|
field: 'id',
|
|
name: 'id',
|
|
sortable: false,
|
|
style: 'width: 20px'
|
|
},
|
|
{
|
|
label: t('common.field.name'),
|
|
align: 'left',
|
|
field: 'name',
|
|
name: 'name',
|
|
sortable: true
|
|
},
|
|
{
|
|
label: t('admin.users.email'),
|
|
align: 'left',
|
|
field: 'email',
|
|
name: 'email',
|
|
sortable: false
|
|
},
|
|
{
|
|
align: 'left',
|
|
field: 'createdAt',
|
|
name: 'date',
|
|
sortable: false
|
|
},
|
|
{
|
|
label: '',
|
|
align: 'right',
|
|
field: 'edit',
|
|
name: 'edit',
|
|
sortable: false,
|
|
style: 'width: 250px'
|
|
}
|
|
]
|
|
|
|
const permissions = [
|
|
{
|
|
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
|
|
},
|
|
{
|
|
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
|
|
}
|
|
]
|
|
|
|
const rules = [
|
|
{
|
|
permission: 'read:pages',
|
|
title: 'Read Pages',
|
|
hint: 'Can view and search pages.',
|
|
warning: false,
|
|
restrictedForSystem: false,
|
|
disabled: false
|
|
},
|
|
{
|
|
permission: 'write:pages',
|
|
title: 'Write Pages',
|
|
hint: 'Can create and edit pages.',
|
|
warning: false,
|
|
restrictedForSystem: true,
|
|
disabled: false
|
|
},
|
|
{
|
|
permission: 'review:pages',
|
|
title: 'Review Pages',
|
|
hint: 'Can review and approve edits submitted by users.',
|
|
warning: false,
|
|
restrictedForSystem: true,
|
|
disabled: false
|
|
},
|
|
{
|
|
permission: 'manage:pages',
|
|
title: 'Manage Pages',
|
|
hint: 'Can move existing pages to other locations the user has write access to.',
|
|
warning: false,
|
|
restrictedForSystem: true,
|
|
disabled: false
|
|
},
|
|
{
|
|
permission: 'delete:pages',
|
|
title: 'Delete Pages',
|
|
hint: 'Can delete existing pages.',
|
|
warning: false,
|
|
restrictedForSystem: true,
|
|
disabled: false
|
|
},
|
|
{
|
|
permission: 'write:styles',
|
|
title: 'Use CSS',
|
|
hint: 'Can insert CSS styles in pages.',
|
|
warning: false,
|
|
restrictedForSystem: true,
|
|
disabled: false
|
|
},
|
|
{
|
|
permission: 'write:scripts',
|
|
title: 'Use JavaScript',
|
|
hint: 'Can insert JavaScript in pages.',
|
|
warning: false,
|
|
restrictedForSystem: true,
|
|
disabled: false
|
|
},
|
|
{
|
|
permission: 'read:source',
|
|
title: 'View Pages Source',
|
|
hint: 'Can view pages source.',
|
|
warning: false,
|
|
restrictedForSystem: false,
|
|
disabled: false
|
|
},
|
|
{
|
|
permission: 'read:history',
|
|
title: 'View Page History',
|
|
hint: 'Can view previous versions of pages.',
|
|
warning: false,
|
|
restrictedForSystem: false,
|
|
disabled: false
|
|
},
|
|
{
|
|
permission: 'read:assets',
|
|
title: 'View Assets',
|
|
hint: 'Can view / use assets (such as images and files) in pages.',
|
|
warning: false,
|
|
restrictedForSystem: false,
|
|
disabled: false
|
|
},
|
|
{
|
|
permission: 'write:assets',
|
|
title: 'Upload Assets',
|
|
hint: 'Can upload new assets (such as images and files).',
|
|
warning: false,
|
|
restrictedForSystem: true,
|
|
disabled: false
|
|
},
|
|
{
|
|
permission: 'manage:assets',
|
|
title: 'Manage Assets',
|
|
hint: 'Can edit and delete existing assets (such as images and files).',
|
|
warning: false,
|
|
restrictedForSystem: true,
|
|
disabled: false
|
|
},
|
|
{
|
|
permission: 'read:comments',
|
|
title: 'Read Comments',
|
|
hint: 'Can view page comments.',
|
|
warning: false,
|
|
restrictedForSystem: false,
|
|
disabled: false
|
|
},
|
|
{
|
|
permission: 'write:comments',
|
|
title: 'Write Comments',
|
|
hint: 'Can post new comments on pages.',
|
|
warning: false,
|
|
restrictedForSystem: false,
|
|
disabled: false
|
|
},
|
|
{
|
|
permission: 'manage:comments',
|
|
title: 'Manage Comments',
|
|
hint: 'Can edit and delete existing page comments.',
|
|
warning: false,
|
|
restrictedForSystem: true,
|
|
disabled: false
|
|
}
|
|
]
|
|
|
|
// VALIDATION RULES
|
|
|
|
const groupNameValidation = [
|
|
val => /^[^<>"]+$/.test(val) || t('admin.groups.nameInvalidChars')
|
|
]
|
|
|
|
// COMPUTED
|
|
|
|
const usersTotalPages = computed(() => {
|
|
if (state.usersTotal < 1) { return 0 }
|
|
return Math.ceil(state.usersTotal / state.usersPageSize)
|
|
})
|
|
|
|
// WATCHERS
|
|
|
|
watch(() => route.params.section, checkRoute)
|
|
watch([() => state.usersPage, () => state.usersFilter], refreshUsers)
|
|
|
|
// METHODS
|
|
|
|
function close () {
|
|
adminStore.$patch({ overlay: '' })
|
|
}
|
|
|
|
function checkRoute () {
|
|
if (!route.params.section) {
|
|
router.replace({ params: { section: 'overview' } })
|
|
} else if (route.params.section === 'users') {
|
|
refreshUsers()
|
|
}
|
|
}
|
|
|
|
function humanizeDate (val) {
|
|
if (!val) { return '---' }
|
|
return DateTime.fromISO(val).toLocaleString(DateTime.DATETIME_FULL)
|
|
}
|
|
|
|
function getRuleModeColor (mode) {
|
|
return ({
|
|
DENY: 'text-negative',
|
|
ALLOW: 'text-positive',
|
|
FORCEALLOW: 'text-blue'
|
|
})[mode]
|
|
}
|
|
|
|
function getRuleModeClass (mode) {
|
|
return 'is-' + mode.toLowerCase()
|
|
}
|
|
|
|
function getRuleModeIcon (mode) {
|
|
return ({
|
|
DENY: 'las la-ban',
|
|
ALLOW: 'las la-check',
|
|
FORCEALLOW: 'las la-check-double'
|
|
})[mode] || 'las la-frog'
|
|
}
|
|
|
|
function getNextRuleMode (mode) {
|
|
return ({
|
|
DENY: 'FORCEALLOW',
|
|
ALLOW: 'DENY',
|
|
FORCEALLOW: 'ALLOW'
|
|
})[mode] || 'ALLOW'
|
|
}
|
|
|
|
function getRuleModeName (mode) {
|
|
switch (mode) {
|
|
case 'ALLOW': return t('admin.groups.ruleAllow')
|
|
case 'DENY': return t('admin.groups.ruleDeny')
|
|
case 'FORCEALLOW': return t('admin.groups.ruleForceAllow')
|
|
default: return '???'
|
|
}
|
|
}
|
|
|
|
function refresh () {
|
|
fetchGroup()
|
|
}
|
|
|
|
async function fetchGroup () {
|
|
state.isLoading = true
|
|
try {
|
|
const resp = await APOLLO_CLIENT.query({
|
|
query: gql`
|
|
query adminFetchGroup (
|
|
$id: UUID!
|
|
) {
|
|
groupById(
|
|
id: $id
|
|
) {
|
|
id
|
|
name
|
|
redirectOnLogin
|
|
redirectOnFirstLogin
|
|
redirectOnLogout
|
|
isSystem
|
|
permissions
|
|
rules {
|
|
id
|
|
name
|
|
path
|
|
roles
|
|
match
|
|
mode
|
|
locales
|
|
sites
|
|
}
|
|
userCount
|
|
createdAt
|
|
updatedAt
|
|
}
|
|
}
|
|
`,
|
|
variables: {
|
|
id: adminStore.overlayOpts.id
|
|
},
|
|
fetchPolicy: 'network-only'
|
|
})
|
|
if (resp?.data?.groupById) {
|
|
state.group = cloneDeep(resp.data.groupById)
|
|
state.usersTotal = state.group.userCount ?? 0
|
|
} else {
|
|
throw new Error('An unexpected error occured while fetching group details.')
|
|
}
|
|
} catch (err) {
|
|
$q.notify({
|
|
type: 'negative',
|
|
message: err.message
|
|
})
|
|
}
|
|
state.isLoading = false
|
|
}
|
|
|
|
function newRule () {
|
|
state.group.rules.push({
|
|
id: uuid(),
|
|
name: t('admin.groups.ruleUntitled'),
|
|
mode: 'ALLOW',
|
|
match: 'START',
|
|
roles: [],
|
|
path: '',
|
|
locales: [],
|
|
sites: []
|
|
})
|
|
}
|
|
|
|
function deleteRule (id) {
|
|
state.group.rules = state.group.rules.filter(r => r.id !== id)
|
|
}
|
|
|
|
function exportRules () {
|
|
if (state.group.rules.length < 1) {
|
|
return $q.notify({
|
|
type: 'negative',
|
|
message: t('admin.groups.exportRulesNoneError')
|
|
})
|
|
}
|
|
exportFile('rules.json', JSON.stringify(state.group.rules, null, 2), { mimeType: 'application/json;charset=UTF-8' })
|
|
}
|
|
|
|
async function importRules () {
|
|
try {
|
|
const blob = await fileOpen({
|
|
mimeTypes: ['application/json'],
|
|
extensions: ['.json'],
|
|
startIn: 'downloads',
|
|
excludeAcceptAllOption: true
|
|
})
|
|
const rulesRaw = await blob.text()
|
|
const rules = JSON.parse(rulesRaw)
|
|
if (!Array.isArray(rules) || rules.length < 1) {
|
|
throw new Error('Invalid Rules Format')
|
|
}
|
|
$q.dialog({
|
|
title: t('admin.groups.importModeTitle'),
|
|
message: t('admin.groups.importModeText'),
|
|
options: {
|
|
model: 'replace',
|
|
type: 'radio',
|
|
items: [
|
|
{ label: t('admin.groups.importModeReplace'), value: 'replace' },
|
|
{ label: t('admin.groups.importModeAdd'), value: 'add' }
|
|
]
|
|
},
|
|
persistent: true
|
|
}).onOk(choice => {
|
|
if (choice === 'replace') {
|
|
state.group.rules = []
|
|
}
|
|
state.group.rules = [
|
|
...state.group.rules,
|
|
...rules.map(r => ({
|
|
id: uuid(),
|
|
name: r.name || t('admin.groups.ruleUntitled'),
|
|
mode: ['ALLOW', 'DENY', 'FORCEALLOW'].includes(r.mode) ? r.mode : 'DENY',
|
|
match: ['START', 'END', 'REGEX', 'TAG', 'TAGALL', 'EXACT'].includes(r.match) ? r.match : 'START',
|
|
roles: r.roles || [],
|
|
path: r.path || '',
|
|
locales: r.locales.filter(l => some(adminStore.locales, ['code', l])),
|
|
sites: r.sites.filter(s => some(adminStore.sites, ['id', s]))
|
|
}))
|
|
]
|
|
$q.notify({
|
|
type: 'positive',
|
|
message: t('admin.groups.importSuccess')
|
|
})
|
|
})
|
|
} catch (err) {
|
|
$q.notify({
|
|
type: 'negative',
|
|
message: t('admin.groups.importFailed') + ` [${err.message}]`
|
|
})
|
|
}
|
|
}
|
|
|
|
async function refreshUsers () {
|
|
state.isLoadingUsers = true
|
|
try {
|
|
const resp = await APOLLO_CLIENT.query({
|
|
query: gql`
|
|
query adminFetchGroupUsers (
|
|
$filter: String
|
|
$page: Int
|
|
$pageSize: Int
|
|
$groupId: UUID!
|
|
) {
|
|
groupById (
|
|
id: $groupId
|
|
) {
|
|
id
|
|
userCount
|
|
users (
|
|
filter: $filter
|
|
page: $page
|
|
pageSize: $pageSize
|
|
) {
|
|
id
|
|
name
|
|
email
|
|
isSystem
|
|
isActive
|
|
createdAt
|
|
lastLoginAt
|
|
}
|
|
}
|
|
}
|
|
`,
|
|
variables: {
|
|
filter: state.usersFilter,
|
|
page: state.usersPage,
|
|
pageSize: state.usersPageSize,
|
|
groupId: adminStore.overlayOpts.id
|
|
},
|
|
fetchPolicy: 'network-only'
|
|
})
|
|
if (resp?.data?.groupById?.users) {
|
|
state.usersTotal = resp.data.groupById.userCount ?? 0
|
|
state.users = cloneDeep(resp.data.groupById.users)
|
|
} else {
|
|
throw new Error('An unexpected error occured while fetching group users.')
|
|
}
|
|
} catch (err) {
|
|
$q.notify({
|
|
type: 'negative',
|
|
message: err.message
|
|
})
|
|
}
|
|
state.isLoadingUsers = false
|
|
}
|
|
|
|
function assignUser () {
|
|
|
|
}
|
|
|
|
function unassignUser () {
|
|
|
|
}
|
|
|
|
// MOUNTED
|
|
|
|
onMounted(() => {
|
|
checkRoute()
|
|
fetchGroup()
|
|
})
|
|
|
|
</script>
|
|
|
|
<style lang="scss">
|
|
.admin-groups-rule {
|
|
position: relative;
|
|
padding: 10px 0 24px 40px;
|
|
|
|
&-icon {
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
bottom: 0;
|
|
width: 31px;
|
|
|
|
&::before {
|
|
position: absolute;
|
|
content: "";
|
|
border-radius: 100%;
|
|
width: 31px;
|
|
height: 31px;
|
|
background-color: currentColor;
|
|
top: 4px;
|
|
}
|
|
|
|
&::after {
|
|
position: absolute;
|
|
content: "";
|
|
width: 3px;
|
|
top: 41px;
|
|
bottom: 0;
|
|
left: 14px;
|
|
opacity: .4;
|
|
background-color: currentColor;
|
|
display: block;
|
|
}
|
|
|
|
.q-icon {
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
font-size: 16px;
|
|
height: 38px;
|
|
line-height: 38px;
|
|
width: 100%;
|
|
align-items: center;
|
|
justify-content: center;
|
|
display: flex;
|
|
}
|
|
}
|
|
|
|
&-name {
|
|
line-height: 12px;
|
|
display: flex;
|
|
flex-wrap: nowrap;
|
|
padding-top: 4px;
|
|
|
|
&-text {
|
|
flex: 0 0;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
input {
|
|
font-weight: 700;
|
|
color: $grey-6;
|
|
letter-spacing: 1px;
|
|
font-size: 12px;
|
|
line-height: 12px;
|
|
border: none;
|
|
padding: 0 0 0 5px;
|
|
outline: none;
|
|
flex: 1;
|
|
background-color: transparent;
|
|
|
|
&::placeholder {
|
|
color: $grey-5;
|
|
}
|
|
|
|
@at-root .body--dark & {
|
|
color: rgba(255,255,255,.7);
|
|
|
|
&::placeholder {
|
|
color: rgba(255,255,255,.4);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
&-card {
|
|
background-color: $grey-2 !important;
|
|
|
|
@at-root .body--dark & {
|
|
background-color: $dark-6 !important;
|
|
}
|
|
|
|
&-permissions {
|
|
background-color: rgba($positive, .1);
|
|
border-bottom: 1px solid rgba($positive, .3);
|
|
display: flex;
|
|
align-items: center;
|
|
|
|
.q-select {
|
|
flex-basis: 100%;
|
|
}
|
|
|
|
&.is-allow {
|
|
background-color: rgba($positive, .1);
|
|
border-bottom: 1px solid rgba($positive, .3);
|
|
}
|
|
&.is-deny {
|
|
background-color: rgba($negative, .1);
|
|
border-bottom: 1px solid rgba($negative, .3);
|
|
}
|
|
&.is-forceallow {
|
|
background-color: rgba($blue, .1);
|
|
border-bottom: 1px solid rgba($blue, .3);
|
|
}
|
|
}
|
|
|
|
&-filters {
|
|
background-color: $grey-3;
|
|
flex-basis: 300px;
|
|
|
|
.text-caption:first-child {
|
|
color: $grey-7;
|
|
}
|
|
|
|
@at-root .body--dark & {
|
|
background-color: $dark-5;
|
|
}
|
|
}
|
|
&-pattern {
|
|
flex-grow: 1;
|
|
}
|
|
}
|
|
}
|
|
</style>
|