- #84, #138 improved "character selection" on login page (expired cookies are deleted, character panel layout improvements)

- added new "Server info panel" to the login page
- added new cronjob to delete expired cookie authentication data
This commit is contained in:
Exodus4D
2016-05-02 17:30:26 +02:00
parent ef8f5666aa
commit 8900276cf5
16 changed files with 440 additions and 93 deletions

View File

@@ -17,8 +17,11 @@ importSystemData = Cron\CcpSystemsUpdate->importSystemData, @hourly
; disable outdated maps
deactivateMapData = Cron\MapUpdate->deactivateMapData, @hourly
; delete character log data
deleteLogData = Cron\CharacterUpdate->deleteLogData, @hourly
; delete disabled maps
deleteMapData = Cron\MapUpdate->deleteMapData, @downtime
; delete character log data
deleteLogData = Cron\CharacterUpdate->deleteLogData, @hourly
; delete expired character cookie authentication data
deleteAuthenticationData = Cron\CharacterUpdate->deleteAuthenticationData, @downtime

View File

@@ -48,7 +48,7 @@ class AppController extends Controller {
// characters from cookies
$f3->set('cookieCharacters', $this->getCookieByName(self::COOKIE_PREFIX_CHARACTER, true));
$f3->set('getCharacterGrid', function($characters){
return ( ((12 / count($characters)) <= 4) ? 4 : (12 / count($characters)) );
return ( ((12 / count($characters)) <= 3) ? 3 : (12 / count($characters)) );
});
}

View File

