From 95119fcd3decd2be10849ca1db316ffd0483e3b0 Mon Sep 17 00:00:00 2001 From: Exodus4D Date: Tue, 5 Apr 2016 21:31:03 +0200 Subject: [PATCH] pathfinder-84 [Feature Request] CREST Pilot Tracking, many smaller Bugfixes --- app/main/controller/api/access.php | 23 +- app/main/controller/api/connection.php | 25 +- app/main/controller/api/map.php | 93 ++-- app/main/controller/api/signature.php | 9 +- app/main/controller/api/system.php | 15 +- app/main/controller/api/user.php | 408 +++----------- app/main/controller/appcontroller.php | 4 +- app/main/controller/ccp/sso.php | 185 ++++--- app/main/controller/controller.php | 29 +- app/main/controller/setup.php | 3 - app/main/data/mapper/abstractiterator.php | 29 +- app/main/data/mapper/ccpsystemsmapper.php | 2 +- app/main/data/mapper/igbheader.php | 47 ++ app/main/lib/web.php | 67 ++- app/main/model/basicmodel.php | 237 +++++---- app/main/model/characterlogmodel.php | 135 +++-- app/main/model/charactermodel.php | 122 ++++- app/main/model/connectionmodel.php | 17 +- app/main/model/mapmodel.php | 118 ++-- app/main/model/registrationkeymodel.php | 49 -- app/main/model/systemmodel.php | 41 +- app/main/model/systemsignaturemodel.php | 16 +- app/main/model/userapimodel.php | 143 ----- app/main/model/usercharactermodel.php | 45 +- app/main/model/usermapmodel.php | 75 --- app/main/model/usermodel.php | 288 +--------- app/pathfinder.ini | 8 +- app/routes.ini | 15 +- gulpfile.js | 28 +- js/app.js | 2 +- js/app/init.js | 1 + js/app/logging.js | 1 + js/app/login.js | 48 -- js/app/map/map.js | 19 +- js/app/mappage.js | 71 ++- js/app/module_map.js | 37 +- js/app/page.js | 53 +- js/app/ui/dialog/account_settings.js | 502 ++++-------------- js/app/ui/dialog/delete_account.js | 7 +- js/app/ui/dialog/map_info.js | 15 +- js/app/ui/dialog/map_settings.js | 30 +- js/app/ui/dialog/releases.js | 2 - js/app/ui/dialog/sharing_settings.js | 109 ---- js/app/ui/form_element.js | 7 +- js/app/ui/system_graph.js | 95 ++-- js/app/ui/system_killboard.js | 20 +- js/app/ui/system_signature.js | 4 + js/app/util.js | 89 +++- js/lib/validator.min.js | 128 +---- public/css/pathfinder.css | 2 +- public/js/v1.0.0RC3/app.js | 2 +- public/js/v1.0.0RC3/app/init.js | 1 + public/js/v1.0.0RC3/app/logging.js | 1 + public/js/v1.0.0RC3/app/login.js | 48 -- public/js/v1.0.0RC3/app/map/map.js | 19 +- public/js/v1.0.0RC3/app/mappage.js | 71 ++- public/js/v1.0.0RC3/app/module_map.js | 37 +- public/js/v1.0.0RC3/app/page.js | 53 +- .../app/ui/dialog/account_settings.js | 502 ++++-------------- .../v1.0.0RC3/app/ui/dialog/delete_account.js | 7 +- public/js/v1.0.0RC3/app/ui/dialog/map_info.js | 15 +- .../v1.0.0RC3/app/ui/dialog/map_settings.js | 30 +- public/js/v1.0.0RC3/app/ui/dialog/releases.js | 2 - .../app/ui/dialog/sharing_settings.js | 109 ---- public/js/v1.0.0RC3/app/ui/form_element.js | 7 +- public/js/v1.0.0RC3/app/ui/system_graph.js | 95 ++-- .../js/v1.0.0RC3/app/ui/system_killboard.js | 20 +- .../js/v1.0.0RC3/app/ui/system_signature.js | 4 + public/js/v1.0.0RC3/app/util.js | 89 +++- public/js/v1.0.0RC3/lib/validator.min.js | 128 +---- public/templates/dialog/delete_account.html | 15 - public/templates/dialog/map.html | 20 +- public/templates/dialog/notification.html | 5 + public/templates/dialog/settings.html | 442 +++++++-------- public/templates/dialog/sharing_settings.html | 78 --- public/templates/form/character_panel.html | 59 -- public/templates/modules/header.html | 2 +- public/templates/status/5xx.html | 9 +- public/templates/tooltip/character_info.html | 6 +- .../templates/tooltip/character_switch.html | 11 + public/templates/view/index.html | 2 +- public/templates/view/login.html | 2 +- public/templates/view/setup.html | 13 - sass/layout/_dialogs.scss | 124 +---- sass/layout/_main.scss | 1 + sass/layout/_system-info.scss | 35 +- 86 files changed, 1940 insertions(+), 3642 deletions(-) create mode 100644 app/main/data/mapper/igbheader.php delete mode 100644 app/main/model/registrationkeymodel.php delete mode 100644 app/main/model/userapimodel.php delete mode 100644 app/main/model/usermapmodel.php delete mode 100644 js/app/ui/dialog/sharing_settings.js delete mode 100644 public/js/v1.0.0RC3/app/ui/dialog/sharing_settings.js delete mode 100644 public/templates/dialog/sharing_settings.html delete mode 100644 public/templates/form/character_panel.html create mode 100644 public/templates/tooltip/character_switch.html diff --git a/app/main/controller/api/access.php b/app/main/controller/api/access.php index e21ff90d..02e45f1a 100644 --- a/app/main/controller/api/access.php +++ b/app/main/controller/api/access.php @@ -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){ diff --git a/app/main/controller/api/connection.php b/app/main/controller/api/connection.php index 6bd2d409..f4d6b460 100644 --- a/app/main/controller/api/connection.php +++ b/app/main/controller/api/connection.php @@ -40,7 +40,6 @@ class Connection extends Controller\AccessController{ $activeCharacter = $this->getCharacter(); if($activeCharacter){ - $user = $activeCharacter->getUser(); // get map model and check map access /** @@ -49,7 +48,7 @@ class Connection extends Controller\AccessController{ $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'] ); @@ -57,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'] ); @@ -103,17 +105,20 @@ class Connection extends Controller\AccessController{ $connectionIds = $f3->get('POST.connectionIds'); $activeCharacter = $this->getCharacter(); - /** - * @var Model\ConnectionModel $connection - */ - $connection = Model\BasicModel::getNew('ConnectionModel'); - foreach($connectionIds as $connectionId){ - $connection->getById($connectionId); - $connection->delete( $activeCharacter->getUser() ); + if($activeCharacter = $this->getCharacter()){ + /** + * @var Model\ConnectionModel $connection + */ + $connection = Model\BasicModel::getNew('ConnectionModel'); + foreach($connectionIds as $connectionId){ + $connection->getById($connectionId); + $connection->delete( $activeCharacter ); - $connection->reset(); + $connection->reset(); + } } + echo json_encode([]); } diff --git a/app/main/controller/api/map.php b/app/main/controller/api/map.php index 06557d04..707be222 100644 --- a/app/main/controller/api/map.php +++ b/app/main/controller/api/map.php @@ -39,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'); @@ -59,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'); @@ -72,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'); @@ -86,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'); @@ -99,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'); @@ -113,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'); @@ -127,17 +128,33 @@ 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); } /** @@ -213,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( @@ -296,7 +312,6 @@ class Map extends Controller\AccessController { $activeCharacter = $this->getCharacter(0); if($activeCharacter){ - $user = $activeCharacter->getUser(); /** * @var $map Model\MapModel @@ -306,7 +321,7 @@ class Map extends Controller\AccessController { if( $map->dry() || - $map->hasAccess($user) + $map->hasAccess($activeCharacter) ){ // new map $map->setData($formData); @@ -315,36 +330,36 @@ 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(); /** - * @var $tempUser Model\UserModel + * @var $tempCharacter Model\CharacterModel */ - $tempUser = Model\BasicModel::getNew('UserModel'); + $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()){ $corporation = $activeCharacter->getCorporation(); @@ -459,7 +474,7 @@ class Map extends Controller\AccessController { */ $map = Model\BasicModel::getNew('MapModel'); $map->getById($mapData['id']); - $map->delete( $activeCharacter->getUser() ); + $map->delete( $activeCharacter ); } echo json_encode([]); @@ -480,15 +495,15 @@ class Map extends Controller\AccessController { 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) || - !empty($mapData) + !empty($mapData) || + !$f3->exists($cacheKey) ){ // get current map data ======================================================== - $maps = $activeCharacter->getUser()->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 @@ -583,7 +598,7 @@ class Map extends Controller\AccessController { // 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; + $responseTTL = (int)$f3->get('PATHFINDER.TIMER.UPDATE_SERVER_MAP.DELAY') / 1000; $f3->set($cacheKey, $return, $responseTTL); }else{ @@ -631,17 +646,16 @@ class Map extends Controller\AccessController { public function updateUserData(\Base $f3){ $return = (object) []; $return->error = []; - $activeCharacter = $this->getCharacter(); + $activeCharacter = $this->getCharacter(0); if($activeCharacter){ - $user = $activeCharacter->getUser(); 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 - $activeCharacter->updateLog(); + $activeCharacter = $activeCharacter->updateLog(); // if data is requested extend the cache key in order to get new data $requestSystemData = (object) []; @@ -657,7 +671,7 @@ class Map extends Controller\AccessController { $cacheKey = 'user_data_' . $tempId . '_' . $requestSystemData->systemId; if( !$f3->exists($cacheKey) ){ foreach($mapIds as $mapId){ - $map = $user->getMap($mapId); + $map = $activeCharacter->getMap($mapId); if( !is_null($map) ){ $return->mapUserData[] = $map->getUserData(); @@ -677,7 +691,7 @@ class Map extends Controller\AccessController { // cache time (seconds) should be equal or less than request trigger time // prevent request flooding - $responseTTL = $f3->get('PATHFINDER.TIMER.UPDATE_SERVER_USER_DATA.DELAY') / 1000; + $responseTTL = (int)$f3->get('PATHFINDER.TIMER.UPDATE_SERVER_USER_DATA.DELAY') / 1000; // cache response $f3->set($cacheKey, $return, $responseTTL); @@ -688,9 +702,10 @@ class Map extends Controller\AccessController { $return = $f3->get($cacheKey); } } + // get current user data -> this should not be cached because each user has different personal data // even if they have multiple characters using the same map! - $return->userData = $user->getData(); + $return->userData = $activeCharacter->getUser()->getData(); }else{ // user logged off $return->error[] = $this->getLogoutError(); diff --git a/app/main/controller/api/signature.php b/app/main/controller/api/signature.php index 6b729224..623e095b 100644 --- a/app/main/controller/api/signature.php +++ b/app/main/controller/api/signature.php @@ -40,7 +40,7 @@ class Signature extends \Controller\AccessController{ if(!$system->dry()){ // check access - if( $system->hasAccess($activeCharacter->getUser()) ){ + if( $system->hasAccess($activeCharacter) ){ $signatureData = $system->getSignaturesData(); } } @@ -75,7 +75,6 @@ class Signature extends \Controller\AccessController{ $activeCharacter = $this->getCharacter(); if($activeCharacter){ - $user = $activeCharacter->getUser(); /** * @var Model\SystemModel $system @@ -95,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) ){ @@ -186,7 +185,7 @@ class Signature extends \Controller\AccessController{ $signature = Model\BasicModel::getNew('SystemSignatureModel'); foreach($signatureIds as $signatureId){ $signature->getById($signatureId); - $signature->delete( $activeCharacter->getUser() ); + $signature->delete( $activeCharacter ); $signature->reset(); } diff --git a/app/main/controller/api/system.php b/app/main/controller/api/system.php index ea57f956..636e1aea 100644 --- a/app/main/controller/api/system.php +++ b/app/main/controller/api/system.php @@ -192,7 +192,6 @@ class System extends \Controller\AccessController { $activeCharacter = $this->getCharacter(); if($activeCharacter){ - $user = $activeCharacter->getUser(); $systemData = (array)$postData['systemData']; $mapData = (array)$postData['mapData']; @@ -205,7 +204,7 @@ class System extends \Controller\AccessController { $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; } @@ -219,7 +218,7 @@ class System extends \Controller\AccessController { $map = Model\BasicModel::getNew('MapModel'); $map->getById($mapData['id']); if( !$map->dry() ){ - if( $map->hasAccess($user) ){ + if( $map->hasAccess($activeCharacter) ){ $systemData['mapId'] = $map; @@ -270,9 +269,9 @@ class System extends \Controller\AccessController { $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; @@ -340,17 +339,15 @@ class System extends \Controller\AccessController { */ public function delete(\Base $f3){ $systemIds = $f3->get('POST.systemIds'); - $activeCharacter = $this->getCharacter(); - if($activeCharacter){ - $user = $activeCharacter->getUser(); + if($activeCharacter = $this->getCharacter()){ /** * @var Model\SystemModel $system */ $system = Model\BasicModel::getNew('SystemModel'); foreach((array)$systemIds as $systemId){ $system->getById($systemId); - $system->delete($user); + $system->delete($activeCharacter); $system->reset(); } } diff --git a/app/main/controller/api/user.php b/app/main/controller/api/user.php index 2014ab47..0343c207 100644 --- a/app/main/controller/api/user.php +++ b/app/main/controller/api/user.php @@ -15,6 +15,10 @@ 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'; @@ -36,7 +40,7 @@ class User extends Controller\Controller{ * valid reasons for captcha images * @var string array */ - private static $captchaReason = ['createAccount', 'deleteAccount']; + private static $captchaReason = [self::SESSION_CAPTCHA_ACCOUNT_UPDATE, self::SESSION_CAPTCHA_ACCOUNT_DELETE]; /** * login a valid character @@ -106,7 +110,7 @@ class User extends Controller\Controller{ 'fonts/oxygen-bold-webfont.ttf', 14, 6, - 'SESSION.' . $reason, + $reason, '', $colorText, $colorBG @@ -155,14 +159,12 @@ class User extends Controller\Controller{ $return = (object) []; - $privateSharing = 0; - $corporationSharing = 0; - $allianceSharing = 0; - $activeCharacter = $this->getCharacter(); if($activeCharacter){ - $user = $activeCharacter->getUser(); + $privateSharing = 0; + $corporationSharing = 0; + $allianceSharing = 0; // form values if(isset($data['formData'])){ @@ -181,8 +183,8 @@ class User extends Controller\Controller{ } } - $user->shared = $privateSharing; - $user->save(); + $activeCharacter->shared = $privateSharing; + $activeCharacter = $activeCharacter->save(); // update corp/ally --------------------------------------------------------------- $corporation = $activeCharacter->getCorporation(); @@ -198,6 +200,7 @@ class User extends Controller\Controller{ $alliance->save(); } + $user = $activeCharacter->getUser(); $return->userData = $user->getData(); } @@ -205,61 +208,9 @@ 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 - */ - 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 + * update user account data + * -> a fresh user automatically generated on first login with a new character + * -> see CREST SSO login * @param \Base $f3 */ public function saveAccount(\Base $f3){ @@ -268,183 +219,100 @@ class User extends Controller\Controller{ $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{ - $activeCharacter = $this->getCharacter(0); - $user = $activeCharacter->getUser(); + 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(is_null($user)){ - - // 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 = 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->logInByData( $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 $newUserData = $user->getData(); } + }catch(Exception\ValidationException $e){ $validationError = (object) []; $validationError->type = 'error'; @@ -461,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 \Base $f3 - * @throws Exception - */ - public function sendInvite(\Base $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); @@ -577,10 +342,10 @@ class User extends Controller\Controller{ $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']) && @@ -589,13 +354,8 @@ class User extends Controller\Controller{ ){ $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 . ',

'; $msg .= 'your account data has been successfully deleted.'; @@ -616,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 diff --git a/app/main/controller/appcontroller.php b/app/main/controller/appcontroller.php index 8317796b..4874609a 100644 --- a/app/main/controller/appcontroller.php +++ b/app/main/controller/appcontroller.php @@ -19,7 +19,9 @@ class AppController extends Controller { parent::afterroute($f3); // clear all SSO related temp data - $f3->clear(Ccp\Sso::SESSION_KEY_SSO); + if( $f3->exists(Ccp\Sso::SESSION_KEY_SSO) ){ + $f3->clear(Ccp\Sso::SESSION_KEY_SSO); + } } /** diff --git a/app/main/controller/ccp/sso.php b/app/main/controller/ccp/sso.php index 956d8050..49e704b9 100644 --- a/app/main/controller/ccp/sso.php +++ b/app/main/controller/ccp/sso.php @@ -34,6 +34,10 @@ class Sso extends Api\User{ 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'; @@ -44,6 +48,9 @@ class Sso extends Api\User{ 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 @@ -62,6 +69,12 @@ class Sso extends Api\User{ * @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)); @@ -89,6 +102,10 @@ class Sso extends Api\User{ 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( @@ -98,8 +115,15 @@ class Sso extends Api\User{ !empty($getParams['state']) && $f3->get(self::SESSION_KEY_SSO_STATE) === $getParams['state'] ){ - // clear 'state' for new next request + // $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']); @@ -111,74 +135,91 @@ class Sso extends Api\User{ $verificationCharacterData = $this->verifyCharacterData($accessData->accessToken); if( !is_null($verificationCharacterData)){ - // verification data available. Data is needed for "ownerHash" check - // get character data from CREST - $characterData = $this->getCharacterData($accessData->accessToken); + // check if login is restricted to a characterID + if( + $requiredCharacterId <= 0 || + $verificationCharacterData->CharacterID === $requiredCharacterId + ){ + // verification available data. Data is needed for "ownerHash" check - if(isset($characterData->character)){ - // add "ownerHash" and CREST tokens - $characterData->character['ownerHash'] = $verificationCharacterData->CharacterOwnerHash; - $characterData->character['crestAccessToken'] = $accessData->accessToken; - $characterData->character['crestRefreshToken'] = $accessData->refreshToken; + // get character data from CREST + $characterData = $this->getCharacterData($accessData->accessToken); - // add/update static character data - $characterModel = $this->updateCharacter($characterData); + if(isset($characterData->character)){ + // add "ownerHash" and CREST tokens + $characterData->character['ownerHash'] = $verificationCharacterData->CharacterOwnerHash; + $characterData->character['crestAccessToken'] = $accessData->accessToken; + $characterData->character['crestRefreshToken'] = $accessData->refreshToken; - if( !is_null($characterModel) ){ - // check if character is authorized to log in - if($characterModel->isAuthorized()){ + // add/update static character data + $characterModel = $this->updateCharacter($characterData); - // character is authorized to log in - // -> update character log (current location,...) - $characterModel = $characterModel->updateLog(); + if( !is_null($characterModel) ){ + // check if character is authorized to log in + if($characterModel->isAuthorized()){ - // check if there is already a user created who owns this char - $user = $characterModel->getUser(); + // character is authorized to log in + // -> update character log (current location,...) + $characterModel = $characterModel->updateLog(); - if(is_null($user)){ - // no user found -> create one and connect to character - /** - * @var Model\UserModel $user - */ - $user = Model\BasicModel::getNew('UserModel'); - $user->name = $characterModel->name; - $user->save(); + // 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(); + } - /** - * @var Model\UserCharacterModel $userCharactersModel - */ - $userCharactersModel = Model\BasicModel::getNew('UserCharacterModel'); + if( is_null($userCharactersModel = $characterModel->userCharacter) ){ + $userCharactersModel = Model\BasicModel::getNew('UserCharacterModel'); + $userCharactersModel->characterId = $characterModel; + } + + // user might have changed $userCharactersModel->userId = $user; - $userCharactersModel->characterId = $characterModel; $userCharactersModel->save(); // get updated character model $characterModel = $userCharactersModel->getCharacter(); - } - // login by character - $loginCheck = $this->loginByCharacter($characterModel); + // login by character + $loginCheck = $this->loginByCharacter($characterModel); - if($loginCheck){ - // route to "map" - $f3->reroute('@map'); + if($loginCheck){ + // route to "map" + $f3->reroute('@map'); + }else{ + $f3->set(self::SESSION_KEY_SSO_ERROR, sprintf(self::ERROR_LOGIN_FAILED, $characterModel->name)); + } }else{ - $f3->set(self::SESSION_KEY_SSO_ERROR, sprintf(self::ERROR_LOGIN_FAILED, $characterModel->name)); + // character is not authorized to log in + $f3->set(self::SESSION_KEY_SSO_ERROR, sprintf(self::ERROR_CHARACTER_FORBIDDEN, $characterModel->name)); } - }else{ - // character is not authorized to log in - $f3->set(self::SESSION_KEY_SSO_ERROR, 'Character "' . $characterModel->name . '" is not authorized to log in.'); } } + }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)); } } } - // on error -> route back to login form - $f3->reroute('@login'); + if($authFromMapAlias){ + // on error -> route back to map + $f3->reroute('@map'); + }else{ + // on error -> route back to login form + $f3->reroute('@login'); + } } /** @@ -266,16 +307,18 @@ class Sso extends Api\User{ $apiResponse = Lib\Web::instance()->request($verifyAuthCodeUrl, $requestOptions); if($apiResponse['body']){ - $authCodeRequestData = json_decode($apiResponse['body']); + $authCodeRequestData = json_decode($apiResponse['body'], true); - if(isset($authCodeRequestData->access_token)){ - // this token is required for endpoints that require Auth - $accessData->accessToken = $authCodeRequestData->access_token; - } + 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; + 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( @@ -450,25 +493,37 @@ class Sso extends Api\User{ } /** - * get current character location data + * get current character location data (result is cached!) * -> solarSystem data where character is currently active * @param $accessToken - * @return object + * @param int $ttl + * @return array */ - public function getCharacterLocationData($accessToken){ - $endpoints = $this->getEndpoints($accessToken); - $locationData = (object) []; + public function getCharacterLocationData($accessToken, $ttl = 10){ + $locationData = []; - $endpoint = $this->walkEndpoint($accessToken, $endpoints, [ - 'decode', - 'character', - 'location' - ]); + // 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( !empty($endpoint) ){ - if(isset($endpoint['solarSystem'])){ - $locationData->system = (new Mapper\CrestSystem($endpoint['solarSystem']))->getData(); + 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; diff --git a/app/main/controller/controller.php b/app/main/controller/controller.php index bdee6ad7..4f1603c9 100644 --- a/app/main/controller/controller.php +++ b/app/main/controller/controller.php @@ -120,7 +120,7 @@ class Controller { * @return Model\CharacterModel|null * @throws \Exception */ - public function getCharacter($ttl = 5){ + public function getCharacter($ttl = 0){ $character = null; if( $this->getF3()->exists(Api\User::SESSION_KEY_CHARACTER_ID) ){ @@ -170,33 +170,6 @@ class Controller { } } - /** - * verifies weather a given username and password is valid - * @param string $userName - * @param string $password - * @return Model\UserModel|null - */ - protected function _verifyUser($userName, $password) { - $validUser = null; - - /** - * @var $user \Model\UserModel - */ - $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 boolean diff --git a/app/main/controller/setup.php b/app/main/controller/setup.php index 709086e2..22c937f8 100644 --- a/app/main/controller/setup.php +++ b/app/main/controller/setup.php @@ -30,17 +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', diff --git a/app/main/data/mapper/abstractiterator.php b/app/main/data/mapper/abstractiterator.php index cd8892e9..4ce14274 100644 --- a/app/main/data/mapper/abstractiterator.php +++ b/app/main/data/mapper/abstractiterator.php @@ -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; diff --git a/app/main/data/mapper/ccpsystemsmapper.php b/app/main/data/mapper/ccpsystemsmapper.php index 1eac1157..d9d347b0 100644 --- a/app/main/data/mapper/ccpsystemsmapper.php +++ b/app/main/data/mapper/ccpsystemsmapper.php @@ -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() ); diff --git a/app/main/data/mapper/igbheader.php b/app/main/data/mapper/igbheader.php new file mode 100644 index 00000000..ab46894f --- /dev/null +++ b/app/main/data/mapper/igbheader.php @@ -0,0 +1,47 @@ + ['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'] + ]; +} \ No newline at end of file diff --git a/app/main/lib/web.php b/app/main/lib/web.php index 7c9e89b9..28ee6fd5 100644 --- a/app/main/lib/web.php +++ b/app/main/lib/web.php @@ -8,8 +8,15 @@ 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 @@ -79,7 +86,7 @@ class Web extends \Web { $options['method'] . ' ' . $url . ' ' . $headers - ).'.url'; + ) . 'url'; } /** @@ -96,11 +103,42 @@ class Web extends \Web { $result = parent::request($url, $options); $statusCode = $this->getStatuscodeFromHeaders( $result['headers'] ); - if($statusCode == 200){ - // request succeeded -> check if response should be cached - if( $ttl = $this->getCacheTimeFromHeaders( $result['headers'] ) ){ - $f3->set($hash, $result, $ttl); - } + 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); @@ -109,4 +147,21 @@ class Web extends \Web { 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; + } + } \ No newline at end of file diff --git a/app/main/model/basicmodel.php b/app/main/model/basicmodel.php index 900a88e6..672ef3b2 100644 --- a/app/main/model/basicmodel.php +++ b/app/main/model/basicmodel.php @@ -108,18 +108,27 @@ abstract 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'); } + } } @@ -247,6 +256,67 @@ abstract 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 @@ -350,10 +420,10 @@ abstract class BasicModel extends \DB\Cortex { /** * function should be overwritten in child classes with access restriction - * @param UserModel $user + * @param CharacterModel $characterModel * @return bool */ - public function hasAccess(UserModel $user){ + public function hasAccess(CharacterModel $characterModel){ return true; } @@ -365,113 +435,6 @@ abstract class BasicModel extends \DB\Cortex { return true; } - /** - * 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); - } - - } - } - - /** - * 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); - } - /** * export and download table data as *.csv * this is primarily used for static tables @@ -584,6 +547,52 @@ abstract 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 diff --git a/app/main/model/characterlogmodel.php b/app/main/model/characterlogmodel.php index fd47696c..adc1b08e 100644 --- a/app/main/model/characterlogmodel.php +++ b/app/main/model/characterlogmodel.php @@ -41,6 +41,9 @@ class CharacterLogModel extends BasicModel { ] ] ], + + // -------------------------------------------------------------------- + 'systemId' => [ 'type' => Schema::DT_INT, 'index' => true @@ -50,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 @@ -59,32 +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){ - - parent::__construct($db, $table, $fluid, $ttl); - - // events ----------------------------------------- - $this->beforeerase(function($self){ - $self->clearCacheData(); - }); - } - /** - * set log data from object - * @param object $logData + * set log data from array + * @param array $logData */ public function setData($logData){ - if( !empty($logData->system) ){ - $this->systemId = $logData->system['id']; - $this->systemName = $logData->system['name']; + + 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 = ''; + } + } /** @@ -95,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; - } } \ No newline at end of file diff --git a/app/main/model/charactermodel.php b/app/main/model/charactermodel.php index 3e16177a..f69ae2a4 100644 --- a/app/main/model/charactermodel.php +++ b/app/main/model/charactermodel.php @@ -8,8 +8,10 @@ namespace Model; +use Controller; use Controller\Ccp; use DB\SQL\Schema; +use Data\Mapper as Mapper; class CharacterModel extends BasicModel { @@ -78,11 +80,19 @@ 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'] ] ]; @@ -92,16 +102,24 @@ class CharacterModel extends BasicModel { * @return \stdClass */ public function getData($addCharacterLogData = false){ + $characterData = null; + + $cacheKeyModifier = ''; // check if there is cached data - // temporary disabled (performance test) - $characterData = $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()){ @@ -122,7 +140,7 @@ 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; @@ -314,37 +332,58 @@ class CharacterModel extends BasicModel { /** * update character log (active system, ...) - * -> CREST API request for character log data + * -> HTTP Header Data (if IGB) + * -> CREST API request for character log data (if not IGB) * @return CharacterModel */ public function updateLog(){ - $characterModel = $this; - $ssoController = new Ccp\Sso(); + $logData = []; + $headerData = Controller\Controller::getIGBHeaderData(); - $locationData = $ssoController->getCharacterLocationData($this->getAccessToken()); + // check if IGB Data is available + if( + $headerData->trusted === true && + !empty($headerData->values) + ){ + // format header data + $formattedHeaderData = (new Mapper\IgbHeader($headerData->values))->getData(); - if( empty((array)$locationData) ){ + // 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(); - $characterModel = $this->save(); + $this->save(); } }else{ // character is currently in-game if( !$characterLog = $this->getLog() ){ // create new log $characterLog = $this->rel('characterLog'); - $characterLog->characterId = $this; + $characterLog->characterId = $this->_id; } - $characterLog->setData($locationData); + $characterLog->setData($logData); $characterLog->save(); + $this->characterLog = $characterLog; - $characterModel = $this->save(); } - return $characterModel; + return $this; } /** @@ -358,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; + } + } \ No newline at end of file diff --git a/app/main/model/connectionmodel.php b/app/main/model/connectionmodel.php index 26a999cd..ab1503e3 100644 --- a/app/main/model/connectionmodel.php +++ b/app/main/model/connectionmodel.php @@ -103,11 +103,11 @@ class ConnectionModel extends BasicModel{ /** * check object for model access - * @param UserModel $user + * @param CharacterModel $characterModel * @return mixed */ - public function hasAccess(UserModel $user){ - return $this->mapId->hasAccess($user); + public function hasAccess(CharacterModel $characterModel){ + return $this->mapId->hasAccess($characterModel); } /** @@ -127,13 +127,12 @@ class ConnectionModel extends BasicModel{ /** * delete a connection - * @param UserModel $user + * @param CharacterModel $characterModel */ - public function delete(UserModel $user){ - - if(!$this->dry()){ - // check if editor has access - if($this->hasAccess($user)){ + public function delete(CharacterModel $characterModel){ + if( !$this->dry() ){ + // check if character has access + if($this->hasAccess($characterModel)){ $this->erase(); } } diff --git a/app/main/model/mapmodel.php b/app/main/model/mapmodel.php index 254e537b..757029c3 100644 --- a/app/main/model/mapmodel.php +++ b/app/main/model/mapmodel.php @@ -61,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'] @@ -157,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; @@ -176,6 +182,9 @@ class MapModel extends BasicModel { $allianceData = []; foreach($alliances as $alliance){ + /** + * @var $alliance AllianceModel + */ $allianceData[] = $alliance->getData(); } $mapData->access->alliance = $allianceData; @@ -230,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){ @@ -251,6 +261,9 @@ class MapModel extends BasicModel { $systemData = []; foreach($systems as $system){ + /** + * @var $system SystemModel + */ $systemData[] = $system->getData(); } @@ -284,6 +297,9 @@ class MapModel extends BasicModel { $connectionData = []; foreach($connections as $connection){ + /** + * @var $connection ConnectionModel + */ $connectionData[] = $connection->getData(); } @@ -291,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; } @@ -356,16 +372,16 @@ class MapModel extends BasicModel { * clear access for a given type of objects * @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 $userMapModel){ + case 'character': + foreach((array)$this->mapCharacters as $characterMapModel){ /** - * @var UserMapModel $userMapModel + * @var CharacterMapModel $characterMapModel */ - $userMapModel->erase(); + $characterMapModel->erase(); }; break; case 'corporation': @@ -389,17 +405,17 @@ class MapModel extends BasicModel { } /** - * checks weather a user has access to this map or not - * @param UserModel $user + * checks weather a character has access to this map or not + * @param CharacterModel $characterModel * @return bool */ - public function hasAccess(UserModel $user){ + public function hasAccess(CharacterModel $characterModel){ $hasAccess = false; 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; @@ -412,42 +428,39 @@ class MapModel extends BasicModel { } /** - * get all user models that have access to this map - * note: This function is just for "private" maps - * @return UserModel[] + * get all (private) characters for this map + * @return CharacterModel array */ - public function getUsers(){ - $users = []; + private function getCharacters(){ + $characters = []; - if($this->isPrivate()){ - $this->filter('mapUsers', ['active = ?', 1]); + $this->filter('mapCharacters', ['active = ?', 1]); - if($this->mapUsers){ - foreach($this->mapUsers as $mapUser){ - $users[] = $mapUser->userId; - } + if($this->mapCharacters){ + foreach($this->mapCharacters as $characterMapModel){ + $characters[] = $characterMapModel->characterId; } } - return $users; + return $characters; } /** * get all character models that are currently online "viewing" this map * @return CharacterModel[] */ - private function getCharacters(){ + private function getActiveCharacters(){ $characters = []; if($this->isPrivate()){ - $users = $this->getUsers(); + $activeCharacters = $this->getCharacters(); // add active character for each user - foreach($users as $user){ + foreach($activeCharacters as $activeCharacter){ /** * @var UserModel $user */ - $characters = array_merge($characters, $user->getActiveCharacters()); + $characters[] = $activeCharacter; } }elseif($this->isCorporation()){ $corporations = $this->getCorporations(); @@ -484,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); @@ -492,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); } } @@ -541,16 +554,13 @@ class MapModel extends BasicModel { /** * delete this map and all dependencies - * @param UserModel $user + * @param CharacterModel $characterModel */ - public function delete(UserModel $user){ - - if(!$this->dry()){ - // check if user has access - if($this->hasAccess($user)){ + 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(); } } @@ -663,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']); } } diff --git a/app/main/model/registrationkeymodel.php b/app/main/model/registrationkeymodel.php deleted file mode 100644 index 0b3ddc7b..00000000 --- a/app/main/model/registrationkeymodel.php +++ /dev/null @@ -1,49 +0,0 @@ - [ - '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 - ] - ]; - -} \ No newline at end of file diff --git a/app/main/model/systemmodel.php b/app/main/model/systemmodel.php index 7a40d669..170cc828 100644 --- a/app/main/model/systemmodel.php +++ b/app/main/model/systemmodel.php @@ -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 UserModel $user + * @param CharacterModel $characterModel * @return mixed */ - public function hasAccess(UserModel $user){ - return $this->mapId->hasAccess($user); + 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 UserModel $user + * @param CharacterModel $characterModel */ - public function delete(UserModel $user){ - - if(! $this->dry()){ - // check if user has access - if($this->hasAccess($user)){ + 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 UserModel $user + * @param CharacterModel $characterModel * @param $id * @return bool|null */ - public function getSignatureById(UserModel $user, $id){ + public function getSignatureById(CharacterModel $characterModel, $id){ $signature = null; - if($this->hasAccess($user)){ + 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 UserModel $user + * @param CharacterModel $characterModel * @param $name * @return mixed|null */ - public function getSignatureByName(UserModel $user, $name){ + public function getSignatureByName(CharacterModel $characterModel, $name){ $signature = null; - if($this->hasAccess($user)){ + if($this->hasAccess($characterModel)){ $this->filter('signatures', ['active = ? AND name = ?', 1, $name]); if($this->signatures){ $signature = reset( $this->signatures ); diff --git a/app/main/model/systemsignaturemodel.php b/app/main/model/systemsignaturemodel.php index 682b3caa..258dea66 100644 --- a/app/main/model/systemsignaturemodel.php +++ b/app/main/model/systemsignaturemodel.php @@ -151,21 +151,21 @@ class SystemSignatureModel extends BasicModel { /** * check object for model access - * @param UserModel $user + * @param CharacterModel $characterModel * @return bool */ - public function hasAccess(UserModel $user){ - return $this->systemId->hasAccess($user); + public function hasAccess(CharacterModel $characterModel){ + return $this->systemId->hasAccess($characterModel); } /** * delete signature - * @param UserModel $user + * @param CharacterModel $characterModel */ - public function delete(UserModel $user){ - if(!$this->dry()){ - // check if editor has access - if($this->hasAccess($user)){ + public function delete(CharacterModel $characterModel){ + if( !$this->dry() ){ + // check if character has access + if($this->hasAccess($characterModel)){ $this->erase(); } } diff --git a/app/main/model/userapimodel.php b/app/main/model/userapimodel.php deleted file mode 100644 index a43701c1..00000000 --- a/app/main/model/userapimodel.php +++ /dev/null @@ -1,143 +0,0 @@ - [ - '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; - } - - /** - * 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(); - } - - } - -} \ No newline at end of file diff --git a/app/main/model/usercharactermodel.php b/app/main/model/usercharactermodel.php index eb852d2b..5d661286 100644 --- a/app/main/model/usercharactermodel.php +++ b/app/main/model/usercharactermodel.php @@ -32,17 +32,6 @@ 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, @@ -54,12 +43,6 @@ class UserCharacterModel extends BasicModel { 'on-delete' => 'CASCADE' ] ] - ], - 'isMain' => [ - 'type' => Schema::DT_BOOLEAN, - 'nullable' => false, - 'default' => 0, - 'index' => true ] ]; @@ -68,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; @@ -95,27 +76,6 @@ class UserCharacterModel extends BasicModel { return true; } - - /** - * check if this character is Main character or not - * @return bool - */ - public function isMain(){ - $isMain = false; - if($this->isMain == 1){ - $isMain = true; - } - - return $isMain; - } - - /** - * set this character as main character - */ - public function setMain($value = 0){ - $this->isMain = $value; - } - /** * get the character model of this character * @return mixed @@ -135,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; diff --git a/app/main/model/usermapmodel.php b/app/main/model/usermapmodel.php deleted file mode 100644 index bd39f5cb..00000000 --- a/app/main/model/usermapmodel.php +++ /dev/null @@ -1,75 +0,0 @@ - [ - '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' - ] - ] - ], - 'mapId' => [ - 'type' => Schema::DT_INT, - 'index' => true, - 'belongs-to-one' => 'Model\MapModel', - 'constraint' => [ - [ - 'table' => 'map', - 'on-delete' => 'CASCADE' - ] - ] - ] - ]; - - /** - * see parent - */ - public function clearCacheData(){ - parent::clearCacheData(); - - // clear map cache as well - $this->mapId->clearCacheData(); - } - - /** - * overwrites parent - * @param null $db - * @param null $table - * @param null $fields - * @return bool - */ - public static function setup($db=null, $table=null, $fields=null){ - $status = parent::setup($db,$table,$fields); - - if($status === true){ - $status = parent::setMultiColumnIndex(['userId', 'mapId'], true); - } - - return $status; - } - -} \ No newline at end of file diff --git a/app/main/model/usermodel.php b/app/main/model/usermodel.php index 195b0645..b8f47662 100644 --- a/app/main/model/usermodel.php +++ b/app/main/model/usermodel.php @@ -35,24 +35,8 @@ class UserModel extends BasicModel { 'nullable' => false, 'default' => '' ], - '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'] ] ]; @@ -62,11 +46,6 @@ class UserModel extends BasicModel { 'min' => 5, 'max' => 25 ] - ], - 'password' => [ - 'length' => [ - 'min' => 6 - ] ] ]; @@ -84,9 +63,6 @@ class UserModel extends BasicModel { // add sensitive user data $userData->email = $this->email; - // user shared info - $userData->shared = $this->shared; - // all chars $userData->characters = []; $characters = $this->getCharacters(); @@ -97,7 +73,7 @@ class UserModel extends BasicModel { $userData->characters[] = $character->getData(); } - // set active character with log data + // get active character with log data $activeCharacter = $this->getActiveCharacter(); $userData->character = $activeCharacter->getData(true); @@ -134,20 +110,6 @@ class UserModel extends BasicModel { return $email; } - /** - * set a password hash for this user - * @param string $password - * @return 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 @@ -179,133 +141,6 @@ 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 MapModel[] - */ - public function getMaps(){ - - $this->filter( - 'userMaps', - ['active = ?', 1], - ['order' => 'created'] - ); - - $maps = []; - if($this->userMaps){ - $mapCountPrivate = 0; - foreach($this->userMaps as &$userMap){ - if( - $userMap->mapId->isActive() && - $mapCountPrivate < self::getF3()->get('PATHFINDER.MAX_MAPS_PRIVATE') - ){ - $maps[] = &$userMap->mapId; - $mapCountPrivate++; - } - } - } - - // get current active character - $controller = new Controller\Controller(); - $activeCharacter = $controller->getCharacter(); - $corporation = $activeCharacter->getCorporation(); - $alliance = $activeCharacter->getAlliance(); - - if($alliance){ - $maps = array_merge($maps, $alliance->getMaps()); - } - - if($corporation){ - $maps = array_merge($maps, $corporation->getMaps()); - } - - return $maps; - } - - /** - * get mapModel by id and check if user has access - * @param int $mapId - * @return MapModel|null - * @throws Exception - */ - 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 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 @@ -322,24 +157,6 @@ class UserModel extends BasicModel { return $userCharacters; } - /** - * Get the main user character for this user - * @return null - */ - public function getMainUserCharacter(){ - $mainUserCharacter = null; - $userCharacters = $this->getUserCharacters(); - - foreach($userCharacters as $userCharacter){ - if($userCharacter->isMain()){ - $mainUserCharacter = $userCharacter; - break; - } - } - - return $mainUserCharacter; - } - /** * get the current active character for this user * -> EITHER - the current active one for the current user @@ -359,7 +176,7 @@ class UserModel extends BasicModel { }else{ // set "first" found as active for this user if($activeCharacters = $this->getActiveCharacters()){ - $activeCharacter = &$activeCharacters[0]; + $activeCharacter = $activeCharacters[0]; } } @@ -409,105 +226,4 @@ class UserModel extends BasicModel { return $activeCharacters; } - /** - * 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\Controller::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); - } - } - } - */ - - } \ No newline at end of file diff --git a/app/pathfinder.ini b/app/pathfinder.ini index e793d78b..3406204e 100644 --- a/app/pathfinder.ini +++ b/app/pathfinder.ini @@ -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 @@ -35,8 +35,8 @@ INVITE = 0 INVITE_LIMIT = 50 [PATHFINDER.LOGIN] - -CORPORATION = 1000166 +; restrict login to specific corporations/alliances by id (e.g. 1000166,1000080) +CORPORATION = ALLIANCE = ; View ============================================================================================ @@ -83,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] diff --git a/app/routes.ini b/app/routes.ini index a389954f..20318742 100644 --- a/app/routes.ini +++ b/app/routes.ini @@ -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\Ccp\Sso->@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 diff --git a/gulpfile.js b/gulpfile.js index 662fa753..8dbdfc7c 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -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 */ diff --git a/js/app.js b/js/app.js index 529205b1..187a9beb 100644 --- a/js/app.js +++ b/js/app.js @@ -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 diff --git a/js/app/init.js b/js/app/init.js index 477638d3..6d14aedf 100644 --- a/js/app/init.js +++ b/js/app/init.js @@ -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 diff --git a/js/app/logging.js b/js/app/logging.js index b9280511..ad74c1b9 100644 --- a/js/app/logging.js +++ b/js/app/logging.js @@ -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', diff --git a/js/app/login.js b/js/app/login.js index 38f94603..430d7ded 100644 --- a/js/app/login.js +++ b/js/app/login.js @@ -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(); diff --git a/js/app/map/map.js b/js/app/map/map.js index 097f0d7e..b438d308 100644 --- a/js/app/map/map.js +++ b/js/app/map/map.js @@ -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 + }; + }); \ No newline at end of file diff --git a/js/app/mappage.js b/js/app/mappage.js index bd50e472..16b8b6f8 100644 --- a/js/app/mappage.js +++ b/js/app/mappage.js @@ -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([ }; - }); }); \ No newline at end of file diff --git a/js/app/module_map.js b/js/app/module_map.js index cfdd733c..dd6a1626 100644 --- a/js/app/module_map.js +++ b/js/app/module_map.js @@ -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 = $('
', { @@ -395,7 +400,7 @@ define([ // update Tab element -> set data linkElement.updateTabData(options); - // tabs content ==================================== + // tabs content ======================================================= var contentElement = $('
', { 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){ diff --git a/js/app/page.js b/js/app/page.js index 4358055c..d38b1895 100644 --- a/js/app/page.js +++ b/js/app/page.js @@ -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([ $('', { class: 'list-group-item', href: '#' - }).html('  Sharing settings').prepend( + }).html('  Settings').prepend( $('',{ - class: 'fa fa-share-alt fa-fw' + class: 'fa fa-gears fa-fw' }) ).on('click', function(){ - $(document).triggerMenuEvent('ShowSharingSettings'); - }) + $(document).triggerMenuEvent('ShowSettingsDialog'); + }) ).append( $('', { 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 diff --git a/js/app/ui/dialog/account_settings.js b/js/app/ui/dialog/account_settings.js index 10ae3165..e6c2cbd1 100644 --- a/js/app/ui/dialog/account_settings.js +++ b/js/app/ui/dialog/account_settings.js @@ -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: '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: ' save', + className: 'btn-success', + callback: function() { - return false; - } - }, - next: { - label: 'next', - 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: ' Enable', + off: 'Disable ', + onstyle: 'success', + offstyle: 'warning', + width: 90, + height: 30 }); }); - - }); - }; - }); \ No newline at end of file diff --git a/js/app/ui/dialog/delete_account.js b/js/app/ui/dialog/delete_account.js index 590c8946..5facd5c3 100644 --- a/js/app/ui/dialog/delete_account.js +++ b/js/app/ui/dialog/delete_account.js @@ -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); }); }); diff --git a/js/app/ui/dialog/map_info.js b/js/app/ui/dialog/map_info.js index 4f65b8db..aefe4c5c 100644 --- a/js/app/ui/dialog/map_info.js +++ b/js/app/ui/dialog/map_info.js @@ -642,7 +642,6 @@ define([ } } } -console.log(usersData); var userDataTable = userTable.dataTable( { pageLength: 20, @@ -670,7 +669,7 @@ console.log(usersData); data: 'log.ship', render: { _: function(data, type, row, meta){ - return ''; + return ''; } } },{ @@ -736,6 +735,16 @@ console.log(usersData); } },{ 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 @@ console.log(usersData); }); } },{ - targets: 8, + targets: 9, title: '', orderable: false, searchable: false, diff --git a/js/app/ui/dialog/map_settings.js b/js/app/ui/dialog/map_settings.js index 61f0468f..0a8cbf59 100644 --- a/js/app/ui/dialog/map_settings.js +++ b/js/app/ui/dialog/map_settings.js @@ -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){ diff --git a/js/app/ui/dialog/releases.js b/js/app/ui/dialog/releases.js index edefaa08..6a7c1a90 100644 --- a/js/app/ui/dialog/releases.js +++ b/js/app/ui/dialog/releases.js @@ -47,8 +47,6 @@ define([ releasesDialog.find('ul.timeline').append(content); } - // console.log() - $('.timeline > li').velocity('transition.expandIn', { stagger: 300, duration: 240, diff --git a/js/app/ui/dialog/sharing_settings.js b/js/app/ui/dialog/sharing_settings.js deleted file mode 100644 index bba2b5e6..00000000 --- a/js/app/ui/dialog/sharing_settings.js +++ /dev/null @@ -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: ' 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: ' Enable', - off: 'Disable ', - onstyle: 'success', - offstyle: 'warning', - width: 90, - height: 30 - }); - }); - }); - }else{ - Util.showNotify({title: 'No userData found', type: 'warning'}); - - } - } - }; - -}); \ No newline at end of file diff --git a/js/app/ui/form_element.js b/js/app/ui/form_element.js index 9259d9b2..a6f839d2 100644 --- a/js/app/ui/form_element.js +++ b/js/app/ui/form_element.js @@ -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 = ''; + case 'character': + imagePath = Init.url.ccpImageServer + 'Character/' + data.id + '_32.jpg'; + previewContent = ''; break; case 'corporation': imagePath = Init.url.ccpImageServer + 'Corporation/' + data.id + '_32.png'; diff --git a/js/app/ui/system_graph.js b/js/app/ui/system_graph.js index 83beb4af..1a845c4e 100644 --- a/js/app/ui/system_graph.js +++ b/js/app/ui/system_graph.js @@ -150,57 +150,58 @@ define([ dataType: 'json' }).done(function(systemGraphsData){ - // create new (hidden) module container - var moduleElement = $('
', { - 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 = $('
', { - class: 'row' - }); - moduleElement.append(rowElement); - - $.each(systemGraphsData, function(systemId, graphsData){ - $.each(graphsData, function(graphKey, graphData){ - - var colElement = $('
', { - class: ['col-xs-12', 'col-sm-6', 'col-md-4'].join(' ') - }); - - var headlineElement = $('
').text( getInfoForGraph(graphKey, 'headline') ); - - colElement.append(headlineElement); - - var graphElement = $('
', { - 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 = $('
', { + class: [config.moduleClass, config.systemGraphModuleClass].join(' '), + css: {opacity: 0} }); - }); - moduleElement.append($('
', { - 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 = $('
', { + class: 'row' + }); + moduleElement.append(rowElement); + $.each(systemGraphsData, function(systemId, graphsData){ + $.each(graphsData, function(graphKey, graphData){ + + var colElement = $('
', { + class: ['col-xs-12', 'col-sm-6', 'col-md-4'].join(' ') + }); + + var headlineElement = $('
').text( getInfoForGraph(graphKey, 'headline') ); + + colElement.append(headlineElement); + + var graphElement = $('
', { + class: config.systemGraphClass + }); + + colElement.append(graphElement); + + rowElement.append(colElement); + initGraph(graphElement, graphKey, graphData, eventLine); + }); + }); + + moduleElement.append($('
', { + 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'}); diff --git a/js/app/ui/system_killboard.js b/js/app/ui/system_killboard.js index 4a3699f3..68a01e40 100644 --- a/js/app/ui/system_killboard.js +++ b/js/app/ui/system_killboard.js @@ -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]); diff --git a/js/app/ui/system_signature.js b/js/app/ui/system_signature.js index 6bd680b2..79c73340 100644 --- a/js/app/ui/system_signature.js +++ b/js/app/ui/system_signature.js @@ -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, diff --git a/js/app/util.js b/js/app/util.js index 5e28ce1f..64332bb7 100644 --- a/js/app/util.js +++ b/js/app/util.js @@ -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){ diff --git a/js/lib/validator.min.js b/js/lib/validator.min.js index 30cf93e8..9a49ff3d 100644 --- a/js/lib/validator.min.js +++ b/js/lib/validator.min.js @@ -1,131 +1,9 @@ /*! - * Validator v0.7.2 for Bootstrap 3, by @1000hz - * Copyright 2015 Cina Saffary + * Validator v0.10.1 for Bootstrap 3, by @1000hz + * Copyright 2016 Cina Saffary * Licensed under http://opensource.org/licenses/MIT * * https://github.com/1000hz/bootstrap-validator */ -+function (a) { - "use strict"; - function b(b) { - return this.each(function () { - var d = a(this), e = a.extend({}, c.DEFAULTS, d.data(), "object" == typeof b && b), f = d.data("bs.validator"); - (f || "destroy" != b) && (f || d.data("bs.validator", f = new c(this, e)), "string" == typeof b && f[b]()) - }) - } - - var c = function (b, c) { - this.$element = a(b), this.options = c, this.$element.attr("novalidate", !0), this.toggleSubmit(), this.$element.on("input.bs.validator change.bs.validator focusout.bs.validator", a.proxy(this.validateInput, this)), this.$element.on("submit.bs.validator", a.proxy(this.onSubmit, this)), this.$element.find("[data-match]").each(function () { - var b = a(this), c = b.data("match"); - a(c).on("input.bs.validator", function () { - b.val() && b.trigger("input.bs.validator") - }) - }) - }; - c.DEFAULTS = { - delay: 500, - html: !1, - disable: !0, - errors: {match: "Does not match", minlength: "Not long enough"} - }, c.VALIDATORS = { - "native": function (a) { - var b = a[0]; - return b.checkValidity ? b.checkValidity() : !0 - }, match: function (b) { - var c = b.data("match"); - return !b.val() || b.val() === a(c).val() - }, minlength: function (a) { - var b = a.data("minlength"); - return !a.val() || a.val().length >= b - } - }, c.prototype.validateInput = function (b) { - var c = a(b.target), d = c.data("bs.validator.errors"); - if (c.is('[type="radio"]') && (c = this.$element.find('input[name="' + c.attr("name") + '"]')), this.$element.trigger(b = a.Event("validate.bs.validator", {relatedTarget: c[0]})), !b.isDefaultPrevented()) { - var e = this; - this.runValidators(c).done(function (f) { - c.data("bs.validator.errors", f), f.length ? e.showErrors(c) : e.clearErrors(c), d && f.toString() === d.toString() || (b = f.length ? a.Event("invalid.bs.validator", { - relatedTarget: c[0], - detail: f - }) : a.Event("valid.bs.validator", { - relatedTarget: c[0], - detail: d - }), e.$element.trigger(b)), e.toggleSubmit(), e.$element.trigger(a.Event("validated.bs.validator", {relatedTarget: c[0]})) - }) - } - }, c.prototype.runValidators = function (b) { - function d(a) { - return b.data(a + "-error") || b.data("error") || "native" == a && b[0].validationMessage || g.errors[a] - } - - var e = [], f = ([c.VALIDATORS['native'] ], a.Deferred()), g = this.options; - return b.data("bs.validator.deferred") && b.data("bs.validator.deferred").reject(), b.data("bs.validator.deferred", f), a.each(c.VALIDATORS, a.proxy(function (a, c) { - if ((b.data(a) || "native" == a) && !c.call(this, b)) { - var f = d(a); - !~e.indexOf(f) && e.push(f) - } - }, this)), !e.length && b.val() && b.data("remote") ? this.defer(b, function () { - var c = {}; - c[b.attr("name")] = b.val(), a.get(b.data("remote"), c).fail(function (a, b, c) { - e.push(d("remote") || c) - }).always(function () { - f.resolve(e) - }) - }) : f.resolve(e), f.promise() - }, c.prototype.validate = function () { - var a = this.options.delay; - return this.options.delay = 0, this.$element.find(":input").trigger("input.bs.validator"), this.options.delay = a, this - }, c.prototype.showErrors = function (b) { - var c = this.options.html ? "html" : "text"; - this.defer(b, function () { - var d = b.closest(".form-group"), e = d.find(".help-block.with-errors"), f = b.data("bs.validator.errors"); - f.length && (f = a("