Merge pull request #122 from exodus4d/pathfinder-84

Pathfinder 84
This commit is contained in:
Mark Friedrich
2016-04-05 21:35:33 +02:00
187 changed files with 42672 additions and 4245 deletions

View File

@@ -5,7 +5,7 @@ Mapping tool for [*EVE ONLINE*](https://www.eveonline.com)
- Official Forum post [https://forums.eveonline.com](https://forums.eveonline.com/default.aspx?g=posts&m=6021776#post6021776)
- Screenshots [imgur.com](http://imgur.com/a/k2aVa)
- Video [youtube.com](https://www.youtube.com/channel/UC7HU7XEoMbqRwqxDTbMjSPg)
- Community [google +](https://plus.google.com/u/0/b/110257318165279088853/110257318165279088853)
- Community [google +](https://plus.google.com/+Pathfinder-wSpace)
- Licence [MIT](http://opensource.org/licenses/MIT)
##### IMPORTANT Information:

View File

@@ -2,16 +2,16 @@
[ENVIRONMENT]
; project environment (DEVELOP, PRODUCTION).
; This effects: DB connection, Mail-Server connection
; This effects: DB connection, Mail-Server, SSO, CREST configurations in this file
; configuration below
SERVER = DEVELOP
[ENVIRONMENT.DEVELOP]
; base dir (Default: "auto-detect"
; base dir (Default: "auto-detect")
BASE =
; deployment URL e.g. http://localhost
; deployment URL (e.g. http://localhost)
URL = http://pathfinder.local
; Verbosity level of the stack trace
; level of debug/error stack trace
DEBUG = 3
; main db
DB_DNS = mysql:host=localhost;port=3306;dbname=
@@ -25,11 +25,13 @@ DB_CCP_NAME = eve_parallax_min
DB_CCP_USER = root
DB_CCP_PASS =
; CCP SSO settings
; CCP SSO settings (OAuth2) - visit: https://developers.eveonline.com/applications
CCP_CREST_URL = https://api-sisi.testeveonline.com
SSO_CCP_URL = https://sisilogin.testeveonline.com
SSO_CCP_CLIENT_ID =
SSO_CCP_SECRET_KEY =
; SMTP settings. see: https://developers.eveonline.com/applications
; SMTP settings (optional)
SMTP_HOST = localhost
SMTP_PORT = 25
SMTP_SCHEME = ""
@@ -40,10 +42,11 @@ SMTP_FROM = pathfinder@localhost.com
SMTP_ERROR = pathfinder@localhost.com
[ENVIRONMENT.PRODUCTION]
; base dir (Default: "auto-detect"
BASE = /www/htdocs/www.pathfinder-w.space
; deployment URL
; deployment URL (e.g. https://www.pathfinder-w.space)
URL = https://www.pathfinder-w.space
; Verbosity level of the stack trace
; level of debug/error stack trace
DEBUG = 0
; main db
DB_DNS = mysql:host=localhost;port=3306;dbname=
@@ -58,10 +61,12 @@ DB_CCP_USER =
DB_CCP_PASS =
; CCP SSO settings
CCP_CREST_URL = https://crest-tq.eveonline.com
SSO_CCP_URL = https://login.eveonline.com
SSO_CCP_CLIENT_ID =
SSO_CCP_SECRET_KEY =
; SMTP settings
; SMTP settings (optional)
SMTP_HOST = localhost
SMTP_PORT = 25
SMTP_SCHEME = TLS

View File

@@ -13,14 +13,14 @@
* | | < | <| -__|-- __|
* |__|__|__||__|__|_____|_____|
*
* Copyright (c) 2014 by ikkez
* Christian Knuth <ikkez0n3@gmail.com>
* Copyright (c) 2016 by ikkez
* Christian Knuth <mail@ikkez.de>
* https://github.com/ikkez/F3-Sugar/
*
* @package DB
* @version 1.4.1-dev
* @version 1.4.1
* @date 29.01.2016
* @since 24.04.2012
* @date 04.06.2015
*/
namespace DB;
@@ -173,7 +173,9 @@ class Cortex extends Cursor {
list($key, $relField) = explode('.',$val,2);
$this->relWhitelist[$key][(int)$exclude][] = $relField;
unset($fields[$i]);
$fields[] = $key;
}
$fields = array_unique($fields);
$schema = $this->whitelist ?: $this->mapper->fields();
if (!$schema && !$this->dbsType != 'sql' && $this->dry()) {
$schema = $this->load()->mapper->fields();

View File

@@ -7,20 +7,21 @@
*/
namespace Controller;
use Controller\Api as Api;
use Model;
class AccessController extends Controller {
/**
* event handler
* @param $f3
* @param \Base $f3
*/
function beforeroute($f3) {
function beforeroute(\Base $f3) {
parent::beforeroute($f3);
// Any CMS route of a child class of this one, requires a
// valid logged in user!
$loginCheck = $this->_checkLogIn();
$loginCheck = $this->checkLogIn($f3);
if( !$loginCheck ){
// no user found or LogIn timer expired
@@ -30,16 +31,16 @@ class AccessController extends Controller {
/**
* checks weather a user is currently logged in
* @param \Base $f3
* @return bool
*/
private function _checkLogIn(){
private function checkLogIn($f3){
$loginCheck = false;
if($this->f3->get('SESSION.user.time') > 0){
if($f3->get(Api\User::SESSION_KEY_CHARACTER_TIME) > 0){
// check logIn time
$logInTime = new \DateTime();
$logInTime->setTimestamp($this->f3->get('SESSION.user.time'));
$logInTime->setTimestamp( $f3->get(Api\User::SESSION_KEY_CHARACTER_TIME) );
$now = new \DateTime();
$timeDiff = $now->diff($logInTime);
@@ -48,7 +49,7 @@ class AccessController extends Controller {
$minutes += $timeDiff->h * 60;
$minutes += $timeDiff->i;
if($minutes <= $this->f3->get('PATHFINDER.TIMER.LOGGED')){
if($minutes <= $f3->get('PATHFINDER.TIMER.LOGGED')){
$loginCheck = true;
}
}

View File

@@ -7,25 +7,24 @@
*/
namespace controller\api;
use Controller;
use Model;
class Access extends \Controller\AccessController {
class Access extends Controller\AccessController {
/**
* event handler
* @param $f3
* @param \Base $f3
*/
function beforeroute($f3) {
parent::beforeroute($f3);
function beforeroute(\Base $f3) {
// set header for all routes
header('Content-type: application/json');
parent::beforeroute($f3);
}
/**
* search user/corporation or alliance by name
* @param $f3
* search character/corporation or alliance by name
* @param \Base $f3
* @param $params
*/
public function search($f3, $params){
@@ -41,8 +40,8 @@ class Access extends \Controller\AccessController {
$accessModel = null;
switch($searchType){
case 'user':
$accessModel = Model\BasicModel::getNew('UserModel');
case 'character':
$accessModel = Model\BasicModel::getNew('CharacterModel');
break;
case 'corporation':
$accessModel = Model\BasicModel::getNew('CorporationModel');
@@ -55,12 +54,12 @@ class Access extends \Controller\AccessController {
if( is_object($accessModel) ){
// find "active" entries that have their "sharing" option activated
$accessList = $accessModel->find( array(
$accessList = $accessModel->find( [
"LOWER(name) LIKE :token AND " .
"active = 1 AND " .
"shared = 1 ",
':token' => '%' . $searchToken . '%'
));
]);
if($accessList){
foreach($accessList as $accessObject){

View File

@@ -7,27 +7,26 @@
*/
namespace Controller\Api;
use Controller;
use Model;
class Connection extends \Controller\AccessController{
class Connection extends Controller\AccessController{
/**
* @param $f3
* @param \Base $f3
*/
function beforeroute($f3) {
parent::beforeroute($f3);
function beforeroute(\Base $f3) {
// set header for all routes
header('Content-type: application/json');
parent::beforeroute($f3);
}
/**
* save a new connection or updates an existing (drag/drop) between two systems
* if a connection is changed (drag&drop) to another system. -> this function is called for update
* @param $f3
* @param \Base $f3
*/
public function save($f3){
public function save(\Base $f3){
$postData = (array)$f3->get('POST');
$newConnectionData = [];
@@ -38,14 +37,18 @@ class Connection extends \Controller\AccessController{
$mapData = (array)$postData['mapData'];
$connectionData = (array)$postData['connectionData'];
$user = $this->_getUser();
$activeCharacter = $this->getCharacter();
if($activeCharacter){
if($user){
// get map model and check map access
/**
* @var Model\MapModel $map
*/
$map = Model\BasicModel::getNew('MapModel');
$map->getById( (int)$mapData['id'] );
if( $map->hasAccess($user) ){
if( $map->hasAccess($activeCharacter) ){
$source = $map->getSystem( (int)$connectionData['source'] );
$target = $map->getSystem( (int)$connectionData['target'] );
@@ -53,6 +56,9 @@ class Connection extends \Controller\AccessController{
!is_null($source) &&
!is_null($target)
){
/**
* @var $connection Model\ConnectionModel
*/
$connection = Model\BasicModel::getNew('ConnectionModel');
$connection->getById( (int)$connectionData['id'] );
@@ -90,20 +96,29 @@ class Connection extends \Controller\AccessController{
echo json_encode($newConnectionData);
}
public function delete($f3){
/**
* delete connection
* @param \Base $f3
* @throws \Exception
*/
public function delete(\Base $f3){
$connectionIds = $f3->get('POST.connectionIds');
$activeCharacter = $this->getCharacter();
$user = $this->_getUser();
$connection = Model\BasicModel::getNew('ConnectionModel');
if($activeCharacter = $this->getCharacter()){
/**
* @var Model\ConnectionModel $connection
*/
$connection = Model\BasicModel::getNew('ConnectionModel');
foreach($connectionIds as $connectionId){
$connection->getById($connectionId);
$connection->delete( $activeCharacter );
foreach($connectionIds as $connectionId){
$connection->getById($connectionId);
$connection->delete($user);
$connection->reset();
$connection->reset();
}
}
echo json_encode([]);
}

View File

@@ -7,6 +7,7 @@
*/
namespace Controller\Api;
use Controller;
use Model;
/**
@@ -14,25 +15,23 @@ use Model;
* Class Map
* @package Controller\Api
*/
class Map extends \Controller\AccessController {
class Map extends Controller\AccessController {
/**
* event handler
* @param $f3
* @param \Base $f3
*/
function beforeroute($f3) {
function beforeroute(\Base $f3) {
// set header for all routes
header('Content-type: application/json');
parent::beforeroute($f3);
}
/**
* Get all required static config data for program initialization
* @param $f3
* @param \Base $f3
*/
public function init($f3){
public function init(\Base $f3){
// expire time in seconds
$expireTimeHead = 60 * 60 * 12;
@@ -40,10 +39,11 @@ class Map extends \Controller\AccessController {
$f3->expire($expireTimeHead);
$initData = [];
$return = (object) [];
$return->error = [];
// static program data ------------------------------------------------
$initData['timer'] = $f3->get('PATHFINDER.TIMER');
$return->timer = $f3->get('PATHFINDER.TIMER');
// get all available map types ----------------------------------------
$mapType = Model\BasicModel::getNew('MapTypeModel');
@@ -60,7 +60,7 @@ class Map extends \Controller\AccessController {
$mapTypeData[$rowData->name] = $data;
}
$initData['mapTypes'] = $mapTypeData;
$return->mapTypes = $mapTypeData;
// get all available map scopes ---------------------------------------
$mapScope = Model\BasicModel::getNew('MapScopeModel');
@@ -73,7 +73,7 @@ class Map extends \Controller\AccessController {
];
$mapScopeData[$rowData->name] = $data;
}
$initData['mapScopes'] = $mapScopeData;
$return->mapScopes = $mapScopeData;
// get all available system status ------------------------------------
$systemStatus = Model\BasicModel::getNew('SystemStatusModel');
@@ -87,7 +87,7 @@ class Map extends \Controller\AccessController {
];
$systemScopeData[$rowData->name] = $data;
}
$initData['systemStatus'] = $systemScopeData;
$return->systemStatus = $systemScopeData;
// get all available system types -------------------------------------
$systemType = Model\BasicModel::getNew('SystemTypeModel');
@@ -100,7 +100,7 @@ class Map extends \Controller\AccessController {
];
$systemTypeData[$rowData->name] = $data;
}
$initData['systemType'] = $systemTypeData;
$return->systemType = $systemTypeData;
// get available connection scopes ------------------------------------
$connectionScope = Model\BasicModel::getNew('ConnectionScopeModel');
@@ -114,7 +114,7 @@ class Map extends \Controller\AccessController {
];
$connectionScopeData[$rowData->name] = $data;
}
$initData['connectionScopes'] = $connectionScopeData;
$return->connectionScopes = $connectionScopeData;
// get available character status -------------------------------------
$characterStatus = Model\BasicModel::getNew('CharacterStatusModel');
@@ -128,24 +128,40 @@ class Map extends \Controller\AccessController {
];
$characterStatusData[$rowData->name] = $data;
}
$initData['characterStatus'] = $characterStatusData;
$return->characterStatus = $characterStatusData;
// get max number of shared entities per map --------------------------
$maxSharedCount = [
'user' => $f3->get('PATHFINDER.MAX_SHARED_USER'),
'character' => $f3->get('PATHFINDER.MAX_SHARED_CHARACTER'),
'corporation' => $f3->get('PATHFINDER.MAX_SHARED_CORPORATION'),
'alliance' => $f3->get('PATHFINDER.MAX_SHARED_ALLIANCE'),
];
$initData['maxSharedCount'] = $maxSharedCount;
$return->maxSharedCount = $maxSharedCount;
echo json_encode($initData);
// get program routes -------------------------------------------------
$return->routes = [
'ssoLogin' => $this->getF3()->alias( 'sso', ['action' => 'requestAuthorization'] )
];
// get SSO error messages that should be shown immediately ------------
// -> e.g. errors while character switch from previous HTTP requests
if( $f3->exists(Controller\Ccp\Sso::SESSION_KEY_SSO_ERROR) ){
$ssoError = (object) [];
$ssoError->type = 'error';
$ssoError->title = 'Login failed';
$ssoError->message = $f3->get(Controller\Ccp\Sso::SESSION_KEY_SSO_ERROR);
$return->error[] = $ssoError;
$f3->clear(Controller\Ccp\Sso::SESSION_KEY_SSO_ERROR);
}
echo json_encode($return);
}
/**
* import new map data
* @param $f3
* @param \Base $f3
*/
public function import($f3){
public function import(\Base $f3){
$importData = (array)$f3->get('POST');
$return = (object) [];
@@ -155,13 +171,24 @@ class Map extends \Controller\AccessController {
isset($importData['typeId']) &&
count($importData['mapData']) > 0
){
$user = $this->_getUser();
$activeCharacter = $this->getCharacter();
if($user){
$activeCharacter = $user->getActiveUserCharacter();
if($activeCharacter){
$user = $activeCharacter->getUser();
/**
* @var $map Model\MapModel
*/
$map = Model\BasicModel::getNew('MapModel');
/**
* @var $system Model\SystemModel
*/
$system = Model\BasicModel::getNew('SystemModel');
/**
* @var $connection Model\ConnectionModel
*/
$connection = Model\BasicModel::getNew('ConnectionModel');
foreach($importData['mapData'] as $mapData){
@@ -194,8 +221,8 @@ class Map extends \Controller\AccessController {
$system->setData($systemData);
$system->mapId = $map;
$system->createdCharacterId = $activeCharacter->characterId;
$system->updatedCharacterId = $activeCharacter->characterId;
$system->createdCharacterId = $activeCharacter;
$system->updatedCharacterId = $activeCharacter;
$system->save();
$tempSystemIdMapping[$oldId] = $system->id;
@@ -203,7 +230,6 @@ class Map extends \Controller\AccessController {
}
}
foreach($mapData['data']['connections'] as $connectionData){
// check if source and target IDs match with new system ID
if(
@@ -228,13 +254,11 @@ class Map extends \Controller\AccessController {
if($map->isPrivate()){
$map->setAccess($user);
}elseif($map->isCorporation()){
$corporation = $activeCharacter->getCharacter()->getCorporation();
if($corporation){
if($corporation = $activeCharacter->getCorporation()){
$map->setAccess($corporation);
}
}elseif($map->isAlliance()){
$alliance = $activeCharacter->getCharacter()->getAlliance();
if($alliance){
if($alliance = $activeCharacter->getAlliance()){
$map->setAccess($alliance);
}
}
@@ -260,7 +284,7 @@ class Map extends \Controller\AccessController {
}
}else{
// user not found
$return->error[] = $this->getUserLoggedOffError();
$return->error[] = $this->getLogoutError();
}
}else{
// map data missing
@@ -276,25 +300,28 @@ class Map extends \Controller\AccessController {
/**
* save a new map or update an existing map
* @param $f3
* @param \Base $f3
*/
public function save($f3){
public function save(\Base $f3){
$formData = (array)$f3->get('POST.formData');
$return = (object) [];
$return->error = [];
if( isset($formData['id']) ){
$activeCharacter = $this->getCharacter(0);
$user = $this->_getUser(0);
if($activeCharacter){
if($user){
/**
* @var $map Model\MapModel
*/
$map = Model\BasicModel::getNew('MapModel');
$map->getById( (int)$formData['id'] );
if(
$map->dry() ||
$map->hasAccess($user)
$map->hasAccess($activeCharacter)
){
// new map
$map->setData($formData);
@@ -303,118 +330,117 @@ class Map extends \Controller\AccessController {
// save global map access. Depends on map "type"
if($map->isPrivate()){
// share map between users -> set access
if(isset($formData['mapUsers'])){
// share map between characters -> set access
if(isset($formData['mapCharacters'])){
// avoid abuse -> respect share limits
$accessUsers = array_slice( $formData['mapUsers'], 0, $f3->get('PATHFINDER.MAX_SHARED_USER') );
$accessCharacters = array_slice( $formData['mapCharacters'], 0, $f3->get('PATHFINDER.MAX_SHARED_CHARACTER') );
// clear map access. In case something has removed from access list
$map->clearAccess();
$tempUser = Model\BasicModel::getNew('UserModel');
/**
* @var $tempCharacter Model\CharacterModel
*/
$tempCharacter = Model\BasicModel::getNew('CharacterModel');
foreach($accessUsers as $userId){
$tempUser->getById( (int)$userId );
foreach($accessCharacters as $characterId){
$tempCharacter->getById( (int)$characterId );
if(
!$tempUser->dry() &&
$tempUser->shared == 1 // check if map shared is enabled
!$tempCharacter->dry() &&
$tempCharacter->shared == 1 // check if map shared is enabled
){
$map->setAccess($tempUser);
$map->setAccess($tempCharacter);
}
$tempUser->reset();
$tempCharacter->reset();
}
}
// the current user itself should always have access
// the current character itself should always have access
// just in case he removed himself :)
$map->setAccess($user);
$map->setAccess($activeCharacter);
}elseif($map->isCorporation()){
$activeCharacter = $user->getActiveUserCharacter();
$corporation = $activeCharacter->getCorporation();
if($activeCharacter){
$corporation = $activeCharacter->getCharacter()->getCorporation();
if($corporation){
// the current user has to have a corporation when
// working on corporation maps!
if($corporation){
// the current user has to have a corporation when
// working on corporation maps!
// share map between corporations -> set access
if(isset($formData['mapCorporations'])){
// avoid abuse -> respect share limits
$accessCorporations = array_slice( $formData['mapCorporations'], 0, $f3->get('PATHFINDER.MAX_SHARED_CORPORATION') );
// share map between corporations -> set access
if(isset($formData['mapCorporations'])){
// avoid abuse -> respect share limits
$accessCorporations = array_slice( $formData['mapCorporations'], 0, $f3->get('PATHFINDER.MAX_SHARED_CORPORATION') );
// clear map access. In case something has removed from access list
$map->clearAccess();
// clear map access. In case something has removed from access list
$map->clearAccess();
/**
* @var $tempCorporation Model\CorporationModel
*/
$tempCorporation = Model\BasicModel::getNew('CorporationModel');
$tempCorporation = Model\BasicModel::getNew('CorporationModel');
foreach($accessCorporations as $corporationId){
$tempCorporation->getById( (int)$corporationId );
foreach($accessCorporations as $corporationId){
$tempCorporation->getById( (int)$corporationId );
if(
!$tempCorporation->dry() &&
$tempCorporation->shared == 1 // check if map shared is enabled
){
$map->setAccess($tempCorporation);
}
$tempCorporation->reset();
if(
!$tempCorporation->dry() &&
$tempCorporation->shared == 1 // check if map shared is enabled
){
$map->setAccess($tempCorporation);
}
}
// the corporation of the current user should always have access
$map->setAccess($corporation);
$tempCorporation->reset();
}
}
// the corporation of the current user should always have access
$map->setAccess($corporation);
}
}elseif($map->isAlliance()){
$activeCharacter = $user->getActiveUserCharacter();
$alliance = $activeCharacter->getAlliance();
if($activeCharacter){
$alliance = $activeCharacter->getCharacter()->getAlliance();
if($alliance){
// the current user has to have a alliance when
// working on alliance maps!
if($alliance){
// the current user has to have a alliance when
// working on alliance maps!
// share map between alliances -> set access
if(isset($formData['mapAlliances'])){
// avoid abuse -> respect share limits
$accessAlliances = array_slice( $formData['mapAlliances'], 0, $f3->get('PATHFINDER.MAX_SHARED_ALLIANCE') );
// share map between alliances -> set access
if(isset($formData['mapAlliances'])){
// avoid abuse -> respect share limits
$accessAlliances = array_slice( $formData['mapAlliances'], 0, $f3->get('PATHFINDER.MAX_SHARED_ALLIANCE') );
// clear map access. In case something has removed from access list
$map->clearAccess();
// clear map access. In case something has removed from access list
$map->clearAccess();
/**
* @var $tempAlliance Model\AllianceModel
*/
$tempAlliance = Model\BasicModel::getNew('AllianceModel');
$tempAlliance = Model\BasicModel::getNew('AllianceModel');
foreach($accessAlliances as $allianceId){
$tempAlliance->getById( (int)$allianceId );
foreach($accessAlliances as $allianceId){
$tempAlliance->getById( (int)$allianceId );
if(
!$tempAlliance->dry() &&
$tempAlliance->shared == 1 // check if map shared is enabled
){
$map->setAccess($tempAlliance);
}
$tempAlliance->reset();
if(
!$tempAlliance->dry() &&
$tempAlliance->shared == 1 // check if map shared is enabled
){
$map->setAccess($tempAlliance);
}
$tempAlliance->reset();
}
// the alliance of the current user should always have access
$map->setAccess($alliance);
}
// the alliance of the current user should always have access
$map->setAccess($alliance);
}
}
// reload the same map model (refresh)
// this makes sure all data is up2date
$map->getById( $map->id, 0 );
$return->mapData = $map->getData();
}else{
// map access denied
$captchaError = (object) [];
@@ -423,7 +449,6 @@ class Map extends \Controller\AccessController {
$return->error[] = $captchaError;
}
}
}else{
// map id field missing
$idError = (object) [];
@@ -437,17 +462,19 @@ class Map extends \Controller\AccessController {
/**
* delete a map and all dependencies
* @param $f3
* @param \Base $f3
*/
public function delete($f3){
public function delete(\Base $f3){
$mapData = (array)$f3->get('POST.mapData');
$activeCharacter = $this->getCharacter();
$user = $this->_getUser();
if($user){
if($activeCharacter){
/**
* @var $map Model\MapModel
*/
$map = Model\BasicModel::getNew('MapModel');
$map->getById($mapData['id']);
$map->delete($user);
$map->delete( $activeCharacter );
}
echo json_encode([]);
@@ -455,34 +482,28 @@ class Map extends \Controller\AccessController {
/**
* update map data
* function is called continuously
* @param $f3
* -> function is called continuously (trigger) by any active client
* @param \Base $f3
*/
public function updateData($f3){
// cache time(s) per user should be equal or less than this function is called
// prevent request flooding
$responseTTL = $f3->get('PATHFINDER.TIMER.UPDATE_SERVER_MAP.DELAY') / 1000;
public function updateData(\Base $f3){
$mapData = (array)$f3->get('POST.mapData');
$user = $this->_getUser();
$activeCharacter = $this->getCharacter();
$return = (object) [];
$return->error = [];
if($user){
// -> get active character
$activeCharacter = $user->getActiveUserCharacter();
if($activeCharacter){
$cacheKey = 'user_map_data_' . $activeCharacter->id;
$cacheKey = 'user_map_data_' . $activeCharacter->_id;
// if there is any system/connection change data submitted -> save new data
if(
$f3->exists($cacheKey) === false ||
!empty($mapData)
!empty($mapData) ||
!$f3->exists($cacheKey)
){
// get current map data ========================================================
$maps = $user->getMaps();
$maps = $activeCharacter->getMaps();
// loop all submitted map data that should be saved
// -> currently there will only be ONE map data change submitted -> single loop
@@ -532,7 +553,7 @@ class Map extends \Controller\AccessController {
unset($systemData['updated']);
$system = $filteredMap->systems->current();
$system->setData($systemData);
$system->updatedCharacterId = $activeCharacter->characterId;
$system->updatedCharacterId = $activeCharacter;
$system->save();
// a system belongs to ONE map -> speed up for multiple maps
@@ -561,7 +582,7 @@ class Map extends \Controller\AccessController {
unset($connectionData['updated']);
$connection = $filteredMap->connections->current();
$connection->setData($connectionData);
$connection->save($user);
$connection->save();
// a connection belongs to ONE map -> speed up for multiple maps
unset($connectionData[$i]);
@@ -574,6 +595,11 @@ class Map extends \Controller\AccessController {
// format map Data for return
$return->mapData = self::getFormattedMapData($maps);
// cache time(s) per user should be equal or less than this function is called
// prevent request flooding
$responseTTL = (int)$f3->get('PATHFINDER.TIMER.UPDATE_SERVER_MAP.DELAY') / 1000;
$f3->set($cacheKey, $return, $responseTTL);
}else{
// get from cache
@@ -582,23 +608,24 @@ class Map extends \Controller\AccessController {
}else{
// user logged off
$return->error[] = $this->getUserLoggedOffError();
$return->error[] = $this->getLogoutError();
}
echo json_encode( $return );
}
/**
* get formatted map data
* @param $mapModels
* @return Model\MapModel[]
* @return array
*/
public static function getFormattedMapData($mapModels){
$mapData = [];
foreach($mapModels as $mapModel){
foreach($mapModels as &$mapModel){
/**
* @var $mapModel Model\MapModel
*/
$allMapData = $mapModel->getData();
$mapData[] = [
'config' => $allMapData->mapData,
'data' => [
@@ -613,33 +640,22 @@ class Map extends \Controller\AccessController {
/**
* update map data api
* function is called continuously
* @param $f3
* -> function is called continuously by any active client
* @param \Base $f3
*/
public function updateUserData($f3){
// cache time(s) should be equal or less than request trigger time
// prevent request flooding
$responseTTL = $f3->get('PATHFINDER.TIMER.UPDATE_SERVER_USER_DATA.DELAY') / 1000;
// if the cache key will be set -> cache request
$cacheKey = null;
public function updateUserData(\Base $f3){
$return = (object) [];
$return->error = [];
$activeCharacter = $this->getCharacter(0);
$user = $this->_getUser();
if($user){
if($activeCharacter){
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');
// update current location (IGB data)
$user->updateCharacterLog(60 * 5);
// update current location
$activeCharacter = $activeCharacter->updateLog();
// if data is requested extend the cache key in order to get new data
$requestSystemData = (object) [];
@@ -649,14 +665,13 @@ class Map extends \Controller\AccessController {
// IMPORTANT for now -> just update a single map (save performance)
$mapIds = array_slice($mapIds, 0, 1);
// the userMasData is cached per map (this must be changed if multiple maps
// the userMapData is cached per map (this must be changed if multiple maps
// will be allowed in future...
$tempId = (int)$mapIds[0];
$cacheKey = 'user_data_' . $tempId . '_' . $requestSystemData->systemId;
if( $f3->exists($cacheKey) === false ){
if( !$f3->exists($cacheKey) ){
foreach($mapIds as $mapId){
$map = $user->getMap($mapId);
$map = $activeCharacter->getMap($mapId);
if( !is_null($map) ){
$return->mapUserData[] = $map->getUserData();
@@ -666,7 +681,7 @@ class Map extends \Controller\AccessController {
$system = $map->getSystem( $requestSystemData->systemId );
if( !is_null($system) ){
// data for the current selected system
// data for currently selected system
$return->system = $system->getData();
$return->system->signatures = $system->getSignaturesData();
}
@@ -674,6 +689,10 @@ class Map extends \Controller\AccessController {
}
}
// cache time (seconds) should be equal or less than request trigger time
// prevent request flooding
$responseTTL = (int)$f3->get('PATHFINDER.TIMER.UPDATE_SERVER_USER_DATA.DELAY') / 1000;
// cache response
$f3->set($cacheKey, $return, $responseTTL);
}else{
@@ -686,14 +705,12 @@ class Map extends \Controller\AccessController {
// 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();
$return->userData = $activeCharacter->getUser()->getData();
}else{
// user logged off
$return->error[] = $this->getUserLoggedOffError();
$return->error[] = $this->getLogoutError();
}
echo json_encode( $return );
}

View File

@@ -14,14 +14,12 @@ class Signature extends \Controller\AccessController{
/**
* event handler
* @param $f3
* @param \Base $f3
*/
function beforeroute($f3) {
parent::beforeroute($f3);
function beforeroute(\Base $f3) {
// set header for all routes
header('Content-type: application/json');
parent::beforeroute($f3);
}
/**
@@ -31,18 +29,18 @@ class Signature extends \Controller\AccessController{
public function getAll($f3){
$signatureData = [];
$systemIds = $f3->get('POST.systemIds');
$activeCharacter = $this->getCharacter();
$user = $this->_getUser();
/**
* @var Model\SystemModel $system
*/
$system = Model\BasicModel::getNew('SystemModel');
foreach($systemIds as $systemId){
$system->getById($systemId);
if(!$system->dry()){
// check access
if($system->hasAccess($user)){
if( $system->hasAccess($activeCharacter) ){
$signatureData = $system->getSignaturesData();
}
}
@@ -74,11 +72,13 @@ class Signature extends \Controller\AccessController{
}
if( !is_null($signatureData) ){
$user = $this->_getUser();
$activeCharacter = $this->getCharacter();
if($user){
$activeUserCharacter = $user->getActiveUserCharacter();
$activeCharacter = $activeUserCharacter->getCharacter();
if($activeCharacter){
/**
* @var Model\SystemModel $system
*/
$system = Model\BasicModel::getNew('SystemModel');
// update/add all submitted signatures
@@ -94,9 +94,9 @@ class Signature extends \Controller\AccessController{
$signature = null;
if( isset($data['pk']) ){
// try to get system by "primary key"
$signature = $system->getSignatureById($user, (int)$data['pk']);
$signature = $system->getSignatureById($activeCharacter, (int)$data['pk']);
}elseif( isset($data['name']) ){
$signature = $system->getSignatureByName($user, $data['name']);
$signature = $system->getSignatureByName($activeCharacter, $data['name']);
}
if( is_null($signature) ){
@@ -173,23 +173,23 @@ class Signature extends \Controller\AccessController{
/**
* delete signatures
* @param $f3
* @param \Base $f3
*/
public function delete($f3){
$signatureIds = $f3->get('POST.signatureIds');
$activeCharacter = $this->getCharacter();
$user = $this->_getUser();
/**
* @var Model\SystemSignatureModel $signature
*/
$signature = Model\BasicModel::getNew('SystemSignatureModel');
foreach($signatureIds as $signatureId){
$signature->getById($signatureId);
$signature->delete($user);
$signature->delete( $activeCharacter );
$signature->reset();
}
echo json_encode([]);
}
}

View File

@@ -63,9 +63,9 @@ class System extends \Controller\AccessController {
private $limitQuery = "";
/**
* @param $f3
* @param \Base $f3
*/
function beforeroute($f3) {
function beforeroute(\Base $f3) {
parent::beforeroute($f3);
@@ -92,7 +92,8 @@ class System extends \Controller\AccessController {
* get static system Data from CCPs Static DB export
* search column for IDs can be (solarSystemID, regionID, constellationID)
* @param array $columnIDs
* @return null
* @param string $column
* @return Model\SystemModel[]
* @throws \Exception
*/
protected function _getSystemModelByIds($columnIDs = [], $column = 'solarSystemID'){
@@ -110,10 +111,12 @@ class System extends \Controller\AccessController {
// format result
$mapper = new Mapper\CcpSystemsMapper($rows);
$ccpSystemsData = $mapper->getData();
foreach($ccpSystemsData as $ccpSystemData){
/**
* @var Model\SystemModel $system
*/
$system = Model\BasicModel::getNew('SystemModel');
$system->setData($ccpSystemData);
$systemModels[] = $system;
@@ -142,10 +145,10 @@ class System extends \Controller\AccessController {
/**
* search systems by name
* @param $f3
* @param $params
* @param \Base $f3
* @param array $params
*/
public function search($f3, $params){
public function search(\Base $f3, $params){
$ccpDB = $this->getDB('CCP');
@@ -172,10 +175,9 @@ class System extends \Controller\AccessController {
/**
* save a new system to a a map
* @param $f3
* @param \Base $f3
*/
public function save($f3){
public function save(\Base $f3){
$newSystemData = [];
$postData = (array)$f3->get('POST');
@@ -187,22 +189,22 @@ class System extends \Controller\AccessController {
isset($postData['systemData']) &&
isset($postData['mapData'])
){
$user = $this->_getUser();
$activeCharacter = $this->getCharacter();
if($user){
if($activeCharacter){
$systemData = (array)$postData['systemData'];
$mapData = (array)$postData['mapData'];
$activeCharacter = $user->getActiveUserCharacter();
if( isset($systemData['id']) ){
// update existing system
/**
* @var $system Model\SystemModel
*/
$system = Model\BasicModel::getNew('SystemModel');
$system->getById($systemData['id']);
if( !$system->dry() ){
if( $system->hasAccess($user) ){
if( $system->hasAccess($activeCharacter) ){
// system model found
$systemModel = $system;
}
@@ -210,18 +212,20 @@ class System extends \Controller\AccessController {
}elseif( isset($mapData['id']) ){
// save NEW system
/**
* @var $map Model\MapModel
*/
$map = Model\BasicModel::getNew('MapModel');
$map->getById($mapData['id']);
if( !$map->dry() ){
if( $map->hasAccess($user) ){
if( $map->hasAccess($activeCharacter) ){
$systemData['mapId'] = $map;
// get static system data (CCP DB)
$systemModel = array_values( $this->_getSystemModelByIds([$systemData['systemId']]) )[0];
$systemModel->createdCharacterId = $activeCharacter->characterId;
$systemModel->createdCharacterId = $activeCharacter;
}
}
@@ -229,50 +233,23 @@ class System extends \Controller\AccessController {
}
}
if( !is_null($systemModel) ){
// set/update system
$systemModel->setData($systemData);
$systemModel->updatedCharacterId = $activeCharacter->characterId;
$systemModel->updatedCharacterId = $activeCharacter;
$systemModel->save();
$newSystemData = $systemModel->getData();
}
echo json_encode($newSystemData);
}
/**
* delete systems and all its connections
* @param $f3
*/
public function delete($f3){
$systemIds = $f3->get('POST.systemIds');
$user = $this->_getUser();
if($user){
$system = Model\BasicModel::getNew('SystemModel');
foreach((array)$systemIds as $systemId){
$system->getById($systemId);
$system->delete($user);
$system->reset();
}
}
echo json_encode([]);
}
/**
* get system log data from CCP API import
* system Kills, Jumps,....
* @param $f3
* @param \Base $f3
*/
public function graphData($f3){
public function graphData(\Base $f3){
$graphData = [];
$systemIds = $f3->get('POST.systemIds');
@@ -288,14 +265,13 @@ class System extends \Controller\AccessController {
];
foreach($systemIds as $systemId){
foreach($logTables as $label => $ModelClass){
$systemLogModel = Model\BasicModel::getNew($ModelClass);
// 10min cache (could be up to 1h cache time)
$systemLogModel->getByForeignKey('systemId', $systemId, array(), 60 * 10);
$systemLogModel->getByForeignKey('systemId', $systemId, [], 60 * 10);
if(!$systemLogModel->dry()){
if( !$systemLogModel->dry() ){
$counter = 0;
for( $i = $logEntryCount; $i >= 1; $i--){
$column = 'value' . $i;
@@ -313,7 +289,6 @@ class System extends \Controller\AccessController {
$counter++;
}
}
}
}
@@ -322,25 +297,22 @@ class System extends \Controller\AccessController {
/**
* get system data for all systems within a constellation
* @param $f3
* @param $params
* @param \Base $f3
* @param array $params
*/
public function constellationData($f3, $params){
public function constellationData(\Base $f3, $params){
$return = (object) [];
$return->error = [];
$return->systemData = [];
$constellationId = 0;
$activeCharacter = $this->getCharacter();
$user = $this->_getUser();
if($user){
if($activeCharacter){
// check for search parameter
if( isset($params['arg1']) ){
$constellationId = (int)$params['arg1'];
}
$cacheKey = 'CACHE_CONSTELLATION_SYSTEMS_' . self::formatHiveKey($constellationId);
if($f3->exists($cacheKey)){
@@ -361,7 +333,27 @@ class System extends \Controller\AccessController {
echo json_encode($return);
}
/**
* delete systems and all its connections
* @param \Base $f3
*/
public function delete(\Base $f3){
$systemIds = $f3->get('POST.systemIds');
if($activeCharacter = $this->getCharacter()){
/**
* @var Model\SystemModel $system
*/
$system = Model\BasicModel::getNew('SystemModel');
foreach((array)$systemIds as $systemId){
$system->getById($systemId);
$system->delete($activeCharacter);
$system->reset();
}
}
echo json_encode([]);
}
}

View File

@@ -15,87 +15,80 @@ use DB;
class User extends Controller\Controller{
// captcha specific session keys
const SESSION_CAPTCHA_ACCOUNT_UPDATE = 'SESSION.CAPTCHA.ACCOUNT.UPDATE';
const SESSION_CAPTCHA_ACCOUNT_DELETE = 'SESSION.CAPTCHA.ACCOUNT.DELETE';
// user specific session keys
const SESSION_KEY_USER = 'SESSION.USER';
const SESSION_KEY_USER_ID = 'SESSION.USER.ID';
const SESSION_KEY_USER_NAME = 'SESSION.USER.NAME';
// character specific session keys
const SESSION_KEY_CHARACTER = 'SESSION.CHARACTER';
const SESSION_KEY_CHARACTER_ID = 'SESSION.CHARACTER.ID';
const SESSION_KEY_CHARACTER_NAME = 'SESSION.CHARACTER.NAME';
const SESSION_KEY_CHARACTER_TIME = 'SESSION.CHARACTER.TIME';
const SESSION_KEY_CHARACTER_ACCESS_TOKEN = 'SESSION.CHARACTER.ACCESS_TOKEN';
const SESSION_KEY_CHARACTER_REFRESH_TOKEN = 'SESSION.CHARACTER.REFRESH_TOKEN';
// log text
const LOG_LOGGED_IN = 'userId: %s, userName: %s, charId: %s, charName: %s';
/**
* valid reasons for captcha images
* @var array
* @var string array
*/
private static $captchaReason = ['createAccount', 'deleteAccount'];
private static $captchaReason = [self::SESSION_CAPTCHA_ACCOUNT_UPDATE, self::SESSION_CAPTCHA_ACCOUNT_DELETE];
/**
* login function
* @param $f3
* login a valid character
* @param Model\CharacterModel $characterModel
* @return bool
*/
public function logIn($f3){
$data = $data = $f3->get('POST');
protected function loginByCharacter(Model\CharacterModel &$characterModel){
$login = false;
$return = (object) [];
$user = null;
if($data['loginData']){
$loginData = $data['loginData'];
$user = $this->logUserIn( $loginData['userName'], $loginData['userPassword'] );
}
// set "vague" error
if(is_null($user)){
$return->error = [];
$loginError = (object) [];
$loginError->type = 'login';
$return->error[] = $loginError;
}else{
// update/check api data
$user->updateApiData();
// route user to map app
$return->reroute = rtrim(self::getEnvironmentData('URL'), '/') . $f3->alias('map');
}
echo json_encode($return);
}
/**
* core function for user login
* @param $userName
* @param $password
* @return Model\UserModel|null
*/
private function logUserIn($userName, $password){
// try to verify user
$user = $this->_verifyUser($userName, $password);
if( !is_null($user)){
// user is verified -> ready for login
// set Session login
$dateTime = new \DateTime();
$this->f3->set('SESSION.user', [
'time' => $dateTime->getTimestamp(),
'name' => $user->name,
'id' => $user->id
if($user = $characterModel->getUser()){
// set user/character data to session -------------------
$this->f3->set(self::SESSION_KEY_USER, [
'ID' => $user->_id,
'NAME' => $user->name
]);
// save user login information
$user->touch('lastLogin');
$user->save();
$dateTime = new \DateTime();
$this->f3->set(self::SESSION_KEY_CHARACTER, [
'ID' => $characterModel->_id,
'NAME' => $characterModel->name,
'TIME' => $dateTime->getTimestamp()
]);
// save log
$logText = "id: %s, name: %s, ip: %s";
// save user login information ---------------------------
$characterModel->touch('lastLogin');
$characterModel->save();
// write login log --------------------------------------
self::getLogger( $this->f3->get('PATHFINDER.LOGFILES.LOGIN') )->write(
sprintf($logText, $user->id, $user->name, $this->f3->get('IP'))
sprintf(self::LOG_LOGGED_IN,
$user->_id,
$user->name,
$characterModel->_id,
$characterModel->name
)
);
$login = true;
}
return $user;
return $login;
}
/**
* get captcha image and store key to session
* @param $f3
* @param \Base $f3
*/
public function getCaptcha($f3){
public function getCaptcha(\Base $f3){
$data = $f3->get('POST');
$return = (object) [];
@@ -117,7 +110,7 @@ class User extends Controller\Controller{
'fonts/oxygen-bold-webfont.ttf',
14,
6,
'SESSION.' . $reason,
$reason,
'',
$colorText,
$colorBG
@@ -136,29 +129,22 @@ class User extends Controller\Controller{
/**
* delete the character log entry for the current active (main) character
* @param $f3
* @param \Base $f3
*/
public function deleteLog($f3){
$user = $this->_getUser();
if($user){
$activeUserCharacter = $user->getActiveUserCharacter();
if($activeUserCharacter){
$character = $activeUserCharacter->getCharacter();
if($characterLog = $character->getLog()){
$characterLog->erase();
}
public function deleteLog(\Base $f3){
$activeCharacter = $this->getCharacter();
if($activeCharacter){
if($characterLog = $activeCharacter->getLog()){
$characterLog->erase();
}
}
}
/**
* log the current user out + clear character system log data
* @param $f3
* @param \Base $f3
*/
public function logOut($f3){
public function logOut(\Base $f3){
$this->deleteLog($f3);
parent::logOut($f3);
}
@@ -166,20 +152,19 @@ class User extends Controller\Controller{
/**
* save/update "map sharing" configurations for all map types
* the user has access to
* @param $f3
* @param \Base $f3
*/
public function saveSharingConfig($f3){
public function saveSharingConfig(\Base $f3){
$data = $f3->get('POST');
$return = (object) [];
$privateSharing = 0;
$corporationSharing = 0;
$allianceSharing = 0;
$activeCharacter = $this->getCharacter();
$user = $this->_getUser();
if($user){
if($activeCharacter){
$privateSharing = 0;
$corporationSharing = 0;
$allianceSharing = 0;
// form values
if(isset($data['formData'])){
@@ -198,28 +183,24 @@ class User extends Controller\Controller{
}
}
$user->shared = $privateSharing;
$user->save();
$activeCharacter->shared = $privateSharing;
$activeCharacter = $activeCharacter->save();
// update corp/ally ---------------------------------------------------------------
$corporation = $activeCharacter->getCorporation();
$alliance = $activeCharacter->getAlliance();
$activeUserCharacter = $user->getActiveUserCharacter();
if(is_object($activeUserCharacter)){
$corporation = $activeUserCharacter->getCharacter()->getCorporation();
$alliance = $activeUserCharacter->getCharacter()->getAlliance();
if(is_object($corporation)){
$corporation->shared = $corporationSharing;
$corporation->save();
}
if(is_object($alliance)){
$alliance->shared = $allianceSharing;
$alliance->save();
}
if(is_object($corporation)){
$corporation->shared = $corporationSharing;
$corporation->save();
}
if(is_object($alliance)){
$alliance->shared = $allianceSharing;
$alliance->save();
}
$user = $activeCharacter->getUser();
$return->userData = $user->getData();
}
@@ -227,246 +208,111 @@ class User extends Controller\Controller{
}
/**
* 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
* update user account data
* -> a fresh user automatically generated on first login with a new character
* -> see CREST SSO login
* @param \Base $f3
*/
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 account data
* @param $f3
*/
public function saveAccount($f3){
public function saveAccount(\Base $f3){
$data = $f3->get('POST');
$return = (object) [];
$return->error = [];
$captcha = $f3->get('SESSION.createAccount');
$captcha = $f3->get(self::SESSION_CAPTCHA_ACCOUNT_UPDATE);
// reset captcha -> forces user to enter new one
$f3->clear('SESSION.createAccount');
$f3->clear(self::SESSION_CAPTCHA_ACCOUNT_UPDATE);
$newUserData = null;
// check for new user
$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'];
if( isset($data['formData']) ){
$formData = $data['formData'];
try{
$user = $this->_getUser(0);
if($activeCharacter = $this->getCharacter(0)){
$user = $activeCharacter->getUser();
// captcha is send -> check captcha
if(
isset($settingsData['captcha']) &&
!empty($settingsData['captcha'])
){
if($settingsData['captcha'] === $captcha){
// 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;
// captcha is send -> check captcha ---------------------------------
if(
isset($formData['captcha']) &&
!empty($formData['captcha'])
){
if($formData['captcha'] === $captcha){
// change/set sensitive user data requires captcha!
// set username
if(
isset($settingsData['name']) &&
!empty($settingsData['name'])
isset($formData['name']) &&
!empty($formData['name'])
){
$user->name = $settingsData['name'];
}
}
// change/set email
if(
isset($settingsData['email']) &&
isset($settingsData['email_confirm']) &&
!empty($settingsData['email']) &&
!empty($settingsData['email_confirm']) &&
$settingsData['email'] == $settingsData['email_confirm']
){
$user->email = $settingsData['email'];
}
// change/set password
if(
isset($settingsData['password']) &&
isset($settingsData['password_confirm']) &&
!empty($settingsData['password']) &&
!empty($settingsData['password_confirm']) &&
$settingsData['password'] == $settingsData['password_confirm']
){
$user->password = $settingsData['password'];
}
}else{
// captcha was send but not valid -> return error
$captchaError = (object) [];
$captchaError->type = 'error';
$captchaError->message = 'Captcha does not match';
$return->error[] = $captchaError;
}
}
// saving additional user info requires valid user object (no captcha required)
if($user){
// save API data
if(
isset($settingsData['keyId']) &&
isset($settingsData['vCode']) &&
is_array($settingsData['keyId']) &&
is_array($settingsData['vCode'])
){
// get all existing API models for this user
$apiModels = $user->getAPIs();
foreach($settingsData['keyId'] as $i => $keyId){
$api = null;
// search for existing API model
foreach($apiModels as $key => $apiModel){
if($apiModel->keyId == $keyId){
$api = $apiModel;
// make sure model is up2data -> cast()
$api->cast();
unset($apiModels[$key]);
break;
}
$user->name = $formData['name'];
}
if(is_null($api)){
// new API Key
$api = Model\BasicModel::getNew('UserApiModel');
$api->userId = $user;
// set email
if(
isset($formData['email']) &&
isset($formData['email_confirm']) &&
!empty($formData['email']) &&
!empty($formData['email_confirm']) &&
$formData['email'] == $formData['email_confirm']
){
$user->email = $formData['email'];
}
$api->keyId = $keyId;
$api->vCode = $settingsData['vCode'][$i];
$api->save();
// save/update user model
// this will fail if model validation fails!
$user->save();
$characterCount = $api->updateCharacters();
}else{
// captcha was send but not valid -> return error
$captchaError = (object) [];
$captchaError->type = 'error';
$captchaError->message = 'Captcha does not match';
$return->error[] = $captchaError;
}
}
if($characterCount == 0){
// no characters found -> return warning
$characterError = (object) [];
$characterError->type = 'warning';
$characterError->message = 'API verification failed. No Characters found for KeyId ' . $api->keyId;
$return->error[] = $characterError;
}
// sharing config ---------------------------------------------------
if(isset($formData['share'])){
$privateSharing = 0;
$corporationSharing = 0;
$allianceSharing = 0;
if(isset($formData['privateSharing'])){
$privateSharing = 1;
}
// delete API models that no longer exists
foreach($apiModels as $apiModel){
$apiModel->delete();
if(isset($formData['corporationSharing'])){
$corporationSharing = 1;
}
// get fresh updated user object (API info may have has changed)
$user = $this->_getUser(0);
}
if(isset($formData['allianceSharing'])){
$allianceSharing = 1;
}
// set main character
if( isset($settingsData['mainCharacterId']) ){
$user->setMainCharacterId((int)$settingsData['mainCharacterId']);
}
// update private/corp/ally
$corporation = $activeCharacter->getCorporation();
$alliance = $activeCharacter->getAlliance();
// check if the user already has a main character
// if not -> save the next best character as main
$mainUserCharacter = $user->getMainUserCharacter();
if(is_object($corporation)){
$corporation->shared = $corporationSharing;
$corporation->save();
}
// set main character if no main character exists
if(is_null($mainUserCharacter)){
$user->setMainCharacterId();
}
if(is_object($alliance)){
$alliance->shared = $allianceSharing;
$alliance->save();
}
// save/update user model
// 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'] );
// return reroute path
$return->reroute = rtrim(self::getEnvironmentData('URL'), '/') . $this->f3->alias('map');
$activeCharacter->shared = $privateSharing;
$activeCharacter->save();
}
// get fresh updated user object
$user = $this->_getUser(0);
$newUserData = $user->getData();
}
}catch(Exception\ValidationException $e){
$validationError = (object) [];
$validationError->type = 'error';
@@ -483,109 +329,6 @@ class User extends Controller\Controller{
// return new/updated user data
$return->userData = $newUserData;
}
echo json_encode($return);
}
/**
* send mail with registration key
* -> check INVITE in pathfinder.ini
* @param $f3
* @throws Exception
*/
public function sendInvite($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 by 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->sendInviteKey($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);
@@ -593,31 +336,26 @@ class User extends Controller\Controller{
/**
* delete current user account from DB
* @param $f3
* @param \Base $f3
*/
public function deleteAccount($f3){
public function deleteAccount(\Base $f3){
$data = $f3->get('POST.formData');
$return = (object) [];
$captcha = $f3->get('SESSION.deleteAccount');
$captcha = $f3->get(self::SESSION_CAPTCHA_ACCOUNT_DELETE);
// reset captcha -> forces user to enter new one
$f3->clear('SESSION.deleteAccount');
$f3->clear(self::SESSION_CAPTCHA_ACCOUNT_DELETE);
if(
isset($data['captcha']) &&
!empty($data['captcha']) &&
$data['captcha'] === $captcha
){
$user = $this->_getUser(0);
$activeCharacter = $this->getCharacter(0);
$user = $activeCharacter->getUser();
$validUser = $this->_verifyUser( $user->name, $data['password']);
if(
is_object($validUser) &&
is_object($user) &&
$user->id === $validUser->id
){
if($user){
// send delete account mail
$msg = 'Hello ' . $user->name . ',<br><br>';
$msg .= 'your account data has been successfully deleted.';
@@ -638,12 +376,6 @@ class User extends Controller\Controller{
$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

View File

@@ -7,15 +7,28 @@
*/
namespace Controller;
use Controller\Ccp as Ccp;
class AppController extends Controller {
/**
* show main login (index) page
* @param $f3
* event handler after routing
* @param \Base $f3
*/
public function init($f3) {
public function afterroute(\Base $f3){
parent::afterroute($f3);
// clear all SSO related temp data
if( $f3->exists(Ccp\Sso::SESSION_KEY_SSO) ){
$f3->clear(Ccp\Sso::SESSION_KEY_SSO);
}
}
/**
* show main login (index) page
* @param \Base $f3
*/
public function init(\Base $f3) {
// page title
$f3->set('pageTitle', 'Login');

View File

@@ -0,0 +1,659 @@
<?php
/**
* Created by PhpStorm.
* User: Exodus
* Date: 23.01.2016
* Time: 17:18
*
* Handles access to EVE-Online "CREST API" and "SSO" auth functions
* - Add your API credentials in "environment.ini"
* - Check "PATHFINDER.API" in "pathfinder.ini" for correct API URLs
* Hint: \Web::instance()->request automatically caches responses by their response "Cache-Control" header!
*/
namespace Controller\Ccp;
use Controller;
use Controller\Api as Api;
use Data\Mapper as Mapper;
use Model;
use Lib;
class Sso extends Api\User{
/**
* @var int timeout (seconds) for API calls
*/
const CREST_TIMEOUT = 3;
/**
* @var int expire time (seconds) for an valid "accessToken"
*/
const ACCESS_KEY_EXPIRE_TIME = 20 * 60;
// SSO specific session keys
const SESSION_KEY_SSO = 'SESSION.SSO';
const SESSION_KEY_SSO_ERROR = 'SESSION.SSO.ERROR';
const SESSION_KEY_SSO_STATE = 'SESSION.SSO.STATE';
const SESSION_KEY_SSO_CHARACTER_ID = 'SESSION.SSO.CHARACTER.ID';
// cache keys
const CACHE_KEY_LOCATION_DATA = 'CACHED.LOCATION.%s';
// error messages
const ERROR_CCP_SSO_URL = 'Invalid "ENVIRONMENT.[ENVIRONMENT].SSO_CCP_URL" url. %s';
const ERROR_CCP_CREST_URL = 'Invalid "ENVIRONMENT.[ENVIRONMENT].CCP_CREST_URL" url. %s';
const ERROR_RESOURCE_DEPRECATED = 'Resource: %s has been marked as deprecated. %s';
const ERROR_ACCESS_TOKEN = 'Unable to get a valid "access_token. %s';
const ERROR_VERIFY_CHARACTER = 'Unable to verify character data. %s';
const ERROR_GET_ENDPOINT = 'Unable to get endpoint data. $s';
const ERROR_FIND_ENDPOINT = 'Unable to find endpoint: %s';
const ERROR_LOGIN_FAILED = 'Failed authentication due to technical problems: %s';
const ERROR_CHARACTER_FORBIDDEN = 'Character "%s" is not authorized to log in';
const ERROR_CHARACTER_MISMATCH = 'The character "%s" you tried to log in, does not match';
const ERROR_SERVICE_TIMEOUT = 'CCP SSO service timeout (%ss). Try again later';
/**
* CREST "Scopes" are used by pathfinder
* -> Enable scopes: https://developers.eveonline.com
* @var array
*/
private $requestScopes = [
// 'characterFittingsRead',
// 'characterFittingsWrite',
'characterLocationRead',
'characterNavigationWrite'
];
/**
* redirect user to CCP SSO page and request authorization
* @param \Base $f3
*/
public function requestAuthorization($f3){
$params = $f3->get('GET');
if(isset($params['characterId'])){
// restrict login to this characterId e.g. for character switch
$f3->set(self::SESSION_KEY_SSO_CHARACTER_ID, (int)trim($params['characterId']) );
}
// used for "state" check between request and callback
$state = bin2hex(mcrypt_create_iv(12, MCRYPT_DEV_URANDOM));
$f3->set(self::SESSION_KEY_SSO_STATE, $state);
$urlParams = [
'response_type' => 'code',
'redirect_uri' => Controller\Controller::getEnvironmentData('URL') . $f3->build('/sso/callbackAuthorization'),
'client_id' => Controller\Controller::getEnvironmentData('SSO_CCP_CLIENT_ID'),
'scope' => implode(' ', $this->requestScopes),
'state' => $state
];
$ssoAuthUrl = self::getAuthorizationEndpoint() . '?' . http_build_query($urlParams, '', '&', PHP_QUERY_RFC3986 );
$f3->status(302);
$f3->reroute($ssoAuthUrl);
}
/**
* callback handler for CCP SSO user Auth
* -> see requestAuthorization()
* @param \Base $f3
*/
public function callbackAuthorization($f3){
$getParams = (array)$f3->get('GET');
// users can log in either from @login (new user) or @map (existing user) root alias
// -> in case login fails, users should be redirected differently
$authFromMapAlias = false;
if($f3->exists(self::SESSION_KEY_SSO_STATE)){
// check response and validate 'state'
if(
isset($getParams['code']) &&
isset($getParams['state']) &&
!empty($getParams['code']) &&
!empty($getParams['state']) &&
$f3->get(self::SESSION_KEY_SSO_STATE) === $getParams['state']
){
// $requestedCharacterId can be [-1 => add char, 0 => new user, >0 => specific user]
$requiredCharacterId = (int)$f3->get(self::SESSION_KEY_SSO_CHARACTER_ID);
if($requiredCharacterId !== 0){
$authFromMapAlias = true;
}
// clear 'state' for new next login request
$f3->clear(self::SESSION_KEY_SSO_STATE);
$f3->clear(self::SESSION_KEY_SSO_CHARACTER_ID);
$accessData = $this->getCrestAccessData($getParams['code']);
if(
isset($accessData->accessToken) &&
isset($accessData->refreshToken)
){
// login succeeded -> get basic character data for current login
$verificationCharacterData = $this->verifyCharacterData($accessData->accessToken);
if( !is_null($verificationCharacterData)){
// check if login is restricted to a characterID
if(
$requiredCharacterId <= 0 ||
$verificationCharacterData->CharacterID === $requiredCharacterId
){
// verification available data. Data is needed for "ownerHash" check
// get character data from CREST
$characterData = $this->getCharacterData($accessData->accessToken);
if(isset($characterData->character)){
// add "ownerHash" and CREST tokens
$characterData->character['ownerHash'] = $verificationCharacterData->CharacterOwnerHash;
$characterData->character['crestAccessToken'] = $accessData->accessToken;
$characterData->character['crestRefreshToken'] = $accessData->refreshToken;
// add/update static character data
$characterModel = $this->updateCharacter($characterData);
if( !is_null($characterModel) ){
// check if character is authorized to log in
if($characterModel->isAuthorized()){
// character is authorized to log in
// -> update character log (current location,...)
$characterModel = $characterModel->updateLog();
// check if there is already an active user logged in
if($activeCharacter = $this->getCharacter()){
// connect character with current user
$user = $activeCharacter->getUser();
}elseif( is_null( $user = $characterModel->getUser()) ){
// no user found (new character) -> create new user and connect to character
$user = Model\BasicModel::getNew('UserModel');
$user->name = $characterModel->name;
$user->save();
}
if( is_null($userCharactersModel = $characterModel->userCharacter) ){
$userCharactersModel = Model\BasicModel::getNew('UserCharacterModel');
$userCharactersModel->characterId = $characterModel;
}
// user might have changed
$userCharactersModel->userId = $user;
$userCharactersModel->save();
// get updated character model
$characterModel = $userCharactersModel->getCharacter();
// login by character
$loginCheck = $this->loginByCharacter($characterModel);
if($loginCheck){
// route to "map"
$f3->reroute('@map');
}else{
$f3->set(self::SESSION_KEY_SSO_ERROR, sprintf(self::ERROR_LOGIN_FAILED, $characterModel->name));
}
}else{
// character is not authorized to log in
$f3->set(self::SESSION_KEY_SSO_ERROR, sprintf(self::ERROR_CHARACTER_FORBIDDEN, $characterModel->name));
}
}
}
}else{
// characterID is not allowed to login
$f3->set(self::SESSION_KEY_SSO_ERROR, sprintf(self::ERROR_CHARACTER_MISMATCH, $verificationCharacterData->CharacterName));
}
}
}else{
// CREST "accessData" missing (e.g. timeout)
$f3->set(self::SESSION_KEY_SSO_ERROR, sprintf(self::ERROR_SERVICE_TIMEOUT, self::CREST_TIMEOUT));
}
}
}
if($authFromMapAlias){
// on error -> route back to map
$f3->reroute('@map');
}else{
// on error -> route back to login form
$f3->reroute('@login');
}
}
/**
* get a valid "access_token" for oAuth 2.0 verification
* -> if $authCode is set -> request NEW "access_token"
* -> else check for existing (not expired) "access_token"
* -> else try to refresh auth and get fresh "access_token"
* @param bool $authCode
* @return null|\stdClass
*/
public function getCrestAccessData($authCode){
$accessData = null;
if( !empty($authCode) ){
// Authentication Code is set -> request new "accessToken"
$accessData = $this->verifyAuthorizationCode($authCode);
}else{
// Unable to get Token -> trigger error
self::getCrestLogger()->write(sprintf(self::ERROR_ACCESS_TOKEN, $authCode));
}
return $accessData;
}
/**
* verify authorization code, and get an "access_token" data
* @param $authCode
* @return \stdClass
*/
protected function verifyAuthorizationCode($authCode){
$requestParams = [
'grant_type' => 'authorization_code',
'code' => $authCode
];
return $this->requestAccessData($requestParams);
}
/**
* get new "access_token" by an existing "refresh_token"
* -> if "access_token" is expired, this function gets a fresh one
* @param $refreshToken
* @return \stdClass
*/
public function refreshAccessToken($refreshToken){
$requestParams = [
'grant_type' => 'refresh_token',
'refresh_token' => $refreshToken
];
return $this->requestAccessData($requestParams);
}
/**
* request an "access_token" AND "refresh_token" data
* -> this can either be done by sending a valid "authorization code"
* OR by providing a valid "refresh_token"
* @param $requestParams
* @return \stdClass
*/
protected function requestAccessData($requestParams){
$verifyAuthCodeUrl = self::getVerifyAuthorizationCodeEndpoint();
$verifyAuthCodeUrlParts = parse_url($verifyAuthCodeUrl);
$accessData = (object) [];
$accessData->accessToken = null;
$accessData->refreshToken = null;
if($verifyAuthCodeUrlParts){
$contentType = 'application/x-www-form-urlencoded';
$requestOptions = [
'timeout' => self::CREST_TIMEOUT,
'method' => 'POST',
'user_agent' => $this->getUserAgent(),
'header' => [
'Authorization: Basic ' . $this->getAuthorizationHeader(),
'Content-Type: ' . $contentType,
'Host: ' . $verifyAuthCodeUrlParts['host']
]
];
// content (parameters to send with)
$requestOptions['content'] = http_build_query($requestParams);
$apiResponse = Lib\Web::instance()->request($verifyAuthCodeUrl, $requestOptions);
if($apiResponse['body']){
$authCodeRequestData = json_decode($apiResponse['body'], true);
if( !empty($authCodeRequestData) ){
if( isset($authCodeRequestData['access_token']) ){
// this token is required for endpoints that require Auth
$accessData->accessToken = $authCodeRequestData['access_token'];
}
if(isset($authCodeRequestData['refresh_token'])){
// this token is used to refresh/get a new access_token when expires
$accessData->refreshToken = $authCodeRequestData['refresh_token'];
}
}
}else{
self::getCrestLogger()->write(
sprintf(
self::ERROR_ACCESS_TOKEN,
print_r($requestParams, true)
)
);
}
}else{
self::getCrestLogger()->write(
sprintf(self::ERROR_CCP_SSO_URL, __METHOD__)
);
}
return $accessData;
}
/**
* verify character data by "access_token"
* -> get some basic information (like character id)
* -> if more character information is required, use CREST endpoints request instead
* @param $accessToken
* @return mixed|null
*/
protected function verifyCharacterData($accessToken){
$verifyUserUrl = self::getVerifyUserEndpoint();
$verifyUrlParts = parse_url($verifyUserUrl);
$characterData = null;
if($verifyUrlParts){
$requestOptions = [
'timeout' => self::CREST_TIMEOUT,
'method' => 'GET',
'user_agent' => $this->getUserAgent(),
'header' => [
'Authorization: Bearer ' . $accessToken,
'Host: ' . $verifyUrlParts['host']
]
];
$apiResponse = Lib\Web::instance()->request($verifyUserUrl, $requestOptions);
if($apiResponse['body']){
$characterData = json_decode($apiResponse['body']);
}else{
self::getCrestLogger()->write(sprintf(self::ERROR_VERIFY_CHARACTER, __METHOD__));
}
}else{
self::getCrestLogger()->write(sprintf(self::ERROR_CCP_SSO_URL, __METHOD__));
}
return $characterData;
}
/**
* get all available Endpoints
* @param $accessToken
* @return mixed|null
*/
protected function getEndpoints($accessToken){
$crestUrl = self::getCrestEndpoint();
$contentType = 'application/vnd.ccp.eve.Api-v3+json';
$endpoint = $this->getEndpoint($accessToken, $crestUrl, $contentType);
return $endpoint;
}
/**
* get a specific endpoint by its $resourceUrl
* @param $accessToken
* @param $resourceUrl
* @param string $contentType
* @return mixed|null
*/
protected function getEndpoint($accessToken, $resourceUrl, $contentType = ''){
$resourceUrlParts = parse_url($resourceUrl);
$endpoint = null;
if($resourceUrlParts){
$requestOptions = [
'timeout' => self::CREST_TIMEOUT,
'method' => 'GET',
'user_agent' => $this->getUserAgent(),
'header' => [
'Authorization: Bearer ' . $accessToken,
'Host: login.eveonline.com',
'Host: ' . $resourceUrlParts['host']
]
];
// if specific contentType is required -> add it to request header
// CREST versioning can be done by calling different "Accept:" Headers
if( !empty($contentType) ){
$requestOptions['header'][] = 'Accept: ' . $contentType;
}
$apiResponse = Lib\Web::instance()->request($resourceUrl, $requestOptions);
if($apiResponse['headers']){
// check headers for error
$this->checkResponseHeaders($apiResponse['headers'], $requestOptions);
if($apiResponse['body']){
$endpoint = json_decode($apiResponse['body'], true);
}else{
self::getCrestLogger()->write(sprintf(self::ERROR_GET_ENDPOINT, __METHOD__));
}
}
}else{
self::getCrestLogger()->write(sprintf(self::ERROR_CCP_CREST_URL, __METHOD__));
}
return $endpoint;
}
/**
* recursively walk down the CREST API tree by a given $path array
* -> return "leaf" endpoint
* @param $accessToken
* @param $endpoint
* @param array $path
* @return null|string
*/
protected function walkEndpoint($accessToken, $endpoint, $path = []){
$targetEndpoint = null;
if( !empty($path) ){
$newNode = array_shift($path);
if(isset($endpoint[$newNode])){
$currentEndpoint = $endpoint[$newNode];
if(isset($currentEndpoint['href'])){
$newEndpoint = $this->getEndpoint($accessToken, $currentEndpoint['href']);
$targetEndpoint = $this->walkEndpoint($accessToken, $newEndpoint, $path);
}else{
// leaf found
$targetEndpoint = $currentEndpoint;
}
}else{
// endpoint not found
self::getCrestLogger()->write(sprintf(self::ERROR_FIND_ENDPOINT, $newNode));
}
}else{
$targetEndpoint = $endpoint;
}
return $targetEndpoint;
}
/**
* get character data
* @param $accessToken
* @return array
*/
protected function getCharacterData($accessToken){
$endpoints = $this->getEndpoints($accessToken);
$characterData = (object) [];
$endpoint = $this->walkEndpoint($accessToken, $endpoints, [
'decode',
'character'
]);
if( !empty($endpoint) ){
$characterData->character = (new Mapper\CrestCharacter($endpoint))->getData();
if(isset($endpoint['corporation'])){
$characterData->corporation = (new Mapper\CrestCorporation($endpoint['corporation']))->getData();
}
}
return $characterData;
}
/**
* get current character location data (result is cached!)
* -> solarSystem data where character is currently active
* @param $accessToken
* @param int $ttl
* @return array
*/
public function getCharacterLocationData($accessToken, $ttl = 10){
$locationData = [];
// in addition to the cURL caching (based on cache-control headers,
// the final location data is cached additionally -> speed up
$cacheKey = sprintf(self::CACHE_KEY_LOCATION_DATA, 'TOKEN_' . hash('md5', $accessToken));
if( !$this->getF3()->exists($cacheKey) ){
$endpoints = $this->getEndpoints($accessToken);
$endpoint = $this->walkEndpoint($accessToken, $endpoints, [
'decode',
'character',
'location'
]);
if( !empty($endpoint) ){
if(isset($endpoint['solarSystem'])){
$locationData['system'] = (new Mapper\CrestSystem($endpoint['solarSystem']))->getData();
}
}
$this->getF3()->set($cacheKey, $locationData, $ttl);
}else{
$locationData = $this->getF3()->get($cacheKey);
}
return $locationData;
}
/**
* update character
* @param $characterData
* @return \Model\CharacterModel
* @throws \Exception
*/
protected function updateCharacter($characterData){
$characterModel = null;
$corporationModel = null;
$allianceModel = null;
if( isset($characterData->corporation) ){
/**
* @var Model\CorporationModel $corporationModel
*/
$corporationModel = Model\BasicModel::getNew('CorporationModel');
$corporationModel->getById($characterData->corporation['id'], 0);
$corporationModel->copyfrom($characterData->corporation);
$corporationModel->save();
}
if( isset($characterData->alliance) ){
/**
* @var Model\AllianceModel $allianceModel
*/
$allianceModel = Model\BasicModel::getNew('AllianceModel');
$allianceModel->getById($characterData->alliance['id'], 0);
$allianceModel->copyfrom($characterData->alliance);
$allianceModel->save();
}
if( isset($characterData->character) ){
/**
* @var Model\CharacterModel $characterModel
*/
$characterModel = Model\BasicModel::getNew('CharacterModel');
$characterModel->getById($characterData->character['id'], 0);
$characterModel->copyfrom($characterData->character);
$characterModel->corporationId = $corporationModel;
$characterModel->allianceId = $allianceModel;
$characterModel = $characterModel->save();
}
return $characterModel;
}
/**
* check response "Header" data for errors
* @param $headers
* @param string $requestUrl
* @param string $contentType
*/
protected function checkResponseHeaders($headers, $requestUrl = '', $contentType = ''){
$headers = (array)$headers;
if(preg_grep ('/^X-Deprecated/i', $headers)){
self::getCrestLogger()->write(sprintf(self::ERROR_RESOURCE_DEPRECATED, $requestUrl, $contentType));
}
}
/**
* get "Authorization:" Header data
* -> This header is required for any Auth-required endpoints!
* @return string
*/
protected function getAuthorizationHeader(){
return base64_encode(
Controller\Controller::getEnvironmentData('SSO_CCP_CLIENT_ID') . ':'
. Controller\Controller::getEnvironmentData('SSO_CCP_SECRET_KEY')
);
}
/**
* get CCP CREST url from configuration file
* -> throw error if url is broken/missing
* @return string
*/
static function getCrestEndpoint(){
$url = '';
if( \Audit::instance()->url(self::getEnvironmentData('CCP_CREST_URL')) ){
$url = self::getEnvironmentData('CCP_CREST_URL');
}else{
$error = sprintf(self::ERROR_CCP_CREST_URL, __METHOD__);
self::getCrestLogger()->write($error);
\Base::instance()->error(502, $error);
}
return $url;
}
/**
* get CCP SSO url from configuration file
* -> throw error if url is broken/missing
* @return string
*/
static function getSsoUrlRoot(){
$url = '';
if( \Audit::instance()->url(self::getEnvironmentData('SSO_CCP_URL')) ){
$url = self::getEnvironmentData('SSO_CCP_URL');
}else{
$error = sprintf(self::ERROR_CCP_SSO_URL, __METHOD__);
self::getCrestLogger()->write($error);
\Base::instance()->error(502, $error);
}
return $url;
}
static function getAuthorizationEndpoint(){
return self::getSsoUrlRoot() . '/oauth/authorize';
}
static function getVerifyAuthorizationCodeEndpoint(){
return self::getSsoUrlRoot() . '/oauth/token';
}
static function getVerifyUserEndpoint(){
return self::getSsoUrlRoot() . '/oauth/verify';
}
/**
* get logger for CREST logging
* @return \Log
*/
static function getCrestLogger(){
return parent::getLogger('crest');
}
}

View File

@@ -1,184 +0,0 @@
<?php
/**
* Created by PhpStorm.
* User: exodus4d
* Date: 28.03.15
* Time: 17:01
*/
namespace Controller;
use Model;
/**
* CCP API controller
* Class CcpApiController
* @package Controller
*/
class CcpApiController extends Controller{
/**
* get HTTP request options for API (curl) request
* @return array
*/
protected function getRequestOptions(){
$requestOptions = [
'timeout' => 8,
'method' => 'POST',
'user_agent' => $this->getUserAgent(),
'follow_location' => false // otherwise CURLOPT_FOLLOWLOCATION will fail
];
return $requestOptions;
}
/**
* request character information from CCP API
* @param $keyID
* @param $vCode
* @return bool|\SimpleXMLElement
*/
public function requestCharacters($keyID, $vCode){
$apiPath = $this->getF3()->get('PATHFINDER.API.CCP_XML') . '/account/APIKeyInfo.xml.aspx';
$xml = false;
// build request URL
$options = $this->getRequestOptions();
$options['content'] = http_build_query( [
'keyID' => $keyID,
'vCode' => $vCode
]);
$apiResponse = \Web::instance()->request($apiPath, $options );
if($apiResponse['body']){
$xml = simplexml_load_string($apiResponse['body']);
}
return $xml;
}
/**
* update all character information for a given apiModel
* @param $userApiModel
* @return int
* @throws \Exception
*/
public function updateCharacters($userApiModel){
$xml = $this->requestCharacters($userApiModel->keyId, $userApiModel->vCode);
$characterCount = 0;
// important -> user API model must be up2date
// if not -> matched userCharacter cant be found
$userApiModel->getById($userApiModel->id, 0);
if($xml){
// request successful
$rowApiData = $xml->result->key->rowset;
if(
is_object($rowApiData) &&
$rowApiData->children()
){
$characterModel = Model\BasicModel::getNew('CharacterModel');
$corporationModel = Model\BasicModel::getNew('CorporationModel');
$allianceModel = Model\BasicModel::getNew('AllianceModel');
foreach($rowApiData->children() as $characterApiData){
// map attributes to array
$attributeData = current( $characterApiData->attributes() );
$newCharacter = true;
$characterId = (int)$attributeData['characterID'];
$characterModel->getById($characterId, 0);
$corporationModelTemp = null;
$allianceModelTemp = null;
// check if corporation already exists
if($attributeData['corporationID'] > 0){
$corporationModel->getById($attributeData['corporationID'], 0);
if( $corporationModel->dry() ){
$corporationModel->id = $attributeData['corporationID'];
$corporationModel->name = $attributeData['corporationName'];
$corporationModel->save();
}
$corporationModelTemp = $corporationModel;
}
// check if alliance already exists
if($attributeData['allianceID'] > 0){
$allianceModel->getById($attributeData['allianceID'], 0);
if( $allianceModel->dry() ){
$allianceModel->id = $attributeData['allianceID'];
$allianceModel->name = $attributeData['allianceName'];
$allianceModel->save();
}
$allianceModelTemp = $allianceModel;
}
if($userApiModel->userCharacters){
$userApiModel->userCharacters->rewind();
while($userApiModel->userCharacters->valid()){
$tempCharacterModel = $userApiModel->userCharacters->current()->getCharacter();
// character already exists -> update
if($tempCharacterModel->id == $characterId){
$characterModel = $tempCharacterModel;
// unset userCharacter -> all leftover models are no longer part of this API
// --> delete leftover models at the end
$userApiModel->userCharacters->offsetUnset($userApiModel->userCharacters->key());
$newCharacter = false;
break;
}else{
$userApiModel->userCharacters->next();
}
}
$userApiModel->userCharacters->rewind();
}
$characterModel->id = $characterId;
$characterModel->name = $attributeData['characterName'];
$characterModel->corporationId = $corporationModelTemp;
$characterModel->allianceId = $allianceModelTemp;
$characterModel->factionId = $attributeData['factionID'];
$characterModel->factionName = $attributeData['factionName'];
$characterModel->save();
if($newCharacter){
// new character for this API
$userCharactersModel = Model\BasicModel::getNew('UserCharacterModel', 0);
$userCharactersModel->userId = $userApiModel->userId;
$userCharactersModel->apiId = $userApiModel;
$userCharactersModel->characterId = $characterModel;
$userCharactersModel->save();
}
$corporationModel->reset();
$allianceModel->reset();
$characterModel->reset();
$characterCount++;
}
}
// delete leftover userCharacters from this API
if(count($userApiModel->userCharacters) > 0){
while($userApiModel->userCharacters->valid()){
$userApiModel->userCharacters->current()->erase();
$userApiModel->userCharacters->next();
}
}
}
return $characterCount;
}
}

View File

@@ -1,500 +0,0 @@
<?php
/**
* Created by PhpStorm.
* User: Exodus
* Date: 23.01.2016
* Time: 17:18
*
* Handles access to EVE-Online "CREST API" and "SSO" auth functions
* - Add your API credentials in "environment.ini"
* - Check "PATHFINDER.API" in "pathfinder.ini" for correct API URLs
* Hint: \Web::instance()->request automatically caches responses by their response "Cache-Control" header!
*/
namespace Controller;
use Data\Mapper as Mapper;
use Model;
class CcpSsoController extends Controller {
const SESSION_KEY_ACCESS_TOKEN = 'SESSION.sso.access_token';
const SESSION_KEY_REFRESH_TOKEN = 'SESSION.sso.refresh_token';
const ERROR_CCP_SSO_URL = 'Invalid "PATHFINDER.API.CCP_SSO" url. %s';
const ERROR_CCP_CREST_URL = 'Invalid "PATHFINDER.API.CCP_CREST" url. %s';
const ERROR_RESOURCE_DEPRECATED = 'Resource: %s has been marked deprecated. %s';
const ERROR_ACCESS_TOKEN = 'Unable to get a valid "access_token. %s';
const ERROR_VERIFY_CHARACTER = 'Unable to verify character data. %s';
const ERROR_GET_ENDPOINTS = 'Unable to get endpoints data. $s';
const ERROR_GET_ENDPOINT = 'Unable to get endpoint data. $s';
const ERROR_FIND_ENDPOINT = 'Unable to find endpoint: %s';
/**
* "Scopes" that are used by pathfinder
* -> Enable scopes: https://developers.eveonline.com
* @var array
*/
private $requestScopes = [
'characterLocationRead',
'characterNavigationWrite'
];
/**
* timeout for API calls
* @var int
*/
private $apiTimeout = 3;
/**
* redirect user to CCP SSO page and request authorization
* @param $f3
*/
public function requestAuthorization($f3){
// used for state check between request and callback
$state = bin2hex(mcrypt_create_iv(12, MCRYPT_DEV_URANDOM));
$f3->set('SESSION.sso.state', $state);
$urlParams = [
'response_type' => 'code',
'redirect_uri' => Controller::getEnvironmentData('URL') . $f3->build('/sso/callbackAuthorization'),
'client_id' => Controller::getEnvironmentData('SSO_CCP_CLIENT_ID'),
'scope' => implode(' ', $this->requestScopes),
'state' => $state
];
$ssoAuthUrl = self::getAuthorizationEndpoint() . '?' . http_build_query($urlParams, '', '&', PHP_QUERY_RFC3986 );
$f3->status(302);
$f3->reroute($ssoAuthUrl);
}
/**
* callback handler for CCP SSO user Auth
* -> see requestAuthorization()
* @param $f3
*/
public function callbackAuthorization($f3){
$getParams = (array)$f3->get('GET');
if($f3->exists('SESSION.sso.state')){
// check response and validate 'state'
if(
isset($getParams['code']) &&
isset($getParams['state']) &&
!empty($getParams['code']) &&
!empty($getParams['state']) &&
$f3->get('SESSION.sso.state') === $getParams['state']
){
// clear 'state' for new next request
$f3->clear('SESSION.sso.state');
$accessToken = $this->getAccessToken($getParams['code']);
if($accessToken){
$data = $this->verifyCharacterData($accessToken);
$characterData = $this->getCharacterData($accessToken);
$characterModel = $this->updateCharacter($characterData);
if( !is_null($characterModel) ){
// everything OK -> login succeeded
}
}
}
}
// on error -> route back to login form
$this->getF3()->reroute('@login');
}
/**
* get a valid "access_token" for oAuth 2.0 verification
* -> if $authCode is set -> request NEW "access_token"
* -> else check for existing (not expired) "access_token"
* -> else try to refresh auth and get fresh "access_token"
* @param bool $authCode
* @return bool|mixed
*/
private function getAccessToken($authCode = false){
$accessToken = false;
if( !empty($authCode) ){
// Authentication Code is set -> request new Access Token -------------------------------------------------
// clear "old" token (if exist and still valid)
$this->getF3()->clear(self::SESSION_KEY_ACCESS_TOKEN);
$accessToken = $this->verifyAuthorizationCode($authCode);
}elseif($this->getF3()->exists(self::SESSION_KEY_ACCESS_TOKEN)){
// Access Token exists and not expired --------------------------------------------------------------------
$accessToken = $this->getF3()->get(self::SESSION_KEY_ACCESS_TOKEN);
}elseif($this->getF3()->exists(self::SESSION_KEY_REFRESH_TOKEN)){
// Refresh Token exists -> refresh Access Token -----------------------------------------------------------
$accessToken = $this->refreshAccessToken($this->getF3()->get(self::SESSION_KEY_REFRESH_TOKEN));
}else{
// Unable to get Token -> trigger error -------------------------------------------------------------------
$this->getLogger('error')->write(sprintf(self::ERROR_ACCESS_TOKEN, $authCode));
}
return $accessToken;
}
/**
* verify authorization code, and get an "access_token" data
* @param $authCode
* @return bool|mixed
*/
private function verifyAuthorizationCode($authCode){
$requestParams = [
'grant_type' => 'authorization_code',
'code' => $authCode
];
return $this->requestAccessToken($requestParams);
}
/**
* get new "access_token" by an existing "refresh_token"
* -> if "access_token" is expired, this function gets a fresh one
* @param $refreshToken
* @return bool|mixed
*/
private function refreshAccessToken($refreshToken){
$requestParams = [
'grant_type' => 'refresh_token',
'refresh_token' => $refreshToken
];
return $this->requestAccessToken($requestParams);
}
/**
* request an "access_token" AND "refresh_token" data
* -> this can either be done by sending a valid "authorization code"
* OR by providing a valid "refresh_token"
* @param $requestParams
* @return bool|mixed
*/
private function requestAccessToken($requestParams){
$verifyAuthCodeUrl = self::getVerifyAuthorizationCodeEndpoint();
$verifyAuthCodeUrlParts = parse_url($verifyAuthCodeUrl);
$accessToken = false;
if($verifyAuthCodeUrlParts){
$contentType = 'application/x-www-form-urlencoded';
$requestOptions = [
'timeout' => $this->apiTimeout,
'method' => 'POST',
'user_agent' => $this->getUserAgent(),
'header' => [
'Authorization: Basic ' . $this->getAuthorizationHeader(),
'Content-Type: ' . $contentType,
'Host: ' . $verifyAuthCodeUrlParts['host']
]
];
// content (parameters to send with)
$requestOptions['content'] = http_build_query($requestParams);
$apiResponse = \Web::instance()->request($verifyAuthCodeUrl, $requestOptions);
if($apiResponse['body']){
$authCodeRequestData = json_decode($apiResponse['body']);
if(property_exists($authCodeRequestData, 'refresh_token')){
// this token is used to refresh/get a new access_token when expires
$this->getF3()->set(self::SESSION_KEY_REFRESH_TOKEN, $authCodeRequestData->refresh_token);
}
if(property_exists($authCodeRequestData, 'access_token')){
// this token is required for endpoints that require Auth
$accessToken = $this->getF3()->set(self::SESSION_KEY_ACCESS_TOKEN, $authCodeRequestData->access_token);
}
}else{
$this->getLogger('error')->write(sprintf(self::ERROR_ACCESS_TOKEN, print_r($requestParams, true)));
}
}else{
$this->getLogger('error')->write(sprintf(self::ERROR_CCP_SSO_URL, __METHOD__));
}
return $accessToken;
}
/**
* verify character data by "access_token"
* -> get some basic information (like character id)
* -> if more character information is required, use CREST endpoints request instead
* @param $accessToken
* @return bool|mixed
*/
private function verifyCharacterData($accessToken){
$verifyUserUrl = self::getVerifyUserEndpoint();
$verifyUrlParts = parse_url($verifyUserUrl);
$characterData = false;
if($verifyUrlParts){
$requestOptions = [
'timeout' => $this->apiTimeout,
'method' => 'GET',
'user_agent' => $this->getUserAgent(),
'header' => [
'Authorization: Bearer ' . $accessToken,
'Host: ' . $verifyUrlParts['host']
]
];
$apiResponse = \Web::instance()->request($verifyUserUrl, $requestOptions);
if($apiResponse['body']){
$characterData = json_decode($apiResponse['body']);
}else{
$this->getLogger('error')->write(sprintf(self::ERROR_VERIFY_CHARACTER, __METHOD__));
}
}else{
$this->getLogger('error')->write(sprintf(self::ERROR_CCP_SSO_URL, __METHOD__));
}
return $characterData;
}
/**
* get all available Endpoints
* @param $accessToken
* @return bool|mixed
*/
private function getEndpoints($accessToken){
$crestUrl = self::getCrestEndpoint();
$endpointsData = false;
$crestUrlParts = parse_url($crestUrl);
if($crestUrlParts){
// represents API version
$contentType = 'application/vnd.ccp.eve.Api-v3+json';
$requestOptions = [
'timeout' => $this->apiTimeout,
'method' => 'GET',
'user_agent' => $this->getUserAgent(),
'header' => [
'Authorization: Bearer ' . $accessToken,
'Accept: ' . $contentType,
'Host: ' . $crestUrlParts['host']
]
];
$apiResponse = \Web::instance()->request($crestUrl, $requestOptions);
if($apiResponse['headers']){
// check headers for error
$this->checkResponseHeaders($apiResponse['headers'], $crestUrl, $contentType);
if($apiResponse['body']){
$endpointsData = json_decode($apiResponse['body'], true);
}else{
$this->getLogger('error')->write(sprintf(self::ERROR_GET_ENDPOINTS, __METHOD__));
}
}
}else{
$this->getLogger('error')->write(sprintf(self::ERROR_CCP_CREST_URL, __METHOD__));
}
return $endpointsData;
}
private function walkEndpoint($accessToken, $endpoint, $path = []){
$targetEndpoint = null;
if( !empty($path) ){
$newNode = array_shift($path);
if(isset($endpoint[$newNode])){
$currentEndpoint = $endpoint[$newNode];
if(isset($currentEndpoint['href'])){
$newEndpoint = $this->getEndpoint($accessToken, $currentEndpoint['href']);
$targetEndpoint = $this->walkEndpoint($accessToken, $newEndpoint, $path);
}else{
// TODO leaf
$targetEndpoint = ' target:) ';
}
}else{
// endpoint not found
$this->getLogger('error')->write(sprintf(self::ERROR_FIND_ENDPOINT, $newNode));
}
}else{
$targetEndpoint = $endpoint;
}
return $targetEndpoint;
}
/**
* get a specific endpoint by its $resourceUrl
* @param $accessToken
* @param $resourceUrl
* @return mixed|null
*/
private function getEndpoint($accessToken, $resourceUrl){
$resourceUrlParts = parse_url($resourceUrl);
$endpoint = null;
if($resourceUrlParts){
$requestOptions = [
'timeout' => $this->apiTimeout,
'method' => 'GET',
'user_agent' => $this->getUserAgent(),
'header' => [
'Authorization: Bearer ' . $accessToken,
'Host: login.eveonline.com',
'Host: ' . $resourceUrlParts['host']
]
];
$apiResponse = \Web::instance()->request($resourceUrl, $requestOptions);
if($apiResponse['headers']){
// check headers for error
$this->checkResponseHeaders($apiResponse['headers'], $requestOptions);
if($apiResponse['body']){
$endpoint = json_decode($apiResponse['body'], true);
}else{
$this->getLogger('error')->write(sprintf(self::ERROR_GET_ENDPOINT, __METHOD__));
}
}
}else{
$this->getLogger('error')->write(sprintf(self::ERROR_CCP_CREST_URL, __METHOD__));
}
return $endpoint;
}
/**
* get character data
* @param $accessToken
* @return array
*/
private function getCharacterData($accessToken){
$endpoints = $this->getEndpoints($accessToken);
$characterData = [];
$endpoint = $this->walkEndpoint($accessToken, $endpoints, [
'decode',
'character'
]);
if( !empty($endpoint) ){
$characterData['character'] = (new Mapper\CrestCharacter($endpoint))->getData();
if(isset($endpoint['corporation'])){
$characterData['corporation'] = (new Mapper\CrestCorporation($endpoint['corporation']))->getData();
}
}
return $characterData;
}
/*
private function getCharacterLocation($accessToken){
$endpoints = $this->getEndpoints($accessToken);
$endpoint = $this->walkEndpoint($accessToken, $endpoints, [
'decode',
'character',
'location'
]);
var_dump($endpoint);
die(' END getCharacterLocation() ');
$characterData = [];
return $characterData;
} */
/**
* update character
* @param $characterData
* @return \Model\CharacterModel
* @throws \Exception
*/
private function updateCharacter($characterData){
$characterModel = null;
$corporationModel = null;
$allianceModel = null;
if( !empty($characterData['corporation']) ){
$corporationModel = Model\BasicModel::getNew('CorporationModel');
$corporationModel->getById($characterData['corporation']['id'], 0);
$corporationModel->copyfrom($characterData['corporation']);
$corporationModel->save();
}
if( !empty($characterData['alliance']) ){
$allianceModel = Model\BasicModel::getNew('AllianceModel');
$allianceModel->getById($characterData['alliance']['id'], 0);
$allianceModel->copyfrom($characterData['alliance']);
$allianceModel->save();
}
if( !empty($characterData['character']) ){
$characterModel = Model\BasicModel::getNew('CharacterModel');
$characterModel->getById($characterData['character']['id'], 0);
$characterModel->copyfrom($characterData['character']);
$characterModel->corporationId = $corporationModel;
$characterModel->allianceId = $allianceModel;
$characterModel->save();
}
return $characterModel;
}
/**
* check response "Header" data for errors
* @param $headers
* @param string $requestUrl
* @param string $contentType
*/
private function checkResponseHeaders($headers, $requestUrl = '', $contentType = ''){
$headers = (array)$headers;
if(preg_grep ('/^X-Deprecated/i', $headers)){
$this->getLogger('error')->write(sprintf(self::ERROR_RESOURCE_DEPRECATED, $requestUrl, $contentType));
}
}
/**
* get "Authorization:" Header data
* -> This header is required for any Auth-required endpoints!
* @return string
*/
private function getAuthorizationHeader(){
return base64_encode(
Controller::getEnvironmentData('SSO_CCP_CLIENT_ID') . ':'
. Controller::getEnvironmentData('SSO_CCP_SECRET_KEY')
);
}
static function getAuthorizationEndpoint(){
return \Base::instance()->get('PATHFINDER.API.CCP_SSO') . '/oauth/authorize';
}
static function getVerifyAuthorizationCodeEndpoint(){
return \Base::instance()->get('PATHFINDER.API.CCP_SSO') . '/oauth/token';
}
static function getVerifyUserEndpoint(){
return \Base::instance()->get('PATHFINDER.API.CCP_SSO') . '/oauth/verify';
}
static function getCrestEndpoint(){
return \Base::instance()->get('PATHFINDER.API.CCP_CREST');
}
}

View File

@@ -7,50 +7,62 @@
*/
namespace Controller;
use Controller\Api as Api;
use Model;
use DB;
class Controller {
/**
* @var \Base
*/
protected $f3;
private $template;
/**
* @param mixed $template
* @var string template for render
*/
public function setTemplate($template){
protected $template;
/**
* @param string $template
*/
protected function setTemplate($template){
$this->template = $template;
}
/**
* @return mixed
* @return string
*/
public function getTemplate(){
protected function getTemplate(){
return $this->template;
}
/**
* set global f3 instance
* @param null $f3
* @return null|static
* set $f3 base object
* @param \Base $f3
*/
protected function getF3($f3 = null){
if(is_object($f3)){
$this->f3 = $f3;
}else{
$this->f3 = \Base::instance();
}
protected function setF3(\Base $f3){
$this->f3 = $f3;
}
/**
* get $f3 base object
* @return \Base
*/
protected function getF3(){
if( !($this->f3 instanceof \Base) ){
$this->setF3( \Base::instance() );
}
return $this->f3;
}
/**
* event handler for all "views"
* some global template variables are set in here
* @param $f3
* @param \Base $f3
*/
function beforeroute($f3) {
$this->getF3($f3);
function beforeroute(\Base $f3) {
$this->setF3($f3);
// initiate DB connection
DB\Database::instance('PF');
@@ -73,8 +85,9 @@ class Controller {
/**
* event handler after routing
* -> render view
* @param \Base $f3
*/
public function afterroute($f3){
public function afterroute(\Base $f3){
if($this->getTemplate()){
// Ajax calls don´t need a page render..
// this happens on client side
@@ -85,7 +98,7 @@ class Controller {
/**
* set change the DB connection
* @param string $database
* @return mixed|void
* @return DB\SQL
*/
protected function getDB($database = 'PF'){
return DB\Database::instance()->getDB($database);
@@ -96,49 +109,47 @@ class Controller {
*/
protected function initSession(){
// init DB Session (not file based)
if( $this->getDB('PF') instanceof \DB\SQL){
new \DB\SQL\Session($this->getDB('PF'));
if( $this->getDB('PF') instanceof DB\SQL){
new DB\SQL\Session($this->getDB('PF'));
}
}
/**
* get current user model
* get current character model
* @param int $ttl
* @return bool|null
* @return Model\CharacterModel|null
* @throws \Exception
*/
protected function _getUser($ttl = 5){
$user = false;
public function getCharacter($ttl = 0){
$character = null;
if( $this->f3->exists('SESSION.user.id') ){
$userId = (int)$this->f3->get('SESSION.user.id');
if( $this->getF3()->exists(Api\User::SESSION_KEY_CHARACTER_ID) ){
$characterId = (int)$this->getF3()->get(Api\User::SESSION_KEY_CHARACTER_ID);
if($characterId){
/**
* @var $characterModel \Model\CharacterModel
*/
$characterModel = Model\BasicModel::getNew('CharacterModel');
$characterModel->getById($characterId, $ttl);
if($userId > 0){
$userModel = Model\BasicModel::getNew('UserModel', $ttl);
$userModel->getById($userId, $ttl);
if( !$userModel->dry() ){
$user = $userModel;
if( !$characterModel->dry() ){
$character = &$characterModel;
}
}
}
return $user;
return $character;
}
/**
* log the current user out
* @param $f3
* log out current user
* @param \Base $f3
*/
public function logOut($f3){
public function logOut(\Base $f3){
// destroy session
$f3->clear('SESSION');
if( !$f3->get('AJAX') ){
// redirect to landing page
$f3->reroute('@login');
}else{
if( $f3->get('AJAX') ){
$params = $f3->get('POST');
$return = (object) [];
if(
@@ -148,55 +159,29 @@ class Controller {
$return->reroute = rtrim(self::getEnvironmentData('URL'), '/') . $f3->alias('login');
}else{
// no reroute -> errors can be shown
$return->error[] = $this->getUserLoggedOffError();
$return->error[] = $this->getLogoutError();
}
echo json_encode($return);
die();
}else{
// redirect to landing page
$f3->reroute('@login');
}
}
/**
* verifies weather a given username and password is valid
* @param $userName
* @param $password
* @return Model\UserModel|null
*/
protected function _verifyUser($userName, $password) {
$validUser = null;
$user = Model\BasicModel::getNew('UserModel', 0);
$user->getByName($userName);
// check userName is valid
if( !$user->dry() ){
// check if password is valid
$isValid = $user->verify($password);
if($isValid === true){
$validUser = $user;
}
}
return $validUser;
}
/**
* check weather the page is IGB trusted or not
* @return mixed
* @return boolean
*/
static function isIGBTrusted(){
$igbHeaderData = self::getIGBHeaderData();
return $igbHeaderData->trusted;
}
/**
* get all eve IGB specific header data
* @return object
* @return \stdClass
*/
static function getIGBHeaderData(){
$data = (object) [];
@@ -229,7 +214,6 @@ class Controller {
/**
* Helper function to return all headers because
* getallheaders() is not available under nginx
*
* @return array (string $key -> string $value)
*/
static function getRequestHeaders(){
@@ -261,7 +245,7 @@ class Controller {
/**
* get some server information
* @param int $ttl cache time (default: 1h)
* @return object
* @return \stdClass
*/
static function getServerData($ttl = 3600){
$f3 = \Base::instance();
@@ -308,25 +292,21 @@ class Controller {
*/
static function isIGB(){
$isIGB = false;
$igbHeaderData = self::getIGBHeaderData();
if(count($igbHeaderData->values) > 0){
$isIGB = true;
}
return $isIGB;
}
/**
* get error object is a user is not found/logged of
* @return object
* @return \stdClass
*/
protected function getUserLoggedOffError(){
protected function getLogoutError(){
$userError = (object) [];
$userError->type = 'error';
$userError->message = 'User not found';
return $userError;
}
@@ -341,8 +321,8 @@ class Controller {
/**
* get a log controller e.g. "debug"
* @param $loggerType
* @return mixed
* @param string $loggerType
* @return \Log
*/
static function getLogger($loggerType){
return LogController::getLogger($loggerType);
@@ -351,7 +331,7 @@ class Controller {
/**
* removes illegal characters from a Hive-key that are not allowed
* @param $key
* @return mixed
* @return string
*/
static function formatHiveKey($key){
$illegalCharacters = ['-', ' '];
@@ -360,8 +340,8 @@ class Controller {
/**
* get environment specific configuration data
* @param $key
* @return mixed|null
* @param string $key
* @return string|null
*/
static function getEnvironmentData($key){
$f3 = \Base::instance();
@@ -378,7 +358,7 @@ class Controller {
/**
* get current server environment status
* -> "DEVELOP" or "PRODUCTION"
* @return mixed
* @return string
*/
static function getEnvironment(){
$f3 = \Base::instance();
@@ -396,7 +376,7 @@ class Controller {
/**
* get required MySQL variable value
* @param $key
* @return mixed|null
* @return string|null
*/
static function getRequiredMySqlVariables($key){
$f3 = \Base::instance();
@@ -413,7 +393,7 @@ class Controller {
* get a program URL by alias
* -> if no $alias given -> get "default" route (index.php)
* @param null $alias
* @return bool
* @return bool|string
*/
protected function getRouteUrl($alias = null){
$url = false;
@@ -452,9 +432,9 @@ class Controller {
* onError() callback function
* -> on AJAX request -> return JSON with error information
* -> on HTTP request -> render error page
* @param $f3
* @param \Base $f3
*/
public function showError($f3){
public function showError(\Base $f3){
// set HTTP status
$errorCode = $f3->get('ERROR.code');
if(!empty($errorCode)){
@@ -510,8 +490,10 @@ class Controller {
/**
* Callback for framework "unload"
* check -> config.ini
* @param \Base $f3
* @return bool
*/
public function unload($f3){
public function unload(\Base $f3){
return true;
}

View File

@@ -8,8 +8,11 @@
namespace Controller;
class MapController extends \Controller\AccessController {
class MapController extends AccessController {
/**
* @param \Base $f3
*/
public function init($f3) {
// page title

View File

@@ -30,16 +30,14 @@ class Setup extends Controller {
'Model\SystemStatusModel',
'Model\SystemNeighbourModel',
'Model\WormholeModel',
'Model\RegistrationKeyModel',
'Model\CharacterStatusModel',
'Model\ConnectionScopeModel',
'Model\UserMapModel',
'Model\CharacterMapModel',
'Model\AllianceMapModel',
'Model\CorporationMapModel',
'Model\UserApiModel',
'Model\UserCharacterModel',
'Model\CharacterModel',
'Model\CharacterLogModel',
@@ -76,9 +74,9 @@ class Setup extends Controller {
/**
* event handler for all "views"
* some global template variables are set in here
* @param $f3
* @param \Base $f3
*/
function beforeroute($f3) {
function beforeroute(\Base $f3) {
// page title
$f3->set('pageTitle', 'Setup');
@@ -92,7 +90,7 @@ class Setup extends Controller {
$f3->set('pathJs', 'public/js/' . $f3->get('PATHFINDER.VERSION') );
}
public function afterroute($f3) {
public function afterroute(\Base $f3) {
// js view (file)
$f3->set('jsView', 'setup');
@@ -103,7 +101,7 @@ class Setup extends Controller {
/**
* main setup route handler
* works as dispatcher for setup functions
* @param $f3
* @param \Base $f3
*/
public function init($f3){
$params = $f3->get('GET');
@@ -141,7 +139,7 @@ class Setup extends Controller {
/**
* get server information
* @param $f3
* @param \Base $f3
* @return array
*/
protected function getServerInformation($f3){
@@ -178,7 +176,7 @@ class Setup extends Controller {
/**
* check all required backend requirements
* (Fat Free Framework)
* @param $f3
* @param \Base $f3
* @return array
*/
protected function checkRequirements($f3){
@@ -288,7 +286,7 @@ class Setup extends Controller {
/**
* get database connection information
* @param $f3
* @param \Base $f3
* @param bool|false $exec
* @return array
*/
@@ -413,6 +411,7 @@ class Setup extends Controller {
$changedType = false;
$changedUnique = false;
$changedIndex = false;
$addConstraints = [];
// set (new) column information -------------------------------------------------------
$requiredTables[$requiredTableName]['fieldConf'][$columnName]['exists'] = true;
@@ -427,17 +426,22 @@ class Setup extends Controller {
$constraint = $col->newConstraint($constraintData);
$foreignKeyExists = $col->constraintExists($constraint);
// constraint information -> show in template
$requiredTables[$requiredTableName]['foreignKeys'][] = [
'exists' => $foreignKeyExists,
'keyName' => $constraint->getConstraintName()
];
$col->addConstraint($constraint);
if(!$foreignKeyExists){
if($foreignKeyExists){
// drop constraint and re-add again at the and, in case something has changed
$col->dropConstraint($constraint);
}else{
$tableStatusCheckCount++;
$foreignKeyStatusCheck = false;
}
$addConstraints[] = $constraint;
}
}
@@ -452,11 +456,21 @@ class Setup extends Controller {
$tableStatusCheckCount++;
}
// check if column unique changed -----------------------------------------------------
// check if column index changed ------------------------------------------------------
$indexUpdate = false;
$indexKey = (bool)$hasIndex;
$indexUnique = (bool)$hasUnique;
if($currentColIndex != $fieldConf['index']){
$changedIndex = true;
$columnStatusCheck = false;
$tableStatusCheckCount++;
$indexUpdate = true;
$indexKey = (bool) $fieldConf['index'];
}
// check if column unique changed -----------------------------------------------------
if($currentColIndexData['unique'] != $fieldConf['unique']){
$changedUnique = true;
$columnStatusCheck = false;
@@ -466,15 +480,6 @@ class Setup extends Controller {
$indexUnique =(bool)$fieldConf['unique'];
}
// check if column index changed ------------------------------------------------------
if($currentColIndex != $fieldConf['index']){
$changedIndex = true;
$columnStatusCheck = false;
$tableStatusCheckCount++;
$indexUpdate = true;
$indexKey = (bool) $fieldConf['index'];
}
// build table with changed columns ---------------------------------------------------
if(!$columnStatusCheck || !$foreignKeyStatusCheck){
@@ -495,6 +500,12 @@ class Setup extends Controller {
$tableModifier->updateColumn($columnName, $col);
}
// (re-)add constraints !after! index update is done
// otherwise index update will fail if there are existing constraints
foreach($addConstraints as $constraint){
$col->addConstraint($constraint);
}
$buildStatus = $tableModifier->build($exec);
if(
@@ -559,7 +570,7 @@ class Setup extends Controller {
}
/** check MySQL params
* @param $f3
* @param \Base $f3
* @param $db
* @return array
*/

View File

@@ -47,43 +47,48 @@ class AbstractIterator extends \RecursiveArrayIterator {
*/
static function recursiveIterator($iterator){
$keyWhitelist = array_keys(static::$map);
while($iterator->valid()){
if(array_key_exists($iterator->key(), static::$map)){
if( isset(static::$map[$iterator->key()]) ){
$mapValue = static::$map[$iterator->key()];
// check for mapping key
if($iterator->hasChildren()){
// recursive call for child elements
$iterator->offsetSet($iterator->key(), forward_static_call(array('self', __METHOD__), $iterator->getChildren())->getArrayCopy());
$iterator->next();
}elseif(is_array(static::$map[$iterator->key()])){
}elseif(is_array($mapValue)){
// a -> array mapping
$parentKey = array_keys(static::$map[$iterator->key()])[0];
$entryKey = array_values(static::$map[$iterator->key()])[0];
$parentKey = array_keys($mapValue)[0];
$entryKey = array_values($mapValue)[0];
// check if key already exists
if($iterator->offsetExists($parentKey)){
$currentValue = $iterator->offsetGet($parentKey);
// add new array entry
$currentValue[$entryKey] = $iterator->current();
$iterator->offsetSet($parentKey, $currentValue);
}else{
$iterator->offsetSet($parentKey, [$entryKey => $iterator->current()]);
$keyWhitelist[] = $parentKey;
}
}elseif(is_object(static::$map[$iterator->key()])){
$iterator->offsetUnset($iterator->key());
}elseif(is_object($mapValue)){
// a -> a (format by function)
$formatFunction = static::$map[$iterator->key()];
$formatFunction = $mapValue;
$iterator->offsetSet($iterator->key(), call_user_func($formatFunction, $iterator));
// just value change no key change
$iterator->next();
}elseif(static::$map[$iterator->key()] !== $iterator->key()){
}elseif($mapValue !== $iterator->key()){
// a -> b mapping (key changed)
$iterator->offsetSet(static::$map[$iterator->key()], $iterator->current());
$iterator->offsetSet($mapValue, $iterator->current());
$iterator->offsetUnset($iterator->key());
$keyWhitelist[] = $mapValue;
}else{
// a -> a (no changes)
$iterator->next();
@@ -91,13 +96,13 @@ class AbstractIterator extends \RecursiveArrayIterator {
}elseif(
static::$removeUnmapped &&
!in_array($iterator->key(), static::$map)
!in_array($iterator->key(), $keyWhitelist)
){
$iterator->offsetUnset($iterator->key());
}else{
$iterator->next();
}
}
return $iterator;

View File

@@ -166,7 +166,7 @@ class CcpSystemsMapper extends AbstractIterator {
// just value change no key change
$removeOldEntry = false;
$iterator -> next();
$iterator->next();
}else{
// a -> b mapping
$iterator->offsetSet( self::$map[$iterator->key()], $iterator->current() );

View File

@@ -0,0 +1,18 @@
<?php
/**
* Created by PhpStorm.
* User: Exodus
* Date: 07.02.2016
* Time: 14:34
*/
namespace Data\Mapper;
class CrestSystem extends AbstractIterator {
protected static $map = [
'id' => 'id',
'name' => 'name'
];
}

View File

@@ -0,0 +1,47 @@
<?php
/**
* Created by PhpStorm.
* User: Exodus
* Date: 01.04.2016
* Time: 19:48
*/
namespace Data\Mapper;
class IgbHeader extends AbstractIterator {
protected static $map = [
'charid' => ['character' => 'id'],
'charname' => ['character' => 'name'],
// --------------------------------------------------------------------
'solarsystemid' => ['system' => 'id'],
'solarsystemname' => ['system' => 'name'],
'constellationid' => ['constellation' => 'id'],
'constellationname' => ['constellation' => 'name'],
'regionid' => ['region' => 'id'],
'regionname' => ['region' => 'name'],
// --------------------------------------------------------------------
'shiptypeid' => ['ship' => 'typeId'],
'shiptypename' => ['ship' => 'typeName'],
'shipid' => ['ship' => 'id'],
'shipname' => ['ship' => 'name'],
'stationid' => ['station' => 'id'],
'stationname' => ['station' => 'name'],
// --------------------------------------------------------------------
'corpid' => ['corporation' => 'id'],
'corpname' => ['corporation' => 'name'],
'allianceid' => ['alliance' => 'id'],
'alliancename' => ['alliance' => 'name']
];
}

View File

@@ -80,7 +80,7 @@ class TableModifier extends SQL\TableModifier {
public function dropConstraint($constraint){
if($constraint->isValid()){
$this->queries[] = "ALTER TABLE " . $this->db->quotekey($this->name) . "
DROP FOREIGN KEY " . $this->db->quotekey($constraint->getConstraintName());
DROP FOREIGN KEY " . $this->db->quotekey($constraint->getConstraintName()) . ";";
}else{
trigger_error(sprintf(self::TEXT_ConstraintNotValid, 'table: ' . $this->name . ' constraintName: ' . $constraint->getConstraintName()));
}
@@ -93,18 +93,13 @@ class TableModifier extends SQL\TableModifier {
public function addConstraint($constraint){
if($constraint->isValid()){
if($this->constraintExists($constraint)){
// drop constraint and re-add in case something has changed
$this->dropConstraint($constraint);
}
$this->queries[] = "
ALTER TABLE " . $this->db->quotekey($this->name) . "
ADD CONSTRAINT " . $this->db->quotekey($constraint->getConstraintName()) . "
FOREIGN KEY (" . implode(', ', $constraint->getKeys()) . ")
REFERENCES " . $this->db->quotekey($constraint->getReferencedTable()) . " (" . implode(', ', $constraint->getReferencedCols()) . ")
ON DELETE " . $constraint->getOnDelete() . "
ON UPDATE " . $constraint->getOnUpdate();
ON UPDATE " . $constraint->getOnUpdate() . ";";
}else{
trigger_error(sprintf(self::TEXT_ConstraintNotValid, 'table: ' . $this->name . ' constraintName: ' . $constraint->getConstraintName()));
}
@@ -120,7 +115,15 @@ class Column extends SQL\Column {
const TEXT_TableNameMissing = 'Table name missing for FOREIGN KEY in `%s`';
/**
* ass constraint to this column
* drop constraint from this column
* @param Constraint $constraint
*/
public function dropConstraint(Constraint $constraint){
$this->table->dropConstraint($constraint);
}
/**
* add constraint to this column
* @param Constraint $constraint
*/
public function addConstraint(Constraint $constraint){

167
app/main/lib/web.php Normal file
View File

@@ -0,0 +1,167 @@
<?php
/**
* Created by PhpStorm.
* User: Exodus
* Date: 12.03.2016
* Time: 12:28
*/
namespace Lib;
use controller\LogController;
class Web extends \Web {
const ERROR_DEFAULT_MSG = 'method: \'%s\', url: \'%s\'';
const ERROR_STATUS_UNKNOWN = 'HTTP response - unknown HTTP status: \'%s\'. url: \'%s\'';
const ERROR_TIMEOUT = 'Request timeout \'%ss\'. url: \'%s\'';
/**
* end of line
* @var string
*/
private $eol = "\r\n";
/**
* get status code from Header data array
* @param array $headers
* @return int
*/
protected function getStatuscodeFromHeaders($headers = []){
$statusCode = 0;
if(
preg_match(
'/HTTP\/1\.\d (\d{3}?)/',
implode($this->eol, $headers),
$matches
)
){
$statusCode = (int)$matches[1];
}
return $statusCode;
}
/**
* get cache time in seconds from Header data array
* @param array $headers
* @return int
*/
protected function getCacheTimeFromHeaders($headers = []){
$cacheTime = 0;
if(
preg_match(
'/Cache-Control:(.*?)max-age=([0-9]+)/',
implode($this->eol, $headers),
$matches
)
){
$cacheTime = (int)$matches[2];
}elseif(
preg_match(
'/Access-Control-Max-Age: ([0-9]+)/',
implode($this->eol, $headers),
$matches
)
){
$cacheTime = (int)$matches[1];
}
return $cacheTime;
}
/**
* get a unique cache kay for a request
* @param $url
* @param null $options
* @return string
*/
protected function getCacheKey($url, $options = null){
$f3 = \Base::instance();
$headers = isset($options['header']) ? implode($this->eol, (array) $options['header']) : '';
return $f3->hash(
$options['method'] . ' '
. $url . ' '
. $headers
) . 'url';
}
/**
* perform curl() request
* -> caches response by returned HTTP Cache header data
* @param string $url
* @param array|null $options
* @return array|FALSE|mixed
*/
public function request($url,array $options = null) {
$f3 = \Base::instance();
if( !$f3->exists( $hash = $this->getCacheKey($url, $options) ) ){
$result = parent::request($url, $options);
$statusCode = $this->getStatuscodeFromHeaders( $result['headers'] );
switch($statusCode){
case 200:
// request succeeded -> check if response should be cached
$ttl = $this->getCacheTimeFromHeaders( $result['headers'] );
if(
$ttl > 0 &&
!empty( json_decode( $result['body'], true ) )
){
$f3->set($hash, $result, $ttl);
}
break;
case 500:
case 501:
case 502:
case 503:
case 504:
case 505:
$f3->error($statusCode, $this->getErrorMessageFromJsonResponse(
$options['method'],
$url,
json_decode($result['body'])
));
break;
case 0:
// timeout
LogController::getLogger('error')->write(
sprintf(self::ERROR_TIMEOUT, $options['timeout'], $url)
);
break;
default:
// unknown status
LogController::getLogger('error')->write(
sprintf(self::ERROR_STATUS_UNKNOWN, $statusCode, $url)
);
break;
}
}else{
$result = $f3->get($hash);
}
return $result;
}
/**
* get error message from response object
* @param string $method
* @param string $url
* @param \stdClass $responseBody
* @return string
*/
protected function getErrorMessageFromJsonResponse($method, $url, $responseBody){
if( empty($responseBody->message) ){
$message = sprintf(self::ERROR_DEFAULT_MSG, $method, $url);
}else{
$message = $responseBody->message . ', url: \'' . $url . '\'';
}
return $message;
}
}

View File

@@ -13,7 +13,7 @@ use Exception;
use Controller;
use DB;
class BasicModel extends \DB\Cortex {
abstract class BasicModel extends \DB\Cortex {
/**
* Hive key with DB object
@@ -81,15 +81,17 @@ class BasicModel extends \DB\Cortex {
$self->clearCacheData();
});
// model updated
$this->afterupdate( function($self){
$self->clearCacheData();
});
// model updated
$this->beforeinsert( function($self){
$self->beforeInsertEvent($self);
});
$this->aftererase( function($self){
$self->aftereraseEvent($self);
});
}
@@ -106,18 +108,27 @@ class BasicModel extends \DB\Cortex {
return;
}
if($key != 'updated'){
if(
!$this->dry() &&
$key != 'updated'
){
if( $this->exists($key) ){
$currentVal = $this->get($key);
// if current value is not a relational object
// and value has changed -> update table col
if(
!is_object($currentVal) &&
$currentVal != $val
){
if(is_object($currentVal)){
if(
is_numeric($val) &&
is_subclass_of($currentVal, 'Model\BasicModel') &&
$currentVal->_id !== (int)$val
){
$this->touch('updated');
}
}elseif($currentVal != $val){
$this->touch('updated');
}
}
}
@@ -226,7 +237,7 @@ class BasicModel extends \DB\Cortex {
$cacheKey = null;
// set a model unique cache key if the model is saved
if( $this->_id > 0){
if( $this->id > 0){
// check if there is a given key prefix
// -> if not, use the standard key.
// this is useful for caching multiple data sets according to one row entry
@@ -245,6 +256,67 @@ class BasicModel extends \DB\Cortex {
return $cacheKey;
}
/**
* get cached data from this model
* @param string $dataCacheKeyPrefix - optional key prefix
* @return \stdClass|null
*/
protected function getCacheData($dataCacheKeyPrefix = ''){
$cacheKey = $this->getCacheKey($dataCacheKeyPrefix);
$cacheData = null;
if( !is_null($cacheKey) ){
$f3 = self::getF3();
if( $f3->exists($cacheKey) ){
$cacheData = $f3->get( $cacheKey );
}
}
return $cacheData;
}
/**
* update/set the getData() cache for this object
* @param $cacheData
* @param string $dataCacheKeyPrefix
* @param int $data_ttl
*/
public function updateCacheData($cacheData, $dataCacheKeyPrefix = '', $data_ttl = 300){
$cacheDataTmp = (array)$cacheData;
// check if data should be cached
// and cacheData is not empty
if(
$data_ttl > 0 &&
!empty( $cacheDataTmp )
){
$cacheKey = $this->getCacheKey($dataCacheKeyPrefix);
if( !is_null($cacheKey) ){
self::getF3()->set($cacheKey, $cacheData, $data_ttl);
}
}
}
/**
* unset the getData() cache for this object
*/
public function clearCacheData(){
$cacheKey = $this->getCacheKey();
if( !is_null($cacheKey) ){
$f3 = self::getF3();
if( $f3->exists($cacheKey) ){
$f3->clear($cacheKey);
}
}
}
/**
* Throws a validation error for a giben column
* @param $col
@@ -338,11 +410,20 @@ class BasicModel extends \DB\Cortex {
}
/**
* function should be overwritten in child classes with access restriction
* @param $accessObject
* Event "Hook" function
* can be overwritten
* @return bool
*/
public function hasAccess($accessObject){
public function aftereraseEvent($self){
return true;
}
/**
* function should be overwritten in child classes with access restriction
* @param CharacterModel $characterModel
* @return bool
*/
public function hasAccess(CharacterModel $characterModel){
return true;
}
@@ -354,113 +435,6 @@ class BasicModel extends \DB\Cortex {
return true;
}
/**
* get cached data from this model
* @param string $dataCacheKeyPrefix - optional key prefix
* @return mixed|null
*/
protected function getCacheData($dataCacheKeyPrefix = ''){
$cacheKey = $this->getCacheKey($dataCacheKeyPrefix);
$cacheData = null;
if( !is_null($cacheKey) ){
$f3 = self::getF3();
if( $f3->exists($cacheKey) ){
$cacheData = $f3->get( $cacheKey );
}
}
return $cacheData;
}
/**
* update/set the getData() cache for this object
* @param $cacheData
* @param string $dataCacheKeyPrefix
* @param int $data_ttl
*/
public function updateCacheData($cacheData, $dataCacheKeyPrefix = '', $data_ttl = 300){
$cacheDataTmp = (array)$cacheData;
// check if data should be cached
// and cacheData is not empty
if(
$data_ttl > 0 &&
!empty( $cacheDataTmp )
){
$cacheKey = $this->getCacheKey($dataCacheKeyPrefix);
if( !is_null($cacheKey) ){
self::getF3()->set($cacheKey, $cacheData, $data_ttl);
}
}
}
/**
* unset the getData() cache for this object
*/
public function clearCacheData(){
$cacheKey = $this->getCacheKey();
if( !is_null($cacheKey) ){
$f3 = self::getF3();
if( $f3->exists($cacheKey) ){
$f3->clear($cacheKey);
}
}
}
/**
* get the current class name
* -> namespace not included
* @return string
*/
public static function getClassName(){
$parts = explode('\\', static::class);
return end($parts);
}
/**
* factory for all Models
* @param $model
* @param int $ttl
* @return null
* @throws \Exception
*/
public static function getNew($model, $ttl = 86400){
$class = null;
$model = '\\' . __NAMESPACE__ . '\\' . $model;
if(class_exists($model)){
$class = new $model( null, null, null, $ttl );
}else{
throw new \Exception('No model class found');
}
return $class;
}
/**
* get the framework instance (singleton)
* @return static
*/
public static function getF3(){
return \Base::instance();
}
/**
* debug log function
* @param $text
*/
public static function log($text){
Controller\LogController::getLogger('debug')->write($text);
}
/**
* export and download table data as *.csv
* this is primarily used for static tables
@@ -573,6 +547,52 @@ class BasicModel extends \DB\Cortex {
return ['added' => $addedCount, 'updated' => $updatedCount, 'deleted' => $deletedCount];
}
/**
* get the current class name
* -> namespace not included
* @return string
*/
public static function getClassName(){
$parts = explode('\\', static::class);
return end($parts);
}
/**
* factory for all Models
* @param string $model
* @param int $ttl
* @return BasicModel
* @throws \Exception
*/
public static function getNew($model, $ttl = 86400){
$class = null;
$model = '\\' . __NAMESPACE__ . '\\' . $model;
if(class_exists($model)){
$class = new $model( null, null, null, $ttl );
}else{
throw new \Exception('No model class found');
}
return $class;
}
/**
* get the framework instance (singleton)
* @return \Base
*/
public static function getF3(){
return \Base::instance();
}
/**
* debug log function
* @param string $text
*/
public static function log($text){
Controller\LogController::getLogger('debug')->write($text);
}
/**
* get tableModifier class for this table
* @return bool|DB\SQL\TableModifier

View File

@@ -14,6 +14,14 @@ class CharacterLogModel extends BasicModel {
protected $table = 'character_log';
/**
* caching for relational data
* -> 10s matches REST API - Expire: Header-Data
* for "Location" calls
* @var int
*/
protected $rel_ttl = 10;
protected $fieldConf = [
'active' => [
'type' => Schema::DT_BOOL,
@@ -33,6 +41,9 @@ class CharacterLogModel extends BasicModel {
]
]
],
// --------------------------------------------------------------------
'systemId' => [
'type' => Schema::DT_INT,
'index' => true
@@ -42,6 +53,35 @@ class CharacterLogModel extends BasicModel {
'nullable' => false,
'default' => ''
],
'constellationId' => [
'type' => Schema::DT_INT,
'index' => true
],
'constellationName' => [
'type' => Schema::DT_VARCHAR128,
'nullable' => false,
'default' => ''
],
'regionId' => [
'type' => Schema::DT_INT,
'index' => true
],
'regionName' => [
'type' => Schema::DT_VARCHAR128,
'nullable' => false,
'default' => ''
],
// --------------------------------------------------------------------
'shipTypeId' => [
'type' => Schema::DT_INT,
'index' => true
],
'shipTypeName' => [
'type' => Schema::DT_VARCHAR128,
'nullable' => false,
'default' => ''
],
'shipId' => [
'type' => Schema::DT_INT,
'index' => true
@@ -51,21 +91,69 @@ class CharacterLogModel extends BasicModel {
'nullable' => false,
'default' => ''
],
'shipTypeName' => [
'stationId' => [
'type' => Schema::DT_INT,
'index' => true
],
'stationName' => [
'type' => Schema::DT_VARCHAR128,
'nullable' => false,
'default' => ''
]
];
public function __construct($db = NULL, $table = NULL, $fluid = NULL, $ttl = 0){
/**
* set log data from array
* @param array $logData
*/
public function setData($logData){
parent::__construct($db, $table, $fluid, $ttl);
if( isset($logData['system']) ){
$this->systemId = (int)$logData['system']['id'];
$this->systemName = $logData['system']['name'];
}else{
$this->systemId = null;
$this->systemName = '';
}
if( isset($logData['constellation']) ){
$this->constellationId = (int)$logData['constellation']['id'];
$this->constellationName = $logData['constellation']['name'];
}else{
$this->constellationId = null;
$this->constellationName = '';
}
if( isset($logData['region']) ){
$this->regionId = (int)$logData['region']['id'];
$this->regionName = $logData['region']['name'];
}else{
$this->regionId = null;
$this->regionName = '';
}
// --------------------------------------------------------------------
if( isset($logData['ship']) ){
$this->shipTypeId = (int)$logData['ship']['typeId'];
$this->shipTypeName = $logData['ship']['typeName'];
$this->shipId = (int)$logData['ship']['id'];
$this->shipName = $logData['ship']['name'];
}else{
$this->shipTypeId = null;
$this->shipTypeName = '';
$this->shipId = null;
$this->shipName = '';
}
if( isset($logData['station']) ){
$this->stationId = (int)$logData['station']['id'];
$this->stationName = $logData['station']['name'];
}else{
$this->stationId = null;
$this->stationName = '';
}
// events -----------------------------------------
$this->beforeerase(function($self){
$self->clearCacheData();
});
}
/**
@@ -76,30 +164,30 @@ class CharacterLogModel extends BasicModel {
$logData = (object) [];
$logData->system = (object) [];
$logData->system->id = $this->systemId;
$logData->system->id = (int)$this->systemId;
$logData->system->name = $this->systemName;
$logData->constellation = (object) [];
$logData->constellation->id = (int)$this->constellationId;
$logData->constellation->name = $this->constellationName;
$logData->region = (object) [];
$logData->region->id = (int)$this->regionId;
$logData->region->name = $this->regionName;
// --------------------------------------------------------------------
$logData->ship = (object) [];
$logData->ship->typeId = (int)$this->shipTypeId;
$logData->ship->typeName = $this->shipTypeName;
$logData->ship->id = $this->shipId;
$logData->ship->name = $this->shipName;
$logData->ship->typeName = $this->shipTypeName;
$logData->station = (object) [];
$logData->station->id = (int)$this->stationId;
$logData->station->name = $this->stationName;
return $logData;
}
/**
* see parent
*/
public function clearCacheData(){
parent::clearCacheData();
// delete log cache key as well
$f3 = self::getF3();
$character = $this->characterId;
$character->clearCacheData();
$f3->clear('LOGGED.user.character.id_' . $character->id);
return true;
}
}

View File

@@ -1,18 +1,18 @@
<?php
/**
* Created by PhpStorm.
* User: exodus4d
* Date: 28.02.15
* Time: 11:57
* User: Exodus
* Date: 07.02.2016
* Time: 12:31
*/
namespace Model;
use DB\SQL\Schema;
class UserMapModel extends BasicModel {
class CharacterMapModel extends BasicModel {
protected $table = 'user_map';
protected $table = 'character_map';
protected $fieldConf = [
'active' => [
@@ -21,13 +21,13 @@ class UserMapModel extends BasicModel {
'default' => 1,
'index' => true
],
'userId' => [
'characterId' => [
'type' => Schema::DT_INT,
'index' => true,
'belongs-to-one' => 'Model\UserModel',
'belongs-to-one' => 'Model\CharacterModel',
'constraint' => [
[
'table' => 'user',
'table' => 'character',
'on-delete' => 'CASCADE'
]
]
@@ -66,10 +66,10 @@ class UserMapModel extends BasicModel {
$status = parent::setup($db,$table,$fields);
if($status === true){
$status = parent::setMultiColumnIndex(['userId', 'mapId'], true);
$status = parent::setMultiColumnIndex(['characterId', 'mapId'], true);
}
return $status;
}
}
}

View File

@@ -8,13 +8,20 @@
namespace Model;
use Controller;
use Controller\Ccp;
use DB\SQL\Schema;
use Data\Mapper as Mapper;
class CharacterModel extends BasicModel {
protected $table = 'character';
protected $fieldConf = [
'lastLogin' => [
'type' => Schema::DT_TIMESTAMP,
'index' => true
],
'active' => [
'type' => Schema::DT_BOOL,
'nullable' => false,
@@ -26,6 +33,22 @@ class CharacterModel extends BasicModel {
'nullable' => false,
'default' => ''
],
'ownerHash' => [
'type' => Schema::DT_VARCHAR128,
'nullable' => false,
'default' => ''
],
'crestAccessToken' => [
'type' => Schema::DT_VARCHAR256
],
'crestAccessTokenUpdated' => [
'type' => Schema::DT_TIMESTAMP,
'default' => Schema::DF_CURRENT_TIMESTAMP,
'index' => true
],
'crestRefreshToken' => [
'type' => Schema::DT_VARCHAR256
],
'corporationId' => [
'type' => Schema::DT_INT,
'index' => true,
@@ -57,29 +80,46 @@ class CharacterModel extends BasicModel {
'nullable' => false,
'default' => ''
],
'shared' => [
'type' => Schema::DT_BOOL,
'nullable' => false,
'default' => 0
],
'userCharacter' => [
'has-one' => ['Model\UserCharacterModel', 'characterId']
],
'characterLog' => [
'has-one' => ['Model\CharacterLogModel', 'characterId']
],
'characterMaps' => [
'has-many' => ['Model\CharacterMapModel', 'characterId']
]
];
/**
* get character data
* @param bool|false $addCharacterLogData
* @return object
* @return \stdClass
*/
public function getData($addCharacterLogData = false){
$characterData = null;
$cacheKeyModifier = '';
// check if there is cached data
// temporary disabled (performance test)
$characterData = null; //$this->getCacheData();
if($addCharacterLogData){
$cacheKeyModifier = strtoupper($this->table) . '_LOG';
}
$characterData = $this->getCacheData($cacheKeyModifier);
if(is_null($characterData)){
// no cached character data found
$characterData = (object) [];
$characterData->id = $this->id;
$characterData->name = $this->name;
$characterData->shared = $this->shared;
if($addCharacterLogData){
if($logModel = $this->getLog()){
@@ -100,24 +140,56 @@ class CharacterModel extends BasicModel {
// max caching time for a system
// the cached date has to be cleared manually on any change
// this includes system, connection,... changes (all dependencies)
$this->updateCacheData($characterData, '', 300);
$this->updateCacheData($characterData, $cacheKeyModifier, 10);
}
return $characterData;
}
/**
* set unique "ownerHash" for this character
* -> Hash will change when character is transferred (sold)
* @param string $ownerHash
* @return string
*/
public function set_ownerHash($ownerHash){
if (
$this->hasUserCharacter() &&
$this->ownerHash !== $ownerHash
){
$this->userCharacter->erase();
}
return $ownerHash;
}
/**
* set CREST accessToken for current session
* -> update "tokenUpdated" column on change
* -> this is required for expire checking!
* @param string $accessToken
* @return string
*/
public function set_crestAccessToken($accessToken){
if($this->crestAccessToken !== $accessToken){
$this->touch('crestAccessTokenUpdated');
}
return $accessToken;
}
/**
* check whether this character has already a user assigned to it
* @return bool
*/
public function hasUserCharacter(){
return is_object($this->userCharacter);
}
/**
* check whether this character has a corporation
* @return bool
*/
public function hasCorporation(){
$hasCorporation = false;
if($this->corporationId){
$hasCorporation = true;
}
return $hasCorporation;
return is_object($this->corporationId);
}
/**
@@ -125,46 +197,198 @@ class CharacterModel extends BasicModel {
* @return bool
*/
public function hasAlliance(){
$hasAlliance = false;
return is_object($this->allianceId);
}
if($this->allianceId){
$hasAlliance = true;
/**
* @return UserModel|null
*/
public function getUser(){
$user = null;
if($this->hasUserCharacter()){
/**
* @var $user UserModel
*/
$user = $this->userCharacter->userId;
}
return $hasAlliance;
return $user;
}
/**
* get the corporation for this user
* @return mixed|null
* @return \Model\CorporationModel|null
*/
public function getCorporation(){
$corporation = null;
if($this->hasCorporation()){
$corporation = $this->corporationId;
}
return $corporation;
return $this->corporationId;
}
/**
* get the alliance of this user
* @return mixed|null
* @return \Model\AllianceModel|null
*/
public function getAlliance(){
$alliance = null;
return $this->allianceId;
}
if($this->hasAlliance()){
$alliance = $this->allianceId;
/**
* get CREST API "access_token" from OAuth
* @return bool|string
*/
private function getAccessToken(){
$accessToken = false;
// check if there is already an "accessToken" for this user
// check expire timer for stored "accessToken"
if(
!empty($this->crestAccessToken) &&
!empty($this->crestAccessTokenUpdated)
){
$timezone = new \DateTimeZone( $this->getF3()->get('TZ') );
$tokenTime = \DateTime::createFromFormat(
'Y-m-d H:i:s',
$this->crestAccessTokenUpdated,
$timezone
);
// add expire time buffer for this "accessToken"
// token should be marked as "deprecated" BEFORE it actually expires.
$timeBuffer = 2 * 60;
$tokenTime->add(new \DateInterval('PT' . (Ccp\Sso::ACCESS_KEY_EXPIRE_TIME - $timeBuffer) . 'S'));
$now = new \DateTime('now', $timezone);
if($tokenTime->getTimestamp() > $now->getTimestamp()){
$accessToken = $this->crestAccessToken;
}
}
return $alliance;
// if no "accessToken" was found -> get a fresh one by an existing "refreshToken"
if(
!$accessToken &&
!empty($this->crestRefreshToken)
){
// no accessToken found OR token is deprecated
$ssoController = new Ccp\Sso();
$accessData = $ssoController->refreshAccessToken($this->crestRefreshToken);
if(
isset($accessData->accessToken) &&
isset($accessData->refreshToken)
){
$this->crestAccessToken = $accessData->accessToken;
$this->save();
$accessToken = $this->crestAccessToken;
}
}
return $accessToken;
}
/**
* checks whether this character is authorized to log in
* -> check corp/ally whitelist config (pathfinder.ini)
* @return bool
*/
public function isAuthorized(){
$isAuthorized = false;
$f3 = self::getF3();
$whitelistCorporations = $whitelistAlliance = [];
if( !empty($f3->get('PATHFINDER.LOGIN.CORPORATION')) ){
$whitelistCorporations = array_map('trim',(array) $f3->get('PATHFINDER.LOGIN.CORPORATION') );
}
if( !empty($f3->get('PATHFINDER.LOGIN.ALLIANCE')) ){
$whitelistAlliance = array_map('trim',(array) $f3->get('PATHFINDER.LOGIN.ALLIANCE') );
}
if(
empty($whitelistCorporations) &&
empty($whitelistAlliance)
){
// no corp/ally restrictions set -> any character is allowed to login
$isAuthorized = true;
}else{
// check if character corporation is set in whitelist
if(
!empty($whitelistCorporations) &&
$this->hasCorporation() &&
in_array($this->getCorporation()->_id, $whitelistCorporations)
){
$isAuthorized = true;
}
// check if character alliance is set in whitelist
if(
!$isAuthorized &&
!empty($whitelistAlliance) &&
$this->hasAlliance() &&
in_array($this->getAlliance()->_id, $whitelistAlliance)
){
$isAuthorized = true;
}
}
return $isAuthorized;
}
/**
* update character log (active system, ...)
* -> HTTP Header Data (if IGB)
* -> CREST API request for character log data (if not IGB)
* @return CharacterModel
*/
public function updateLog(){
$logData = [];
$headerData = Controller\Controller::getIGBHeaderData();
// check if IGB Data is available
if(
$headerData->trusted === true &&
!empty($headerData->values)
){
// format header data
$formattedHeaderData = (new Mapper\IgbHeader($headerData->values))->getData();
// just for security -> check if Header Data matches THIS character
if(
isset($formattedHeaderData['character']) &&
$formattedHeaderData['character']['id'] == $this->_id
){
$logData = $formattedHeaderData;
}
}else{
// get Location Data from CREST endpoint
// user is NOT with IGB online OR has not jet set "trusted" page
$ssoController = new Ccp\Sso();
$logData = $ssoController->getCharacterLocationData($this->getAccessToken());
}
if( empty($logData) ){
// character is not in-game
if(is_object($this->characterLog)){
// delete existing log
$this->characterLog->erase();
$this->save();
}
}else{
// character is currently in-game
if( !$characterLog = $this->getLog() ){
// create new log
$characterLog = $this->rel('characterLog');
$characterLog->characterId = $this->_id;
}
$characterLog->setData($logData);
$characterLog->save();
$this->characterLog = $characterLog;
}
return $this;
}
/**
* get the character log entry for this character
* @return bool|null
* @return bool|CharacterLogModel
*/
public function getLog(){
@@ -173,10 +397,65 @@ class CharacterModel extends BasicModel {
is_object($this->characterLog) &&
!$this->characterLog->dry()
){
$characterLog = $this->characterLog;
$characterLog = &$this->characterLog;
}
return $characterLog;
}
/**
* get mapModel by id and check if user has access
* @param int $mapId
* @return MapModel|null
*/
public function getMap(int $mapId){
/**
* @var $map MapModel
*/
$map = self::getNew('MapModel');
$map->getById( $mapId );
$returnMap = null;
if($map->hasAccess($this)){
$returnMap = $map;
}
return $returnMap;
}
/**
* get all accessible map models for this character
* @return MapModel[]
*/
public function getMaps(){
$this->filter(
'characterMaps',
['active = ?', 1],
['order' => 'created']
);
$maps = [];
if($this->characterMaps){
$mapCountPrivate = 0;
foreach($this->characterMaps as &$characterMap){
if($mapCountPrivate < self::getF3()->get('PATHFINDER.MAX_MAPS_PRIVATE')){
$maps[] = &$characterMap->mapId;
$mapCountPrivate++;
}
}
}
// get
if($alliance = $this->getAlliance()){
$maps = array_merge($maps, $alliance->getMaps());
}
if($corporation = $this->getCorporation()){
$maps = array_merge($maps, $corporation->getMaps());
}
return $maps;
}
}

View File

@@ -103,11 +103,11 @@ class ConnectionModel extends BasicModel{
/**
* check object for model access
* @param $accessObject
* @return bool
* @param CharacterModel $characterModel
* @return mixed
*/
public function hasAccess($accessObject){
return $this->mapId->hasAccess($accessObject);
public function hasAccess(CharacterModel $characterModel){
return $this->mapId->hasAccess($characterModel);
}
/**
@@ -127,13 +127,12 @@ class ConnectionModel extends BasicModel{
/**
* delete a connection
* @param $accessObject
* @param CharacterModel $characterModel
*/
public function delete($accessObject){
if(!$this->dry()){
// check if editor has access
if($this->hasAccess($accessObject)){
public function delete(CharacterModel $characterModel){
if( !$this->dry() ){
// check if character has access
if($this->hasAccess($characterModel)){
$this->erase();
}
}

View File

@@ -8,6 +8,7 @@
namespace Model;
use Controller\Api\User;
use DB\SQL\Schema;
class MapModel extends BasicModel {
@@ -60,8 +61,8 @@ class MapModel extends BasicModel {
'connections' => [
'has-many' => ['Model\ConnectionModel', 'mapId']
],
'mapUsers' => [
'has-many' => ['Model\UserMapModel', 'mapId']
'mapCharacters' => [
'has-many' => ['Model\CharacterMapModel', 'mapId']
],
'mapCorporations' => [
'has-many' => ['Model\CorporationMapModel', 'mapId']
@@ -117,16 +118,14 @@ class MapModel extends BasicModel {
}
}
/**
* get map data
* -> this includes system and connection data as well!
* @return array
* @return \stdClass
*/
public function getData(){
// check if there is cached data
$mapDataAll = $this->getCacheData();
// check if there is cached data
$mapDataAll = $this->getCacheData();
if(is_null($mapDataAll)){
// no cached map data found
@@ -158,17 +157,23 @@ class MapModel extends BasicModel {
// get access object data -------------------------------------
if($this->isPrivate()){
$users = $this->getUsers();
$userData = [];
foreach($users as $user){
$userData[] = $user->getSimpleData();
$characters = $this->getCharacters();
$characterData = [];
foreach($characters as $character){
/**
* @var $character CharacterModel
*/
$characterData[] = $character->getData();
}
$mapData->access->user = $userData;
$mapData->access->character = $characterData;
} elseif($this->isCorporation()){
$corporations = $this->getCorporations();
$corporationData = [];
foreach($corporations as $corporation){
/**
* @var $corporation CorporationModel
*/
$corporationData[] = $corporation->getData();
}
$mapData->access->corporation = $corporationData;
@@ -177,6 +182,9 @@ class MapModel extends BasicModel {
$allianceData = [];
foreach($alliances as $alliance){
/**
* @var $alliance AllianceModel
*/
$allianceData[] = $alliance->getData();
}
$mapData->access->alliance = $allianceData;
@@ -231,8 +239,9 @@ class MapModel extends BasicModel {
$this->filter('systems',
['active = :active AND id > 0',
':active' => 1
],
['order' => 'posX']);
],
['order' => 'posX']
);
$systems = [];
if($this->systems){
@@ -252,6 +261,9 @@ class MapModel extends BasicModel {
$systemData = [];
foreach($systems as $system){
/**
* @var $system SystemModel
*/
$systemData[] = $system->getData();
}
@@ -285,6 +297,9 @@ class MapModel extends BasicModel {
$connectionData = [];
foreach($connections as $connection){
/**
* @var $connection ConnectionModel
*/
$connectionData[] = $connection->getData();
}
@@ -292,26 +307,26 @@ class MapModel extends BasicModel {
}
/**
* set map access for an object (user, corporation or alliance)
* set map access for an object (character, corporation or alliance)
* @param $obj
*/
public function setAccess($obj){
$newAccessGranted = false;
if($obj instanceof UserModel){
if($obj instanceof CharacterModel){
// private map
// check whether the user has already map access
$this->has('mapUsers', ['active = 1 AND userId = :userId', ':userId' => $obj->id]);
$this->has('mapCharacters', ['active = 1 AND characterId = :characterId', ':characterId' => $obj->id]);
$result = $this->findone(['id = :id', ':id' => $this->id]);
if($result === false){
// grant access for the user
$userMap = self::getNew('UserMapModel');
$userMap->userId = $obj;
$userMap->mapId = $this;
$userMap->save();
// grant access for the character
$characterMap = self::getNew('CharacterMapModel');
$characterMap-> characterId = $obj;
$characterMap->mapId = $this;
$characterMap->save();
$newAccessGranted = true;
}
@@ -355,25 +370,34 @@ class MapModel extends BasicModel {
/**
* clear access for a given type of objects
* @param $clearKeys
* @param array $clearKeys
*/
public function clearAccess($clearKeys = ['user', 'corporation', 'alliance']){
public function clearAccess($clearKeys = ['character', 'corporation', 'alliance']){
foreach($clearKeys as $key){
switch($key){
case 'user':
foreach((array)$this->mapUsers as $obj){
$obj->erase();
case 'character':
foreach((array)$this->mapCharacters as $characterMapModel){
/**
* @var CharacterMapModel $characterMapModel
*/
$characterMapModel->erase();
};
break;
case 'corporation':
foreach((array)$this->mapCorporations as $obj){
$obj->erase();
foreach((array)$this->mapCorporations as $corporationMapModel){
/**
* @var CorporationMapModel $corporationMapModel
*/
$corporationMapModel->erase();
};
break;
case 'alliance':
foreach((array)$this->mapAlliances as $obj){
$obj->erase();
foreach((array)$this->mapAlliances as $allianceMapModel){
/**
* @var AllianceMapModel $allianceMapModel
*/
$allianceMapModel->erase();
};
break;
}
@@ -381,22 +405,17 @@ class MapModel extends BasicModel {
}
/**
* checks weather a user has access to this map or not
* @param $user
* checks weather a character has access to this map or not
* @param CharacterModel $characterModel
* @return bool
*/
public function hasAccess($user){
public function hasAccess(CharacterModel $characterModel){
$hasAccess = false;
if(
!$this->dry() &&
$user instanceof UserModel
){
if( !$this->dry() ){
// get all maps the user has access to
// this includes corporation and alliance maps
$maps = $user->getMaps();
$maps = $characterModel->getMaps();
foreach($maps as $map){
if($map->id === $this->id){
$hasAccess = true;
@@ -409,54 +428,56 @@ class MapModel extends BasicModel {
}
/**
* get all user models that have access to this map
* note: This function is just for "private" maps
* @return array
*/
public function getUsers(){
$users = [];
if($this->isPrivate()){
$this->filter('mapUsers', ['active = ?', 1]);
if($this->mapUsers){
foreach($this->mapUsers as $mapUser){
$users[] = $mapUser->userId;
}
}
}
return $users;
}
/**
* get all character models that are currently online "viewing" this map
* @return array
* get all (private) characters for this map
* @return CharacterModel array
*/
private function getCharacters(){
$characters = [];
$this->filter('mapCharacters', ['active = ?', 1]);
if($this->mapCharacters){
foreach($this->mapCharacters as $characterMapModel){
$characters[] = $characterMapModel->characterId;
}
}
return $characters;
}
/**
* get all character models that are currently online "viewing" this map
* @return CharacterModel[]
*/
private function getActiveCharacters(){
$characters = [];
if($this->isPrivate()){
$users = $this->getUsers();
$activeCharacters = $this->getCharacters();
foreach($users as $user){
// get all active character logs for a user
$tempActiveUserCharacters = $user->getActiveUserCharacters();
foreach($tempActiveUserCharacters as $tempActiveUserCharacter){
$characters[] = $tempActiveUserCharacter;
}
// add active character for each user
foreach($activeCharacters as $activeCharacter){
/**
* @var UserModel $user
*/
$characters[] = $activeCharacter;
}
}elseif($this->isCorporation()){
$corporations = $this->getCorporations();
foreach($corporations as $corporation){
/**
* @var CorporationModel $corporation
*/
$characters = array_merge($characters, $corporation->getCharacters());
}
}elseif($this->isAlliance()){
$alliances = $this->getAlliances();
foreach($alliances as $alliance){
/**
* @var AllianceModel $alliance
*/
$characters = array_merge($characters, $alliance->getCharacters());
}
}
@@ -476,7 +497,7 @@ class MapModel extends BasicModel {
if(is_null($charactersData)){
$charactersData = [];
$characters = $this->getCharacters();
$characters = $this->getActiveCharacters();
foreach($characters as $character){
$charactersData[] = $character->getData(true);
@@ -484,7 +505,7 @@ class MapModel extends BasicModel {
// cache active characters (if found)
if(!empty($charactersData)){
$this->updateCacheData($charactersData, 'CHARACTERS', 10);
$this->updateCacheData($charactersData, 'CHARACTERS', 5);
}
}
@@ -531,19 +552,15 @@ class MapModel extends BasicModel {
return $alliances;
}
/**
* delete this map and all dependencies
* @param $accessObject
* @param CharacterModel $characterModel
*/
public function delete($accessObject){
if(!$this->dry()){
// check if editor has access
if($this->hasAccess($accessObject)){
public function delete(CharacterModel $characterModel){
if( !$this->dry() ){
// check if character has access
if($this->hasAccess($characterModel)){
// all map related tables will be deleted on cascade
// delete map
$this->erase();
}
}
@@ -656,9 +673,9 @@ class MapModel extends BasicModel {
if( $mapModel->isPrivate() ){
$mapModel->clearAccess(['corporation', 'alliance']);
}elseif( $mapModel->isCorporation() ){
$mapModel->clearAccess(['user', 'alliance']);
$mapModel->clearAccess(['character', 'alliance']);
}elseif( $mapModel->isAlliance() ){
$mapModel->clearAccess(['user', 'corporation']);
$mapModel->clearAccess(['character', 'corporation']);
}
}

View File

@@ -1,49 +0,0 @@
<?php
/**
* Created by PhpStorm.
* User: exodus4d
* Date: 29.08.15
* Time: 11:57
*/
namespace Model;
use DB\SQL\Schema;
class RegistrationKeyModel extends BasicModel {
protected $table = 'registration_key';
protected $fieldConf = [
'active' => [
'type' => Schema::DT_BOOL,
'nullable' => false,
'default' => 1,
'index' => true
],
'ip' => [
'type' => Schema::DT_VARCHAR128,
'nullable' => false,
'default' => ''
],
'used' => [
'type' => Schema::DT_BOOL,
'nullable' => false,
'default' => 0
],
'email' => [
'type' => Schema::DT_VARCHAR128,
'nullable' => false,
'default' => '',
'index' => true
],
'registrationKey' => [
'type' => Schema::DT_VARCHAR128,
'nullable' => false,
'default' => '',
'index' => true,
'unique' => true
]
];
}

View File

@@ -177,18 +177,18 @@ class SystemModel extends BasicModel {
}else{
// special array data
if($key == 'constellation'){
$this->constellationId = $value['id'];
$this->constellationId = (int)$value['id'];
$this->constellation = $value['name'];
}elseif($key == 'region'){
$this->regionId = $value['id'];
$this->regionId = (int)$value['id'];
$this->region = $value['name'];
}elseif($key == 'type'){
$this->typeId = $value['id'];
$this->typeId = (int)$value['id'];
}elseif($key == 'status'){
$this->statusId = $value['id'];
$this->statusId = (int)$value['id'];
}elseif($key == 'position'){
$this->posX = $value['x'];
$this->posY = $value['y'];
$this->posX = (int)$value['x'];
$this->posY = (int)$value['y'];
}
}
}
@@ -313,23 +313,22 @@ class SystemModel extends BasicModel {
/**
* check object for model access
* @param $accessObject
* @return bool
* @param CharacterModel $characterModel
* @return mixed
*/
public function hasAccess($accessObject){
return $this->mapId->hasAccess($accessObject);
public function hasAccess(CharacterModel $characterModel){
return $this->mapId->hasAccess($characterModel);
}
/**
* delete a system from a map
* hint: signatures and connections will be deleted on cascade
* @param $accessObject
* @param CharacterModel $characterModel
*/
public function delete($accessObject){
if(! $this->dry()){
// check if user has access
if($this->hasAccess($accessObject)){
public function delete(CharacterModel $characterModel){
if( !$this->dry() ){
// check if character has access
if($this->hasAccess($characterModel)){
$this->erase();
}
}
@@ -367,14 +366,14 @@ class SystemModel extends BasicModel {
/**
* get Signature by id and check for access
* @param $accessObject
* @param CharacterModel $characterModel
* @param $id
* @return bool|null
*/
public function getSignatureById($accessObject, $id){
public function getSignatureById(CharacterModel $characterModel, $id){
$signature = null;
if($this->hasAccess($accessObject)){
if($this->hasAccess($characterModel)){
$this->filter('signatures', ['active = ? AND id = ?', 1, $id]);
if($this->signatures){
$signature = reset( $this->signatures );
@@ -386,14 +385,14 @@ class SystemModel extends BasicModel {
/**
* get a signature by its "unique" 3-digit name
* @param $accessObject
* @param CharacterModel $characterModel
* @param $name
* @return mixed|null
*/
public function getSignatureByName($accessObject, $name){
public function getSignatureByName(CharacterModel $characterModel, $name){
$signature = null;
if($this->hasAccess($accessObject)){
if($this->hasAccess($characterModel)){
$this->filter('signatures', ['active = ? AND name = ?', 1, $name]);
if($this->signatures){
$signature = reset( $this->signatures );

View File

@@ -151,18 +151,21 @@ class SystemSignatureModel extends BasicModel {
/**
* check object for model access
* @param $accessObject
* @param CharacterModel $characterModel
* @return bool
*/
public function hasAccess($accessObject){
return $this->systemId->hasAccess($accessObject);
public function hasAccess(CharacterModel $characterModel){
return $this->systemId->hasAccess($characterModel);
}
public function delete($accessObject){
if(!$this->dry()){
// check if editor has access
if($this->hasAccess($accessObject)){
/**
* delete signature
* @param CharacterModel $characterModel
*/
public function delete(CharacterModel $characterModel){
if( !$this->dry() ){
// check if character has access
if($this->hasAccess($characterModel)){
$this->erase();
}
}

View File

@@ -1,152 +0,0 @@
<?php
/**
* Created by PhpStorm.
* User: exodus4d
* Date: 28.03.15
* Time: 16:41
*/
namespace Model;
use DB\SQL\Schema;
use Controller;
class UserApiModel extends BasicModel {
protected $table = 'user_api';
protected $fieldConf = [
'active' => [
'type' => Schema::DT_BOOL,
'nullable' => false,
'default' => 1,
'index' => true
],
'userId' => [
'type' => Schema::DT_INT,
'index' => true,
'belongs-to-one' => 'Model\UserModel',
'constraint' => [
[
'table' => 'user',
'on-delete' => 'CASCADE'
]
]
],
'keyId' => [
'type' => Schema::DT_INT,
'index' => true,
'unique' => true
],
'vCode' => [
'type' => Schema::DT_VARCHAR128,
'nullable' => false,
'default' => '',
],
'userCharacters' => [
'has-many' => ['Model\UserCharacterModel', 'apiId']
],
];
/**
* get all data for this api
* @return object
*/
public function getData(){
$apiData = (object) [];
$apiData->keyId = $this->keyId;
$apiData->vCode = $this->vCode;
return $apiData;
}
/**
* @return int
*/
public function updateCharacters(){
$apiController = new Controller\CcpApiController();
return $apiController->updateCharacters($this);
}
/**
* get all characters for this API
* @return array|mixed
*/
public function getUserCharacters(){
$this->filter('userCharacters', ['active = ?', 1]);
$userCharacters = [];
if($this->userCharacters){
$userCharacters = $this->userCharacters;
}
return $userCharacters;
}
/**
* search for a user character model by a characterId
* @param $characterId
* @return null
*/
public function getUserCharacterById($characterId){
$userCharacters = $this->getUserCharacters();
$returnUserCharacter = null;
foreach($userCharacters as $userCharacter){
if($userCharacter->characterId->id == $characterId){
$returnUserCharacter = $userCharacter;
break;
}
}
return $returnUserCharacter;
}
/**
* check if this api model has a main character
* @return bool
*/
public function hasMainCharacter(){
$hasMain = false;
$characters = $this->getUserCharacters();
foreach($characters as $character){
if($character->isMain()){
$hasMain = true;
break;
}
}
return $hasMain;
}
/**
* get the user object for this model
* @return mixed
*/
public function getUser(){
return $this->userId;
}
/**
* delete this api model
*/
public function delete(){
// check if this api model had a main character
$user = $this->userId;
$setNewMain = false;
if($this->hasMainCharacter()){
$setNewMain = true;
}
$this->erase();
if($setNewMain){
$user->setMainCharacterId();
}
}
}

View File

@@ -32,20 +32,10 @@ class UserCharacterModel extends BasicModel {
]
]
],
'apiId' => [
'type' => Schema::DT_INT,
'index' => true,
'belongs-to-one' => 'Model\UserApiModel',
'constraint' => [
[
'table' => 'user_api',
'on-delete' => 'CASCADE'
]
]
],
'characterId' => [
'type' => Schema::DT_INT,
'index' => true,
'unique' => true,
'belongs-to-one' => 'Model\CharacterModel',
'constraint' => [
[
@@ -53,12 +43,6 @@ class UserCharacterModel extends BasicModel {
'on-delete' => 'CASCADE'
]
]
],
'isMain' => [
'type' => Schema::DT_BOOLEAN,
'nullable' => false,
'default' => 0,
'index' => true
]
];
@@ -67,9 +51,7 @@ class UserCharacterModel extends BasicModel {
* @param $characterData
*/
public function setData($characterData){
foreach((array)$characterData as $key => $value){
if(!is_array($value)){
if($this->exists($key)){
$this->$key = $value;
@@ -79,52 +61,19 @@ class UserCharacterModel extends BasicModel {
}
/**
* get all character data
* @param $addCharacterLogData
* @return array
*/
public function getData($addCharacterLogData = false){
// get characterModel
$characterModel = $this->getCharacter();
// get static character data
$characterData = $characterModel->getData($addCharacterLogData);
// add user specific character data
$characterData->isMain = $this->isMain;
// check for corporation
if( is_object( $characterModel->corporationId ) ){
$characterData->corporation = $characterModel->corporationId->getData();
}
// check for alliance
if( is_object( $characterModel->allianceId ) ){
$characterData->alliance = $characterModel->allianceId->getData();
}
return $characterData;
}
/**
* check if this character is Main character or not
* event "Hook"
* -> remove user if there are no other characters bound to this user
* @param $self
* @return bool
*/
public function isMain(){
$isMain = false;
if($this->isMain == 1){
$isMain = true;
public function aftereraseEvent($self){
if(
is_object($self->userId) &&
is_null($self->userId->userCharacters)
){
$self->userId->erase();
}
return $isMain;
}
/**
* set this character as main character
*/
public function setMain($value = 0){
$this->isMain = $value;
return true;
}
/**
@@ -146,10 +95,7 @@ class UserCharacterModel extends BasicModel {
$status = parent::setup($db,$table,$fields);
if($status === true){
$status = parent::setMultiColumnIndex(['userId', 'apiId', 'characterId'], true);
if($status === true){
$status = parent::setMultiColumnIndex(['userId', 'apiId']);
}
$status = parent::setMultiColumnIndex(['userId', 'characterId'], true);
}
return $status;

View File

@@ -10,6 +10,7 @@ namespace Model;
use DB\SQL\Schema;
use Controller;
use Controller\Api;
use Exception;
class UserModel extends BasicModel {
@@ -17,10 +18,6 @@ class UserModel extends BasicModel {
protected $table = 'user';
protected $fieldConf = [
'lastLogin' => [
'type' => Schema::DT_TIMESTAMP,
'index' => true
],
'active' => [
'type' => Schema::DT_BOOL,
'nullable' => false,
@@ -31,34 +28,15 @@ class UserModel extends BasicModel {
'type' => Schema::DT_VARCHAR128,
'nullable' => false,
'default' => '',
'index' => true,
'unique' => true
'index' => true
],
'email' => [
'type' => Schema::DT_VARCHAR128,
'nullable' => false,
'default' => '',
'index' => true,
'unique' => true
],
'password' => [
'type' => Schema::DT_VARCHAR128,
'nullable' => false,
'default' => ''
],
'shared' => [
'type' => Schema::DT_BOOL,
'nullable' => false,
'default' => 0
],
'apis' => [
'has-many' => ['Model\UserApiModel', 'userId']
],
'userCharacters' => [
'has-many' => ['Model\UserCharacterModel', 'userId']
],
'userMaps' => [
'has-many' => ['Model\UserMapModel', 'userId']
]
];
@@ -68,24 +46,14 @@ class UserModel extends BasicModel {
'min' => 5,
'max' => 25
]
],
'email' => [
'length' => [
'min' => 5
]
],
'password' => [
'length' => [
'min' => 6
]
]
];
/**
* get all data for this user
* ! caution ! this function returns sensitive data!
* -> ! caution ! this function returns sensitive data! (e.g. email,..)
* -> user getSimpleData() for faster performance and public user data
* @return object
* @return \stdClass
*/
public function getData(){
@@ -95,27 +63,19 @@ class UserModel extends BasicModel {
// add sensitive user data
$userData->email = $this->email;
// user shared info
$userData->shared = $this->shared;
// api data
$APIs = $this->getAPIs();
foreach($APIs as $api){
$userData->api[] = $api->getData();
}
// all chars
$userData->characters = [];
$userCharacters = $this->getUserCharacters();
foreach($userCharacters as $userCharacter){
$userData->characters[] = $userCharacter->getData();
$characters = $this->getCharacters();
foreach($characters as $character){
/**
* @var $character CharacterModel
*/
$userData->characters[] = $character->getData();
}
// set active character with log data
$activeUserCharacter = $this->getActiveUserCharacter();
if($activeUserCharacter){
$userData->character = $activeUserCharacter->getData(true);
}
// get active character with log data
$activeCharacter = $this->getActiveCharacter();
$userData->character = $activeCharacter->getData(true);
return $userData;
}
@@ -123,7 +83,7 @@ class UserModel extends BasicModel {
/**
* get public user data
* - check out getData() for all user data
* @return object
* @return \stdClass
*/
public function getSimpleData(){
$userData = (object) [];
@@ -135,31 +95,21 @@ class UserModel extends BasicModel {
/**
* validate and set a email address for this user
* @param $email
* @return mixed
* -> empty email is allowed!
* @param string $email
* @return string
*/
public function set_email($email){
if (\Audit::instance()->email($email) == false) {
if (
!empty($email) &&
\Audit::instance()->email($email) == false
) {
// no valid email address
$this->throwValidationError('email');
}
return $email;
}
/**
* set a password hash for this user
* @param $password
* @return FALSE|string
*/
public function set_password($password){
if(strlen($password) < 6){
$this->throwValidationError('password');
}
$salt = uniqid('', true);
return \Bcrypt::instance()->hash($password, $salt);
}
/**
* check if new user registration is allowed
* @return bool
@@ -191,366 +141,89 @@ class UserModel extends BasicModel {
return $this->getByForeignKey('name', $name, [], 0);
}
/**
* verify a user by his password
* @param $password
* @return bool
*/
public function verify($password){
$valid = false;
if(! $this->dry()){
$valid = (bool) \Bcrypt::instance()->verify($password, $this->password);
}
return $valid;
}
/**
* get all accessible map models for this user
* @return array
*/
public function getMaps(){
$f3 = self::getF3();
$this->filter(
'userMaps',
['active = ?', 1],
['order' => 'created']
);
$maps = [];
if($this->userMaps){
$mapCountPrivate = 0;
foreach($this->userMaps as $userMap){
if(
$userMap->mapId->isActive() &&
$mapCountPrivate < $f3->get('PATHFINDER.MAX_MAPS_PRIVATE')
){
$maps[] = $userMap->mapId;
$mapCountPrivate++;
}
}
}
$activeUserCharacter = $this->getActiveUserCharacter();
if($activeUserCharacter){
$character = $activeUserCharacter->getCharacter();
$corporation = $character->getCorporation();
$alliance = $character->getAlliance();
if($alliance){
$allianceMaps = $alliance->getMaps();
$maps = array_merge($maps, $allianceMaps);
}
if($corporation){
$corporationMaps = $corporation->getMaps();
$maps = array_merge($maps, $corporationMaps);
}
}
return $maps;
}
/**
* get mapModel by id and check if user has access
* @param $mapId
* @return null
* @throws \Exception
*/
public function getMap($mapId){
$map = self::getNew('MapModel');
$map->getById( (int)$mapId );
$returnMap = null;
if($map->hasAccess($this)){
$returnMap = $map;
}
return $returnMap;
}
/**
* get all API models for this user
* @return array|mixed
*/
public function getAPIs(){
$this->filter('apis', ['active = ?', 1]);
$apis = [];
if($this->apis){
$apis = $this->apis;
}
return $apis;
}
/**
* set main character ID for this user.
* If id does not match with his API chars -> select "random" main character
* @param int $characterId
*/
public function setMainCharacterId($characterId = 0){
if(is_int($characterId)){
$userCharacters = $this->getUserCharacters();
if(count($userCharacters) > 0){
$mainSet = false;
foreach($userCharacters as $userCharacter){
if($characterId == $userCharacter->getCharacter()->id){
$mainSet = true;
$userCharacter->setMain(1);
}else{
$userCharacter->setMain(0);
}
$userCharacter->save();
}
// set first character as "main"
if( !$mainSet ){
$userCharacter = reset($userCharacters);
$userCharacter->setMain(1);
$userCharacter->save();
}
}
}
}
/**
* get all userCharacters models for a user
* characters will be checked/updated on login by CCP API call
* @return array|mixed
* @return UserCharacterModel[]
*/
public function getUserCharacters(){
$this->filter('userCharacters', ['active = ?', 1]);
$this->filter('apis', ['active = ?', 1]);
// if a user has multiple API keys saved for ONE character,
// skip double characters!
$userCharacters = [];
if($this->apis){
$this->apis->rewind();
while($this->apis->valid()){
$this->apis->current()->filter('userCharacters', ['active = ?', 1]);
if($this->apis->current()->userCharacters){
$this->apis->current()->userCharacters->rewind();
while($this->apis->current()->userCharacters->valid()){
$tempCharacterId = $this->apis->current()->userCharacters->current()->characterId->get('id');
if( !isset($userCharacters[ $tempCharacterId ]) ){
$userCharacters[ $tempCharacterId ] = $this->apis->current()->userCharacters->current();
}
$this->apis->current()->userCharacters->next();
}
}
$this->apis->next();
}
if($this->userCharacters){
$userCharacters = $this->userCharacters;
}
return $userCharacters;
}
/**
* Get the main user character for this user
* @return null
* get the current active character for this user
* -> EITHER - the current active one for the current user
* -> OR - get the first active one
* @return null|CharacterModel
*/
public function getMainUserCharacter(){
$mainUserCharacter = null;
public function getActiveCharacter(){
$activeCharacter = null;
$controller = new Controller\Controller();
$currentActiveCharacter = $controller->getCharacter();
if(
!is_null($currentActiveCharacter) &&
$currentActiveCharacter->getUser()->_id === $this->id
){
$activeCharacter = &$currentActiveCharacter;
}else{
// set "first" found as active for this user
if($activeCharacters = $this->getActiveCharacters()){
$activeCharacter = $activeCharacters[0];
}
}
return $activeCharacter;
}
/**
* get all characters for this user
* @return CharacterModel[]
*/
public function getCharacters(){
$userCharacters = $this->getUserCharacters();
$characters = [];
foreach($userCharacters as $userCharacter){
if($userCharacter->isMain()){
$mainUserCharacter = $userCharacter;
break;
/**
* @var $userCharacter UserCharacterModel
*/
if( $currentCharacter = $userCharacter->getCharacter() ){
// check if userCharacter has a valid character
// -> this should never fail!
$characters[] = $currentCharacter;
}
}
return $mainUserCharacter;
return $characters;
}
/**
* get the active user character for this user
* either there is an active Character (IGB) or the character labeled as "main"
* @return null
*/
public function getActiveUserCharacter(){
$activeUserCharacter = null;
$headerData = Controller\CcpApiController::getIGBHeaderData();
// check if IGB Data is available
if( !empty($headerData->values) ){
// search for the active character by IGB Header Data
$this->filter('userCharacters',
[
'active = :active AND characterId = :characterId',
':active' => 1,
':characterId' => intval($headerData->values['charid'])
],
['limit' => 1]
);
if($this->userCharacters){
// check if userCharacter has active log
$userCharacter = current($this->userCharacters);
if( $userCharacter->getCharacter()->getLog() ){
$activeUserCharacter = $userCharacter;
}
}
}
// if no active character is found
// e.g. not online in IGB
// -> get main Character
if(is_null($activeUserCharacter)){
$activeUserCharacter = $this->getMainUserCharacter();
}
return $activeUserCharacter;
}
/**
* get all active user characters (with log entry)
* get all active characters (with log entry)
* hint: a user can have multiple active characters
* @return array
* @return CharacterModel[]
*/
public function getActiveUserCharacters(){
public function getActiveCharacters(){
$userCharacters = $this->getUserCharacters();
$activeUserCharacters = [];
$activeCharacters = [];
foreach($userCharacters as $userCharacter){
$characterLog = $userCharacter->getCharacter()->getLog();
if($characterLog){
$activeUserCharacters[] = $userCharacter;
/**
* @var $userCharacter UserCharacterModel
*/
$characterModel = $userCharacter->getCharacter();
if($characterLog = $characterModel->getLog()){
$activeCharacters[] = $characterModel;
}
}
return $activeUserCharacters;
return $activeCharacters;
}
/**
* update/check API information.
* request API information from CCP
*/
public function updateApiData(){
$this->filter('apis', ['active = ?', 1]);
if($this->apis){
$this->apis->rewind();
while($this->apis->valid()){
$this->apis->current()->updateCharacters();
$this->apis->next();
}
}
}
/**
* updated the character log entry for a user character by IGB Header data
* @param int $ttl cache time in seconds
* @throws \Exception
*/
public function updateCharacterLog($ttl = 0){
$headerData = Controller\CcpApiController::getIGBHeaderData();
// check if IGB Data is available
if( !empty($headerData->values) ){
$f3 = self::getF3();
// check if system has changed since the last call
// current location is stored (global) to avoid unnecessary DB calls
$sessionCharacterKey = 'LOGGED.user.character.id_' . $headerData->values['charid'];
if($f3->exists($sessionCharacterKey)){
// cache data exists
$cacheData = $f3->get($sessionCharacterKey);
}else{
// new cache data
$cacheData = [
'systemId' => 0,
'shipId' => 0
];
}
if(
$cacheData['systemId'] != $headerData->values['solarsystemid'] ||
$cacheData['shipId'] != $headerData->values['shiptypeid']
){
$cacheData['systemId'] = (int)$headerData->values['solarsystemid'];
$cacheData['shipId'] = (int)$headerData->values['shiptypeid'];
// character has changed system, or character just logged on
$character = self::getNew('CharacterModel');
$character->getById( (int)$headerData->values['charid'] );
if( $character->dry() ){
// this can happen if a valid user plays the game with a not registered character
// whose API is not registered -> save new character or update character data
$corporationId = array_key_exists('corpid', $headerData->values) ? $headerData->values['corpid'] : null;
$allianceId = array_key_exists('allianceid', $headerData->values) ? $headerData->values['allianceid'] : null;
// check if corp exists
if( !is_null($corporationId) ){
$corporation = self::getNew('CorporationModel');
$corporation->getById( (int)$corporationId );
if( $corporation->dry() ){
$corporation->id = $corporationId;
$corporation->name = $headerData->values['corpname'];
$corporation->save();
}
}
// check if ally exists
if( !is_null($allianceId) ){
$alliance = self::getNew('AllianceModel');
$alliance->getById( (int)$allianceId );
if( $alliance->dry() ){
$alliance->id = $allianceId;
$alliance->name = $headerData->values['alliancename'];
$alliance->save();
}
}
$character->id = (int) $headerData->values['charid'];
$character->name = $headerData->values['charname'];
$character->corporationId = $corporationId;
$character->allianceId = $allianceId;
$character->save();
}
// check if this character has an active log
if( !$characterLog = $character->getLog() ){
$characterLog = self::getNew('CharacterLogModel');
}
// set character log values
$characterLog->characterId = $character;
$characterLog->systemId = (int)$headerData->values['solarsystemid'];
$characterLog->systemName = $headerData->values['solarsystemname'];
$characterLog->shipId = (int)$headerData->values['shiptypeid'];
$characterLog->shipName = $headerData->values['shipname'];
$characterLog->shipTypeName = $headerData->values['shiptypename'];
$characterLog->save();
// clear cache for the characterModel as well
$character->clearCacheData();
// cache character log information
$f3->set($sessionCharacterKey, $cacheData, $ttl);
}
}
}
}

View File

@@ -3,7 +3,7 @@
[PATHFINDER]
NAME = PATHFINDER
; installed version (used for CSS/JS cache busting)
VERSION = v1.0.0RC2
VERSION = v1.0.0RC3
; contact information (DO NOT CHANGE)
CONTACT = https://github.com/exodus4d
; source code (DO NOT CHANGE)
@@ -15,7 +15,7 @@ MAX_MAPS_CORPORATION = 3
MAX_MAPS_ALLIANCE = 3
; Max number of shared entities per map
MAX_SHARED_USER = 10
MAX_SHARED_CHARACTER = 10
MAX_SHARED_CORPORATION = 3
MAX_SHARED_ALLIANCE = 2
@@ -34,6 +34,11 @@ INVITE = 0
; the limit of registration keys. Increase it to hand out more keys
INVITE_LIMIT = 50
[PATHFINDER.LOGIN]
; restrict login to specific corporations/alliances by id (e.g. 1000166,1000080)
CORPORATION =
ALLIANCE =
; View ============================================================================================
[PATHFINDER.VIEW]
; static page templates
@@ -78,7 +83,7 @@ EXECUTION_LIMIT = 50
; map user update ping (ajax) (ms)
[PATHFINDER.TIMER.UPDATE_SERVER_USER_DATA]
DELAY = 5000
EXECUTION_LIMIT = 200
EXECUTION_LIMIT = 300
; update client user data (ms)
[PATHFINDER.TIMER.UPDATE_CLIENT_USER_DATA]
@@ -102,11 +107,6 @@ DELETE_ACCOUNT = delete_account
; API =============================================================================================
[PATHFINDER.API]
; CCP SSO OAuth 2.0
CCP_SSO = https://login.eveonline.com
; CCP CREST API
CCP_CREST = https://crest-tq.eveonline.com
; CCP_CREST = https://api-sisi.testeveonline.com
; CCP XML APIv2
CCP_XML = https://api.eveonline.com
; GitHub Developer API

View File

@@ -3,16 +3,15 @@
[routes]
; DB setup setup
; IMPORTANT: remove/comment this line after setup/update is finished!
GET @setup: /setup = Controller\Setup->init, 0
GET @setup: /setup [sync] = Controller\Setup->init, 0
; login (index) page
GET @login: / = Controller\AppController->init, 0
GET @login: / [sync] = Controller\AppController->init, 0
; CCP SSO redirect
GET @sso: /sso/@action = Controller\CcpSsoController->@action, 0
GET @sso: /sso/@action [sync] = Controller\Ccp\Sso->@action, 0
; map page
GET @map: /map = Controller\MapController->init, 0
GET @map: /map [sync] = Controller\MapController->init, 0
; ajax wildcard APIs (throttled)
GET|POST /api/@controller/@action [ajax] = Controller\Api\@controller->@action, 0, 512
GET|POST /api/@controller/@action/@arg1 [ajax] = Controller\Api\@controller->@action, 0, 512
GET|POST /api/@controller/@action/@arg1/@arg2 [ajax] = Controller\Api\@controller->@action, 0, 512
GET|POST /api/@controller/@action [ajax] = Controller\Api\@controller->@action, 0, 512
GET|POST /api/@controller/@action/@arg1 [ajax] = Controller\Api\@controller->@action, 0, 512
GET|POST /api/@controller/@action/@arg1/@arg2 [ajax] = Controller\Api\@controller->@action, 0, 512

View File

@@ -6,7 +6,6 @@ var plumber = require('gulp-plumber');
var gzip = require('gulp-gzip');
var gulpif = require('gulp-if');
var clean = require('gulp-clean');
var critical = require('critical');
var runSequence = require('run-sequence');
var exec = require('child_process').exec;
@@ -91,13 +90,7 @@ gulp.task('jshint', function(){
errorHandler: onError
}))
.pipe(jshint())
.pipe(jshint.reporter(stylish))
.pipe(notify({
icon: path.resolve(__dirname, _src.ICON),
title: 'JSHint',
message: 'Task complete',
onLast: true
}));
.pipe(jshint.reporter(stylish));
// .pipe(jshint.reporter('fail')); // uncomment this line to stop build on error
});
@@ -247,25 +240,6 @@ gulp.task('default', function(tag) {
);
});
/*
// This removes all CSS styles "above the fold" from *.css and inlines them
// -> to improve pagespeed. The remaining (main) css file will be lazy loaded afterwards...
// https://github.com/addyosmani/critical
gulp.task('critical', function (cb) {
critical.generate({
inline: true,
base: './',
src: './public/templates/view/index.html',
dest: './public/templates/view/index-critical.html',
extract: true,
minify: true,
width: 2560,
height: 1440
});
});
*/
/**
* clear all backend (fat free framework) cache files
*/

View File

@@ -45,7 +45,7 @@ requirejs.config({
hoverIntent: 'lib/jquery.hoverIntent.minified', // v1.8.0 Hover intention - http://cherne.net/brian/resources/jquery.hoverIntent.html
fullScreen: 'lib/jquery.fullscreen.min', // v0.5.0 Full screen mode - https://github.com/private-face/jquery.fullscreen
select2: 'lib/select2.min', // v4.0.0 Drop Down customization - https://select2.github.io/
validator: 'lib/validator.min', // v0.7.2 Validator for Bootstrap 3 - https://github.com/1000hz/bootstrap-validator
validator: 'lib/validator.min', // v0.10.1 Validator for Bootstrap 3 - https://github.com/1000hz/bootstrap-validator
lazylinepainter: 'lib/jquery.lazylinepainter-1.5.1.min', // v1.5.1 SVG line animation plugin - http://lazylinepainter.info/
blueImpGallery: 'lib/blueimp-gallery', // v2.15.2 Image Gallery - https://github.com/blueimp/Gallery/
blueImpGalleryHelper: 'lib/blueimp-helper', // helper function for Blue Imp Gallery

View File

@@ -16,6 +16,7 @@ define(['jquery'], function($) {
logOut: 'api/user/logOut', // ajax URL - logout
deleteLog: 'api/user/deleteLog', // ajax URL - delete character log
saveUserConfig: 'api/user/saveAccount', // ajax URL - saves/update user account
getUserData: 'api/user/getData', // ajax URL - get user data
saveSharingConfig: 'api/user/saveSharingConfig', // ajax URL - save "sharing settings" dialog
deleteAccount: 'api/user/deleteAccount', // ajax URL - delete Account data
// access API

View File

@@ -83,6 +83,7 @@ define([
autoWidth: false,
hover: false,
pageLength: 15,
lengthMenu: [[10, 15, 25, 50, 50], [10, 15, 25, 50, 50]],
data: logData, // load cached logs (if available)
language: {
emptyTable: 'No entries',

View File

@@ -84,54 +84,6 @@ define([
});
});
// login buttons ------------------------------------------------
var loginForm = $('#' + config.loginFormId);
loginForm.on('submit', function(e){
e.preventDefault();
var loginFormMessageContainer = $('#' + config.loginMessageContainerId);
// validate form
loginForm.validator('validate');
// check weather the form is valid
var formValid = loginForm.isValidForm();
if(formValid === true){
// show splash overlay
$('.' + config.splashOverlayClass).showSplashOverlay(function(){
var loginData = {loginData: loginForm.getFormValues()};
$.ajax({
type: 'POST',
url: Init.path.logIn,
data: loginData,
dataType: 'json'
}).done(function(data){
// login error
if(data.error !== undefined){
$('.' + config.splashOverlayClass).hideSplashOverlay();
loginFormMessageContainer.showMessage({title: 'Login failed', text: ' Invalid username or password', type: 'error'});
}else if(data.reroute !== undefined){
window.location = data.reroute;
}
}).fail(function( jqXHR, status, error) {
$('.' + config.splashOverlayClass).hideSplashOverlay();
var reason = status + ' ' + error;
Util.showNotify({title: jqXHR.status + ': login', text: reason, type: 'error'});
// show Form message
loginFormMessageContainer.showMessage({title: 'Login failed', text: ' internal server error', type: 'error'});
});
});
}
});
// releases -----------------------------------------------------
$('.' + config.navigationVersionLinkClass).on('click', function(e){
$.fn.releasesDialog();

View File

@@ -602,12 +602,12 @@ define([
system.attr('data-mapid', parseInt(mapContainer.data('id')));
// locked system
if( Boolean( system.data( 'locked') ) !== Boolean( parseInt( data.locked ) )){
if( Boolean( system.data( 'locked') ) !== data.locked ){
system.toggleLockSystem(false, {hideNotification: true, hideCounter: true, map: map});
}
// rally system
if( Boolean( system.data( 'rally') ) !== Boolean( parseInt( data.rally ) )){
if( Boolean( system.data( 'rally') ) !== data.rally ){
system.toggleRallyPoint(false, {hideNotification: true, hideCounter: true});
}
@@ -3099,12 +3099,10 @@ define([
// this is restricted to IGB-usage! CharacterLog data is always set through the IGB
// ->this prevent adding the same system multiple times, if a user is online with IGB AND OOG
if(
CCP.isInGameBrowser() === true &&
currentUserOnMap === false &&
currentCharacterLog &&
mapTracking
){
// add new system to the map
var requestData = {
systemData: {
@@ -3344,6 +3342,15 @@ define([
return data;
};
/**
* removes a map instance from local cache
* @param mapId
*/
var clearMapInstance = function(mapId){
if(typeof activeInstances[mapId] === 'object'){
delete activeInstances[mapId];
}
};
/**
* get a new jsPlumb map instance or or get a cached one for update
@@ -3583,4 +3590,8 @@ define([
});
};
return {
clearMapInstance: clearMapInstance
};
});

View File

@@ -39,14 +39,26 @@ define([
// map init load static data =======================================================
$.getJSON( Init.path.initMap, function( initData ) {
Init.timer = initData.timer;
Init.mapTypes = initData.mapTypes;
Init.mapScopes = initData.mapScopes;
Init.connectionScopes = initData.connectionScopes;
Init.systemStatus = initData.systemStatus;
Init.systemType = initData.systemType;
Init.characterStatus = initData.characterStatus;
Init.maxSharedCount = initData.maxSharedCount;
if( initData.error.length > 0 ){
for(var i = 0; i < initData.error.length; i++){
Util.showNotify({
title: initData.error[i].title,
text: initData.error[i].message,
type: initData.error[i].type
});
}
}
Init.timer = initData.timer;
Init.mapTypes = initData.mapTypes;
Init.mapScopes = initData.mapScopes;
Init.connectionScopes = initData.connectionScopes;
Init.systemStatus = initData.systemStatus;
Init.systemType = initData.systemType;
Init.characterStatus = initData.characterStatus;
Init.maxSharedCount = initData.maxSharedCount;
Init.routes = initData.routes;
// init tab change observer, Once the timers are available
Page.initTabChangeObserver();
@@ -168,14 +180,7 @@ define([
}
}
}).fail(function( jqXHR, status, error) {
// clear both main update request trigger timer
clearUpdateTimeouts();
var reason = status + ' ' + jqXHR.status + ': ' + error;
$(document).trigger('pf:shutdown', {reason: reason});
});
}).fail(handleAjaxErrorResponse);
};
// ping for user data update =======================================================
@@ -258,14 +263,35 @@ define([
}
}
}).fail(function( jqXHR, status, error) {
}).fail(handleAjaxErrorResponse);
// clear both main update request trigger timer
clearUpdateTimeouts();
};
var reason = status + ' ' + jqXHR.status + ': ' + error;
$(document).trigger('pf:shutdown', {reason: reason});
});
/**
* Ajax error response handler function for main-ping functions
* @param jqXHR
* @param status
* @param error
*/
var handleAjaxErrorResponse = function(jqXHR, status, error){
// clear both main update request trigger timer
clearUpdateTimeouts();
var reason = status + ' ' + jqXHR.status + ': ' + error;
var errorData = [];
if(jqXHR.responseText){
var errorObj = $.parseJSON(jqXHR.responseText);
if(
errorObj.error &&
errorObj.error.length > 0
){
errorData = errorObj.error;
}
}
$(document).trigger('pf:shutdown', {reason: reason, error: errorData});
};
@@ -287,7 +313,6 @@ define([
};
});
});

View File

@@ -2,6 +2,7 @@ define([
'jquery',
'app/init',
'app/util',
'app/map/map',
'app/counter',
'app/ui/system_info',
'app/ui/system_graph',
@@ -9,9 +10,8 @@ define([
'app/ui/system_route',
'app/ui/system_killboard',
'datatablesTableTools',
'datatablesResponsive',
'app/map/map'
], function($, Init, Util) {
'datatablesResponsive'
], function($, Init, Util, Map) {
'use strict';
@@ -259,6 +259,11 @@ define([
});
};
/**
* get a fresh tab element
* @param options
* @returns {*|jQuery|HTMLElement}
*/
var getTabElement = function(options){
var tabElement = $('<div>', {
@@ -395,7 +400,7 @@ define([
// update Tab element -> set data
linkElement.updateTabData(options);
// tabs content ====================================
// tabs content =======================================================
var contentElement = $('<div>', {
id: config.mapTabIdPrefix + parseInt( options.id ),
class: [config.mapTabContentClass].join(' ')
@@ -405,8 +410,7 @@ define([
tabContent.append(contentElement);
// init tab =========================================================
// init tab ===========================================================
linkElement.on('click', function(e){
e.preventDefault();
@@ -482,8 +486,11 @@ define([
liElement.remove();
contentElement.remove();
// remove map instance from local cache
Map.clearMapInstance(mapId);
if(findNewActiveTab === true){
tabElement.find('a:first').tab('show');
tabElement.find('.' + config.mapTabClass + ':not(.pull-right):first a').tab('show');
}
}
@@ -540,6 +547,9 @@ define([
// tab element already exists
var tabElements = mapModuleElement.getMapTabElements();
// map ID that is currently active
var activeMapId = 0;
// mapIds that are currently active
var activeMapIds = [];
@@ -582,6 +592,15 @@ define([
var newTabElements = tabMapElement.addTab(data.config);
// check if there is any active map yet (this is not the case
// when ALL maps are removed AND new maps are added in one call
// e.g. character switch)
if(tabMapElement.find('.' + config.mapTabClass + '.active:not(.pull-right)').length === 0){
tabMapElement.find('.' + config.mapTabClass + ':not(.pull-right):first a').tab('show');
activeMapId = data.config.id;
}
// set observer for manually triggered map events
newTabElements.contentElement.setTabContentObserver();
@@ -596,7 +615,9 @@ define([
});
// get current active map
var activeMapId = Util.getMapModule().getActiveMap().data('id');
if(activeMapId === 0){
activeMapId = Util.getMapModule().getActiveMap().data('id');
}
var activeMapData = Util.getCurrentMapData(activeMapId);
if(activeMapData !== false){

View File

@@ -14,7 +14,6 @@ define([
'text!templates/modules/footer.html',
'dialog/notification',
'dialog/trust',
'dialog/sharing_settings',
'dialog/map_info',
'dialog/account_settings',
'dialog/manual',
@@ -143,13 +142,13 @@ define([
$('<a>', {
class: 'list-group-item',
href: '#'
}).html('&nbsp;&nbsp;Sharing settings').prepend(
}).html('&nbsp;&nbsp;Settings').prepend(
$('<i>',{
class: 'fa fa-share-alt fa-fw'
class: 'fa fa-gears fa-fw'
})
).on('click', function(){
$(document).triggerMenuEvent('ShowSharingSettings');
})
$(document).triggerMenuEvent('ShowSettingsDialog');
})
).append(
$('<a>', {
class: 'list-group-item',
@@ -400,11 +399,6 @@ define([
slideMenu.slidebars.toggle('right');
});
// settings
$('.' + config.headUserCharacterClass).find('a').on('click', function(){
$(document).triggerMenuEvent('ShowSettingsDialog');
});
// active pilots
$('.' + config.headActiveUserClass).find('a').on('click', function(){
$(document).triggerMenuEvent('ShowMapInfo');
@@ -439,11 +433,8 @@ define([
});
// set default values for map tracking checkbox
if(CCP.isInGameBrowser() === false){
mapTrackingCheckbox.bootstrapToggle('disable');
}else{
mapTrackingCheckbox.bootstrapToggle('on');
}
// -> always "enable"
mapTrackingCheckbox.bootstrapToggle('on');
mapTrackingCheckbox.on('change', function(e) {
var value = $(this).is(':checked');
@@ -502,13 +493,6 @@ define([
*/
var setDocumentObserver = function(){
// tab close/reload detected
window.addEventListener('beforeunload', function (e) {
// logout
deleteLog();
});
// on "full-screen" change event
$(document).on('fscreenchange', function(e, state, elem){
@@ -526,12 +510,6 @@ define([
}
});
$(document).on('pf:menuShowSharingSettings', function(e){
// show sharing settings dialog
$.fn.showSharingSettingsDialog();
return false;
});
$(document).on('pf:menuShowSystemEffectInfo', function(e){
// show system effects info box
$.fn.showSystemEffectInfoDialog();
@@ -670,12 +648,22 @@ define([
title: 'Shutdown',
headline: 'Emergency shutdown',
text: [
'Sorry! Under normal circumstances that should not happen',
data.reason
]
],
textSmaller: []
}
};
// add error information (if available)
if(
data.error &&
data.error.length
){
for(var i = 0; i < data.error.length; i++){
options.content.textSmaller.push(data.error[i].message);
}
}
$.fn.showNotificationDialog(options);
$(document).setProgramStatus('offline');
@@ -752,7 +740,7 @@ define([
newCharacterName = userData.character.name;
if(userData.character.log){
newShipId = userData.character.log.ship.id;
newShipId = userData.character.log.ship.typeId;
newShipName = userData.character.log.ship.typeName;
}
}
@@ -769,6 +757,9 @@ define([
animateHeaderElement(userInfoElement, function(){
userInfoElement.find('span').text( newCharacterName );
userInfoElement.find('img').attr('src', Init.url.ccpImageServer + 'Character/' + newCharacterId + '_32.jpg' );
// init "character switch" popover
userInfoElement.initCharacterSwitchPopover(userData);
}, showCharacterElement);
// set new id for next check

View File

@@ -1,5 +1,5 @@
/**
* user register/settings dialog
* user settings/share dialog
*/
define([
@@ -12,22 +12,13 @@ define([
'use strict';
var config = {
dialogWizardNavigationClass: 'pf-wizard-navigation', // class for wizard navigation bar
// select character dialog
settingsDialogId: 'pf-settings-dialog', // id for "settings" dialog
settingsImageWrapperClass: 'pf-dialog-image-wrapper', // class for image wrapper (animated)
settingsImageInfoClass: 'pf-dialog-character-info', // class for character info layer (visible on hover)
settingsMainClass: 'pf-dialog-character-main', // class for main character highlighting
settingsNavigationButtonClass: 'pf-dialog-navigation-button', // class for all navigation buttons
settingsFinishButtonClass: 'pf-dialog-finish-button', // class for "finish" button
settingsPrevButtonClass: 'pf-dialog-prev-button', // class for "prev" button
settingsNextButtonClass: 'pf-dialog-next-button', // class for "next" button
settingsCloneApiRowClass: 'pf-dialog-api-row', // class for form row with API data (will be cloned)
settingsCloneRowButtonClass: 'pf-dialog-clone-button', // class for clone button (api row)
settingsDeleteRowButtonClass: 'pf-dialog-delete-button', // class for delete button (api row)
settingsAccountContainerId: 'pf-settings-dialog-account', // id for the "account" container
settingsShareContainerId: 'pf-settings-dialog-share', // id for the "share" container
// captcha
captchaKeyUpdateAccount: 'SESSION.CAPTCHA.ACCOUNT.UPDATE', // key for captcha reason
captchaImageWrapperId: 'pf-dialog-captcha-wrapper', // id for "captcha image" wrapper
captchaImageId: 'pf-dialog-captcha-image', // id for "captcha image"
@@ -35,80 +26,9 @@ define([
icon: {
size: 'fa-2x'
}
},
// character status
settingsCharacterStatusOwn : { // "own" -> my characters
name: 'own',
class: 'pf-user-status-own'
}
};
/**
* get active Tab link element for a dialog
* @param dialog
* @returns {JQuery|*}
*/
var getActiveTabElement = function(dialog){
var navigationBarElement = $(dialog).find('.' + config.dialogWizardNavigationClass);
var currentActiveTab = navigationBarElement.find('li.active');
return currentActiveTab;
};
/**
* init popovers in dialog
* @param dialogElement
*/
var initPopover = function(dialogElement){
var apiCloneButtons = dialogElement.find('.' + config.settingsCloneRowButtonClass);
var apiDeleteButtons = dialogElement.find('.' + config.settingsDeleteRowButtonClass);
var confirmationSettings = {
container: 'body',
placement: 'left',
btnCancelClass: 'btn btn-sm btn-default',
btnCancelLabel: 'cancel',
btnCancelIcon: 'fa fa-fw fa-ban'
};
// add API key row
var cloneConfirmationSettings = $.extend({
title: 'Add additional key',
btnOkClass: 'btn btn-sm btn-success',
btnOkLabel: 'confirm',
btnOkIcon: 'fa fa-fw fa-check',
onConfirm: function(e) {
var cloneRow = dialogElement.find('.' + config.settingsCloneApiRowClass).last();
var newApiRow = cloneRow.clone();
newApiRow.find('.form-group').removeClass('has-success has-error');
newApiRow.find('input').val('');
cloneRow.after(newApiRow);
// init new row with popups
initPopover(dialogElement);
}
}, confirmationSettings);
// delete API key row
var deleteConfirmationSettings = $.extend({
title: 'Delete key',
btnOkClass: 'btn btn-sm btn-danger',
btnOkLabel: 'delete',
btnOkIcon: 'fa fa-fw fa-close',
onConfirm: function(e, target) {
$(target).parents('.row').remove();
}
}, confirmationSettings);
$(apiCloneButtons).confirmation(cloneConfirmationSettings);
$(apiDeleteButtons).confirmation(deleteConfirmationSettings);
};
/**
* show "register/settings" dialog
* @param options
@@ -122,213 +42,124 @@ define([
return false;
}
var reroutePath = '';
// check navigation buttons and show/hide them
var checkNavigationButton = function(dialog){
var navigationBarElement = $(dialog).find('.' + config.dialogWizardNavigationClass);
var currentActiveTab = navigationBarElement.find('li.active');
var hidePrevButton = currentActiveTab.prevAll().length > 0 ? false : true;
var hideNextButton = currentActiveTab.nextAll().length > 0 ? false : true;
if(hidePrevButton){
$('.' + config.settingsPrevButtonClass).hide();
}else{
$('.' + config.settingsPrevButtonClass).show();
}
if(hideNextButton){
$('.' + config.settingsNextButtonClass).hide();
}else{
$('.' + config.settingsNextButtonClass).show();
}
};
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(options.register === 1){
Init.currentUserData = {};
Init.currentUserData.api = [{
keyId: '',
vCode: ''
}];
}else if(Init.currentUserData.api === undefined){
Init.currentUserData.api = [{
keyId: '',
vCode: ''
}];
}
var data = {
id: config.settingsDialogId,
register: options.register === 1 ? 1 : 0,
invite : options.invite === 1 ? 1 : 0,
navigationClass: config.dialogWizardNavigationClass,
settingsAccountContainerId: config.settingsAccountContainerId,
settingsShareContainerId: config.settingsShareContainerId,
userData: Init.currentUserData,
cloneApiRowClass: config.settingsCloneApiRowClass,
cloneRowButtonClass: config.settingsCloneRowButtonClass,
deleteRowButtonClass: config.settingsDeleteRowButtonClass,
captchaImageWrapperId: config.captchaImageWrapperId,
captchaImageId: config.captchaImageId,
formErrorContainerClass: Util.config.formErrorContainerClass,
formWarningContainerClass: Util.config.formWarningContainerClass
ccpImageServer: Init.url.ccpImageServer
};
var content = Mustache.render(template, data);
var selectCharacterDialog = bootbox.dialog({
title: options.register === 1 ? 'Registration' : 'Account settings',
var accountSettingsDialog = bootbox.dialog({
title: 'Account settings',
message: content,
buttons: {
close: {
label: 'finish',
className: ['btn-success', 'pull-right', config.settingsFinishButtonClass].join(' '),
callback: function(e){
if(options.register === 1){
if(reroutePath !== undefined){
// root user to main app
window.location = reroutePath;
}
}else{
// close dialog
return true;
}
}
label: 'cancel',
className: 'btn-default'
},
prev: {
label: '<i class="fa fa-fw fa-angle-left"></i>back',
className: ['btn-default', 'pull-left', config.settingsNavigationButtonClass, config.settingsPrevButtonClass].join(' '),
callback: function (e) {
var currentActiveTab = getActiveTabElement(this);
currentActiveTab.removeClass('finished');
currentActiveTab.prev('li').find('a').tab('show');
success: {
label: '<i class="fa fa-check fa-fw"></i>&nbsp;save',
className: 'btn-success',
callback: function() {
return false;
}
},
next: {
label: 'next<i class="fa fa-fw fa-angle-right"></i>',
className: ['btn-primary', 'pull-right', config.settingsNavigationButtonClass, config.settingsNextButtonClass].join(' '),
callback: function (e) {
var dialogElement = $(this);
var currentActiveTab = getActiveTabElement(dialogElement);
var currentActiveLink = currentActiveTab.find('a');
var tabContentElement = $(currentActiveLink.attr('href'));
var form = tabContentElement.find('form');
var changeTab = function(){
currentActiveTab.addClass('finished');
currentActiveLink.removeClass('btn-danger btn-default');
currentActiveLink.addClass('btn-primary');
currentActiveTab.next('li').find('a').tab('show');
};
// get the current active form
var form = $('#' + config.settingsDialogId).find('form').filter(':visible');
// validate form
form.validator('validate');
// check weather the form is valid
var formValid = form.isValidForm();
if(!formValid){
currentActiveTab.removeClass('disabled');
currentActiveLink.removeClass('btn-default btn-primary');
currentActiveLink.addClass('btn-danger');
}else{
if(formValid === true){
var tabFormValues = form.getFormValues();
if(! $.isEmptyObject(tabFormValues) ){
// send Tab data and store values
var requestData = {
formData: tabFormValues
};
// send Tab data and store values
var requestData = {
settingsData: tabFormValues
};
accountSettingsDialog.find('.modal-content').showLoadingAnimation();
selectCharacterDialog.find('.modal-content').showLoadingAnimation();
$.ajax({
type: 'POST',
url: Init.path.saveUserConfig,
data: requestData,
dataType: 'json'
}).done(function(responseData){
accountSettingsDialog.find('.modal-content').hideLoadingAnimation();
$.ajax({
type: 'POST',
url: Init.path.saveUserConfig,
data: requestData,
dataType: 'json'
}).done(function(responseData){
selectCharacterDialog.find('.modal-content').hideLoadingAnimation();
// set new captcha for any request
// captcha is required for sensitive data (not for all data)
if(
responseData.error &&
responseData.error.length > 0
){
form.showFormMessage(responseData.error);
// set new captcha for any request
// captcha is required for sensitive data (not for all data)
if(
responseData.error &&
responseData.error.length > 0
){
form.showFormMessage(responseData.error);
$('#' + config.captchaImageWrapperId).showCaptchaImage('createAccount', function(){
$('#captcha').resetFormFields();
});
}else{
// store new/updated user data -> update head
if(responseData.userData){
Util.setCurrentUserData(responseData.userData);
}
// store reroute path after registration finished
if(responseData.reroute){
reroutePath = responseData.reroute;
}
dialogElement.find('.alert').velocity('transition.slideDownOut',{
duration: 500,
complete: function(){
// switch tab
changeTab();
$('#' + config.captchaImageWrapperId).showCaptchaImage('createAccount', function(){
$('#captcha').resetFormFields();
});
}
});
Util.showNotify({title: 'Account saved', type: 'success'});
}
}).fail(function( jqXHR, status, error) {
selectCharacterDialog.find('.modal-content').hideLoadingAnimation();
var reason = status + ' ' + error;
Util.showNotify({title: jqXHR.status + ': saveAccount', text: reason, type: 'error'});
// set new captcha for any request
// captcha is required for sensitive data (not for all)
$('#' + config.captchaImageWrapperId).showCaptchaImage('createAccount', function(){
$('#' + config.captchaImageWrapperId).showCaptchaImage(config.captchaKeyUpdateAccount, function(){
$('#captcha').resetFormFields();
});
}else{
// store new/updated user data -> update head
if(responseData.userData){
Util.setCurrentUserData(responseData.userData);
}
// check for DB errors
if(jqXHR.status === 500){
if(jqXHR.responseText){
var errorObj = $.parseJSON(jqXHR.responseText);
if(
errorObj.error &&
errorObj.error.length > 0
){
form.showFormMessage(errorObj.error);
}
form.find('.alert').velocity('transition.slideDownOut',{
duration: 500,
complete: function(){
$('#' + config.captchaImageWrapperId).showCaptchaImage(config.captchaKeyUpdateAccount, function(){
$('#captcha').resetFormFields();
});
}
}
});
if( options.register !== 1 ){
$(document).setProgramStatus('problem');
}
Util.showNotify({title: 'Account saved', type: 'success'});
// close dialog/menu
$(document).trigger('pf:closeMenu', [{}]);
accountSettingsDialog.modal('hide');
}
}).fail(function( jqXHR, status, error) {
accountSettingsDialog.find('.modal-content').hideLoadingAnimation();
var reason = status + ' ' + error;
Util.showNotify({title: jqXHR.status + ': saveAccountSettings', text: reason, type: 'error'});
// set new captcha for any request
// captcha is required for sensitive data (not for all)
$('#' + config.captchaImageWrapperId).showCaptchaImage(config.captchaKeyUpdateAccount, function(){
$('#captcha').resetFormFields();
});
}else{
// no request required -> change tab
changeTab();
}
// check for DB errors
if(jqXHR.status === 500){
if(jqXHR.responseText){
var errorObj = $.parseJSON(jqXHR.responseText);
if(
errorObj.error &&
errorObj.error.length > 0
){
form.showFormMessage(errorObj.error);
}
}
}
$(document).setProgramStatus('problem');
});
}
return false;
@@ -338,163 +169,42 @@ define([
});
// after modal is shown =======================================================================
selectCharacterDialog.on('shown.bs.modal', function(e) {
accountSettingsDialog.on('shown.bs.modal', function(e) {
var dialogElement = $(this);
var tabLinkElements = dialogElement.find('a[data-toggle="tab"]');
var form = dialogElement.find('form');
// request captcha image and show
$('#' + config.captchaImageWrapperId).showCaptchaImage('createAccount');
var captchaImageWrapperContainer = $('#' + config.captchaImageWrapperId);
captchaImageWrapperContainer.showCaptchaImage(config.captchaKeyUpdateAccount);
// init captcha refresh button
captchaImageWrapperContainer.find('i').on('click', function(){
captchaImageWrapperContainer.showCaptchaImage(config.captchaKeyUpdateAccount);
});
// init dialog tooltips
dialogElement.initTooltips();
// init popups
initPopover( dialogElement );
// init form validation
form.initFormValidation();
});
// on Tab switch ======================================================================
tabLinkElements.on('shown.bs.tab', function (e) {
// check navigation buttons (hide/show)
checkNavigationButton(dialogElement);
$(e.target).removeClass('disabled');
// hide finish button
dialogElement.find('.' + config.settingsFinishButtonClass).hide();
if($(e.target).text() < $(e.relatedTarget).text()){
var currentActiveTab = getActiveTabElement(dialogElement);
var nextTabElements = currentActiveTab.nextAll();
// disable all next tabs
currentActiveTab.removeClass('finished');
nextTabElements.removeClass('finished');
nextTabElements.find('a').removeClass('btn-primary btn-danger').addClass('btn-default disabled');
}
if($(e.target).text() === '3'){
// load character tab -----------------------------------------------
requirejs(['text!templates/form/character_panel.html', 'mustache'], function(template, Mustache) {
// all characters for the current user
var characters = Init.currentUserData.characters;
// calculate grid class
var characterCount = characters.length;
var gridClass = ((12 / characterCount) < 4)? 4 : 12 / characterCount ;
// add character status information for each character
var statusInfo = {};
statusInfo.class = config.settingsCharacterStatusOwn.class;
statusInfo.label = config.settingsCharacterStatusOwn.name;
var mainCharacter = 0;
for(var i = 0; i < characters.length; i++){
characters[i].status = statusInfo;
if(characters[i].isMain === 1){
mainCharacter = characters[i].id;
}else if(mainCharacter === 0){
// mark at least one character as "main" if no main char was found
// e.g. first account setup
mainCharacter = characters[i].id;
}
}
var characterTemplateData = {
imageWrapperClass: config.settingsImageWrapperClass,
imageInfoClass: config.settingsImageInfoClass,
imageWrapperMainClass: config.settingsMainClass,
charactersData: characters,
gridClass: 'col-sm-' + gridClass,
mainCharacter: mainCharacter
};
var content = Mustache.render(template, characterTemplateData);
var characterForm = dialogElement.find('#pf-dialog-settings-character form');
// add form HTML
characterForm.html(content);
var imageWrapperElements = dialogElement.find('.' + config.settingsImageWrapperClass);
// special effects :)
imageWrapperElements.velocity('stop').delay(100).velocity('transition.flipBounceXIn', {
display: 'inline-block',
stagger: 60,
drag: true,
duration: 400,
complete: function(){
// init new character tooltips
dialogElement.initTooltips();
}
});
// Hover effect for character info layer
imageWrapperElements.hoverIntent(function(e){
var characterInfoElement = $(this).find('.' + config.settingsImageInfoClass);
characterInfoElement.velocity('finish').velocity({
width: ['100%', [ 400, 15 ] ]
},{
easing: 'easeInSine'
});
}, function(e){
var characterInfoElement = $(this).find('.' + config.settingsImageInfoClass);
characterInfoElement.velocity('finish').velocity({
width: 0
},{
duration: 150,
easing: 'easeInOutSine'
});
});
// click event on character image
imageWrapperElements.on('click', function(e){
var wrapperElement = $(this);
var characterId = wrapperElement.data('id');
// update layout if character is selected
if(characterId > 0){
// update hidden field with new mainCharacterId
dialogElement.find('input[name="mainCharacterId"]').val(characterId);
imageWrapperElements.removeClass( config.settingsMainClass );
wrapperElement.addClass( config.settingsMainClass );
}
});
});
}else if($(e.target).text() === '4'){
// show finish button
dialogElement.find('.' + config.settingsFinishButtonClass).show();
// show success message
dialogElement.find('h1').velocity('stop').delay(200).velocity('transition.flipBounceXIn', {
duration: 500
}).delay(100).velocity('callout.pulse');
}
// events for tab change
accountSettingsDialog.find('.navbar a').on('shown.bs.tab', function(e){
// init "toggle" switches on current active tab
accountSettingsDialog.find( $(this).attr('href') ).find('input[type="checkbox"]').bootstrapToggle({
on: '<i class="fa fa-fw fa-check"></i>&nbsp;Enable',
off: 'Disable&nbsp;<i class="fa fa-fw fa-ban"></i>',
onstyle: 'success',
offstyle: 'warning',
width: 90,
height: 30
});
});
});
};
});

View File

@@ -15,6 +15,7 @@ define([
deleteAccountId: 'pf-dialog-delete-account', // dialog id
// captcha
captchaKeyDeleteAccount: 'SESSION.CAPTCHA.ACCOUNT.DELETE', // key for captcha reason
captchaImageWrapperId: 'pf-dialog-captcha-wrapper' // id for "captcha image" wrapper
};
@@ -82,8 +83,8 @@ define([
){
form.showFormMessage(responseData.error);
$('#' + config.captchaImageWrapperId).showCaptchaImage('deleteAccount', function(){
form.find('[name="captcha"], [name="password"]').resetFormFields();
$('#' + config.captchaImageWrapperId).showCaptchaImage(config.captchaKeyDeleteAccount, function(){
form.find('[name="captcha"]').resetFormFields();
});
}
@@ -106,7 +107,7 @@ define([
// after modal is shown =======================================================================
deleteAccountDialog.on('shown.bs.modal', function(e) {
// request captcha image and show
$('#' + config.captchaImageWrapperId).showCaptchaImage('deleteAccount');
$('#' + config.captchaImageWrapperId).showCaptchaImage(config.captchaKeyDeleteAccount);
});
});

View File

@@ -643,7 +643,6 @@ define([
}
}
var userDataTable = userTable.dataTable( {
pageLength: 20,
paging: true,
@@ -670,7 +669,7 @@ define([
data: 'log.ship',
render: {
_: function(data, type, row, meta){
return '<img src="' + Init.url.ccpImageServer + 'Render/' + data.id + '_32.png" />';
return '<img src="' + Init.url.ccpImageServer + 'Render/' + data.typeId + '_32.png" />';
}
}
},{
@@ -736,6 +735,16 @@ define([
}
},{
targets: 7,
title: 'station',
orderable: true,
searchable: true,
data: 'log.station',
render: {
_: 'name',
sort: 'name'
}
},{
targets: 8,
title: '',
orderable: false,
searchable: false,
@@ -754,7 +763,7 @@ define([
});
}
},{
targets: 8,
targets: 9,
title: '',
orderable: false,
searchable: false,

View File

@@ -20,7 +20,7 @@ define([
dialogMapSettingsContainerId: 'pf-map-dialog-settings', // id for the "settings" container
dialogMapDownloadContainerId: 'pf-map-dialog-download', // id for the "download" container
userSelectId: 'pf-map-dialog-user-select', // id for "user" select
characterSelectId: 'pf-map-dialog-character-select', // id for "character" select
corporationSelectId: 'pf-map-dialog-corporation-select', // id for "corporation" select
allianceSelectId: 'pf-map-dialog-alliance-select', // id for "alliance" select
@@ -100,7 +100,7 @@ define([
contentEditMap = $(contentEditMap);
// current map access info
var accessUser = [];
var accessCharacter = [];
var accessCorporation = [];
var accessAlliance = [];
@@ -112,7 +112,7 @@ define([
contentEditMap.find('select[name="scopeId"]').val( mapData.config.scope.id );
contentEditMap.find('select[name="typeId"]').val( mapData.config.type.id );
accessUser = mapData.config.access.user;
accessCharacter = mapData.config.access.character;
accessCorporation = mapData.config.access.corporation;
accessAlliance = mapData.config.access.alliance;
}
@@ -145,17 +145,17 @@ define([
hideDownloadTab: hideDownloadTab,
// settings tab --------------
userSelectId: config.userSelectId,
characterSelectId: config.characterSelectId,
corporationSelectId: config.corporationSelectId,
allianceSelectId: config.allianceSelectId,
// map access objects --------
accessUser: accessUser,
accessCharacter: accessCharacter,
accessCorporation: accessCorporation,
accessAlliance: accessAlliance,
// access limitations --------
maxUser: Init.maxSharedCount.user,
maxCharacter: Init.maxSharedCount.character,
maxCorporation: Init.maxSharedCount.corporation,
maxAlliance: Init.maxSharedCount.alliance,
@@ -287,7 +287,7 @@ define([
// events for tab change
mapInfoDialog.find('.navbar a').on('shown.bs.tab', function(e){
var selectElementUser = mapInfoDialog.find('#' + config.userSelectId);
var selectElementCharacter = mapInfoDialog.find('#' + config.characterSelectId);
var selectElementCorporation = mapInfoDialog.find('#' + config.corporationSelectId);
var selectElementAlliance = mapInfoDialog.find('#' + config.allianceSelectId);
@@ -295,8 +295,8 @@ define([
// "settings" tab
initSettingsSelectFields(mapInfoDialog);
}else{
if( $(selectElementUser).data('select2') !== undefined ){
$(selectElementUser).select2('destroy');
if( $(selectElementCharacter).data('select2') !== undefined ){
$(selectElementCharacter).select2('destroy');
}
if( $(selectElementCorporation).data('select2') !== undefined ){
@@ -550,14 +550,14 @@ define([
*/
var initSettingsSelectFields = function(mapInfoDialog){
var selectElementUser = mapInfoDialog.find('#' + config.userSelectId);
var selectElementCharacter = mapInfoDialog.find('#' + config.characterSelectId);
var selectElementCorporation = mapInfoDialog.find('#' + config.corporationSelectId);
var selectElementAlliance = mapInfoDialog.find('#' + config.allianceSelectId);
// init corporation select live search
selectElementUser.initAccessSelect({
type: 'user',
maxSelectionLength: Init.maxSharedCount.user
// init character select live search
selectElementCharacter.initAccessSelect({
type: 'character',
maxSelectionLength: Init.maxSharedCount.character
});
// init corporation select live search
@@ -575,7 +575,7 @@ define([
/**
* shows the delete map Dialog
* @param mapElement
* @param mapData
*/
$.fn.showDeleteMapDialog = function(mapData){

View File

@@ -47,8 +47,6 @@ define([
releasesDialog.find('ul.timeline').append(content);
}
// console.log()
$('.timeline > li').velocity('transition.expandIn', {
stagger: 300,
duration: 240,

View File

@@ -1,109 +0,0 @@
/**
* sharing settings dialog
*/
define([
'jquery',
'app/init',
'app/util',
'app/render',
'bootbox'
], function($, Init, Util, Render, bootbox) {
'use strict';
var config = {
// select character dialog
sharingDialogId: 'pf-sharing-dialog', // id for "sharing settings" dialog
};
$.fn.showSharingSettingsDialog = function(){
var sharingDialogElement = $('#' + config.sharingDialogId);
if(!sharingDialogElement.is(':visible')){
var userData = Util.getCurrentUserData();
if(userData){
requirejs([
'text!templates/dialog/sharing_settings.html',
'mustache'
], function(templatSharingDialog, Mustache) {
var data = {
id: config.sharingDialogId,
ccpImageServer: Init.url.ccpImageServer,
userData: userData
};
// render "new map" tab content -------------------------------------------
var contentSharingDialog = Mustache.render(templatSharingDialog, data);
var sharingSettingsDialog = bootbox.dialog({
title: 'Sharing settings',
message: $(contentSharingDialog),
buttons: {
close: {
label: 'cancel',
className: 'btn-default'
},
success: {
label: '<i class="fa fa-check fa-fw"></i>&nbsp;save',
className: 'btn-success',
callback: function() {
var form = $('#' + config.sharingDialogId).find('form');
var sharingSettingsData = {formData: form.getFormValues()};
$.ajax({
type: 'POST',
url: Init.path.saveSharingConfig,
data: sharingSettingsData,
dataType: 'json'
}).done(function(data){
if(data.userData !== undefined){
// store current user data global (cache)
Util.setCurrentUserData(data.userData);
$(document).trigger('pf:closeMenu', [{}]);
$(sharingSettingsDialog).modal('hide');
// success
Util.showNotify({title: 'Sharing settings saved', type: 'success'});
}
}).fail(function( jqXHR, status, error) {
var reason = status + ' ' + error;
Util.showNotify({title: jqXHR.status + ': shareSettings', text: reason, type: 'warning'});
$(document).setProgramStatus('problem');
});
return false;
}
}
}
});
// after modal is shown ---------------------------------------------------
sharingSettingsDialog.on('shown.bs.modal', function(e) {
$(this).find('input[type="checkbox"]').bootstrapToggle({
on: '<i class="fa fa-fw fa-check"></i>&nbsp;Enable',
off: 'Disable&nbsp;<i class="fa fa-fw fa-ban"></i>',
onstyle: 'success',
offstyle: 'warning',
width: 90,
height: 30
});
});
});
}else{
Util.showNotify({title: 'No userData found', type: 'warning'});
}
}
};
});

View File

@@ -131,7 +131,7 @@ define([
/**
* init a select element as an ajax based "select2" object for Access resources
* user (private map), corporation (corp map), alliance (ally map)
* character (private map), corporation (corp map), alliance (ally map)
* @param options
*/
$.fn.initAccessSelect = function(options){
@@ -162,8 +162,9 @@ define([
var previewContent = '';
switch(options.type){
case 'user':
previewContent = '<i class="fa fa-lg fa-user"></i>';
case 'character':
imagePath = Init.url.ccpImageServer + 'Character/' + data.id + '_32.jpg';
previewContent = '<img src="' + imagePath + '" style="max-width: 100%" />';
break;
case 'corporation':
imagePath = Init.url.ccpImageServer + 'Corporation/' + data.id + '_32.png';

View File

@@ -150,57 +150,58 @@ define([
dataType: 'json'
}).done(function(systemGraphsData){
// create new (hidden) module container
var moduleElement = $('<div>', {
class: [config.moduleClass, config.systemGraphModuleClass].join(' '),
css: {opacity: 0}
});
// insert at the correct position
if($(parentElement).children().length === 1){
$(parentElement).append(moduleElement);
}else{
$(parentElement).find('>:first-child').after(moduleElement);
}
// row element
var rowElement = $('<div>', {
class: 'row'
});
moduleElement.append(rowElement);
$.each(systemGraphsData, function(systemId, graphsData){
$.each(graphsData, function(graphKey, graphData){
var colElement = $('<div>', {
class: ['col-xs-12', 'col-sm-6', 'col-md-4'].join(' ')
});
var headlineElement = $('<h5>').text( getInfoForGraph(graphKey, 'headline') );
colElement.append(headlineElement);
var graphElement = $('<div>', {
class: config.systemGraphClass
});
colElement.append(graphElement);
rowElement.append(colElement);
initGraph(graphElement, graphKey, graphData, eventLine);
if(systemGraphsData.length > 0){
// create new (hidden) module container
var moduleElement = $('<div>', {
class: [config.moduleClass, config.systemGraphModuleClass].join(' '),
css: {opacity: 0}
});
});
moduleElement.append($('<div>', {
css: {'clear': 'both'}
}));
// insert at the correct position
if($(parentElement).children().length === 1){
$(parentElement).append(moduleElement);
}else{
$(parentElement).find('>:first-child').after(moduleElement);
}
// show module
moduleElement.velocity('transition.slideDownIn', {
duration: Init.animationSpeed.mapModule,
delay: Init.animationSpeed.mapModule
});
// row element
var rowElement = $('<div>', {
class: 'row'
});
moduleElement.append(rowElement);
$.each(systemGraphsData, function(systemId, graphsData){
$.each(graphsData, function(graphKey, graphData){
var colElement = $('<div>', {
class: ['col-xs-12', 'col-sm-6', 'col-md-4'].join(' ')
});
var headlineElement = $('<h5>').text( getInfoForGraph(graphKey, 'headline') );
colElement.append(headlineElement);
var graphElement = $('<div>', {
class: config.systemGraphClass
});
colElement.append(graphElement);
rowElement.append(colElement);
initGraph(graphElement, graphKey, graphData, eventLine);
});
});
moduleElement.append($('<div>', {
css: {'clear': 'both'}
}));
// show module
moduleElement.velocity('transition.slideDownIn', {
duration: Init.animationSpeed.mapModule,
delay: Init.animationSpeed.mapModule
});
}
}).fail(function( jqXHR, status, error) {
var reason = status + ' ' + error;
Util.showNotify({title: jqXHR.status + ': System graph data', text: reason, type: 'warning'});

View File

@@ -333,34 +333,34 @@ define([
$.ajax({
url: url,
type: 'GET',
dataType: 'jsonp'
}).done(function(kbData){
dataType: 'json'
}).done(function(kbData) {
// the API wont return more than 200KMs ! - remember last bar block with complete KM information
var lastCompleteDiffHourData = 0;
// loop kills and count kills by hour
for(var i = 0; i < kbData.length; i++){
for (var i = 0; i < kbData.length; i++) {
var killmailData = kbData[i];
var killDate = getDateObjectByTimeString(killmailData.killTime);
// get time diff
var timeDiffMin = Math.round( ( serverDate - killDate ) / 1000 / 60 );
var timeDiffHour = Math.round( timeDiffMin / 60 );
var timeDiffMin = Math.round(( serverDate - killDate ) / 1000 / 60);
var timeDiffHour = Math.round(timeDiffMin / 60);
// update chart data
if(chartData[timeDiffHour]){
if (chartData[timeDiffHour]) {
chartData[timeDiffHour].kills++;
// add kill mail data
if(chartData[timeDiffHour].killmails === undefined){
if (chartData[timeDiffHour].killmails === undefined) {
chartData[timeDiffHour].killmails = [];
}
chartData[timeDiffHour].killmails.push(killmailData);
if(timeDiffHour > lastCompleteDiffHourData){
if (timeDiffHour > lastCompleteDiffHourData) {
lastCompleteDiffHourData = timeDiffHour;
}
}
@@ -368,7 +368,7 @@ define([
}
// remove empty chart Data
if(kbData.length >= maxKillmailCount){
if (kbData.length >= maxKillmailCount) {
chartData = chartData.splice(0, lastCompleteDiffHourData + 1);
}
@@ -378,7 +378,7 @@ define([
cache.systemKillsGraphData[cacheKey].count = kbData.length;
// draw table
drawGraph( cache.systemKillsGraphData[cacheKey] );
drawGraph(cache.systemKillsGraphData[cacheKey]);
// show killmail information
showKillmails(moduleElement, cache.systemKillsGraphData[cacheKey]);

View File

@@ -1634,6 +1634,10 @@ define([
// submit all fields within a table row
var formFields = rowElement.find('.editable');
// the "toggle" makes sure to take care about open editable fields (e.g. description)
// otherwise, changes would not be submitted in this field (not necessary)
formFields.editable('toggle');
// submit all xEditable fields
formFields.editable('submit', {
url: Init.path.saveSignatureData,

View File

@@ -317,15 +317,17 @@ define([
/**
* init form elements for validation (bootstrap3 validation)
* @returns {any|JQuery|*}
* @param options
* @returns {*}
*/
$.fn.initFormValidation = function(){
$.fn.initFormValidation = function(options){
options = (typeof options === 'undefined')? {} : options;
return this.each(function(){
var form = $(this);
// init form validation
form.validator();
form.validator(options);
// validation event listener
form.on('valid.bs.validator', function(validatorObj){
@@ -342,7 +344,6 @@ define([
inputGroup.removeClass('has-success').addClass('has-error');
}
});
});
};
@@ -545,6 +546,84 @@ define([
}
};
/**
* add character switch popover
* @param userData
*/
$.fn.initCharacterSwitchPopover = function(userData){
var elements = $(this);
var eventNamespace = 'hideCharacterPopup';
requirejs(['text!templates/tooltip/character_switch.html', 'mustache'], function (template, Mustache) {
var data = {
routes: Init.routes,
userData: userData,
otherCharacters: $.grep( userData.characters, function( character ) {
// exclude current active character
return character.id !== userData.character.id;
})
};
var content = Mustache.render(template, data);
return elements.each(function() {
var element = $(this);
// destroy "popover" and remove "click" event for animation
element.popover('destroy').off();
// init popover and add specific class to it (for styling)
element.popover({
html: true,
title: 'select character',
trigger: 'click',
placement: 'bottom',
content: content,
animation: false
}).data('bs.popover').tip().addClass('pf-character-info-popover');
element.on('click', function(e) {
e.preventDefault();
var easeEffect = $(this).attr('data-easein');
var popover = $(this).data('bs.popover').tip();
var velocityOptions = {
duration: CCP.isInGameBrowser() ? 0 : Init.animationSpeed.dialogEvents
};
switch(easeEffect){
case 'shake':
case 'pulse':
case 'tada':
case 'flash':
case 'bounce':
case 'swing':
popover.velocity('callout.' + easeEffect, velocityOptions);
break;
default:
popover.velocity('transition.' + easeEffect, velocityOptions);
break;
}
});
// hide popup on clicking "somewhere" outside
$('body').off('click.' + eventNamespace).on('click.' + eventNamespace, function (e) {
//the 'is' for buttons that trigger popups
//the 'has' for icons within a button that triggers a popup
if (
!$(element).is(e.target) &&
$(element).has(e.target).length === 0 &&
$('.popover').has(e.target).length === 0
){
$(element).popover('hide');
}
});
});
});
};
/**
* add a wormhole tooltip with wh specific data to elements
* @param tooltipData
@@ -575,7 +654,6 @@ define([
var popover = element.data('bs.popover');
popover.options.content = content;
});
});
};
@@ -1129,7 +1207,6 @@ define([
* @returns {string}
*/
var getSystemEffectTable = function(data){
var table = '';
if(data.length > 0){

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 199 KiB

After

Width:  |  Height:  |  Size: 144 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 224 KiB

After

Width:  |  Height:  |  Size: 174 KiB

View File

@@ -1,2 +0,0 @@
var mainScriptPath=document.body.getAttribute("data-script"),jsBaseUrl=document.body.getAttribute("data-js-path");requirejs.config({baseUrl:"js",paths:{layout:"layout",config:"app/config",dialog:"app/ui/dialog",templates:"../../templates",img:"../../img",login:"./app/login",mappage:"./app/mappage",setup:"./app/setup",jquery:"lib/jquery-1.11.3.min",bootstrap:"lib/bootstrap.min",text:"lib/requirejs/text",mustache:"lib/mustache.min",velocity:"lib/velocity.min",velocityUI:"lib/velocity.ui.min",slidebars:"lib/slidebars",jsPlumb:"lib/dom.jsPlumb-1.7.6",farahey:"lib/farahey-0.5",customScrollbar:"lib/jquery.mCustomScrollbar.concat.min",datatables:"lib/datatables/jquery.dataTables.min",datatablesResponsive:"lib/datatables/extensions/responsive/dataTables.responsive",datatablesTableTools:"lib/datatables/extensions/tabletools/js/dataTables.tableTools",xEditable:"lib/bootstrap-editable.min",morris:"lib/morris.min",raphael:"lib/raphael-min",bootbox:"lib/bootbox.min",easyPieChart:"lib/jquery.easypiechart.min",dragToSelect:"lib/jquery.dragToSelect",hoverIntent:"lib/jquery.hoverIntent.minified",fullScreen:"lib/jquery.fullscreen.min",select2:"lib/select2.min",validator:"lib/validator.min",lazylinepainter:"lib/jquery.lazylinepainter-1.5.1.min",blueImpGallery:"lib/blueimp-gallery",blueImpGalleryHelper:"lib/blueimp-helper",blueImpGalleryBootstrap:"lib/bootstrap-image-gallery",bootstrapConfirmation:"lib/bootstrap-confirmation",bootstrapToggle:"lib/bootstrap2-toggle.min",lazyload:"lib/jquery.lazyload.min",easePack:"lib/EasePack.min",tweenLite:"lib/TweenLite.min",pnotify:"lib/pnotify/pnotify.core","pnotify.buttons":"lib/pnotify/pnotify.buttons","pnotify.confirm":"lib/pnotify/pnotify.confirm","pnotify.nonblock":"lib/pnotify/pnotify.nonblock","pnotify.desktop":"lib/pnotify/pnotify.desktop","pnotify.history":"lib/pnotify/pnotify.history","pnotify.callbacks":"lib/pnotify/pnotify.callbacks","pnotify.reference":"lib/pnotify/pnotify.reference"},shim:{bootstrap:{deps:["jquery"]},farahey:{deps:["jsPlumb"]},velocity:{deps:["jquery"]},velocityUI:{deps:["velocity"]},slidebars:{deps:["jquery"]},customScrollbar:{deps:["jquery"]},datatables:{deps:["jquery"]},datatablesBootstrap:{deps:["datatables"]},datatablesResponsive:{deps:["datatables"]},datatablesTableTools:{deps:["datatables"]},xEditable:{deps:["bootstrap"]},bootbox:{deps:["jquery","bootstrap"],exports:"bootbox"},morris:{deps:["jquery","raphael"],exports:"Morris"},pnotify:{deps:["jquery"]},easyPieChart:{deps:["jquery"]},dragToSelect:{deps:["jquery"]},hoverIntent:{deps:["jquery"]},fullScreen:{deps:["jquery"]},select2:{deps:["jquery"],exports:"Select2"},validator:{deps:["jquery","bootstrap"]},lazylinepainter:{deps:["jquery","bootstrap"]},blueImpGallery:{deps:["jquery"]},bootstrapConfirmation:{deps:["bootstrap"]},bootstrapToggle:{deps:["jquery"]},lazyload:{deps:["jquery"]}}});require.config({baseUrl:jsBaseUrl});requirejs([mainScriptPath]);
//# sourceMappingURL=app.js.map

View File

@@ -1 +0,0 @@
{"version":3,"sources":["app.js.src.js"],"names":["mainScriptPath","document","body","getAttribute","jsBaseUrl","requirejs","config","baseUrl","paths","layout","dialog","templates","img","login","mappage","setup","jquery","bootstrap","text","mustache","velocity","velocityUI","slidebars","jsPlumb","farahey","customScrollbar","datatables","datatablesResponsive","datatablesTableTools","xEditable","morris","raphael","bootbox","easyPieChart","dragToSelect","hoverIntent","fullScreen","select2","validator","lazylinepainter","blueImpGallery","blueImpGalleryHelper","blueImpGalleryBootstrap","bootstrapConfirmation","bootstrapToggle","lazyload","easePack","tweenLite","pnotify","pnotify.buttons","pnotify.confirm","pnotify.nonblock","pnotify.desktop","pnotify.history","pnotify.callbacks","pnotify.reference","shim","deps","datatablesBootstrap","exports","require"],"mappings":"AACA,GAAIA,gBAAiBC,SAASC,KAAKC,aAAa,eAI5CC,UAAYH,SAASC,KAAKC,aAAa,eAG3CE,WAAUC,QACNC,QAAS,KAETC,OACIC,OAAQ,SACRH,OAAQ,aACRI,OAAQ,gBACRC,UAAW,kBACXC,IAAK,YAGLC,MAAO,cACPC,QAAS,gBACTC,MAAO,cAEPC,OAAQ,wBACRC,UAAW,oBACXC,KAAM,qBACNC,SAAU,mBACVC,SAAU,mBACVC,WAAY,sBACZC,UAAW,gBACXC,QAAS,wBACTC,QAAS,kBACTC,gBAAiB,yCACjBC,WAAY,uCAEZC,qBAAsB,6DAEtBC,qBAAsB,gEACtBC,UAAW,6BACXC,OAAQ,iBACRC,QAAS,kBACTC,QAAS,kBACTC,aAAc,8BACdC,aAAc,0BACdC,YAAa,kCACbC,WAAY,4BACZC,QAAS,kBACTC,UAAW,oBACXC,gBAAiB,uCACjBC,eAAgB,sBAChBC,qBAAsB,qBACtBC,wBAAyB,8BACzBC,sBAAuB,6BACvBC,gBAAiB,4BACjBC,SAAU,0BAGVC,SAAU,mBACVC,UAAW,oBAGXC,QAAS,2BACTC,kBAAmB,8BACnBC,kBAAmB,8BACnBC,mBAAoB,+BACpBC,kBAAmB,8BACnBC,kBAAmB,8BACnBC,oBAAqB,gCACrBC,oBAAqB,iCAGzBC,MACIvC,WACIwC,MAAO,WAEXjC,SACIiC,MAAO,YAEXrC,UACIqC,MAAO,WAEXpC,YACIoC,MAAO,aAEXnC,WACImC,MAAO,WAEXhC,iBACIgC,MAAO,WAEX/B,YACI+B,MAAO,WAEXC,qBACID,MAAO,eAEX9B,sBACI8B,MAAO,eAEX7B,sBACI6B,MAAO,eAEX5B,WACI4B,MAAO,cAEXzB,SACIyB,MAAO,SAAU,aACjBE,QAAS,WAEb7B,QACI2B,MAAO,SAAU,WACjBE,QAAS,UAEbX,SACIS,MAAQ,WAEZxB,cACIwB,MAAQ,WAEZvB,cACIuB,MAAQ,WAEZtB,aACIsB,MAAQ,WAEZrB,YACIqB,MAAQ,WAEZpB,SACIoB,MAAQ,UACRE,QAAS,WAEbrB,WACImB,MAAQ,SAAU,cAEtBlB,iBACIkB,MAAQ,SAAU,cAEtBjB,gBACIiB,MAAQ,WAEZd,uBACIc,MAAQ,cAEZb,iBACIa,MAAQ,WAEZZ,UACIY,MAAQ,aAQpBG,SAAQtD,QACJC,QAASH,WAIbC,YAAYL","file":"app.js.map"}

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

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,132 +0,0 @@
app/login.js
----------------
lib/jquery-1.11.3.min.js
app/init.js
app/config/system_effect.js
app/config/signature_type.js
lib/bootstrap.min.js
lib/bootbox.min.js
app/ccp.js
lib/velocity.min.js
lib/velocity.ui.min.js
lib/jquery.mCustomScrollbar.concat.min.js
lib/validator.min.js
lib/bootstrap-editable.min.js
lib/jquery.easypiechart.min.js
lib/jquery.hoverIntent.minified.js
lib/bootstrap-confirmation.js
lib/bootstrap2-toggle.min.js
app/util.js
lib/mustache.min.js
app/render.js
lib/blueimp-helper.js
lib/blueimp-gallery.js
lib/jquery.lazyload.min.js
lib/EasePack.min.js
lib/TweenLite.min.js
app/ui/header.js
lib/jquery.lazylinepainter-1.5.1.min.js
app/ui/logo.js
app/ui/demo_map.js
app/ui/dialog/account_settings.js
app/ui/dialog/notification.js
app/ui/dialog/manual.js
app/ui/dialog/releases.js
app/ui/dialog/credit.js
app/login.js
lib/requirejs/text.js
app/mappage.js
----------------
lib/jquery-1.11.3.min.js
app/init.js
app/config/system_effect.js
app/config/signature_type.js
lib/bootstrap.min.js
lib/bootbox.min.js
app/ccp.js
lib/velocity.min.js
lib/velocity.ui.min.js
lib/jquery.mCustomScrollbar.concat.min.js
lib/validator.min.js
lib/bootstrap-editable.min.js
lib/jquery.easypiechart.min.js
lib/jquery.hoverIntent.minified.js
lib/bootstrap-confirmation.js
lib/bootstrap2-toggle.min.js
app/util.js
lib/mustache.min.js
app/render.js
app/logging.js
lib/requirejs/text.js
text!img/logo.svg!strip
text!templates/modules/header.html
text!templates/modules/footer.html
app/ui/dialog/notification.js
app/ui/dialog/trust.js
app/ui/dialog/sharing_settings.js
app/ui/dialog/map_info.js
app/ui/dialog/account_settings.js
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
lib/slidebars.js
app/counter.js
app/ui/system_info.js
lib/raphael-min.js
lib/morris.min.js
app/ui/system_graph.js
app/ui/system_signature.js
app/ui/system_route.js
app/ui/system_killboard.js
lib/datatables/jquery.dataTables.min.js
lib/datatables/extensions/tabletools/js/dataTables.tableTools.js
lib/datatables/extensions/responsive/dataTables.responsive.js
lib/dom.jsPlumb-1.7.6.js
lib/farahey-0.5.js
app/map/magnetizing.js
lib/jquery.dragToSelect.js
lib/select2.min.js
app/map/contextmenu.js
app/map/overlay.js
app/map/map.js
app/module_map.js
app/page.js
app/ui/form_element.js
app/mappage.js
app/setup.js
----------------
lib/jquery-1.11.3.min.js
app/init.js
app/config/system_effect.js
app/config/signature_type.js
lib/bootstrap.min.js
lib/bootbox.min.js
app/ccp.js
lib/velocity.min.js
lib/velocity.ui.min.js
lib/jquery.mCustomScrollbar.concat.min.js
lib/validator.min.js
lib/bootstrap-editable.min.js
lib/jquery.easypiechart.min.js
lib/jquery.hoverIntent.minified.js
lib/bootstrap-confirmation.js
lib/bootstrap2-toggle.min.js
app/util.js
app/setup.js
app/notification.js
----------------
app/init.js
lib/pnotify/pnotify.core.js
lib/pnotify/pnotify.nonblock.js
lib/pnotify/pnotify.desktop.js
lib/pnotify/pnotify.callbacks.js
app/notification.js

View File

@@ -1,2 +0,0 @@
!function(t){"use strict";"function"==typeof define&&define.amd?define(["jquery","blueImpGallery"],t):t(window.jQuery,window.blueimp.Gallery)}(function(t,o){"use strict";t.extend(o.prototype.options,{useBootstrapModal:!0});var e=o.prototype.close,n=o.prototype.imageFactory,i=o.prototype.videoFactory,r=o.prototype.textFactory;t.extend(o.prototype,{modalFactory:function(o,e,n,i){if(!this.options.useBootstrapModal||n)return i.call(this,o,e,n);var r=this,a=t(this.container).children(".modal"),c=a.clone().show().on("click",function(t){if(t.target===c[0]||t.target===c.children()[0]){t.preventDefault();t.stopPropagation();r.close()}}),l=i.call(this,o,function(t){e({type:t.type,target:c[0]});c.addClass("in")},n);c.find(".modal-title").text(l.title||String.fromCharCode(160));c.find(".modal-body").append(l);return c[0]},imageFactory:function(t,o,e){return this.modalFactory(t,o,e,n)},videoFactory:function(t,o,e){return this.modalFactory(t,o,e,i)},textFactory:function(t,o,e){return this.modalFactory(t,o,e,r)},close:function(){this.container.find(".modal").removeClass("in");e.call(this)}})});
//# sourceMappingURL=bootstrap-image-gallery.js.map

View File

@@ -1 +0,0 @@
{"version":3,"sources":["bootstrap-image-gallery.js.src.js"],"names":["factory","define","amd","window","jQuery","blueimp","Gallery","$","extend","prototype","options","useBootstrapModal","close","imageFactory","videoFactory","textFactory","modalFactory","obj","callback","factoryInterface","this","call","that","modalTemplate","container","children","modal","clone","show","on","event","target","preventDefault","stopPropagation","element","type","addClass","find","text","title","String","fromCharCode","append","removeClass"],"mappings":"CAaC,SAAUA,GACP,YACsB,mBAAXC,SAAyBA,OAAOC,IAEvCD,QACI,SACA,kBACDD,GAEHA,EACIG,OAAOC,OACPD,OAAOE,QAAQC,UAGzB,SAAUC,EAAGD,GACX,YAEAC,GAAEC,OAAOF,EAAQG,UAAUC,SACvBC,mBAAmB,GAGvB,IAAIC,GAAQN,EAAQG,UAAUG,MAC1BC,EAAeP,EAAQG,UAAUI,aACjCC,EAAeR,EAAQG,UAAUK,aACjCC,EAAcT,EAAQG,UAAUM,WAEpCR,GAAEC,OAAOF,EAAQG,WAEbO,aAAc,SAAUC,EAAKC,EAAUC,EAAkBnB,GACrD,IAAKoB,KAAKV,QAAQC,mBAAqBQ,EACnC,MAAOnB,GAAQqB,KAAKD,KAAMH,EAAKC,EAAUC,EAE7C,IAAIG,GAAOF,KACPG,EAAgBhB,EAAEa,KAAKI,WAAWC,SAAS,UAC3CC,EAAQH,EAAcI,QAAQC,OACzBC,GAAG,QAAS,SAAUC,GAEnB,GAAIA,EAAMC,SAAWL,EAAM,IACnBI,EAAMC,SAAWL,EAAMD,WAAW,GAAI,CAC1CK,EAAME,gBACNF,GAAMG,iBACNX,GAAKV,WAGjBsB,EAAUlC,EAAQqB,KAAKD,KAAMH,EAAK,SAAUa,GACxCZ,GACIiB,KAAML,EAAMK,KACZJ,OAAQL,EAAM,IAElBA,GAAMU,SAAS,OAChBjB,EACPO,GAAMW,KAAK,gBAAgBC,KAAKJ,EAAQK,OAASC,OAAOC,aAAa,KACrEf,GAAMW,KAAK,eAAeK,OAAOR,EACjC,OAAOR,GAAM,IAGjBb,aAAc,SAAUI,EAAKC,EAAUC,GACnC,MAAOC,MAAKJ,aAAaC,EAAKC,EAAUC,EAAkBN,IAG9DC,aAAc,SAAUG,EAAKC,EAAUC,GACnC,MAAOC,MAAKJ,aAAaC,EAAKC,EAAUC,EAAkBL,IAG9DC,YAAa,SAAUE,EAAKC,EAAUC,GAClC,MAAOC,MAAKJ,aAAaC,EAAKC,EAAUC,EAAkBJ,IAG9DH,MAAO,WACHQ,KAAKI,UAAUa,KAAK,UAAUM,YAAY,KAC1C/B,GAAMS,KAAKD","file":"bootstrap-image-gallery.js.map"}

View File

@@ -1,2 +0,0 @@
!function(e,t,a){var n=function(e,t){"use strict";e.extend(!0,t.defaults,{dom:"<'row'<'col-xs-6'l><'col-xs-6'f>r><'row'<'col-xs-12't>><'row'<'col-xs-6'i><'col-xs-6'p>>",renderer:"bootstrap"});e.extend(t.ext.classes,{sWrapper:"dataTables_wrapper form-inline dt-bootstrap",sFilterInput:"form-control input-sm",sLengthSelect:"form-control input-sm"});t.ext.renderer.pageButton.bootstrap=function(a,n,o,s,r,i){var l,d,c=new t.Api(a),u=a.oClasses,b=a.oLanguage.oPaginate,p=function(t,n){var s,f,T,x,m=function(t){t.preventDefault();e(t.currentTarget).hasClass("disabled")||c.page(t.data.action).draw(!1)};for(s=0,f=n.length;f>s;s++){x=n[s];if(e.isArray(x))p(t,x);else{l="";d="";switch(x){case"ellipsis":l="&hellip;";d="disabled";break;case"first":l=b.sFirst;d=x+(r>0?"":" disabled");break;case"previous":l=b.sPrevious;d=x+(r>0?"":" disabled");break;case"next":l=b.sNext;d=x+(i-1>r?"":" disabled");break;case"last":l=b.sLast;d=x+(i-1>r?"":" disabled");break;default:l=x+1;d=r===x?"active":""}if(l){T=e("<li>",{"class":u.sPageButton+" "+d,"aria-controls":a.sTableId,tabindex:a.iTabIndex,id:0===o&&"string"==typeof x?a.sTableId+"_"+x:null}).append(e("<a>",{href:"#"}).html(l)).appendTo(t);a.oApi._fnBindAction(T,{action:x},m)}}}};p(e(n).empty().html('<ul class="pagination"/>').children("ul"),s)};if(t.TableTools){e.extend(!0,t.TableTools.classes,{container:"DTTT btn-group",buttons:{normal:"btn btn-default",disabled:"disabled"},collection:{container:"DTTT_dropdown dropdown-menu",buttons:{normal:"",disabled:"disabled"}},print:{info:"DTTT_print_info"},select:{row:"active"}});e.extend(!0,t.TableTools.DEFAULTS.oTags,{collection:{container:"ul",button:"li",liner:"a"}})}};"function"==typeof define&&define.amd?define(["jquery",""],n):"object"==typeof exports?n(require("jquery"),require("datatables")):jQuery&&n(jQuery,jQuery.fn.dataTable)}(window,document);
//# sourceMappingURL=dataTables.bootstrap.js.map

View File

@@ -1 +0,0 @@
{"version":3,"sources":["dataTables.bootstrap.js.src.js"],"names":["window","document","undefined","factory","$","DataTable","extend","defaults","dom","renderer","ext","classes","sWrapper","sFilterInput","sLengthSelect","pageButton","bootstrap","settings","host","idx","buttons","page","pages","btnDisplay","btnClass","api","Api","oClasses","lang","oLanguage","oPaginate","attach","container","i","ien","node","button","clickHandler","e","preventDefault","currentTarget","hasClass","data","action","draw","length","isArray","sFirst","sPrevious","sNext","sLast","class","sPageButton","aria-controls","sTableId","tabindex","iTabIndex","id","append","href","html","appendTo","oApi","_fnBindAction","empty","children","TableTools","normal","disabled","collection","print","info","select","row","DEFAULTS","oTags","liner","define","amd","exports","require","jQuery","fn","dataTable"],"mappings":"CAUA,SAAUA,EAAQC,EAAUC,GAE5B,GAAIC,GAAU,SAAUC,EAAGC,GAC3B,YAIAD,GAAEE,QAAQ,EAAMD,EAAUE,UACzBC,IACC,2FAGDC,SAAU,aAKXL,GAAEE,OAAQD,EAAUK,IAAIC,SACvBC,SAAe,8CACfC,aAAe,wBACfC,cAAe,yBAKhBT,GAAUK,IAAID,SAASM,WAAWC,UAAY,SAAWC,EAAUC,EAAMC,EAAKC,EAASC,EAAMC,GAC5F,GAGIC,GAAYC,EAHZC,EAAU,GAAIpB,GAAUqB,IAAKT,GAC7BN,EAAUM,EAASU,SACnBC,EAAUX,EAASY,UAAUC,UAG7BC,EAAS,SAAUC,EAAWZ,GACjC,GAAIa,GAAGC,EAAKC,EAAMC,EACdC,EAAe,SAAWC,GAC7BA,EAAEC,gBACInC,GAAEkC,EAAEE,eAAeC,SAAS,aACjChB,EAAIJ,KAAMiB,EAAEI,KAAKC,QAASC,MAAM,GAIlC,KAAMX,EAAE,EAAGC,EAAId,EAAQyB,OAAWX,EAAFD,EAAQA,IAAM,CAC7CG,EAAShB,EAAQa,EAEjB,IAAK7B,EAAE0C,QAASV,GACfL,EAAQC,EAAWI,OAEf,CACJb,EAAa,EACbC,GAAW,EAEX,QAASY,GACR,IAAK,WACJb,EAAa,UACbC,GAAW,UACX,MAED,KAAK,QACJD,EAAaK,EAAKmB,MAClBvB,GAAWY,GAAUf,EAAO,EAC3B,GAAK,YACN,MAED,KAAK,WACJE,EAAaK,EAAKoB,SAClBxB,GAAWY,GAAUf,EAAO,EAC3B,GAAK,YACN,MAED,KAAK,OACJE,EAAaK,EAAKqB,KAClBzB,GAAWY,GAAiBd,EAAM,EAAbD,EACpB,GAAK,YACN,MAED,KAAK,OACJE,EAAaK,EAAKsB,KAClB1B,GAAWY,GAAiBd,EAAM,EAAbD,EACpB,GAAK,YACN,MAED,SACCE,EAAaa,EAAS,CACtBZ,GAAWH,IAASe,EACnB,SAAW,GAId,GAAKb,EAAa,CACjBY,EAAO/B,EAAE,QACP+C,QAASxC,EAAQyC,YAAY,IAAI5B,EACjC6B,gBAAiBpC,EAASqC,SAC1BC,SAAYtC,EAASuC,UACrBC,GAAc,IAARtC,GAA+B,gBAAXiB,GACzBnB,EAASqC,SAAU,IAAKlB,EACxB,OAEDsB,OAAQtD,EAAE,OACTuD,KAAQ,MAERC,KAAMrC,IAEPsC,SAAU7B,EAEZf,GAAS6C,KAAKC,cACb5B,GAAOQ,OAAQP,GAASC,MAO7BN,GACC3B,EAAEc,GAAM8C,QAAQJ,KAAK,4BAA4BK,SAAS,MAC1D7C,GASF,IAAKf,EAAU6D,WAAa,CAE3B9D,EAAEE,QAAQ,EAAMD,EAAU6D,WAAWvD,SACpCqB,UAAa,iBACbZ,SACC+C,OAAU,kBACVC,SAAY,YAEbC,YACCrC,UAAa,8BACbZ,SACC+C,OAAU,GACVC,SAAY,aAGdE,OACCC,KAAQ,mBAETC,QACCC,IAAO,WAKTrE,GAAEE,QAAQ,EAAMD,EAAU6D,WAAWQ,SAASC,OAC7CN,YACCrC,UAAa,KACbI,OAAU,KACVwC,MAAS,QASW,mBAAXC,SAAyBA,OAAOC,IAC3CD,QAAS,SAAU,IAAK1E,GAEI,gBAAZ4E,SAEb5E,EAAS6E,QAAQ,UAAWA,QAAQ,eAE9BC,QAET9E,EAAS8E,OAAQA,OAAOC,GAAGC,YAIzBnF,OAAQC","file":"dataTables.bootstrap.js.map"}

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

File diff suppressed because one or more lines are too long

View File

@@ -45,7 +45,7 @@ requirejs.config({
hoverIntent: 'lib/jquery.hoverIntent.minified', // v1.8.0 Hover intention - http://cherne.net/brian/resources/jquery.hoverIntent.html
fullScreen: 'lib/jquery.fullscreen.min', // v0.5.0 Full screen mode - https://github.com/private-face/jquery.fullscreen
select2: 'lib/select2.min', // v4.0.0 Drop Down customization - https://select2.github.io/
validator: 'lib/validator.min', // v0.7.2 Validator for Bootstrap 3 - https://github.com/1000hz/bootstrap-validator
validator: 'lib/validator.min', // v0.10.1 Validator for Bootstrap 3 - https://github.com/1000hz/bootstrap-validator
lazylinepainter: 'lib/jquery.lazylinepainter-1.5.1.min', // v1.5.1 SVG line animation plugin - http://lazylinepainter.info/
blueImpGallery: 'lib/blueimp-gallery', // v2.15.2 Image Gallery - https://github.com/blueimp/Gallery/
blueImpGalleryHelper: 'lib/blueimp-helper', // helper function for Blue Imp Gallery

View File

@@ -0,0 +1,60 @@
/**
* Global CCPEvE function wrapper
*/
define(['jquery'], function($) {
'use strict';
/**
* checks weather the program URL is IGB trusted or not
* @returns {boolean}
*/
var isTrusted = function(){
var isPageTrusted = false;
if(isInGameBrowser()){
var trustedAttribute = $('body').attr('data-trusted');
if(trustedAttribute === '1'){
isPageTrusted = true;
}
}else{
// out of game browser is always trusted
isPageTrusted = true;
}
return isPageTrusted;
};
/**
* show IGB trust message
*/
var requestTrust = function(){
if(
isInGameBrowser() &&
!isTrusted()
){
CCPEVE.requestTrust( location.protocol + '//' + location.host );
}
};
/**
* in-game or out-of-game browser
* @returns {boolean}
*/
var isInGameBrowser = function(){
var inGame = false;
if(typeof CCPEVE === 'object'){
inGame = true;
}
return inGame;
};
return {
isInGameBrowser: isInGameBrowser,
isTrusted: isTrusted,
requestTrust: requestTrust
};
});

View File

@@ -0,0 +1,340 @@
/**
* Created by exodus4d on 06.07.2015.
* static signature types
*
* (*) marked fields are in-game verified and
* proofed, signature names (copy & paste from scanning window)
*/
define([], function() {
'use strict';
// system effects
var signatureTypes = {
1: { // system type (wh)
1: { // C1 (area id)
1: { // Combat
1: 'Perimeter Ambush Point',
2: 'Perimeter Camp',
3: 'Phase Catalyst Node',
4: 'The Line'
},
2: { // Relic
1: 'Forgotten Perimeter Coronation Platform',
2: 'Forgotten Perimeter Power Array'
},
3: { // Data
1: 'Unsecured Perimeter Amplifier',
2: 'Unsecured Perimeter Information Center '
},
4: { // Gas
1: 'Barren Perimeter Reservoir', //*
2: 'Token Perimeter Reservoir', //*
3: 'Minor Perimeter Reservoir', //*
4: 'Sizeable Perimeter Reservoir', //*
5: 'Ordinary Perimeter Reservoir' //*
},
5: { // Wormhole
1: 'H121 - C1',
2: 'C125 - C2',
3: 'O883 - C3',
4: 'M609 - C4',
5: 'L614 - C5',
6: 'S804 - C6'
},
6: { // ORE
1: 'Ordinary Perimeter Deposit', //*
2: 'Common Perimeter Deposit', //*
3: 'Unexceptional Frontier Deposit', //*
4: 'Average Frontier Deposit', //*
5: 'Isolated Core Deposit', //*
6: 'Uncommon Core Deposit' //*
},
7: { // Ghost
}
},
2: { // C2
1: { // Combat
1: 'Perimeter Checkpoint',
2: 'Perimeter Hangar',
3: 'The Ruins of Enclave Cohort 27',
4: 'Sleeper Data Sanctuary'
},
2: { // Relic
1: 'Forgotten Perimeter Gateway',
2: 'Forgotten Perimeter Habitation Coils'
},
3: { // Data
1: 'Unsecured Perimeter Comms Relay',
2: 'Unsecured Perimeter Transponder Farm '
},
4: { // Gas
1: 'Barren Perimeter Reservoir', //*
2: 'Token Perimeter Reservoir', //*
3: 'Minor Perimeter Reservoir', //*
4: 'Sizeable Perimeter Reservoir', //*
5: 'Ordinary Perimeter Reservoir' //*
},
5: { // Wormhole
// no *wandering* w-space -> k-space wormholes
// all holes are statics or K162
},
6: { // ORE
1: 'Ordinary Perimeter Deposit', //*
2: 'Common Perimeter Deposit', //*
3: 'Unexceptional Frontier Deposit', //*
4: 'Average Frontier Deposit', //*
5: 'Isolated Core Deposit', //*
6: 'Uncommon Core Deposit' //*
},
7: { // Ghost
}
},
3: { // C3
1: { // Combat
1: 'Fortification Frontier Stronghold',
2: 'Outpost Frontier Stronghold',
3: 'Solar Cell',
4: 'The Oruze Construct'
},
2: { // Relic
1: 'Forgotten Frontier Quarantine Outpost',
2: 'Forgotten Frontier Recursive Depot'
},
3: { // Data
1: 'Unsecured Frontier Database',
2: 'Unsecured Frontier Receiver'
},
4: { // Gas
1: 'Barren Perimeter Reservoir', //*
2: 'Token Perimeter Reservoir', //*
3: 'Minor Perimeter Reservoir', //*
4: 'Sizeable Perimeter Reservoir', //*
5: 'Ordinary Perimeter Reservoir', //*
6: 'Bountiful Frontier Reservoir', //*
7: 'Vast Frontier Reservoir' //*
},
5: { // Wormhole
1: 'V301 - C1',
2: 'I182 - C2',
3: 'N968 - C3',
4: 'T405 - C4',
5: 'N770 - C5',
6: 'A982 - C6'
},
6: { // ORE
1: 'Ordinary Perimeter Deposit', //*
2: 'Common Perimeter Deposit', //*
3: 'Unexceptional Frontier Deposit', //*
4: 'Average Frontier Deposit', //*
5: 'Infrequent Core Deposit', //*
6: 'Unusual Core Deposit' //*
},
7: { // Ghost
}
},
4: { // C4
1: { // Combat
1: 'Frontier Barracks',
2: 'Frontier Command Post',
3: 'Integrated Terminus',
4: 'Sleeper Information Sanctum'
},
2: { // Relic
1: 'Forgotten Frontier Conversion Module',
2: 'Forgotten Frontier Evacuation Center'
},
3: { // Data
1: 'Unsecured Frontier Digital Nexus',
2: 'Unsecured Frontier Trinary Hub'
},
4: { // Gas
1: 'Barren Perimeter Reservoir', //*
2: 'Token Perimeter Reservoir', //*
3: 'Minor Perimeter Reservoir', //*
4: 'Sizeable Perimeter Reservoir', //*
5: 'Ordinary Perimeter Reservoir', //*
6: 'Vast Frontier Reservoir' //*
},
5: { // Wormhole
// no *wandering* w-space -> k-space wormholes
// all holes are statics or K162
},
6: { // ORE
1: 'Ordinary Perimeter Deposit', //*
2: 'Common Perimeter Deposit', //*
3: 'Unexceptional Frontier Deposit', //*
4: 'Average Frontier Deposit', //*
5: 'Unusual Core Deposit' //*
},
7: { // Ghost
}
},
5: { // C5
1: { // Combat
1: 'Core Garrison', //*
2: 'Core Stronghold', //*
3: 'Oruze Osobnyk', //*
4: 'Quarantine Area'
},
2: { // Relic
1: 'Forgotten Core Data Field',
2: 'Forgotten Core Information Pen'
},
3: { // Data
1: 'Unsecured Frontier Enclave Relay',
2: 'Unsecured Frontier Server Bank'
},
4: { // Gas
1: 'Barren Perimeter Reservoir', //*
2: 'Token Perimeter Reservoir', //*
3: 'Sizeable Perimeter Reservoir', //*
4: 'Ordinary Perimeter Reservoir', //*
5: 'Bountiful Frontier Reservoir', //*
6: 'Instrumental Core Reservoir', //*
7: 'Vital Core Reservoir', //*
8: 'Minor Perimeter Reservoir' //*
},
5: { // Wormhole
1: 'D792 - HS',
2: 'C140 - LS',
3: 'Z142 - 0.0'
},
6: { // ORE
1: 'Ordinary Perimeter Deposit', //*
2: 'Common Perimeter Deposit', //*
3: 'Rarified Core Deposit' //*
},
7: { // Ghost
}
},
6: { // C6
1: { // Combat
1: 'Core Citadel', //*
2: 'Core Bastion', //*
3: 'Strange Energy Readings', //*
4: 'The Mirror' //*
},
2: { // Relic
1: 'Forgotten Core Assembly Hall', //*
2: 'Forgotten Core Circuitry Disassembler' //*
},
3: { // Data
1: 'Unsecured Core Backup Array', //*
2: 'Unsecured Core Emergence' //*
},
4: { // Gas
1: 'Token Perimeter Reservoir', //*
2: 'Minor Perimeter Reservoir', //*
3: 'Sizeable Perimeter Reservoir', //*
4: 'Ordinary Perimeter Reservoir', //*
5: 'Bountiful Frontier Reservoir', //*
6: 'Vast Frontier Reservoir', //*
7: 'Instrumental Core Reservoir', //*
8: 'Vital Core Reservoir' //*
},
5: { // Wormhole
1: 'D792 - HS',
2: 'C391 - LS',
3: 'Z142 - 0.0'
},
6: { // ORE
1: 'Ordinary Perimeter Deposit', //*
2: 'Common Perimeter Deposit', //*
3: 'Unexceptional Frontier Deposit', //*
4: 'Average Frontier Deposit', //*
5: 'Rarified Core Deposit' //*
},
7: { // Ghost
1: 'Superior Blood Raider Covert Research Facility' //*
}
},
13: { // Shattered Wormholes
5: { // Wormhole (some of them are static)
1: 'P060 - C1',
2: 'Z647 - C1',
3: 'D382 - C2',
4: 'L005 - C2',
5: 'N766 - C2',
6: 'C247 - C3',
7: 'K346 - C3',
8: 'M267 - C3',
9: 'O477 - C3',
10: 'X877 - C4',
11: 'Y683 - C4',
12: 'H296 - C5',
13: 'H900 - C5',
14: 'H296 - C5',
15: 'N062 - C5',
16: 'V911 - C5',
17: 'U574 - C6',
18: 'V753 - C6',
19: 'W237 - C6',
20: 'B274 - HS',
21: 'D792 - HS',
22: 'D845 - HS',
23: 'N110 - HS',
24: 'A239 - LS',
25: 'C391 - LS',
26: 'J244 - LS',
27: 'U201 - LS',
28: 'U210 - LS',
29: 'C248 - NS',
30: 'E545 - NS',
31: 'K346 - NS',
32: 'Z060 - NS'
}
}
}, // system type (k-space)
2: {
10: { // High Sec
5: { // Wormhole
1: 'Z971 - C1',
2: 'R943 - C2',
3: 'X702 - C3',
// no C4
4: 'M555 - C5',
5: 'B041 - C6',
6: 'A641 - HS',
7: 'R051 - LS',
8: 'V283 - NS'
}
},
11: { // Low Sec
5: { // Wormhole
1: 'Z971 - C1',
2: 'R943 - C2',
3: 'X702 - C3',
// no C4
4: 'N432 - C5',
5: 'U319 - C6',
6: 'B449 - HS',
7: 'N944 - LS',
8: 'S199 - NS'
}
},
12: { // 0.0
5: { // Wormhole
1: 'Z971 - C1',
2: 'R943 - C2',
3: 'X702 - C3',
// no C4
4: 'N432 - C5',
5: 'U319 - C6',
6: 'B449 - HS',
7: 'N944 - LS',
8: 'S199 - NS'
}
}
}
};
return signatureTypes;
});

View File

@@ -0,0 +1,733 @@
/**
* Created by exodus4d on 06.07.2015.
* static system effects
*/
define([], function() {
'use strict';
// system effects
var systemEffects = {
wh: {
magnetar: {
1: [
{
effect: 'Damage',
value: '+30%'
},{
effect: 'Missile explosion radius',
value: '+15%'
},{
effect: 'Drone Tracking',
value: '-15%'
},{
effect: 'Targeting Range',
value: '-15%'
},{
effect: 'Tracking Speed',
value: '-15%'
},{
effect: 'Target Painter Strength',
value: '-15%'
}
],
2: [
{
effect: 'Damage',
value: '+44%'
},{
effect: 'Missile explosion radius',
value: '+22%'
},{
effect: 'Drone Tracking',
value: '-22%'
},{
effect: 'Targeting Range',
value: '-22%'
},{
effect: 'Tracking Speed',
value: '-22%'
},{
effect: 'Target Painter Strength',
value: '-22%'
}
],
3: [
{
effect: 'Damage',
value: '+58%'
},{
effect: 'Missile explosion radius',
value: '+29%'
},{
effect: 'Drone Tracking',
value: '-29%'
},{
effect: 'Targeting Range',
value: '-29%'
},{
effect: 'Tracking Speed',
value: '-29%'
},{
effect: 'Target Painter Strength',
value: '-29%'
}
],
4: [
{
effect: 'Damage',
value: '+72%'
},{
effect: 'Missile explosion radius',
value: '+36%'
},{
effect: 'Drone Tracking',
value: '-36%'
},{
effect: 'Targeting Range',
value: '-36%'
},{
effect: 'Tracking Speed',
value: '-36%'
},{
effect: 'Target Painter Strength',
value: '-36%'
}
],
5: [
{
effect: 'Damage',
value: '+86%'
},{
effect: 'Missile explosion radius',
value: '+43%'
},{
effect: 'Drone Tracking',
value: '-43%'
},{
effect: 'Targeting Range',
value: '-43%'
},{
effect: 'Tracking Speed',
value: '-43%'
},{
effect: 'Target Painter Strength',
value: '-43%'
}
],
6: [
{
effect: 'Damage',
value: '+100%'
},{
effect: 'Missile explosion radius',
value: '+50%'
},{
effect: 'Drone Tracking',
value: '-50%'
},{
effect: 'Targeting Range',
value: '-50%'
},{
effect: 'Tracking Speed',
value: '-50%'
},{
effect: 'Target Painter Strength',
value: '-50%'
}
]
},
redGiant: {
1: [
{
effect: 'Heat Damage',
value: '+15%'
},{
effect: 'Overload Bonus',
value: '+30%'
},{
effect: 'Smart Bomb Range',
value: '+30%'
},{
effect: 'Smart Bomb Damage',
value: '+30%'
},{
effect: 'Bomb Damage',
value: '+30%'
}
],
2: [
{
effect: 'Heat Damage',
value: '+22%'
},{
effect: 'Overload Bonus',
value: '+44%'
},{
effect: 'Smart Bomb Range',
value: '+44%'
},{
effect: 'Smart Bomb Damage',
value: '+44%'
},{
effect: 'Bomb Damage',
value: '+44%'
}
],
3: [
{
effect: 'Heat Damage',
value: '+29%'
},{
effect: 'Overload Bonus',
value: '+58%'
},{
effect: 'Smart Bomb Range',
value: '+58%'
},{
effect: 'Smart Bomb Damage',
value: '+58%'
},{
effect: 'Bomb Damage',
value: '+58%'
}
],
4: [
{
effect: 'Heat Damage',
value: '+36%'
},{
effect: 'Overload Bonus',
value: '+72%'
},{
effect: 'Smart Bomb Range',
value: '+72%'
},{
effect: 'Smart Bomb Damage',
value: '+72%'
},{
effect: 'Bomb Damage',
value: '+72%'
}
],
5: [
{
effect: 'Heat Damage',
value: '+43%'
},{
effect: 'Overload Bonus',
value: '+86%'
},{
effect: 'Smart Bomb Range',
value: '+86%'
},{
effect: 'Smart Bomb Damage',
value: '+86%'
},{
effect: 'Bomb Damage',
value: '+86%'
}
],
6: [
{
effect: 'Heat Damage',
value: '+50%'
},{
effect: 'Overload Bonus',
value: '+100%'
},{
effect: 'Smart Bomb Range',
value: '+100%'
},{
effect: 'Smart Bomb Damage',
value: '+100%'
},{
effect: 'Bomb Damage',
value: '+100%'
}
]
},
pulsar: {
1: [
{
effect: 'Shield HP',
value: '+30%'
},{
effect: 'Armor Resists',
value: '-15%'
},{
effect: 'Capacitor recharge',
value: '-15%'
},{
effect: 'Signature',
value: '+30%'
},{
effect: 'NOS / Neut Drain Amount',
value: '+30%'
}
],
2: [
{
effect: 'Shield HP',
value: '+44%'
},{
effect: 'Armor Resists',
value: '-22%'
},{
effect: 'Capacitor recharge',
value: '-22%'
},{
effect: 'Signature',
value: '+44%'
},{
effect: 'NOS / Neut Drain Amount',
value: '+44%'
}
],
3: [
{
effect: 'Shield HP',
value: '+58%'
},{
effect: 'Armor Resists',
value: '-29%'
},{
effect: 'Capacitor recharge',
value: '-29%'
},{
effect: 'Signature',
value: '+58%'
},{
effect: 'NOS / Neut Drain Amount',
value: '+58%'
}
],
4: [
{
effect: 'Shield HP',
value: '+72%'
},{
effect: 'Armor Resists',
value: '-36%'
},{
effect: 'Capacitor recharge',
value: '-36%'
},{
effect: 'Signature',
value: '+72%'
},{
effect: 'NOS / Neut Drain Amount',
value: '+72%'
}
],
5: [
{
effect: 'Shield HP',
value: '+86%'
},{
effect: 'Armor Resists',
value: '-43%'
},{
effect: 'Capacitor recharge',
value: '-43%'
},{
effect: 'Signature',
value: '+86%'
},{
effect: 'NOS / Neut Drain Amount',
value: '+86%'
}
],
6: [
{
effect: 'Shield HP',
value: '+100%'
},{
effect: 'Armor Resists',
value: '-50%'
},{
effect: 'Capacitor recharge',
value: '-50%'
},{
effect: 'Signature',
value: '+100%'
},{
effect: 'NOS / Neut Drain Amount',
value: '+100%'
}
]
},
wolfRayet: {
1: [
{
effect: 'Armor HP',
value: '+30%'
},{
effect: 'Shield Resist',
value: '-15%'
},{
effect: 'Small Weapon Damage',
value: '+60%'
},{
effect: 'Signature Size',
value: '-15%'
}
],
2: [
{
effect: 'Armor HP',
value: '+44%'
},{
effect: 'Shield Resist',
value: '-22%'
},{
effect: 'Small Weapon Damage',
value: '+88%'
},{
effect: 'Signature Size',
value: '-22%'
}
],
3: [
{
effect: 'Armor HP',
value: '+58%'
},{
effect: 'Shield Resist',
value: '-29%'
},{
effect: 'Small Weapon Damage',
value: '+116%'
},{
effect: 'Signature Size',
value: '-29%'
}
],
4: [
{
effect: 'Armor HP',
value: '+72%'
},{
effect: 'Shield Resist',
value: '-36%'
},{
effect: 'Small Weapon Damage',
value: '+144%'
},{
effect: 'Signature Size',
value: '-36%'
}
],
5: [
{
effect: 'Armor HP',
value: '+86%'
},{
effect: 'Shield Resist',
value: '-43%'
},{
effect: 'Small Weapon Damage',
value: '+172%'
},{
effect: 'Signature Size',
value: '-43%'
}
],
6: [
{
effect: 'Armor HP',
value: '+100%'
},{
effect: 'Shield Resist',
value: '-50%'
},{
effect: 'Small Weapon Damage',
value: '+200%'
},{
effect: 'Signature Size',
value: '-50%'
}
]
},
cataclysmic: {
1: [
{
effect: 'Local armor repair amount',
value: '-15%'
},{
effect: 'Local shield boost amount',
value: '-15%'
},{
effect: 'Shield transfer amount',
value: '+30%'
},{
effect: 'Remote repair amount',
value: '+30%'
},{
effect: 'Capacitor capacity',
value: '+30%'
},{
effect: 'Capacitor recharge time',
value: '+15%'
},{
effect: 'Remote Capacitor Transmitter amount',
value: '-15%'
}
],
2: [
{
effect: 'Local armor repair amount',
value: '-22%'
},{
effect: 'Local shield boost amount',
value: '-22%'
},{
effect: 'Shield transfer amount',
value: '+44%'
},{
effect: 'Remote repair amount',
value: '+44%'
},{
effect: 'Capacitor capacity',
value: '+44%'
},{
effect: 'Capacitor recharge time',
value: '+22%'
},{
effect: 'Remote Capacitor Transmitter amount',
value: '-22%'
}
],
3: [
{
effect: 'Local armor repair amount',
value: '-29%'
},{
effect: 'Local shield boost amount',
value: '-29%'
},{
effect: 'Shield transfer amount',
value: '+58%'
},{
effect: 'Remote repair amount',
value: '+58%'
},{
effect: 'Capacitor capacity',
value: '+58%'
},{
effect: 'Capacitor recharge time',
value: '+29%'
},{
effect: 'Remote Capacitor Transmitter amount',
value: '-29%'
}
],
4: [
{
effect: 'Local armor repair amount',
value: '-36%'
},{
effect: 'Local shield boost amount',
value: '-36%'
},{
effect: 'Shield transfer amount',
value: '+72%'
},{
effect: 'Remote repair amount',
value: '+72%'
},{
effect: 'Capacitor capacity',
value: '+72%'
},{
effect: 'Capacitor recharge time',
value: '+36%'
},{
effect: 'Remote Capacitor Transmitter amount',
value: '-36%'
}
],
5: [
{
effect: 'Local armor repair amount',
value: '-43%'
},{
effect: 'Local shield boost amount',
value: '-43%'
},{
effect: 'Shield transfer amount',
value: '+86%'
},{
effect: 'Remote repair amount',
value: '+86%'
},{
effect: 'Capacitor capacity',
value: '+86%'
},{
effect: 'Capacitor recharge time',
value: '+43%'
},{
effect: 'Remote Capacitor Transmitter amount',
value: '-43%'
}
],
6: [
{
effect: 'Local armor repair amount',
value: '-50%'
},{
effect: 'Local shield boost amount',
value: '-50%'
},{
effect: 'Shield transfer amount',
value: '+100%'
},{
effect: 'Remote repair amount',
value: '+100%'
},{
effect: 'Capacitor capacity',
value: '+100%'
},{
effect: 'Capacitor recharge time',
value: '+50%'
},{
effect: 'Remote Capacitor Transmitter amount',
value: '-50%'
}
]
},
blackHole: {
1: [
{
effect: 'Missile velocity',
value: '+15%'
},{
effect: 'Missile Explosion Velocity',
value: '+30%'
},{
effect: 'Ship velocity',
value: '+30%'
},{
effect: 'Stasis Webifier Strength',
value: '-15%'
},{
effect: 'Inertia',
value: '+15%'
},{
effect: 'Targeting range',
value: '+30%'
}
],
2: [
{
effect: 'Missile velocity',
value: '+22%'
},{
effect: 'Missile Explosion Velocity',
value: '+44%'
},{
effect: 'Ship velocity',
value: '+44%'
},{
effect: 'Stasis Webifier Strength',
value: '-22%'
},{
effect: 'Inertia',
value: '+22%'
},{
effect: 'Targeting range',
value: '+44%'
}
],
3: [
{
effect: 'Missile velocity',
value: '+29%'
},{
effect: 'Missile Explosion Velocity',
value: '+58%'
},{
effect: 'Ship velocity',
value: '+58%'
},{
effect: 'Stasis Webifier Strength',
value: '-29%'
},{
effect: 'Inertia',
value: '+29%'
},{
effect: 'Targeting range',
value: '+58%'
}
],
4: [
{
effect: 'Missile velocity',
value: '+36%'
},{
effect: 'Missile Explosion Velocity',
value: '+72%'
},{
effect: 'Ship velocity',
value: '+72%'
},{
effect: 'Stasis Webifier Strength',
value: '-36%'
},{
effect: 'Inertia',
value: '+36%'
},{
effect: 'Targeting range',
value: '+72%'
}
],
5: [
{
effect: 'Missile velocity',
value: '+43%'
},{
effect: 'Missile Explosion Velocity',
value: '+86%'
},{
effect: 'Ship velocity',
value: '+86%'
},{
effect: 'Stasis Webifier Strength',
value: '-43%'
},{
effect: 'Inertia',
value: '+43%'
},{
effect: 'Targeting range',
value: '+86%'
}
],
6: [
{
effect: 'Missile velocity',
value: '+50%'
},{
effect: 'Missile Explosion Velocity',
value: '+100%'
},{
effect: 'Ship velocity',
value: '+100%'
},{
effect: 'Stasis Webifier Strength',
value: '-50%'
},{
effect: 'Inertia',
value: '+50%'
},{
effect: 'Targeting range',
value: '+100%'
}
]
}
}
};
return systemEffects;
});

View File

@@ -0,0 +1,101 @@
define(["jquery"], function($) {
"use strict";
var config = {
counterDigitSmallClass: 'pf-digit-counter-small',
counterDigitLargeClass: 'pf-digit-counter-large'
};
var updateDateDiff = function(element, tempDate){
var date1 = new Date();
var date2 = tempDate;
//Customise date2 for your required future time
var diff = (date1 - date2)/1000;
diff = Math.abs(Math.floor(diff));
var days = Math.floor(diff/(24*60*60));
var leftSec = diff - days * 24*60*60;
var hrs = Math.floor(leftSec/(60*60));
leftSec = leftSec - hrs * 60*60;
var min = Math.floor(leftSec/(60));
leftSec = leftSec - min * 60;
var value = [];
if(
days > 0 ||
value.length > 0
){
value.push('<span class="' + config.counterDigitLargeClass + '">' + days + 'd' + '</span>');
}
if(
hrs > 0 ||
value.length > 0
){
value.push('<span class="' + config.counterDigitSmallClass + '">' + hrs + 'h' + '</span>');
}
if(
min > 0 ||
value.length > 0
){
value.push('<span class="' + config.counterDigitSmallClass + '">' + min + 'm' + '</span>');
}
if(
leftSec >= 0 ||
value.length > 0
){
value.push('<span class="' + config.counterDigitSmallClass + '">' + leftSec + 's' + '</span>');
}
element.html(value.join(' '));
};
/**
* init a live counter based on a unix timestamp
* @returns {*}
*/
$.fn.initTimestampCounter = function(){
return this.each(function(){
var element = $(this);
var timestamp = parseInt( element.text() );
// do not init twice
if(timestamp > 0){
// mark as init
element.attr('data-counter', 'init');
var date = new Date( timestamp * 1000);
updateDateDiff(element, date);
var refreshIntervalId = window.setInterval(function(){
// update element with current time
if( !element.hasClass('stopCounter')){
updateDateDiff(element, date);
}else{
clearInterval( element.data('interval') );
}
}, 100);
element.data('interval', refreshIntervalId);
}
});
};
});

View File

@@ -0,0 +1,386 @@
/**
* Init
*/
define(['jquery'], function($) {
'use strict';
var Config = {
path: {
img: 'public/img/', // path for images
// user API
getCaptcha: 'api/user/getCaptcha', // ajax URL - get captcha image
sendInviteKey: 'api/user/sendInvite', // 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
saveUserConfig: 'api/user/saveAccount', // ajax URL - saves/update user account
getUserData: 'api/user/getData', // ajax URL - get user data
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
initMap: 'api/map/init', // ajax URL - get static data
updateMapData: 'api/map/updateData', // ajax URL - main map update trigger
updateUserData: 'api/map/updateUserData', // ajax URL - main map user data trigger
// map API
saveMap: 'api/map/save', // ajax URL - save/update map
deleteMap: 'api/map/delete', // ajax URL - delete map
importMap: 'api/map/import', // ajax URL - import map
// system API
searchSystem: 'api/system/search', // ajax URL - search system by name
saveSystem: 'api/system/save', // ajax URL - saves system to map
deleteSystem: 'api/system/delete', // ajax URL - delete system from map
getSystemGraphData: 'api/system/graphData', // ajax URL - get all system graph data
getConstellationData: 'api/system/constellationData', // ajax URL - get system constellation data
// connection API
saveConnection: 'api/connection/save', // ajax URL - save new connection to map
deleteConnection: 'api/connection/delete', // ajax URL - delete connection from map
// signature API
getSignatures: 'api/signature/getAll', // ajax URL - get all signature data for system
saveSignatureData: 'api/signature/save', // ajax URL - save signature data for system
deleteSignatureData: 'api/signature/delete', // ajax URL - delete signature data for system
// route API
searchRoute: 'api/route/search', // ajax URL - search system routes
// GitHub API
gitHubReleases: 'api/github/releases' // ajax URL - get release info from GitHub
},
url: {
ccpImageServer: 'https://image.eveonline.com/', // CCP image Server
zKillboard: 'https://zkillboard.com/api/' // killboard api
},
animationSpeed: {
splashOverlay: 300, // "splash" loading overlay
headerLink: 100, // links in head bar
mapOverlay: 200, // show/hide duration for map overlays
mapMoveSystem: 300, // system position has changed animation
mapDeleteSystem: 200, // remove system from map
mapModule: 200, // show/hide of an map module
dialogEvents: 180 // dialog events /slide/show/...
},
mapIcons: [ // map tab-icons
{
class: 'fa-desktop',
label: 'desktop',
unicode: '&#xf108;'
},{
class: 'fa-bookmark',
label: 'bookmark',
unicode: '&#xf02e;'
},{
class: 'fa-cube',
label: 'cube',
unicode: '&#xf1b2;'
},{
class: 'fa-plane',
label: 'plane',
unicode: '&#xf072;'
},{
class: 'fa-globe',
label: 'globe',
unicode: '&#xf0ac;'
},{
class: 'fa-rocket',
label: 'rocket',
unicode: '&#xf135;'
},{
class: 'fa-life-ring',
label: 'life ring',
unicode: '&#xf1cd;'
},{
class: 'fa-heart',
label: 'heart',
unicode: '&#xf004;'
}
],
classes: {
// log types
logTypes: {
info: {
class: 'pf-log-info',
label: 'info'
},
warning: {
class: 'pf-log-warning',
label: 'warning'
},
error: {
class: 'pf-log-error',
label: 'error'
}
},
// system effects
systemEffects: {
effect: {
class: 'pf-system-effect',
name: 'no effect'
},
magnetar: {
class: 'pf-system-effect-magnetar',
name: 'magnetar'
},
redGiant: {
class: 'pf-system-effect-redgiant',
name: 'red gaint'
},
pulsar: {
class: 'pf-system-effect-pulsar',
name: 'pulsar'
},
wolfRayet: {
class: 'pf-system-effect-wolfrayet',
name: 'wolf rayet'
},
cataclysmic: {
class: 'pf-system-effect-cataclysmic',
name: 'cytaclysmic'
},
blackHole: {
class: 'pf-system-effect-blackhole',
name: 'black hole'
}
},
// system security
systemSecurity: {
security: {
class: 'pf-system-sec'
},
'SH': {
class: 'pf-system-sec-unknown'
},
'H': {
class: 'pf-system-sec-highSec'
},
'L': {
class: 'pf-system-sec-lowSec'
},
'0.0': {
class: 'pf-system-sec-nullSec'
},
'C6': {
class: 'pf-system-sec-high'
},
'C5': {
class: 'pf-system-sec-high'
},
'C4': {
class: 'pf-system-sec-mid'
},
'C3': {
class: 'pf-system-sec-mid'
},
'C2': {
class: 'pf-system-sec-low'
},
'C1': {
class: 'pf-system-sec-low'
}
},
// true sec
trueSec: {
'0.0': {
class: 'pf-system-security-0-0'
},
'0.1': {
class: 'pf-system-security-0-1'
},
'0.2': {
class: 'pf-system-security-0-2'
},
'0.3': {
class: 'pf-system-security-0-3'
},
'0.4': {
class: 'pf-system-security-0-4'
},
'0.5': {
class: 'pf-system-security-0-5'
},
'0.6': {
class: 'pf-system-security-0-6'
},
'0.7': {
class: 'pf-system-security-0-7'
},
'0.8': {
class: 'pf-system-security-0-8'
},
'0.9': {
class: 'pf-system-security-0-9'
},
'1.0': {
class: 'pf-system-security-1-0'
}
},
// system info
systemInfo: {
rally: {
class: 'pf-system-info-rally',
label: 'rally point'
}
},
// easy-pie-charts
pieChart: {
class: 'pf-pie-chart', // class for all pie charts
pieChartMapCounterClass: 'pf-pie-chart-map-timer' // class for timer chart
}
},
// map scopes
defaultMapScope: 'wh', // default scope for connection
// map connection types
connectionTypes: {
jumpbridge: {
cssClass: 'pf-map-connection-jumpbridge',
paintStyle: {
dashstyle: '4 2 1 2'
}
},
stargate: {
cssClass: 'pf-map-connection-stargate',
paintStyle: {
dashstyle: '0' // solid line
}
},
wh_eol: {
cssClass: 'pf-map-connection-wh-eol',
paintStyle: {
dashstyle: '0' // solid line
}
},
wh_fresh: {
cssClass: 'pf-map-connection-wh-fresh',
paintStyle: {
dashstyle: '0' // solid line
}
},
wh_reduced: {
cssClass: 'pf-map-connection-wh-reduced',
paintStyle: {
dashstyle: '0' // solid line
}
},
wh_critical: {
cssClass: 'pf-map-connection-wh-critical',
paintStyle: {
dashstyle: '0' // solid line
}
},
frigate: {
cssClass: 'pf-map-connection-frig',
paintStyle: {
dashstyle: '0.99'
},
overlays:[
[ 'Label',
{
label: 'frig',
cssClass: ['pf-map-connection-overlay', 'frig'].join(' ')
} ]
]
},
preserve_mass: {
cssClass: 'pf-map-connection-preserve-mass',
overlays:[
[ 'Label',
{
label: '<i class="fa fa-warning"></i>&nbsp;save mass',
cssClass: ['pf-map-connection-overlay', 'mass'].join(' '),
width:50, length:30,
location: 0.5
} ]
]
}
},
// signature groups
signatureGroups: {
1: {
name: 'combat site', //*
label: 'Combat'
},
2: {
name: 'relic site', //*
label: 'Relic'
},
3: {
name: 'data site',
label: 'Data'
},
4: {
name: 'gas site',
label: 'Gas'
},
5: {
name: 'wormhole',
label: 'Wormhole'
},
6: {
name: 'ore site',
label: 'Ore'
},
7: {
name: 'ghost',
label: 'Ghost'
}
},
// frigate wormholes
frigateWormholes: {
1: { // C1
},
2: { // C2
1: 'L005 - C2',
2: 'C008 - C5',
3: 'Q003 - 0.0'
},
3: { // C3
1: 'E004 - C1',
2: 'L005 - C2',
3: 'M001 - C4'
},
4: { // C4
1: 'L005 - C2',
2: 'G008 - C6',
3: 'Q003 - 0.0'
},
5: { // C5
1: 'E004 - C1',
2: 'L005 - C2',
3: 'Z006 - C3',
4: 'C008 - C5',
5: 'Q003 - 0.0'
},
6: { // C6
1: 'E004 - C1',
2: 'Z006 - C3',
5: 'Q003 - 0.0'
},
13: { // Shattered Wormholes (some of them are static)
1: 'E004 - C1',
2: 'L005 - C2',
3: 'Z006 - C3',
4: 'M001 - C4',
5: 'C008 - C5',
6: 'G008 - C6',
7: 'Q003 - C7'
}
},
// incoming wormholes
incomingWormholes: {
1: 'K162 - C1/2/3 (unknown)',
2: 'K162 - C4/5 (dangerous)',
3: 'K162 - C6 (deadly)',
4: 'K162 - HS',
5: 'K162 - LS',
6: 'K162 - 0.0'
}
};
return Config;
});

View File

@@ -0,0 +1,499 @@
/**
* logging
*/
define([
'jquery',
'app/init',
'app/util',
'bootbox'
], function($, Init, Util, bootbox) {
'use strict';
var logData = []; // cache object for all log entries
var logDataTable = null; // "Datatables" Object
// Morris charts data
var maxGraphDataCount = 30; // max date entries for a graph
var chartData = {}; // chart Data object for all Morris Log graphs
var config = {
dialogDynamicAreaClass: 'pf-dynamic-area', // class for dynamic areas
logGraphClass: 'pf-log-graph' // class for all log Morris graphs
};
/**
* get log time string
* @returns {string}
*/
var getLogTime = function(){
var serverTime = Util.getServerTime();
var logTime = serverTime.toLocaleTimeString('en-US', { hour12: false });
return logTime;
};
/**
* shows the logging dialog
*/
var showDialog = function(){
// dialog content
var content = $('<div>');
// content row for log graphs
var rowElementGraphs = $('<div>', {
class: 'row'
});
content.append(rowElementGraphs);
var tableHeadline = $('<h4>', {
text: ' Processes'
}).prepend( $('<i>', {
class: ['fa', 'fa-fw', 'fa-lg', 'fa-list-alt'].join(' ')
}));
// add content Structure to dome before table initialization
content.append(tableHeadline);
// log table area --------------------------------------------------
var logTableArea = $('<div>', {
class: config.dialogDynamicAreaClass
});
var logTable = $('<table>', {
class: ['compact', 'stripe', 'order-column', 'row-border'].join(' ')
});
logTableArea.append(logTable);
content.append(logTableArea);
// init log table
logDataTable = logTable.DataTable( {
paging: true,
ordering: true,
order: [ 1, 'desc' ],
autoWidth: false,
hover: false,
pageLength: 15,
lengthMenu: [[10, 15, 25, 50, 50], [10, 15, 25, 50, 50]],
data: logData, // load cached logs (if available)
language: {
emptyTable: 'No entries',
zeroRecords: 'No entries found',
lengthMenu: 'Show _MENU_ entries',
info: 'Showing _START_ to _END_ of _TOTAL_ entries'
},
columnDefs: [
{
targets: 0,
title: '<i class="fa fa-lg fa-tag"></i>',
width: '18px',
searchable: false,
class: ['text-center'].join(' '),
data: 'status'
},{
targets: 1,
title: '<i class="fa fa-lg fa-fw fa-clock-o"></i>&nbsp;&nbsp;',
width: '50px',
searchable: true,
class: 'text-right',
data: 'time'
},{
targets: 2,
title: '<i class="fa fa-lg fa-fw fa-history"></i>&nbsp;&nbsp;',
width: '35px',
searchable: false,
class: 'text-right',
sType: 'html',
data: 'duration'
},{
targets: 3,
title: 'description',
searchable: true,
data: 'description'
},{
targets: 4,
title: 'type',
width: '40px',
searchable: true,
class: ['text-center'].join(' '),
data: 'type'
},{
targets: 5,
title: 'Prozess-ID&nbsp;&nbsp;&nbsp;',
width: '80px',
searchable: false,
class: 'text-right',
data: 'key'
}
]
});
// open dialog
var logDialog = bootbox.dialog({
title: 'Task-Manager',
message: content,
size: 'large',
buttons: {
close: {
label: 'close',
className: 'btn-default'
}
}
});
// modal dialog is shown
logDialog.on('shown.bs.modal', function(e) {
// show Morris graphs ----------------------------------------------------------
// function for chart label formation
var labelYFormat = function(y){
return Math.round(y) + 'ms';
};
for(var key in chartData) {
if(chartData.hasOwnProperty(key)) {
// create a chart for each key
var colElementGraph = $('<div>', {
class: ['col-md-6'].join(' ')
});
// graph element
var graphElement = $('<div>', {
class: config.logGraphClass
});
var graphArea = $('<div>', {
class: config.dialogDynamicAreaClass
}).append( graphElement );
// headline
var headline = $('<h4>', {
text: key
}).prepend(
$('<span>', {
class: ['txt-color', 'txt-color-grayLight'].join(' '),
text: 'Prozess-ID: '
})
);
// show update ping between function calls
var updateElement = $('<small>', {
class: ['txt-color', 'txt-color-blue', 'pull-right'].join(' ')
});
headline.append(updateElement).append('<br>');
// show average execution time
var averageElement = $('<small>', {
class: 'pull-right'
});
headline.append(averageElement);
colElementGraph.append( headline );
colElementGraph.append( graphArea );
graphArea.showLoadingAnimation();
rowElementGraphs.append( colElementGraph );
// cache DOM Elements that will be updated frequently
chartData[key].averageElement = averageElement;
chartData[key].updateElement = updateElement;
chartData[key].graph = Morris.Area({
element: graphElement,
data: [],
xkey: 'x',
ykeys: ['y'],
labels: [key],
units: 'ms',
parseTime: false,
ymin: 0,
yLabelFormat: labelYFormat,
padding: 10,
hideHover: true,
pointSize: 3,
lineColors: ['#375959'],
pointFillColors: ['#477372'],
pointStrokeColors: ['#313335'],
lineWidth: 2,
grid: false,
gridStrokeWidth: 0.3,
gridTextSize: 9,
gridTextFamily: 'Oxygen Bold',
gridTextColor: '#63676a',
behaveLikeLine: true,
goals: [],
goalLineColors: ['#66c84f'],
smooth: false,
fillOpacity: 0.3,
resize: true
});
graphArea.hideLoadingAnimation();
}
}
// ------------------------------------------------------------------------------
// add TableTool Buttons
var tt = new $.fn.DataTable.TableTools( logDataTable, {
sSwfPath: require.toUrl('') + 'lib/datatables/extensions/tabletools/swf/copy_csv_xls.swf',
aButtons: [ 'copy', 'csv', 'print' ]
});
$(tt.fnContainer()).insertBefore('.bootbox-body div.dataTables_wrapper');
// add button icons
$('.DTTT_button_csv').prepend( $('<i>', {
class: ['fa', 'fa-fw', 'fa-download'].join(' ')
}));
$('.DTTT_button_copy').prepend( $('<i>', {
class: ['fa', 'fa-fw', 'fa-clipboard'].join(' ')
}));
$('.DTTT_button_print').prepend( $('<i>', {
class: ['fa', 'fa-fw', 'fa-print'].join(' ')
}));
});
// modal dialog is closed
logDialog.on('hidden.bs.modal', function(e) {
// clear memory -> destroy all charts
for (var key in chartData) {
if (chartData.hasOwnProperty(key)) {
chartData[key].graph = null;
}
}
});
// modal dialog before hide
logDialog.on('hide.bs.modal', function(e) {
// destroy logTable
logDataTable.destroy(true);
logDataTable= null;
// remove event -> prevent calling this multiple times
$(this).off('hide.bs.modal');
});
};
/**
* updates the log graph for a log key
* @param key
* @param duration
*/
var updateLogGraph = function(key, duration){
// check if graph data already exist
if( !(chartData.hasOwnProperty(key))){
chartData[key] = {};
chartData[key].data = [];
chartData[key].graph = null;
chartData[key].averageElement = null;
chartData[key].updateElement = null;
}
// add new value
chartData[key].data.unshift(duration);
if(chartData[key].data.length > maxGraphDataCount){
chartData[key].data = chartData[key].data.slice(0, maxGraphDataCount);
}
function getGraphData(data) {
var tempChartData = {
data: [],
dataSum: 0,
average: 0
};
for(var x = 0; x < maxGraphDataCount; x++){
var value = 0;
if(data[x]){
value = data[x];
tempChartData.dataSum = Number( (tempChartData.dataSum + value).toFixed(2) );
}
tempChartData.data.push({
x: x,
y: value
});
}
// calculate average
tempChartData.average = Number( ( tempChartData.dataSum / data.length ).toFixed(2) );
return tempChartData;
}
var tempChartData = getGraphData(chartData[key].data);
// add new data to graph (Morris chart) - if is already initialized
if(chartData[key].graph !== null){
var avgElement = chartData[key].averageElement;
var updateElement = chartData[key].updateElement;
var delay = Util.getCurrentTriggerDelay( key, 0 );
if(delay){
updateElement[0].textContent = ' delay: ' + delay + 'ms ';
}
// set/change average line
chartData[key].graph.options.goals = [tempChartData.average];
// change avg. display
avgElement[0].textContent = 'Avg. ' + tempChartData.average + 'ms';
var avgStatus = getLogStatusByDuration(key, tempChartData.average);
var avgStatusClass = Util.getLogInfo( avgStatus, 'class' );
//change avg. display class
if( !avgElement.hasClass(avgStatusClass) ){
// avg status changed!
avgElement.removeClass().addClass('pull-right txt-color ' + avgStatusClass);
// change goals line color
if(avgStatus === 'warning'){
chartData[key].graph.options.goalLineColors = ['#e28a0d'];
$(document).setProgramStatus('problem');
}else{
chartData[key].graph.options.goalLineColors = ['#5cb85c'];
}
}
// set new data and redraw
chartData[key].graph.setData( tempChartData.data );
}
return tempChartData.data;
};
/**
* get the log "status" by log duration (ms).
* If duration > warning limit -> show as warning
* @param logKey
* @param logDuration
* @returns {string}
*/
var getLogStatusByDuration = function(logKey, logDuration){
var logStatus = 'info';
if( logDuration > Init.timer[logKey].EXECUTION_LIMIT ){
logStatus = 'warning';
}
return logStatus;
};
/**
* get the css class for a specific log type
* @param logType
* @returns {string}
*/
var getLogTypeIconClass = function(logType){
var logIconClass = '';
switch(logType){
case 'client':
logIconClass = 'fa-user';
break;
case 'server':
logIconClass = 'fa-download';
break;
}
return logIconClass;
};
/**
* init logging -> set global log event
*/
var init = function(){
var maxEntries = 150;
// set global logging listener
$(window).on('pf:log', function(e, logKey, options){
// check required logging information
if(
options &&
options.duration &&
options.description
){
var logDescription = options.description;
var logDuration = options.duration;
var logType = options.type;
// check log status by duration
var logStatus = getLogStatusByDuration(logKey, logDuration);
var statusClass = Util.getLogInfo( logStatus, 'class' );
var typeIconClass = getLogTypeIconClass(logType);
// update graph data
updateLogGraph(logKey, logDuration);
var logRowData = {
status: '<i class="fa fa-fw fa-circle txt-color ' + statusClass + '"></i>',
time: getLogTime(),
duration: '<span class="txt-color ' + statusClass + '">' + logDuration + '<small>ms</small></span>',
description: logDescription,
type: '<i class="fa ' + typeIconClass + '"></i>',
key: logKey
};
if(logDataTable){
// add row if dataTable is initialized before new log
logDataTable.row.add( logRowData ).draw(false);
}else{
// add row data to cache
logData.push(logRowData);
}
}
// delete old log entries from table ---------------------------------
var rowCount = logData.length;
if( rowCount >= maxEntries ){
if(logDataTable){
logDataTable.rows(0, {order:'index'}).remove().draw(false);
}else{
logData.shift();
}
}
// cache logs in order to keep previous logs in table after reopening the dialog
if(logDataTable){
logData = logDataTable.rows({order:'index'}).data();
}
});
};
return {
init: init,
getLogTime: getLogTime,
showDialog: showDialog
};
});

View File

@@ -0,0 +1,521 @@
/**
* Main loginPage application
*/
define([
'jquery',
'app/init',
'app/util',
'app/render',
'app/ccp',
'blueImpGallery',
'bootbox',
'lazyload',
'app/ui/header',
'app/ui/logo',
'app/ui/demo_map',
'dialog/account_settings',
'dialog/notification',
'dialog/manual',
'dialog/releases',
'dialog/credit'
], function($, Init, Util, Render, CCP, Gallery, bootbox) {
'use strict';
var config = {
splashOverlayClass: 'pf-splash', // class for "splash" overlay
// 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)
// map bg
headMapBgId: 'pf-header-map-bg', // id for header background container
mapNeocomId: 'pf-map-neocom', // id for map "neocom" image
mapBrowserId: 'pf-map-browser', // id for "browser" image
mapBgImageId: 'pf-map-bg-image', // id for "background" map image
// navigation
navigationElementId: 'pf-navbar', // id for navbar element
navigationLinkManualClass: 'pf-navbar-manual', // class for "manual" trigger link
navigationLinkLicenseClass: 'pf-navbar-license', // class for "license" trigger link
navigationVersionLinkClass: 'pf-navbar-version-info', // class for "version information"
// login form
loginFormId: 'pf-login-form', // id for login form
loginButtonClass: 'pf-login-button', // class for "login" button(s)
registerButtonClass: 'pf-register-button', // class for "register" button(s)
loginMessageContainerId: 'pf-login-message-container', // id for login form message container
// gallery
galleryId: 'pf-gallery', // id for gallery container
galleryThumbImageClass: 'pf-landing-image-preview', // class for gallery thumb images
galleryThumbContainerId: 'pf-landing-gallery-thumb-container', // id for gallery thumb images
galleryCarouselId: 'pf-landing-gallery-carousel', // id for "carousel" element
// animation
animateElementClass: 'pf-animate-on-visible' // class for elements that will be animated to show
};
/**
* set page observer
*/
var setPageObserver = function(){
// login form =====================================================================================
// register buttons ---------------------------------------------
$('.' + config.registerButtonClass).on('click', function(e){
e.preventDefault();
// logout current user (if there e.g. to register a second account)
Util.logout({
ajaxData: {
reroute: 0
}
});
// show register/settings dialog
$.fn.showSettingsDialog({
register: 1,
invite : parseInt( $('body').data('invite') )
});
});
// releases -----------------------------------------------------
$('.' + config.navigationVersionLinkClass).on('click', function(e){
$.fn.releasesDialog();
});
// manual -------------------------------------------------------
$('.' + config.navigationLinkManualClass).on('click', function(e){
e.preventDefault();
$.fn.showMapManual();
});
// license ------------------------------------------------------
$('.' + config.navigationLinkLicenseClass).on('click', function(e){
e.preventDefault();
$.fn.showCreditsDialog(false, true);
});
// tooltips -----------------------------------------------------
var mapTooltipOptions = {
toggle: 'tooltip',
container: 'body',
delay: 150
};
$('[title]').not('.slide img').tooltip(mapTooltipOptions);
};
/**
* show "registration key" dialog (see "Invite" feature)
*/
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>&nbsp;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.sendInviteKey,
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
*/
var initCarousel = function(){
// check if carousel exists (e.g. not in IGB browser
if($('#' + config.galleryCarouselId).length === 0){
return;
}
// extent "blueimp" gallery for a textFactory method to show HTML templates
Gallery.prototype.textFactory = function (obj, callback) {
var newSlideContent = $('<div>')
.addClass('text-content')
.attr('title', obj.title);
var moduleConfig = {
name: obj.href, // template name
position: newSlideContent,
functions: {
after: function(){
// element inserted -> load complete
callback({
type: 'complete',
target: newSlideContent[0]
});
}
}
};
// render HTML file (template)
var moduleData = {
id: config.headHeaderMapId,
bgId: config.headMapBgId,
neocomId: config.mapNeocomId,
browserId: config.mapBrowserId,
mapBgImageId: config.mapBgImageId
};
Render.showModule(moduleConfig, moduleData);
return newSlideContent[0];
};
// initialize carousel ------------------------------------------
var carousel = new Gallery([
{
title: 'IGB',
href: 'ui/map',
type: 'text/html'
},
{
href: 'public/img/landing/responsive.jpg',
title: 'Responsive layout',
type: 'image/jpg',
thumbnail: ''
},
{
href: 'public/img/landing/pathfinder_1.jpg',
title: 'Map view',
type: 'image/jpg',
thumbnail: ''
},
{
href: 'public/img/landing/pathfinder_3.jpg',
title: 'Map information',
type: 'image/jpg',
thumbnail: ''
},
{
href: 'public/img/landing/pathfinder_2.jpg',
title: 'System information',
type: 'image/jpg',
thumbnail: ''
}
], {
container: '#' + config.galleryCarouselId,
carousel: true,
startSlideshow: false,
titleProperty: 'title',
transitionSpeed: 600,
slideshowInterval: 5000,
onopened: function () {
// Callback function executed when the Gallery has been initialized
// and the initialization transition has been completed.
// -> show "demo" map
$('#' + config.headHeaderMapId).drawDemoMap(function(){
// zoom map SVGs
$('#' + config.headHeaderMapId + ' svg').velocity({
scaleX: 0.66,
scaleY: 0.66
}, {
duration: 360
});
// position map container
$('#' + config.headHeaderMapId).velocity({
marginTop: '130px',
marginLeft: '-50px'
}, {
duration: 360,
complete: function(){
// show browser
$('#' + config.mapBrowserId).velocity('transition.slideUpBigIn', {
duration: 360,
complete: function(){
// show neocom
$('#' + config.mapNeocomId).velocity('transition.slideLeftIn', {
duration: 180
});
// show background
$('#' + config.mapBgImageId).velocity('transition.shrinkIn', {
duration: 360
});
// when map is shown -> start carousel looping
carousel.play();
}
});
}
});
});
}
});
};
/**
* init image gallery
*/
var initGallery = function(){
requirejs(['blueImpGalleryBootstrap'], function() {
// thumb links
var thumbLinks = $('a[data-gallery="#pf-gallery"]');
var borderless = false;
var galleryElement = $('#' + config.galleryId);
galleryElement.data('useBootstrapModal', !borderless);
galleryElement.toggleClass('blueimp-gallery-controls', borderless);
// init gallery on click
thumbLinks.on('click', function(e){
e.preventDefault();
e = e || window.event;
var target = e.target || e.srcElement;
var link = target.src ? target.parentNode : target;
var options = {
index: link,
event: e,
container: '#' + config.galleryId,
titleProperty: 'description'
};
new Gallery(thumbLinks, options);
});
});
};
var initYoutube = function(){
$(".youtube").each(function() {
// Based on the YouTube ID, we can easily find the thumbnail image
$(this).css('background-image', 'url(https://i.ytimg.com/vi/' + this.id + '/sddefault.jpg)');
// Overlay the Play icon to make it look like a video player
$(this).append($('<div/>', {'class': 'play'}));
$(document).delegate('#'+this.id, 'click', function() {
// Create an iFrame with autoplay set to true
var iframe_url = "https://www.youtube.com/embed/" + this.id + "?autoplay=1&autohide=1";
if ($(this).data('params')) iframe_url+='&'+$(this).data('params');
// The height and width of the iFrame should be the same as parent
var iframe = $('<iframe/>', {
frameborder: '0',
src: iframe_url,
width: $(this).width(),
height: $(this).height(),
class: 'pricing-big'
});
// Replace the YouTube thumbnail with YouTube HTML5 Player
$(this).replaceWith(iframe);
});
});
};
/**
* init scrollspy for navigation bar
*/
var initScrollspy = function(){
// init scrollspy
// show elements that are currently in the viewport
var showVisibleElements = function(){
// find all elements that should be animated
var visibleElements = $('.' + config.animateElementClass).isInViewport();
$(visibleElements).removeClass( config.animateElementClass );
$(visibleElements).velocity('transition.flipXIn', {
duration: 600,
stagger: 60,
delay: 500,
complete: function(element){
$(element).find('.fade').addClass('in');
},
visibility: 'visible'
});
};
$( window ).scroll(function() {
// check for new visible elements
showVisibleElements();
});
// initial check for visible elements
showVisibleElements();
// event listener for navigation links
$('.page-scroll').on('click', function(){
// get element to scroll
var anchorTag = $(this).attr('data-anchor');
// scroll to container
$(anchorTag).velocity('scroll', {
duration: 300,
easing: 'swing'
});
});
};
/**
* main init "landing" page
*/
$(function(){
// show app information in browser console
Util.showVersionInfo();
// show log off message
var isLogOut = location.search.split('logout')[1];
if(isLogOut !== undefined){
// show logout dialog
var options = {
buttons: {
close: {
label: 'close',
className: ['btn-default'].join(' ')
}
},
content: {
icon: 'fa-sign-out',
class: 'txt-color-warning',
title: 'Logout',
headline: 'Logout',
text: [
'For security reasons, you were logged out automatically',
'Please log in again'
]
}
};
$.fn.showNotificationDialog(options);
// change url (remove logout parameter)
if (history.pushState) {
history.pushState({}, '', location.protocol + '//' + location.host + location.pathname);
}
}
// show get registration key dialog
var showRegistrationDialog = location.search.split('register')[1];
if(showRegistrationDialog !== undefined){
showRequestRegistrationKeyDialog();
}
// init "lazy loading" for images
$('.' + config.galleryThumbImageClass).lazyload({
threshold : 300
});
// hide splash loading animation
$('.' + config.splashOverlayClass).hideSplashOverlay();
// init carousel
initCarousel();
// init scrollspy
initScrollspy();
// init gallery
initGallery();
// init youtube videos
initYoutube();
// show logo -> hide animation in IGB
if( !CCP.isInGameBrowser() ){
$('#' + config.logoContainerId).drawLogo(function(){
// init header animation
$('#' + config.headerContainerId).initHeader(function(){
});
}, false);
}
setPageObserver();
});
});

View File

@@ -0,0 +1,111 @@
/**
* context menu
*/
define([
'jquery',
'app/ccp'
], function($, CCP) {
'use strict';
$.fn.contextMenu = function (settings) {
// animation
var animationInType = CCP.isInGameBrowser() ? 'fadeIn' : 'transition.flipXIn';
var animationInDuration = CCP.isInGameBrowser() ? 0 : 150;
var animationOutType = CCP.isInGameBrowser() ? 'fadeOut' : 'transition.flipXOut';
var animationOutDuration = CCP.isInGameBrowser() ? 0 : 150;
return this.each(function () {
// Open context menu
$(this).off('pf:openContextMenu').on('pf:openContextMenu', function (e, originalEvent, component, hiddenOptions, activeOptions) {
// hide all other open context menus
$('#pf-dialog-wrapper > .dropdown-menu').hide();
var contextMenu = $(settings.menuSelector);
var menuLiElements = contextMenu.find('li');
// show all menu entries
menuLiElements.show();
// disable specific menu entries
for(var i = 0; i < hiddenOptions.length; i++){
contextMenu.find('li[data-action="' + hiddenOptions[i] + '"]').hide();
}
// deactivate all menu entries
menuLiElements.removeClass('active');
//set active specific menu entries
for(var j = 0; j < activeOptions.length; j++){
contextMenu.find('li[data-action="' + activeOptions[j] + '"]').addClass('active');
}
//open menu
contextMenu.css({
position: 'absolute',
left: getLeftLocation(originalEvent),
top: getTopLocation(originalEvent)
}).velocity(animationInType, {
duration: animationInDuration,
complete: function(){
// set context menu "click" observer
$(this).off('click').one('click', {component: component, position:{x: originalEvent.offsetX, y: originalEvent.offsetY}}, function (e) {
// hide contextmenu
$(this).hide();
var params = {
selectedMenu: $(e.target),
component: e.data.component,
position: e.data.position
};
settings.menuSelected.call(this, params);
});
}
});
//make sure menu closes on any click
$(document).one('click.closeContextmenu', function () {
$('.dropdown-menu[role="menu"]').velocity(animationOutType, {
duration: animationOutDuration
});
});
return false;
});
});
function getLeftLocation(e) {
var mouseWidth = e.pageX;
var pageWidth = $(window).width();
var menuWidth = $(settings.menuSelector).width();
// opening menu would pass the side of the page
if (mouseWidth + menuWidth > pageWidth &&
menuWidth < mouseWidth) {
return mouseWidth - menuWidth;
}
return mouseWidth;
}
function getTopLocation(e) {
var mouseHeight = e.pageY;
var pageHeight = $(window).height();
var menuHeight = $(settings.menuSelector).height();
// opening menu would pass the bottom of the page
if (mouseHeight + menuHeight > pageHeight &&
menuHeight < mouseHeight) {
return mouseHeight - menuHeight;
}
return mouseHeight;
}
};
});

View File

@@ -0,0 +1,156 @@
/**
* Map "magnetizing" feature
* jsPlumb extension used: http://morrisonpitt.com/farahey/
*/
define([
'jquery',
'farahey'
], function($) {
'use strict';
var config = {
systemClass: 'pf-system' // class for all systems
};
/**
* Cached current "Magnetizer" object
* @type {Magnetizer}
*/
var m8 = null;
/**
* init a jsPlumb (map) Element for "magnetised" function.
* this is optional and prevents systems from being overlapped
*/
$.fn.initMagnetizer = function(){
var mapContainer = this;
var systemsOnMap = mapContainer.find('.' + config.systemClass);
/**
* helper function
* get current system offset
* @param system
* @returns {{left, top}}
* @private
*/
var _offset = function(system) {
var _ = function(p) {
var v = system.style[p];
return parseInt(v.substring(0, v.length - 2));
};
return {
left:_('left'),
top:_('top')
};
};
/**
* helper function
* set new syste offset
* @param system
* @param o
* @private
*/
var _setOffset = function(system, o) {
var markAsUpdated = false;
// new position must be within parent container
// no negative offset!
if(
o.left >= 0 &&
o.left <= 2300
){
markAsUpdated = true;
system.style.left = o.left + 'px';
}
if(
o.top >= 0 &&
o.top <= 498
){
markAsUpdated = true;
system.style.top = o.top + 'px';
}
if(markAsUpdated === true){
$(system).markAsChanged();
}
};
/**
* helper function
* exclude current dragged element(s) from position update
* @param id
* @returns {boolean}
* @private
*/
var _dragFilter = function(id) {
return !$('#' + id).hasClass('jsPlumb_dragged');
};
// main init for "magnetize" feature ------------------------------------------------------
m8 = new Magnetizer({
container: mapContainer,
getContainerPosition:function(c) {
return c.offset();
},
getPosition:_offset,
getSize: function(system) {
return [ $(system).outerWidth(), $(system).outerHeight() ];
},
getId : function(system) {
return $(system).attr('id');
},
setPosition:_setOffset,
elements: systemsOnMap,
filter:_dragFilter,
padding:[8, 8]
});
};
$.fn.destroyMagnetizer = function(){
var mapContainer = this;
// remove cached "magnetizer" instance
m8 = null;
};
/**
* update system positions for "all" systems that are effected by drag&drop
* @param map
* @param e
*/
var executeAtEvent = function(map, e){
// check if magnetizer is active
if(m8 !== null && e ){
m8.executeAtEvent(e);
map.repaintEverything();
}
};
/**
* rearrange all systems of a map
* needs "magnetization" to be active
* @param map
*/
var executeAtCenter = function(map){
if(m8 !== null){
m8.executeAtCenter();
map.repaintEverything();
}
};
return {
executeAtCenter: executeAtCenter,
executeAtEvent: executeAtEvent
};
});

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,273 @@
/**
* map overlay functions
*/
define([
'jquery',
'app/init',
'app/util'
], function($, Init, Util) {
'use strict';
var config = {
logTimerCount: 3, // map log timer in seconds
// map
mapWrapperClass: 'pf-map-wrapper', // wrapper div (scrollable)
// map overlays
mapOverlayClass: 'pf-map-overlay', // class for all map overlays
mapOverlayTimerClass: 'pf-map-overlay-timer', // class for map overlay timer e.g. map timer
mapOverlayInfoClass: 'pf-map-overlay-info' // class for map overlay info e.g. map info
};
// overlay options (all available map options shown in overlay)
var options = {
filter: {
title: 'active filter',
class: 'pf-map-overlay-filter',
iconClass: ['fa', 'fa-fw', 'fa-filter']
},
mapSnapToGrid: {
title: 'active grid',
class: 'pf-map-overlay-grid',
iconClass: ['glyphicon', 'glyphicon-th']
},
mapMagnetizer: {
title: 'active magnetizer',
class: 'pf-map-overlay-magnetizer',
iconClass: ['fa', 'fa-fw', 'fa-magnet']
}
};
/**
* get map overlay element by type e.g. timer/counter, info - overlay
* @param overlayType
* @returns {*}
*/
$.fn.getMapOverlay = function(overlayType){
var mapWrapperElement = $(this).parents('.' + config.mapWrapperClass);
var mapOverlay = null;
switch(overlayType){
case 'timer':
mapOverlay = mapWrapperElement.find('.' + config.mapOverlayTimerClass);
break;
case 'info':
mapOverlay = mapWrapperElement.find('.' + config.mapOverlayInfoClass);
break;
}
return mapOverlay;
};
/**
* draws the map update counter to the map overlay timer
* @param percent
* @returns {*}
*/
$.fn.setMapUpdateCounter = function(percent, value){
var mapOverlayTimer = $(this);
// check if counter already exists
var counterChart = mapOverlayTimer.getMapCounter();
if(counterChart.length === 0){
// create new counter
counterChart = $('<div>', {
class: [Init.classes.pieChart.class, Init.classes.pieChart.pieChartMapCounterClass].join(' ')
}).attr('data-percent', percent).append(
$('<span>', {
text: value
})
);
mapOverlayTimer.append(counterChart);
// init counter
counterChart.initMapUpdateCounter();
// set tooltip
mapOverlayTimer.attr('data-placement', 'left');
mapOverlayTimer.attr('title', 'update counter');
mapOverlayTimer.tooltip();
}
return counterChart;
};
/**
* get the map counter chart by an overlay
* @returns {*}
*/
$.fn.getMapCounter = function(){
var mapOverlayTimer = $(this);
return mapOverlayTimer.find('.' + Init.classes.pieChart.pieChartMapCounterClass);
};
/**
* start the map update counter or reset
*/
$.fn.startMapUpdateCounter = function(){
var mapOverlayTimer = $(this);
var counterChart = mapOverlayTimer.getMapCounter();
var maxSeconds = config.logTimerCount;
var counterChartLabel = counterChart.find('span');
var percentPerCount = 100 / maxSeconds;
// update counter
var updateChart = function(tempSeconds){
var pieChart = counterChart.data('easyPieChart');
if(pieChart !== undefined){
counterChart.data('easyPieChart').update( percentPerCount * tempSeconds);
}
counterChartLabel.text(tempSeconds);
};
// main timer function is called on any counter update
var timer = function(){
// decrease timer
var currentSeconds = counterChart.data('currentSeconds');
currentSeconds--;
counterChart.data('currentSeconds', currentSeconds);
if(currentSeconds >= 0){
// update counter
updateChart(currentSeconds);
}else{
// hide counter and reset
clearInterval(mapUpdateCounter);
mapOverlayTimer.velocity('transition.whirlOut', {
duration: Init.animationSpeed.mapOverlay,
complete: function(){
counterChart.data('interval', false);
}
});
}
};
// get current seconds (in case the timer is already running)
var currentSeconds = counterChart.data('currentSeconds');
// start values for timer and chart
counterChart.data('currentSeconds', maxSeconds);
updateChart(maxSeconds);
if(
currentSeconds === undefined ||
currentSeconds < 0
){
// start timer
var mapUpdateCounter = setInterval(timer, 1000);
// store counter interval
counterChart.data('interval', mapUpdateCounter);
// show overlay
if(mapOverlayTimer.is(':hidden')){
mapOverlayTimer.velocity('stop').velocity('transition.whirlIn', { duration: Init.animationSpeed.mapOverlay });
}
}
};
/**
* update (show/hide) a overlay icon in the "info"-overlay
* show/hide the overlay itself is no icons are visible
* @param option
* @param viewType
*/
$.fn.updateOverlayIcon = function(option, viewType){
var mapOverlayInfo = $(this);
var showOverlay = false;
var mapOverlayIconClass = options[option].class;
// look for the overlay icon that should be updated
var iconElement = mapOverlayInfo.find('.' + mapOverlayIconClass);
if(iconElement){
if(viewType === 'show'){
showOverlay = true;
iconElement.velocity('fadeIn');
}else if(viewType === 'hide'){
iconElement.hide();
// check if there is any visible icon remaining
var visibleIcons = mapOverlayInfo.find('i:visible');
if(visibleIcons.length > 0){
showOverlay = true;
}
}
}
// show the entire overlay if there is at least one active icon
if(
showOverlay === true &&
mapOverlayInfo.is(':hidden')
){
// show overlay
mapOverlayInfo.velocity('stop').velocity('transition.whirlIn', { duration: Init.animationSpeed.mapOverlay });
}else if(
showOverlay === false &&
mapOverlayInfo.is(':visible')
){
// hide overlay
mapOverlayInfo.velocity('stop').velocity('transition.whirlOut', { duration: Init.animationSpeed.mapOverlay });
}
};
/**
* init all map overlays on a "parent" element
* @returns {any|JQuery|*}
*/
$.fn.initMapOverlays = function(){
return this.each(function(){
var parentElement = $(this);
var mapOverlayTimer = $('<div>', {
class: [config.mapOverlayClass, config.mapOverlayTimerClass].join(' ')
});
parentElement.append(mapOverlayTimer);
// ---------------------------------------------------------------------------
// add map overlay info. after scrollbar is initialized
var mapOverlayInfo = $('<div>', {
class: [config.mapOverlayClass, config.mapOverlayInfoClass].join(' ')
});
// add all overlay elements
for (var prop in options) {
if(options.hasOwnProperty(prop)){
mapOverlayInfo.append(
$('<i>', {
class: options[prop].iconClass.concat( ['pull-right', options[prop].class] ).join(' ')
}).attr('title', options[prop].title).tooltip({
placement: 'left',
container: 'body'
})
);
}
}
parentElement.append(mapOverlayInfo);
// reset map update timer
mapOverlayTimer.setMapUpdateCounter(100, config.logTimerCount);
});
};
});

View File

@@ -0,0 +1,318 @@
/**
* Main map application
*/
define([
'jquery',
'app/init',
'app/util',
'app/render',
'app/logging',
'app/ccp',
'app/page',
'app/ui/form_element',
'app/module_map'
], function($, Init, Util, Render, Logging, CCP, Page) {
'use strict';
/**
* main init "map" page
*/
$(function(){
// load page
$('body').loadPageStructure();
// show app information in browser console
Util.showVersionInfo();
// init logging
Logging.init();
if( !CCP.isTrusted() ){
// show trust message
$(document).trigger('pf:showTrustDialog');
return;
}
var mapModule = $('#' + Util.config.mapModuleId);
// map init load static data =======================================================
$.getJSON( Init.path.initMap, function( initData ) {
if( initData.error.length > 0 ){
for(var i = 0; i < initData.error.length; i++){
Util.showNotify({
title: initData.error[i].title,
text: initData.error[i].message,
type: initData.error[i].type
});
}
}
Init.timer = initData.timer;
Init.mapTypes = initData.mapTypes;
Init.mapScopes = initData.mapScopes;
Init.connectionScopes = initData.connectionScopes;
Init.systemStatus = initData.systemStatus;
Init.systemType = initData.systemType;
Init.characterStatus = initData.characterStatus;
Init.maxSharedCount = initData.maxSharedCount;
Init.routes = initData.routes;
// init tab change observer, Once the timers are available
Page.initTabChangeObserver();
// init map module
mapModule.initMapModule();
}).fail(function( jqXHR, status, error) {
var reason = status + ' ' + jqXHR.status + ': ' + error;
$(document).trigger('pf:shutdown', {reason: reason});
});
/**
* main function for init all map relevant trigger calls
*/
$.fn.initMapModule = function(){
var mapModule = $(this);
// log keys ------------------------------------------------------------------------
// ajax request update map data
var logKeyServerMapData = 'UPDATE_SERVER_MAP';
// update client map data
var logKeyClientMapData = 'UPDATE_CLIENT_MAP';
// ajax request update map user data
var logKeyServerUserData = 'UPDATE_SERVER_USER_DATA';
// update client map user data
var logKeyClientUserData = 'UPDATE_CLIENT_USER_DATA';
// main update intervals/trigger (heartbeat)
var updateTimeouts = {
mapUpdate: 0,
userUpdate: 0
};
// ping for main map update ========================================================
var triggerMapUpdatePing = function(){
// check each execution time if map module is still available
var check = $('#' + mapModule.attr('id')).length;
if(check === 0){
// program crash stop any update
return;
}
// get updated map data
var updatedMapData = mapModule.getMapModuleDataForUpdate();
// wrap array to object
updatedMapData = {mapData: updatedMapData};
// start log
Util.timeStart(logKeyServerMapData);
// store updatedMapData
$.ajax({
type: 'POST',
url: Init.path.updateMapData,
data: updatedMapData,
dataType: 'json'
}).done(function(data){
// log request time
var duration = Util.timeStop(logKeyServerMapData);
Util.log(logKeyServerMapData, {duration: duration, type: 'server', description: 'request map data'});
if(
data.error &&
data.error.length > 0
){
// any error in the main trigger functions result in a user log-off
$(document).trigger('pf:menuLogout');
}else{
$(document).setProgramStatus('online');
if(data.mapData.length === 0){
// clear all existing maps
mapModule.clearMapModule();
// no map data available -> show "new map" dialog
$(document).trigger('pf:menuShowMapSettings', {tab: 'new'});
}else{
// map data found
// start log
Util.timeStart(logKeyClientMapData);
// load/update main map module
mapModule.updateMapModule(data.mapData);
// log client map update time
duration = Util.timeStop(logKeyClientMapData);
Util.log(logKeyClientMapData, {duration: duration, type: 'client', description: 'update map'});
}
// get the current update delay (this can change if a user is inactive)
var mapUpdateDelay = Util.getCurrentTriggerDelay( logKeyServerMapData, 0 );
// init new trigger
updateTimeouts.mapUpdate = setTimeout(function(){
triggerMapUpdatePing();
}, mapUpdateDelay);
// initial start for the userUpdate trigger
// this should only be called at the first time!
if(updateTimeouts.userUpdate === 0){
// start user update trigger after map loaded
updateTimeouts.userUpdate = setTimeout(function(){
triggerUserUpdatePing();
}, 3000);
}
}
}).fail(handleAjaxErrorResponse);
};
// ping for user data update =======================================================
var triggerUserUpdatePing = function(){
// IMPORTANT: Get user data for ONE map that is currently visible
// On later releases this can be easy changed to "full update" all maps for a user
//
var mapIds = [];
var activeMap = Util.getMapModule().getActiveMap();
if(activeMap){
mapIds = [ activeMap.data('id') ];
}
var updatedUserData = {
mapIds: mapIds,
systemData: Util.getCurrentSystemData()
};
Util.timeStart(logKeyServerUserData);
$.ajax({
type: 'POST',
url: Init.path.updateUserData,
data: updatedUserData,
dataType: 'json'
}).done(function(data){
// log request time
var duration = Util.timeStop(logKeyServerUserData);
Util.log(logKeyServerUserData, {duration: duration, type: 'server', description:'request user data'});
if(data.error.length > 0){
// any error in the main trigger functions result in a user log-off
$(document).trigger('pf:menuLogout');
}else{
$(document).setProgramStatus('online');
if(data.userData !== undefined){
// store current user data global (cache)
var userData = Util.setCurrentUserData(data.userData);
if(userData.character === undefined){
// no active character found -> show settings dialog
Util.showNotify({title: 'Main character not set', text: 'Check your API data and set a main character', type: 'error'});
$(document).triggerMenuEvent('ShowSettingsDialog');
}
// store current map user data (cache)
if(data.mapUserData !== undefined){
Util.setCurrentMapUserData(data.mapUserData);
}
// start log
Util.timeStart(logKeyClientUserData);
// active character data found
mapModule.updateMapModuleData();
// log client user data update time
duration = Util.timeStop(logKeyClientUserData);
Util.log(logKeyClientUserData, {duration: duration, type: 'client', description:'update users'});
// update system info panels
if(data.system){
mapModule.updateSystemModuleData(data.system);
}
// get the current update delay (this can change if a user is inactive)
var mapUserUpdateDelay = Util.getCurrentTriggerDelay( logKeyServerUserData, 0 );
// init new trigger
updateTimeouts.userUpdate = setTimeout(function(){
triggerUserUpdatePing();
}, mapUserUpdateDelay);
}
}
}).fail(handleAjaxErrorResponse);
};
/**
* Ajax error response handler function for main-ping functions
* @param jqXHR
* @param status
* @param error
*/
var handleAjaxErrorResponse = function(jqXHR, status, error){
// clear both main update request trigger timer
clearUpdateTimeouts();
var reason = status + ' ' + jqXHR.status + ': ' + error;
var errorData = [];
if(jqXHR.responseText){
var errorObj = $.parseJSON(jqXHR.responseText);
if(
errorObj.error &&
errorObj.error.length > 0
){
errorData = errorObj.error;
}
}
$(document).trigger('pf:shutdown', {reason: reason, error: errorData});
};
/**
* clear both main update timeouts
* -> stop program from working -> shutdown
*/
var clearUpdateTimeouts = function(){
for(var intervalKey in updateTimeouts) {
if(updateTimeouts.hasOwnProperty(intervalKey)){
clearTimeout( updateTimeouts[intervalKey] );
}
}
};
// initial start of the map update function
triggerMapUpdatePing();
};
});
});

View File

@@ -0,0 +1,769 @@
define([
'jquery',
'app/init',
'app/util',
'app/map/map',
'app/counter',
'app/ui/system_info',
'app/ui/system_graph',
'app/ui/system_signature',
'app/ui/system_route',
'app/ui/system_killboard',
'datatablesTableTools',
'datatablesResponsive'
], function($, Init, Util, Map) {
'use strict';
var config = {
dynamicElementWrapperId: 'pf-dialog-wrapper', // parent Element for dynamic content (dialogs,..)
mapTabElementId: 'pf-map-tab-element', // id for map tab element (tabs + content)
mapTabBarId: 'pf-map-tabs', // id for map tab bar
mapTabIdPrefix: 'pf-map-tab-', // id prefix for a map tab
mapTabClass: 'pf-map-tab', // class for a map tab
mapTabLinkTextClass: 'nav-tabs-link', // class for span elements in a tab
mapTabIconClass: 'pf-map-tab-icon', // class for map icon
mapTabSharedIconClass: 'pf-map-tab-shared-icon', // class for map shared icon
mapTabContentClass: 'pf-map-tab-content', // class for tab content container
mapTabContentSystemInfoClass: 'pf-map-tab-content-system',
mapWrapperClass: 'pf-map-wrapper', // scrollable
mapClass: 'pf-map', // class for each map
// TabContentStructure
mapTabContentRow: 'pf-map-content-row', // main row for Tab content (grid)
mapTabContentCell: 'pf-map-content-col', // column
mapTabContentCellFirst: 'pf-map-content-col-first', // first column
mapTabContentCellSecond: 'pf-map-content-col-second', // second column
// module
moduleClass: 'pf-module', // class for a module
moduleClosedClass: 'pf-module-closed' // class for a closed module
};
var mapTabChangeBlocked = false; // flag for preventing map tab switch
/**
* get all maps for a maps module
* @param mapModule
* @returns {*}
*/
$.fn.getMaps = function(){
var maps = $(this).find('.' + config.mapClass);
return maps;
};
/**
* get the current active map for
* @returns {*}
*/
$.fn.getActiveMap = function(){
var map = $(this).find('.active.' + config.mapTabContentClass + ' .' + config.mapClass);
if(map.length === 0){
map = false;
}
return map;
};
/**
* set Tab Observer, events are triggered within map.js
* @param mapContentModule
*/
$.fn.setTabContentObserver = function(){
return this.each(function(){
// update Tab Content with system data information
$(this).on('pf:drawSystemModules', function(e){
drawSystemModules($( e.target ));
});
$(this).on('pf:removeSystemModules', function(e){
removeSystemModules($( e.target ));
});
});
};
/**
* clear all system info modules and remove them
* @param tabContentElement
*/
var removeSystemModules = function(tabContentElement, callback){
tabContentElement.find('.' + config.moduleClass).velocity('transition.slideDownOut', {
duration: Init.animationSpeed.mapModule,
complete: function(tempElement){
$(tempElement).remove();
if(callback){
callback();
}
}
});
};
/**
* clears and updates the system info element (signature table, system info,...)
* @param tabContentElement
*/
var drawSystemModules = function(tabContentElement){
var currentSystemData = Util.getCurrentSystemData();
// get Table cell for system Info
var firstCell = $(tabContentElement).find('.' + config.mapTabContentCellFirst);
var secondCell = $(tabContentElement).find('.' + config.mapTabContentCellSecond);
// draw system info module
firstCell.drawSystemInfoModule(currentSystemData.mapId, currentSystemData.systemData);
// draw system graph module
firstCell.drawSystemGraphModule(currentSystemData.systemData);
// draw signature table module
firstCell.drawSignatureTableModule(currentSystemData.systemData);
// draw system routes module
secondCell.drawSystemRouteModule(currentSystemData.systemData);
// draw system killboard module
secondCell.drawSystemKillboardModule(currentSystemData.systemData);
// set Module Observer
setModuleObserver();
};
/**
* set observer for each module
*/
var setModuleObserver = function(){
// toggle height for a module
$(document).off('click.toggleModuleHeight').on('click.toggleModuleHeight', '.' + config.moduleClass, function(e){
var moduleElement = $(this);
// get click position
var posX = moduleElement.offset().left;
var posY = moduleElement.offset().top;
var clickX = e.pageX - posX;
var clickY = e.pageY - posY;
// check for top-left click
if(clickX <= 6 && clickY <= 6){
// remember height
if(! moduleElement.data('origHeight')){
moduleElement.data('origHeight', moduleElement.outerHeight());
}
if(moduleElement.hasClass( config.moduleClosedClass )){
var moduleHeight = moduleElement.data('origHeight');
moduleElement.velocity('finish').velocity({
height: [ moduleHeight + 'px', [ 400, 15 ] ]
},{
duration: 400,
easing: 'easeInSine',
complete: function(){
moduleElement.removeClass( config.moduleClosedClass );
moduleElement.removeData();
}
});
}else{
moduleElement.velocity('finish').velocity({
height: [ '40px', [ 400, 15 ] ]
},{
duration: 400,
easing: 'easeInSine',
complete: function(){
moduleElement.addClass( config.moduleClosedClass );
}
});
}
}
});
};
/**
* updates only visible/active map module
* @returns {boolean}
*/
$.fn.updateMapModuleData = function(){
var mapModule = $(this);
// get all active map elements for module
var mapElement = mapModule.getActiveMap();
if(mapElement !== false){
var mapId = mapElement.data('id');
var currentMapUserData = Util.getCurrentMapUserData(mapId);
// update map with current user data
if(currentMapUserData){
mapElement.updateUserData(currentMapUserData);
}
}
return true;
};
/**
* update system info panels (below map)
* @param systemData
*/
$.fn.updateSystemModuleData = function(systemData){
var mapModule = $(this);
if(systemData){
// check if current open system is still the requested info system
var currentSystemData = Util.getCurrentSystemData();
if(currentSystemData){
if(systemData.id === currentSystemData.systemData.id){
// trigger system update event
$(document).trigger('pf:updateSystemModules', [systemData]);
}
}
}
};
/**
* load all structure elements into a TabsContent div (tab body)
*/
$.fn.initContentStructure = function(){
return this.each(function(){
// init bootstrap Grid
var contentStructure = $('<div>', {
class: ['row', config.mapTabContentRow].join(' ')
}).append(
$('<div>', {
class: ['col-xs-12', 'col-md-8', config.mapTabContentCellFirst, config.mapTabContentCell].join(' ')
})
).append(
$('<div>', {
class: ['col-xs-12', 'col-md-4', config.mapTabContentCellSecond, config.mapTabContentCell].join(' ')
})
);
// append grid structure
$(this).append(contentStructure);
});
};
/**
* get a fresh tab element
* @param options
* @returns {*|jQuery|HTMLElement}
*/
var getTabElement = function(options){
var tabElement = $('<div>', {
id: config.mapTabElementId
});
var tabBar = $('<ul>', {
class: ['nav', 'nav-tabs'].join(' '),
id: options.barId
}).attr('role', 'tablist');
var tabContent = $('<div>', {
class: 'tab-content'
}).attr('data-map-tabs', options.barId);
tabElement.append(tabBar);
tabElement.append(tabContent);
return tabElement;
};
/**
* set data for a map tab, or update an existing map tab with new data
* @param options
*/
$.fn.updateTabData = function(options){
var tabElement = $(this);
// set "main" data
tabElement.data('map-id', options.id).data('updated', options.updated);
// change "tab" link
tabElement.attr('href', '#' + config.mapTabIdPrefix + options.id);
// change "map" icon
var mapIconElement = tabElement.find('.' + config.mapTabIconClass);
mapIconElement.removeClass().addClass([config.mapTabIconClass, 'fa', 'fa-fw', options.icon].join(' '));
// change "shared" icon
var mapSharedIconElement = tabElement.find('.' + config.mapTabSharedIconClass);
mapSharedIconElement.hide();
// check if the map is a "shared" map
if(options.access){
if(
options.access.user.length > 1 ||
options.access.corporation.length > 1 ||
options.access.alliance.length > 1
){
mapSharedIconElement.show();
}
}
// change map name label
var tabLinkTextElement = tabElement.find('.' + config.mapTabLinkTextClass);
tabLinkTextElement.text(options.name);
// change tabClass
var listElement = tabElement.parent();
// new tab classes
var tabClasses = [config.mapTabClass, options.type.classTab ];
// check if tab was "active" before
if( listElement.hasClass('active') ){
tabClasses.push('active');
}
listElement.removeClass().addClass( tabClasses.join(' ') );
// set title for tooltip
if(options.type.name !== undefined){
tabLinkTextElement.attr('title', options.type.name + ' map');
}
var mapTooltipOptions = {
placement: 'bottom',
container: 'body',
trigger: 'hover',
delay: 150
};
listElement.find('[title]').tooltip(mapTooltipOptions).tooltip('fixTitle');
if(options.right === true){
listElement.addClass('pull-right');
}
};
/**
* add a new tab to tab-map-module end returns the new objects
* @param options
* @returns {{listElement: (*|void), contentElement: (*|HTMLElement)}}
*/
$.fn.addTab = function(options){
var tabElement = $(this);
var tabBar = tabElement.find('ul.nav-tabs');
var tabContent = tabElement.find('div.tab-content');
var listElement = $('<li>').attr('role', 'presentation');
if(options.right === true){
listElement.addClass('pull-right');
}
// link element
var linkElement = $('<a>').attr('role', 'tab');
// map icon element
var mapIconElement = $('<i>', {
class: config.mapTabIconClass
});
// map shared icon element
var mapSharedIconElement = $('<i>', {
class: [config.mapTabSharedIconClass, 'fa', 'fa-fw', 'fa-share-alt'].join(' '),
title: 'shared map'
});
// text element
var textElement = $('<span>', {
class: config.mapTabLinkTextClass
});
var newListElement = listElement.append(
linkElement.append(mapIconElement).append(textElement).append(mapSharedIconElement)
);
tabBar.append( newListElement );
// update Tab element -> set data
linkElement.updateTabData(options);
// tabs content =======================================================
var contentElement = $('<div>', {
id: config.mapTabIdPrefix + parseInt( options.id ),
class: [config.mapTabContentClass].join(' ')
});
contentElement.addClass('tab-pane');
tabContent.append(contentElement);
// init tab ===========================================================
linkElement.on('click', function(e){
e.preventDefault();
// callback function after tab switch
function switchTabCallback(mapElement, tabLinkElement){
tabLinkElement.tab('show');
// unfreeze map
mapElement.data('frozen', false);
return false;
}
if(mapTabChangeBlocked === false){
var tabLinkElement = $(this);
var mapId = tabLinkElement.data('map-id');
// ignore "add" tab. no need for map change
if(mapId > 0){
var mapElement = $('#' + config.mapTabElementId).getActiveMap();
if(mapId !== mapElement.data('id')){
// block tabs until switch is done
mapTabChangeBlocked = true;
// freeze active map -> no user data update while map switch
mapElement.data('frozen', true);
// hide current map with animation
mapElement.visualizeMap('hide', function(){
// un-block map tabs
mapTabChangeBlocked = switchTabCallback(mapElement, tabLinkElement);
});
}
}else{
tabLinkElement.tab('show');
}
}
});
return {
listElement: newListElement,
contentElement: contentElement
};
};
/**
* deletes a map tab for a given map id
* @param mapId
*/
$.fn.deleteTab = function(mapId){
var tabElement = $(this);
var linkElement = tabElement.find('a[href="#' + config.mapTabIdPrefix + mapId + '"]');
var deletedTabName = '';
if(linkElement.length > 0){
deletedTabName = linkElement.find('.' + config.mapTabLinkTextClass).text();
var liElement = linkElement.parent();
var contentElement = tabElement.find('div[id="' + config.mapTabIdPrefix + mapId + '"]');
var findNewActiveTab = false;
// check if liElement was active
if(liElement.hasClass('active')){
// search any remaining li element and set active
findNewActiveTab = true;
}
liElement.remove();
contentElement.remove();
// remove map instance from local cache
Map.clearMapInstance(mapId);
if(findNewActiveTab === true){
tabElement.find('.' + config.mapTabClass + ':not(.pull-right):first a').tab('show');
}
}
return deletedTabName;
};
/**
* clear all active maps
*/
$.fn.clearMapModule = function(){
var mapModuleElement = $(this);
var tabMapElement = $('#' + config.mapTabElementId);
if(tabMapElement.length > 0){
var tabElements = mapModuleElement.getMapTabElements();
for(var i = 0; i < tabElements.length; i++){
var tabElement = $(tabElements[i]);
var mapId = tabElement.data('map-id');
if(mapId > 0){
tabMapElement.deleteTab(mapId);
}
}
}
};
/**
* load/update map module into element (all maps)
* @param mapData
* @returns {boolean}
*/
$.fn.updateMapModule = function(mapData){
if(mapData.length === 0){
return true;
}
// store current map data global (cache)
// temp store current map data to prevent data-change while function execution!
var tempMapData = Util.setCurrentMapData(mapData);
var mapModuleElement = $(this);
// check if tabs module is already loaded
var tabMapElement = $('#' + config.mapTabElementId);
// check if tabs have changed
var tabsChanged = false;
if(tabMapElement.length > 0){
// tab element already exists
var tabElements = mapModuleElement.getMapTabElements();
// map ID that is currently active
var activeMapId = 0;
// mapIds that are currently active
var activeMapIds = [];
// check whether a tab/map is still active
for(var i = 0; i < tabElements.length; i++){
var tabElement = $(tabElements[i]);
var mapId = tabElement.data('map-id');
if(mapId > 0){
var tabMapData = Util.getCurrentMapData(mapId);
if(tabMapData !== false){
// map data available ->
activeMapIds.push(mapId);
// check for map data change and update tab
if(tabMapData.config.updated !== tabElement.data('updated')){
tabElement.updateTabData(tabMapData.config);
}
}else{
// map data not available -> remove tab
var deletedTabName = tabMapElement.deleteTab(mapId);
tabsChanged = true;
if(deletedTabName.length > 0){
Util.showNotify({title: 'Map removed', text: deletedTabName + ' deleted', type: 'warning'});
}
}
}
}
// add new tabs for new maps
$.each(tempMapData, function(i, data){
if( activeMapIds.indexOf( data.config.id ) === -1 ){
// add new map tab
var newTabElements = tabMapElement.addTab(data.config);
// check if there is any active map yet (this is not the case
// when ALL maps are removed AND new maps are added in one call
// e.g. character switch)
if(tabMapElement.find('.' + config.mapTabClass + '.active:not(.pull-right)').length === 0){
tabMapElement.find('.' + config.mapTabClass + ':not(.pull-right):first a').tab('show');
activeMapId = data.config.id;
}
// set observer for manually triggered map events
newTabElements.contentElement.setTabContentObserver();
// load all the structure elements for the new tab
newTabElements.contentElement.initContentStructure();
tabsChanged = true;
Util.showNotify({title: 'Map added', text: data.config.name + ' added', type: 'success'});
}
});
// get current active map
if(activeMapId === 0){
activeMapId = Util.getMapModule().getActiveMap().data('id');
}
var activeMapData = Util.getCurrentMapData(activeMapId);
if(activeMapData !== false){
// update active map with new mapData
var currentTabContentElement = $('#' + config.mapTabIdPrefix + activeMapId);
$( currentTabContentElement).loadMap( activeMapData, {} );
}
}else{
// create Tab Element
tabsChanged = true;
var options = {
barId: config.mapTabBarId
};
tabMapElement = getTabElement(options);
// add new tab for each map
for(var j = 0; j < tempMapData.length; j++){
var data = tempMapData[j];
tabMapElement.addTab(data.config);
}
// add "add" button
var tabAddOptions = {
id: 0,
type: {
classTab: Util.getInfoForMap( 'standard', 'classTab')
},
icon: 'fa-plus',
name: 'add',
right: true
};
tabMapElement.addTab(tabAddOptions);
mapModuleElement.prepend(tabMapElement);
// set first Tab active
tabMapElement.find('.' + config.mapTabClass + ':first a').tab('show');
// ==============================================================
// this new created module
var tabContentElements = tabMapElement.find('.' + config.mapTabContentClass);
// set observer for manually triggered map events
tabContentElements.setTabContentObserver();
// load all the structure elements for ALL Tab Content Body
tabContentElements.initContentStructure();
// load first map i in first tab content container
$( tabContentElements[0] ).loadMap( tempMapData[0], {showAnimation: true} );
}
if(tabsChanged === true){
// remove previous event handlers
var allTabElements = mapModuleElement.getMapTabElements();
allTabElements.off('show.bs.tab');
allTabElements.off('shown.bs.tab');
allTabElements.off('hide.bs.tab');
// check for "new map" action before tap-change
allTabElements.on('show.bs.tab', function (e) {
var mapId = $(e.target).data('map-id');
if(mapId === 0){
// add new Tab selected
$(document).trigger('pf:menuShowMapSettings', {tab: 'new'});
e.preventDefault();
}
});
// load new map right after tab-change
allTabElements.on('shown.bs.tab', function (e) {
var mapId = $(e.target).data('map-id');
var tabMapData = Util.getCurrentMapData(mapId);
if(tabMapData !== false){
// load map
var currentTabContentElement = $('#' + config.mapTabIdPrefix + mapId);
$( currentTabContentElement).loadMap( tabMapData, {showAnimation: true} );
// "wake up" scrollbar for map and get previous state back
var scrollableElement = currentTabContentElement.find('.' + config.mapWrapperClass);
$(scrollableElement).mCustomScrollbar( 'update');
}else{
// no map data found -> remove tab
tabMapElement.deleteTab(mapId);
}
});
allTabElements.on('hide.bs.tab', function (e) {
var newMapId = $(e.relatedTarget).data('map-id');
var oldMapId = $(e.target).data('map-id');
// disable map if new map is selected -> not "add button"
if(newMapId > 0){
var currentTabContentElement = $('#' + config.mapTabIdPrefix + oldMapId);
// disable scrollbar for map that will be hidden. "freeze" current state
var scrollableElement = currentTabContentElement.find('.' + config.mapWrapperClass);
$(scrollableElement).mCustomScrollbar( 'disable' );
}
});
}
return true;
};
/**
* collect all data (systems/connections) for export/save from each active map in the map module
* if no change detected -> do not attach map data to return array
* @returns {Array}
*/
$.fn.getMapModuleDataForUpdate = function(){
// get all active map elements for module
var mapElements = $(this).getMaps();
var data = [];
for(var i = 0; i < mapElements.length; i++){
// get all changed (system / connection) data from this map
var mapData = $(mapElements[i]).getMapDataFromClient({forceData: false, checkForChange: true});
if(mapData !== false){
if(
mapData.data.systems.length > 0 ||
mapData.data.connections.length > 0
){
data.push(mapData);
}
}
}
return data;
};
});

Some files were not shown because too many files have changed in this diff Show More