feat: admin create user + markdown help fix

pull/1004/head
Nick 5 years ago
parent 577a061033
commit 2d2cf90514

@ -64,7 +64,7 @@ const graphQLLink = ApolloLink.from([
) )
store.commit('showNotification', { store.commit('showNotification', {
style: 'red', style: 'red',
message: `An expected error occured.`, message: `An unexpected error occured.`,
icon: 'warning' icon: 'warning'
}) })
} }

@ -26,8 +26,8 @@
v-list-item(to='/pages', v-if='hasPermission([`manage:system`, `write:pages`, `manage:pages`, `delete:pages`])') v-list-item(to='/pages', v-if='hasPermission([`manage:system`, `write:pages`, `manage:pages`, `delete:pages`])')
v-list-item-avatar(size='24'): v-icon mdi-file-document-outline v-list-item-avatar(size='24'): v-icon mdi-file-document-outline
v-list-item-title {{ $t('admin:pages.title') }} v-list-item-title {{ $t('admin:pages.title') }}
v-list-item-action v-list-item-action.pr-3
v-chip(x-small, disabled, :color='darkMode ? `grey darken-3-d4` : `grey lighten-4`') v-chip(x-small, :color='darkMode ? `grey darken-3-d4` : `grey lighten-5`')
.caption.grey--text {{ info.pagesTotal }} .caption.grey--text {{ info.pagesTotal }}
v-list-item(to='/theme', v-if='hasPermission([`manage:system`, `manage:theme`])') v-list-item(to='/theme', v-if='hasPermission([`manage:system`, `manage:theme`])')
v-list-item-avatar(size='24'): v-icon mdi-palette-outline v-list-item-avatar(size='24'): v-icon mdi-palette-outline
@ -38,14 +38,14 @@
v-list-item(to='/groups', v-if='hasPermission([`manage:system`, `manage:groups`, `write:groups`])') v-list-item(to='/groups', v-if='hasPermission([`manage:system`, `manage:groups`, `write:groups`])')
v-list-item-avatar(size='24'): v-icon mdi-account-group v-list-item-avatar(size='24'): v-icon mdi-account-group
v-list-item-title {{ $t('admin:groups.title') }} v-list-item-title {{ $t('admin:groups.title') }}
v-list-item-action v-list-item-action.pr-3
v-chip(x-small, disabled, :color='darkMode ? `grey darken-3-d4` : `grey lighten-4`') v-chip(x-small, :color='darkMode ? `grey darken-3-d4` : `grey lighten-4`')
.caption.grey--text {{ info.groupsTotal }} .caption.grey--text {{ info.groupsTotal }}
v-list-item(to='/users', v-if='hasPermission([`manage:system`, `manage:groups`, `write:groups`, `manage:users`, `write:users`])') v-list-item(to='/users', v-if='hasPermission([`manage:system`, `manage:groups`, `write:groups`, `manage:users`, `write:users`])')
v-list-item-avatar(size='24'): v-icon mdi-account-box v-list-item-avatar(size='24'): v-icon mdi-account-box
v-list-item-title {{ $t('admin:users.title') }} v-list-item-title {{ $t('admin:users.title') }}
v-list-item-action v-list-item-action.pr-3
v-chip(x-small, disabled, :color='darkMode ? `grey darken-3-d4` : `grey lighten-4`') v-chip(x-small, :color='darkMode ? `grey darken-3-d4` : `grey lighten-4`')
.caption.grey--text {{ info.usersTotal }} .caption.grey--text {{ info.usersTotal }}
template(v-if='hasPermission(`manage:system`)') template(v-if='hasPermission(`manage:system`)')
v-divider.my-2 v-divider.my-2

@ -1,27 +1,26 @@
<template lang="pug"> <template lang="pug">
v-card.wiki-form(flat) v-card(flat)
v-card-text v-card-text
v-text-field( v-text-field(
outline outlined
background-color='grey lighten-3'
v-model='group.name' v-model='group.name'
label='Group Name' label='Group Name'
counter='255' counter='255'
prepend-icon='people' prepend-icon='mdi-account-group'
) )
v-alert.radius-7( v-alert.radius-7(
v-if='group.isSystem' v-if='group.isSystem'
color='orange darken-2' color='orange darken-2'
:class='$vuetify.theme.dark ? "grey darken-4" : "orange lighten-5"' :class='$vuetify.theme.dark ? "grey darken-4" : "orange lighten-5"'
outline outlined
:value='true' :value='true'
icon='lock_outline' icon='mdi-lock-outline'
) This is a system group. Some permissions cannot be modified. ) This is a system group. Some permissions cannot be modified.
v-container.px-3.pb-3.pt-0(fluid, grid-list-md) v-container.px-3.pb-3.pt-0(fluid, grid-list-md)
v-layout(row, wrap) v-layout(row, wrap)
v-flex(xs12, md6, lg4, v-for='pmGroup in permissions', :key='pmGroup.category') v-flex(xs12, md6, lg4, v-for='pmGroup in permissions', :key='pmGroup.category')
v-card.md2(flat, :class='$vuetify.theme.dark ? "grey darken-3-d5" : "white"') v-card.md2(flat, :class='$vuetify.theme.dark ? "grey darken-3-d5" : "grey lighten-5"')
v-subheader {{pmGroup.category}} .overline.px-5.pt-5.pb-3.grey--text.text--darken-2 {{pmGroup.category}}
v-card-text.pt-0 v-card-text.pt-0
template(v-for='(pm, idx) in pmGroup.items') template(v-for='(pm, idx) in pmGroup.items')
v-checkbox.pt-0( v-checkbox.pt-0(
@ -32,7 +31,7 @@
color='primary' color='primary'
v-model='group.permissions' v-model='group.permissions'
:value='pm.permission' :value='pm.permission'
:append-icon='pm.warning ? "warning" : null', :append-icon='pm.warning ? "mdi-alert" : null',
:disabled='(group.isSystem && pm.restrictedForSystem) || group.id === 1 || pm.disabled' :disabled='(group.isSystem && pm.restrictedForSystem) || group.id === 1 || pm.disabled'
) )
v-divider.mt-3(v-if='idx < pmGroup.items.length - 1') v-divider.mt-3(v-if='idx < pmGroup.items.length - 1')

