From 07d5be71b28ff128409d15b546b056fadf9bbfc1 Mon Sep 17 00:00:00 2001 From: Mark Friedrich Date: Sat, 27 Oct 2018 00:45:53 +0200 Subject: [PATCH] =?UTF-8?q?-=20added=20custom/editable=20ship=20jump=20log?= =?UTF-8?q?s,=20#709=20-=20fixed=20DB=20setup=20error:=20"`system`.`descri?= =?UTF-8?q?ption`=20can=C2=B4t=20have=20a=20default=20value",=20closed=20#?= =?UTF-8?q?701=20-=20upgraded=20"lazyload"=20js=20lib=20`v1.9.5`=20?= =?UTF-8?q?=E2=86=92=20`v1.9.7`=20-=20upgraded=20multiple=203rd=20party=20?= =?UTF-8?q?NPM=20dependencies=20for=20Gulp=20build?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/main/controller/accesscontroller.php | 15 +- app/main/controller/admin.php | 10 - app/main/controller/api/connection.php | 3 +- app/main/controller/api/github.php | 3 - app/main/controller/api/map.php | 14 +- .../api/rest/abstractrestcontroller.php | 45 ++ app/main/controller/api/rest/log.php | 112 ++++ app/main/controller/api/route.php | 5 +- app/main/controller/api/statistic.php | 1 - app/main/controller/api/system.php | 9 +- app/main/controller/api/user.php | 24 +- app/main/controller/ccp/sso.php | 12 - app/main/controller/controller.php | 74 ++- app/main/controller/logcontroller.php | 1 - app/main/controller/mapcontroller.php | 1 - app/main/controller/setup.php | 9 +- app/main/cron/mapupdate.php | 1 - app/main/db/database.php | 2 - app/main/exception/baseexception.php | 23 - app/main/exception/configexception.php | 18 + app/main/exception/databaseexception.php | 8 +- app/main/exception/pathfinderexception.php | 56 +- app/main/exception/registrationexception.php | 30 +- app/main/exception/validationexception.php | 33 +- app/main/lib/ccpclient.php | 3 - app/main/lib/config.php | 8 +- app/main/lib/logging/AbstractCharacterLog.php | 1 - app/main/lib/logging/RallyLog.php | 1 - .../handler/AbstractSlackWebhookHandler.php | 1 - .../handler/SlackMapWebhookHandler.php | 1 - .../handler/SlackRallyWebhookHandler.php | 1 - app/main/lib/web.php | 1 - app/main/model/abstractmaptrackingmodel.php | 4 +- app/main/model/activitylogmodel.php | 1 + app/main/model/alliancemodel.php | 1 - app/main/model/basicmodel.php | 54 +- app/main/model/charactermodel.php | 5 - app/main/model/connectionlogmodel.php | 68 ++- app/main/model/connectionmodel.php | 37 +- app/main/model/corporationmodel.php | 1 - app/main/model/mapmodel.php | 25 +- app/main/model/systemmodel.php | 10 +- app/main/model/systemsignaturemodel.php | 4 +- app/main/model/universe/categorymodel.php | 12 +- .../model/universe/constellationmodel.php | 2 +- app/main/model/universe/groupmodel.php | 14 +- app/main/model/universe/planetmodel.php | 4 +- app/main/model/universe/stargatemodel.php | 6 +- app/main/model/universe/starmodel.php | 2 +- app/main/model/universe/structuremodel.php | 2 +- app/main/model/universe/systemmodel.php | 4 +- app/main/model/universe/systemstaticmodel.php | 4 +- app/main/model/universe/typemodel.php | 11 +- app/main/model/universe/wormholemodel.php | 2 +- app/main/model/usermodel.php | 3 - app/routes.ini | 3 + composer-dev.json | 2 + composer.json | 2 + js/app.js | 2 +- js/app/datatables.loader.js | 3 + js/app/init.js | 1 + js/app/login.js | 4 +- js/app/mappage.js | 22 +- js/app/page.js | 21 +- js/app/ui/dialog/delete_account.js | 2 +- js/app/ui/dialog/stats.js | 2 +- js/app/ui/form_element.js | 7 +- js/app/ui/module/connection_info.js | 491 +++++++++++++++--- js/app/ui/module/system_intel.js | 2 +- js/app/util.js | 66 ++- js/lib/jquery.lazyload.min.js | 2 +- package.json | 41 +- public/css/v1.4.3/pathfinder.css | 4 +- public/css/v1.4.3/pathfinder.css.map | 4 +- public/js/v1.4.3/app.js | 2 +- public/js/v1.4.3/app/datatables.loader.js | 3 + public/js/v1.4.3/app/init.js | 1 + public/js/v1.4.3/app/login.js | 4 +- public/js/v1.4.3/app/mappage.js | 22 +- public/js/v1.4.3/app/page.js | 21 +- .../js/v1.4.3/app/ui/dialog/delete_account.js | 2 +- public/js/v1.4.3/app/ui/dialog/stats.js | 2 +- public/js/v1.4.3/app/ui/form_element.js | 7 +- .../v1.4.3/app/ui/module/connection_info.js | 491 +++++++++++++++--- .../js/v1.4.3/app/ui/module/system_intel.js | 2 +- public/js/v1.4.3/app/util.js | 66 ++- public/js/v1.4.3/lib/jquery.lazyload.min.js | 2 +- public/templates/dialog/connection_log.html | 51 ++ public/templates/dialog/map.html | 46 +- sass/_main.scss | 2 +- sass/layout/_dialogs.scss | 13 +- sass/layout/_forms.scss | 10 + sass/layout/_main.scss | 49 +- sass/layout/_system-info.scss | 10 +- .../select2/theme/pathfinder/_layout.scss | 37 +- 95 files changed, 1766 insertions(+), 560 deletions(-) create mode 100644 app/main/controller/api/rest/abstractrestcontroller.php create mode 100644 app/main/controller/api/rest/log.php delete mode 100644 app/main/exception/baseexception.php create mode 100644 app/main/exception/configexception.php create mode 100644 public/templates/dialog/connection_log.html diff --git a/app/main/controller/accesscontroller.php b/app/main/controller/accesscontroller.php index 19c1348b..72322209 100644 --- a/app/main/controller/accesscontroller.php +++ b/app/main/controller/accesscontroller.php @@ -20,7 +20,6 @@ class AccessController extends Controller { * @param $params * @return bool * @throws \Exception - * @throws \Exception\PathfinderException * @throws \ZMQSocketException */ function beforeroute(\Base $f3, $params): bool { @@ -29,15 +28,7 @@ class AccessController extends Controller { // requires a valid logged in user! if( !$this->isLoggedIn($f3) ){ // no character found or login timer expired - $this->logoutCharacter(); - - if($f3->get('AJAX')){ - // unauthorized request - $f3->status(403); - }else{ - // redirect to landing page - $f3->reroute(['login']); - } + $this->logoutCharacter($f3); // skip route handler and afterroute() $return = false; } @@ -51,7 +42,6 @@ class AccessController extends Controller { * @param \Base $f3 * @return bool * @throws \Exception - * @throws \Exception\PathfinderException */ protected function isLoggedIn(\Base $f3): bool { $loginCheck = false; @@ -71,7 +61,6 @@ class AccessController extends Controller { * @param \Base $f3 * @param Model\CharacterModel $character * @return bool - * @throws \Exception\PathfinderException */ private function checkLogTimer(\Base $f3, Model\CharacterModel $character){ $loginCheck = false; @@ -104,7 +93,6 @@ class AccessController extends Controller { * @param Model\MapModel $map * @return int (number of active connections for this map) * @throws \Exception - * @throws \Exception\PathfinderException * @throws \ZMQSocketException */ protected function broadcastMapData(Model\MapModel $map){ @@ -117,7 +105,6 @@ class AccessController extends Controller { * @param Model\MapModel $map * @return array * @throws \Exception - * @throws \Exception\PathfinderException */ protected function getFormattedMapData(Model\MapModel $map){ $mapData = $map->getData(); diff --git a/app/main/controller/admin.php b/app/main/controller/admin.php index de66a957..a9fe322f 100644 --- a/app/main/controller/admin.php +++ b/app/main/controller/admin.php @@ -37,7 +37,6 @@ class Admin extends Controller{ * @param $params * @return bool * @throws \Exception - * @throws \Exception\PathfinderException */ function beforeroute(\Base $f3, $params): bool { $return = parent::beforeroute($f3, $params); @@ -67,7 +66,6 @@ class Admin extends Controller{ /** * event handler after routing * @param \Base $f3 - * @throws \Exception\PathfinderException */ public function afterroute(\Base $f3) { // js view (file) @@ -123,7 +121,6 @@ class Admin extends Controller{ * @param $params * @param null $character * @throws \Exception - * @throws \Exception\PathfinderException */ public function dispatch(\Base $f3, $params, $character = null){ if($character instanceof CharacterModel){ @@ -232,7 +229,6 @@ class Admin extends Controller{ * @param CharacterModel $character * @param int $kickCharacterId * @param int $minutes - * @throws \Exception\PathfinderException */ protected function kickCharacter(CharacterModel $character, $kickCharacterId, $minutes){ $kickOptions = self::KICK_OPTIONS; @@ -262,7 +258,6 @@ class Admin extends Controller{ * @param CharacterModel $character * @param int $banCharacterId * @param int $value - * @throws \Exception\PathfinderException */ protected function banCharacter(CharacterModel $character, $banCharacterId, $value){ $banCharacters = $this->filterValidCharacters($character, $banCharacterId); @@ -309,7 +304,6 @@ class Admin extends Controller{ * @param CharacterModel $character * @param int $mapId * @param int $value - * @throws \Exception\PathfinderException */ protected function activateMap(CharacterModel $character, int $mapId, int $value){ $maps = $this->filterValidMaps($character, $mapId); @@ -322,7 +316,6 @@ class Admin extends Controller{ /** * @param CharacterModel $character * @param int $mapId - * @throws \Exception\PathfinderException */ protected function deleteMap(CharacterModel $character, int $mapId){ $maps = $this->filterValidMaps($character, $mapId); @@ -336,7 +329,6 @@ class Admin extends Controller{ * @param CharacterModel $character * @param int $mapId * @return \DB\CortexCollection[]|MapModel[] - * @throws \Exception\PathfinderException */ protected function filterValidMaps(CharacterModel $character, int $mapId) { $maps = []; @@ -355,7 +347,6 @@ class Admin extends Controller{ * get log file for "admin" logs * @param string $type * @return \Log - * @throws \Exception\PathfinderException */ static function getLogger($type = 'ADMIN'){ return parent::getLogger('ADMIN'); @@ -406,7 +397,6 @@ class Admin extends Controller{ * init /maps page data * @param \Base $f3 * @param CharacterModel $character - * @throws \Exception\PathfinderException */ protected function initMaps(\Base $f3, CharacterModel $character){ $data = (object) []; diff --git a/app/main/controller/api/connection.php b/app/main/controller/api/connection.php index 8fa19d63..9c91f3b5 100644 --- a/app/main/controller/api/connection.php +++ b/app/main/controller/api/connection.php @@ -7,6 +7,7 @@ */ namespace Controller\Api; + use Controller; use Model; @@ -97,7 +98,7 @@ class Connection extends Controller\AccessController { $map = Model\BasicModel::getNew('MapModel'); $map->getById($mapId); - if( $map->hasAccess($activeCharacter) ){ + if($map->hasAccess($activeCharacter)){ foreach($connectionIds as $connectionId){ if( $connection = $map->getConnectionById($connectionId) ){ $connection->delete( $activeCharacter ); diff --git a/app/main/controller/api/github.php b/app/main/controller/api/github.php index 20d4f0c2..720b8efd 100644 --- a/app/main/controller/api/github.php +++ b/app/main/controller/api/github.php @@ -29,7 +29,6 @@ class GitHub extends Controller\Controller { /** * get HTTP request options for API (curl) request * @return array - * @throws \Exception\PathfinderException */ protected function getRequestReleaseOptions() : array { $options = $this->getBaseRequestOptions(); @@ -41,7 +40,6 @@ class GitHub extends Controller\Controller { * get HTTP request options for API (curl) request * @param string $text * @return array - * @throws \Exception\PathfinderException */ protected function getRequestMarkdownOptions(string $text) : array { $params = [ @@ -59,7 +57,6 @@ class GitHub extends Controller\Controller { /** * get release information from GitHub * @param \Base $f3 - * @throws \Exception\PathfinderException */ public function releases(\Base $f3){ $cacheKey = 'CACHE_GITHUB_RELEASES'; diff --git a/app/main/controller/api/map.php b/app/main/controller/api/map.php index 14ce90de..5e38300f 100644 --- a/app/main/controller/api/map.php +++ b/app/main/controller/api/map.php @@ -7,6 +7,7 @@ */ namespace Controller\Api; + use Controller; use data\file\FileHandler; use lib\Config; @@ -50,7 +51,6 @@ class Map extends Controller\AccessController { * Get all required static config data for program initialization * @param \Base $f3 * @throws Exception - * @throws Exception\PathfinderException */ public function initData(\Base $f3){ // expire time in seconds @@ -221,6 +221,7 @@ class Map extends Controller\AccessController { // universe category data --------------------------------------------------------------------------------- $return->universeCategories = [ + 6 => Model\Universe\BasicUniverseModel::getNew('CategoryModel')->getById(6)->getData(['mass']), 65 => Model\Universe\BasicUniverseModel::getNew('CategoryModel')->getById(65)->getData() ]; @@ -572,11 +573,7 @@ class Map extends Controller\AccessController { $return->mapData = $map->getData(); }catch(Exception\ValidationException $e){ - $validationError = (object) []; - $validationError->type = 'error'; - $validationError->field = $e->getField(); - $validationError->message = $e->getMessage(); - $return->error[] = $validationError; + $return->error[] = $e->getError(); } }else{ // map access denied @@ -634,7 +631,6 @@ class Map extends Controller\AccessController { * -> if characters with map access found -> broadcast mapData to them * @param Model\MapModel $map * @throws Exception - * @throws Exception\PathfinderException * @throws \ZMQSocketException */ protected function broadcastMapAccess(Model\MapModel $map){ @@ -708,7 +704,6 @@ class Map extends Controller\AccessController { * -> function is called continuously (trigger) by any active client * @param \Base $f3 * @throws Exception - * @throws Exception\PathfinderException */ public function updateData(\Base $f3){ $postData = (array)$f3->get('POST'); @@ -848,7 +843,6 @@ class Map extends Controller\AccessController { * @param Model\MapModel[] $mapModels * @return array * @throws Exception - * @throws Exception\PathfinderException */ protected function getFormattedMapsData($mapModels){ $mapData = []; @@ -864,7 +858,6 @@ class Map extends Controller\AccessController { * -> function is called continuously by any active client * @param \Base $f3 * @throws Exception - * @throws Exception\PathfinderException */ public function updateUserData(\Base $f3){ $postData = (array)$f3->get('POST'); @@ -1196,7 +1189,6 @@ class Map extends Controller\AccessController { * get map log data * @param \Base $f3 * @throws Exception - * @throws Exception\PathfinderException */ public function getLogData(\Base $f3){ $postData = (array)$f3->get('POST'); diff --git a/app/main/controller/api/rest/abstractrestcontroller.php b/app/main/controller/api/rest/abstractrestcontroller.php new file mode 100644 index 00000000..a782ff35 --- /dev/null +++ b/app/main/controller/api/rest/abstractrestcontroller.php @@ -0,0 +1,45 @@ + $_POST does not include request data -> request BODY might contain JSON + * @param \Base $f3 + * @return array + */ + protected function getRequestData(\Base $f3) : array { + $data = []; + if( !empty($body = $f3->get('BODY')) ){ + $bodyDecode = json_decode($body, true); + if(($jsonError = json_last_error()) === JSON_ERROR_NONE){ + $data = $bodyDecode; + }else{ + $f3->set('HALT', true); + $f3->error(400, 'Request data: ' . json_last_error_msg()); + } + } + + return $data; + } + + /** + * render API response to client + * @param $output + */ + protected function out($output){ + echo json_encode($output); + } + +} \ No newline at end of file diff --git a/app/main/controller/api/rest/log.php b/app/main/controller/api/rest/log.php new file mode 100644 index 00000000..ce679339 --- /dev/null +++ b/app/main/controller/api/rest/log.php @@ -0,0 +1,112 @@ +getRequestData($f3); + $connectionData = []; + + if($connectionId = (int)$requestData['connectionId']){ + $activeCharacter = $this->getCharacter(); + + /** + * @var Model\ConnectionModel $connection + */ + $connection = Model\BasicModel::getNew('ConnectionModel'); + $connection->getById($connectionId); + + if($connection->hasAccess($activeCharacter)){ + $log = $connection->getNewLog(); + $log->setData($requestData); + $log->record = false; // log not recorded by ESI + $log->save(); + + $connectionData[] = $log->getConnection()->getData(true, true); + } + } + + $this->out($connectionData); + } + + /** + * delete (deactivate) log data + * @param \Base $f3 + * @param $params + * @throws \Exception + */ + public function delete(\Base $f3, $params){ + $logId = (int)$params['id']; + $connectionData = []; + + if($log = $this->update($logId, ['active' => false])){ + $connectionData[] = $log->getConnection()->getData(true, true); + } + + $this->out($connectionData); + } + + + /** + * update log data + * @param \Base $f3 + * @param $params + * @throws \Exception + */ + public function patch(\Base $f3, $params){ + $logId = (int)$params['id']; + $requestData = $this->getRequestData($f3); + $connectionData = []; + + if($log = $this->update($logId, $requestData)){ + $connectionData[] = $log->getConnection()->getData(true, true); + } + + $this->out($connectionData); + } + + // ---------------------------------------------------------------------------------------------------------------- + + /** + * update existing connectionLog with new data + * @param int $logId + * @param array $logData + * @return bool|Model\ConnectionLogModel + * @throws \Exception + */ + private function update(int $logId, array $logData){ + $log = false; + if($logId){ + $activeCharacter = $this->getCharacter(); + /** + * @var Model\ConnectionLogModel $log + */ + $log = Model\BasicModel::getNew('ConnectionLogModel'); + $log->getById($logId, 0, false); + + if($log->hasAccess($activeCharacter)){ + $log->setData($logData); + + if(isset($logData['active'])){ + $log->setActive((bool)$logData['active']); + } + $log->save(); + } + } + return $log; + } +} \ No newline at end of file diff --git a/app/main/controller/api/route.php b/app/main/controller/api/route.php index cecb0bcd..25272832 100644 --- a/app/main/controller/api/route.php +++ b/app/main/controller/api/route.php @@ -414,7 +414,7 @@ class Route extends Controller\AccessController { * @param array $mapIds * @param array $filterData * @return array - * @throws \Exception\PathfinderException + * @throws \Exception */ public function searchRoute(int $systemFromId, int $systemToId, $searchDepth = 0, array $mapIds = [], array $filterData = []) : array { // search root by ESI API @@ -439,7 +439,6 @@ class Route extends Controller\AccessController { * @param array $filterData * @return array * @throws \Exception - * @throws \Exception\PathfinderException */ private function searchRouteCustom(int $systemFromId, int $systemToId, $searchDepth = 0, array $mapIds = [], array $filterData = []) : array { // reset all previous set jump data @@ -519,7 +518,6 @@ class Route extends Controller\AccessController { * @param array $filterData * @return array * @throws \Exception - * @throws \Exception\PathfinderException */ private function searchRouteESI(int $systemFromId, int $systemToId, int $searchDepth = 0, array $mapIds = [], array $filterData = []) : array { // reset all previous set jump data @@ -645,7 +643,6 @@ class Route extends Controller\AccessController { * search multiple route between two systems * @param \Base $f3 * @throws \Exception - * @throws \Exception\PathfinderException */ public function search($f3){ $requestData = (array)$f3->get('POST'); diff --git a/app/main/controller/api/statistic.php b/app/main/controller/api/statistic.php index a2eb9d66..b002adb2 100644 --- a/app/main/controller/api/statistic.php +++ b/app/main/controller/api/statistic.php @@ -123,7 +123,6 @@ class Statistic extends Controller\AccessController { * @param int $yearEnd * @param int $weekEnd * @return array - * @throws \Exception\PathfinderException */ protected function queryStatistic( CharacterModel $character, $typeId, $yearStart, $weekStart, $yearEnd, $weekEnd){ $data = []; diff --git a/app/main/controller/api/system.php b/app/main/controller/api/system.php index 890ed6b2..f7edf6ee 100644 --- a/app/main/controller/api/system.php +++ b/app/main/controller/api/system.php @@ -100,14 +100,8 @@ class System extends Controller\AccessController { $return->error = $systemModel->getErrors(); } }catch(Exception\ValidationException $e){ - $validationError = (object) []; - $validationError->type = 'error'; - $validationError->field = $e->getField(); - $validationError->message = $e->getMessage(); - $return->error[] = $validationError; + $return->error[] = $e->getError(); } - - } } @@ -246,7 +240,6 @@ class System extends Controller\AccessController { * send Rally Point poke * @param \Base $f3 * @throws \Exception - * @throws \Exception\PathfinderException */ public function pokeRally(\Base $f3){ $rallyData = (array)$f3->get('POST'); diff --git a/app/main/controller/api/user.php b/app/main/controller/api/user.php index 3d4d4464..2afca5b8 100644 --- a/app/main/controller/api/user.php +++ b/app/main/controller/api/user.php @@ -109,7 +109,6 @@ class User extends Controller\Controller{ * -> return character data (if valid) * @param \Base $f3 * @throws Exception - * @throws Exception\PathfinderException */ public function getCookieCharacter(\Base $f3){ $data = $f3->get('POST'); @@ -201,15 +200,10 @@ class User extends Controller\Controller{ /** * log the current user out + clear character system log data * @param \Base $f3 - * @throws Exception * @throws \ZMQSocketException */ public function logout(\Base $f3){ - $this->logoutCharacter(false, true, true, true); - - $return = (object) []; - $return->reroute = rtrim(self::getEnvironmentData('URL'), '/') . $f3->alias('login'); - echo json_encode($return); + $this->logoutCharacter($f3, false, true, true, true); } /** @@ -345,17 +339,9 @@ class User extends Controller\Controller{ } }catch(Exception\ValidationException $e){ - $validationError = (object) []; - $validationError->type = 'error'; - $validationError->field = $e->getField(); - $validationError->message = $e->getMessage(); - $return->error[] = $validationError; + $return->error[] = $e->getError(); }catch(Exception\RegistrationException $e){ - $registrationError = (object) []; - $registrationError->type = 'error'; - $registrationError->field = $e->getField(); - $registrationError->message = $e->getMessage(); - $return->error[] = $registrationError; + $return->error[] = $e->getError(); } // return new/updated user data @@ -394,10 +380,8 @@ class User extends Controller\Controller{ sprintf(self::LOG_DELETE_ACCOUNT, $user->id, $user->name) ); - $this->logoutCharacter(true, true, true, true); + $this->logoutCharacter($f3, true, true, true, true); $user->erase(); - - $return->reroute = rtrim(self::getEnvironmentData('URL'), '/') . $f3->alias('login'); } }else{ // captcha not valid -> return error diff --git a/app/main/controller/ccp/sso.php b/app/main/controller/ccp/sso.php index 7c34fdff..7cbd3d5e 100644 --- a/app/main/controller/ccp/sso.php +++ b/app/main/controller/ccp/sso.php @@ -52,7 +52,6 @@ class Sso extends Api\User{ * redirect user to CCP SSO page and request authorization * -> cf. Controller->getCookieCharacters() ( equivalent cookie based login) * @param \Base $f3 - * @throws \Exception\PathfinderException */ public function requestAdminAuthorization($f3){ // store browser tabId to be "targeted" after login @@ -67,7 +66,6 @@ class Sso extends Api\User{ * -> cf. Controller->getCookieCharacters() ( equivalent cookie based login) * @param \Base $f3 * @throws \Exception - * @throws \Exception\PathfinderException */ public function requestAuthorization($f3){ $params = $f3->get('GET'); @@ -133,7 +131,6 @@ class Sso extends Api\User{ * @param \Base $f3 * @param array $scopes * @param string $rootAlias - * @throws \Exception\PathfinderException */ private function rerouteAuthorization(\Base $f3, $scopes = [], $rootAlias = 'login'){ if( !empty( Controller\Controller::getEnvironmentData('CCP_SSO_CLIENT_ID') ) ){ @@ -166,7 +163,6 @@ class Sso extends Api\User{ * -> see requestAuthorization() * @param \Base $f3 * @throws \Exception - * @throws \Exception\PathfinderException */ public function callbackAuthorization($f3){ $getParams = (array)$f3->get('GET'); @@ -307,7 +303,6 @@ class Sso extends Api\User{ * login by cookie name * @param \Base $f3 * @throws \Exception - * @throws \Exception\PathfinderException */ public function login(\Base $f3){ $data = (array)$f3->get('GET'); @@ -345,7 +340,6 @@ class Sso extends Api\User{ * -> else try to refresh auth and get fresh "access_token" * @param bool $authCode * @return null|\stdClass - * @throws \Exception\PathfinderException */ public function getSsoAccessData($authCode){ $accessData = null; @@ -365,7 +359,6 @@ class Sso extends Api\User{ * verify authorization code, and get an "access_token" data * @param $authCode * @return \stdClass - * @throws \Exception\PathfinderException */ protected function verifyAuthorizationCode($authCode){ $requestParams = [ @@ -381,7 +374,6 @@ class Sso extends Api\User{ * -> if "access_token" is expired, this function gets a fresh one * @param $refreshToken * @return \stdClass - * @throws \Exception\PathfinderException */ public function refreshAccessToken($refreshToken){ $requestParams = [ @@ -398,7 +390,6 @@ class Sso extends Api\User{ * OR by providing a valid "refresh_token" * @param $requestParams * @return \stdClass - * @throws \Exception\PathfinderException */ protected function requestAccessData($requestParams){ $verifyAuthCodeUrl = self::getVerifyAuthorizationCodeEndpoint(); @@ -463,7 +454,6 @@ class Sso extends Api\User{ * -> if more character information is required, use ESI "characters" endpoints request instead * @param $accessToken * @return mixed|null - * @throws \Exception\PathfinderException */ public function verifyCharacterData($accessToken){ $verifyUserUrl = self::getVerifyUserEndpoint(); @@ -586,7 +576,6 @@ class Sso extends Api\User{ * get CCP SSO url from configuration file * -> throw error if url is broken/missing * @return string - * @throws \Exception\PathfinderException */ static function getSsoUrlRoot(){ $url = ''; @@ -616,7 +605,6 @@ class Sso extends Api\User{ /** * get logger for SSO logging * @return \Log - * @throws \Exception\PathfinderException */ static function getSSOLogger(){ return parent::getLogger('SSO'); diff --git a/app/main/controller/controller.php b/app/main/controller/controller.php index 8e09414b..53ecdf41 100644 --- a/app/main/controller/controller.php +++ b/app/main/controller/controller.php @@ -9,6 +9,7 @@ namespace Controller; use Controller\Api as Api; +use Exception\PathfinderException; use lib\Config; use lib\Resource; use lib\Monolog; @@ -66,7 +67,6 @@ class Controller { * @param \Base $f3 * @param $params * @return bool - * @throws \Exception\PathfinderException */ function beforeroute(\Base $f3, $params): bool { // initiate DB connection @@ -103,10 +103,10 @@ class Controller { header($resource->buildHeader(), false); } - if($this->getTemplate()){ + if($file = $this->getTemplate()){ // Ajax calls don´t need a page render.. // this happens on client side - echo \Template::instance()->render( $this->getTemplate() ); + echo \Template::instance()->render($file); } } @@ -131,7 +131,6 @@ class Controller { * @param $session * @param $sid * @return bool - * @throws \Exception\PathfinderException */ $onSuspect = function($session, $sid){ self::getLogger('SESSION_SUSPECT')->write( sprintf( @@ -160,7 +159,6 @@ class Controller { /** * init new Resource handler * @param \Base $f3 - * @throws \Exception\PathfinderException */ protected function initResource(\Base $f3){ $resource = Resource::instance(); @@ -228,7 +226,6 @@ class Controller { * -> store validation data in DB * @param Model\CharacterModel $character * @throws \Exception - * @throws \Exception\PathfinderException */ protected function setLoginCookie(Model\CharacterModel $character){ if( $this->getCookieState() ){ @@ -286,7 +283,6 @@ class Controller { * @param bool $checkAuthorization * @return Model\CharacterModel[] * @throws \Exception - * @throws \Exception\PathfinderException */ protected function getCookieCharacters($cookieData = [], $checkAuthorization = true){ $characters = []; @@ -489,15 +485,16 @@ class Controller { /** * log out current character or all active characters (multiple browser tabs) + * -> send response data to client + * @param \Base $f3 * @param bool $all * @param bool $deleteSession * @param bool $deleteLog * @param bool $deleteCookie - * @throws \Exception * @throws \ZMQSocketException */ - protected function logoutCharacter(bool $all = false, bool $deleteSession = true, bool $deleteLog = true, bool $deleteCookie = false){ - $sessionCharacterData = (array)$this->getF3()->get(Api\User::SESSION_KEY_CHARACTERS); + protected function logoutCharacter(\Base $f3, bool $all = false, bool $deleteSession = true, bool $deleteLog = true, bool $deleteCookie = false){ + $sessionCharacterData = (array)$f3->get(Api\User::SESSION_KEY_CHARACTERS); if($sessionCharacterData){ $activeCharacterId = ($activeCharacter = $this->getCharacter()) ? $activeCharacter->_id : 0; @@ -523,6 +520,20 @@ class Controller { (new Socket( Config::getSocketUri() ))->sendData('characterLogout', $characterIds); } } + + if($f3->get('AJAX')){ + $status = 403; + $f3->status($status); + + $return = (object) []; + $return->reroute = rtrim(self::getEnvironmentData('URL'), '/') . $f3->alias('login'); + $return->error[] = $this->getErrorObject($status, Config::getMessageFromHTTPStatus($status)); + + echo json_encode($return); + }else{ + // redirect to landing page + $f3->reroute(['login']); + } } /** @@ -627,7 +638,6 @@ class Controller { /** * get a custom userAgent string for API calls * @return string - * @throws \Exception\PathfinderException */ protected function getUserAgent(){ $userAgent = ''; @@ -663,18 +673,26 @@ class Controller { * -> on HTTP request -> render error page * @param \Base $f3 * @return bool - * @throws \Exception\PathfinderException */ public function showError(\Base $f3){ if(!headers_sent()){ // collect error info ------------------------------------------------------------------------------------- - $error = $this->getErrorObject( - $f3->get('ERROR.code'), - $f3->get('ERROR.status'), - $f3->get('ERROR.text'), - $f3->get('DEBUG') === 3 ? $f3->get('ERROR.trace') : null - ); + $errorData = $f3->get('ERROR'); + $exception = $f3->get('EXCEPTION'); + + if($exception instanceof PathfinderException){ + // ... handle Pathfinder exceptions (e.g. validation Exceptions,..) + $error = $exception->getError(); + }else{ + // ... handle error $f3->error() calls + $error = $this->getErrorObject( + $errorData['code'], + $errorData['status'], + $errorData['text'], + $f3->get('DEBUG') === 3 ? $errorData['trace'] : null + ); + } // check if error is a PDO Exception ---------------------------------------------------------------------- if(strpos(strtolower( $f3->get('ERROR.text') ), 'duplicate') !== false){ @@ -725,24 +743,6 @@ class Controller { * @return bool */ public function unload(\Base $f3){ - // track some 4xx Client side errors - // 5xx errors are handled in "ONERROR" callback - $status = http_response_code(); - if(!headers_sent() && $status >= 300){ - if($f3->get('AJAX')){ - $params = (array)$f3->get('POST'); - $return = (object) []; - if((bool)$params['reroute']){ - $return->reroute = rtrim(self::getEnvironmentData('URL'), '/') . $f3->alias('login'); - }else{ - // no reroute -> errors can be shown - $return->error[] = $this->getErrorObject($status, Config::getMessageFromHTTPStatus($status)); - } - - echo json_encode($return); - } - } - // store all user activities that are buffered for logging in this request // this should work even on non HTTP200 responses $this->logActivities(); @@ -887,7 +887,6 @@ class Controller { * get the current registration status * 0=registration stop |1=new registration allowed * @return int - * @throws \Exception\PathfinderException */ static function getRegistrationStatus(){ return (int)Config::getPathfinderData('registration.status'); @@ -898,7 +897,6 @@ class Controller { * -> set in pathfinder.ini * @param string $type * @return \Log|null - * @throws \Exception\PathfinderException */ static function getLogger($type){ return LogController::getLogger($type); diff --git a/app/main/controller/logcontroller.php b/app/main/controller/logcontroller.php index 891916b9..f8fd88bb 100644 --- a/app/main/controller/logcontroller.php +++ b/app/main/controller/logcontroller.php @@ -163,7 +163,6 @@ class LogController extends \Prefab { * get Logger instance * @param string $type * @return \Log|null - * @throws \Exception\PathfinderException */ public static function getLogger($type){ $logFiles = Config::getPathfinderData('logfiles'); diff --git a/app/main/controller/mapcontroller.php b/app/main/controller/mapcontroller.php index f3c0049d..c898be0d 100644 --- a/app/main/controller/mapcontroller.php +++ b/app/main/controller/mapcontroller.php @@ -16,7 +16,6 @@ class MapController extends AccessController { /** * @param \Base $f3 * @throws \Exception - * @throws \Exception\PathfinderException */ public function init(\Base $f3) { $character = $this->getCharacter(); diff --git a/app/main/controller/setup.php b/app/main/controller/setup.php index e7031daf..ce262583 100644 --- a/app/main/controller/setup.php +++ b/app/main/controller/setup.php @@ -137,7 +137,6 @@ class Setup extends Controller { * @param \Base $f3 * @param array $params * @return bool - * @throws \Exception\PathfinderException */ function beforeroute(\Base $f3, $params): bool { $this->initResource($f3); @@ -162,7 +161,6 @@ class Setup extends Controller { /** * @param \Base $f3 - * @throws \Exception\PathfinderException */ public function afterroute(\Base $f3) { // js view (file) @@ -789,7 +787,6 @@ class Setup extends Controller { * get default map config * @param \Base $f3 * @return array - * @throws \Exception\PathfinderException */ protected function getMapsDefaultConfig(\Base $f3): array { $matrix = \Matrix::instance(); @@ -1517,8 +1514,8 @@ class Setup extends Controller { */ protected function invalidateCookies(\Base $f3){ $this->getDB('PF'); - $authentidationModel = Model\BasicModel::getNew('CharacterAuthenticationModel'); - $results = $authentidationModel->find(); + $authenticationModel = Model\BasicModel::getNew('CharacterAuthenticationModel'); + $results = $authenticationModel->find(); if($results){ foreach($results as $result){ $result->erase(); @@ -1537,7 +1534,7 @@ class Setup extends Controller { if($bytes){ $base = log($bytes, 1024); $suffixes = array('', 'KB', 'M', 'GB', 'TB'); - $result = round(pow(1024, $base - floor($base)), $precision) .''. $suffixes[floor($base)]; + $result = round(pow(1024, $base - floor($base)), $precision) .''. $suffixes[(int)floor($base)]; } return $result; } diff --git a/app/main/cron/mapupdate.php b/app/main/cron/mapupdate.php index 19e8e79d..24c62c5e 100644 --- a/app/main/cron/mapupdate.php +++ b/app/main/cron/mapupdate.php @@ -22,7 +22,6 @@ class MapUpdate extends AbstractCron { * deactivate all "private" maps whose lifetime is over * >> php index.php "/cron/deactivateMapData" * @param \Base $f3 - * @throws \Exception\PathfinderException */ function deactivateMapData(\Base $f3){ $this->setMaxExecutionTime(); diff --git a/app/main/db/database.php b/app/main/db/database.php index c947b6fd..f5f09bcb 100644 --- a/app/main/db/database.php +++ b/app/main/db/database.php @@ -116,7 +116,6 @@ class Database extends \Prefab { * @param string $password * @param string $alias * @return SQL|null - * @throws \Exception\PathfinderException */ protected function connect($dns, $name, $user, $password, $alias){ $db = null; @@ -286,7 +285,6 @@ class Database extends \Prefab { /** * get logger for DB logging * @return \Log - * @throws \Exception\PathfinderException */ static function getLogger(){ return LogController::getLogger('ERROR'); diff --git a/app/main/exception/baseexception.php b/app/main/exception/baseexception.php deleted file mode 100644 index af4b580c..00000000 --- a/app/main/exception/baseexception.php +++ /dev/null @@ -1,23 +0,0 @@ - 500 + ]; + +} \ No newline at end of file diff --git a/app/main/exception/databaseexception.php b/app/main/exception/databaseexception.php index a6a72bec..3aafc3c1 100644 --- a/app/main/exception/databaseexception.php +++ b/app/main/exception/databaseexception.php @@ -8,9 +8,13 @@ namespace Exception; -class DatabaseException extends BaseException { +class DatabaseException extends PathfinderException { + + protected $codes = [ + 1500 => 500 + ]; public function __construct(string $message){ - parent::__construct($message, self::DB_EXCEPTION); + parent::__construct($message, 1500); } } \ No newline at end of file diff --git a/app/main/exception/pathfinderexception.php b/app/main/exception/pathfinderexception.php index f5aaad1a..8fa45264 100644 --- a/app/main/exception/pathfinderexception.php +++ b/app/main/exception/pathfinderexception.php @@ -1,17 +1,59 @@ can be specified by using custom Exception codes + */ + const DEFAULT_RESPONSECODE = 500; + + /** + * lists all exception codes + * @var array + */ + protected $codes = [ + 0 => self::DEFAULT_RESPONSECODE + ]; + + public function __construct(string $message, int $code = 0){ + if( !array_key_exists($code, $this->codes) ){ + // exception code not specified by child class + $code = 0; + } + parent::__construct($message, $code); } -} \ No newline at end of file + + /** + * get error object + * @return \stdClass + */ + public function getError() : \stdClass { + $error = (object) []; + $error->type = 'error'; + $error->code = $this->getResponseCode(); + $error->status = @constant('Base::HTTP_' . $this->getResponseCode()); + $error->message = $this->getMessage(); + //$error->trace = $this->getTraceAsString(); + $error->trace = $this->getTrace(); + return $error; + } + + /** + * returns the HTTP response code for the client from exception + * -> if Exception is not handled/catched 'somewhere' this code is used by the final onError handler + * @return int + */ + public function getResponseCode() : int { + return $this->codes[$this->getCode()]; + } +} \ No newline at end of file diff --git a/app/main/exception/registrationexception.php b/app/main/exception/registrationexception.php index d91cbdc9..b98e107b 100644 --- a/app/main/exception/registrationexception.php +++ b/app/main/exception/registrationexception.php @@ -9,7 +9,11 @@ namespace Exception; -class RegistrationException extends BaseException{ +class RegistrationException extends PathfinderException{ + + protected $codes = [ + 2000 => 403 + ]; /** * form field name that causes this exception @@ -17,22 +21,18 @@ class RegistrationException extends BaseException{ */ private $field; - /** - * @return mixed - */ - public function getField(){ - return $this->field; - } - - /** - * @param mixed $field - */ - public function setField($field){ + public function __construct(string $message, string $field = ''){ + parent::__construct($message, 2000); $this->field = $field; } - public function __construct($message, $field = ''){ - parent::__construct($message, self::REGISTRATION_EXCEPTION); - $this->setField($field); + /** + * get error object + * @return \stdClass + */ + public function getError() : \stdClass { + $error = parent::getError(); + $error->field = $this->field; + return $error; } } \ No newline at end of file diff --git a/app/main/exception/validationexception.php b/app/main/exception/validationexception.php index 200f4d0c..bf56c3b3 100644 --- a/app/main/exception/validationexception.php +++ b/app/main/exception/validationexception.php @@ -9,7 +9,11 @@ namespace Exception; -class ValidationException extends BaseException { +class ValidationException extends PathfinderException { + + protected $codes = [ + 2000 => 593 + ]; /** * table column that triggers the exception @@ -17,35 +21,18 @@ class ValidationException extends BaseException { */ private $field; - /** - * @return string - */ - public function getField(): string { - return $this->field; - } - - /** - * @param string $field - */ - public function setField(string $field){ - $this->field = $field; - } - - public function __construct(string $message, string $field = ''){ - parent::__construct($message, self::VALIDATION_EXCEPTION); - $this->setField($field); + parent::__construct($message, 2000); + $this->field = $field; } /** * get error object * @return \stdClass */ - public function getError(){ - $error = (object) []; - $error->type = 'error'; - $error->field = $this->getField(); - $error->message = $this->getMessage(); + public function getError() : \stdClass { + $error = parent::getError(); + $error->field = $this->field; return $error; } } \ No newline at end of file diff --git a/app/main/lib/ccpclient.php b/app/main/lib/ccpclient.php index 9c3c5e95..53ec41c6 100644 --- a/app/main/lib/ccpclient.php +++ b/app/main/lib/ccpclient.php @@ -24,7 +24,6 @@ class CcpClient extends \Prefab { * get ApiClient instance * @param \Base $f3 * @return ApiClient|null - * @throws \Exception\PathfinderException */ protected function getClient(\Base $f3){ $client = null; @@ -45,7 +44,6 @@ class CcpClient extends \Prefab { /** * @return string - * @throws \Exception\PathfinderException */ protected function getUserAgent(){ $userAgent = ''; @@ -71,7 +69,6 @@ class CcpClient extends \Prefab { * @param $name * @param $arguments * @return array|mixed - * @throws \Exception\PathfinderException */ public function __call($name, $arguments){ $return = []; diff --git a/app/main/lib/config.php b/app/main/lib/config.php index cec90539..66934f41 100644 --- a/app/main/lib/config.php +++ b/app/main/lib/config.php @@ -208,7 +208,6 @@ class Config extends \Prefab { /** * get SMTP config values * @return \stdClass - * @throws Exception\PathfinderException */ static function getSMTPConfig(): \stdClass{ $config = new \stdClass(); @@ -253,7 +252,6 @@ class Config extends \Prefab { * get email for notifications by hive key * @param $key * @return mixed - * @throws Exception\PathfinderException */ static function getNotificationMail($key){ return self::getPathfinderData('notification' . ($key ? '.' . $key : '')); @@ -264,7 +262,6 @@ class Config extends \Prefab { * -> read from pathfinder.ini * @param string $mapType * @return mixed - * @throws Exception\PathfinderException */ static function getMapsDefaultConfig($mapType = ''){ if( $mapConfig = self::getPathfinderData('map' . ($mapType ? '.' . $mapType : '')) ){ @@ -380,16 +377,15 @@ class Config extends \Prefab { /** * @param string $key * @return null|mixed - * @throws Exception\PathfinderException */ static function getPathfinderData($key = ''){ $hiveKey = self::HIVE_KEY_PATHFINDER . ($key ? '.' . strtoupper($key) : ''); $data = null; // make sure it is always defined try{ if( !\Base::instance()->exists($hiveKey, $data) ){ - throw new Exception\PathfinderException(sprintf(self::ERROR_CONF_PATHFINDER, $hiveKey)); + throw new Exception\ConfigException(sprintf(self::ERROR_CONF_PATHFINDER, $hiveKey)); } - }catch (Exception\PathfinderException $e){ + }catch (Exception\ConfigException $e){ LogController::getLogger('ERROR')->write($e->getMessage()); } diff --git a/app/main/lib/logging/AbstractCharacterLog.php b/app/main/lib/logging/AbstractCharacterLog.php index 1580b017..a4083070 100644 --- a/app/main/lib/logging/AbstractCharacterLog.php +++ b/app/main/lib/logging/AbstractCharacterLog.php @@ -68,7 +68,6 @@ abstract class AbstractCharacterLog extends AbstractChannelLog{ /** * get character thumbnailUrl * @return string - * @throws \Exception\PathfinderException */ protected function getThumbUrl(): string { $url = ''; diff --git a/app/main/lib/logging/RallyLog.php b/app/main/lib/logging/RallyLog.php index 7fa1b577..b6171c3c 100644 --- a/app/main/lib/logging/RallyLog.php +++ b/app/main/lib/logging/RallyLog.php @@ -37,7 +37,6 @@ class RallyLog extends AbstractCharacterLog{ /** * @return string - * @throws \Exception\PathfinderException */ protected function getThumbUrl() : string{ $url = ''; diff --git a/app/main/lib/logging/handler/AbstractSlackWebhookHandler.php b/app/main/lib/logging/handler/AbstractSlackWebhookHandler.php index 756fb37a..5e348ce7 100644 --- a/app/main/lib/logging/handler/AbstractSlackWebhookHandler.php +++ b/app/main/lib/logging/handler/AbstractSlackWebhookHandler.php @@ -179,7 +179,6 @@ abstract class AbstractSlackWebhookHandler extends Handler\AbstractProcessingHan * @param array $attachment * @param array $characterData * @return array - * @throws \Exception\PathfinderException */ protected function setAuthor(array $attachment, array $characterData): array { if( !empty($characterData['id']) && !empty($characterData['name'])){ diff --git a/app/main/lib/logging/handler/SlackMapWebhookHandler.php b/app/main/lib/logging/handler/SlackMapWebhookHandler.php index 53bb5733..3d49aad6 100644 --- a/app/main/lib/logging/handler/SlackMapWebhookHandler.php +++ b/app/main/lib/logging/handler/SlackMapWebhookHandler.php @@ -15,7 +15,6 @@ class SlackMapWebhookHandler extends AbstractSlackWebhookHandler { /** * @param array $record * @return array - * @throws \Exception\PathfinderException */ protected function getSlackData(array $record) : array{ $postData = parent::getSlackData($record); diff --git a/app/main/lib/logging/handler/SlackRallyWebhookHandler.php b/app/main/lib/logging/handler/SlackRallyWebhookHandler.php index 71850c06..db3d0ceb 100644 --- a/app/main/lib/logging/handler/SlackRallyWebhookHandler.php +++ b/app/main/lib/logging/handler/SlackRallyWebhookHandler.php @@ -15,7 +15,6 @@ class SlackRallyWebhookHandler extends AbstractSlackWebhookHandler { /** * @param array $record * @return array - * @throws \Exception\PathfinderException */ protected function getSlackData(array $record) : array{ $postData = parent::getSlackData($record); diff --git a/app/main/lib/web.php b/app/main/lib/web.php index 6b0eb91d..bf1ff510 100644 --- a/app/main/lib/web.php +++ b/app/main/lib/web.php @@ -100,7 +100,6 @@ class Web extends \Web { * @param array $additionalOptions * @param int $retryCount request counter for failed call * @return array|FALSE|mixed - * @throws \Exception\PathfinderException */ public function request($url,array $options = null, $additionalOptions = [], $retryCount = 0 ) { $f3 = \Base::instance(); diff --git a/app/main/model/abstractmaptrackingmodel.php b/app/main/model/abstractmaptrackingmodel.php index 38e26a6b..9a2cce45 100644 --- a/app/main/model/abstractmaptrackingmodel.php +++ b/app/main/model/abstractmaptrackingmodel.php @@ -23,7 +23,7 @@ abstract class AbstractMapTrackingModel extends BasicModel implements LogModelIn 'on-delete' => 'CASCADE' ] ], - 'validate' => 'validate_notDry' + 'validate' => 'notDry' ], 'updatedCharacterId' => [ 'type' => Schema::DT_INT, @@ -35,7 +35,7 @@ abstract class AbstractMapTrackingModel extends BasicModel implements LogModelIn 'on-delete' => 'CASCADE' ] ], - 'validate' => 'validate_notDry' + 'validate' => 'notDry' ] ]; diff --git a/app/main/model/activitylogmodel.php b/app/main/model/activitylogmodel.php index bcee4e71..33275cb7 100644 --- a/app/main/model/activitylogmodel.php +++ b/app/main/model/activitylogmodel.php @@ -178,6 +178,7 @@ class ActivityLogModel extends BasicModel { * @param null $table * @param null $fields * @return bool + * @throws \Exception */ public static function setup($db=null, $table=null, $fields=null){ $status = parent::setup($db,$table,$fields); diff --git a/app/main/model/alliancemodel.php b/app/main/model/alliancemodel.php index 5c74debf..274cc6c7 100644 --- a/app/main/model/alliancemodel.php +++ b/app/main/model/alliancemodel.php @@ -77,7 +77,6 @@ class AllianceModel extends BasicModel { /** * get all maps for this alliance * @return array|mixed - * @throws \Exception\PathfinderException */ public function getMaps(){ $maps = []; diff --git a/app/main/model/basicmodel.php b/app/main/model/basicmodel.php index 48c19eaa..817c3d18 100644 --- a/app/main/model/basicmodel.php +++ b/app/main/model/basicmodel.php @@ -118,7 +118,7 @@ abstract class BasicModel extends \DB\Cortex { parent::__construct($db, $table, $fluid, $ttl); // insert events ------------------------------------------------------------------------------------ - $this->beforeinsert( function($self, $pkeys){ + $this->beforeinsert(function($self, $pkeys){ return $self->beforeInsertEvent($self, $pkeys); }); @@ -127,21 +127,21 @@ abstract class BasicModel extends \DB\Cortex { }); // update events ------------------------------------------------------------------------------------ - $this->beforeupdate( function($self, $pkeys){ + $this->beforeupdate(function($self, $pkeys){ return $self->beforeUpdateEvent($self, $pkeys); }); - $this->afterupdate( function($self, $pkeys){ + $this->afterupdate(function($self, $pkeys){ $self->afterUpdateEvent($self, $pkeys); }); // erase events ------------------------------------------------------------------------------------- - $this->beforeerase( function($self, $pkeys){ + $this->beforeerase(function($self, $pkeys){ return $self->beforeEraseEvent($self, $pkeys); }); - $this->aftererase( function($self, $pkeys){ + $this->aftererase(function($self, $pkeys){ $self->afterEraseEvent($self, $pkeys); }); } @@ -264,7 +264,7 @@ abstract class BasicModel extends \DB\Cortex { * get static fields for this model instance * @return array */ - protected function getStaticFieldConf(): array { + protected function getStaticFieldConf() : array { $staticFieldConfig = []; // static tables (fixed data) do not require them... @@ -299,13 +299,14 @@ abstract class BasicModel extends \DB\Cortex { * @param $val * @return bool */ - protected function validateField(string $key, $val): bool { + protected function validateField(string $key, $val) : bool { $valid = true; if($fieldConf = $this->fieldConf[$key]){ if($method = $this->fieldConf[$key]['validate']){ if( !is_string($method)){ - $method = 'validate_' . $key; + $method = $key; } + $method = 'validate_' . $method; if(method_exists($this, $method)){ // validate $key (column) with this method... $valid = $this->$method($key, $val); @@ -325,7 +326,7 @@ abstract class BasicModel extends \DB\Cortex { * @return bool * @throws \Exception\ValidationException */ - protected function validate_notDry($key, $val): bool { + protected function validate_notDry($key, $val) : bool { $valid = true; if($colConf = $this->fieldConf[$key]){ if(isset($colConf['belongs-to-one'])){ @@ -344,6 +345,30 @@ abstract class BasicModel extends \DB\Cortex { return $valid; } + /** + * validates a model field to be not empty + * @param $key + * @param $val + * @return bool + */ + protected function validate_notEmpty($key, $val) : bool { + $valid = false; + + if($colConf = $this->fieldConf[$key]){ + switch($colConf['type']){ + case Schema::DT_INT: + case Schema::DT_FLOAT: + if( (is_int($val) || ctype_digit($val)) && (int)$val > 0){ + $valid = true; + } + break; + default: + } + } + + return $valid; + } + /** * get key for for all objects in this table * @return string @@ -628,7 +653,7 @@ abstract class BasicModel extends \DB\Cortex { * function should be overwritten in parent classes * @return bool */ - public function isValid(): bool { + public function isValid() : bool { return true; } @@ -776,7 +801,7 @@ abstract class BasicModel extends \DB\Cortex { * @param string $action * @return Logging\LogInterface */ - protected function newLog($action = ''): Logging\LogInterface{ + protected function newLog($action = '') : Logging\LogInterface{ return new Logging\DefaultLog($action); } @@ -800,7 +825,7 @@ abstract class BasicModel extends \DB\Cortex { * get all validation errors * @return array */ - public function getErrors(): array { + public function getErrors() : array { return $this->validationError; } @@ -808,7 +833,7 @@ abstract class BasicModel extends \DB\Cortex { * checks whether data is outdated and should be refreshed * @return bool */ - protected function isOutdated(): bool { + protected function isOutdated() : bool { $outdated = true; if(!$this->dry()){ $timezone = $this->getF3()->get('getTimeZone')(); @@ -832,7 +857,7 @@ abstract class BasicModel extends \DB\Cortex { }catch(ValidationException $e){ $this->setValidationError($e); }catch(DatabaseException $e){ - self::getF3()->error($e->getCode(), $e->getMessage(), $e->getTrace()); + self::getF3()->error($e->getResponseCode(), $e->getMessage(), $e->getTrace()); } } @@ -913,7 +938,6 @@ abstract class BasicModel extends \DB\Cortex { * debug log function * @param string $text * @param string $type - * @throws \Exception\PathfinderException */ public static function log($text, $type = 'DEBUG'){ Controller\LogController::getLogger($type)->write($text); diff --git a/app/main/model/charactermodel.php b/app/main/model/charactermodel.php index 2cb360e8..8dbb1b4a 100644 --- a/app/main/model/charactermodel.php +++ b/app/main/model/charactermodel.php @@ -473,7 +473,6 @@ class CharacterModel extends BasicModel { * get ESI API "access_token" from OAuth * @return bool|mixed * @throws \Exception - * @throws \Exception\PathfinderException */ public function getAccessToken(){ $accessToken = false; @@ -544,7 +543,6 @@ class CharacterModel extends BasicModel { * checks whether this character is authorized to log in * -> check corp/ally whitelist config (pathfinder.ini) * @return bool - * @throws \Exception\PathfinderException */ public function isAuthorized(){ $authStatus = 'UNKNOWN'; @@ -614,7 +612,6 @@ class CharacterModel extends BasicModel { * get Pathfinder role for character * @return RoleModel * @throws \Exception - * @throws \Exception\PathfinderException */ public function requestRole() : RoleModel{ $role = null; @@ -660,7 +657,6 @@ class CharacterModel extends BasicModel { * request all corporation roles granted to this character * @return array * @throws \Exception - * @throws \Exception\PathfinderException */ protected function requestRoles(){ $rolesData = []; @@ -1021,7 +1017,6 @@ class CharacterModel extends BasicModel { /** * get all accessible map models for this character * @return MapModel[] - * @throws \Exception\PathfinderException */ public function getMaps(){ $this->filter( diff --git a/app/main/model/connectionlogmodel.php b/app/main/model/connectionlogmodel.php index d4cc21fe..065e6c82 100644 --- a/app/main/model/connectionlogmodel.php +++ b/app/main/model/connectionlogmodel.php @@ -30,11 +30,19 @@ class ConnectionLogModel extends BasicModel { 'table' => 'connection', 'on-delete' => 'CASCADE' ] - ] + ], + 'validate' => 'notDry' + ], + 'record' => [ + 'type' => Schema::DT_BOOL, + 'nullable' => false, + 'default' => 1, + 'index' => true ], 'shipTypeId' => [ 'type' => Schema::DT_INT, - 'index' => true + 'index' => true, + 'validate' => 'notEmpty' ], 'shipTypeName' => [ 'type' => Schema::DT_VARCHAR128, @@ -44,11 +52,13 @@ class ConnectionLogModel extends BasicModel { 'shipMass' => [ 'type' => Schema::DT_FLOAT, 'nullable' => false, - 'default' => 0 + 'default' => 0, + 'validate' => 'notEmpty' ], 'characterId' => [ 'type' => Schema::DT_INT, - 'index' => true + 'index' => true, + 'validate' => 'notEmpty' ], 'characterName' => [ 'type' => Schema::DT_VARCHAR128, @@ -57,6 +67,14 @@ class ConnectionLogModel extends BasicModel { ] ]; + /** + * set map data by an associative array + * @param array $data + */ + public function setData(array $data){ + $this->copyfrom($data, ['shipTypeId', 'shipTypeName', 'shipMass', 'characterId', 'characterName']); + } + /** * get connection log data * @return \stdClass @@ -64,6 +82,8 @@ class ConnectionLogModel extends BasicModel { public function getData() : \stdClass { $logData = (object) []; $logData->id = $this->id; + $logData->active = $this->active; + $logData->record = $this->record; $logData->connection = (object) []; $logData->connection->id = $this->get('connectionId', true); @@ -73,12 +93,46 @@ class ConnectionLogModel extends BasicModel { $logData->ship->typeName = $this->shipTypeName; $logData->ship->mass = $this->shipMass; + $logData->character = (object) []; + $logData->character->id = $this->characterId; + $logData->character->name = $this->characterName; + $logData->created = (object) []; $logData->created->created = strtotime($this->created); - $logData->created->character = (object) []; - $logData->created->character->id = $this->characterId; - $logData->created->character->name = $this->characterName; + + $logData->updated = (object) []; + $logData->updated->updated = strtotime($this->updated); return $logData; } + + /** + * validate shipTypeId + * @param string $key + * @param string $val + * @return bool + */ + protected function validate_shipTypeId(string $key, string $val): bool { + return !empty((int)$val); + } + + /** + * @return ConnectionModel + */ + public function getConnection() : ConnectionModel { + return $this->get('connectionId'); + } + + /** + * check object for model access + * @param CharacterModel $characterModel + * @return bool + */ + public function hasAccess(CharacterModel $characterModel) : bool { + $access = false; + if( !$this->dry() ){ + $access = $this->getConnection()->hasAccess($characterModel); + } + return $access; + } } \ No newline at end of file diff --git a/app/main/model/connectionmodel.php b/app/main/model/connectionmodel.php index dc4f04cf..8d295682 100644 --- a/app/main/model/connectionmodel.php +++ b/app/main/model/connectionmodel.php @@ -137,9 +137,9 @@ class ConnectionModel extends AbstractMapTrackingModel { /** * check object for model access * @param CharacterModel $characterModel - * @return mixed + * @return bool */ - public function hasAccess(CharacterModel $characterModel){ + public function hasAccess(CharacterModel $characterModel) : bool { $access = false; if( !$this->dry() ){ $access = $this->mapId->hasAccess($characterModel); @@ -149,7 +149,7 @@ class ConnectionModel extends AbstractMapTrackingModel { /** * set default connection type by search route between endpoints - * @throws \Exception\PathfinderException + * @throws \Exception */ public function setDefaultTypeData(){ if( @@ -215,7 +215,6 @@ class ConnectionModel extends AbstractMapTrackingModel { * @param $pkeys * @return bool * @throws \Exception\DatabaseException - * @throws \Exception\PathfinderException */ public function beforeInsertEvent($self, $pkeys){ // check for "default" connection type and add them if missing @@ -266,8 +265,8 @@ class ConnectionModel extends AbstractMapTrackingModel { /** * @param string $action - * @return Logging\LogInterface - * @throws \Exception\PathfinderException + * @return logging\LogInterface + * @throws \Exception\ConfigException */ public function newLog($action = ''): Logging\LogInterface{ return $this->getMap()->newLog($action)->setTempData($this->getLogObjectData()); @@ -335,10 +334,6 @@ class ConnectionModel extends AbstractMapTrackingModel { */ public function getLogs(){ $logs = []; - $this->filter('connectionLog', [ - 'active = :active', - ':active' => 1 - ]); if($this->connectionLog){ $logs = $this->connectionLog; @@ -377,20 +372,34 @@ class ConnectionModel extends AbstractMapTrackingModel { return $logsData; } + /** + * get blank connectionLog model + * @return ConnectionLogModel + * @throws \Exception + */ + public function getNewLog() : ConnectionLogModel { + /** + * @var $log ConnectionLogModel + */ + $log = self::getNew('ConnectionLogModel'); + $log->connectionId = $this; + return $log; + } + /** * log new mass for this connection * @param CharacterLogModel $characterLog - * @return $this + * @return ConnectionModel + * @throws \Exception */ - public function logMass(CharacterLogModel $characterLog){ + public function logMass(CharacterLogModel $characterLog) : self { if( !$characterLog->dry() ){ - $log = $this->rel('connectionLog'); + $log = $this->getNewLog(); $log->shipTypeId = $characterLog->shipTypeId; $log->shipTypeName = $characterLog->shipTypeName; $log->shipMass = $characterLog->shipMass; $log->characterId = $characterLog->characterId->_id; $log->characterName = $characterLog->characterId->name; - $log->connectionId = $this; $log->save(); } diff --git a/app/main/model/corporationmodel.php b/app/main/model/corporationmodel.php index 8109e2af..8a5ecaa2 100644 --- a/app/main/model/corporationmodel.php +++ b/app/main/model/corporationmodel.php @@ -181,7 +181,6 @@ class CorporationModel extends BasicModel { * @param array $mapIds * @param array $options * @return array - * @throws \Exception\PathfinderException */ public function getMaps($mapIds = [], $options = []){ $maps = []; diff --git a/app/main/model/mapmodel.php b/app/main/model/mapmodel.php index e7ed18bf..c229c9aa 100644 --- a/app/main/model/mapmodel.php +++ b/app/main/model/mapmodel.php @@ -10,9 +10,9 @@ namespace Model; use DB\SQL\Schema; use data\file\FileHandler; +use Exception\ConfigException; use lib\Config; use lib\logging; -use Exception\PathfinderException; class MapModel extends AbstractMapTrackingModel { @@ -44,7 +44,7 @@ class MapModel extends AbstractMapTrackingModel { 'on-delete' => 'CASCADE' ] ], - 'validate' => 'validate_notDry', + 'validate' => 'notDry', 'activity-log' => true ], 'typeId' => [ @@ -57,7 +57,7 @@ class MapModel extends AbstractMapTrackingModel { 'on-delete' => 'CASCADE' ] ], - 'validate' => 'validate_notDry', + 'validate' => 'notDry', 'activity-log' => true ], 'name' => [ @@ -199,7 +199,6 @@ class MapModel extends AbstractMapTrackingModel { * get data * -> this includes system and connection data as well * @return \stdClass - * @throws PathfinderException * @throws \Exception */ public function getData(){ @@ -791,7 +790,6 @@ class MapModel extends AbstractMapTrackingModel { * checks whether a character has access to this map or not * @param CharacterModel $characterModel * @return bool - * @throws PathfinderException */ public function hasAccess(CharacterModel $characterModel) : bool { $hasAccess = false; @@ -972,8 +970,8 @@ class MapModel extends AbstractMapTrackingModel { /** * @param string $action - * @return Logging\LogInterface - * @throws PathfinderException + * @return logging\LogInterface + * @throws ConfigException */ public function newLog($action = ''): Logging\LogInterface{ $logChannelData = $this->getLogChannelData(); @@ -1048,7 +1046,6 @@ class MapModel extends AbstractMapTrackingModel { /** * check if "activity logging" is enabled for this map type * @return bool - * @throws PathfinderException */ public function isActivityLogEnabled(): bool { return $this->logActivity && (bool) Config::getMapsDefaultConfig($this->typeId->name)['log_activity_enabled']; @@ -1057,7 +1054,6 @@ class MapModel extends AbstractMapTrackingModel { /** * check if "history logging" is enabled for this map type * @return bool - * @throws PathfinderException */ public function isHistoryLogEnabled(): bool { return $this->logHistory && (bool) Config::getMapsDefaultConfig($this->typeId->name)['log_history_enabled']; @@ -1067,7 +1063,7 @@ class MapModel extends AbstractMapTrackingModel { * check if "Slack WebHook" is enabled for this map type * @param string $channel * @return bool - * @throws PathfinderException + * @throws ConfigException */ public function isSlackChannelEnabled(string $channel): bool { $enabled = false; @@ -1077,7 +1073,7 @@ class MapModel extends AbstractMapTrackingModel { switch($channel){ case 'slackChannelHistory': $defaultMapConfigKey = 'send_history_slack_enabled'; break; case 'slackChannelRally': $defaultMapConfigKey = 'send_rally_slack_enabled'; break; - default: throw new PathfinderException(sprintf(self::ERROR_SLACK_CHANNEL, $channel)); + default: throw new ConfigException(sprintf(self::ERROR_SLACK_CHANNEL, $channel)); } if((bool) Config::getMapsDefaultConfig($this->typeId->name)[$defaultMapConfigKey]){ @@ -1095,7 +1091,7 @@ class MapModel extends AbstractMapTrackingModel { * check if "Discord WebHook" is enabled for this map type * @param string $channel * @return bool - * @throws PathfinderException + * @throws ConfigException */ public function isDiscordChannelEnabled(string $channel): bool { $enabled = false; @@ -1105,7 +1101,7 @@ class MapModel extends AbstractMapTrackingModel { switch($channel){ case 'discordWebHookURLHistory': $defaultMapConfigKey = 'send_history_discord_enabled'; break; case 'discordWebHookURLRally': $defaultMapConfigKey = 'send_rally_discord_enabled'; break; - default: throw new PathfinderException(sprintf(self::ERROR_DISCORD_CHANNEL, $channel)); + default: throw new ConfigException(sprintf(self::ERROR_DISCORD_CHANNEL, $channel)); } if((bool) Config::getMapsDefaultConfig($this->typeId->name)[$defaultMapConfigKey]){ @@ -1123,7 +1119,6 @@ class MapModel extends AbstractMapTrackingModel { * check if "E-Mail" Log is enabled for this map * @param string $type * @return bool - * @throws PathfinderException */ public function isMailSendEnabled(string $type): bool{ $enabled = false; @@ -1197,7 +1192,6 @@ class MapModel extends AbstractMapTrackingModel { * @param string $type * @param bool $addJson * @return \stdClass - * @throws PathfinderException */ public function getSMTPConfig(string $type, bool $addJson = true): \stdClass{ $config = Config::getSMTPConfig(); @@ -1350,7 +1344,6 @@ class MapModel extends AbstractMapTrackingModel { * get all active characters (with active log) * grouped by systems * @return \stdClass - * @throws PathfinderException * @throws \Exception */ public function getUserData(){ diff --git a/app/main/model/systemmodel.php b/app/main/model/systemmodel.php index 5d685093..e2081b1a 100644 --- a/app/main/model/systemmodel.php +++ b/app/main/model/systemmodel.php @@ -95,8 +95,6 @@ class SystemModel extends AbstractMapTrackingModel { ], 'description' => [ 'type' => Schema::DT_TEXT, - 'nullable' => false, - 'default' => '', 'activity-log' => true, 'validate' => true ], @@ -151,7 +149,7 @@ class SystemModel extends AbstractMapTrackingModel { $systemData->locked = $this->locked; $systemData->rallyUpdated = strtotime($this->rallyUpdated); $systemData->rallyPoke = $this->rallyPoke; - $systemData->description = $this->description; + $systemData->description = $this->description ? : ''; $systemData->position = (object) []; $systemData->position->x = $this->posX; @@ -490,8 +488,8 @@ class SystemModel extends AbstractMapTrackingModel { /** * @param string $action - * @return Logging\LogInterface - * @throws \Exception\PathfinderException + * @return logging\LogInterface + * @throws \Exception\ConfigException */ public function newLog($action = ''): Logging\LogInterface{ return $this->getMap()->newLog($action)->setTempData($this->getLogObjectData()); @@ -654,7 +652,7 @@ class SystemModel extends AbstractMapTrackingModel { * -> send to an Email * @param array $rallyData * @param CharacterModel $characterModel - * @throws \Exception\PathfinderException + * @throws \Exception\ConfigException */ public function sendRallyPoke(array $rallyData, CharacterModel $characterModel){ // rally log needs at least one handler to be valid diff --git a/app/main/model/systemsignaturemodel.php b/app/main/model/systemsignaturemodel.php index 323d49d3..d218fe3d 100644 --- a/app/main/model/systemsignaturemodel.php +++ b/app/main/model/systemsignaturemodel.php @@ -162,8 +162,8 @@ class SystemSignatureModel extends AbstractMapTrackingModel { /** * @param string $action - * @return Logging\LogInterface - * @throws \Exception\PathfinderException + * @return logging\LogInterface + * @throws \Exception\ConfigException */ public function newLog($action = ''): Logging\LogInterface{ return $this->getMap()->newLog($action)->setTempData($this->getLogObjectData()); diff --git a/app/main/model/universe/categorymodel.php b/app/main/model/universe/categorymodel.php index d75b37c3..dc0db9f4 100644 --- a/app/main/model/universe/categorymodel.php +++ b/app/main/model/universe/categorymodel.php @@ -33,14 +33,15 @@ class CategoryModel extends BasicUniverseModel { /** * get category data - * @return object + * @param array $additionalData + * @return null|object */ - public function getData(){ + public function getData(array $additionalData = []){ $categoryData = (object) []; $categoryData->id = $this->id; $categoryData->name = $this->name; - if($groupsData = $this->getGroupsData()){ + if($groupsData = $this->getGroupsData($additionalData)){ $categoryData->groups = $groupsData; } @@ -69,14 +70,15 @@ class CategoryModel extends BasicUniverseModel { } /** + * @param array $additionalData * @return array */ - protected function getGroupsData() : array { + protected function getGroupsData(array $additionalData = []) : array { $groupsData = []; $groups = $this->getGroups(); foreach($groups as $group){ - $groupsData[] = $group->getData(); + $groupsData[] = $group->getData($additionalData); } return $groupsData; diff --git a/app/main/model/universe/constellationmodel.php b/app/main/model/universe/constellationmodel.php index 05ff6019..8051a537 100644 --- a/app/main/model/universe/constellationmodel.php +++ b/app/main/model/universe/constellationmodel.php @@ -30,7 +30,7 @@ class ConstellationModel extends BasicUniverseModel { 'on-delete' => 'CASCADE' ] ], - 'validate' => 'validate_notDry' + 'validate' => 'notDry' ], 'x' => [ 'type' => Schema::DT_BIGINT, diff --git a/app/main/model/universe/groupmodel.php b/app/main/model/universe/groupmodel.php index c81d271d..85ad5a92 100644 --- a/app/main/model/universe/groupmodel.php +++ b/app/main/model/universe/groupmodel.php @@ -36,7 +36,7 @@ class GroupModel extends BasicUniverseModel { 'on-delete' => 'CASCADE' ] ], - 'validate' => 'validate_notDry' + 'validate' => 'notDry' ], 'types' => [ 'has-many' => ['Model\Universe\TypeModel', 'groupId'] @@ -45,14 +45,15 @@ class GroupModel extends BasicUniverseModel { /** * get group data - * @return object + * @param array $additionalData + * @return null|object */ - public function getData(){ + public function getData(array $additionalData = []){ $groupData = (object) []; $groupData->id = $this->id; $groupData->name = $this->name; - if($typesData = $this->getTypesData()){ + if($typesData = $this->getTypesData($additionalData)){ $groupData->types = $typesData; } @@ -81,14 +82,15 @@ class GroupModel extends BasicUniverseModel { } /** + * @param array $additionalData * @return array */ - protected function getTypesData() : array { + protected function getTypesData(array $additionalData = []) : array { $typesData = []; $types = $this->getTypes(); foreach($types as $type){ - $typesData[] = $type->getData(); + $typesData[] = $type->getData($additionalData); } return $typesData; diff --git a/app/main/model/universe/planetmodel.php b/app/main/model/universe/planetmodel.php index 7f60c5bd..f2f90431 100644 --- a/app/main/model/universe/planetmodel.php +++ b/app/main/model/universe/planetmodel.php @@ -30,7 +30,7 @@ class PlanetModel extends BasicUniverseModel { 'on-delete' => 'CASCADE' ] ], - 'validate' => 'validate_notDry' + 'validate' => 'notDry' ], 'typeId' => [ 'type' => Schema::DT_INT, @@ -42,7 +42,7 @@ class PlanetModel extends BasicUniverseModel { 'on-delete' => 'SET NULL' ] ], - 'validate' => 'validate_notDry' + 'validate' => 'notDry' ], 'x' => [ 'type' => Schema::DT_BIGINT, diff --git a/app/main/model/universe/stargatemodel.php b/app/main/model/universe/stargatemodel.php index 40782765..da6b8cbf 100644 --- a/app/main/model/universe/stargatemodel.php +++ b/app/main/model/universe/stargatemodel.php @@ -30,7 +30,7 @@ class StargateModel extends BasicUniverseModel { 'on-delete' => 'CASCADE' ] ], - 'validate' => 'validate_notDry' + 'validate' => 'notDry' ], 'typeId' => [ 'type' => Schema::DT_INT, @@ -42,7 +42,7 @@ class StargateModel extends BasicUniverseModel { 'on-delete' => 'SET NULL' ] ], - 'validate' => 'validate_notDry' + 'validate' => 'notDry' ], 'destinationSystemId' => [ 'type' => Schema::DT_INT, @@ -54,7 +54,7 @@ class StargateModel extends BasicUniverseModel { 'on-delete' => 'CASCADE' ] ], - 'validate' => 'validate_notDry' + 'validate' => 'notDry' ], 'x' => [ 'type' => Schema::DT_BIGINT, diff --git a/app/main/model/universe/starmodel.php b/app/main/model/universe/starmodel.php index a8e64c19..8f57a9d2 100644 --- a/app/main/model/universe/starmodel.php +++ b/app/main/model/universe/starmodel.php @@ -30,7 +30,7 @@ class StarModel extends BasicUniverseModel { 'on-delete' => 'SET NULL' ] ], - 'validate' => 'validate_notDry' + 'validate' => 'notDry' ], 'age' => [ 'type' => Schema::DT_BIGINT, diff --git a/app/main/model/universe/structuremodel.php b/app/main/model/universe/structuremodel.php index 5032ef81..1e40e9fe 100644 --- a/app/main/model/universe/structuremodel.php +++ b/app/main/model/universe/structuremodel.php @@ -37,7 +37,7 @@ class StructureModel extends BasicUniverseModel { 'on-delete' => 'CASCADE' ] ], - 'validate' => 'validate_notDry' + 'validate' => 'notDry' ], 'x' => [ 'type' => Schema::DT_FLOAT, diff --git a/app/main/model/universe/systemmodel.php b/app/main/model/universe/systemmodel.php index 0756daac..af79fe66 100644 --- a/app/main/model/universe/systemmodel.php +++ b/app/main/model/universe/systemmodel.php @@ -32,7 +32,7 @@ class SystemModel extends BasicUniverseModel { 'on-delete' => 'CASCADE' ] ], - 'validate' => 'validate_notDry' + 'validate' => 'notDry' ], 'starId' => [ 'type' => Schema::DT_INT, @@ -44,7 +44,7 @@ class SystemModel extends BasicUniverseModel { 'on-delete' => 'CASCADE' ] ], - 'validate' => 'validate_notDry' + 'validate' => 'notDry' ], 'security' => [ 'type' => Schema::DT_VARCHAR128 diff --git a/app/main/model/universe/systemstaticmodel.php b/app/main/model/universe/systemstaticmodel.php index 9b4d32ca..8664ef73 100644 --- a/app/main/model/universe/systemstaticmodel.php +++ b/app/main/model/universe/systemstaticmodel.php @@ -25,7 +25,7 @@ class SystemStaticModel extends BasicUniverseModel { 'on-delete' => 'CASCADE' ] ], - 'validate' => 'validate_notDry' + 'validate' => 'notDry' ], 'wormholeId' => [ 'type' => Schema::DT_INT, @@ -37,7 +37,7 @@ class SystemStaticModel extends BasicUniverseModel { 'on-delete' => 'CASCADE' ] ], - 'validate' => 'validate_notDry' + 'validate' => 'notDry' ] ]; diff --git a/app/main/model/universe/typemodel.php b/app/main/model/universe/typemodel.php index 45087636..7a69ab69 100644 --- a/app/main/model/universe/typemodel.php +++ b/app/main/model/universe/typemodel.php @@ -59,7 +59,7 @@ class TypeModel extends BasicUniverseModel { 'on-delete' => 'CASCADE' ] ], - 'validate' => 'validate_notDry', + 'validate' => 'notDry', ], 'marketGroupId' => [ 'type' => Schema::DT_INT, @@ -99,13 +99,18 @@ class TypeModel extends BasicUniverseModel { /** * get type data - * @return object + * @param array $additionalData + * @return null|object */ - public function getData(){ + public function getData(array $additionalData = []){ $typeData = (object) []; $typeData->id = $this->id; $typeData->name = $this->name; + foreach($additionalData as $key){ + $typeData->$key = $this->$key; + } + return $typeData; } diff --git a/app/main/model/universe/wormholemodel.php b/app/main/model/universe/wormholemodel.php index 35d4d202..725e5df3 100644 --- a/app/main/model/universe/wormholemodel.php +++ b/app/main/model/universe/wormholemodel.php @@ -35,7 +35,7 @@ class WormholeModel extends BasicUniverseModel { 'on-delete' => 'SET NULL' ] ], - 'validate' => 'validate_notDry' + 'validate' => 'notDry' ], 'static' => [ 'type' => Schema::DT_BOOL, diff --git a/app/main/model/usermodel.php b/app/main/model/usermodel.php index 27f4a3f5..4438c137 100644 --- a/app/main/model/usermodel.php +++ b/app/main/model/usermodel.php @@ -94,7 +94,6 @@ class UserModel extends BasicModel { * @param UserModel $self * @param $pkeys * @return bool - * @throws Exception\PathfinderException * @throws Exception\RegistrationException */ public function beforeInsertEvent($self, $pkeys){ @@ -137,7 +136,6 @@ class UserModel extends BasicModel { /** * checks whether user has a valid email address and pathfinder has a valid SMTP config * @return bool - * @throws Exception\PathfinderException */ protected function isMailSendEnabled() : bool{ return Config::isValidSMTPConfig($this->getSMTPConfig()); @@ -146,7 +144,6 @@ class UserModel extends BasicModel { /** * get SMTP config for this user * @return \stdClass - * @throws Exception\PathfinderException */ protected function getSMTPConfig() : \stdClass{ $config = Config::getSMTPConfig(); diff --git a/app/routes.ini b/app/routes.ini index c897c9f5..032e679a 100644 --- a/app/routes.ini +++ b/app/routes.ini @@ -18,3 +18,6 @@ GET|POST /api/@controller/@action [ajax] = Controller\Api\@cont 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 +[maps] +/api/rest/@controller* [ajax] = Controller\Api\Rest\@controller, 0, 512 +/api/rest/@controller/@id [ajax] = Controller\Api\Rest\@controller, 0, 512 \ No newline at end of file diff --git a/composer-dev.json b/composer-dev.json index e1d07696..d90eaa1a 100644 --- a/composer-dev.json +++ b/composer-dev.json @@ -21,6 +21,8 @@ }], "require": { "php-64bit": ">=7.0", + "ext-pdo": "*", + "ext-openssl": "*", "ext-curl": "*", "ext-json": "*", "ext-mbstring": "*", diff --git a/composer.json b/composer.json index 6507b0cd..7afbda72 100644 --- a/composer.json +++ b/composer.json @@ -21,6 +21,8 @@ }], "require": { "php-64bit": ">=7.0", + "ext-pdo": "*", + "ext-openssl": "*", "ext-curl": "*", "ext-json": "*", "ext-mbstring": "*", diff --git a/js/app.js b/js/app.js index de330a66..8ff598a1 100644 --- a/js/app.js +++ b/js/app.js @@ -54,7 +54,7 @@ requirejs.config({ blueImpGalleryBootstrap: 'lib/bootstrap-image-gallery', // v3.4.2 Bootstrap extension for Blue Imp Gallery - https://blueimp.github.io/Bootstrap-Image-Gallery bootstrapConfirmation: 'lib/bootstrap-confirmation', // v1.0.5 Bootstrap extension for inline confirm dialog - https://github.com/tavicu/bs-confirmation bootstrapToggle: 'lib/bootstrap-toggle.min', // v2.2.0 Bootstrap Toggle (Checkbox) - http://www.bootstraptoggle.com - lazyload: 'lib/jquery.lazyload.min', // v1.9.5 LazyLoader images - http://www.appelsiini.net/projects/lazyload + lazyload: 'lib/jquery.lazyload.min', // v1.9.7 LazyLoader images - http://www.appelsiini.net/projects/lazyload sortable: 'lib/sortable.min', // v1.6.0 Sortable - drag&drop reorder - https://github.com/rubaxa/Sortable 'summernote.loader': './app/summernote.loader', // v0.8.10 Summernote WYSIWYG editor -https://summernote.org diff --git a/js/app/datatables.loader.js b/js/app/datatables.loader.js index 7076767a..58c2109c 100644 --- a/js/app/datatables.loader.js +++ b/js/app/datatables.loader.js @@ -18,6 +18,9 @@ define([ lengthMenu: [[5, 10, 25, 50, -1], [5, 10, 25, 50, 'All']], order: [], // no default order because columnDefs is empty autoWidth: false, + language: { + info: '_START_ - _END_ of _TOTAL_ entries' + }, responsive: { breakpoints: Init.breakpoints, details: false diff --git a/js/app/init.js b/js/app/init.js index 38d1ccd1..dcb3a80f 100644 --- a/js/app/init.js +++ b/js/app/init.js @@ -9,6 +9,7 @@ define(['jquery'], ($) => { let Config = { path: { img: '/public/img/', // path for images + api: '/api/rest', //ajax URL - REST API // user API getCaptcha: '/api/user/getCaptcha', // ajax URL - get captcha image getServerStatus: '/api/user/getEveServerStatus', // ajax URL - get EVE-Online server status diff --git a/js/app/login.js b/js/app/login.js index 0b7a0045..40a2cad1 100644 --- a/js/app/login.js +++ b/js/app/login.js @@ -343,7 +343,7 @@ define([ let initGallery = (newElements) => { if( newElements.length > 0){ // We have to add ALL thumbnail elements to the gallery! - // -> even those wthat are invisible (not lazyLoaded) now! + // -> even those which are invisible (not lazyLoaded) now! // -> This is required for "swipe" through all images let allThumbLinks = getThumbnailElements(); @@ -814,7 +814,7 @@ define([ // init carousel initCarousel(); - // init scrollspy + // init scrollSpy // -> after "Carousel"! required for correct "viewport" calculation (Gallery)! initScrollSpy(); diff --git a/js/app/mappage.js b/js/app/mappage.js index 3ddef1fb..cc53aa73 100644 --- a/js/app/mappage.js +++ b/js/app/mappage.js @@ -79,15 +79,21 @@ define([ let reason = status + ' ' + jqXHR.status + ': ' + error; let errorData = []; + let redirect = false; // redirect user to other page e.g. login + let reload = true; // reload current page (default: true) if(jqXHR.responseJSON){ // handle JSON - let errorObj = jqXHR.responseJSON; + let responseObj = jqXHR.responseJSON; if( - errorObj.error && - errorObj.error.length > 0 + responseObj.error && + responseObj.error.length > 0 ){ - errorData = errorObj.error; + errorData = responseObj.error; + } + + if(responseObj.reroute){ + redirect = responseObj.reroute; } }else{ // handle HTML @@ -98,7 +104,13 @@ define([ } console.error(' ↪ %s Error response: %o', jqXHR.url, errorData); - $(document).trigger('pf:shutdown', {status: jqXHR.status, reason: reason, error: errorData}); + $(document).trigger('pf:shutdown', { + status: jqXHR.status, + reason: reason, + error: errorData, + redirect: redirect, + reload: reload + }); }; // map init functions ========================================================================================= diff --git a/js/app/page.js b/js/app/page.js index f7e91be7..eef0e1e7 100644 --- a/js/app/page.js +++ b/js/app/page.js @@ -787,12 +787,14 @@ define([ label: ' restart', className: ['btn-primary'].join(' '), callback: function(){ - // check if error was 5xx -> reload page - // -> else try to logout -> ajax request - if(data.status >= 500 && data.status < 600){ - // redirect to login - window.location = '../'; + if(data.redirect) { + // ... redirect user to e.g. login form page ... + Util.redirect(data.redirect, ['logout']); + }else if(data.reload){ + // ... or reload current page ... + location.reload(); }else{ + // ... fallback try to logout user documentElement.trigger('pf:menuLogout'); } } @@ -811,12 +813,9 @@ define([ }; // add error information (if available) - if( - data.error && - data.error.length - ){ - for(let i = 0; i < data.error.length; i++){ - options.content.textSmaller.push(data.error[i].message); + if(data.error && data.error.length){ + for(let error of data.error){ + options.content.textSmaller.push(error.message); } } diff --git a/js/app/ui/dialog/delete_account.js b/js/app/ui/dialog/delete_account.js index a3d7ce6a..30413023 100644 --- a/js/app/ui/dialog/delete_account.js +++ b/js/app/ui/dialog/delete_account.js @@ -76,7 +76,7 @@ define([ dialogElement.find('.modal-content').hideLoadingAnimation(); if(responseData.reroute !== undefined){ - Util.redirect(responseData.reroute, []); + Util.redirect(responseData.reroute); }else if( responseData.error && responseData.error.length > 0 diff --git a/js/app/ui/dialog/stats.js b/js/app/ui/dialog/stats.js index 883ec312..6e3f2a8a 100644 --- a/js/app/ui/dialog/stats.js +++ b/js/app/ui/dialog/stats.js @@ -753,7 +753,7 @@ define([ * show activity stats dialog */ $.fn.showStatsDialog = function(){ - requirejs(['text!templates/dialog/stats.html', 'mustache', 'datatables.loader'], function(template, Mustache){ + requirejs(['text!templates/dialog/stats.html', 'mustache', 'datatables.loader'], (template, Mustache) => { // get current statistics map settings let logActivityEnabled = false; let activeMap = Util.getMapModule().getActiveMap(); diff --git a/js/app/ui/form_element.js b/js/app/ui/form_element.js index 327433b8..71cad2b2 100644 --- a/js/app/ui/form_element.js +++ b/js/app/ui/form_element.js @@ -12,7 +12,8 @@ define([ let config = { // Select2 - resultOptionImageClass: 'pf-result-image' // class for Select2 result option entry with image + resultOptionImageClass: 'pf-result-image', // class for Select2 result option entry with image + select2ImageLazyLoadClass: 'pf-select2-image-lazyLoad' // class for Select2 result images that should be lazy loaded }; /** @@ -60,7 +61,7 @@ define([ } if(imagePath){ - thumb = ''; + thumb = ''; }else if(iconName){ thumb = ''; } @@ -564,6 +565,7 @@ define([ return group; }); },*/ + disabled: options.hasOwnProperty('disabled') ? options.disabled : false, allowClear: options.maxSelectionLength <= 1, maximumSelectionLength: options.maxSelectionLength, templateResult: formatCategoryTypeResultData @@ -605,6 +607,7 @@ define([ return { id: type.id, text: type.name, + mass: type.hasOwnProperty('mass') ? type.mass : null, groupId: this.groupId, categoryId: this.categoryId, categoryType: this.categoryType diff --git a/js/app/ui/module/connection_info.js b/js/app/ui/module/connection_info.js index c6c9d324..da1e503f 100644 --- a/js/app/ui/module/connection_info.js +++ b/js/app/ui/module/connection_info.js @@ -6,8 +6,9 @@ define([ 'jquery', 'app/init', 'app/util', + 'bootbox', 'app/map/util' -], ($, Init, Util, MapUtil) => { +], ($, Init, Util, bootbox, MapUtil) => { 'use strict'; let config = { @@ -46,9 +47,17 @@ define([ connectionInfoTableCellMassLeftClass: 'pf-connection-info-mass-left', // class for "mass left" table cell // dataTable + tableToolbarCondensedClass: 'pf-dataTable-condensed-toolbar', // class for condensed table toolbar connectionInfoTableClass: 'pf-connection-info-table', // class for connection tables tableCellImageClass: 'pf-table-image-cell', // class for table "image" cells tableCellCounterClass: 'pf-table-counter-cell', // class for table "counter" cells + tableCellActionClass: 'pf-table-action-cell', // class for "action" cells + + // connection dialog + connectionDialogId: 'pf-connection-info-dialog', // id for "connection" dialog + typeSelectId: 'pf-connection-info-dialog-type-select', // id for "ship type" select + shipMassId: 'pf-connection-info-dialog-mass', // id for "ship mass" input + characterSelectId: 'pf-connection-info-dialog-character-select', // id for "character" select // config showShip: true // default for "show current ship mass" toggle @@ -511,6 +520,34 @@ define([ return moduleElement.find('.' + config.connectionInfoPanelClass).not('#' + getConnectionElementId(0)); }; + /** + * enrich connectionData with "logs" data (if available) and other "missing" data + * @param connectionsData + * @param newConnectionsData + * @returns {*} + */ + let enrichConnectionsData = (connectionsData, newConnectionsData) => { + for(let i = 0; i < connectionsData.length; i++){ + for(let newConnectionData of newConnectionsData){ + if(connectionsData[i].id === newConnectionData.id){ + // copy some missing data + connectionsData[i].character = newConnectionData.character; + connectionsData[i].created = newConnectionData.created; + // check for mass logs and copy data + if(newConnectionData.logs && newConnectionData.logs.length){ + connectionsData[i].logs = newConnectionData.logs; + } + // check for signatures and copy data + if(newConnectionData.signatures && newConnectionData.signatures.length){ + connectionsData[i].signatures = newConnectionData.signatures; + } + break; + } + } + } + return connectionsData; + }; + /** * request connection log data * @param requestData @@ -530,24 +567,7 @@ define([ dataType: 'json', context: context }).done(function(connectionsData){ - // enrich connectionData with "logs" data (if available) and other "missing" data - for(let i = 0; i < this.connectionsData.length; i++){ - for(let connectionData of connectionsData){ - if(this.connectionsData[i].id === connectionData.id){ - // copy some missing data - this.connectionsData[i].created = connectionData.created; - // check for mass logs and copy data - if(connectionData.logs && connectionData.logs.length){ - this.connectionsData[i].logs = connectionData.logs; - } - // check for signatures and copy data - if(connectionData.signatures && connectionData.signatures.length){ - this.connectionsData[i].signatures = connectionData.signatures; - } - break; - } - } - } + this.connectionsData = enrichConnectionsData(this.connectionsData, connectionsData); callback(this.moduleElement, this.connectionsData); }).always(function(){ @@ -592,9 +612,9 @@ define([ */ let addConnectionsData = (moduleElement, connectionsData) => { - let getRowIndexesByData = (dataTable, colName, value) => { - return dataTable.rows().eq(0).filter((rowIdx) => { - return (dataTable.cell(rowIdx, colName + ':name').data() === value); + let getRowIndexesByData = (tableApi, colName, value) => { + return tableApi.rows().eq(0).filter((rowIdx) => { + return (tableApi.cell(rowIdx, colName + ':name').data() === value); }); }; @@ -607,42 +627,47 @@ define([ connectionInfoElement.data('connectionData', connectionData); // update dataTable --------------------------------------------------------------- - let dataTable = connectionElement.find('.dataTable').dataTable().api(); + let tableApi = connectionElement.find('.dataTable').dataTable().api(); if(connectionData.logs && connectionData.logs.length > 0){ for(let i = 0; i < connectionData.logs.length; i++){ let rowData = connectionData.logs[i]; - let row = null; + let rowNew = null; let animationStatus = null; - let indexes = getRowIndexesByData(dataTable, 'index', rowData.id); + let indexes = getRowIndexesByData(tableApi, 'index', rowData.id); if(indexes.length === 0){ // row not found -> add new row - row = dataTable.row.add( rowData ); + rowNew = tableApi.row.add(rowData); animationStatus = 'added'; - } - /* else{ - // we DON´t expect changes -> no row update) + }else{ // update row with FIRST index - //row = dataTable.row( parseInt(indexes[0]) ); - // update row data - //row.data(connectionData.logs[i]); - //animationStatus = 'changed'; - } */ + let row = tableApi.row( parseInt(indexes[0])); + let rowDataCurrent = row.data(); + + // check if row data changed + if(rowDataCurrent.updated.updated !== rowData.updated.updated){ + // ... row changed -> delete old and re-add + // -> cell actions might have changed + row.remove(); + rowNew = tableApi.row.add(rowData); + animationStatus = 'changed'; + } + } if( animationStatus !== null && - row.length > 0 + rowNew.length > 0 ){ - row.nodes().to$().data('animationStatus', animationStatus); + rowNew.nodes().to$().data('animationStatus', animationStatus); } } }else{ // clear table or leave empty - dataTable.clear(); + tableApi.clear(); } // redraw dataTable - dataTable.draw(false); + tableApi.draw(false); } } }; @@ -663,11 +688,45 @@ define([ let table = $('', { class: ['compact', 'stripe', 'order-column', 'row-border', 'nowrap', config.connectionInfoTableClass].join(' ') - }).append(''); + }).append(''); connectionElement.append(table); // init empty table let logTable = table.DataTable({ + dom: '<"container-fluid"' + + '<"row ' + config.tableToolbarCondensedClass + '"' + + '<"col-xs-5"i><"col-xs-5"p><"col-xs-2 text-right"B>>' + + '<"row"tr>>', + buttons: { + name: 'tableTools', + buttons: [ + { + name: 'addLog', + className: config.moduleHeadlineIconClass, + text: '', + action: function(e, tableApi, node, conf){ + let logData = {}; + + // pre-fill form with current character data (if available) + let currentUserData = Util.getCurrentUserData(); + if(currentUserData && currentUserData.character){ + logData.character = { + id: currentUserData.character.id, + name: currentUserData.character.name + }; + if(currentUserData.character.log){ + logData.ship = { + id: currentUserData.character.log.ship.typeId, + name: currentUserData.character.log.ship.typeName + }; + } + } + + showLogDialog(moduleElement, connectionElement, connectionData, logData); + } + } + ] + }, pageLength: 8, paging: true, pagingType: 'simple', @@ -678,11 +737,14 @@ define([ searching: false, hover: false, autoWidth: false, - // rowId: 'systemTo', language: { emptyTable: 'No jumps recorded', - info: '_START_ to _END_ of _MAX_', - infoEmpty: '' + info: '_START_ - _END_ of _MAX_', + infoEmpty: '', + paginate: { + previous: '', + next: '' + } }, columnDefs: [ { @@ -692,10 +754,25 @@ define([ orderable: false, searchable: false, width: 20, - class: 'text-center', - data: 'id' + className: ['text-center', 'txt-color'].join(' '), + data: 'id', + createdCell: function(cell, cellData, rowData, rowIndex, colIndex){ + if( + !rowData.record || + (rowData.updated.updated !== rowData.created.created) + ){ + // log was manually modified or added + $(cell) + .addClass(Util.config.helpClass) + .addClass( 'txt-color-orange').tooltip({ + container: 'body', + title: 'added/updated manually' + }); + } + } },{ targets: 1, + name: 'ship', title: '', width: 26, orderable: false, @@ -715,16 +792,17 @@ define([ } },{ targets: 2, + name: 'character', title: '', width: 26, orderable: false, className: [Util.config.helpDefaultClass, 'text-center', config.tableCellImageClass].join(' '), - data: 'created.character', + data: 'character', render: { - _: function(data, type, row){ - let value = data.name; + _: (cellData, type, rowData, meta) => { + let value = cellData.name; if(type === 'display'){ - value = ''; + value = ''; } return value; } @@ -734,20 +812,26 @@ define([ } },{ targets: 3, + name: 'mass', title: 'mass', className: ['text-right'].join(' ') , data: 'ship.mass', render: { - _: function(data, type, row){ - let value = data; + _: (cellData, type, rowData, meta) => { + let value = cellData; if(type === 'display'){ value = Util.formatMassValue(value); + if(!rowData.active){ + // log is "deleted" + value = '' + value + ''; + } } return value; } } },{ targets: 4, + name: 'created', title: 'log', width: 55, className: ['text-right', config.tableCellCounterClass].join(' '), @@ -755,6 +839,108 @@ define([ createdCell: function(cell, cellData, rowData, rowIndex, colIndex){ $(cell).initTimestampCounter('d'); } + },{ + targets: 5, + name: 'edit', + title: '', + orderable: false, + searchable: false, + width: 10, + className: ['text-center', config.tableCellActionClass, config.moduleHeadlineIconClass].join(' '), + data: null, + render: { + display: data => { + let icon = ''; + if(data.active){ + icon = ''; + } + return icon; + } + }, + createdCell: function(cell, cellData, rowData, rowIndex, colIndex){ + let tableApi = this.api(); + + if($(cell).is(':empty')){ + $(cell).removeClass(config.tableCellActionClass + ' ' + config.moduleHeadlineIconClass); + }else{ + $(cell).on('click', function(e){ + showLogDialog(moduleElement, connectionElement, connectionData, rowData); + }); + } + } + },{ + targets: 6, + name: 'delete', + title: '', + orderable: false, + searchable: false, + width: 10, + className: ['text-center', config.tableCellActionClass].join(' '), + data: 'active', + render: { + display: data => { + let val = ''; + if(data){ + val = ''; + } + return val; + } + }, + createdCell: function(cell, cellData, rowData, rowIndex, colIndex){ + let tableApi = this.api(); + + if(rowData.active){ + let confirmationSettings = { + container: 'body', + placement: 'left', + btnCancelClass: 'btn btn-sm btn-default', + btnCancelLabel: 'cancel', + btnCancelIcon: 'fas fa-fw fa-ban', + title: 'delete jump log', + btnOkClass: 'btn btn-sm btn-danger', + btnOkLabel: 'delete', + btnOkIcon: 'fas fa-fw fa-times', + onConfirm : function(e, target){ + // get current row data (important!) + // -> "rowData" param is not current state, values are "on createCell()" state + rowData = tableApi.row($(cell).parents('tr')).data(); + + connectionElement.find('table').showLoadingAnimation(); + + request('DELETE', 'log', rowData.id, {}, { + connectionElement: connectionElement + }, requestAlways) + .then( + payload => { + addConnectionsData(moduleElement, enrichConnectionsData([connectionData], payload.data)); + }, + Util.handleAjaxErrorResponse + ); + } + }; + + // init confirmation dialog + $(cell).confirmation(confirmationSettings); + }else { + $(cell).on('click', function(e){ + connectionElement.find('table').showLoadingAnimation(); + + let requestData = { + active: 1 + }; + + request('PATCH', 'log', rowData.id, requestData, { + connectionElement: connectionElement + }, requestAlways) + .then( + payload => { + addConnectionsData(moduleElement, enrichConnectionsData([connectionData], payload.data)); + }, + Util.handleAjaxErrorResponse + ); + }); + } + } } ], drawCallback: function(settings){ @@ -772,27 +958,34 @@ define([ }, footerCallback: function(row, data, start, end, display ){ - - let api = this.api(); - let sumColumnIndexes = [3]; + let tableApi = this.api(); + let sumColumnIndexes = ['mass:name', 'delete:name']; // column data for "sum" columns over this page - let pageTotalColumns = api + let pageTotalColumns = tableApi .columns( sumColumnIndexes, { page: 'all'} ) .data(); // sum columns for "total" sum - pageTotalColumns.each((colData, index) => { - pageTotalColumns[index] = colData.reduce((a, b) => { - return parseInt(a) + parseInt(b); + pageTotalColumns.each((colData, colIndex) => { + pageTotalColumns[colIndex] = colData.reduce((sum, val, rowIndex) => { + // sum "mass" (colIndex 0) only if not "deleted" (colIndex 1) + if(colIndex === 0 && pageTotalColumns[1][rowIndex]){ + return sum + parseInt(val); + }else{ + return sum; + } }, 0); }); - $(sumColumnIndexes).each((index, value) => { - $( api.column( value ).footer() ).text( Util.formatMassValue(pageTotalColumns[index]) ); + sumColumnIndexes.forEach((colSelector, index) => { + // only "mass" column footer needs updates + if(colSelector === 'mass:name'){ + $(tableApi.column(colSelector).footer()).text( Util.formatMassValue(pageTotalColumns[index]) ); - // save mass for further reCalculation of "info" table - connectionElement.find('.' + config.connectionInfoTableCellMassLogClass).data('mass', pageTotalColumns[index]); + // save mass for further reCalculation of "info" table + connectionElement.find('.' + config.connectionInfoTableCellMassLogClass).data('mass', pageTotalColumns[index]); + } }); // calculate "info" table ----------------------------------------------------- @@ -805,14 +998,186 @@ define([ logTable.on('order.dt search.dt', function(){ let pageInfo = logTable.page.info(); - logTable.column(0, {search:'applied', order:'applied'}).nodes().each((cell, i) => { - let content = (pageInfo.recordsTotal - i) + '.  '; + logTable.column('index:name', {search:'applied', order:'applied'}).nodes().each((cell, i) => { + let content = (pageInfo.recordsTotal - i) + '.'; $(cell).html(content); }); }); } }; + let request = (action, entity, ids = [], data = {}, context = {}, always = null) => { + + let requestExecutor = (resolve, reject) => { + let payload = { + action: 'request', + name: action.toLowerCase() + entity.charAt(0).toUpperCase() + entity.slice(1) + }; + + // build request url -------------------------------------------------------------------------------------- + let url = Init.path.api + '/' + entity; + + let path = ''; + if(isNaN(ids)){ + if(Array.isArray(ids)){ + path += '/' + ids.join(','); + } + }else{ + let id = parseInt(ids, 10); + path += id ? '/' + id : ''; + } + url += path; + + $.ajax({ + type: action, + url: url, + data: JSON.stringify(data), + contentType: 'application/json; charset=utf-8', + dataType: 'json', + context: context + }).done(function(response){ + payload.data = response; + payload.context = this; + resolve(payload); + }).fail(function(jqXHR, status, error){ + payload.data = { + jqXHR: jqXHR, + status: status, + error: error + }; + payload.context = this; + reject(payload); + }).always(function(){ + if(always){ + always(this); + } + }); + }; + + return new Promise(requestExecutor); + }; + + /** + * + * @param context + */ + let requestAlways = (context) => { + context.connectionElement.find('table').hideLoadingAnimation(); + }; + + /** + * show jump log dialog + * @param moduleElement + * @param connectionElement + * @param connectionData + * @param logData + */ + let showLogDialog = (moduleElement, connectionElement, connectionData, logData = {}) => { + + let data = { + id: config.connectionDialogId, + typeSelectId: config.typeSelectId, + shipMassId: config.shipMassId, + characterSelectId: config.characterSelectId, + logData: logData, + massFormat: () => { + return (val, render) => { + return (parseInt(render(val) || 0) / 1000) || ''; + }; + } + }; + + requirejs(['text!templates/dialog/connection_log.html', 'mustache'], (template, Mustache) => { + let content = Mustache.render(template, data); + + let connectionDialog = bootbox.dialog({ + title: 'Jump log', + message: content, + show: false, + buttons: { + close: { + label: 'cancel', + className: 'btn-default' + }, + success: { + label: ' save', + className: 'btn-success', + callback: function(){ + let form = this.find('form'); + + // validate form + form.validator('validate'); + + // check whether the form is valid + let formValid = form.isValidForm(); + + if(formValid){ + // get form data + let formData = form.getFormValues(); + formData.id = Util.getObjVal(logData, 'id') || 0; + formData.connectionId = Util.getObjVal(connectionData, 'id') || 0; + formData.shipTypeId = Util.getObjVal(formData, 'shipTypeId') || 0; + formData.shipMass = parseInt((Util.getObjVal(formData, 'shipMass') || 0) * 1000); + formData.characterId = Util.getObjVal(formData, 'characterId') || 0; + + // we need some "additional" form data from the Select2 dropdown + // -> data is required on the backend side + let formDataShip = form.find('#' + config.typeSelectId).select2('data'); + let formDataCharacter = form.find('#' + config.characterSelectId).select2('data'); + formData.shipTypeName = formDataShip.length ? formDataShip[0].text : ''; + formData.characterName = formDataCharacter.length ? formDataCharacter[0].text : ''; + + let method = formData.id ? 'PATCH' : 'PUT'; + + request(method, 'log', formData.id, formData, { + connectionElement: connectionElement, + formElement: form + }, requestAlways) + .then( + payload => { + addConnectionsData(moduleElement, enrichConnectionsData([connectionData], payload.data)); + this.modal('hide'); + }, + Util.handleAjaxErrorResponse + ); + + } + + return false; + } + } + } + }); + + connectionDialog.on('show.bs.modal', function(e){ + let modalContent = $('#' + config.connectionDialogId); + + // init type select live search + let selectElementType = modalContent.find('#' + config.typeSelectId); + selectElementType.initUniverseTypeSelect({ + categoryIds: [6], + maxSelectionLength: 1, + selected: [Util.getObjVal(logData, 'ship.typeId')] + }).on('select2:select select2:unselecting', function(e){ + // get ship mass from selected ship type and update mass input field + let shipMass = e.params.data ? e.params.data.mass / 1000 : ''; + modalContent.find('#' + config.shipMassId).val(shipMass); + }); + + // init character select live search + let selectElementCharacter = modalContent.find('#' + config.characterSelectId); + selectElementCharacter.initUniverseSearch({ + categoryNames: ['character'], + maxSelectionLength: 1 + }); + + }); + + // show dialog + connectionDialog.modal('show'); + }); + }; + /** * remove connection Panel from moduleElement * @param connectionElement diff --git a/js/app/ui/module/system_intel.js b/js/app/ui/module/system_intel.js index 64dcc35b..62b5caae 100644 --- a/js/app/ui/module/system_intel.js +++ b/js/app/ui/module/system_intel.js @@ -337,7 +337,7 @@ define([ data: statusData }); - // init character counter + // init char counter let textarea = modalContent.find('#' + config.descriptionTextareaId); let charCounter = modalContent.find('.' + config.descriptionTextareaCharCounter); Util.updateCounter(textarea, charCounter, maxDescriptionLength); diff --git a/js/app/util.js b/js/app/util.js index 5d2623eb..2204de10 100644 --- a/js/app/util.js +++ b/js/app/util.js @@ -8,6 +8,7 @@ define([ 'conf/signature_type', 'bootbox', 'localForage', + 'lazyload', 'velocity', 'velocityUI', 'customScrollbar', @@ -70,6 +71,7 @@ define([ // select2 select2Class: 'pf-select2', // class for all "Select2"
', { class: ['compact', 'stripe', 'order-column', 'row-border', 'nowrap', config.connectionInfoTableClass].join(' ') - }).append(''); + }).append(''); connectionElement.append(table); // init empty table let logTable = table.DataTable({ + dom: '<"container-fluid"' + + '<"row ' + config.tableToolbarCondensedClass + '"' + + '<"col-xs-5"i><"col-xs-5"p><"col-xs-2 text-right"B>>' + + '<"row"tr>>', + buttons: { + name: 'tableTools', + buttons: [ + { + name: 'addLog', + className: config.moduleHeadlineIconClass, + text: '', + action: function(e, tableApi, node, conf){ + let logData = {}; + + // pre-fill form with current character data (if available) + let currentUserData = Util.getCurrentUserData(); + if(currentUserData && currentUserData.character){ + logData.character = { + id: currentUserData.character.id, + name: currentUserData.character.name + }; + if(currentUserData.character.log){ + logData.ship = { + id: currentUserData.character.log.ship.typeId, + name: currentUserData.character.log.ship.typeName + }; + } + } + + showLogDialog(moduleElement, connectionElement, connectionData, logData); + } + } + ] + }, pageLength: 8, paging: true, pagingType: 'simple', @@ -678,11 +737,14 @@ define([ searching: false, hover: false, autoWidth: false, - // rowId: 'systemTo', language: { emptyTable: 'No jumps recorded', - info: '_START_ to _END_ of _MAX_', - infoEmpty: '' + info: '_START_ - _END_ of _MAX_', + infoEmpty: '', + paginate: { + previous: '', + next: '' + } }, columnDefs: [ { @@ -692,10 +754,25 @@ define([ orderable: false, searchable: false, width: 20, - class: 'text-center', - data: 'id' + className: ['text-center', 'txt-color'].join(' '), + data: 'id', + createdCell: function(cell, cellData, rowData, rowIndex, colIndex){ + if( + !rowData.record || + (rowData.updated.updated !== rowData.created.created) + ){ + // log was manually modified or added + $(cell) + .addClass(Util.config.helpClass) + .addClass( 'txt-color-orange').tooltip({ + container: 'body', + title: 'added/updated manually' + }); + } + } },{ targets: 1, + name: 'ship', title: '', width: 26, orderable: false, @@ -715,16 +792,17 @@ define([ } },{ targets: 2, + name: 'character', title: '', width: 26, orderable: false, className: [Util.config.helpDefaultClass, 'text-center', config.tableCellImageClass].join(' '), - data: 'created.character', + data: 'character', render: { - _: function(data, type, row){ - let value = data.name; + _: (cellData, type, rowData, meta) => { + let value = cellData.name; if(type === 'display'){ - value = ''; + value = ''; } return value; } @@ -734,20 +812,26 @@ define([ } },{ targets: 3, + name: 'mass', title: 'mass', className: ['text-right'].join(' ') , data: 'ship.mass', render: { - _: function(data, type, row){ - let value = data; + _: (cellData, type, rowData, meta) => { + let value = cellData; if(type === 'display'){ value = Util.formatMassValue(value); + if(!rowData.active){ + // log is "deleted" + value = '' + value + ''; + } } return value; } } },{ targets: 4, + name: 'created', title: 'log', width: 55, className: ['text-right', config.tableCellCounterClass].join(' '), @@ -755,6 +839,108 @@ define([ createdCell: function(cell, cellData, rowData, rowIndex, colIndex){ $(cell).initTimestampCounter('d'); } + },{ + targets: 5, + name: 'edit', + title: '', + orderable: false, + searchable: false, + width: 10, + className: ['text-center', config.tableCellActionClass, config.moduleHeadlineIconClass].join(' '), + data: null, + render: { + display: data => { + let icon = ''; + if(data.active){ + icon = ''; + } + return icon; + } + }, + createdCell: function(cell, cellData, rowData, rowIndex, colIndex){ + let tableApi = this.api(); + + if($(cell).is(':empty')){ + $(cell).removeClass(config.tableCellActionClass + ' ' + config.moduleHeadlineIconClass); + }else{ + $(cell).on('click', function(e){ + showLogDialog(moduleElement, connectionElement, connectionData, rowData); + }); + } + } + },{ + targets: 6, + name: 'delete', + title: '', + orderable: false, + searchable: false, + width: 10, + className: ['text-center', config.tableCellActionClass].join(' '), + data: 'active', + render: { + display: data => { + let val = ''; + if(data){ + val = ''; + } + return val; + } + }, + createdCell: function(cell, cellData, rowData, rowIndex, colIndex){ + let tableApi = this.api(); + + if(rowData.active){ + let confirmationSettings = { + container: 'body', + placement: 'left', + btnCancelClass: 'btn btn-sm btn-default', + btnCancelLabel: 'cancel', + btnCancelIcon: 'fas fa-fw fa-ban', + title: 'delete jump log', + btnOkClass: 'btn btn-sm btn-danger', + btnOkLabel: 'delete', + btnOkIcon: 'fas fa-fw fa-times', + onConfirm : function(e, target){ + // get current row data (important!) + // -> "rowData" param is not current state, values are "on createCell()" state + rowData = tableApi.row($(cell).parents('tr')).data(); + + connectionElement.find('table').showLoadingAnimation(); + + request('DELETE', 'log', rowData.id, {}, { + connectionElement: connectionElement + }, requestAlways) + .then( + payload => { + addConnectionsData(moduleElement, enrichConnectionsData([connectionData], payload.data)); + }, + Util.handleAjaxErrorResponse + ); + } + }; + + // init confirmation dialog + $(cell).confirmation(confirmationSettings); + }else { + $(cell).on('click', function(e){ + connectionElement.find('table').showLoadingAnimation(); + + let requestData = { + active: 1 + }; + + request('PATCH', 'log', rowData.id, requestData, { + connectionElement: connectionElement + }, requestAlways) + .then( + payload => { + addConnectionsData(moduleElement, enrichConnectionsData([connectionData], payload.data)); + }, + Util.handleAjaxErrorResponse + ); + }); + } + } } ], drawCallback: function(settings){ @@ -772,27 +958,34 @@ define([ }, footerCallback: function(row, data, start, end, display ){ - - let api = this.api(); - let sumColumnIndexes = [3]; + let tableApi = this.api(); + let sumColumnIndexes = ['mass:name', 'delete:name']; // column data for "sum" columns over this page - let pageTotalColumns = api + let pageTotalColumns = tableApi .columns( sumColumnIndexes, { page: 'all'} ) .data(); // sum columns for "total" sum - pageTotalColumns.each((colData, index) => { - pageTotalColumns[index] = colData.reduce((a, b) => { - return parseInt(a) + parseInt(b); + pageTotalColumns.each((colData, colIndex) => { + pageTotalColumns[colIndex] = colData.reduce((sum, val, rowIndex) => { + // sum "mass" (colIndex 0) only if not "deleted" (colIndex 1) + if(colIndex === 0 && pageTotalColumns[1][rowIndex]){ + return sum + parseInt(val); + }else{ + return sum; + } }, 0); }); - $(sumColumnIndexes).each((index, value) => { - $( api.column( value ).footer() ).text( Util.formatMassValue(pageTotalColumns[index]) ); + sumColumnIndexes.forEach((colSelector, index) => { + // only "mass" column footer needs updates + if(colSelector === 'mass:name'){ + $(tableApi.column(colSelector).footer()).text( Util.formatMassValue(pageTotalColumns[index]) ); - // save mass for further reCalculation of "info" table - connectionElement.find('.' + config.connectionInfoTableCellMassLogClass).data('mass', pageTotalColumns[index]); + // save mass for further reCalculation of "info" table + connectionElement.find('.' + config.connectionInfoTableCellMassLogClass).data('mass', pageTotalColumns[index]); + } }); // calculate "info" table ----------------------------------------------------- @@ -805,14 +998,186 @@ define([ logTable.on('order.dt search.dt', function(){ let pageInfo = logTable.page.info(); - logTable.column(0, {search:'applied', order:'applied'}).nodes().each((cell, i) => { - let content = (pageInfo.recordsTotal - i) + '.  '; + logTable.column('index:name', {search:'applied', order:'applied'}).nodes().each((cell, i) => { + let content = (pageInfo.recordsTotal - i) + '.'; $(cell).html(content); }); }); } }; + let request = (action, entity, ids = [], data = {}, context = {}, always = null) => { + + let requestExecutor = (resolve, reject) => { + let payload = { + action: 'request', + name: action.toLowerCase() + entity.charAt(0).toUpperCase() + entity.slice(1) + }; + + // build request url -------------------------------------------------------------------------------------- + let url = Init.path.api + '/' + entity; + + let path = ''; + if(isNaN(ids)){ + if(Array.isArray(ids)){ + path += '/' + ids.join(','); + } + }else{ + let id = parseInt(ids, 10); + path += id ? '/' + id : ''; + } + url += path; + + $.ajax({ + type: action, + url: url, + data: JSON.stringify(data), + contentType: 'application/json; charset=utf-8', + dataType: 'json', + context: context + }).done(function(response){ + payload.data = response; + payload.context = this; + resolve(payload); + }).fail(function(jqXHR, status, error){ + payload.data = { + jqXHR: jqXHR, + status: status, + error: error + }; + payload.context = this; + reject(payload); + }).always(function(){ + if(always){ + always(this); + } + }); + }; + + return new Promise(requestExecutor); + }; + + /** + * + * @param context + */ + let requestAlways = (context) => { + context.connectionElement.find('table').hideLoadingAnimation(); + }; + + /** + * show jump log dialog + * @param moduleElement + * @param connectionElement + * @param connectionData + * @param logData + */ + let showLogDialog = (moduleElement, connectionElement, connectionData, logData = {}) => { + + let data = { + id: config.connectionDialogId, + typeSelectId: config.typeSelectId, + shipMassId: config.shipMassId, + characterSelectId: config.characterSelectId, + logData: logData, + massFormat: () => { + return (val, render) => { + return (parseInt(render(val) || 0) / 1000) || ''; + }; + } + }; + + requirejs(['text!templates/dialog/connection_log.html', 'mustache'], (template, Mustache) => { + let content = Mustache.render(template, data); + + let connectionDialog = bootbox.dialog({ + title: 'Jump log', + message: content, + show: false, + buttons: { + close: { + label: 'cancel', + className: 'btn-default' + }, + success: { + label: ' save', + className: 'btn-success', + callback: function(){ + let form = this.find('form'); + + // validate form + form.validator('validate'); + + // check whether the form is valid + let formValid = form.isValidForm(); + + if(formValid){ + // get form data + let formData = form.getFormValues(); + formData.id = Util.getObjVal(logData, 'id') || 0; + formData.connectionId = Util.getObjVal(connectionData, 'id') || 0; + formData.shipTypeId = Util.getObjVal(formData, 'shipTypeId') || 0; + formData.shipMass = parseInt((Util.getObjVal(formData, 'shipMass') || 0) * 1000); + formData.characterId = Util.getObjVal(formData, 'characterId') || 0; + + // we need some "additional" form data from the Select2 dropdown + // -> data is required on the backend side + let formDataShip = form.find('#' + config.typeSelectId).select2('data'); + let formDataCharacter = form.find('#' + config.characterSelectId).select2('data'); + formData.shipTypeName = formDataShip.length ? formDataShip[0].text : ''; + formData.characterName = formDataCharacter.length ? formDataCharacter[0].text : ''; + + let method = formData.id ? 'PATCH' : 'PUT'; + + request(method, 'log', formData.id, formData, { + connectionElement: connectionElement, + formElement: form + }, requestAlways) + .then( + payload => { + addConnectionsData(moduleElement, enrichConnectionsData([connectionData], payload.data)); + this.modal('hide'); + }, + Util.handleAjaxErrorResponse + ); + + } + + return false; + } + } + } + }); + + connectionDialog.on('show.bs.modal', function(e){ + let modalContent = $('#' + config.connectionDialogId); + + // init type select live search + let selectElementType = modalContent.find('#' + config.typeSelectId); + selectElementType.initUniverseTypeSelect({ + categoryIds: [6], + maxSelectionLength: 1, + selected: [Util.getObjVal(logData, 'ship.typeId')] + }).on('select2:select select2:unselecting', function(e){ + // get ship mass from selected ship type and update mass input field + let shipMass = e.params.data ? e.params.data.mass / 1000 : ''; + modalContent.find('#' + config.shipMassId).val(shipMass); + }); + + // init character select live search + let selectElementCharacter = modalContent.find('#' + config.characterSelectId); + selectElementCharacter.initUniverseSearch({ + categoryNames: ['character'], + maxSelectionLength: 1 + }); + + }); + + // show dialog + connectionDialog.modal('show'); + }); + }; + /** * remove connection Panel from moduleElement * @param connectionElement diff --git a/public/js/v1.4.3/app/ui/module/system_intel.js b/public/js/v1.4.3/app/ui/module/system_intel.js index 64dcc35b..62b5caae 100644 --- a/public/js/v1.4.3/app/ui/module/system_intel.js +++ b/public/js/v1.4.3/app/ui/module/system_intel.js @@ -337,7 +337,7 @@ define([ data: statusData }); - // init character counter + // init char counter let textarea = modalContent.find('#' + config.descriptionTextareaId); let charCounter = modalContent.find('.' + config.descriptionTextareaCharCounter); Util.updateCounter(textarea, charCounter, maxDescriptionLength); diff --git a/public/js/v1.4.3/app/util.js b/public/js/v1.4.3/app/util.js index 5d2623eb..2204de10 100644 --- a/public/js/v1.4.3/app/util.js +++ b/public/js/v1.4.3/app/util.js @@ -8,6 +8,7 @@ define([ 'conf/signature_type', 'bootbox', 'localForage', + 'lazyload', 'velocity', 'velocityUI', 'customScrollbar', @@ -70,6 +71,7 @@ define([ // select2 select2Class: 'pf-select2', // class for all "Select2" + + + + + + +
+
+ +
+
+ t + +
+
+
+
+
+ + +
+
+
+ +
+
+ + + +
+
+
+
+
+ + \ No newline at end of file diff --git a/public/templates/dialog/map.html b/public/templates/dialog/map.html index 63aef9e1..eada8f50 100644 --- a/public/templates/dialog/map.html +++ b/public/templates/dialog/map.html @@ -43,7 +43,7 @@ {{#hasRightMapUpdate}} -
+

Map options

@@ -116,7 +116,7 @@
-
+
- - - - - - - - + + + + + + +
@@ -181,23 +180,24 @@
- - + + +
-
-
-
- -
-
- - +
+
+ +
+
+ + +
@@ -219,7 +219,7 @@
-
+