Implemented user editing
This commit is contained in:
parent
7aa680c26d
commit
b17916be4b
|
@ -1,3 +1,4 @@
|
||||||
|
const _ = require('lodash');
|
||||||
const AuthTokens = require('../authtokens');
|
const AuthTokens = require('../authtokens');
|
||||||
const bcrypt = require('bcrypt');
|
const bcrypt = require('bcrypt');
|
||||||
|
|
||||||
|
|
|
@ -83,6 +83,7 @@ Vue.filter('formatSizeSI', (value) =>
|
||||||
|
|
||||||
const Code = () => import('./route/Code.vue');
|
const Code = () => import('./route/Code.vue');
|
||||||
const AdminCodeDetail = () => import('./route/admin/CodeDetail.vue');
|
const AdminCodeDetail = () => import('./route/admin/CodeDetail.vue');
|
||||||
|
const AdminUserDetail = () => import('./route/admin/UserDetail.vue');
|
||||||
|
|
||||||
|
|
||||||
const router = new VueRouter({
|
const router = new VueRouter({
|
||||||
|
@ -101,6 +102,8 @@ const router = new VueRouter({
|
||||||
{ path: 'codes/edit/:codeParam', component: AdminCodeDetail, props: true },
|
{ path: 'codes/edit/:codeParam', component: AdminCodeDetail, props: true },
|
||||||
{ path: 'codes', component: () => import('./route/admin/Codes.vue') },
|
{ path: 'codes', component: () => import('./route/admin/Codes.vue') },
|
||||||
{ path: 'profile', component: () => import('./route/admin/Profile.vue') },
|
{ path: 'profile', component: () => import('./route/admin/Profile.vue') },
|
||||||
|
{ path: 'users/add', component: AdminUserDetail },
|
||||||
|
{ path: 'users/edit/:idParam', component: AdminUserDetail, props: true },
|
||||||
{ path: 'users', component: () => import('./route/admin/Users.vue') },
|
{ path: 'users', component: () => import('./route/admin/Users.vue') },
|
||||||
{ path: '', name: 'adminRoot', component: () => import('./route/admin/Login.vue') }
|
{ path: '', name: 'adminRoot', component: () => import('./route/admin/Login.vue') }
|
||||||
]
|
]
|
||||||
|
|
|
@ -51,7 +51,8 @@ export default {
|
||||||
uploads: {
|
uploads: {
|
||||||
created: 'Date',
|
created: 'Date',
|
||||||
code: 'Code',
|
code: 'Code',
|
||||||
owner: 'Owner'
|
owner: 'Owner',
|
||||||
|
userDeleted: 'deleted'
|
||||||
},
|
},
|
||||||
|
|
||||||
codes: {
|
codes: {
|
||||||
|
@ -60,7 +61,8 @@ export default {
|
||||||
list: {
|
list: {
|
||||||
code: 'Code',
|
code: 'Code',
|
||||||
owner: 'Owner',
|
owner: 'Owner',
|
||||||
actions: 'Actions'
|
actions: 'Actions',
|
||||||
|
userDeleted: 'deleted'
|
||||||
},
|
},
|
||||||
|
|
||||||
detail: {
|
detail: {
|
||||||
|
@ -74,6 +76,32 @@ export default {
|
||||||
message: 'Message',
|
message: 'Message',
|
||||||
messageHint: 'The message will be displayed to the user on the upload page after the code is entered. Markdown is supported.'
|
messageHint: 'The message will be displayed to the user on the upload page after the code is entered. Markdown is supported.'
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
users: {
|
||||||
|
add: 'Add user',
|
||||||
|
|
||||||
|
list: {
|
||||||
|
username: 'Username / name',
|
||||||
|
inactive: '(inactive)',
|
||||||
|
email: 'Email',
|
||||||
|
actions: 'Actions'
|
||||||
|
},
|
||||||
|
|
||||||
|
detail: {
|
||||||
|
username: 'Username',
|
||||||
|
name: 'Name',
|
||||||
|
email: 'Email',
|
||||||
|
password: 'Password',
|
||||||
|
passwordHint: 'Enter a new password to change the password. Leave blank to keep the current password.',
|
||||||
|
auth: {
|
||||||
|
title: 'Authorization',
|
||||||
|
viewAllCodes: 'Manage codes created by other users',
|
||||||
|
viewAllUploads: 'Manage uploads created by other users',
|
||||||
|
manageUsers: 'Manage users'
|
||||||
|
},
|
||||||
|
active: 'Active'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -51,7 +51,8 @@ export default {
|
||||||
uploads: {
|
uploads: {
|
||||||
created: 'Datum',
|
created: 'Datum',
|
||||||
code: 'Code',
|
code: 'Code',
|
||||||
owner: 'Eigenaar'
|
owner: 'Eigenaar',
|
||||||
|
userDeleted: 'verwijderd'
|
||||||
},
|
},
|
||||||
|
|
||||||
codes: {
|
codes: {
|
||||||
|
@ -60,7 +61,8 @@ export default {
|
||||||
list: {
|
list: {
|
||||||
code: 'Code',
|
code: 'Code',
|
||||||
owner: 'Eigenaar',
|
owner: 'Eigenaar',
|
||||||
actions: 'Acties'
|
actions: 'Acties',
|
||||||
|
userDeleted: 'verwijderd'
|
||||||
},
|
},
|
||||||
|
|
||||||
detail: {
|
detail: {
|
||||||
|
|
|
@ -4,18 +4,22 @@
|
||||||
|
|
||||||
<div v-if="codes !== null">
|
<div v-if="codes !== null">
|
||||||
<div class="pure-g list-header">
|
<div class="pure-g list-header">
|
||||||
<div class="pure-u-1-2"><span class="text">{{ $t('admin.codes.list.code') }}</span></div>
|
<div class="pure-u-9-24"><span class="text">{{ $t('admin.codes.list.code') }}</span></div>
|
||||||
<div class="pure-u-1-4"><span class="text" v-if="hasAuth('viewAllCodes')">{{ $t('admin.codes.list.owner') }}</span></div>
|
<div class="pure-u-9-24"><span class="text" v-if="hasAuth('viewAllCodes')">{{ $t('admin.codes.list.owner') }}</span></div>
|
||||||
<div class="pure-u-1-4"><span class="text">{{ $t('admin.codes.list.actions') }}</span></div>
|
<div class="pure-u-1-4"><span class="text">{{ $t('admin.codes.list.actions') }}</span></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-for="code in codes" class="list">
|
<div v-for="code in codes" class="list">
|
||||||
<div class="pure-g row">
|
<div class="pure-g row">
|
||||||
<div class="pure-u-1-2">
|
<div class="pure-u-9-24">
|
||||||
<span class="text code"><router-link :to="'codes/edit/' + encodeURIComponent(code.id)">{{ code.id }}</router-link></span>
|
<span class="text code"><router-link :to="'codes/edit/' + encodeURIComponent(code.id)">{{ code.id }}</router-link></span>
|
||||||
<span class="text description" v-if="code.description">{{ code.description }}</span>
|
<span class="text description" v-if="code.description">{{ code.description }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="pure-u-1-4"><span class="text" v-if="hasAuth('viewAllCodes')">{{ code.username }}</span></div>
|
<div class="pure-u-9-24">
|
||||||
|
<span class="text" v-if="hasAuth('viewAllCodes')" :class="{ userDeleted: !code.username }">
|
||||||
|
{{ code.username || $t('admin.codes.list.userDeleted') }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
<div class="pure-u-1-4">
|
<div class="pure-u-1-4">
|
||||||
<button class="pure-button"v-if="confirmDelete == code.id" @click.prevent="cancelDelete"><fa icon="ban"></fa></button>
|
<button class="pure-button"v-if="confirmDelete == code.id" @click.prevent="cancelDelete"><fa icon="ban"></fa></button>
|
||||||
<button class="pure-button" :class="{ confirmDelete: confirmDelete == code.id }" @click.prevent="deleteClick(code.id)"><fa icon="trash-alt"></fa></button>
|
<button class="pure-button" :class="{ confirmDelete: confirmDelete == code.id }" @click.prevent="deleteClick(code.id)"><fa icon="trash-alt"></fa></button>
|
||||||
|
|
|
@ -192,4 +192,10 @@ $list-padding: .2rem;
|
||||||
font-size: 75%;
|
font-size: 75%;
|
||||||
color: #808080;
|
color: #808080;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.userDeleted
|
||||||
|
{
|
||||||
|
font-style: italic;
|
||||||
|
color: #808080;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
|
@ -1,12 +1,16 @@
|
||||||
<template>
|
<template>
|
||||||
<div id="uploads">
|
<div id="uploads">
|
||||||
<div v-if="uploads !== null">
|
<div v-if="uploads !== null" class="list">
|
||||||
<div v-for="upload in uploads" class="list">
|
<div v-for="upload in uploads">
|
||||||
<div class="properties">
|
<div class="properties">
|
||||||
<div class="pure-g">
|
<div class="pure-g">
|
||||||
<div class="pure-u-1-1"><span class="text codedescription">{{ upload.codedescription || upload.code }}</span></div>
|
<div class="pure-u-1-1"><span class="text codedescription">{{ upload.codedescription || upload.code }}</span></div>
|
||||||
<div class="pure-u-1-3"><span class="text">{{ upload.code }}</span></div>
|
<div class="pure-u-1-3"><span class="text">{{ upload.code }}</span></div>
|
||||||
<div class="pure-u-1-3"><span class="text" v-if="hasAuth('viewAllUploads')">{{ upload.username }}</span></div>
|
<div class="pure-u-1-3">
|
||||||
|
<span class="text" v-if="hasAuth('viewAllUploads')" :class="{ userDeleted: !upload.username }">
|
||||||
|
{{ upload.username || $t('admin.uploads.userDeleted') }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
<div class="pure-u-1-3 right"><span class="text">{{ upload.created | formatDateTime }}</span></div>
|
<div class="pure-u-1-3 right"><span class="text">{{ upload.created | formatDateTime }}</span></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -0,0 +1,189 @@
|
||||||
|
<template>
|
||||||
|
<div id="user">
|
||||||
|
<div v-if="user !== null">
|
||||||
|
<form class="pure-form pure-form-aligned">
|
||||||
|
<fieldset>
|
||||||
|
<div class="pure-control-group">
|
||||||
|
<label for="username">{{ $t('admin.users.detail.username') }}</label>
|
||||||
|
<input id="username" type="text" v-model="user.username" class="pure-input-2-3">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="pure-control-group">
|
||||||
|
<label for="name">{{ $t('admin.users.detail.name') }}</label>
|
||||||
|
<input id="name" type="text" v-model="user.name" class="pure-input-2-3">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="pure-control-group">
|
||||||
|
<label for="email">{{ $t('admin.users.detail.email') }}</label>
|
||||||
|
<input id="email" type="text" v-model="user.email" class="pure-input-2-3">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="pure-control-group">
|
||||||
|
<label for="password">{{ $t('admin.users.detail.password') }}</label>
|
||||||
|
<input id="password" type="text" v-model="user.password" class="pure-input-2-3">
|
||||||
|
</div>
|
||||||
|
<div class="pure-form-description">
|
||||||
|
{{ $t('admin.users.detail.passwordHint') }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="pure-controls">
|
||||||
|
<span class="title">{{ $t('admin.users.detail.auth.title') }}</span>
|
||||||
|
<label for="authviewAllCodes" class="pure-checkbox">
|
||||||
|
<input id="authviewAllCodes" type="checkbox" v-model="user.auth.viewAllCodes">
|
||||||
|
{{ $t('admin.users.detail.auth.viewAllCodes' )}}
|
||||||
|
</label>
|
||||||
|
<label for="authviewAllUploads" class="pure-checkbox">
|
||||||
|
<input id="authviewAllUploads" type="checkbox" v-model="user.auth.viewAllUploads">
|
||||||
|
{{ $t('admin.users.detail.auth.viewAllUploads' )}}
|
||||||
|
</label>
|
||||||
|
<label for="authmanageUsers" class="pure-checkbox">
|
||||||
|
<input id="authmanageUsers" type="checkbox" v-model="user.auth.manageUsers">
|
||||||
|
{{ $t('admin.users.detail.auth.manageUsers' )}}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="pure-controls">
|
||||||
|
<label for="active" class="pure-checkbox">
|
||||||
|
<input id="active" type="checkbox" v-model="user.active"> {{ $t('admin.users.detail.active' )}}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="pure-controls">
|
||||||
|
<button class="pure-button pure-button-primary" @click="save" :disabled="saving">{{ $t('admin.save') }} <fa icon="spinner" pulse v-if="saving"></fa></button>
|
||||||
|
<router-link to="/admin/users" class="pure-button">{{ $t('admin.cancel') }}</router-link>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import axios from 'axios';
|
||||||
|
import shared from '../../shared';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data()
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
user: null,
|
||||||
|
saving: false
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
props: ['idParam'],
|
||||||
|
|
||||||
|
|
||||||
|
watch: {
|
||||||
|
'$route' (to, from)
|
||||||
|
{
|
||||||
|
var self = this;
|
||||||
|
self.checkId();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
created()
|
||||||
|
{
|
||||||
|
var self = this;
|
||||||
|
self.checkId();
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
checkId()
|
||||||
|
{
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
if (self.idParam)
|
||||||
|
{
|
||||||
|
axios.get('/admin/users/' + encodeURIComponent(self.idParam), {
|
||||||
|
headers: {
|
||||||
|
Authorization: 'Bearer ' + shared.adminToken
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then((response) =>
|
||||||
|
{
|
||||||
|
var user = response.data;
|
||||||
|
var auth = user.auth;
|
||||||
|
|
||||||
|
user.auth = {
|
||||||
|
viewAllCodes: auth.indexOf('viewAllCodes') > -1,
|
||||||
|
viewAllUploads: auth.indexOf('viewAllUploads') > -1,
|
||||||
|
manageUsers: auth.indexOf('manageUsers') > -1
|
||||||
|
}
|
||||||
|
|
||||||
|
self.user = user;
|
||||||
|
})
|
||||||
|
.catch((error) => { shared.$emit('apiError', error, this.$router) });
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
self.user = {
|
||||||
|
expiration: null,
|
||||||
|
message: null
|
||||||
|
};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
getCodeUrl(user)
|
||||||
|
{
|
||||||
|
var port = ':' + window.location.port;
|
||||||
|
|
||||||
|
if ((window.location.protocol == 'http:' && window.location.port == 80) ||
|
||||||
|
(window.location.protocol == 'https:' && window.location.port == 443))
|
||||||
|
port = '';
|
||||||
|
|
||||||
|
return window.location.protocol + '//' +
|
||||||
|
window.location.hostname + port + '/c/' +
|
||||||
|
encodeURIComponent(user);
|
||||||
|
},
|
||||||
|
|
||||||
|
save()
|
||||||
|
{
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
self.saving = true;
|
||||||
|
|
||||||
|
var auth = [];
|
||||||
|
if (self.user.auth.viewAllCodes) auth.push('viewAllCodes');
|
||||||
|
if (self.user.auth.viewAllUploads) auth.push('viewAllUploads');
|
||||||
|
if (self.user.auth.manageUsers) auth.push('manageUsers');
|
||||||
|
|
||||||
|
axios.post('/admin/users', {
|
||||||
|
id: self.user.id,
|
||||||
|
username: self.user.username,
|
||||||
|
name: self.user.name,
|
||||||
|
password: self.user.password,
|
||||||
|
email: self.user.email,
|
||||||
|
auth: auth,
|
||||||
|
active: self.user.active
|
||||||
|
}, {
|
||||||
|
headers: {
|
||||||
|
Authorization: 'Bearer ' + shared.adminToken
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then((response) =>
|
||||||
|
{
|
||||||
|
if (self.user.id)
|
||||||
|
this.$router.push('/admin/users');
|
||||||
|
else
|
||||||
|
this.$router.push('/admin/users/edit/' + response.data);
|
||||||
|
})
|
||||||
|
.catch((error) => { shared.$emit('apiError', error, this.$router) })
|
||||||
|
.then(() =>
|
||||||
|
{
|
||||||
|
self.saving = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.title
|
||||||
|
{
|
||||||
|
display: block;
|
||||||
|
margin-bottom: .5rem;
|
||||||
|
color: #808080;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -1,15 +1,113 @@
|
||||||
<template>
|
<template>
|
||||||
<div id="users">
|
<div id="users">
|
||||||
Users!
|
<router-link to="users/add" class="pure-button pure-button-primary">{{ $t('admin.users.add') }}</router-link>
|
||||||
|
|
||||||
|
<div v-if="users !== null">
|
||||||
|
<div class="pure-g list-header">
|
||||||
|
<div class="pure-u-9-24"><span class="text">{{ $t('admin.users.list.username') }}</span></div>
|
||||||
|
<div class="pure-u-9-24"><span class="text">{{ $t('admin.users.list.email') }}</span></div>
|
||||||
|
<div class="pure-u-1-4"><span class="text">{{ $t('admin.users.list.actions') }}</span></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-for="user in users" class="list">
|
||||||
|
<div class="pure-g row" :class="{ inactive: !user.active }">
|
||||||
|
<div class="pure-u-9-24">
|
||||||
|
<span class="text"><router-link :to="'users/edit/' + encodeURIComponent(user.id)">{{ user.username }}</router-link></span>
|
||||||
|
<span class="text name">{{ user.name }}<span v-if="!user.active"> {{ $t('admin.users.list.inactive') }}</span></span>
|
||||||
|
</div>
|
||||||
|
<div class="pure-u-9-24"><span class="text">{{ user.email }}</span></div>
|
||||||
|
<div class="pure-u-1-4">
|
||||||
|
<button class="pure-button"v-if="confirmDelete == user.id" @click.prevent="cancelDelete"><fa icon="ban"></fa></button>
|
||||||
|
<button class="pure-button" :class="{ confirmDelete: confirmDelete == user.id }" @click.prevent="deleteClick(user.id)"><fa icon="trash-alt"></fa></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="users.length == 0" class="nodata">
|
||||||
|
{{ $t('admin.empty') }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-else class="loading">
|
||||||
|
{{ $t('admin.loading') }}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import axios from 'axios';
|
||||||
|
import shared from '../../shared';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data()
|
data()
|
||||||
{
|
{
|
||||||
return {
|
return {
|
||||||
|
users: null,
|
||||||
|
confirmDelete: null
|
||||||
};
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
created()
|
||||||
|
{
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
axios.get('/admin/users', {
|
||||||
|
headers: {
|
||||||
|
Authorization: 'Bearer ' + shared.adminToken
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then((response) =>
|
||||||
|
{
|
||||||
|
self.users = _.orderBy(response.data, ['active', 'username'], ['desc', 'asc']);
|
||||||
|
})
|
||||||
|
.catch((error) => { shared.$emit('apiError', error, this.$router) });
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
deleteClick(userId)
|
||||||
|
{
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
if (self.confirmDelete == userId)
|
||||||
|
{
|
||||||
|
self.confirmDelete = null;
|
||||||
|
|
||||||
|
axios.delete('/admin/users/' + encodeURIComponent(userId), {
|
||||||
|
headers: {
|
||||||
|
Authorization: 'Bearer ' + shared.adminToken
|
||||||
|
}})
|
||||||
|
.then((response) =>
|
||||||
|
{
|
||||||
|
var index = _.findIndex(self.users, (item) => { return item.id == userId; });
|
||||||
|
if (index > -1)
|
||||||
|
self.users.splice(index, 1);
|
||||||
|
})
|
||||||
|
.catch((error) => { shared.$emit('apiError', error, this.$router) });
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
self.confirmDelete = userId;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
cancelDelete()
|
||||||
|
{
|
||||||
|
var self = this;
|
||||||
|
self.confirmDelete = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.row.inactive .text
|
||||||
|
{
|
||||||
|
color: #808080;
|
||||||
|
}
|
||||||
|
|
||||||
|
.name
|
||||||
|
{
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
</style>
|
Loading…
Reference in New Issue