@ -1,66 +1,66 @@
<template lang="pug"> <template lang="pug">
v-card.wiki-form v-card
v-card-text(v-if='group.id === 1') v-card-text(v-if='group.id === 1')
v-alert.radius-7( v-alert.radius-7(
:class='$vuetify.theme.dark ? "grey darken-4" : "orange lighten-5"' :class='$vuetify.theme.dark ? "grey darken-4" : "orange lighten-5"'
color='orange darken-2' color='orange darken-2'
outline outlined
:value='true' icon='mdi-lock-outline'
icon='lock_outline'
) This group has access to everything. ) This group has access to everything.
template(v-else) template(v-else)
v-card-title(:class='$vuetify.theme.dark ? `grey darken-3-d5` : `grey lighten-5`') v-card-title(:class='$vuetify.theme.dark ? `grey darken-3-d5` : ``')
v-alert.radius-7( v-alert.radius-7.caption(
:class='$vuetify.theme.dark ? `grey darken-3-d3` : `white`' :class='$vuetify.theme.dark ? `grey darken-3-d3` : `grey lighten-4`'
:value='true'
color='grey' color='grey'
outline outlined
icon='info' icon='mdi-information'
) You must enable global content permissions (under Permissions tab) for page rules to have any effect. ) You must enable global content permissions (under Permissions tab) for page rules to have any effect.
v-spacer v-spacer
v-btn(depressed, color='primary', @click='addRule') v-btn.mx-2(depressed, color='primary', @click='addRule')
v-icon(left) add v-icon(left) mdi-plus
| Add Rule | Add Rule
v-menu( v-menu(
right right
offset-y offset-y
nudge-left='115' nudge-left='115'
) )
v-btn.is-icon(slot='activator', flat, outline, color='primary') template(v-slot:activator='{ on }')
v-icon more_horiz v-btn.is-icon(v-on='on', outlined, color='primary')
v-icon mdi-dots-horizontal
v-list(dense) v-list(dense)
v-list-item(@click='comingSoon') v-list-item(@click='comingSoon')
v-list-item-avatar v-list-item-avatar
v-icon keyboard_capslock v-icon mdi-application-import
v-list-item-title Load Preset v-list-item-title Load Preset
v-divider v-divider
v-list-item(@click='comingSoon') v-list-item(@click='comingSoon')
v-list-item-avatar v-list-item-avatar
v-icon publish v-icon mdi-application-export
v-list-item-title Save As Preset v-list-item-title Save As Preset
v-divider v-divider
v-list-item(@click='comingSoon') v-list-item(@click='comingSoon')
v-list-item-avatar v-list-item-avatar
v-icon cloud_upload v-icon mdi-cloud-upload
v-list-item-title Import Rules v-list-item-title Import Rules
v-divider v-divider
v-list-item(@click='comingSoon') v-list-item(@click='comingSoon')
v-list-item-avatar v-list-item-avatar
v-icon cloud_download v-icon mdi-cloud-download
v-list-item-title Export Rules v-list-item-title Export Rules
v-card-text(:class='$vuetify.theme.dark ? `grey darken-4-l5` : `white`') v-card-text(:class='$vuetify.theme.dark ? `grey darken-4-l5` : `white`')
.rules .rules
.caption(v-if='group.pageRules.length === 0') .caption(v-if='group.pageRules.length === 0')
em(:class='$vuetify.theme.dark ? `grey--text` : `blue-grey--text`') This group has no page rules yet. em(:class='$vuetify.theme.dark ? `grey--text` : `blue-grey--text`') This group has no page rules yet.
.rule(v-for='rule of group.pageRules', :key='rule.id') .rule(v-for='rule of group.pageRules', :key='rule.id')
v-btn.ma-0.rule-deny-btn( v-btn.ma-0.radius-4.rule-deny-btn(
solo solo
:color='rule.deny ? "red" : "green"' :color='rule.deny ? "red" : "green"'
dark dark
@click='rule.deny = !rule.deny' @click='rule.deny = !rule.deny'
height='48'
) )
v-icon(v-if='rule.deny') block v-icon(v-if='rule.deny') mdi-cancel
v-icon(v-else) check_circle v-icon(v-else) mdi-check-circle
//- Roles //- Roles
v-select.ml-1( v-select.ml-1(
solo solo
@ -83,7 +83,7 @@
template(slot='item', slot-scope='props') template(slot='item', slot-scope='props')
v-list-item-action(style='min-width: 30px;') v-list-item-action(style='min-width: 30px;')
v-checkbox( v-checkbox(
v-model='props.tile.props.value' v-model='props.attrs.inputValue'
hide-details hide-details
color='primary' color='primary'
) )
@ -103,7 +103,7 @@
dense dense
) )
template(slot='selection', slot-scope='{ item, index }') template(slot='selection', slot-scope='{ item, index }')
.body-1 {{item.text}} .body-2 {{item.text}}
template(slot='item', slot-scope='data') template(slot='item', slot-scope='data')
v-list-item-avatar v-list-item-avatar
v-avatar.white--text.radius-4(color='blue', size='30', tile) {{ data.item.icon }} v-avatar.white--text.radius-4(color='blue', size='30', tile) {{ data.item.icon }}
@ -133,18 +133,18 @@
color='primary' color='primary'
readonly readonly
) )
v-icon.mr-2(:color='rule.deny ? `red` : `green`') public v-icon.mr-2(:color='rule.deny ? `red` : `green`') mdi-earth
v-list-item-content v-list-item-content
v-list-item-title.body-2 Any Locale v-list-item-title.body-2 Any Locale
v-divider(slot='prepend-item') v-divider(slot='prepend-item')
template(slot='item', slot-scope='props') template(slot='item', slot-scope='props')
v-list-item-action(style='min-width: 30px;') v-list-item-action(style='min-width: 30px;')
v-checkbox( v-checkbox(
v-model='props.tile.props.value' v-model='props.attrs.inputValue'
hide-details hide-details
color='primary' color='primary'
) )
v-icon.mr-2(:color='rule.deny ? `red` : `green`') language v-icon.mr-2(:color='rule.deny ? `red` : `green`') mdi-web
v-list-item-content v-list-item-content
v-list-item-title.body-2 {{props.item.text}} v-list-item-title.body-2 {{props.item.text}}
v-chip.mr-2.grey--text(label, small, :color='$vuetify.theme.dark ? `grey darken-4` : `grey lighten-4`').caption {{props.item.value.toUpperCase()}} v-chip.mr-2.grey--text(label, small, :color='$vuetify.theme.dark ? `grey darken-4` : `grey lighten-4`').caption {{props.item.value.toUpperCase()}}
@ -161,15 +161,15 @@
:color='$vuetify.theme.dark ? `grey` : `blue-grey`' :color='$vuetify.theme.dark ? `grey` : `blue-grey`'
) )
v-btn(icon, @click='removeRule(rule.id)') v-btn.ml-2.mt-1(icon, @click='removeRule(rule.id)', small)
v-icon(:color='$vuetify.theme.dark ? `grey` : `blue-grey`') clear v-icon(:color='$vuetify.theme.dark ? `grey` : `blue-grey`') mdi-close
v-divider.mt-3 v-divider.mt-3
v-subheader.pl-0 Rules Order .overline.py-3 Rules Order
.body-1.pl-3 Rules are applied in order of path specificity. A more precise path will always override a less defined path. .body-2.pl-3 Rules are applied in order of path specificity. A more precise path will always override a less defined path.
.body-1.pl-4 For example, #[span.teal--text /geography/countries] will override #[span.teal--text /geography]. .body-2.pl-5 For example, #[span.teal--text /geography/countries] will override #[span.teal--text /geography].
.body-1.pl-3.pt-2 When 2 rules have the same specificity, the priority is given from lowest to highest as follows: .body-2.pl-3.pt-2 When 2 rules have the same specificity, the priority is given from lowest to highest as follows:
.body-1.pl-3.pt-1 .body-2.pl-3.pt-1
ul ul
li li
strong Path Starts With... strong Path Starts With...
@ -181,9 +181,9 @@
li li
strong Path Is Exactly... strong Path Is Exactly...
em.caption.pl-1 (highest) em.caption.pl-1 (highest)
.body-1.pl-3.pt-2 When 2 rules have the same path specificity AND the same match type, #[strong.red--text DENY] will always override an #[strong.green--text ALLOW] rule. .body-2.pl-3.pt-2 When 2 rules have the same path specificity AND the same match type, #[strong.red--text DENY] will always override an #[strong.green--text ALLOW] rule.
v-divider.mt-3 v-divider.mt-3
v-subheader.pl-0 Regular Expressions .overline.py-3 Regular Expressions
span Expressions that are deemed unsafe or could result in exponential time processing will be rejected upon saving. span Expressions that are deemed unsafe or could result in exponential time processing will be rejected upon saving.
</template> </template>
@ -202,18 +202,18 @@ export default {
data() { data() {
return { return {
roles: [ roles: [
{ text: 'Read Pages', value: 'read:pages', icon: 'insert_drive_file' }, { text: 'Read Pages', value: 'read:pages', icon: 'mdi-file-document-box-search-outline' },
{ text: 'Create Pages', value: 'write:pages', icon: 'insert_drive_file' }, { text: 'Create Pages', value: 'write:pages', icon: 'mdi-file-document-box-plus-outline' },
{ text: 'Edit + Move Pages', value: 'manage:pages', icon: 'insert_drive_file' }, { text: 'Edit + Move Pages', value: 'manage:pages', icon: 'mdi-file-document-edit-outline' },
{ text: 'Delete Pages', value: 'delete:pages', icon: 'insert_drive_file' }, { text: 'Delete Pages', value: 'delete:pages', icon: 'mdi-file-document-box-remove-outline' },
{ text: 'View Pages Source', value: 'read:source', icon: 'code' }, { text: 'View Pages Source', value: 'read:source', icon: 'mdi-code-tags' },
{ text: 'View Pages History', value: 'read:history', icon: 'restore' }, { text: 'View Pages History', value: 'read:history', icon: 'mdi-history' },
{ text: 'Read / Use Assets', value: 'read:assets', icon: 'camera' }, { text: 'Read / Use Assets', value: 'read:assets', icon: 'mdi-image-search-outline' },
{ text: 'Upload Assets', value: 'write:assets', icon: 'camera' }, { text: 'Upload Assets', value: 'write:assets', icon: 'mdi-image-plus' },
{ text: 'Edit + Delete Assets', value: 'manage:assets', icon: 'camera' }, { text: 'Edit + Delete Assets', value: 'manage:assets', icon: 'mdi-image-size-select-large' },
{ text: 'Read Comments', value: 'read:comments', icon: 'insert_comment' }, { text: 'Read Comments', value: 'read:comments', icon: 'mdi-comment-search-outline' },
{ text: 'Create Comments', value: 'write:comments', icon: 'insert_comment' }, { text: 'Create Comments', value: 'write:comments', icon: 'mdi-comment-plus-outline' },
{ text: 'Edit + Delete Comments', value: 'manage:comments', icon: 'insert_comment' } { text: 'Edit + Delete Comments', value: 'manage:comments', icon: 'mdi-comment-remove-outline' }
], ],
matches: [ matches: [
{ text: 'Path Starts With...', value: 'START', icon: '/...' }, { text: 'Path Starts With...', value: 'START', icon: '/...' },
@ -252,6 +252,9 @@ export default {
message: `Coming soon...`, message: `Coming soon...`,
icon: 'directions_boat' icon: 'directions_boat'
}) })
},
dude (stuff) {
console.info(stuff)
} }
} }
} }
@ -264,7 +267,7 @@ export default {
padding: 1rem; padding: 1rem;
position: relative; position: relative;
@at-root .theme--dark & { @at-root .v-application.theme--dark & {
background-color: mc('grey', '800'); background-color: mc('grey', '800');
} }
} }
@ -282,7 +285,7 @@ export default {
opacity: 0; opacity: 0;
} }
@at-root .theme--dark & { @at-root .v-application.theme--dark & {
background-color: mc('grey', '700'); background-color: mc('grey', '700');
} }
@ -306,7 +309,7 @@ export default {
left: -2rem; left: -2rem;
top: -1.3rem; top: -1.3rem;
@at-root .theme--dark & { @at-root .v-application.theme--dark & {
background-color: mc('grey', '800'); background-color: mc('grey', '800');
color: mc('grey', '600'); color: mc('grey', '600');
} }
@ -316,12 +319,5 @@ export default {
.input-group + * { .input-group + * {
margin-left: .5rem; margin-left: .5rem;
} }
&-deny-btn {
height: 48px;
border-radius: 2px 0 0 2px;
min-width: 0;
}
} }
</style> </style>

