New "Invite" feature implemented
This commit is contained in:
@@ -6,7 +6,7 @@ DEBUG = 0
|
||||
; If TRUE, the framework, after having logged stack trace and errors, stops execution (die without any status) when a non-fatal error is detected.
|
||||
HALT = FALSE
|
||||
|
||||
ONERROR = "Controller\MapController->showError"
|
||||
ONERROR = "Controller\Controller->showError"
|
||||
|
||||
; Timezone to use. Sync program with eve server time
|
||||
TZ = "UTC"
|
||||
|
||||
@@ -451,7 +451,7 @@ class Map extends \Controller\AccessController {
|
||||
}
|
||||
|
||||
}else{
|
||||
// user logged of
|
||||
// user logged off
|
||||
$return->error[] = $this->getUserLoggedOffError();
|
||||
}
|
||||
|
||||
@@ -498,14 +498,16 @@ class Map extends \Controller\AccessController {
|
||||
$return = (object) [];
|
||||
$return->error = [];
|
||||
|
||||
if( !empty($f3->get('POST.mapIds')) ){
|
||||
$mapIds = (array)$f3->get('POST.mapIds');
|
||||
// check if data for specific system is requested
|
||||
$systemData = (array)$f3->get('POST.systemData');
|
||||
$user = $this->_getUser();
|
||||
|
||||
if($user){
|
||||
|
||||
if( !empty($f3->get('POST.mapIds')) ){
|
||||
$mapIds = (array)$f3->get('POST.mapIds');
|
||||
// check if data for specific system is requested
|
||||
$systemData = (array)$f3->get('POST.systemData');
|
||||
|
||||
$user = $this->_getUser();
|
||||
|
||||
if($user){
|
||||
// update current location (IGB data)
|
||||
$user->updateCharacterLog(60 * 5);
|
||||
|
||||
@@ -551,16 +553,17 @@ class Map extends \Controller\AccessController {
|
||||
// with the same main char
|
||||
$return = $f3->get($cacheKey);
|
||||
}
|
||||
|
||||
// get current user data -> this should not be cached because each user has different personal data
|
||||
// even if they have multiple characters using the same map!
|
||||
$return->userData = $user->getData();
|
||||
}else{
|
||||
// user logged of
|
||||
$return->error[] = $this->getUserLoggedOffError();
|
||||
}
|
||||
|
||||
// get current user data -> this should not be cached because each user has different personal data
|
||||
// even if they have multiple characters using the same map!
|
||||
$return->userData = $user->getData();
|
||||
}else{
|
||||
// user logged off
|
||||
$return->error[] = $this->getUserLoggedOffError();
|
||||
}
|
||||
|
||||
|
||||
echo json_encode( $return );
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
namespace Controller\Api;
|
||||
use Controller;
|
||||
use controller\MailController;
|
||||
use Model;
|
||||
use Exception;
|
||||
|
||||
@@ -205,6 +206,60 @@ class User extends Controller\Controller{
|
||||
echo json_encode($return);
|
||||
}
|
||||
|
||||
/**
|
||||
* search for a registration key model
|
||||
* e.g. for new user registration with "invite" feature enabled
|
||||
* @param $email
|
||||
* @param $registrationKey
|
||||
* @return bool|Model\RegistrationKeyModel
|
||||
* @throws Exception
|
||||
*/
|
||||
protected function getRegistrationKey($email, $registrationKey){
|
||||
$registrationKeyModel = Model\BasicModel::getNew('RegistrationKeyModel');
|
||||
$registrationKeyModel->load([
|
||||
'registrationKey = :registrationKey AND
|
||||
email = :email AND
|
||||
used = 0 AND
|
||||
active = 1',
|
||||
':registrationKey' => $registrationKey,
|
||||
':email' => $email
|
||||
]);
|
||||
|
||||
if( $registrationKeyModel->dry() ){
|
||||
return false;
|
||||
}else{
|
||||
return $registrationKeyModel;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* check if there is already an active Key for a mail
|
||||
* @param $email
|
||||
* @param bool|false $used
|
||||
* @return bool|null
|
||||
* @throws Exception
|
||||
*/
|
||||
protected function findRegistrationKey($email, $used = false){
|
||||
|
||||
$queryPart = 'email = :email AND active = 1';
|
||||
|
||||
if(is_int($used)){
|
||||
$queryPart .= ' AND used = ' . $used;
|
||||
}
|
||||
|
||||
$registrationKeyModel = Model\BasicModel::getNew('RegistrationKeyModel');
|
||||
$registrationKeyModels = $registrationKeyModel->find([
|
||||
$queryPart,
|
||||
':email' => $email
|
||||
]);
|
||||
|
||||
if( is_object($registrationKeyModels) ){
|
||||
return $registrationKeyModels;
|
||||
}else{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* save/update user data
|
||||
* @param $f3
|
||||
@@ -225,6 +280,10 @@ class User extends Controller\Controller{
|
||||
// check user if if he is new
|
||||
$loginAfterSave = false;
|
||||
|
||||
// valid registration key Model is required for new registration
|
||||
// if "invite" feature is enabled
|
||||
$registrationKeyModel = false;
|
||||
|
||||
if( isset($data['settingsData']) ){
|
||||
$settingsData = $data['settingsData'];
|
||||
|
||||
@@ -242,6 +301,16 @@ class User extends Controller\Controller{
|
||||
// change/set sensitive user data requires captcha!
|
||||
|
||||
if($user === false){
|
||||
|
||||
// check if registration key invite function is enabled
|
||||
if($f3->get('PATHFINDER.REGISTRATION.INVITE') === 1 ){
|
||||
$registrationKeyModel = $this->getRegistrationKey( $settingsData['email'], $settingsData['registrationKey'] );
|
||||
|
||||
if($registrationKeyModel === false){
|
||||
throw new Exception\RegistrationException('Registration key invalid', 'registrationKey');
|
||||
}
|
||||
}
|
||||
|
||||
// new user registration
|
||||
$user = $mapType = Model\BasicModel::getNew('UserModel');
|
||||
$loginAfterSave = true;
|
||||
@@ -359,6 +428,11 @@ class User extends Controller\Controller{
|
||||
// this will fail if model validation fails!
|
||||
$user->save();
|
||||
|
||||
if(is_object($registrationKeyModel)){
|
||||
$registrationKeyModel->used = 1;
|
||||
$registrationKeyModel->save();
|
||||
}
|
||||
|
||||
// log user in (in case he is new
|
||||
if($loginAfterSave){
|
||||
$this->logUserIn( $user->name, $settingsData['password'] );
|
||||
@@ -380,6 +454,7 @@ class User extends Controller\Controller{
|
||||
}catch(Exception\RegistrationException $e){
|
||||
$registrationError = (object) [];
|
||||
$registrationError->type = 'error';
|
||||
$registrationError->field = $e->getField();
|
||||
$registrationError->message = $e->getMessage();
|
||||
$return->error[] = $registrationError;
|
||||
}
|
||||
@@ -390,4 +465,107 @@ class User extends Controller\Controller{
|
||||
}
|
||||
echo json_encode($return);
|
||||
}
|
||||
|
||||
/**
|
||||
* send mail with registration key
|
||||
* -> check INVITE in pathfinder.ini
|
||||
* @param $f3
|
||||
* @throws Exception
|
||||
*/
|
||||
public function sendRegistration($f3){
|
||||
$data = $f3->get('POST.settingsData');
|
||||
$return = (object) [];
|
||||
|
||||
// check invite limit
|
||||
// get handed out key count
|
||||
$tempRegistrationKeyModel = Model\BasicModel::getNew('RegistrationKeyModel');
|
||||
$tempRegistrationKeyModels = $tempRegistrationKeyModel->find([ '
|
||||
email != "" AND
|
||||
active = 1'
|
||||
]);
|
||||
|
||||
$totalKeys = 0;
|
||||
if(is_object($tempRegistrationKeyModels)){
|
||||
$totalKeys = $tempRegistrationKeyModels->count();
|
||||
}
|
||||
|
||||
if(
|
||||
$f3->get('PATHFINDER.REGISTRATION.INVITE') == 1 &&
|
||||
$totalKeys < $f3->get('PATHFINDER.REGISTRATION.INVITE_LIMIT')
|
||||
){
|
||||
// key limit not reached
|
||||
|
||||
if(
|
||||
isset($data['email']) &&
|
||||
!empty($data['email'])
|
||||
){
|
||||
$email = trim($data['email']);
|
||||
|
||||
// check if mail is valid
|
||||
if( \Audit::instance()->email($email) ){
|
||||
|
||||
// new key for this mail is allowed
|
||||
$registrationKeyModel = $this->findRegistrationKey($email, 0);
|
||||
|
||||
if($registrationKeyModel === false){
|
||||
|
||||
// check for total number of invites (active and inactive) -> prevent spamming
|
||||
$allRegistrationKeysByMail = $this->findRegistrationKey($email);
|
||||
|
||||
if(
|
||||
$allRegistrationKeysByMail == false ||
|
||||
$allRegistrationKeysByMail->count() < 3
|
||||
){
|
||||
|
||||
// get a fresh key
|
||||
$registrationKeyModel = Model\BasicModel::getNew('RegistrationKeyModel');
|
||||
$registrationKeyModel->load(['
|
||||
used = 0 AND
|
||||
active = 1 AND
|
||||
email = "" ',
|
||||
':email' => $email
|
||||
], ['limit' => 1]);
|
||||
|
||||
}else{
|
||||
$validationError = (object) [];
|
||||
$validationError->type = 'warning';
|
||||
$validationError->message = 'The number of keys is limited per an Email. You can not get more keys';
|
||||
$return->error[] = $validationError;
|
||||
}
|
||||
|
||||
}else{
|
||||
$registrationKeyModel = $registrationKeyModel[0];
|
||||
}
|
||||
|
||||
// send "old" key again or send a new key
|
||||
if( is_object($registrationKeyModel) ){
|
||||
$msg = 'Your personal Registration Key: ' . $registrationKeyModel->registrationKey;
|
||||
|
||||
$mailController = new MailController();
|
||||
$status = $mailController->sendRegistrationKey($email, $msg);
|
||||
|
||||
if( $status ){
|
||||
$registrationKeyModel->email = $email;
|
||||
$registrationKeyModel->ip = $this->f3->get('IP');
|
||||
$registrationKeyModel->save();
|
||||
}
|
||||
}
|
||||
|
||||
}else{
|
||||
$validationError = (object) [];
|
||||
$validationError->type = 'error';
|
||||
$validationError->field = 'email';
|
||||
$validationError->message = 'Email is not valid';
|
||||
$return->error[] = $validationError;
|
||||
}
|
||||
}
|
||||
}else{
|
||||
$validationError = (object) [];
|
||||
$validationError->type = 'warning';
|
||||
$validationError->message = 'The pool of beta keys has been exhausted, please try again in a few days/weeks';
|
||||
$return->error[] = $validationError;
|
||||
}
|
||||
|
||||
echo json_encode($return);
|
||||
}
|
||||
}
|
||||
@@ -40,7 +40,8 @@ class CcpApiController extends Controller{
|
||||
$requestOptions = [
|
||||
'timeout' => 8,
|
||||
'method' => 'POST',
|
||||
'user_agent' => $this->getUserAgent()
|
||||
'user_agent' => $this->getUserAgent(),
|
||||
'follow_location' => false // otherwise CURLOPT_FOLLOWLOCATION will fail
|
||||
];
|
||||
|
||||
return $requestOptions;
|
||||
@@ -94,7 +95,10 @@ class CcpApiController extends Controller{
|
||||
// request successful
|
||||
$rowApiData = $xml->result->key->rowset;
|
||||
|
||||
if($rowApiData->children()){
|
||||
if(
|
||||
is_object($rowApiData) &&
|
||||
$rowApiData->children()
|
||||
){
|
||||
$characterModel = Model\BasicModel::getNew('CharacterModel');
|
||||
$corporationModel = Model\BasicModel::getNew('CorporationModel');
|
||||
$allianceModel = Model\BasicModel::getNew('AllianceModel');
|
||||
@@ -151,7 +155,6 @@ class CcpApiController extends Controller{
|
||||
}
|
||||
|
||||
$userApiModel->userCharacters->rewind();
|
||||
|
||||
}
|
||||
|
||||
$characterModel->id = $characterId;
|
||||
|
||||
@@ -256,4 +256,39 @@ class Controller {
|
||||
return $data;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* function is called on each error
|
||||
* @param $f3
|
||||
*/
|
||||
public function showError($f3){
|
||||
|
||||
// set HTTP status
|
||||
if(!empty($f3->get('ERROR.code'))){
|
||||
$f3->status($f3->get('ERROR.code'));
|
||||
}
|
||||
|
||||
if($f3->get('AJAX')){
|
||||
header('Content-type: application/json');
|
||||
|
||||
// error on ajax call
|
||||
$errorData = [
|
||||
'status' => $f3->get('ERROR.status'),
|
||||
'code' => $f3->get('ERROR.code'),
|
||||
'text' => $f3->get('ERROR.text')
|
||||
];
|
||||
|
||||
// append stack trace for greater debug level
|
||||
if( $f3->get('DEBUG') === 3){
|
||||
$errorData['trace'] = $f3->get('ERROR.trace');
|
||||
}
|
||||
|
||||
echo json_encode($errorData);
|
||||
}else{
|
||||
echo $f3->get('ERROR.text');
|
||||
}
|
||||
|
||||
die();
|
||||
}
|
||||
|
||||
}
|
||||
43
app/main/controller/mailcontroller.php
Normal file
43
app/main/controller/mailcontroller.php
Normal file
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: Exodus
|
||||
* Date: 30.08.2015
|
||||
* Time: 14:48
|
||||
*/
|
||||
|
||||
namespace controller;
|
||||
|
||||
class MailController extends \SMTP{
|
||||
|
||||
|
||||
public function __construct(){
|
||||
|
||||
$host = Controller::getEnvironmentData('SMTP_HOST');
|
||||
$port = Controller::getEnvironmentData('SMTP_PORT');
|
||||
$scheme = Controller::getEnvironmentData('SMTP_SCHEME');
|
||||
$user = Controller::getEnvironmentData('SMTP_USER');
|
||||
$pw = Controller::getEnvironmentData('SMTP_PASS');
|
||||
|
||||
parent::__construct($host,$port,$scheme,$user,$pw);
|
||||
|
||||
// error handling
|
||||
$this->set('Errors-to', '' . Controller::getEnvironmentData('SMTP_ERROR') . '>');
|
||||
}
|
||||
|
||||
/**
|
||||
* send registration key
|
||||
* @param $to
|
||||
* @param $msg
|
||||
* @return bool
|
||||
*/
|
||||
public function sendRegistrationKey($to, $msg){
|
||||
|
||||
$this->set('To', '"<' . $to . '>');
|
||||
$this->set('From', '"PATHFINDER" <' . Controller::getEnvironmentData('SMTP_FROM') . '>');
|
||||
$this->set('Subject', 'PATHFINDERR - Registration Key');
|
||||
$status = $this->send($msg);
|
||||
|
||||
return $status;
|
||||
}
|
||||
}
|
||||
@@ -31,38 +31,4 @@ class MapController extends \Controller\AccessController {
|
||||
$this->setTemplate('templates/view/index.html');
|
||||
}
|
||||
|
||||
/**
|
||||
* function is called on each error
|
||||
* @param $f3
|
||||
*/
|
||||
public function showError($f3){
|
||||
|
||||
// set HTTP status
|
||||
if(!empty($f3->get('ERROR.code'))){
|
||||
$f3->status($f3->get('ERROR.code'));
|
||||
}
|
||||
|
||||
if($f3->get('AJAX')){
|
||||
header('Content-type: application/json');
|
||||
|
||||
// error on ajax call
|
||||
$errorData = [
|
||||
'status' => $f3->get('ERROR.status'),
|
||||
'code' => $f3->get('ERROR.code'),
|
||||
'text' => $f3->get('ERROR.text')
|
||||
];
|
||||
|
||||
// append stack trace for greater debug level
|
||||
if( $f3->get('DEBUG') === 3){
|
||||
$errorData['trace'] = $f3->get('ERROR.trace');
|
||||
}
|
||||
|
||||
echo json_encode($errorData);
|
||||
}else{
|
||||
echo $f3->get('ERROR.text');
|
||||
}
|
||||
|
||||
die();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -11,8 +11,30 @@ namespace Exception;
|
||||
|
||||
class RegistrationException extends BaseException{
|
||||
|
||||
public function __construct($message){
|
||||
/**
|
||||
* form field name that causes this exception
|
||||
* @var string
|
||||
*/
|
||||
private $field;
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getField(){
|
||||
return $this->field;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $field
|
||||
*/
|
||||
public function setField($field){
|
||||
$this->field = $field;
|
||||
}
|
||||
|
||||
public function __construct($message, $field = ''){
|
||||
|
||||
parent::__construct($message, self::REGISTRATION_FAILED);
|
||||
|
||||
$this->setField($field);
|
||||
}
|
||||
}
|
||||
16
app/main/model/registrationkeymodel.php
Normal file
16
app/main/model/registrationkeymodel.php
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: exodus4d
|
||||
* Date: 29.08.15
|
||||
* Time: 11:57
|
||||
*/
|
||||
|
||||
namespace Model;
|
||||
|
||||
|
||||
class RegistrationKeyModel extends BasicModel {
|
||||
|
||||
protected $table = 'registration_key';
|
||||
|
||||
}
|
||||
@@ -35,7 +35,7 @@ class UserModel extends BasicModel {
|
||||
'name' => [
|
||||
'length' => [
|
||||
'min' => 5,
|
||||
'max' => 20
|
||||
'max' => 25
|
||||
]
|
||||
],
|
||||
'email' => [
|
||||
|
||||
@@ -46,6 +46,15 @@ DB_CCP_NAME = eve_test
|
||||
DB_CCP_USER = root
|
||||
DB_CCP_PASS =
|
||||
|
||||
; SMTP settings
|
||||
SMTP_HOST = localhost
|
||||
SMTP_PORT = 25
|
||||
SMTP_SCHEME = ""
|
||||
SMTP_USER = pathfinder
|
||||
SMTP_PASS = root
|
||||
|
||||
SMTP_FROM = pathfinder@localhost.com
|
||||
|
||||
[PATHFINDER.ENVIRONMENT.PRODUCTION]
|
||||
BASE = /www/htdocs/w0128162/www.pathfinder.exodus4d.de
|
||||
|
||||
@@ -70,6 +79,16 @@ DB_CCP_NAME = d01f20be
|
||||
DB_CCP_USER = d01f20be
|
||||
DB_CCP_PASS = 2gkBWs87zDcApH4A
|
||||
|
||||
; SMTP settings
|
||||
SMTP_HOST = localhost
|
||||
SMTP_PORT = 25
|
||||
SMTP_SCHEME = TLS
|
||||
SMTP_USER = pathfinder
|
||||
SMTP_PASS = root
|
||||
|
||||
SMTP_FROM = pathfinder@localhost.com
|
||||
SMTP_ERROR = pathfinder@localhost.com
|
||||
|
||||
; ======================================================================================================
|
||||
[PATHFINDER.REGISTRATION]
|
||||
; registration status (0=disabled, 1=enabled)
|
||||
@@ -77,6 +96,12 @@ STATUS = 1
|
||||
; disabled message
|
||||
MSG_DISABLED = "User registration is currently not allowed"
|
||||
|
||||
; use the invite system e.g. beta testing. A "registration key" is required (0=disabled, 1=enabled)
|
||||
INVITE = 1
|
||||
|
||||
; the limit of registration keys. Increase it to hand out more keys
|
||||
INVITE_LIMIT = 50
|
||||
|
||||
; ======================================================================================================
|
||||
; Lifetime for map types
|
||||
[PATHFINDER.MAP.PRIVATE]
|
||||
@@ -107,10 +132,6 @@ DBL_CLICK = 250
|
||||
; time for status change visibility in header (ms)
|
||||
PROGRAM_STATUS_VISIBLE = 5000
|
||||
|
||||
; get all client map data (ms)
|
||||
[PATHFINDER.TIMER.GET_CLIENT_MAP_DATA]
|
||||
EXECUTION_LIMIT = 50
|
||||
|
||||
; main map update ping (ajax) (ms)
|
||||
[PATHFINDER.TIMER.UPDATE_SERVER_MAP]
|
||||
DELAY = 5000
|
||||
|
||||
@@ -11,6 +11,7 @@ define(['jquery'], function($) {
|
||||
img: 'public/img/', // path for images
|
||||
// user API
|
||||
getCaptcha: 'api/user/getCaptcha', // ajax URL - get captcha image
|
||||
sendRegistrationKey: 'api/user/sendRegistration', // ajax URL - send registration key
|
||||
logIn: 'api/user/logIn', // ajax URL - login
|
||||
logOut: 'api/user/logOut', // ajax URL - logout
|
||||
deleteLog: 'api/user/deleteLog', // ajax URL - delete character log
|
||||
|
||||
@@ -55,9 +55,11 @@ define([
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* set page observer
|
||||
*/
|
||||
var setPageObserver = function(){
|
||||
|
||||
|
||||
// login form =====================================================================================
|
||||
// register buttons ---------------------------------------------
|
||||
$('.' + config.registerButtonClass).on('click', function(e){
|
||||
@@ -67,7 +69,10 @@ define([
|
||||
Util.logout();
|
||||
|
||||
// show register/settings dialog
|
||||
$.fn.showSettingsDialog(true);
|
||||
$.fn.showSettingsDialog({
|
||||
register: 1,
|
||||
invite : parseInt( $('body').data('invite') )
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -142,6 +147,87 @@ define([
|
||||
|
||||
};
|
||||
|
||||
var showRequestRegistrationKeyDialog = function(){
|
||||
var data = {
|
||||
id: config.signatureReaderDialogId,
|
||||
formErrorContainerClass: Util.config.formErrorContainerClass,
|
||||
formWarningContainerClass: Util.config.formWarningContainerClass
|
||||
};
|
||||
|
||||
requirejs(['text!templates/dialog/registration.html', 'mustache'], function(template, Mustache) {
|
||||
|
||||
var content = Mustache.render(template, data);
|
||||
|
||||
var registrationKeyDialog = bootbox.dialog({
|
||||
title: 'Registration Key',
|
||||
message: content,
|
||||
buttons: {
|
||||
close: {
|
||||
label: 'cancel',
|
||||
className: 'btn-default'
|
||||
},
|
||||
success: {
|
||||
label: '<i class="fa fa-envelope fa-fw"></i> send',
|
||||
className: 'btn-success',
|
||||
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 = {
|
||||
settingsData: formValues
|
||||
};
|
||||
|
||||
var modalContent = registrationKeyDialog.find('.modal-content');
|
||||
modalContent.showLoadingAnimation();
|
||||
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: Init.path.sendRegistrationKey,
|
||||
data: requestData,
|
||||
dataType: 'json'
|
||||
}).done(function(responseData){
|
||||
|
||||
if(
|
||||
responseData.error &&
|
||||
responseData.error.length > 0
|
||||
){
|
||||
form.showFormMessage(responseData.error);
|
||||
|
||||
|
||||
}else{
|
||||
$('.modal').modal('hide');
|
||||
Util.showNotify({title: 'Registration Key send', text: 'Check your Mails', type: 'success'});
|
||||
}
|
||||
|
||||
modalContent.hideLoadingAnimation();
|
||||
}).fail(function( jqXHR, status, error) {
|
||||
modalContent.hideLoadingAnimation();
|
||||
|
||||
var reason = status + ' ' + error;
|
||||
Util.showNotify({title: jqXHR.status + ': send Registration Key', text: reason, type: 'error'});
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* init image carousel
|
||||
@@ -387,6 +473,12 @@ define([
|
||||
}
|
||||
}
|
||||
|
||||
// show get registration key dialog
|
||||
var showRegistrationDialog = location.search.split('register')[1];
|
||||
if(showRegistrationDialog !== undefined){
|
||||
showRequestRegistrationKeyDialog();
|
||||
}
|
||||
|
||||
// init scrollspy
|
||||
initScrollspy();
|
||||
|
||||
|
||||
@@ -65,8 +65,6 @@ define([
|
||||
var mapModule = $(this);
|
||||
|
||||
// log keys ------------------------------------------------------------------------
|
||||
// get map data from client
|
||||
var logKeyGetClientMapData = 'GET_CLIENT_MAP_DATA';
|
||||
|
||||
// ajax request update map data
|
||||
var logKeyServerMapData = 'UPDATE_SERVER_MAP';
|
||||
@@ -98,12 +96,7 @@ define([
|
||||
}
|
||||
|
||||
// get updated map data
|
||||
Util.timeStart(logKeyGetClientMapData);
|
||||
var updatedMapData = mapModule.getMapModuleDataForUpdate();
|
||||
var mapDataLogDuration = Util.timeStop(logKeyGetClientMapData);
|
||||
|
||||
// log execution time
|
||||
Util.log(logKeyGetClientMapData, {duration: mapDataLogDuration, type: 'client', description: 'get client data'});
|
||||
|
||||
// wrap array to object
|
||||
updatedMapData = {mapData: updatedMapData};
|
||||
@@ -223,7 +216,7 @@ define([
|
||||
if(userData.character === undefined){
|
||||
// no active character found -> show settings dialog
|
||||
|
||||
Util.showNotify({title: 'No main character found', text: 'Set up your main character', type: 'error'});
|
||||
Util.showNotify({title: 'Main character missing', text: 'Check API and set a main character', type: 'error'});
|
||||
|
||||
$(document).triggerMenuEvent('ShowSettingsDialog');
|
||||
}
|
||||
|
||||
@@ -518,7 +518,10 @@ define([
|
||||
|
||||
$(document).on('pf:menuShowSettingsDialog', function(e){
|
||||
// show character select dialog
|
||||
$.fn.showSettingsDialog(false);
|
||||
$.fn.showSettingsDialog({
|
||||
register: 0,
|
||||
invite : parseInt( $('body').data('invite') )
|
||||
});
|
||||
return false;
|
||||
});
|
||||
|
||||
|
||||
@@ -154,8 +154,10 @@ define([
|
||||
|
||||
/**
|
||||
* show "register/settings" dialog
|
||||
* @param options
|
||||
* @returns {boolean}
|
||||
*/
|
||||
$.fn.showSettingsDialog = function(register){
|
||||
$.fn.showSettingsDialog = function(options){
|
||||
|
||||
// check if there is already a settings dialog open
|
||||
var settingsDialog = $('#' + config.settingsDialogId);
|
||||
@@ -191,7 +193,7 @@ define([
|
||||
requirejs(['text!templates/dialog/settings.html', 'mustache'], function(template, Mustache) {
|
||||
|
||||
// if this is a new registration there is no API key -> fake an empty API to make fields visible
|
||||
if(register){
|
||||
if(options.register === 1){
|
||||
Init.currentUserData = {};
|
||||
Init.currentUserData.api = [{
|
||||
keyId: '',
|
||||
@@ -206,7 +208,8 @@ define([
|
||||
|
||||
var data = {
|
||||
id: config.settingsDialogId,
|
||||
register: register,
|
||||
register: options.register === 1 ? 1 : 0,
|
||||
invite : options.invite === 1 ? 1 : 0,
|
||||
navigationClass: config.dialogWizardNavigationClass,
|
||||
userData: Init.currentUserData,
|
||||
cloneApiRowClass: config.settingsCloneApiRowClass,
|
||||
@@ -221,7 +224,7 @@ define([
|
||||
var content = Mustache.render(template, data);
|
||||
|
||||
var selectCharacterDialog = bootbox.dialog({
|
||||
title: register ? 'Registration' : 'Account settings',
|
||||
title: options.register === 1 ? 'Registration' : 'Account settings',
|
||||
message: content,
|
||||
buttons: {
|
||||
close: {
|
||||
@@ -229,7 +232,7 @@ define([
|
||||
className: ['btn-success', 'pull-right', config.settingsFinishButtonClass].join(' '),
|
||||
callback: function(e){
|
||||
|
||||
if(register){
|
||||
if(options.register === 1){
|
||||
if(reroutePath !== undefined){
|
||||
// root user to main app
|
||||
window.location = reroutePath;
|
||||
@@ -358,23 +361,20 @@ define([
|
||||
var fieldName = 'name';
|
||||
if(errorObj.text.match( fieldName )){
|
||||
// name exist
|
||||
showFormMessage([{type: 'error', message: 'Username already exists'}]);
|
||||
resetFormField( fieldName );
|
||||
form.showFormMessage([{type: 'error', message: 'Username already exists', field: fieldName}]);
|
||||
}
|
||||
|
||||
fieldName = 'email';
|
||||
if(errorObj.text.match( fieldName )){
|
||||
// name exist
|
||||
showFormMessage([{type: 'error', message: 'Email already exists'}]);
|
||||
resetFormField( fieldName );
|
||||
resetFormField( fieldName + '_confirm');
|
||||
// email exist
|
||||
form.showFormMessage([{type: 'error', message: 'Email already exists', field: fieldName}]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!register){
|
||||
if( options.register !== 1 ){
|
||||
$(document).setProgramStatus('problem');
|
||||
}
|
||||
|
||||
|
||||
@@ -191,6 +191,13 @@ define([
|
||||
type_sort: tempSystemData.type.id
|
||||
};
|
||||
|
||||
// security
|
||||
var securityClass = Util.getSecurityClassForSystem(tempSystemData.security);
|
||||
tempData.security = {
|
||||
security: '<span class="' + securityClass + '">' + tempSystemData.security + '</span>',
|
||||
security_sort: tempSystemData.security
|
||||
};
|
||||
|
||||
// name
|
||||
tempData.name = tempSystemData.name;
|
||||
|
||||
@@ -261,19 +268,6 @@ define([
|
||||
};
|
||||
}
|
||||
|
||||
// rally point
|
||||
if(tempSystemData.rally === 1){
|
||||
tempData.rally = {
|
||||
rally: '<i class="fa fa-users fa-lg fa-fw"></i>',
|
||||
rally_sort: tempSystemData.rally
|
||||
};
|
||||
}else{
|
||||
tempData.rally = {
|
||||
rally: '',
|
||||
rally_sort: 0
|
||||
};
|
||||
}
|
||||
|
||||
// updated
|
||||
tempData.updated = tempSystemData.updated.updated;
|
||||
|
||||
@@ -288,7 +282,7 @@ define([
|
||||
paging: true,
|
||||
lengthMenu: [[5, 10, 20, 50, -1], [5, 10, 20, 50, 'All']],
|
||||
ordering: true,
|
||||
order: [[ 7, 'desc' ], [ 2, 'asc' ]],
|
||||
order: [[ 9, 'desc' ], [ 3, 'asc' ]],
|
||||
autoWidth: false,
|
||||
responsive: {
|
||||
breakpoints: [
|
||||
@@ -318,10 +312,19 @@ define([
|
||||
_: 'type',
|
||||
sort: 'type_sort'
|
||||
}
|
||||
},{
|
||||
title: '',
|
||||
width: '1px',
|
||||
searchable: false,
|
||||
data: 'security',
|
||||
render: {
|
||||
_: 'security',
|
||||
sort: 'security_sort'
|
||||
}
|
||||
},{
|
||||
title: 'sec',
|
||||
width: '18px',
|
||||
className: 'text-center',
|
||||
className: ['text-center', 'min-desktop'].join(' '),
|
||||
searchable: false,
|
||||
data: 'trueSec',
|
||||
render: {
|
||||
@@ -381,16 +384,6 @@ define([
|
||||
_: 'locked',
|
||||
sort: 'locked_sort'
|
||||
}
|
||||
},{
|
||||
title: '<i class="fa fa-users fa-lg fa-fw" title="rally point" data-toggle="tooltip"></i>',
|
||||
width: '15px',
|
||||
className: ['min-desktop'].join(' '),
|
||||
searchable: false,
|
||||
data: 'rally',
|
||||
render: {
|
||||
_: 'rally',
|
||||
sort: 'rally_sort'
|
||||
}
|
||||
},{
|
||||
title: 'updated',
|
||||
width: '80px',
|
||||
|
||||
@@ -739,7 +739,9 @@ define([
|
||||
|
||||
// jump to "next" editable field on save
|
||||
var openNextEditDialogOnSave = function(fields){
|
||||
fields.on('save', function(e){
|
||||
fields.on('save', function(e, a){
|
||||
console.log(e);
|
||||
console.log(a)
|
||||
var currentField = $(this);
|
||||
|
||||
setTimeout(function() {
|
||||
@@ -1504,9 +1506,10 @@ define([
|
||||
$(cell).initTimestampCounter();
|
||||
|
||||
// highlight cell
|
||||
var diff = new Date().getTime() - cellData.updated * 1000;
|
||||
var dateDiff = new Date(diff);
|
||||
if(dateDiff.getUTCDate() > 1){
|
||||
var diff = Math.floor((new Date()).getTime()) - cellData.updated * 1000;
|
||||
|
||||
// age > 1 day
|
||||
if( diff > 86400000){
|
||||
$(cell).addClass('txt-color txt-color-warning');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -153,7 +153,10 @@ define([
|
||||
errorMessage.push( errors[i].message );
|
||||
|
||||
// mark form field as invalid in case of a validation error
|
||||
if(errors[i].field){
|
||||
if(
|
||||
errors[i].field &&
|
||||
errors[i].field.length > 0
|
||||
){
|
||||
var formField = formElement.find('[name="' + errors[i].field + '"]');
|
||||
formField.parents('.form-group').removeClass('has-success').addClass('has-error');
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
40
public/templates/dialog/registration.html
Normal file
40
public/templates/dialog/registration.html
Normal file
@@ -0,0 +1,40 @@
|
||||
<form role="form" class="form-horizontal">
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-11">
|
||||
<blockquote>
|
||||
<p>
|
||||
The number of active accounts is limited during the beta phase. This restriction may help to find bugs and performance issues.
|
||||
The limit will be increased continuously and fully removed when the beta phase is over.
|
||||
</p>
|
||||
<small>If the pool of beta keys has been exhausted, please try again in a few days/weeks.
|
||||
</small>
|
||||
</blockquote>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<div class="form-group">
|
||||
<label for="email" class="col-sm-3 control-label">Email</label>
|
||||
<div class="col-sm-6">
|
||||
<div class="input-group" title="Send invite to this address" data-placement="right">
|
||||
<input name="email" type="email" class="form-control" id="email" value="" placeholder="your@email.com" data-error="Email required" autocomplete="off" required>
|
||||
<span class="input-group-addon"><i class="fa fa-fw fa-envelope"></i></span>
|
||||
</div>
|
||||
<div class="help-block with-errors"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="{{formWarningContainerClass}} alert alert-warning" style="display: none;">
|
||||
<span class="txt-color txt-color-warning">Warning</span>
|
||||
<small> (important non-critical information)</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>
|
||||
@@ -41,20 +41,45 @@
|
||||
{{/register}}
|
||||
|
||||
{{#register}}
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<div class="form-group">
|
||||
<label for="email" class="col-sm-3 control-label">Username</label>
|
||||
<div class="col-sm-6">
|
||||
<div class="input-group" title="Choose your unique username" data-placement="right">
|
||||
<input name="name" type="text" class="form-control" id="name" value="" placeholder="Your username" data-error="Username required" data-minlength="5" data-minlength-error="Min. of 5 characters" autocomplete="off" required>
|
||||
<span class="input-group-addon"><i class="fa fa-user"></i></span>
|
||||
|
||||
{{#invite}}
|
||||
<div class="alert alert-info" style="margin-bottom: 20px">
|
||||
<button type="button" class="close" data-dismiss="alert" aria-label="Close"><i class="fa fa-close"></i></button>
|
||||
<span class="txt-color txt-color-information">Invite active</span>
|
||||
<small>You need a "Registration Key" to complete registration</small>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<div class="form-group">
|
||||
<label for="registrationKey" class="col-sm-3 control-label">Registration Key</label>
|
||||
<div class="col-sm-6">
|
||||
<div class="input-group" title="Enter your personal registration kay" data-placement="right">
|
||||
<input name="registrationKey" type="text" class="form-control" id="registrationKey" value="" placeholder="XXXXXX" data-error="Registration key required" data-minlength="40" data-minlength-error="Min. of 40 characters" autocomplete="off" required>
|
||||
<span class="input-group-addon"><i class="fa fa-fw fa-certificate"></i></span>
|
||||
</div>
|
||||
<div class="help-block with-errors"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{/invite}}
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<div class="form-group">
|
||||
<label for="email" class="col-sm-3 control-label">Username</label>
|
||||
<div class="col-sm-6">
|
||||
<div class="input-group" title="Choose your unique username" data-placement="right">
|
||||
<input name="name" type="text" class="form-control" id="name" value="" placeholder="Your username" data-error="Username required" data-minlength="5" data-minlength-error="Min. of 5 characters" autocomplete="off" required>
|
||||
<span class="input-group-addon"><i class="fa fa-fw fa-user"></i></span>
|
||||
</div>
|
||||
<div class="help-block with-errors"></div>
|
||||
</div>
|
||||
<div class="help-block with-errors"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{/register}}
|
||||
|
||||
{{^register}}
|
||||
@@ -81,7 +106,7 @@
|
||||
<div class="col-sm-6">
|
||||
<div class="input-group" title="Enter your email. It will be kept private!" data-placement="right">
|
||||
<input name="email" type="email" class="form-control" id="email" value="" placeholder="your@email.com" data-error="Email required" autocomplete="off" {{#register}}required{{/register}} >
|
||||
<span class="input-group-addon"><i class="fa fa-envelope"></i></span>
|
||||
<span class="input-group-addon"><i class="fa fa-fw fa-envelope"></i></span>
|
||||
</div>
|
||||
<div class="help-block with-errors"></div>
|
||||
</div>
|
||||
@@ -96,7 +121,7 @@
|
||||
<div class="col-sm-6">
|
||||
<div class="input-group" title="Confirm your email" data-placement="right">
|
||||
<input name="email_confirm" type="email" class="form-control" id="email_confirm" value="" placeholder="your@email.com" data-error="Email required" data-match="#email" data-match-error="Email fields do not match" autocomplete="off" {{#register}}required{{/register}}>
|
||||
<span class="input-group-addon"><i class="fa fa-envelope"></i></span>
|
||||
<span class="input-group-addon"><i class="fa fa-fw fa-envelope"></i></span>
|
||||
</div>
|
||||
<div class="help-block with-errors"></div>
|
||||
</div>
|
||||
@@ -129,7 +154,7 @@
|
||||
<div class="col-sm-6">
|
||||
<div class="input-group" title="Enter your password. Do not use your EVE password" data-placement="right">
|
||||
<input name="password" type="password" class="form-control" id="password" placeholder="" data-minlength="6" data-minlength-error="Min. of 6 characters" {{#register}}required{{/register}}>
|
||||
<span class="input-group-addon"><i class="fa fa-lock"></i></span>
|
||||
<span class="input-group-addon"><i class="fa fa-fw fa-lock"></i></span>
|
||||
</div>
|
||||
<div class="help-block with-errors"></div>
|
||||
</div>
|
||||
@@ -144,7 +169,7 @@
|
||||
<div class="col-sm-6">
|
||||
<div class="input-group" title="Confirm your password" data-placement="right">
|
||||
<input name="password_confirm" type="password" class="form-control" id="password_confirm" placeholder="" data-minlength="6" data-minlength-error="Min. of 6 characters" data-match="#password" data-match-error="Password fields do not match" {{#register}}required{{/register}}>
|
||||
<span class="input-group-addon"><i class="fa fa-lock"></i></span>
|
||||
<span class="input-group-addon"><i class="fa fa-fw fa-lock"></i></span>
|
||||
</div>
|
||||
<div class="help-block with-errors"></div>
|
||||
</div>
|
||||
@@ -174,7 +199,7 @@
|
||||
<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" autocomplete="off" required>
|
||||
<span class="input-group-addon"><i class="fa fa-refresh"></i></span>
|
||||
<span class="input-group-addon"><i class="fa fa-fw fa-refresh"></i></span>
|
||||
</div>
|
||||
<div class="help-block with-errors"></div>
|
||||
</div>
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
<link rel="stylesheet" type="text/css" media="screen" href="public/css/pathfinder.css?{{ @PATHFINDER.VERSION }}">
|
||||
|
||||
</head>
|
||||
<body class="{{ @bodyClass }}" data-trusted="{{ @trusted }}" data-js-path="{{ @pathJs }}" data-script="{{ @jsView }}" data-version="{{ @PATHFINDER.VERSION }}">
|
||||
<body class="{{ @bodyClass }}" data-trusted="{{ @trusted }}" data-js-path="{{ @pathJs }}" data-script="{{ @jsView }}" data-invite="{{ @PATHFINDER.REGISTRATION.INVITE }}" data-version="{{ @PATHFINDER.VERSION }}">
|
||||
|
||||
<include if="{{ @pageContent }}" href="{{ @pageContent }}"/>
|
||||
|
||||
|
||||
@@ -130,10 +130,10 @@
|
||||
<div class="row text-center">
|
||||
<div class="col-sm-6 col-sm-offset-3">
|
||||
<div class="col-sm-4 col-sm-offset-2" data-placement="left" title="{{@registrationStatusTitle}}">
|
||||
<button class="pf-register-button btn-block btn btn-primary {{@registrationStatusButton}}" tabindex="4"><i class="fa fa-fw fa-user-plus"></i> Sign up</button>
|
||||
<button type="button" class="pf-register-button btn-block btn btn-primary {{@registrationStatusButton}}" tabindex="4"><i class="fa fa-fw fa-user-plus"></i> Sign up</button>
|
||||
</div>
|
||||
<div class="col-sm-4">
|
||||
<button class="pf-login-button btn-block btn btn-success" tabindex="3"><i class="fa fa-fw fa-sign-in"></i> Log in</button>
|
||||
<button type="submit" class="pf-login-button btn-block btn btn-success" tabindex="3"><i class="fa fa-fw fa-sign-in"></i> Log in</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -537,7 +537,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel-footer text-align-center" data-placement="top" title="{{@registrationStatusTitle}}">
|
||||
<button class="btn btn-primary btn-block pf-register-button {{@registrationStatusButton}}" role="button"><i class="fa fa-fw fa-user-plus"></i> Sign up</button>
|
||||
<button type="button" class="btn btn-primary btn-block pf-register-button {{@registrationStatusButton}}" role="button"><i class="fa fa-fw fa-user-plus"></i> Sign up</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -776,7 +776,7 @@
|
||||
The program code is open source and can be used by anyone who have the required software skills.
|
||||
Please make sure to keep all 3rd party plugin licence and respect them.
|
||||
At the moment there is no developer guide available. But one the beta phase is finished, i will probably write a short technical documentation.
|
||||
Do not expect any "out of the boy" install routine ot this point.
|
||||
Do not expect any "out of the boy" install routine at this point.
|
||||
<br>
|
||||
Server requirements:
|
||||
</p>
|
||||
|
||||
@@ -165,6 +165,7 @@ select:active, select:hover {
|
||||
left: 0;
|
||||
opacity: 0;
|
||||
background: $gray-darker;
|
||||
z-index: 1060;
|
||||
@include border-radius(5px);
|
||||
|
||||
.pf-loading-overlay-wrapper{
|
||||
@@ -320,9 +321,7 @@ select:active, select:hover {
|
||||
overflow: hidden;
|
||||
@include border-radius(5px);
|
||||
|
||||
&:after{
|
||||
//content: "\f09b";
|
||||
//font: normal normal normal 14px/1 FontAwesome; // shortening font declaration
|
||||
&:before{
|
||||
content:'';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
@@ -823,6 +822,10 @@ select:active, select:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.navbar-text{
|
||||
min-width: 60px; // fixes a load-delay issue for "toggle" map-tracking
|
||||
}
|
||||
|
||||
// tooltips header
|
||||
.tooltip{
|
||||
.tooltip-inner{
|
||||
|
||||
Reference in New Issue
Block a user