diff --git a/client/css/users.css b/client/css/users.css index fb77923..81bfe2e 100644 --- a/client/css/users.css +++ b/client/css/users.css @@ -66,3 +66,6 @@ #user-edit form { width: 22.5em; } +#user-delete form { + width: 100%; +} diff --git a/client/html/user.hbs b/client/html/user.hbs index 03064e5..09783cb 100644 --- a/client/html/user.hbs +++ b/client/html/user.hbs @@ -6,6 +6,9 @@ -->{{#if this.canEditAnything}}
  • Account settings
  • {{/if}}{{#if this.canDelete}}
  • Account deletion
  • {{/if}}
    diff --git a/client/html/user_deletion.hbs b/client/html/user_deletion.hbs new file mode 100644 index 0000000..9ca822b --- /dev/null +++ b/client/html/user_deletion.hbs @@ -0,0 +1,18 @@ +
    +
    +
    +
      +
    • + + +
    • +
    +
    +
    +
    + +
    +
    +
    diff --git a/client/js/api.js b/client/js/api.js index 805fe2d..766fb47 100644 --- a/client/js/api.js +++ b/client/js/api.js @@ -27,6 +27,11 @@ class Api { return this._process(fullUrl, () => request.put(fullUrl).send(data)); } + delete(url, data) { + const fullUrl = this.getFullUrl(url); + return this._process(fullUrl, () => request.delete(fullUrl).send(data)); + } + _process(url, requestFactory) { return new Promise((resolve, reject) => { let req = requestFactory(); diff --git a/client/js/controllers/users_controller.js b/client/js/controllers/users_controller.js index fe42a87..005627e 100644 --- a/client/js/controllers/users_controller.js +++ b/client/js/controllers/users_controller.js @@ -26,6 +26,10 @@ class UsersController { '/user/:name/edit', (ctx, next) => { this.loadUserRoute(ctx, next); }, (ctx, next) => { this.editUserRoute(ctx, next); }); + page( + '/user/:name/delete', + (ctx, next) => { this.loadUserRoute(ctx, next); }, + (ctx, next) => { this.deleteUserRoute(ctx, next); }); page.exit('/user/', (ctx, next) => { this.user = null; }); } @@ -60,6 +64,18 @@ class UsersController { } } + showUserRoute(ctx, next) { + this._show(ctx.state.user, 'summary'); + } + + editUserRoute(ctx, next) { + this._show(ctx.state.user, 'edit'); + } + + deleteUserRoute(ctx, next) { + this._show(ctx.state.user, 'delete'); + } + _register(name, password, email) { const data = { name: name, @@ -119,6 +135,28 @@ class UsersController { }); } + _delete(user) { + const isLoggedIn = api.isLoggedIn() && api.user.id == user.id; + return new Promise((resolve, reject) => { + api.delete('/user/' + user.name) + .then(response => { + if (isLoggedIn) { + api.logout(); + } + resolve(); + if (api.hasPrivilege('users:list')) { + page('/users'); + } else { + page('/'); + } + events.notify(events.Success, 'Account deleted'); + }).catch(response => { + reject(); + events.notify(events.Error, response.description); + }); + }); + } + _show(user, section) { const isLoggedIn = api.isLoggedIn() && api.user.id == user.id; const infix = isLoggedIn ? 'self' : 'any'; @@ -152,18 +190,12 @@ class UsersController { canEditRank: api.hasPrivilege('users:edit:' + infix + ':rank'), canEditAvatar: api.hasPrivilege('users:edit:' + infix + ':avatar'), canEditAnything: api.hasPrivilege('users:edit:' + infix), + canDelete: api.hasPrivilege('users:delete:' + infix), ranks: ranks, edit: (...args) => { return this._edit(user, ...args); }, + delete: (...args) => { return this._delete(user, ...args); }, }); } - - showUserRoute(ctx, next) { - this._show(ctx.state.user, 'summary'); - } - - editUserRoute(ctx, next) { - this._show(ctx.state.user, 'edit'); - } } module.exports = new UsersController(); diff --git a/client/js/views/user_deletion_view.js b/client/js/views/user_deletion_view.js new file mode 100644 index 0000000..3861685 --- /dev/null +++ b/client/js/views/user_deletion_view.js @@ -0,0 +1,30 @@ +'use strict'; + +const BaseView = require('./base_view.js'); + +class UserDeletionView extends BaseView { + constructor() { + super(); + this.template = this.getTemplate('user-deletion-template'); + } + + render(ctx) { + const target = ctx.target; + const source = this.template(ctx); + + const form = source.querySelector('form'); + + this.decorateValidator(form); + + form.addEventListener('submit', e => { + e.preventDefault(); + this.clearMessages(); + this.disableForm(form); + ctx.delete(); + }); + + this.showView(target, source); + } +} + +module.exports = UserDeletionView; diff --git a/client/js/views/user_view.js b/client/js/views/user_view.js index 89cd9c8..cb38fc1 100644 --- a/client/js/views/user_view.js +++ b/client/js/views/user_view.js @@ -1,6 +1,7 @@ 'use strict'; const BaseView = require('./base_view.js'); +const UserDeletionView = require('./user_deletion_view.js'); const UserSummaryView = require('./user_summary_view.js'); const UserEditView = require('./user_edit_view.js'); @@ -8,6 +9,7 @@ class UserView extends BaseView { constructor() { super(); this.template = this.getTemplate('user-template'); + this.deletionView = new UserDeletionView(); this.summaryView = new UserSummaryView(); this.editView = new UserEditView(); } @@ -29,6 +31,8 @@ class UserView extends BaseView { let view = null; if (ctx.section == 'edit') { view = this.editView; + } else if (ctx.section == 'delete') { + view = this.deletionView; } else { view = this.summaryView; }