@ -1,49 +1,52 @@
<template lang="pug"> <template lang="pug">
v-card.wiki-form v-card
v-card-title(:class='$vuetify.theme.dark ? `grey darken-3-d3` : `grey lighten-5`') v-card-title.pb-4(:class='$vuetify.theme.dark ? `grey darken-3-d3` : `grey lighten-5`')
v-text-field( v-text-field(
outline outlined
flat flat
prepend-inner-icon='search' prepend-inner-icon='mdi-magnify'
v-model='search' v-model='search'
label='Search Group Users...' label='Search Group Users...'
hide-details hide-details
) )
v-spacer v-spacer
v-btn(color='primary', depressed, @click='searchUserDialog = true', :disabled='group.id === 2') v-btn(color='primary', depressed, @click='searchUserDialog = true', :disabled='group.id === 2')
v-icon(left) assignment_ind v-icon(left) mdi-clipboard-account
| Assign User | Assign User
v-data-table( v-data-table(
:items='group.users', :items='group.users',
:headers='headers', :headers='headers',
:search='search' :search='search'
:pagination.sync='pagination', :page.sync='pagination'
:rows-per-page-items='[15]' :items-per-page='15'
hide-actions @page-count='pageCount = $event'
must-sort,
hide-default-footer
) )
template(slot='items', slot-scope='props') template(slot='item', slot-scope='props')
tr(:active='props.selected') tr(:active='props.selected')
td.text-xs-right {{ props.item.id }} td.text-xs-right {{ props.item.id }}
td {{ props.item.name }} td {{ props.item.name }}
td {{ props.item.email }} td {{ props.item.email }}
td td
v-menu(bottom, right, min-width='200') v-menu(bottom, right, min-width='200')
v-btn(icon, slot='activator'): v-icon.grey--text.text--darken-1 more_horiz template(v-slot:activator='{ on }')
v-list v-btn(icon, v-on='on', small)
v-icon.grey--text.text--darken-1 mdi-dots-horizontal
v-list(dense, nav)
v-list-item(:to='`/users/` + props.item.id') v-list-item(:to='`/users/` + props.item.id')
v-list-item-action: v-icon(color='primary') person v-list-item-action: v-icon(color='primary') mdi-account-outline
v-list-item-content v-list-item-content
v-list-item-title View User Profile v-list-item-title View User Profile
template(v-if='props.item.id !== 2') template(v-if='props.item.id !== 2')
v-divider
v-list-item(@click='unassignUser(props.item.id)') v-list-item(@click='unassignUser(props.item.id)')
v-list-item-action: v-icon(color='orange') highlight_off v-list-item-action: v-icon(color='orange') mdi-account-remove-outline
v-list-item-content v-list-item-content
v-list-item-title Unassign v-list-item-title Unassign
template(slot='no-data') template(slot='no-data')
v-alert.ma-3(icon='warning', :value='true', outline) No users to display. v-alert.ma-3(icon='warning', outlined) No users to display.
.text-xs-center.py-2(v-if='group.users.length > 15') .text-center.py-2(v-if='group.users.length > 15')
v-pagination(v-model='pagination.page', :length='pages') v-pagination(v-model='pagination', :length='pageCount')
user-search(v-model='searchUserDialog', @select='assignUser') user-search(v-model='searchUserDialog', @select='assignUser')
</template> </template>
@ -73,7 +76,8 @@ export default {
{ text: '', value: 'actions', sortable: false, width: 50 } { text: '', value: 'actions', sortable: false, width: 50 }
], ],
searchUserDialog: false, searchUserDialog: false,
pagination: {}, pagination: 1,
pageCount: 0,
search: '' search: ''
} }
}, },