@@ -429,7 +429,7 @@ class Sso extends Api\User{
* @param array $additionalOptions
* @return mixed|null
*/
protected function getEndpoints($accessToken, $additionalOptions = []){
protected function getEndpoints($accessToken = '', $additionalOptions = []){
$crestUrl = self::getCrestEndpoint();
$additionalOptions['contentType'] = 'application/vnd.ccp.eve.Api-v3+json';
$endpoint = $this->getEndpoint($accessToken, $crestUrl, $additionalOptions);
@@ -645,6 +645,44 @@ class Sso extends Api\User{
return $characterModel;
}
/**
* get CREST server status (online/offline)
* @return \stdClass object
*/
public function getCrestServerStatus(){
$endpoints = $this->getEndpoints();
// set default status e.g. Endpoints don´t work
$data = (object) [];
$data->crestOffline = true;
$data->serverName = 'EVE ONLINE';
$data->serviceStatus = [
'eve' => 'offline',
'server' => 'offline',
];
$data->userCounts = [
'eve' => 0
];
$endpoint = $this->walkEndpoint('', $endpoints, ['serverName']);
if( !empty($endpoint) ){
$data->crestOffline = false;
$data->serverName = (string) $endpoint;
}
$endpoint = $this->walkEndpoint('', $endpoints, ['serviceStatus']);
if( !empty($endpoint) ){
$data->crestOffline = false;
$data->serviceStatus = (new Mapper\CrestServiceStatus($endpoint))->getData();
}
$endpoint = $this->walkEndpoint('', $endpoints, ['userCounts']);
if( !empty($endpoint) ){
$data->crestOffline = false;
$data->userCounts = (new Mapper\CrestUserCounts($endpoint))->getData();
}
return $data;
}
/**
* check response "Header" data for errors
* @param $headers

View File

@@ -8,6 +8,7 @@
namespace Controller;
use Controller\Api as Api;
use Controller\Ccp\Sso;
use Model;
use DB;
@@ -242,29 +243,34 @@ class Controller {
// validate expire data
// validate token
if(
!$characterAuth->dry() &&
strtotime($characterAuth->expires) >= $currentTime->getTimestamp() &&
hash_equals($characterAuth->token, hash('sha256', $data[1]))
){
// cookie information is valid
// -> try to update character information from CREST
// e.g. Corp has changed, this also ensures valid "access_token"
/**
* @var $character Model\CharacterModel
*/
$character = $characterAuth->characterId;
$updateStatus = $character->updateFromCrest();
// check if character still has user (is not the case of "ownerHash" changed
// check if character is still authorized to log in (e.g. corp/ally or config has changed
// -> do NOT remove cookie on failure. This can be a temporary problem (e.g. CREST is down,..)
if( !$characterAuth->dry() ){
if(
empty($updateStatus) &&
$character->hasUserCharacter() &&
$character->isAuthorized()
strtotime($characterAuth->expires) >= $currentTime->getTimestamp() &&
hash_equals($characterAuth->token, hash('sha256', $data[1]))
){
$characters[$name] = $character;
// cookie information is valid
// -> try to update character information from CREST
// e.g. Corp has changed, this also ensures valid "access_token"
/**
* @var $character Model\CharacterModel
*/
$character = $characterAuth->characterId;
$updateStatus = $character->updateFromCrest();
// check if character still has user (is not the case of "ownerHash" changed
// check if character is still authorized to log in (e.g. corp/ally or config has changed
// -> do NOT remove cookie on failure. This can be a temporary problem (e.g. CREST is down,..)
if(
empty($updateStatus) &&
$character->hasUserCharacter() &&
$character->isAuthorized()
){
$characters[$name] = $character;
}
}else{
// clear existing authentication data from DB
$characterAuth->erase();
$invalidCookie = true;
}
}else{
$invalidCookie = true;
@@ -371,6 +377,31 @@ class Controller {
}
}
/**
* get EVE server status from CREST
* @param \Base $f3
*/
public function getEveServerStatus(\Base $f3){
$return = (object) [];
$return->error = [];
// server status can be cached for some seconds
$cacheKey = 'eve_server_status';
if( !$f3->exists($cacheKey) ){
$sso = new Sso();
$return->status = $sso->getCrestServerStatus();
if( !$return->status->crestOffline ){
$f3->set($cacheKey, $return, 60);
}
}else{
// get from cache
$return = $f3->get($cacheKey);
}
echo json_encode($return);
}
/**
* check weather the page is IGB trusted or not
* @return boolean

View File

@@ -17,15 +17,17 @@ class CharacterUpdate {
/**
* delete all character log data
* >> php index.php "/cron/deleteLogData"
* @param $f3
* @param \Base $f3
*/
function deleteLogData($f3){
DB\Database::instance()->getDB('PF');
/**
* @var $characterLogModel Model\CharacterLogModel
*/
$characterLogModel = Model\BasicModel::getNew('CharacterLogModel', 0);
// find "old" character logs
// find expired character logs
$characterLogs = $characterLogModel->find([
'TIMESTAMPDIFF(SECOND, updated, NOW() ) > :lifetime',
':lifetime' => (int)$f3->get('PATHFINDER.CACHE.CHARACTER_LOG')
@@ -33,10 +35,35 @@ class CharacterUpdate {
if(is_object($characterLogs)){
foreach($characterLogs as $characterLog){
// delete log and all cached values
$characterLog->erase();
}
}
}
/**
* delete expired character authentication data
* authentication data is used for cookie based login
* >> php index.php "/cron/deleteAuthenticationData"
* @param $f3
*/
function deleteAuthenticationData($f3){
DB\Database::instance()->getDB('PF');
/**
* @var $authenticationModel Model\CharacterAuthenticationModel
*/
$authenticationModel = Model\BasicModel::getNew('CharacterAuthenticationModel', 0);
// find expired authentication data
$authentications = $authenticationModel->find([
'(expires - NOW()) <= 0'
]);
if(is_object($authentications)){
foreach($authentications as $authentication){
$authentication->erase();
}
}
}
}

View File

@@ -0,0 +1,19 @@
<?php
/**
* Created by PhpStorm.
* User: Exodus
* Date: 01.05.2016
* Time: 19:17
*/
namespace Data\Mapper;
class CrestServiceStatus extends AbstractIterator {
protected static $map = [
'dust' => 'dust',
'eve' => 'eve',
'server' => 'server'
];
}

View File

@@ -0,0 +1,18 @@
<?php
/**
* Created by PhpStorm.
* User: Exodus
* Date: 01.05.2016
* Time: 19:42
*/
namespace Data\Mapper;
class CrestUserCounts extends AbstractIterator {
protected static $map = [
'dust' => 'dust',
'eve' => 'eve'
];
}

View File

@@ -11,6 +11,7 @@ define(['jquery'], function($) {
img: 'public/img/', // path for images
// user API
getCaptcha: 'api/user/getCaptcha', // ajax URL - get captcha image
getServerStatus: 'api/user/getEveServerStatus', // ajax URL - get EVE-Online server status
sendInviteKey: 'api/user/sendInvite', // ajax URL - send registration key
getCookieCharacterData: 'api/user/getCookieCharacter', // ajax URL - get character data from cookie
logIn: 'api/user/logIn', // ajax URL - login

View File

@@ -27,6 +27,7 @@ define([
splashOverlayClass: 'pf-splash', // class for "splash" overlay
// header
headerId: 'pf-landing-top', // id for header
headerContainerId: 'pf-header-container', // id for header container
logoContainerId: 'pf-logo-container', // id for main header logo container
headHeaderMapId: 'pf-header-map', // id for header image (svg animation)
@@ -59,6 +60,9 @@ define([
galleryThumbContainerId: 'pf-landing-gallery-thumb-container', // id for gallery thumb images
galleryCarouselId: 'pf-landing-gallery-carousel', // id for "carousel" element
// server panel
serverPanelId: 'pf-server-panel', // id for EVE Online server status panel
// animation
animateElementClass: 'pf-animate-on-visible' // class for elements that will be animated to show
};
@@ -466,6 +470,41 @@ define([
});
};
/**
* get current EVE-Online server status
* -> show "server panel"
*/
var initServerStatus = function(){
$.ajax({
type: 'POST',
url: Init.path.getServerStatus,
dataType: 'json'
}).done(function(responseData, textStatus, request){
if(responseData.hasOwnProperty('status')){
var data = responseData.status;
data.serverPanelId = config.serverPanelId;
var statusClass = '';
switch(data.serviceStatus.eve.toLowerCase()){
case 'online': statusClass = 'txt-color-green'; break;
case 'vip': statusClass = 'txt-color-orange'; break;
case 'offline': statusClass = 'txt-color-redDarker'; break;
}
data.serviceStatus.style = statusClass;
requirejs(['text!templates/ui/server_panel.html', 'mustache'], function(template, Mustache) {
var content = Mustache.render(template, data);
$('#' + config.headerId).prepend(content);
$('#' + config.serverPanelId).velocity('transition.slideLeftBigIn', {
duration: 240
});
});
}
}).fail(handleAjaxErrorResponse);
};
/**
* load character data from cookie information
* -> all validation is done server side!
@@ -513,7 +552,7 @@ define([
*/
var updateCharacterPanels = function(){
var characterRows = $('.' + config.characterSelectionClass + ' .pf-dynamic-area').parent();
var rowClassIdentifier = ((12 / characterRows.length ) <= 4) ? 4 : (12 / characterRows.length);
var rowClassIdentifier = ((12 / characterRows.length ) <= 3) ? 3 : (12 / characterRows.length);
$(characterRows).removeClass().addClass('col-sm-' + rowClassIdentifier);
};
@@ -562,7 +601,7 @@ define([
cookieName: requestData.cookie,
characterElement: characterElement
}
}).done(function(responseData){
}).done(function(responseData, textStatus, request){
var characterElement = this.characterElement;
characterElement.hideLoadingAnimation();
@@ -608,6 +647,41 @@ define([
});
};
/**
* default ajax error handler
* -> show user notifications
* @param jqXHR
* @param status
* @param error
*/
var handleAjaxErrorResponse = function(jqXHR, status, error){
var type = status;
var title = 'Status ' + jqXHR.status + ': ' + error;
var message = '';
if(jqXHR.responseText){
var errorObj = $.parseJSON(jqXHR.responseText);
if(
errorObj.error &&
errorObj.error.length > 0
){
for(var i = 0; i < errorObj.error.length; i++){
var errorData = errorObj.error[i];
type = errorData.type;
title = 'Status ' + errorData.code + ': ' + errorData.status;
message = errorData.message;
Util.showNotify({title: title, text: message, type: type});
}
}
}else{
Util.showNotify({title: title, text: message, type: type});
}
};
/**
* main init "landing" page
*/
@@ -662,6 +736,9 @@ define([
// hide splash loading animation
$('.' + config.splashOverlayClass).hideSplashOverlay();
// init server status information
initServerStatus();
initCharacterSelect();
// init carousel

File diff suppressed because one or more lines are too long

View File

@@ -11,6 +11,7 @@ define(['jquery'], function($) {
img: 'public/img/', // path for images
// user API
getCaptcha: 'api/user/getCaptcha', // ajax URL - get captcha image
getServerStatus: 'api/user/getEveServerStatus', // ajax URL - get EVE-Online server status
sendInviteKey: 'api/user/sendInvite', // ajax URL - send registration key
getCookieCharacterData: 'api/user/getCookieCharacter', // ajax URL - get character data from cookie
logIn: 'api/user/logIn', // ajax URL - login

View File

@@ -27,6 +27,7 @@ define([
splashOverlayClass: 'pf-splash', // class for "splash" overlay
// header
headerId: 'pf-landing-top', // id for header
headerContainerId: 'pf-header-container', // id for header container
logoContainerId: 'pf-logo-container', // id for main header logo container
headHeaderMapId: 'pf-header-map', // id for header image (svg animation)
@@ -59,6 +60,9 @@ define([
galleryThumbContainerId: 'pf-landing-gallery-thumb-container', // id for gallery thumb images
galleryCarouselId: 'pf-landing-gallery-carousel', // id for "carousel" element
// server panel
serverPanelId: 'pf-server-panel', // id for EVE Online server status panel
// animation
animateElementClass: 'pf-animate-on-visible' // class for elements that will be animated to show
};
@@ -466,6 +470,41 @@ define([
});
};
/**
* get current EVE-Online server status
* -> show "server panel"
*/
var initServerStatus = function(){
$.ajax({
type: 'POST',
url: Init.path.getServerStatus,
dataType: 'json'
}).done(function(responseData, textStatus, request){
if(responseData.hasOwnProperty('status')){
var data = responseData.status;
data.serverPanelId = config.serverPanelId;
var statusClass = '';
switch(data.serviceStatus.eve.toLowerCase()){
case 'online': statusClass = 'txt-color-green'; break;
case 'vip': statusClass = 'txt-color-orange'; break;
case 'offline': statusClass = 'txt-color-redDarker'; break;
}
data.serviceStatus.style = statusClass;
requirejs(['text!templates/ui/server_panel.html', 'mustache'], function(template, Mustache) {
var content = Mustache.render(template, data);
$('#' + config.headerId).prepend(content);
$('#' + config.serverPanelId).velocity('transition.slideLeftBigIn', {
duration: 240
});
});
}
}).fail(handleAjaxErrorResponse);
};
/**
* load character data from cookie information
* -> all validation is done server side!
@@ -513,7 +552,7 @@ define([
*/
var updateCharacterPanels = function(){
var characterRows = $('.' + config.characterSelectionClass + ' .pf-dynamic-area').parent();
var rowClassIdentifier = ((12 / characterRows.length ) <= 4) ? 4 : (12 / characterRows.length);
var rowClassIdentifier = ((12 / characterRows.length ) <= 3) ? 3 : (12 / characterRows.length);
$(characterRows).removeClass().addClass('col-sm-' + rowClassIdentifier);
};
@@ -562,7 +601,7 @@ define([
cookieName: requestData.cookie,
characterElement: characterElement
}
}).done(function(responseData){
}).done(function(responseData, textStatus, request){
var characterElement = this.characterElement;
characterElement.hideLoadingAnimation();
@@ -608,6 +647,41 @@ define([
});
};
/**
* default ajax error handler
* -> show user notifications
* @param jqXHR
* @param status
* @param error
*/
var handleAjaxErrorResponse = function(jqXHR, status, error){
var type = status;
var title = 'Status ' + jqXHR.status + ': ' + error;
var message = '';
if(jqXHR.responseText){
var errorObj = $.parseJSON(jqXHR.responseText);
if(
errorObj.error &&
errorObj.error.length > 0
){
for(var i = 0; i < errorObj.error.length; i++){
var errorData = errorObj.error[i];
type = errorData.type;
title = 'Status ' + errorData.code + ': ' + errorData.status;
message = errorData.message;
Util.showNotify({title: title, text: message, type: type});
}
}
}else{
Util.showNotify({title: title, text: message, type: type});
}
};
/**
* main init "landing" page
*/
@@ -662,6 +736,9 @@ define([
// hide splash loading animation
$('.' + config.splashOverlayClass).hideSplashOverlay();
// init server status information
initServerStatus();
initCharacterSelect();
// init carousel

View File

@@ -1157,7 +1157,7 @@ define([
placement: 'top',
onblur: 'submit',
container: 'body',
toggle: 'manual', // is triggered manually on dblclick
toggle: 'manual', // is triggered manually on dblClick
showbuttons: false
});
@@ -1224,7 +1224,7 @@ define([
/**
* connect two systems
* @param mapConfig
* @param map
* @param connectionData
* @returns new connection
*/
@@ -1242,10 +1242,12 @@ define([
var connection = map.connect({
source: config.systemIdPrefix + mapId + '-' + connectionData.source,
target: config.systemIdPrefix + mapId + '-' + connectionData.target,
/*
parameters: {
connectionId: connectionId,
updated: connectionData.updated
},
*/
type: null
/* experimental (straight connections)
anchors: [
@@ -1255,20 +1257,33 @@ define([
*/
});
// add connection types -----------------------------------------------------
if(connectionData.type){
for(var i = 0; i < connectionData.type.length; i++){
connection.addType(connectionData.type[i]);
}
}
// add connection scope -----------------------------------------------------
// connection have the default map Scope scope
var scope = map.Defaults.Scope;
if(connectionData.scope){
scope = connectionData.scope;
// check if connection is valid (e.g. source/target exist
if( connection instanceof jsPlumb.Connection ){
// set connection parameters
// they should persist even through connection type change (e.g. wh -> stargate,..)
// therefore they shoule be part of the connection not of the connector
connection.setParameters({
connectionId: connectionId,
updated: connectionData.updated
});
// add connection types -----------------------------------------------------
if(connectionData.type){
for(var i = 0; i < connectionData.type.length; i++){
connection.addType(connectionData.type[i]);
}
}
// add connection scope -----------------------------------------------------
// connection have the default map Scope scope
var scope = map.Defaults.Scope;
if(connectionData.scope){
scope = connectionData.scope;
}
setConnectionScope(connection, scope);
}
setConnectionScope(connection, scope);
// set Observer for new Connection -> is automatically set
@@ -1281,62 +1296,65 @@ define([
*/
var saveConnection = function(connection){
var map = connection._jsPlumb.instance;
var mapContainer = $( map.getContainer() );
mapContainer.getMapOverlay('timer').startMapUpdateCounter();
if( connection instanceof jsPlumb.Connection ){
var mapId = mapContainer.data('id');
var connectionData = getDataByConnection(connection);
var map = connection._jsPlumb.instance;
var mapContainer = $( map.getContainer() );
mapContainer.getMapOverlay('timer').startMapUpdateCounter();
var requestData = {
mapData: {
id: mapId
},
connectionData: connectionData
};
var mapId = mapContainer.data('id');
var connectionData = getDataByConnection(connection);
$.ajax({
type: 'POST',
url: Init.path.saveConnection,
data: requestData,
dataType: 'json',
//context: connection
context: {
connection: connection,
map: map,
mapId: mapId
}
}).done(function(newConnectionData){
var requestData = {
mapData: {
id: mapId
},
connectionData: connectionData
};
if( !$.isEmptyObject(newConnectionData) ){
// update connection data e.g. "scope" has auto detected
connection = updateConnection(this.connection, connectionData, newConnectionData);
$.ajax({
type: 'POST',
url: Init.path.saveConnection,
data: requestData,
dataType: 'json',
//context: connection
context: {
connection: connection,
map: map,
mapId: mapId
}
}).done(function(newConnectionData){
// new connection should be cached immediately!
updateConnectionCache(this.mapId, connection);
if( !$.isEmptyObject(newConnectionData) ){
// update connection data e.g. "scope" has auto detected
connection = updateConnection(this.connection, connectionData, newConnectionData);
// connection scope
var scope = Util.getScopeInfoForConnection(newConnectionData.scope, 'label');
// new connection should be cached immediately!
updateConnectionCache(this.mapId, connection);
var title = 'New connection established';
if(connectionData.id > 0){
title = 'Connection switched';
// connection scope
var scope = Util.getScopeInfoForConnection(newConnectionData.scope, 'label');
var title = 'New connection established';
if(connectionData.id > 0){
title = 'Connection switched';
}
Util.showNotify({title: title, text: 'Scope: ' + scope, type: 'success'});
}else{
// some save errors
this.map.detach(this.connection, {fireEvent: false});
}
Util.showNotify({title: title, text: 'Scope: ' + scope, type: 'success'});
}else{
// some save errors
}).fail(function( jqXHR, status, error) {
// remove this connection from map
this.map.detach(this.connection, {fireEvent: false});
}
}).fail(function( jqXHR, status, error) {
// remove this connection from map
this.map.detach(this.connection, {fireEvent: false});
var reason = status + ' ' + error;
Util.showNotify({title: jqXHR.status + ': saveConnection', text: reason, type: 'warning'});
$(document).setProgramStatus('problem');
});
var reason = status + ' ' + error;
Util.showNotify({title: jqXHR.status + ': saveConnection', text: reason, type: 'warning'});
$(document).setProgramStatus('problem');
});
}
};
/**

View File

@@ -0,0 +1,11 @@
<div id="{{ serverPanelId }}">
<h4 class="text-center">{{ serverName }}</h4>
<ul class="fa-ul">
{{#serviceStatus}}
<li><i class="fa-li fa fa-server " aria-hidden="true"></i><span class="txt-color {{ style }}">{{ eve }}</span></li>
{{/serviceStatus}}
{{#userCounts}}
<li><i class="fa-li fa fa-users" aria-hidden="true"></i>{{ eve }}</li>
{{/userCounts}}
</ul>
</div>

View File

@@ -11,4 +11,5 @@
@import "_timeline";
@import "_ribbon";
@import "_loading-bar";
@import "_server-panel";
@import "_youtube";

View File

@@ -0,0 +1,25 @@
#pf-server-panel{
position: fixed;
top: 50px;
min-width: 100px;
left: 10px;
border-radius: 5px;
padding: 7px;
box-shadow: 0 4px 10px rgba(0,0,0,0.4);
background: {
color: rgba(43, 43, 43, 0.7);
}
h4{
margin: 5px 0 10px 0;
}
ul{
margin-bottom: 0;
li{
text-transform: lowercase;
}
}
}