close #15 New account delete option

This commit is contained in:
Exodus4D
2015-09-13 16:53:42 +02:00
parent a1dbbe39d5
commit 5ffb03e73a
16 changed files with 474 additions and 122 deletions

View File

@@ -14,6 +14,13 @@ use Exception;
class User extends Controller\Controller{
/**
* valid reasons for captcha images
* @var array
*/
private static $captchaReason = ['createAccount', 'deleteAccount'];
/**
* login function
* @param $f3
@@ -87,20 +94,39 @@ class User extends Controller\Controller{
* @param $f3
*/
public function getCaptcha($f3){
$data = $f3->get('POST');
$img = new \Image();
$return = (object) [];
$return->error = [];
$imgDump = $img->captcha(
'fonts/oxygen-bold-webfont.ttf',
14,
6,
'SESSION.captcha_code',
'',
'0x66C84F',
'0x313335'
)->dump();
// check if reason for captcha generation is valid
if(
isset($data['reason']) &&
in_array( $data['reason'], self::$captchaReason)
){
$reason = $data['reason'];
echo $f3->base64( $imgDump, 'image/png');
$img = new \Image();
$imgDump = $img->captcha(
'fonts/oxygen-bold-webfont.ttf',
14,
6,
'SESSION.' . $reason,
'',
'0x66C84F',
'0x313335'
)->dump();
$return->img = $f3->base64( $imgDump, 'image/png');
}else{
$captchaError = (object) [];
$captchaError->type = 'error';
$captchaError->message = 'Could not create captcha image';
$return->error[] = $captchaError;
}
echo json_encode($return);
}
/**
@@ -270,10 +296,10 @@ class User extends Controller\Controller{
$return = (object) [];
$return->error = [];
$captcha = $f3->get('SESSION.captcha_code');
$captcha = $f3->get('SESSION.createAccount');
// reset captcha -> forces user to enter new one
$f3->clear('SESSION.captcha_code');
$f3->clear('SESSION.createAccount');
$newUserData = null;
@@ -598,4 +624,71 @@ class User extends Controller\Controller{
echo json_encode($return);
}
/**
* delete current user account from DB
* @param $f3
*/
public function deleteAccount($f3){
$data = $f3->get('POST.formData');
$return = (object) [];
$captcha = $f3->get('SESSION.deleteAccount');
// reset captcha -> forces user to enter new one
$f3->clear('SESSION.deleteAccount');
if(
isset($data['captcha']) &&
!empty($data['captcha']) &&
$data['captcha'] === $captcha
){
$user = $this->_getUser(0);
$validUser = $this->_verifyUser( $user->name, $data['password']);
if(
is_object($validUser) &&
is_object($user) &&
$user->id === $validUser->id
){
// send delete account mail
$msg = 'Hello ' . $user->name . ',<br><br>';
$msg .= 'your account data has been successfully deleted.';
$mailController = new MailController();
$status = $mailController->sendDeleteAccount($user->email, $msg);
if($status){
// save log
$logText = "id: %s, name: %s, ip: %s";
self::getLogger( $this->f3->get('PATHFINDER.LOGFILES.DELETE_ACCOUNT') )->write(
sprintf($logText, $user->id, $user->name, $f3->get('IP'))
);
// remove user
$user->erase();
$this->logOut($f3);
die();
}
}else{
// password does not match current user pw
$passwordError = (object) [];
$passwordError->type = 'error';
$passwordError->message = 'Invalid password';
$return->error[] = $passwordError;
}
}else{
// captcha not valid -> return error
$captchaError = (object) [];
$captchaError->type = 'error';
$captchaError->message = 'Captcha does not match';
$return->error[] = $captchaError;
}
echo json_encode($return);
}
}

View File