@ -10,11 +10,12 @@
v-spacer v-spacer
.caption.grey--text ID #[strong {{group.id}}] .caption.grey--text ID #[strong {{group.id}}]
v-divider.mx-3(vertical) v-divider.mx-3(vertical)
v-btn(color='grey', large, outline, to='/groups') v-btn(color='grey', large, outlined, to='/groups')
v-icon arrow_back v-icon mdi-arrow-left
v-dialog(v-model='deleteGroupDialog', max-width='500', v-if='!group.isSystem') v-dialog(v-model='deleteGroupDialog', max-width='500', v-if='!group.isSystem')
v-btn(color='red', large, outline, slot='activator') template(v-slot:activator='{ on }')
v-icon(color='red') delete v-btn(color='red', large, outlined, v-on='{ on }')
v-icon(color='red') mdi-trash-can-outline
v-card v-card
.dialog-header.is-red Delete Group? .dialog-header.is-red Delete 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-text Are you sure you want to delete group #[strong {{ group.name }}]? All users will be unassigned from this group.
@ -22,11 +23,11 @@
v-spacer v-spacer
v-btn(flat, @click='deleteGroupDialog = false') Cancel v-btn(flat, @click='deleteGroupDialog = false') Cancel
v-btn(color='red', dark, @click='deleteGroup') Delete v-btn(color='red', dark, @click='deleteGroup') Delete
v-btn(color='success', large, depressed, @click='updateGroup') v-btn.ml-2(color='success', large, depressed, @click='updateGroup')
v-icon(left) check v-icon(left) mdi-check
span Update Group span Update Group
v-card.mt-3 v-card.mt-3
v-tabs(v-model='tab', :color='$vuetify.theme.dark ? "primary" : "grey darken-2"', fixed-tabs, slider-color='white', show-arrows, dark) v-tabs(v-model='tab', :background-color='$vuetify.theme.dark ? "primary" : "grey darken-2"', fixed-tabs, slider-color='white', show-arrows, dark)
v-tab(key='permissions') Permissions v-tab(key='permissions') Permissions
v-tab(key='rules') Page Rules v-tab(key='rules') Page Rules
v-tab(key='users') Users v-tab(key='users') Users
@ -69,7 +70,7 @@ export default {
users: [] users: []
}, },
deleteGroupDialog: false, deleteGroupDialog: false,
tab: '1' tab: null
} }
}, },
methods: { methods: {

@ -37,25 +37,29 @@
:items='groups' :items='groups'
:headers='headers' :headers='headers'
:search='search' :search='search'
:pagination.sync='pagination' :page.sync='pagination'
:rows-per-page-items='[15]' :items-per-page='15'
hide-actions :loading='loading'
@page-count='pageCount = $event'
must-sort,
hide-default-footer
) )
template(slot='items', slot-scope='props') template(slot='item', slot-scope='props')
tr.is-clickable(:active='props.selected', @click='$router.push("/groups/" + props.item.id)') tr.is-clickable(:active='props.selected', @click='$router.push("/groups/" + props.item.id)')
td.text-xs-right {{ props.item.id }} td {{ props.item.id }}
td: strong {{ props.item.name }} td: strong {{ props.item.name }}
td {{ props.item.userCount }} td {{ props.item.userCount }}
td {{ props.item.createdAt | moment('calendar') }} td {{ props.item.createdAt | moment('calendar') }}
td {{ props.item.updatedAt | moment('calendar') }} td {{ props.item.updatedAt | moment('calendar') }}
td td
v-tooltip(left, v-if='props.item.isSystem') v-tooltip(left, v-if='props.item.isSystem')
v-icon(slot='activator') lock_outline template(v-slot:activator='{ on }')
v-icon(v-on='on') mdi-lock-outline
span System Group span System Group
template(slot='no-data') template(slot='no-data')
v-alert.ma-3(icon='warning', :value='true', outline) No groups to display. v-alert.ma-3(icon='warning', :value='true', outline) No groups to display.
.text-xs-center.py-2(v-if='this.pages > 0') .text-xs-center.py-2(v-if='pageCount > 1')
v-pagination(v-model='pagination.page', :length='pages') v-pagination(v-model='pagination', :length='pageCount')
</template> </template>
<script> <script>
@ -70,7 +74,8 @@ export default {
newGroupDialog: false, newGroupDialog: false,
newGroupName: '', newGroupName: '',
selectedGroup: {}, selectedGroup: {},
pagination: {}, pagination: 1,
pageCount: 0,
groups: [], groups: [],
headers: [ headers: [
{ text: 'ID', value: 'id', width: 50, align: 'right' }, { text: 'ID', value: 'id', width: 50, align: 'right' },
@ -80,16 +85,8 @@ export default {
{ text: 'Last Updated', value: 'updatedAt', width: 250 }, { text: 'Last Updated', value: 'updatedAt', width: 250 },
{ text: '', value: 'isSystem', width: 20, sortable: false } { text: '', value: 'isSystem', width: 20, sortable: false }
], ],
search: '' search: '',
} loading: false
},
computed: {
pages () {
if (this.pagination.rowsPerPage == null || this.pagination.totalItems == null) {
return 0
}
return Math.ceil(this.pagination.totalItems / this.pagination.rowsPerPage)
} }
}, },
watch: { watch: {
@ -158,6 +155,7 @@ export default {
fetchPolicy: 'network-only', fetchPolicy: 'network-only',
update: (data) => data.groups.list, update: (data) => data.groups.list,
watchLoading (isLoading) { watchLoading (isLoading) {
this.loading = isLoading
this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'admin-groups-refresh') this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'admin-groups-refresh')
} }
} }

@ -46,7 +46,7 @@
v-list-item(:key='tgt.key') v-list-item(:key='tgt.key')
template(v-if='tgt.status === `pending`') template(v-if='tgt.status === `pending`')
v-list-item-avatar(color='purple') v-list-item-avatar(color='purple')
v-icon(color='white') schedule v-icon(color='white') mdi-clock-outline
v-list-item-content v-list-item-content
v-list-item-title.body-2 {{tgt.title}} v-list-item-title.body-2 {{tgt.title}}
v-list-item-sub-title.purple--text.caption {{tgt.status}} v-list-item-sub-title.purple--text.caption {{tgt.status}}
@ -54,20 +54,20 @@
v-progress-circular(indeterminate, :size='20', :width='2', color='purple') v-progress-circular(indeterminate, :size='20', :width='2', color='purple')
template(v-else-if='tgt.status === `operational`') template(v-else-if='tgt.status === `operational`')
v-list-item-avatar(color='green') v-list-item-avatar(color='green')
v-icon(color='white') check_circle v-icon(color='white') mdi-check-circle
v-list-item-content v-list-item-content
v-list-item-title.body-2 {{tgt.title}} v-list-item-title.body-2 {{tgt.title}}
v-list-item-sub-title.green--text.caption {{$t('admin:storage.lastSync', { time: $options.filters.moment(tgt.lastAttempt, 'from') })}} v-list-item-sub-title.green--text.caption {{$t('admin:storage.lastSync', { time: $options.filters.moment(tgt.lastAttempt, 'from') })}}
template(v-else) template(v-else)
v-list-item-avatar(color='red') v-list-item-avatar(color='red')
v-icon(color='white') highlight_off v-icon(color='white') mdi-close-circle-outline
v-list-item-content v-list-item-content
v-list-item-title.body-2 {{tgt.title}} v-list-item-title.body-2 {{tgt.title}}
v-list-item-sub-title.red--text.caption {{$t('admin:storage.lastSyncAttempt', { time: $options.filters.moment(tgt.lastAttempt, 'from') })}} v-list-item-sub-title.red--text.caption {{$t('admin:storage.lastSyncAttempt', { time: $options.filters.moment(tgt.lastAttempt, 'from') })}}
v-list-item-action v-list-item-action
v-menu v-menu
v-btn(slot='activator', icon) v-btn(slot='activator', icon)
v-icon(color='red') info v-icon(color='red') mdi-information
v-card(width='450') v-card(width='450')
v-toolbar(flat, color='red', dark, dense) {{$t('admin:storage.errorMsg')}} v-toolbar(flat, color='red', dark, dense) {{$t('admin:storage.errorMsg')}}
v-card-text {{tgt.message}} v-card-text {{tgt.message}}

@ -1,6 +1,6 @@
<template lang="pug"> <template lang="pug">
v-dialog(v-model='isShown', max-width='650', persistent) v-dialog(v-model='isShown', max-width='650', persistent)
v-card.wiki-form v-card
.dialog-header.is-short .dialog-header.is-short
v-icon.mr-3(color='white') mdi-plus v-icon.mr-3(color='white') mdi-plus
span New User span New User
@ -59,13 +59,14 @@
data-vv-as='Name', data-vv-as='Name',
data-vv-scope='newUser', data-vv-scope='newUser',
:error-messages='errors.collect(`newUser.name`)' :error-messages='errors.collect(`newUser.name`)'
:hint='provider === `local` ? `Can be changed by the user.` : `May be overwritten by the provider during login.`'
key='newUserName' key='newUserName'
persistent-hint persistent-hint
) )
v-select( v-select.mt-2(
:items='groups' :items='groups'
item-text='name' item-text='name'
item-value='key' item-value='id'
outlined outlined
prepend-icon='mdi-account-group' prepend-icon='mdi-account-group'
v-model='group' v-model='group'
@ -90,8 +91,12 @@
v-card-chin v-card-chin
v-spacer v-spacer
v-btn(text, @click='isShown = false') Cancel v-btn(text, @click='isShown = false') Cancel
v-btn(depressed, color='primary', @click='newUser(false)', :disabled='errors.any(`newUser`)') Create v-btn.px-3(depressed, color='primary', @click='newUser(false)', :disabled='errors.any(`newUser`)')
v-btn(depressed, color='primary', @click='newUser(true)', :disabled='errors.any(`newUser`)') Create and Close v-icon(left) mdi-chevron-right
span Create
v-btn.px-3(depressed, color='primary', @click='newUser(true)', :disabled='errors.any(`newUser`)')
v-icon(left) mdi-chevron-double-right
span Create and Close
</template> </template>
<script> <script>
@ -116,7 +121,7 @@ export default {
password: '', password: '',
name: '', name: '',
groups: [], groups: [],
group: '', group: [],
mustChangePwd: false, mustChangePwd: false,
sendWelcomeEmail: false sendWelcomeEmail: false
} }
@ -155,7 +160,7 @@ export default {
email: this.email, email: this.email,
passwordRaw: this.password, passwordRaw: this.password,
name: this.name, name: this.name,
groups: this.groups, groups: this.group,
mustChangePassword: this.mustChangePwd, mustChangePassword: this.mustChangePwd,
sendWelcomeEmail: this.sendWelcomeEmail sendWelcomeEmail: this.sendWelcomeEmail
}, },
@ -176,6 +181,7 @@ export default {
if (close) { if (close) {
this.isShown = false this.isShown = false
this.$emit('refresh')
} else { } else {
this.$refs.emailInput.focus() this.$refs.emailInput.focus()
} }

@ -9,150 +9,162 @@
.subtitle-1.grey--text.animated.fadeInLeft.wait-p2s {{user.name}} .subtitle-1.grey--text.animated.fadeInLeft.wait-p2s {{user.name}}
v-spacer v-spacer
.caption.grey--text.animated.fadeInRight.wait-p5s ID #[strong {{user.id}}] .caption.grey--text.animated.fadeInRight.wait-p5s ID #[strong {{user.id}}]
v-divider.animated.fadeInRight.wait-p3s.mx-3(vertical) v-divider.animated.fadeInRight.wait-p3s.ml-3(vertical)
v-btn.animated.fadeInDown.wait-p2s(color='grey', large, outline, to='/users') v-btn.ml-3.animated.fadeInDown.wait-p2s(color='grey', large, outlined, to='/users')
v-icon arrow_back v-icon mdi-arrow-left
v-dialog(v-model='deleteUserDialog', max-width='500', v-if='user.id !== currentUserId && !user.isSystem') v-dialog(v-model='deleteUserDialog', max-width='500', v-if='user.id !== currentUserId && !user.isSystem')
v-btn.animated.fadeInDown.wait-p1s(color='red', large, outline, slot='activator') template(v-slot:activator='{ on }')
v-icon(color='red') delete v-btn.ml-3.animated.fadeInDown.wait-p1s(color='red', large, outlined, v-on='on')
v-icon(color='red') mdi-trash-can-outline
v-card v-card
.dialog-header.is-red Delete User? .dialog-header.is-red Delete User?
v-card-text Are you sure you want to delete user #[strong {{ user.name }}]? v-card-text Are you sure you want to delete user #[strong {{ user.name }}]?
v-card-actions v-card-actions
v-spacer v-spacer
v-btn(flat, @click='deleteUserDialog = false') Cancel v-btn(text, @click='deleteUserDialog = false') Cancel
v-btn(color='red', dark, @click='deleteUser') Delete v-btn(color='red', dark, @click='deleteUser') Delete
v-btn.animated.fadeInDown(color='primary', large, depressed, @click='updateUser') v-btn.ml-3.animated.fadeInDown(color='primary', large, depressed, @click='updateUser')
v-icon(left) check v-icon(left) mdi-check
span Update User span Update User
v-flex(xs5) v-flex(xs5)
v-card.animated.fadeInUp v-card.animated.fadeInUp
v-toolbar(color='primary', dense, dark, flat) v-toolbar(color='primary', dense, dark, flat)
v-icon.mr-2 directions_run v-icon.mr-2 mdi-information-variant
span Basic Info span Basic Info
v-list.py-0(two-line, dense) v-list.py-0(two-line, dense)
v-list-item v-list-item
v-list-item-avatar v-list-item-avatar(size='32')
v-icon alternate_email v-icon mdi-email-variant
v-list-item-content v-list-item-content
v-list-item-title Email v-list-item-title Email
v-list-item-sub-title {{ user.email }} v-list-item-subtitle {{ user.email }}
v-list-item-action(v-if='!user.isSystem') v-list-item-action(v-if='!user.isSystem')
v-btn(icon, color='grey', flat) v-btn(icon, color='grey', flat, x-small)
v-icon edit v-icon mdi-pencil
v-divider v-divider
v-list-item v-list-item
v-list-item-avatar v-list-item-avatar(size='32')
v-icon person v-icon mdi-account
v-list-item-content v-list-item-content
v-list-item-title Display Name v-list-item-title Display Name
v-list-item-sub-title {{ user.name }} v-list-item-subtitle {{ user.name }}
v-list-item-action v-list-item-action
v-btn(icon, color='grey', flat) v-btn(icon, color='grey', flat, x-small)
v-icon edit v-icon mdi-pencil
v-card.mt-3.animated.fadeInUp.wait-p2s(v-if='!user.isSystem') v-card.mt-3.animated.fadeInUp.wait-p2s(v-if='!user.isSystem')
v-toolbar(color='primary', dense, dark, flat) v-toolbar(color='primary', dense, dark, flat)
v-icon.mr-2 lock_outline v-icon.mr-2 mdi-lock-outline
span Authentication span Authentication
v-list.py-0(two-line, dense) v-list.py-0(two-line, dense)
v-list-item v-list-item
v-list-item-avatar v-list-item-avatar(size='32')
v-icon business v-icon mdi-domain
v-list-item-content v-list-item-content
v-list-item-title Provider v-list-item-title Provider
v-list-item-sub-title {{ user.providerKey }} v-list-item-subtitle {{ user.providerKey }}
v-list-item-action //- v-list-item-action
v-img(src='https://static.requarks.io/logo/wikijs.svg', alt='') //- v-img(src='https://static.requarks.io/logo/wikijs.svg', alt='', contain, max-height='32', position='center right')
template(v-if='user.providerKey === `local`') template(v-if='user.providerKey === `local`')
v-divider v-divider
v-list-item v-list-item
v-list-item-avatar v-list-item-avatar(size='32')
v-icon security v-icon mdi-textbox-password
v-list-item-content v-list-item-content
v-list-item-title Password v-list-item-title Password
v-list-item-sub-title ******** v-list-item-subtitle &bull;&bull;&bull;&bull;&bull;&bull;&bull;&bull;
v-list-item-action v-list-item-action
v-btn(icon, color='grey', flat) v-tooltip(top)
v-icon cached template(v-slot:activator='{ on }')
v-btn(icon, color='grey', flat, x-small, v-on='on')
v-icon mdi-cached
span Change Password
v-list-item-action v-list-item-action
v-btn(icon, color='grey', flat) v-tooltip(top)
v-icon email template(v-slot:activator='{ on }')
v-btn(icon, color='grey', flat, x-small, v-on='on')
v-icon mdi-email
span Send Password Reset Email
v-divider v-divider
v-list-item v-list-item
v-list-item-avatar v-list-item-avatar(size='32')
v-icon screen_lock_portrait v-icon mdi-two-factor-authentication
v-list-item-content v-list-item-content
v-list-item-title Two Factor Authentication (2FA) v-list-item-title Two Factor Authentication (2FA)
v-list-item-sub-title.red--text Inactive v-list-item-subtitle.red--text Inactive
v-list-item-action v-list-item-action
v-btn(icon, color='grey', flat) v-tooltip(top)
v-icon power_settings_new template(v-slot:activator='{ on }')
v-btn(icon, color='grey', flat, x-small, v-on='on')
v-icon mdi-power
span Toggle 2FA
template(v-if='user.providerId') template(v-if='user.providerId')
v-divider v-divider
v-list-item v-list-item
v-list-item-avatar v-list-item-avatar(size='32')
v-icon person v-icon mdi-account
v-list-item-content v-list-item-content
v-list-item-title Provider Id v-list-item-title Provider Id
v-list-item-sub-title {{ user.providerId }} v-list-item-subtitle {{ user.providerId }}
v-card.mt-3.animated.fadeInUp.wait-p4s v-card.mt-3.animated.fadeInUp.wait-p4s
v-toolbar(color='primary', dense, dark, flat) v-toolbar(color='primary', dense, dark, flat)
v-icon.mr-2 people v-icon.mr-2 mdi-account-group
span User Groups span User Groups
v-list(dense) v-list(dense)
template(v-for='(group, idx) in user.groups') template(v-for='(group, idx) in user.groups')
v-list-item v-list-item
v-list-item-avatar v-list-item-avatar(size='32')
v-icon people_outline v-icon mdi-account-group-outline
v-list-item-content v-list-item-content
v-list-item-title {{group.name}} v-list-item-title {{group.name}}
v-list-item-action v-list-item-action(v-if='!user.isSystem')
v-btn(icon, color='red', flat) v-btn(icon, color='red', x-small)
v-icon clear v-icon mdi-close
v-divider(v-if='idx < user.groups.length - 1') v-divider(v-if='idx < user.groups.length - 1')
v-card-chin v-alert.mx-3(v-if='user.groups.length < 1', outlined, color='grey darken-1', icon='mdi-alert')
.caption This user is not assigned to any group yet. You must assign at least 1 group to a user.
v-card-chin(v-if='!user.isSystem')
v-spacer v-spacer
v-btn(small, color='primary', flat) v-btn(color='primary', text)
v-icon(left) how_to_reg v-icon(left) mdi-clipboard-account
span Assign to group span Assign to group
v-flex(xs7) v-flex(xs7)
v-card.animated.fadeInUp.wait-p2s v-card.animated.fadeInUp.wait-p2s
v-toolbar(color='primary', dense, dark, flat) v-toolbar(color='primary', dense, dark, flat)
v-icon.mr-2 directions_walk v-icon.mr-2 mdi-account-badge-outline
span Extended Metadata span Extended Metadata
v-list.py-0(two-line, dense) v-list.py-0(two-line, dense)
v-list-item v-list-item
v-list-item-avatar v-list-item-avatar(size='32')
v-icon public v-icon mdi-map-marker
v-list-item-content v-list-item-content
v-list-item-title Location v-list-item-title Location
v-list-item-sub-title {{ user.location }} v-list-item-subtitle {{ user.location }}
v-list-item-action v-list-item-action
v-btn(icon, color='grey', flat) v-btn(icon, color='grey', flat, x-small)
v-icon edit v-icon mdi-pencil
v-divider v-divider
v-list-item v-list-item
v-list-item-avatar v-list-item-avatar(size='32')
v-icon local_library v-icon mdi-account-badge-horizontal-outline
v-list-item-content v-list-item-content
v-list-item-title Job Title v-list-item-title Job Title
v-list-item-sub-title {{ user.jobTitle }} v-list-item-subtitle {{ user.jobTitle }}
v-list-item-action v-list-item-action
v-btn(icon, color='grey', flat) v-btn(icon, color='grey', flat, x-small)
v-icon edit v-icon mdi-pencil
v-divider v-divider
v-list-item v-list-item
v-list-item-avatar v-list-item-avatar(size='32')
v-icon map v-icon mdi-map-clock-outline
v-list-item-content v-list-item-content
v-list-item-title Timezone v-list-item-title Timezone
v-list-item-sub-title {{ user.timezone }} v-list-item-subtitle {{ user.timezone }}
v-list-item-action v-list-item-action
v-btn(icon, color='grey', flat) v-btn(icon, color='grey', flat, x-small)
v-icon edit v-icon mdi-pencil
v-card.mt-3.animated.fadeInUp.wait-p4s v-card.mt-3.animated.fadeInUp.wait-p4s
v-toolbar(color='primary', dense, dark, flat) v-toolbar(color='primary', dense, dark, flat)
v-icon.mr-2 insert_drive_file v-icon.mr-2 mdi-file-document-box-multiple-outline
span Content span Content
v-card-text v-card-text
em.caption.grey--text Coming soon em.caption.grey--text Coming soon

@ -39,45 +39,36 @@
:items='usersFiltered', :items='usersFiltered',
:headers='headers', :headers='headers',
:search='search', :search='search',
:pagination.sync='pagination', :page.sync='pagination'
:rows-per-page-items='[15]' :items-per-page='15'
:loading='loading' :loading='loading'
hide-actions, @page-count='pageCount = $event'
disable-initial-sort hide-default-footer
) )
template(slot='headers', slot-scope='props') template(slot='item', slot-scope='props')
tr
th.text-xs-left(
v-for='header in props.headers'
:key='header.text'
:width='header.width'
:class='[`column`, header.sortable ? `sortable` : ``, pagination.descending ? `desc` : `asc`, header.value === pagination.sortBy ? `active` : ``]'
@click='changeSort(header.value)'
)
| {{ header.text }}
v-icon(small, v-if='header.sortable') arrow_upward
template(slot='items', slot-scope='props')
tr.is-clickable(:active='props.selected', @click='$router.push("/users/" + props.item.id)') tr.is-clickable(:active='props.selected', @click='$router.push("/users/" + props.item.id)')
//- td //- td
v-checkbox(hide-details, :input-value='props.selected', color='blue darken-2', @click='props.selected = !props.selected') v-checkbox(hide-details, :input-value='props.selected', color='blue darken-2', @click='props.selected = !props.selected')
td.text-xs-right {{ props.item.id }} td {{ props.item.id }}
td: strong {{ props.item.name }} td: strong {{ props.item.name }}
td {{ props.item.email }} td {{ props.item.email }}
td {{ props.item.providerKey }} td {{ props.item.providerKey }}
td {{ props.item.createdAt | moment('from') }} td {{ props.item.createdAt | moment('from') }}
td td
v-tooltip(left, v-if='props.item.isSystem') v-tooltip(left, v-if='props.item.isSystem')
v-icon(slot='activator') lock_outline template(v-slot:activator='{ on }')
v-icon(v-on='{ on }') mdi-lock-outline
span System User span System User
template(slot='no-data') template(slot='no-data')
.pa-3 .pa-3
v-alert(icon='warning', :value='true', outline) No users to display! v-alert.text-left(icon='mdi-alert', outlined, color='grey')
v-card-chin(v-if='this.pages > 1') em.body-2 No users to display!
v-card-chin(v-if='pageCount > 1')
v-spacer v-spacer
v-pagination(v-model='pagination.page', :length='pages') v-pagination(v-model='pagination', :length='pageCount')
v-spacer v-spacer
user-create(v-model='isCreateDialogShown') user-create(v-model='isCreateDialogShown', @refresh='refresh(false)')
</template> </template>
<script> <script>
@ -95,7 +86,8 @@ export default {
data() { data() {
return { return {
selected: [], selected: [],
pagination: {}, pagination: 1,
pageCount: 0,
users: [], users: [],
headers: [ headers: [
{ text: 'ID', value: 'id', width: 80, sortable: true }, { text: 'ID', value: 'id', width: 80, sortable: true },
@ -116,40 +108,20 @@ export default {
usersFiltered () { usersFiltered () {
const all = this.filterStrategy === 'all' || this.filterStrategy === '' const all = this.filterStrategy === 'all' || this.filterStrategy === ''
return _.filter(this.users, u => all || u.providerKey === this.filterStrategy) return _.filter(this.users, u => all || u.providerKey === this.filterStrategy)
},
pages () {
if (this.pagination.rowsPerPage == null || this.usersFiltered.length < 1) {
return 0
}
return Math.ceil(this.usersFiltered.length / this.pagination.rowsPerPage)
} }
}, },
methods: { methods: {
createUser() { createUser() {
this.isCreateDialogShown = true this.isCreateDialogShown = true
}, },
async refresh() { async refresh(notify = true) {
await this.$apollo.queries.users.refetch() await this.$apollo.queries.users.refetch()
if (notify) {
this.$store.commit('showNotification', { this.$store.commit('showNotification', {
message: 'Users list has been refreshed.', message: 'Users list has been refreshed.',
style: 'success', style: 'success',
icon: 'cached' icon: 'cached'
}) })
},
changeSort (column) {
if (this.pagination.sortBy === column) {
this.pagination.descending = !this.pagination.descending
} else {
this.pagination.sortBy = column
this.pagination.descending = false
}
},
toggleAll () {
if (this.selected.length) {
this.selected = []
} else {
this.selected = this.items.slice()
} }
} }
}, },

@ -16,10 +16,10 @@
) )
v-card-text v-card-text
v-text-field( v-text-field(
outline outlined
:label='$t(`common:user.searchPlaceholder`)' :label='$t(`common:user.searchPlaceholder`)'
v-model='search' v-model='search'
prepend-inner-icon='search' prepend-inner-icon='mdi-account-search-outline'
color='primary' color='primary'
ref='searchIpt' ref='searchIpt'
hide-details hide-details
@ -35,14 +35,14 @@
span.body-1.white--text {{usr.name | initials}} span.body-1.white--text {{usr.name | initials}}
v-list-item-content v-list-item-content
v-list-item-title.body-2 {{usr.name}} v-list-item-title.body-2 {{usr.name}}
v-list-item-sub-title {{usr.email}} v-list-item-subtitle {{usr.email}}
v-list-item-action v-list-item-action
v-icon(color='primary') arrow_forward v-icon(color='primary') mdi-arrow-right
v-divider.my-0(v-if='idx < items.length - 1') v-divider.my-0(v-if='idx < items.length - 1')
v-card-chin v-card-chin
v-spacer v-spacer
v-btn( v-btn(
flat text
@click='close' @click='close'
:disabled='loading' :disabled='loading'
) {{$t('common:actions.cancel')}} ) {{$t('common:actions.cancel')}}

@ -7,7 +7,7 @@
v-card-text v-card-text
.d-flex .d-flex
v-toolbar.radius-7(color='teal lighten-5', dense, flat, height='44') v-toolbar.radius-7(color='teal lighten-5', dense, flat, height='44')
v-icon.mr-3(color='teal') info v-icon.mr-3(color='teal') mdi-information-variant
.body-2.teal--text Markdown Reference .body-2.teal--text Markdown Reference
.body-2.mt-3 Bold .body-2.mt-3 Bold
v-layout(row) v-layout(row)
@ -15,8 +15,8 @@
v-card.editor-markdown-help-source(flat) v-card.editor-markdown-help-source(flat)
v-card-text v-card-text
div **Lorem ipsum** div **Lorem ipsum**
v-icon chevron_right v-icon mdi-chevron-right
v-flex(xs6) v-flex
v-card.editor-markdown-help-result(flat) v-card.editor-markdown-help-result(flat)
v-card-text v-card-text
.caption: strong Lorem ipsum .caption: strong Lorem ipsum
@ -26,8 +26,8 @@
v-card.editor-markdown-help-source(flat) v-card.editor-markdown-help-source(flat)
v-card-text v-card-text
div *Lorem ipsum* div *Lorem ipsum*
v-icon chevron_right v-icon mdi-chevron-right
v-flex(xs6) v-flex
v-card.editor-markdown-help-result(flat) v-card.editor-markdown-help-result(flat)
v-card-text v-card-text
.caption: em Lorem ipsum .caption: em Lorem ipsum
@ -37,8 +37,8 @@
v-card.editor-markdown-help-source(flat) v-card.editor-markdown-help-source(flat)
v-card-text v-card-text
div ~~Lorem ipsum~~ div ~~Lorem ipsum~~
v-icon chevron_right v-icon mdi-chevron-right
v-flex(xs6) v-flex
v-card.editor-markdown-help-result(flat) v-card.editor-markdown-help-result(flat)
v-card-text v-card-text
.caption(style='text-decoration: line-through;') Lorem ipsum .caption(style='text-decoration: line-through;') Lorem ipsum
@ -53,8 +53,8 @@
div #### Header 4 div #### Header 4
div ##### Header 5 div ##### Header 5
div ###### Header 6 div ###### Header 6
v-icon chevron_right v-icon mdi-chevron-right
v-flex(xs6) v-flex
v-card.editor-markdown-help-result(flat) v-card.editor-markdown-help-result(flat)
v-card-text v-card-text
div(style='font-weight: 700; font-size: 24px;') Header 1 div(style='font-weight: 700; font-size: 24px;') Header 1
@ -72,8 +72,8 @@
div - Unordered List Item 1 div - Unordered List Item 1
div - Unordered List Item 2 div - Unordered List Item 2
div - Unordered List Item 3 div - Unordered List Item 3
v-icon chevron_right v-icon mdi-chevron-right
v-flex(xs6) v-flex
v-card.editor-markdown-help-result(flat) v-card.editor-markdown-help-result(flat)
v-card-text v-card-text
ul ul
@ -89,8 +89,8 @@
div 1. Ordered List Item 1 div 1. Ordered List Item 1
div 1. Ordered List Item 2 div 1. Ordered List Item 2
div 1. Ordered List Item 3 div 1. Ordered List Item 3
v-icon chevron_right v-icon mdi-chevron-right
v-flex(xs6) v-flex
v-card.editor-markdown-help-result(flat) v-card.editor-markdown-help-result(flat)
v-card-text v-card-text
ol ol
@ -103,8 +103,8 @@
v-card.editor-markdown-help-source(flat) v-card.editor-markdown-help-source(flat)
v-card-text v-card-text
div ![Caption Text](/path/to/image.jpg) div ![Caption Text](/path/to/image.jpg)
v-icon chevron_right v-icon mdi-chevron-right
v-flex(xs6) v-flex
v-card.editor-markdown-help-result(flat) v-card.editor-markdown-help-result(flat)
v-card-text v-card-text
img(src='https://via.placeholder.com/150x50.png') img(src='https://via.placeholder.com/150x50.png')
@ -113,7 +113,7 @@
v-card-text v-card-text
.d-flex .d-flex
v-toolbar.radius-7(color='teal lighten-5', dense, flat, height='44') v-toolbar.radius-7(color='teal lighten-5', dense, flat, height='44')
v-icon.mr-3(color='teal') info v-icon.mr-3(color='teal') mdi-information-variant
.body-2.teal--text Markdown Reference (continued) .body-2.teal--text Markdown Reference (continued)
.body-2.mt-3 Links .body-2.mt-3 Links
v-layout(row) v-layout(row)
@ -121,8 +121,8 @@
v-card.editor-markdown-help-source(flat) v-card.editor-markdown-help-source(flat)
v-card-text v-card-text
div [Link Text](https://wiki.js.org) div [Link Text](https://wiki.js.org)
v-icon chevron_right v-icon mdi-chevron-right
v-flex(xs6) v-flex
v-card.editor-markdown-help-result(flat) v-card.editor-markdown-help-result(flat)
v-card-text v-card-text
.caption: a(href='https://wiki.js.org', target='_blank') Link Text .caption: a(href='https://wiki.js.org', target='_blank') Link Text
@ -132,8 +132,8 @@
v-card.editor-markdown-help-source(flat) v-card.editor-markdown-help-source(flat)
v-card-text v-card-text
div Lorem ^ipsum^ div Lorem ^ipsum^
v-icon chevron_right v-icon mdi-chevron-right
v-flex(xs6) v-flex
v-card.editor-markdown-help-result(flat) v-card.editor-markdown-help-result(flat)
v-card-text v-card-text
.caption Lorem #[sup ipsum] .caption Lorem #[sup ipsum]
@ -143,8 +143,8 @@
v-card.editor-markdown-help-source(flat) v-card.editor-markdown-help-source(flat)
v-card-text v-card-text
div Lorem ~ipsum~ div Lorem ~ipsum~
v-icon chevron_right v-icon mdi-chevron-right
v-flex(xs6) v-flex
v-card.editor-markdown-help-result(flat) v-card.editor-markdown-help-result(flat)
v-card-text v-card-text
.caption: em Lorem #[sub ipsum] .caption: em Lorem #[sub ipsum]
@ -156,8 +156,8 @@
div Lorem ipsum div Lorem ipsum
div --- div ---
div Dolor sit amet div Dolor sit amet
v-icon chevron_right v-icon mdi-chevron-right
v-flex(xs6) v-flex
v-card.editor-markdown-help-result(flat) v-card.editor-markdown-help-result(flat)
v-card-text v-card-text
.caption Lorem ipsum .caption Lorem ipsum
@ -169,8 +169,8 @@
v-card.editor-markdown-help-source(flat) v-card.editor-markdown-help-source(flat)
v-card-text v-card-text
div Lorem `ipsum dolor sit` amet div Lorem `ipsum dolor sit` amet
v-icon chevron_right v-icon mdi-chevron-right
v-flex(xs6) v-flex
v-card.editor-markdown-help-result(flat) v-card.editor-markdown-help-result(flat)
v-card-text v-card-text
.caption Lorem #[code ipsum dolor sit] amet .caption Lorem #[code ipsum dolor sit] amet
@ -185,8 +185,8 @@
div.pl-3 echo 'Lorem ipsum' div.pl-3 echo 'Lorem ipsum'
div } div }
div ``` div ```
v-icon chevron_right v-icon mdi-chevron-right
v-flex(xs6) v-flex
v-card.editor-markdown-help-result(flat) v-card.editor-markdown-help-result(flat)
v-card-text.contents v-card-text.contents
pre.prismjs.line-numbers.language-js pre.prismjs.line-numbers.language-js
@ -211,8 +211,8 @@
div &gt; Lorem ipsum div &gt; Lorem ipsum
div &gt; dolor sit amet div &gt; dolor sit amet
div &gt; consectetur adipiscing elit div &gt; consectetur adipiscing elit
v-icon chevron_right v-icon mdi-chevron-right
v-flex(xs6) v-flex
v-card.editor-markdown-help-result(flat) v-card.editor-markdown-help-result(flat)
v-card-text v-card-text
blockquote(style='border: 1px solid #263238; border-radius: .5rem; padding: 1rem 24px;') Lorem ipsum#[br]dolor sit amet#[br]consectetur adipiscing elit blockquote(style='border: 1px solid #263238; border-radius: .5rem; padding: 1rem 24px;') Lorem ipsum#[br]dolor sit amet#[br]consectetur adipiscing elit
@ -221,7 +221,7 @@
v-card.radius-7.animated.fadeInUp.wait-p2s(light) v-card.radius-7.animated.fadeInUp.wait-p2s(light)
v-card-text v-card-text
v-toolbar.radius-7(color='teal lighten-5', dense, flat) v-toolbar.radius-7(color='teal lighten-5', dense, flat)
v-icon.mr-3(color='teal') keyboard v-icon.mr-3(color='teal') mdi-keyboard
.body-2.teal--text Keyboard Shortcuts .body-2.teal--text Keyboard Shortcuts
v-list.editor-markdown-help-kbd(two-line, dense) v-list.editor-markdown-help-kbd(two-line, dense)
v-list-item v-list-item
@ -255,13 +255,13 @@
v-list-item v-list-item
v-list-item-content v-list-item-content
v-list-item-title.body-2 Distraction Free Mode v-list-item-title.body-2 Distraction Free Mode
v-list-item-sub-title Press <kbd>Esc</kbd> to exit. v-list-item-subtitle Press <kbd>Esc</kbd> to exit.
v-list-item-action #[kbd F11] v-list-item-action #[kbd F11]
v-card.radius-7.animated.fadeInUp.wait-p3s.mt-3(light) v-card.radius-7.animated.fadeInUp.wait-p3s.mt-3(light)
v-card-text v-card-text
v-toolbar.radius-7(color='teal lighten-5', dense, flat) v-toolbar.radius-7(color='teal lighten-5', dense, flat)
v-icon.mr-3(color='teal') mouse v-icon.mr-3(color='teal') mdi-mouse
.body-2.teal--text Multi-Selection .body-2.teal--text Multi-Selection
v-list.editor-markdown-help-kbd(two-line, dense) v-list.editor-markdown-help-kbd(two-line, dense)
v-list-item v-list-item
@ -303,6 +303,10 @@ export default {
font-family: 'Roboto Mono', monospace; font-family: 'Roboto Mono', monospace;
font-size: 14px; font-size: 14px;
color: #FFF !important; color: #FFF !important;
.v-card__text {
color: #FFF !important;
}
} }
&-result { &-result {
@ -327,7 +331,7 @@ export default {
} }
&-kbd { &-kbd {
.v-list__tile__action { .v-list-item__action {
flex-direction: row; flex-direction: row;
align-items: center; align-items: center;
@ -345,12 +349,5 @@ export default {
} }
} }
} }
&-ref {
.v-list__tile__action {
flex-direction: row;
align-items: center;
}
}
} }
</style> </style>

@ -109,7 +109,7 @@
v-spacer v-spacer
template(v-if='screen === "login" && isSocialShown') template(v-if='screen === "login" && isSocialShown')
v-divider v-divider
v-card-text.grey.lighten-4.text-xs-center v-card-text.grey.lighten-4.text-center
.pb-2.body-2.text-xs-center.grey--text.text--darken-2 {{ $t('auth:orLoginUsingStrategy') }} .pb-2.body-2.text-xs-center.grey--text.text--darken-2 {{ $t('auth:orLoginUsingStrategy') }}
v-tooltip(top, v-for='strategy in strategies', :key='strategy.key') v-tooltip(top, v-for='strategy in strategies', :key='strategy.key')
.social-login-btn.mr-2( .social-login-btn.mr-2(

@ -1,4 +1,4 @@
.v-datatable { .v-data-table {
.is-clickable { .is-clickable {
cursor: pointer; cursor: pointer;
} }

@ -1,3 +1,4 @@
const graphHelper = require('../../helpers/graph')
/* global WIKI */ /* global WIKI */
@ -28,8 +29,16 @@ module.exports = {
} }
}, },
UserMutation: { UserMutation: {
create(obj, args) { async create(obj, args) {
return WIKI.models.users.createNewUser(args) try {
await WIKI.models.users.createNewUser(args)
return {
responseResult: graphHelper.generateSuccess('User created successfully')
}
} catch (err) {
return graphHelper.generateError(err)
}
}, },
delete(obj, args) { delete(obj, args) {
return WIKI.models.users.query().deleteById(args.id) return WIKI.models.users.query().deleteById(args.id)

@ -180,6 +180,20 @@ module.exports = class User extends Model {
} }
primaryEmail = _.toLower(primaryEmail) primaryEmail = _.toLower(primaryEmail)
// Find pending social user
if (!user) {
user = await WIKI.models.users.query().findOne({
email: primaryEmail,
providerId: null,
providerKey
})
if (user) {
user = await user.$query().patchAndFetch({
providerId: _.toString(profile.id)
})
}
}
// Parse display name // Parse display name
let displayName = '' let displayName = ''
if (_.isString(profile.displayName) && profile.displayName.length > 0) { if (_.isString(profile.displayName) && profile.displayName.length > 0) {
@ -365,7 +379,9 @@ module.exports = class User extends Model {
email = _.toLower(email) email = _.toLower(email)
// Input validation // Input validation
const validation = validate({ let validation = null
if (providerKey === 'local') {
validation = validate({
email, email,
passwordRaw, passwordRaw,
name name
@ -394,6 +410,29 @@ module.exports = class User extends Model {
} }
} }
}, { format: 'flat' }) }, { format: 'flat' })
} else {
validation = validate({
email,
name
}, {
email: {
email: true,
length: {
maximum: 255
}
},
name: {
presence: {
allowEmpty: false
},
length: {
minimum: 2,
maximum: 255
}
}
}, { format: 'flat' })
}
if (validation && validation.length > 0) { if (validation && validation.length > 0) {
throw new WIKI.Error.InputInvalid(validation[0]) throw new WIKI.Error.InputInvalid(validation[0])
} }
@ -402,19 +441,25 @@ module.exports = class User extends Model {
const usr = await WIKI.models.users.query().findOne({ email, providerKey }) const usr = await WIKI.models.users.query().findOne({ email, providerKey })
if (!usr) { if (!usr) {
// Create the account // Create the account
const newUsr = await WIKI.models.users.query().insert({ let newUsrData = {
provider: providerKey, providerKey,
email, email,
name, name,
password: passwordRaw,
locale: 'en', locale: 'en',
defaultEditor: 'markdown', defaultEditor: 'markdown',
tfaIsActive: false, tfaIsActive: false,
isSystem: false, isSystem: false,
isActive: true, isActive: true,
isVerified: true, isVerified: true,
mustChangePwd: (mustChangePassword === true) mustChangePwd: false
}) }
if (providerKey === `local`) {
newUsrData.password = passwordRaw
newUsrData.mustChangePwd = (mustChangePassword === true)
}
const newUsr = await WIKI.models.users.query().insert(newUsrData)
// Assign to group(s) // Assign to group(s)
if (groups.length > 0) { if (groups.length > 0) {

Loading…
Cancel
Save