@@ -183,7 +183,8 @@ class Controller {
public function logOut($f3){
// destroy session
$f3->clear('SESSION');
$f3->clear('SESSION.user');
$f3->sync('SESSION');
if( !$f3->get('AJAX') ){
// redirect to landing page

View File

@@ -49,7 +49,6 @@ class MailController extends \SMTP{
* @return bool
*/
public function sendInviteKey($to, $msg){
$this->set('To', '<' . $to . '>');
$this->set('From', 'Pathfinder <' . Controller::getEnvironmentData('SMTP_FROM') . '>');
$this->set('Subject', 'Registration Key');
@@ -57,4 +56,19 @@ class MailController extends \SMTP{
return $status;
}
/**
* send mail to removed user account
* @param $to
* @param $msg
* @return bool
*/
public function sendDeleteAccount($to, $msg){
$this->set('To', '<' . $to . '>');
$this->set('From', 'Pathfinder <' . Controller::getEnvironmentData('SMTP_FROM') . '>');
$this->set('Subject', 'Account deleted');
$status = $this->send($msg);
return $status;
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -34,6 +34,7 @@ app/ui/dialog/manual.js
app/ui/dialog/map_settings.js
app/ui/dialog/system_effects.js
app/ui/dialog/jump_info.js
app/ui/dialog/delete_account.js
lib/jquery.lazylinepainter-1.5.1.min.js
app/ui/logo.js
app/ui/dialog/credit.js

View File

@@ -17,6 +17,7 @@ define(['jquery'], function($) {
deleteLog: 'api/user/deleteLog', // ajax URL - delete character log
saveUserConfig: 'api/user/saveConfig', // ajax URL - saves custom configuration
saveSharingConfig: 'api/user/saveSharingConfig', // ajax URL - save "sharing settings" dialog
deleteAccount: 'api/user/deleteAccount', // ajax URL - delete Account data
// access API
searchAccess: 'api/access/search', // ajax URL - search user/corporation/ally by name
// main config/map ping API

View File

@@ -21,6 +21,7 @@ define([
'dialog/map_settings',
'dialog/system_effects',
'dialog/jump_info',
'dialog/delete_account',
'dialog/credit',
'slidebars',
'app/module_map'
@@ -210,6 +211,17 @@ define([
).on('click', function(){
$(document).triggerMenuEvent('NotificationTest');
})
).append(
$('<a>', {
class: 'list-group-item',
href: '#'
}).html('&nbsp;&nbsp;Delete account').prepend(
$('<i>',{
class: 'fa fa-user-times fa-fw'
})
).on('click', function(){
$(document).triggerMenuEvent('DeleteAccount');
})
).append(
$('<a>', {
class: 'list-group-item',
@@ -510,6 +522,12 @@ define([
return false;
});
$(document).on('pf:menuDeleteAccount', function(e){
// show "delete account" dialog
$.fn.showDeleteAccountDialog();
return false;
});
$(document).on('pf:menuManual', function(e){
// show map manual
$.fn.showMapManual();

View File

@@ -7,7 +7,7 @@ define([
'app/init',
'app/util',
'app/render',
'bootbox',
'bootbox'
], function($, Init, Util, Render, bootbox) {
'use strict';
@@ -40,7 +40,7 @@ define([
};
/**
* getz active Tab link element for a dialog
* get active Tab link element for a dialog
* @param dialog
* @returns {JQuery|*}
*/
@@ -51,55 +51,6 @@ define([
return currentActiveTab;
};
/**
* generates a captcha image and return as base64 image/png
* @param callback
*/
var getCaptchaImage = function(callback){
$.ajax({
type: 'POST',
url: Init.path.getCaptcha,
data: {},
dataType: 'text'
}).done(function(base64Image){
callback(base64Image);
}).fail(function( jqXHR, status, error) {
var reason = status + ' ' + error;
Util.showNotify({title: jqXHR.status + ': saveConfig', text: reason, type: 'warning'});
});
};
/**
* clear a field and reset success/error classes
* @param fieldId
*/
var resetFormField = function(fieldId){
var field = $('#' + fieldId);
field.val('');
field.parents('.form-group').removeClass('has-error has-success');
}
/**
* request captcha image and show in form
*/
var showCaptchaImage = function(){
var captchaWrapper = $('#' + config.captchaImageWrapperId);
var captchaImage = $('#' + config.captchaImageId);
captchaWrapper.showLoadingAnimation(config.loadingOptions);
getCaptchaImage(function(base64Image){
captchaImage.attr('src', base64Image).show();
captchaWrapper.hideLoadingAnimation();
// reset captcha field
resetFormField('captcha');
});
};
/**
* init popovers in dialog
* @param dialogElement
@@ -127,7 +78,7 @@ define([
var cloneRow = dialogElement.find('.' + config.settingsCloneApiRowClass).last();
var newApiRow = cloneRow.clone();
newApiRow.find('.form-group').removeClass('has-success has-error')
newApiRow.find('.form-group').removeClass('has-success has-error');
newApiRow.find('input').val('');
cloneRow.after(newApiRow);
@@ -311,7 +262,9 @@ define([
){
form.showFormMessage(responseData.error);
showCaptchaImage();
$('#' + config.captchaImageWrapperId).showCaptchaImage('createAccount', function(){
$('#captcha').resetFormFields();
});
}else{
// store new/updated user data -> update head
if(responseData.userData){
@@ -329,7 +282,9 @@ define([
// switch tab
changeTab();
showCaptchaImage();
$('#' + config.captchaImageWrapperId).showCaptchaImage('createAccount', function(){
$('#captcha').resetFormFields();
});
}
});
@@ -344,7 +299,9 @@ define([
// set new captcha for any request
// captcha is required for sensitive data (not for all)
showCaptchaImage();
$('#' + config.captchaImageWrapperId).showCaptchaImage('createAccount', function(){
$('#captcha').resetFormFields();
});
// check for DB errors
if(jqXHR.status === 500){
@@ -400,7 +357,7 @@ define([
var form = dialogElement.find('form');
// request captcha image and show
showCaptchaImage();
$('#' + config.captchaImageWrapperId).showCaptchaImage('createAccount');
// init dialog tooltips
dialogElement.initTooltips();

View File

@@ -0,0 +1,115 @@
/**
* delete account dialog
*/
define([
'jquery',
'app/init',
'app/util',
'bootbox'
], function($, Init, Util, bootbox) {
'use strict';
var config = {
// global dialog
deleteAccountId: 'pf-dialog-delete-account', // dialog id
// captcha
captchaImageWrapperId: 'pf-dialog-captcha-wrapper' // id for "captcha image" wrapper
};
/**
* shows delete account dialog
*/
$.fn.showDeleteAccountDialog = function(){
requirejs(['text!templates/dialog/delete_account.html', 'mustache'], function(template, Mustache) {
var data = {
deleteAccountId: config.deleteAccountId,
userData: Util.getCurrentUserData(),
captchaImageWrapperId: config.captchaImageWrapperId,
formErrorContainerClass: Util.config.formErrorContainerClass
};
var content = Mustache.render(template, data);
var deleteAccountDialog = bootbox.dialog({
title: 'Delete account',
message: content,
buttons: {
close: {
label: 'cancel',
className: 'btn-default'
},
success: {
label: '<i class="fa fa-user-times fa-fw"></i>&nbsp;delete account',
className: 'btn-danger',
callback: function() {
var dialogElement = $(this);
var form = dialogElement.find('form');
// validate form
form.validator('validate');
var formValid = form.isValidForm();
if(formValid){
var formValues = form.getFormValues();
if(! $.isEmptyObject(formValues) ){
// send Tab data and store values
var requestData = {
formData: formValues
};
dialogElement.find('.modal-content').showLoadingAnimation();
$.ajax({
type: 'POST',
url: Init.path.deleteAccount,
data: requestData,
dataType: 'json'
}).done(function(responseData){
dialogElement.find('.modal-content').hideLoadingAnimation();
if(responseData.reroute !== undefined){
Util.redirect(responseData.reroute, []);
}else if(
responseData.error &&
responseData.error.length > 0
){
form.showFormMessage(responseData.error);
$('#' + config.captchaImageWrapperId).showCaptchaImage('deleteAccount', function(){
form.find('[name="captcha"], [name="password"]').resetFormFields();
});
}
}).fail(function( jqXHR, status, error) {
dialogElement.find('.modal-content').hideLoadingAnimation();
var reason = status + ' ' + error;
Util.showNotify({title: jqXHR.status + ': deleteAccount', text: reason, type: 'error'});
});
}
}
return false;
}
}
}
});
// after modal is shown =======================================================================
deleteAccountDialog.on('shown.bs.modal', function(e) {
// request captcha image and show
$('#' + config.captchaImageWrapperId).showCaptchaImage('deleteAccount');
});
});
};
});

View File

@@ -137,6 +137,73 @@ define([
});
};
/**
* show a unique generated captcha image
* @param reason
* @param callback
* @returns {*}
*/
$.fn.showCaptchaImage = function(reason, callback){
return this.each(function(){
var captchaWrapper = $(this);
var captchaImage = captchaWrapper.find('img');
captchaWrapper.showLoadingAnimation(config.loadingOptions);
getCaptchaImage(reason, function(base64Image){
captchaImage.attr('src', base64Image).show();
captchaWrapper.hideLoadingAnimation({ // config for loading overlay
icon: {
size: 'fa-2x'
}
});
if(callback){
callback();
}
});
});
};
/**
* request a captcha image
* @param callback
*/
var getCaptchaImage = function(reason, callback){
$.ajax({
type: 'POST',
url: Init.path.getCaptcha,
data: {
reason: reason
},
dataType: 'json'
}).done(function(responseData){
if(responseData.error.length > 0){
showNotify({title: 'getCaptchaImage', text: 'Captcha image gneration failed', type: 'error'});
}else{
callback(responseData.img);
}
}).fail(function( jqXHR, status, error) {
var reason = status + ' ' + error;
showNotify({title: jqXHR.status + ': getCaptchaImage', text: reason, type: 'error'});
});
};
/**
* reset/clear form fields
* @returns {*}
*/
$.fn.resetFormFields = function(){
return this.each(function(){
var field = $(this);
field.val('');
field.parents('.form-group').removeClass('has-error has-success');
});
};
/**
* show form messages
* check: showMessage() for en other way of showing messages
@@ -1433,6 +1500,22 @@ define([
return dateString + ' ' + timeString;
};
/**
* redirect
* @param url
* @param params
*/
var redirect = function(url, params){
var currentUrl = document.URL;
if(url !== currentUrl){
if(params.length > 0){
url += '?' + params.join('&');
}
window.location = url;
}
};
/**
* send logout request
*/
@@ -1444,15 +1527,8 @@ define([
data: {},
dataType: 'json'
}).done(function(data){
if(data.reroute !== undefined){
var landingPageUrl = data.reroute;
var currentUrl = document.URL;
// relocate to landing page
if(landingPageUrl !== currentUrl){
window.location = landingPageUrl + '?logout';
}
redirect(data.reroute, ['logout']);
}
}).fail(function( jqXHR, status, error) {
@@ -1504,6 +1580,7 @@ define([
getCurrentCharacterLog: getCurrentCharacterLog,
convertDateToString: convertDateToString,
formatPrice: formatPrice,
redirect: redirect,
logout: logout
};
});

View File

@@ -0,0 +1,75 @@
<div class="row" id="{{deleteAccountId}}">
<div class="col-sm-12">
<form role="form" class="form-horizontal">
{{#userData.name}}
<div class="row">
<div class="col-sm-12">
<div class="form-group">
<label class="col-sm-3 control-label">Username</label>
<div class="col-sm-9">
<p class="form-control-static">{{userData.name}}</p>
</div>
</div>
</div>
</div>
{{/userData.name}}
<div class="row">
<div class="col-sm-12">
<div class="form-group">
<label for="password" class="col-sm-3 control-label">Password</label>
<div class="col-sm-6">
<div class="input-group" title="Enter your password" data-placement="right">
<input name="password" type="password" class="form-control" id="password" placeholder="" data-error="Field is required" autocomplete="off" required>
<span class="input-group-addon"><i class="fa fa-fw fa-lock"></i></span>
</div>
<div class="help-block with-errors"></div>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-sm-12">
<div class="form-group">
<label class="col-sm-3 control-label"></label>
<div class="col-sm-6">
<p id="{{captchaImageWrapperId}}">
<img src="" />
</p>
</div>
</div>
</div>
</div>
<div class="row">
<div class="row">
<div class="col-sm-12">
<div class="form-group">
<label for="captcha" class="col-sm-3 control-label">Captcha</label>
<div class="col-sm-6">
<div class="input-group" title="Enter the characters seen above" data-placement="right">
<input name="captcha" type="text" class="form-control" id="captcha" placeholder="" data-minlength="6" data-minlength-error="Min. of 6 characters" data-error="Field is required" autocomplete="off" required>
<span class="input-group-addon"><i class="fa fa-fw fa-refresh"></i></span>
</div>
<div class="help-block with-errors"></div>
</div>
</div>
</div>
</div>
</div>
<div class="alert alert-warning">
<span class="txt-color txt-color-warning">Delete account</span>
<small>This will permanently remove your account!</small>
</div>
<div class="{{formErrorContainerClass}} alert alert-danger" style="display: none;">
<span class="txt-color txt-color-danger">Error</span>
<small> (important non-critical information)</small>
</div>
</form>
</div>
</div>