diff --git a/app/main/controller/accesscontroller.php b/app/main/controller/accesscontroller.php index 73fc2426..63bd33a5 100644 --- a/app/main/controller/accesscontroller.php +++ b/app/main/controller/accesscontroller.php @@ -24,8 +24,19 @@ class AccessController extends Controller { $loginCheck = $this->checkLogTimer($f3); if( !$loginCheck ){ - // no user found or LogIn timer expired + // no user found or login timer expired $this->logout($f3); + + if( $f3->get('AJAX') ){ + // unauthorized request + $f3->status(403); + }else{ + // redirect to landing page + $f3->reroute('@login'); + } + + // die() triggers unload() function + die(); } } diff --git a/app/main/controller/api/connection.php b/app/main/controller/api/connection.php index 45f296a7..7bb7fe3d 100644 --- a/app/main/controller/api/connection.php +++ b/app/main/controller/api/connection.php @@ -39,56 +39,53 @@ class Connection extends Controller\AccessController{ $activeCharacter = $this->getCharacter(); - if($activeCharacter){ + // get map model and check map access + /** + * @var Model\MapModel $map + */ + $map = Model\BasicModel::getNew('MapModel'); + $map->getById( (int)$mapData['id'] ); - // get map model and check map access - /** - * @var Model\MapModel $map - */ - $map = Model\BasicModel::getNew('MapModel'); - $map->getById( (int)$mapData['id'] ); + if( $map->hasAccess($activeCharacter) ){ + $source = $map->getSystemById( $connectionData['source'] ); + $target = $map->getSystemById( $connectionData['target'] ); - if( $map->hasAccess($activeCharacter) ){ - $source = $map->getSystemById( $connectionData['source'] ); - $target = $map->getSystemById( $connectionData['target'] ); + if( + !is_null($source) && + !is_null($target) + ){ + /** + * @var $connection Model\ConnectionModel + */ + $connection = Model\BasicModel::getNew('ConnectionModel'); + $connection->getById( (int)$connectionData['id'] ); - if( - !is_null($source) && - !is_null($target) - ){ - /** - * @var $connection Model\ConnectionModel - */ - $connection = Model\BasicModel::getNew('ConnectionModel'); - $connection->getById( (int)$connectionData['id'] ); + // search if systems are neighbors + $routeController = new Route(); + $routeController->initJumpData(); + $route = $routeController->findRoute($connectionData['sourceName'], $connectionData['targetName'], 1); - // search if systems are neighbors - $routeController = new Route(); - $routeController->initJumpData(); - $route = $routeController->findRoute($connectionData['sourceName'], $connectionData['targetName'], 1); + if($route['routePossible'] == true){ + // systems are next to each other + $connectionData['scope'] = 'stargate'; + $connectionData['type'] = ['stargate']; + }elseif($connectionData['scope'] == 'stargate'){ + // connection scope changed -> this can not be a stargate + $connectionData['scope'] = 'wh'; + $connectionData['type'] = ['wh_fresh']; + } - if($route['routePossible'] == true){ - // systems are next to each other - $connectionData['scope'] = 'stargate'; - $connectionData['type'] = ['stargate']; - }elseif($connectionData['scope'] == 'stargate'){ - // connection scope changed -> this can not be a stargate - $connectionData['scope'] = 'wh'; - $connectionData['type'] = ['wh_fresh']; - } + $connectionData['mapId'] = $map; - $connectionData['mapId'] = $map; + // "updated" should not be set by client e.g. after manual drag&drop + unset($connectionData['updated']); - // "updated" should not be set by client e.g. after manual drag&drop - unset($connectionData['updated']); + $connection->setData($connectionData); - $connection->setData($connectionData); + if( $connection->isValid() ){ + $connection->save(); - if( $connection->isValid() ){ - $connection->save(); - - $newConnectionData = $connection->getData(); - } + $newConnectionData = $connection->getData(); } } } @@ -104,18 +101,17 @@ class Connection extends Controller\AccessController{ */ public function delete(\Base $f3){ $connectionIds = $f3->get('POST.connectionIds'); + $activeCharacter = $this->getCharacter(); - if($activeCharacter = $this->getCharacter()){ - /** - * @var Model\ConnectionModel $connection - */ - $connection = Model\BasicModel::getNew('ConnectionModel'); - foreach($connectionIds as $connectionId){ - $connection->getById($connectionId); - $connection->delete( $activeCharacter ); + /** + * @var Model\ConnectionModel $connection + */ + $connection = Model\BasicModel::getNew('ConnectionModel'); + foreach($connectionIds as $connectionId){ + $connection->getById($connectionId); + $connection->delete( $activeCharacter ); - $connection->reset(); - } + $connection->reset(); } echo json_encode([]); diff --git a/app/main/controller/api/map.php b/app/main/controller/api/map.php index 23e7f6da..a53303ac 100644 --- a/app/main/controller/api/map.php +++ b/app/main/controller/api/map.php @@ -208,117 +208,111 @@ class Map extends Controller\AccessController { ){ $activeCharacter = $this->getCharacter(); - if($activeCharacter){ + /** + * @var $map Model\MapModel + */ + $map = Model\BasicModel::getNew('MapModel'); - /** - * @var $map Model\MapModel - */ - $map = Model\BasicModel::getNew('MapModel'); + /** + * @var $system Model\SystemModel + */ + $system = Model\BasicModel::getNew('SystemModel'); - /** - * @var $system Model\SystemModel - */ - $system = Model\BasicModel::getNew('SystemModel'); + /** + * @var $connection Model\ConnectionModel + */ + $connection = Model\BasicModel::getNew('ConnectionModel'); + + foreach($importData['mapData'] as $mapData){ + if( + isset($mapData['config']) && + isset($mapData['data']) + ){ - /** - * @var $connection Model\ConnectionModel - */ - $connection = Model\BasicModel::getNew('ConnectionModel'); - foreach($importData['mapData'] as $mapData){ if( - isset($mapData['config']) && - isset($mapData['data']) + isset($mapData['data']['systems']) && + isset($mapData['data']['connections']) ){ + if(isset($mapData['config']['id'])){ + unset($mapData['config']['id']); + } + $map->setData($mapData['config']); + $map->typeId = (int)$importData['typeId']; + $map->save(); - if( - isset($mapData['data']['systems']) && - isset($mapData['data']['connections']) - ){ - if(isset($mapData['config']['id'])){ - unset($mapData['config']['id']); + // new system IDs will be generated + // therefore we need to temp store a mapping between IDs + $tempSystemIdMapping = []; + + foreach($mapData['data']['systems'] as $systemData){ + if(isset($systemData['id'])){ + $oldId = (int)$systemData['id']; + unset($systemData['id']); + + $system->setData($systemData); + $system->mapId = $map; + $system->createdCharacterId = $activeCharacter; + $system->updatedCharacterId = $activeCharacter; + $system->save(); + + $tempSystemIdMapping[$oldId] = $system->id; + $system->reset(); } + } - $map->setData($mapData['config']); - $map->typeId = (int)$importData['typeId']; - $map->save(); - - // new system IDs will be generated - // therefore we need to temp store a mapping between IDs - $tempSystemIdMapping = []; - - foreach($mapData['data']['systems'] as $systemData){ - if(isset($systemData['id'])){ - $oldId = (int)$systemData['id']; - unset($systemData['id']); - - $system->setData($systemData); - $system->mapId = $map; - $system->createdCharacterId = $activeCharacter; - $system->updatedCharacterId = $activeCharacter; - $system->save(); - - $tempSystemIdMapping[$oldId] = $system->id; - $system->reset(); + foreach($mapData['data']['connections'] as $connectionData){ + // check if source and target IDs match with new system ID + if( + isset( $tempSystemIdMapping[$connectionData['source']] ) && + isset( $tempSystemIdMapping[$connectionData['target']] ) + ){ + if(isset($connectionData['id'])){ + unset($connectionData['id']); } + + $connection->setData($connectionData); + $connection->mapId = $map; + $connection->source = $tempSystemIdMapping[$connectionData['source']]; + $connection->target = $tempSystemIdMapping[$connectionData['target']]; + $connection->save(); + + $connection->reset(); } + } - foreach($mapData['data']['connections'] as $connectionData){ - // check if source and target IDs match with new system ID - if( - isset( $tempSystemIdMapping[$connectionData['source']] ) && - isset( $tempSystemIdMapping[$connectionData['target']] ) - ){ - if(isset($connectionData['id'])){ - unset($connectionData['id']); - } - - $connection->setData($connectionData); - $connection->mapId = $map; - $connection->source = $tempSystemIdMapping[$connectionData['source']]; - $connection->target = $tempSystemIdMapping[$connectionData['target']]; - $connection->save(); - - $connection->reset(); - } + // map access info should not automatically imported + if($map->isPrivate()){ + $map->setAccess($activeCharacter); + }elseif($map->isCorporation()){ + if($corporation = $activeCharacter->getCorporation()){ + $map->setAccess($corporation); } - - // map access info should not automatically imported - if($map->isPrivate()){ - $map->setAccess($activeCharacter); - }elseif($map->isCorporation()){ - if($corporation = $activeCharacter->getCorporation()){ - $map->setAccess($corporation); - } - }elseif($map->isAlliance()){ - if($alliance = $activeCharacter->getAlliance()){ - $map->setAccess($alliance); - } + }elseif($map->isAlliance()){ + if($alliance = $activeCharacter->getAlliance()){ + $map->setAccess($alliance); } - - }else{ - // systems || connections missing - $missingConfigError = (object) []; - $missingConfigError->type = 'error'; - $missingConfigError->message = 'Map data not valid (systems || connections) missing'; - $return->error[] = $missingConfigError; } }else{ - // map config || systems/connections missing + // systems || connections missing $missingConfigError = (object) []; $missingConfigError->type = 'error'; - $missingConfigError->message = 'Map data not valid (config || data) missing'; + $missingConfigError->message = 'Map data not valid (systems || connections) missing'; $return->error[] = $missingConfigError; } - - $map->reset(); + }else{ + // map config || systems/connections missing + $missingConfigError = (object) []; + $missingConfigError->type = 'error'; + $missingConfigError->message = 'Map data not valid (config || data) missing'; + $return->error[] = $missingConfigError; } - }else{ - // user not found - $return->error[] = $this->getLogoutError(); + + + $map->reset(); } }else{ // map data missing @@ -345,143 +339,140 @@ class Map extends Controller\AccessController { if( isset($formData['id']) ){ $activeCharacter = $this->getCharacter(0); - if($activeCharacter){ + /** + * @var $map Model\MapModel + */ + $map = Model\BasicModel::getNew('MapModel'); + $map->getById( (int)$formData['id'] ); - /** - * @var $map Model\MapModel - */ - $map = Model\BasicModel::getNew('MapModel'); - $map->getById( (int)$formData['id'] ); + if( + $map->dry() || + $map->hasAccess($activeCharacter) + ){ + // new map + $map->setData($formData); + $map = $map->save(); - if( - $map->dry() || - $map->hasAccess($activeCharacter) - ){ - // new map - $map->setData($formData); - $map = $map->save(); + // save global map access. Depends on map "type" + if($map->isPrivate()){ - // save global map access. Depends on map "type" - if($map->isPrivate()){ + // share map between characters -> set access + if(isset($formData['mapCharacters'])){ + // avoid abuse -> respect share limits + $accessCharacters = array_slice( $formData['mapCharacters'], 0, $f3->get('PATHFINDER.MAX_SHARED_CHARACTER') ); - // share map between characters -> set access - if(isset($formData['mapCharacters'])){ + // clear map access. In case something has removed from access list + $map->clearAccess(); + + /** + * @var $tempCharacter Model\CharacterModel + */ + $tempCharacter = Model\BasicModel::getNew('CharacterModel'); + + foreach($accessCharacters as $characterId){ + $tempCharacter->getById( (int)$characterId ); + + if( + !$tempCharacter->dry() && + $tempCharacter->shared == 1 // check if map shared is enabled + ){ + $map->setAccess($tempCharacter); + } + + $tempCharacter->reset(); + } + } + + // the current character itself should always have access + // just in case he removed himself :) + $map->setAccess($activeCharacter); + }elseif($map->isCorporation()){ + $corporation = $activeCharacter->getCorporation(); + + if($corporation){ + // the current user has to have a corporation when + // working on corporation maps! + + // share map between corporations -> set access + if(isset($formData['mapCorporations'])){ // avoid abuse -> respect share limits - $accessCharacters = array_slice( $formData['mapCharacters'], 0, $f3->get('PATHFINDER.MAX_SHARED_CHARACTER') ); + $accessCorporations = array_slice( $formData['mapCorporations'], 0, $f3->get('PATHFINDER.MAX_SHARED_CORPORATION') ); // clear map access. In case something has removed from access list $map->clearAccess(); /** - * @var $tempCharacter Model\CharacterModel + * @var $tempCorporation Model\CorporationModel */ - $tempCharacter = Model\BasicModel::getNew('CharacterModel'); + $tempCorporation = Model\BasicModel::getNew('CorporationModel'); - foreach($accessCharacters as $characterId){ - $tempCharacter->getById( (int)$characterId ); + foreach($accessCorporations as $corporationId){ + $tempCorporation->getById( (int)$corporationId ); if( - !$tempCharacter->dry() && - $tempCharacter->shared == 1 // check if map shared is enabled + !$tempCorporation->dry() && + $tempCorporation->shared == 1 // check if map shared is enabled ){ - $map->setAccess($tempCharacter); + $map->setAccess($tempCorporation); } - $tempCharacter->reset(); + $tempCorporation->reset(); } } - // the current character itself should always have access - // just in case he removed himself :) - $map->setAccess($activeCharacter); - }elseif($map->isCorporation()){ - $corporation = $activeCharacter->getCorporation(); - - if($corporation){ - // the current user has to have a corporation when - // working on corporation maps! - - // share map between corporations -> set access - if(isset($formData['mapCorporations'])){ - // avoid abuse -> respect share limits - $accessCorporations = array_slice( $formData['mapCorporations'], 0, $f3->get('PATHFINDER.MAX_SHARED_CORPORATION') ); - - // clear map access. In case something has removed from access list - $map->clearAccess(); - - /** - * @var $tempCorporation Model\CorporationModel - */ - $tempCorporation = Model\BasicModel::getNew('CorporationModel'); - - foreach($accessCorporations as $corporationId){ - $tempCorporation->getById( (int)$corporationId ); - - if( - !$tempCorporation->dry() && - $tempCorporation->shared == 1 // check if map shared is enabled - ){ - $map->setAccess($tempCorporation); - } - - $tempCorporation->reset(); - } - } - - // the corporation of the current user should always have access - $map->setAccess($corporation); - } - }elseif($map->isAlliance()){ - $alliance = $activeCharacter->getAlliance(); - - if($alliance){ - // the current user has to have a alliance when - // working on alliance maps! - - // share map between alliances -> set access - if(isset($formData['mapAlliances'])){ - // avoid abuse -> respect share limits - $accessAlliances = array_slice( $formData['mapAlliances'], 0, $f3->get('PATHFINDER.MAX_SHARED_ALLIANCE') ); - - // clear map access. In case something has removed from access list - $map->clearAccess(); - - /** - * @var $tempAlliance Model\AllianceModel - */ - $tempAlliance = Model\BasicModel::getNew('AllianceModel'); - - foreach($accessAlliances as $allianceId){ - $tempAlliance->getById( (int)$allianceId ); - - if( - !$tempAlliance->dry() && - $tempAlliance->shared == 1 // check if map shared is enabled - ){ - $map->setAccess($tempAlliance); - } - - $tempAlliance->reset(); - } - - } - - // the alliance of the current user should always have access - $map->setAccess($alliance); - } + // the corporation of the current user should always have access + $map->setAccess($corporation); } - // reload the same map model (refresh) - // this makes sure all data is up2date - $map->getById( $map->id, 0 ); + }elseif($map->isAlliance()){ + $alliance = $activeCharacter->getAlliance(); - $return->mapData = $map->getData(); - }else{ - // map access denied - $captchaError = (object) []; - $captchaError->type = 'error'; - $captchaError->message = 'Access denied'; - $return->error[] = $captchaError; + if($alliance){ + // the current user has to have a alliance when + // working on alliance maps! + + // share map between alliances -> set access + if(isset($formData['mapAlliances'])){ + // avoid abuse -> respect share limits + $accessAlliances = array_slice( $formData['mapAlliances'], 0, $f3->get('PATHFINDER.MAX_SHARED_ALLIANCE') ); + + // clear map access. In case something has removed from access list + $map->clearAccess(); + + /** + * @var $tempAlliance Model\AllianceModel + */ + $tempAlliance = Model\BasicModel::getNew('AllianceModel'); + + foreach($accessAlliances as $allianceId){ + $tempAlliance->getById( (int)$allianceId ); + + if( + !$tempAlliance->dry() && + $tempAlliance->shared == 1 // check if map shared is enabled + ){ + $map->setAccess($tempAlliance); + } + + $tempAlliance->reset(); + } + + } + + // the alliance of the current user should always have access + $map->setAccess($alliance); + } } + // reload the same map model (refresh) + // this makes sure all data is up2date + $map->getById( $map->id, 0 ); + + $return->mapData = $map->getData(); + }else{ + // map access denied + $captchaError = (object) []; + $captchaError->type = 'error'; + $captchaError->message = 'Access denied'; + $return->error[] = $captchaError; } }else{ // map id field missing @@ -502,14 +493,12 @@ class Map extends Controller\AccessController { $mapData = (array)$f3->get('POST.mapData'); $activeCharacter = $this->getCharacter(); - if($activeCharacter){ - /** - * @var $map Model\MapModel - */ - $map = Model\BasicModel::getNew('MapModel'); - $map->getById($mapData['id']); - $map->delete( $activeCharacter ); - } + /** + * @var $map Model\MapModel + */ + $map = Model\BasicModel::getNew('MapModel'); + $map->getById($mapData['id']); + $map->delete( $activeCharacter ); echo json_encode([]); } @@ -527,121 +516,116 @@ class Map extends Controller\AccessController { $return = (object) []; $return->error = []; - if($activeCharacter){ - $cacheKey = $this->getMapDataCacheKey($activeCharacter); - // if there is any system/connection change data submitted -> save new data - if( - !empty($mapData) || - !$f3->exists($cacheKey) - ){ - // get current map data =============================================================================== - $maps = $activeCharacter->getMaps(); + $cacheKey = $this->getMapDataCacheKey($activeCharacter); - // loop all submitted map data that should be saved - // -> currently there will only be ONE map data change submitted -> single loop - foreach($mapData as $data){ + // if there is any system/connection change data submitted -> save new data + if( + !empty($mapData) || + !$f3->exists($cacheKey) + ){ + // get current map data =============================================================================== + $maps = $activeCharacter->getMaps(); - $systems = []; - $connections = []; + // loop all submitted map data that should be saved + // -> currently there will only be ONE map data change submitted -> single loop + foreach($mapData as $data){ - // check whether system data and/or connection data is send - // empty arrays are not included in ajax requests - if( isset($data['data']['systems']) ){ - $systems = (array)$data['data']['systems']; - } + $systems = []; + $connections = []; - if( isset($data['data']['connections']) ){ - $connections = (array)$data['data']['connections']; - } + // check whether system data and/or connection data is send + // empty arrays are not included in ajax requests + if( isset($data['data']['systems']) ){ + $systems = (array)$data['data']['systems']; + } - // check if system data or connection data is send - if( - count($systems) > 0 || - count($connections) > 0 - ){ + if( isset($data['data']['connections']) ){ + $connections = (array)$data['data']['connections']; + } - // map changes expected ======================================================================= + // check if system data or connection data is send + if( + count($systems) > 0 || + count($connections) > 0 + ){ - // loop current user maps and check for changes - foreach($maps as $map){ + // map changes expected ======================================================================= - // update system data --------------------------------------------------------------------- - foreach($systems as $i => $systemData){ + // loop current user maps and check for changes + foreach($maps as $map){ - // check if current system belongs to the current map - $map->filter('systems', ['id = ?', $systemData['id'] ]); - $filteredMap = $map->find( - ['id = ?', $map->id ], - ['limit' => 1] - ); + // update system data --------------------------------------------------------------------- + foreach($systems as $i => $systemData){ - // this should never fail - if(is_object($filteredMap)){ - $filteredMap = $filteredMap->current(); + // check if current system belongs to the current map + $map->filter('systems', ['id = ?', $systemData['id'] ]); + $filteredMap = $map->find( + ['id = ?', $map->id ], + ['limit' => 1] + ); - // system belongs to the current map - if(is_object($filteredMap->systems)){ - // update - unset($systemData['updated']); - $system = $filteredMap->systems->current(); - $system->setData($systemData); - $system->updatedCharacterId = $activeCharacter; - $system->save(); + // this should never fail + if(is_object($filteredMap)){ + $filteredMap = $filteredMap->current(); - // a system belongs to ONE map -> speed up for multiple maps - unset($systemData[$i]); - } + // system belongs to the current map + if(is_object($filteredMap->systems)){ + // update + unset($systemData['updated']); + $system = $filteredMap->systems->current(); + $system->setData($systemData); + $system->updatedCharacterId = $activeCharacter; + $system->save(); + + // a system belongs to ONE map -> speed up for multiple maps + unset($systemData[$i]); } } + } - // update connection data ----------------------------------------------------------------- - foreach($connections as $i => $connectionData){ + // update connection data ----------------------------------------------------------------- + foreach($connections as $i => $connectionData){ - // check if the current connection belongs to the current map - $map->filter('connections', ['id = ?', $connectionData['id'] ]); - $filteredMap = $map->find( - ['id = ?', $map->id ], - ['limit' => 1] - ); + // check if the current connection belongs to the current map + $map->filter('connections', ['id = ?', $connectionData['id'] ]); + $filteredMap = $map->find( + ['id = ?', $map->id ], + ['limit' => 1] + ); - // this should never fail - if(is_object($filteredMap)){ - $filteredMap = $filteredMap->current(); + // this should never fail + if(is_object($filteredMap)){ + $filteredMap = $filteredMap->current(); - // connection belongs to the current map - if(is_object($filteredMap->connections)){ - // update - unset($connectionData['updated']); - $connection = $filteredMap->connections->current(); - $connection->setData($connectionData); - $connection->save(); + // connection belongs to the current map + if(is_object($filteredMap->connections)){ + // update + unset($connectionData['updated']); + $connection = $filteredMap->connections->current(); + $connection->setData($connectionData); + $connection->save(); - // a connection belongs to ONE map -> speed up for multiple maps - unset($connectionData[$i]); - } + // a connection belongs to ONE map -> speed up for multiple maps + unset($connectionData[$i]); } } } } } - - // format map Data for return - $return->mapData = self::getFormattedMapData($maps); - - // cache time(s) per user should be equal or less than this function is called - // prevent request flooding - $responseTTL = (int)$f3->get('PATHFINDER.TIMER.UPDATE_SERVER_MAP.DELAY') / 1000; - - $f3->set($cacheKey, $return, $responseTTL); - }else{ - // get from cache - $return = $f3->get($cacheKey); } + // format map Data for return + $return->mapData = self::getFormattedMapData($maps); + + // cache time(s) per user should be equal or less than this function is called + // prevent request flooding + $responseTTL = (int)$f3->get('PATHFINDER.TIMER.UPDATE_SERVER_MAP.DELAY') / 1000; + + $f3->set($cacheKey, $return, $responseTTL); }else{ - // user logged off - $return->error[] = $this->getLogoutError(); + // get from cache + $return = $f3->get($cacheKey); } echo json_encode( $return ); @@ -680,71 +664,68 @@ class Map extends Controller\AccessController { $return = (object) []; $return->error = []; - if( $activeCharacter = $this->getCharacter(0) ){ - $postData = $f3->get('POST'); - if( !empty($mapIds = (array)$postData['mapIds']) ){ - // IMPORTANT for now -> just update a single map (save performance) - $mapId = (int)reset($mapIds); - // get map and check map access - $map = $activeCharacter->getMap( (int)$mapId); + $activeCharacter = $this->getCharacter(0); - if( !is_null($map) ){ - $characterMapData = (array)$postData['characterMapData']; + $postData = $f3->get('POST'); + if( !empty($mapIds = (array)$postData['mapIds']) ){ + // IMPORTANT for now -> just update a single map (save performance) + $mapId = (int)reset($mapIds); + // get map and check map access + $map = $activeCharacter->getMap( (int)$mapId); - // check if data for specific system is requested - $systemData = (array)$postData['systemData']; - // if data is requested extend the cache key in order to get new data - $requestSystemData = (object) []; - $requestSystemData->mapId = isset($systemData['mapId']) ? (int) $systemData['mapId'] : 0; - $requestSystemData->systemId = isset($systemData['systemData']['id']) ? (int) $systemData['systemData']['id'] : 0; + if( !is_null($map) ){ + $characterMapData = (array)$postData['characterMapData']; - // update current location - // -> suppress temporary timeout errors - $activeCharacter = $activeCharacter->updateLog(['suppressTimeoutErrors' => true]); + // check if data for specific system is requested + $systemData = (array)$postData['systemData']; + // if data is requested extend the cache key in order to get new data + $requestSystemData = (object) []; + $requestSystemData->mapId = isset($systemData['mapId']) ? (int) $systemData['mapId'] : 0; + $requestSystemData->systemId = isset($systemData['systemData']['id']) ? (int) $systemData['systemData']['id'] : 0; - // check character log (current system) and manipulate map (e.g. add new system) - if( (bool)$characterMapData['mapTracking'] ){ - $map = $this->updateMapData($activeCharacter, $map); - } + // update current location + // -> suppress temporary timeout errors + $activeCharacter = $activeCharacter->updateLog(['suppressTimeoutErrors' => true]); - $cacheKey = $this->getUserDataCacheKey($mapId, $requestSystemData->systemId); - if( !$f3->exists($cacheKey) ){ - $return->mapUserData[] = $map->getUserData(); + // check character log (current system) and manipulate map (e.g. add new system) + if( (bool)$characterMapData['mapTracking'] ){ + $map = $this->updateMapData($activeCharacter, $map); + } - // request signature data for a system if user has map access! - if( $mapId === $requestSystemData->mapId ){ - $system = $map->getSystemById( $requestSystemData->systemId ); + $cacheKey = $this->getUserDataCacheKey($mapId, $requestSystemData->systemId); + if( !$f3->exists($cacheKey) ){ + $return->mapUserData[] = $map->getUserData(); - if( !is_null($system) ){ - // data for currently selected system - $return->system = $system->getData(); - $return->system->signatures = $system->getSignaturesData(); - } + // request signature data for a system if user has map access! + if( $mapId === $requestSystemData->mapId ){ + $system = $map->getSystemById( $requestSystemData->systemId ); + + if( !is_null($system) ){ + // data for currently selected system + $return->system = $system->getData(); + $return->system->signatures = $system->getSignaturesData(); } - - // cache time (seconds) should be equal or less than request trigger time - // prevent request flooding - $responseTTL = (int)$f3->get('PATHFINDER.TIMER.UPDATE_SERVER_USER_DATA.DELAY') / 1000; - - // cache response - $f3->set($cacheKey, $return, $responseTTL); - }else{ - // get from cache - // this should happen if a user has multiple program instances running - // with the same main char - $return = $f3->get($cacheKey); } + + // cache time (seconds) should be equal or less than request trigger time + // prevent request flooding + $responseTTL = (int)$f3->get('PATHFINDER.TIMER.UPDATE_SERVER_USER_DATA.DELAY') / 1000; + + // cache response + $f3->set($cacheKey, $return, $responseTTL); + }else{ + // get from cache + // this should happen if a user has multiple program instances running + // with the same main char + $return = $f3->get($cacheKey); } } - - // get current user data -> this should not be cached because each user has different personal data - // even if they have multiple characters using the same map! - $return->userData = $activeCharacter->getUser()->getData(); - }else{ - // user logged off - $return->error[] = $this->getLogoutError(); } + // get current user data -> this should not be cached because each user has different personal data + // even if they have multiple characters using the same map! + $return->userData = $activeCharacter->getUser()->getData(); + echo json_encode( $return ); } @@ -755,7 +736,7 @@ class Map extends Controller\AccessController { * @param Model\MapModel $map * @return Model\MapModel */ - protected function updateMapData($character, $map){ + protected function updateMapData(Model\CharacterModel $character, Model\MapModel $map){ // update "map data" cache in case of map (system/connection) changes $clearMapDataCache = false; diff --git a/app/main/controller/api/route.php b/app/main/controller/api/route.php index 4d0ec012..474e1c58 100644 --- a/app/main/controller/api/route.php +++ b/app/main/controller/api/route.php @@ -531,10 +531,7 @@ class Route extends \Controller\AccessController { $return->error = []; $return->routesData = []; - if( - $activeCharacter && - !empty($requestData['routeData']) - ){ + if( !empty($requestData['routeData']) ){ $routesData = (array)$requestData['routeData']; diff --git a/app/main/controller/api/signature.php b/app/main/controller/api/signature.php index 4aaf769e..46386c12 100644 --- a/app/main/controller/api/signature.php +++ b/app/main/controller/api/signature.php @@ -35,22 +35,20 @@ class Signature extends Controller\AccessController{ if( !empty($systemIds) ){ $activeCharacter = $this->getCharacter(); - if($activeCharacter){ - /** - * @var Model\SystemModel $system - */ - $system = Model\BasicModel::getNew('SystemModel'); - foreach($systemIds as $systemId){ - $system->getById($systemId); - if( - !$system->dry() && - $system->hasAccess($activeCharacter) - ){ - $signatureData = $system->getSignaturesData(); - } - - $system->reset(); + /** + * @var Model\SystemModel $system + */ + $system = Model\BasicModel::getNew('SystemModel'); + foreach($systemIds as $systemId){ + $system->getById($systemId); + if( + !$system->dry() && + $system->hasAccess($activeCharacter) + ){ + $signatureData = $system->getSignaturesData(); } + + $system->reset(); } } @@ -86,141 +84,138 @@ class Signature extends Controller\AccessController{ if( !is_null($signatureData) ){ $activeCharacter = $this->getCharacter(); - if($activeCharacter){ - // signature ids that were updated/created - $updatedSignatureIds = []; + // signature ids that were updated/created + $updatedSignatureIds = []; - /** - * @var Model\SystemModel $system - */ - $system = Model\BasicModel::getNew('SystemModel'); + /** + * @var Model\SystemModel $system + */ + $system = Model\BasicModel::getNew('SystemModel'); - // update/add all submitted signatures - foreach($signatureData as $data){ - // this key should not be saved (it is an obj) - unset($data['updated']); + // update/add all submitted signatures + foreach($signatureData as $data){ + // this key should not be saved (it is an obj) + unset($data['updated']); - $system->getById( (int)$data['systemId']); + $system->getById( (int)$data['systemId']); - if( !$system->dry() ){ - // update/save signature + if( !$system->dry() ){ + // update/save signature - /** - * @var $signature Model\SystemSignatureModel - */ - $signature = null; - if( isset($data['pk']) ){ - // try to get system by "primary key" - $signature = $system->getSignatureById($activeCharacter, (int)$data['pk']); - }elseif( isset($data['name']) ){ - $signature = $system->getSignatureByName($activeCharacter, $data['name']); - } + /** + * @var $signature Model\SystemSignatureModel + */ + $signature = null; + if( isset($data['pk']) ){ + // try to get system by "primary key" + $signature = $system->getSignatureById($activeCharacter, (int)$data['pk']); + }elseif( isset($data['name']) ){ + $signature = $system->getSignatureByName($activeCharacter, $data['name']); + } - if( is_null($signature) ){ - $signature = Model\BasicModel::getNew('SystemSignatureModel'); - } + if( is_null($signature) ){ + $signature = Model\BasicModel::getNew('SystemSignatureModel'); + } + + if($signature->dry()){ + // new signature + $signature->systemId = $system; + $signature->updatedCharacterId = $activeCharacter; + $signature->createdCharacterId = $activeCharacter; + $signature->setData($data); + }else{ + // update signature + + if( + isset($data['name']) && + isset($data['value']) + ){ + // update single key => value pair + $newData = [ + $data['name'] => $data['value'] + ]; + + // if groupID changed -> typeID set to 0 + if($data['name'] == 'groupId'){ + $newData['typeId'] = 0; + } - if($signature->dry()){ - // new signature - $signature->systemId = $system; - $signature->updatedCharacterId = $activeCharacter; - $signature->createdCharacterId = $activeCharacter; - $signature->setData($data); }else{ - // update signature + // update complete signature (signature reader dialog) + // systemId should not be updated + unset( $data['systemId'] ); + + // description should not be updated + unset( $data['description'] ); + + // prevent some data from overwrite manually changes + // wormhole typeID can not figured out/saved by the sig reader dialog + // -> type could not be identified -> do not overwrite them (e.g. sig update) if( - isset($data['name']) && - isset($data['value']) + $data['groupId'] == 5 || + $data['typeId'] == 0 ){ - // update single key => value pair - $newData = [ - $data['name'] => $data['value'] - ]; - - // if groupID changed -> typeID set to 0 - if($data['name'] == 'groupId'){ - $newData['typeId'] = 0; - } - - }else{ - // update complete signature (signature reader dialog) - - // systemId should not be updated - unset( $data['systemId'] ); - - // description should not be updated - unset( $data['description'] ); - - // prevent some data from overwrite manually changes - // wormhole typeID can not figured out/saved by the sig reader dialog - // -> type could not be identified -> do not overwrite them (e.g. sig update) - if( - $data['groupId'] == 5 || - $data['typeId'] == 0 - ){ - unset( $data['typeId'] ); - } - - // "sig reader" should not overwrite signature group information - if( - $data['groupId'] == 0 && - $signature->groupId > 0 - ){ - unset($data['groupId']); - } - - $newData = $data; + unset( $data['typeId'] ); } - if( $signature->hasChanged($newData) ){ - // Character should only be changed if something else has changed - $signature->updatedCharacterId = $activeCharacter; - $signature->setData($newData); + // "sig reader" should not overwrite signature group information + if( + $data['groupId'] == 0 && + $signature->groupId > 0 + ){ + unset($data['groupId']); } + + $newData = $data; } - $signature->save(); - $updatedSignatureIds[] = $signature->id; - - // get a fresh signature object with the new data. This is a bad work around! - // but i could not figure out what the problem was when using the signature model, saved above :( - // -> some caching problems - /** - * @var $newSignature Model\SystemSignatureModel - */ - $newSignature = Model\BasicModel::getNew('SystemSignatureModel'); - $newSignature->getById( $signature->id, 0); - - $return->signatures[] = $newSignature->getData(); - - $signature->reset(); + if( $signature->hasChanged($newData) ){ + // Character should only be changed if something else has changed + $signature->updatedCharacterId = $activeCharacter; + $signature->setData($newData); + } } - $system->reset(); + $signature->save(); + $updatedSignatureIds[] = $signature->id; + + // get a fresh signature object with the new data. This is a bad work around! + // but i could not figure out what the problem was when using the signature model, saved above :( + // -> some caching problems + /** + * @var $newSignature Model\SystemSignatureModel + */ + $newSignature = Model\BasicModel::getNew('SystemSignatureModel'); + $newSignature->getById( $signature->id, 0); + + $return->signatures[] = $newSignature->getData(); + + $signature->reset(); } - // delete "old" signatures ------------------------------------------------------------------ + $system->reset(); + } + + // delete "old" signatures ------------------------------------------------------------------ + if( + $deleteOldSignatures && + $systemId + ){ + $system->getById($systemId); if( - $deleteOldSignatures && - $systemId + !$system->dry() && + $system->hasAccess($activeCharacter) ){ - $system->getById($systemId); - if( - !$system->dry() && - $system->hasAccess($activeCharacter) - ){ - $allSignatures = $system->getSignatures(); - foreach($allSignatures as $tempSignature){ - if( !in_array($tempSignature->id, $updatedSignatureIds)){ - $tempSignature->delete( $activeCharacter ); - } + $allSignatures = $system->getSignatures(); + foreach($allSignatures as $tempSignature){ + if( !in_array($tempSignature->id, $updatedSignatureIds)){ + $tempSignature->delete( $activeCharacter ); } } - - } + } } diff --git a/app/main/controller/api/system.php b/app/main/controller/api/system.php index 82cc9e21..728771b8 100644 --- a/app/main/controller/api/system.php +++ b/app/main/controller/api/system.php @@ -201,47 +201,45 @@ class System extends \Controller\AccessController { ){ $activeCharacter = $this->getCharacter(); - if($activeCharacter){ - $systemData = (array)$postData['systemData']; - $mapData = (array)$postData['mapData']; + $systemData = (array)$postData['systemData']; + $mapData = (array)$postData['mapData']; - if( isset($systemData['id']) ){ - // update existing system (e.g. changed system description) ------------------- + if( isset($systemData['id']) ){ + // update existing system (e.g. changed system description) ------------------- - /** - * @var $system Model\SystemModel - */ - $system = Model\BasicModel::getNew('SystemModel'); - $system->getById($systemData['id']); - if( !$system->dry() ){ - if( $system->hasAccess($activeCharacter) ){ - // system model found - $systemModel = $system; - } + /** + * @var $system Model\SystemModel + */ + $system = Model\BasicModel::getNew('SystemModel'); + $system->getById($systemData['id']); + if( !$system->dry() ){ + if( $system->hasAccess($activeCharacter) ){ + // system model found + $systemModel = $system; } - }elseif( isset($mapData['id']) ){ - // save NEW system ------------------------------------------------------------ + } + }elseif( isset($mapData['id']) ){ + // save NEW system ------------------------------------------------------------ - /** - * @var $map Model\MapModel - */ - $map = Model\BasicModel::getNew('MapModel'); - $map->getById($mapData['id']); - if( - !$map->dry() && - $map->hasAccess($activeCharacter) - ){ - // make sure system is not already on map - // --> (e.g. multiple simultaneously save() calls for the same system) - if( is_null( $systemModel = $map->getSystemByCCPId($systemData['systemId']) ) ){ - // system not found on map -> get static system data (CCP DB) - $systemModel = $map->getNewSystem($systemData['systemId']); - $systemModel->createdCharacterId = $activeCharacter; - } - - // map is not changeable for a system! (security) - $systemData['mapId'] = $map; + /** + * @var $map Model\MapModel + */ + $map = Model\BasicModel::getNew('MapModel'); + $map->getById($mapData['id']); + if( + !$map->dry() && + $map->hasAccess($activeCharacter) + ){ + // make sure system is not already on map + // --> (e.g. multiple simultaneously save() calls for the same system) + if( is_null( $systemModel = $map->getSystemByCCPId($systemData['systemId']) ) ){ + // system not found on map -> get static system data (CCP DB) + $systemModel = $map->getNewSystem($systemData['systemId']); + $systemModel->createdCharacterId = $activeCharacter; } + + // map is not changeable for a system! (security) + $systemData['mapId'] = $map; } } } @@ -318,27 +316,25 @@ class System extends \Controller\AccessController { $return->error = []; $return->systemData = []; - if( $activeCharacter = $this->getCharacter() ){ - $constellationId = 0; + $constellationId = 0; - // check for search parameter - if( isset($params['arg1']) ){ - $constellationId = (int)$params['arg1']; - } - $cacheKey = 'CACHE_CONSTELLATION_SYSTEMS_' . self::formatHiveKey($constellationId); + // check for search parameter + if( isset($params['arg1']) ){ + $constellationId = (int)$params['arg1']; + } + $cacheKey = 'CACHE_CONSTELLATION_SYSTEMS_' . self::formatHiveKey($constellationId); - if($f3->exists($cacheKey)){ - $return->systemData = $f3->get($cacheKey); - }else{ - if($constellationId > 0){ - $systemModels = $this->getSystemModelByIds([$constellationId], 'constellationID'); + if($f3->exists($cacheKey)){ + $return->systemData = $f3->get($cacheKey); + }else{ + if($constellationId > 0){ + $systemModels = $this->getSystemModelByIds([$constellationId], 'constellationID'); - foreach($systemModels as $systemModel){ - $return->systemData[] = $systemModel->getData(); - } - - $f3->set($cacheKey, $return->systemData, $f3->get('PATHFINDER.CACHE.CONSTELLATION_SYSTEMS') ); + foreach($systemModels as $systemModel){ + $return->systemData[] = $systemModel->getData(); } + + $f3->set($cacheKey, $return->systemData, $f3->get('PATHFINDER.CACHE.CONSTELLATION_SYSTEMS') ); } } @@ -356,10 +352,9 @@ class System extends \Controller\AccessController { $return->error = []; $return->systemData = []; - if( - ($activeCharacter = $this->getCharacter()) && - !empty($postData['systemData']) - ){ + if( !empty($postData['systemData'] )){ + $activeCharacter = $this->getCharacter(); + $return->clearOtherWaypoints = (bool)$postData['clearOtherWaypoints']; $return->first = (bool)$postData['first']; @@ -390,17 +385,16 @@ class System extends \Controller\AccessController { */ public function delete(\Base $f3){ $systemIds = (array)$f3->get('POST.systemIds'); + $activeCharacter = $this->getCharacter(); - if($activeCharacter = $this->getCharacter()){ - /** - * @var Model\SystemModel $system - */ - $system = Model\BasicModel::getNew('SystemModel'); - foreach((array)$systemIds as $systemId){ - $system->getById($systemId); - $system->delete($activeCharacter); - $system->reset(); - } + /** + * @var Model\SystemModel $system + */ + $system = Model\BasicModel::getNew('SystemModel'); + foreach((array)$systemIds as $systemId){ + $system->getById($systemId); + $system->delete($activeCharacter); + $system->reset(); } echo json_encode([]); diff --git a/app/main/controller/api/user.php b/app/main/controller/api/user.php index ac219e10..74e05bc4 100644 --- a/app/main/controller/api/user.php +++ b/app/main/controller/api/user.php @@ -70,7 +70,7 @@ class User extends Controller\Controller{ $characterModel->save(); // write login log -------------------------------------- - self::getLogger( $this->f3->get('PATHFINDER.LOGFILES.LOGIN') )->write( + self::getLogger('LOGIN')->write( sprintf(self::LOG_LOGGED_IN, $user->_id, $user->name, @@ -162,8 +162,7 @@ class User extends Controller\Controller{ * @param \Base $f3 */ public function deleteLog(\Base $f3){ - $activeCharacter = $this->getCharacter(); - if($activeCharacter){ + if($activeCharacter = $this->getCharacter()){ if($characterLog = $activeCharacter->getLog()){ $characterLog->erase(); } @@ -177,6 +176,10 @@ class User extends Controller\Controller{ public function logout(\Base $f3){ $this->deleteLog($f3); parent::logout($f3); + + $return = (object) []; + $return->reroute = rtrim(self::getEnvironmentData('URL'), '/') . $f3->alias('login'); + echo json_encode($return); } /** @@ -338,7 +341,7 @@ class User extends Controller\Controller{ if($status){ // save log $logText = "id: %s, name: %s, ip: %s"; - self::getLogger( $this->f3->get('PATHFINDER.LOGFILES.DELETE_ACCOUNT') )->write( + self::getLogger('DELETE_ACCOUNT')->write( sprintf($logText, $user->id, $user->name, $f3->get('IP')) ); diff --git a/app/main/controller/ccp/sso.php b/app/main/controller/ccp/sso.php index 070cabb3..f554677e 100644 --- a/app/main/controller/ccp/sso.php +++ b/app/main/controller/ccp/sso.php @@ -903,6 +903,6 @@ class Sso extends Api\User{ * @return \Log */ static function getCrestLogger(){ - return parent::getLogger('crest'); + return parent::getLogger('CREST'); } } \ No newline at end of file diff --git a/app/main/controller/controller.php b/app/main/controller/controller.php index e2175c8c..9ba32888 100644 --- a/app/main/controller/controller.php +++ b/app/main/controller/controller.php @@ -19,6 +19,8 @@ class Controller { const COOKIE_NAME_STATE = 'cookie'; const COOKIE_PREFIX_CHARACTER = 'char'; + const LOG_UNAUTHORIZED = 'IP: [%-20s] Agent: [%s]'; + const ERROR_SESSION_SUSPECT = 'Suspect id: [%30s], ip: [%40s], new ip: [%40s], User-Agent: %s '; /** * @var \Base @@ -120,18 +122,13 @@ class Controller { $f3 = $this->getF3(); if( ($ip = $session->ip() )!= $f3->get('IP') ){ // IP address changed -> not critical - $sessionSuspectLogFile = 'PATHFINDER.LOGFILES.SESSION_SUSPECT'; - if( !$f3->devoid($sessionSuspectLogFile) ){ - $this->getLogger( - $f3->get($sessionSuspectLogFile) - )->write( sprintf( - self::ERROR_SESSION_SUSPECT, - $sid, - $session->ip(), - $f3->get('IP'), - $f3->get('AGENT') - )); - } + self::getLogger('SESSION_SUSPECT')->write( sprintf( + self::ERROR_SESSION_SUSPECT, + $sid, + $session->ip(), + $f3->get('IP'), + $f3->get('AGENT') + )); // no more error handling here return true; }elseif($session->agent() != $f3->get('AGENT') ){ @@ -398,24 +395,6 @@ class Controller { // destroy session login data ------------------------------- $f3->clear('SESSION'); - - if( $f3->get('AJAX') ){ - $return = (object) []; - if( - isset($params['reroute']) && - (bool)$params['reroute'] - ){ - $return->reroute = rtrim(self::getEnvironmentData('URL'), '/') . $f3->alias('login'); - }else{ - // no reroute -> errors can be shown - $return->error[] = $this->getLogoutError(); - } - - echo json_encode($return); - }else{ - // redirect to landing page - $f3->reroute('@login'); - } } /** @@ -556,11 +535,51 @@ class Controller { /** * Callback for framework "unload" - * check -> config.ini + * -> this function is called on each request! + * -> configured in config.ini * @param \Base $f3 * @return bool */ public function unload(\Base $f3){ + // track some 4xx Client side errors + // 5xx errors are handled in "ONERROR" callback + $status = http_response_code(); + $halt = false; + + switch( $status ){ + case 403: // Unauthorized + self::getLogger('UNAUTHORIZED')->write(sprintf( + self::LOG_UNAUTHORIZED, + $f3->get('IP'), + $f3->get('AGENT') + )); + $halt = true; + break; + } + + // Ajax + if( + $halt && + $f3->get('AJAX') + ){ + $params = (array)$f3->get('POST'); + $response = (object) []; + $response->type = 'error'; + $response->code = $status; + $response->message = 'Access denied: User not found'; + + $return = (object) []; + if( (bool)$params['reroute']){ + $return->reroute = rtrim(self::getEnvironmentData('URL'), '/') . $f3->alias('login'); + }else{ + // no reroute -> errors can be shown + $return->error[] = $response; + } + + echo json_encode($return); + die(); + } + return true; } @@ -680,12 +699,13 @@ class Controller { } /** - * get a log controller e.g. "debug" - * @param string $loggerType - * @return \Log + * get a Logger object by Hive key + * -> set in pathfinder.ini + * @param string $type + * @return \Log|null */ - static function getLogger($loggerType){ - return LogController::getLogger($loggerType); + static function getLogger($type){ + return LogController::getLogger($type); } /** diff --git a/app/main/controller/logcontroller.php b/app/main/controller/logcontroller.php index daade6cc..3b919ad5 100644 --- a/app/main/controller/logcontroller.php +++ b/app/main/controller/logcontroller.php @@ -11,28 +11,22 @@ namespace controller; class LogController extends Controller { + /** - * get an singleton instance for a logger instance - * @param $logFileName - * @return mixed + * get Logger instance + * @param string $type + * @return \Log|null */ - public static function getLogger($logFileName){ - + public static function getLogger($type){ $f3 = \Base::instance(); - - $hiveKey = 'LOGGER' . $logFileName; - - // check if log controller already exists - if( !$f3->exists($hiveKey) ){ - // create new logger instance - - $logFile = $logFileName . '.log'; - - $f3->set($hiveKey, new \Log($logFile)); + $logFiles = $f3->get('PATHFINDER.LOGFILES'); + $logger = null; + if( !empty($logFiles[$type]) ){ + $logFile = $logFiles[$type] . '.log'; + $logger = new \Log($logFile); } - return $f3->get($hiveKey); + return $logger; } - } \ No newline at end of file diff --git a/app/main/cron/cache.php b/app/main/cron/cache.php index 461111de..a635fb8d 100644 --- a/app/main/cron/cache.php +++ b/app/main/cron/cache.php @@ -9,7 +9,6 @@ namespace cron; use data\filesystem\Search; -use Controller; class Cache { @@ -53,7 +52,7 @@ class Cache { $execTime = microtime(true) - $time_start; // Log ------------------------ - $log = Controller\LogController::getLogger('cron_' . __FUNCTION__); + $log = new \Log('cron_' . __FUNCTION__ . '.log'); $log->write( sprintf(self::LOG_TEXT, __FUNCTION__, $deletedFiles, $deletedSize, $notWritableFiles, $deleteErrors, $execTime) ); } diff --git a/app/main/cron/ccpsystemsupdate.php b/app/main/cron/ccpsystemsupdate.php index 29051583..d6177d60 100644 --- a/app/main/cron/ccpsystemsupdate.php +++ b/app/main/cron/ccpsystemsupdate.php @@ -214,7 +214,7 @@ class CcpSystemsUpdate { $execTimeUpdateTables = $time_end - $time_start; // Log ------------------------ - $log = Controller\LogController::getLogger('cron_' . __FUNCTION__); + $log = new \Log('cron_' . __FUNCTION__ . '.log'); $log->write( sprintf(self::LOG_TEXT, __FUNCTION__, $execTimePrepareSystemLogTables, $execTimeGetJumpData, $execTimeGetKillData, $execTimeUpdateTables) ); } } \ No newline at end of file diff --git a/app/main/cron/mapupdate.php b/app/main/cron/mapupdate.php index aeae6e1b..d2faee0e 100644 --- a/app/main/cron/mapupdate.php +++ b/app/main/cron/mapupdate.php @@ -7,7 +7,6 @@ */ namespace cron; -use Controller; use DB; class MapUpdate { @@ -39,7 +38,7 @@ class MapUpdate { $deactivatedMapsCount = $pfDB->count(); // Log ------------------------ - $log = Controller\LogController::getLogger('cron_' . __FUNCTION__); + $log = new \Log('cron_' . __FUNCTION__ . '.log'); $log->write( sprintf(self::LOG_TEXT_MAPS, __FUNCTION__, $deactivatedMapsCount) ); } @@ -63,7 +62,7 @@ class MapUpdate { $deletedMapsCount = $pfDB->count(); // Log ------------------------ - $log = Controller\LogController::getLogger('cron_' . __FUNCTION__); + $log = new \Log('cron_' . __FUNCTION__ . '.log'); $log->write( sprintf(self::LOG_TEXT_MAPS, __FUNCTION__, $deletedMapsCount) ); } diff --git a/app/main/db/database.php b/app/main/db/database.php index bd4ac98a..c9b8f9ae 100644 --- a/app/main/db/database.php +++ b/app/main/db/database.php @@ -117,7 +117,7 @@ class Database extends \Prefab { }catch(\PDOException $e){ // DB connection error // -> log it - LogController::getLogger('error')->write($e->getMessage()); + LogController::getLogger('ERROR')->write($e->getMessage()); } return $db; diff --git a/app/main/lib/web.php b/app/main/lib/web.php index 99871ac8..2979569b 100644 --- a/app/main/lib/web.php +++ b/app/main/lib/web.php @@ -134,7 +134,7 @@ class Web extends \Web { $url, json_decode($result['body']) ); - LogController::getLogger('error')->write($errorMsg); + LogController::getLogger('ERROR')->write($errorMsg); break; case 500: case 501: @@ -150,7 +150,7 @@ class Web extends \Web { $url, json_decode($result['body']) ); - LogController::getLogger('error')->write($errorMsg); + LogController::getLogger('ERROR')->write($errorMsg); // trigger error $f3->error($statusCode, $errorMsg); @@ -172,7 +172,7 @@ class Web extends \Web { ); // log error - LogController::getLogger('error')->write($errorMsg); + LogController::getLogger('ERROR')->write($errorMsg); if($additionalOptions['suppressTimeoutErrors'] !== true){ // trigger error @@ -188,7 +188,7 @@ class Web extends \Web { $url ); - LogController::getLogger('error')->write($errorMsg); + LogController::getLogger('ERROR')->write($errorMsg); break; } diff --git a/app/main/model/basicmodel.php b/app/main/model/basicmodel.php index 747d29a0..d1a4fbc5 100644 --- a/app/main/model/basicmodel.php +++ b/app/main/model/basicmodel.php @@ -619,12 +619,12 @@ abstract class BasicModel extends \DB\Cortex { /** * debug log function - * @param $text - * @param string $logFile + * @param string $text + * @param string $type */ - public static function log($text, $logFile = null){ - $logFile = isset($logFile) ? $logFile : self::getF3()->get('PATHFINDER.LOGFILES.DEBUG'); - Controller\LogController::getLogger($logFile)->write($text); + public static function log($text, $type = null){ + $type = isset($type) ? $type : 'DEBUG'; + Controller\LogController::getLogger($type)->write($text); } /** diff --git a/app/main/model/charactermodel.php b/app/main/model/charactermodel.php index 088ffff9..640a6ef5 100644 --- a/app/main/model/charactermodel.php +++ b/app/main/model/charactermodel.php @@ -513,6 +513,10 @@ class CharacterModel extends BasicModel { return $maps; } + /** + * character logout + * -> clear authentication data + */ public function logout(){ if($this->characterAuthentications){ foreach($this->characterAuthentications as $characterAuthentication){ diff --git a/app/pathfinder.ini b/app/pathfinder.ini index f96e3163..85ac161d 100644 --- a/app/pathfinder.ini +++ b/app/pathfinder.ini @@ -101,14 +101,20 @@ EXPIRE_MAX = 5184000 ; LOGGING ========================================================================================= [PATHFINDER.LOGFILES] -; just for manuel debug during development -DEBUG = debug +; error log +ERROR = error +; CREST error log +CREST = crest ; login information LOGIN = login ; session warnings (suspect) SESSION_SUSPECT = session_suspect ; account deleted DELETE_ACCOUNT = account_delete +; unauthorized request (HTTP 401) +UNAUTHORIZED = unauthorized +; debug log for development +DEBUG = debug ; API ============================================================================================= [PATHFINDER.API] diff --git a/js/app.js b/js/app.js index 31d2f32b..f98376d4 100644 --- a/js/app.js +++ b/js/app.js @@ -4,7 +4,7 @@ var mainScriptPath = document.body.getAttribute('data-script'); // js baseURL. Depends on the environment. // e.g. use raw files (develop) or build files (production) var jsBaseUrl = document.body.getAttribute('data-js-path'); -6 + // requireJs configuration requirejs.config({ baseUrl: 'js', // path for baseUrl - dynamically set !below! ("build_js" | "js") @@ -21,7 +21,7 @@ requirejs.config({ mappage: './app/mappage', // initial start "map page" view setup: './app/setup', // initial start "setup page" view - jquery: 'lib/jquery-1.11.3.min', // v1.11.3 jQuery + jquery: 'lib/jquery-3.0.0.min', // v3.0.0 jQuery bootstrap: 'lib/bootstrap.min', // v3.3.0 Bootstrap js code - http://getbootstrap.com/javascript text: 'lib/requirejs/text', // v2.0.12 A RequireJS/AMD loader plugin for loading text resources. mustache: 'lib/mustache.min', // v1.0.0 Javascript template engine - http://mustache.github.io @@ -32,11 +32,7 @@ requirejs.config({ jsPlumb: 'lib/dom.jsPlumb-1.7.6', // v1.7.6 jsPlumb (Vanilla)- main map draw plugin https://jsplumbtoolkit.com farahey: 'lib/farahey-0.5', // v0.5 jsPlumb "magnetizing" extension - https://github.com/jsplumb/farahey customScrollbar: 'lib/jquery.mCustomScrollbar.min', // v3.1.3 Custom scroll bars - http://manos.malihu.gr - datatables: 'lib/datatables/jquery.dataTables.min', // v1.10.7 DataTables - https://datatables.net - //datatablesBootstrap: 'lib/datatables/dataTables.bootstrap', // DataTables - not used (bootstrap style) - datatablesResponsive: 'lib/datatables/extensions/responsive/dataTables.responsive', // v1.0.6 TableTools (PlugIn) - https://datatables.net/extensions/responsive - - datatablesTableTools: 'lib/datatables/extensions/tabletools/js/dataTables.tableTools', // v2.2.3 TableTools (PlugIn) - https://datatables.net/extensions/tabletools + mousewheel: 'lib/jquery.mousewheel.min', // v3.1.13 Mousewheel - https://github.com/jquery/jquery-mousewheel xEditable: 'lib/bootstrap-editable.min', // v1.5.1 X-editable - in placed editing morris: 'lib/morris.min', // v0.5.1 Morris.js - graphs and charts raphael: 'lib/raphael-min', // v2.1.2 Raphaël - required for morris (dependency) @@ -59,6 +55,13 @@ requirejs.config({ easePack: 'lib/EasePack.min', tweenLite: 'lib/TweenLite.min', + // datatables // v1.10.12 DataTables - https://datatables.net + 'datatables.net': 'lib/datatables/DataTables-1.10.12/js/jquery.dataTables.min', + 'datatables.net-buttons': 'lib/datatables/Buttons-1.2.1/js/dataTables.buttons.min', + 'datatables.net-buttons-html': 'lib/datatables/Buttons-1.2.1/js/buttons.html5.min', + 'datatables.net-responsive': 'lib/datatables/Responsive-2.1.0/js/dataTables.responsive.min', + 'datatables.net-select': 'lib/datatables/Select-1.2.0/js/dataTables.select.min', + // notification plugin pnotify: 'lib/pnotify/pnotify.core', // v2.0.1 PNotify - notification core file 'pnotify.buttons': 'lib/pnotify/pnotify.buttons', // PNotify - buttons notification extension @@ -68,7 +71,6 @@ requirejs.config({ 'pnotify.history': 'lib/pnotify/pnotify.history', // PNotify - history push notification history extension 'pnotify.callbacks': 'lib/pnotify/pnotify.callbacks', // PNotify - callbacks push notification extension 'pnotify.reference': 'lib/pnotify/pnotify.reference' // PNotify - reference push notification extension - }, shim: { bootstrap: { @@ -87,19 +89,22 @@ requirejs.config({ deps: ['jquery'] }, customScrollbar: { + deps: ['jquery', 'mousewheel'] + }, + 'datatables.net': { deps: ['jquery'] }, - datatables: { - deps: ['jquery'] + 'datatables.net-buttons': { + deps: ['datatables.net'] }, - datatablesBootstrap: { - deps: ['datatables'] + 'datatables.net-buttons-html': { + deps: ['datatables.net-buttons'] }, - datatablesResponsive: { - deps: ['datatables'] + 'datatables.net-responsive': { + deps: ['datatables.net'] }, - datatablesTableTools: { - deps: ['datatables'] + 'datatables.net-select': { + deps: ['datatables.net'] }, xEditable: { deps: ['bootstrap'] diff --git a/js/app/logging.js b/js/app/logging.js index ad74c1b9..64280fde 100644 --- a/js/app/logging.js +++ b/js/app/logging.js @@ -20,7 +20,8 @@ define([ var config = { dialogDynamicAreaClass: 'pf-dynamic-area', // class for dynamic areas - logGraphClass: 'pf-log-graph' // class for all log Morris graphs + logGraphClass: 'pf-log-graph', // class for all log Morris graphs + tableToolsClass: 'pf-table-tools' // class for table tools }; /** @@ -68,15 +69,20 @@ define([ class: config.dialogDynamicAreaClass }); + var logTableActionBar = $('
', { + class: config.tableToolsClass + }); + logTableArea.append(logTableActionBar); + var logTable = $('', { class: ['compact', 'stripe', 'order-column', 'row-border'].join(' ') }); - logTableArea.append(logTable); + content.append(logTableArea); // init log table - logDataTable = logTable.DataTable( { + logDataTable = logTable.DataTable({ paging: true, ordering: true, order: [ 1, 'desc' ], @@ -248,24 +254,23 @@ define([ } // ------------------------------------------------------------------------------ - // add TableTool Buttons - var tt = new $.fn.DataTable.TableTools( logDataTable, { - sSwfPath: require.toUrl('') + 'lib/datatables/extensions/tabletools/swf/copy_csv_xls.swf', - aButtons: [ 'copy', 'csv', 'print' ] - }); + // add dataTable buttons (extension) - $(tt.fnContainer()).insertBefore('.bootbox-body div.dataTables_wrapper'); + var buttons = new $.fn.dataTable.Buttons( logDataTable, { + buttons: [ + { + extend: 'copy', + className: 'btn btn-sm btn-default', + text: ' copy' + },{ + extend: 'csv', + className: 'btn btn-sm btn-default', + text: ' csv' + } + ] + } ); - // add button icons - $('.DTTT_button_csv').prepend( $('', { - class: ['fa', 'fa-fw', 'fa-download'].join(' ') - })); - $('.DTTT_button_copy').prepend( $('', { - class: ['fa', 'fa-fw', 'fa-clipboard'].join(' ') - })); - $('.DTTT_button_print').prepend( $('', { - class: ['fa', 'fa-fw', 'fa-print'].join(' ') - })); + logDataTable.buttons().container().appendTo( $(this).find('.' + config.tableToolsClass)); }); diff --git a/js/app/module_map.js b/js/app/module_map.js index 135920a8..f14c8c59 100644 --- a/js/app/module_map.js +++ b/js/app/module_map.js @@ -9,8 +9,11 @@ define([ 'app/ui/system_signature', 'app/ui/system_route', 'app/ui/system_killboard', - 'datatablesTableTools', - 'datatablesResponsive' + 'datatables.net', + 'datatables.net-buttons', + 'datatables.net-buttons-html', + 'datatables.net-responsive', + 'datatables.net-select' ], function($, Init, Util, Map) { 'use strict'; diff --git a/js/app/page.js b/js/app/page.js index a71bd5bc..92f89220 100644 --- a/js/app/page.js +++ b/js/app/page.js @@ -582,7 +582,6 @@ define([ // logout Util.logout({ ajaxData: { - reroute: 1, clearCookies: clearCookies } }); @@ -629,7 +628,7 @@ define([ icon: 'fa-bolt', class: 'txt-color-danger', title: 'Shutdown', - headline: 'Emergency shutdown', + headline: 'Logged out', text: [ data.reason ], @@ -651,7 +650,7 @@ define([ $(document).setProgramStatus('offline'); - Util.showNotify({title: 'Emergency shutdown', text: data.reason, type: 'error'}, false); + Util.showNotify({title: 'Logged out', text: data.reason, type: 'error'}, false); // remove map ------------------------------------------------------- Util.getMapModule().velocity('fadeOut', { diff --git a/js/app/ui/system_killboard.js b/js/app/ui/system_killboard.js index 68a01e40..10384024 100644 --- a/js/app/ui/system_killboard.js +++ b/js/app/ui/system_killboard.js @@ -384,7 +384,7 @@ define([ showKillmails(moduleElement, cache.systemKillsGraphData[cacheKey]); killboardGraphElement.hideLoadingAnimation(); - }).error(function(e){ + }).fail(function(e){ labelOptions.type = 'label-danger'; label = getLabel( 'zKillboard is not responding', labelOptions ); diff --git a/js/lib/datatables/Buttons-1.2.1/js/buttons.html5.min.js b/js/lib/datatables/Buttons-1.2.1/js/buttons.html5.min.js new file mode 100644 index 00000000..3fc13418 --- /dev/null +++ b/js/lib/datatables/Buttons-1.2.1/js/buttons.html5.min.js @@ -0,0 +1,26 @@ +(function(g){"function"===typeof define&&define.amd?define(["jquery","datatables.net","datatables.net-buttons"],function(j){return g(j,window,document)}):"object"===typeof exports?module.exports=function(j,i,q,r){j||(j=window);if(!i||!i.fn.dataTable)i=require("datatables.net")(j,i).$;i.fn.dataTable.Buttons||require("datatables.net-buttons")(j,i);return g(i,j,j.document,q,r)}:g(jQuery,window,document)})(function(g,j,i,q,r,m){function E(a,b){v===m&&(v=-1===y.serializeToString(g.parseXML(F["xl/worksheets/sheet1.xml"])).indexOf("xmlns:r")); +g.each(b,function(b,c){if(g.isPlainObject(c)){var e=a.folder(b);E(e,c)}else{if(v){var e=c.childNodes[0],f,h,n=[];for(f=e.attributes.length-1;0<=f;f--){h=e.attributes[f].nodeName;var k=e.attributes[f].nodeValue;-1!==h.indexOf(":")&&(n.push({name:h,value:k}),e.removeAttribute(h))}f=0;for(h=n.length;f'+ +e),e=e.replace(/_dt_b_namespace_token_/g,":"));e=e.replace(//g,"");a.file(b,e)}})}function o(a,b,d){var c=a.createElement(b);d&&(d.attr&&g(c).attr(d.attr),d.children&&g.each(d.children,function(a,b){c.appendChild(b)}),d.text&&c.appendChild(a.createTextNode(d.text)));return c}function N(a,b){var d=a.header[b].length,c;a.footer&&a.footer[b].length>d&&(d=a.footer[b].length);for(var e=0,f=a.body.length;ed&& +(d=c),400&&(b=b+f);b=b+(e?e+(""+a[c]).replace(g,h+e)+e:a[c])}return b},j=b.header?k(c.header)+d:"",i=b.footer&&c.footer?d+k(c.footer):"",u=[],D=0,l=c.body.length;D', +"xl/_rels/workbook.xml.rels":'',"[Content_Types].xml":'', +"xl/workbook.xml":'', +"xl/worksheets/sheet1.xml":'',"xl/styles.xml":''}; +s.ext.buttons.copyHtml5={className:"buttons-copy buttons-html5",text:function(a){return a.i18n("buttons.copy","Copy")},action:function(a,b,d,c){var a=L(b,c),e=a.str,d=g("
").css({height:1,width:1,overflow:"hidden",position:"fixed",top:0,left:0});c.customize&&(e=c.customize(e,c));c=g("",k.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue,c.appendChild(b),b.innerHTML="",k.checkClone=b.cloneNode(!0).cloneNode(!0).lastChild.checked,k.noCloneEvent=!0,b.attachEvent&&(b.attachEvent("onclick",function(){k.noCloneEvent=!1}),b.cloneNode(!0).click()),null==k.deleteExpando){k.deleteExpando=!0;try{delete b.test}catch(d){k.deleteExpando=!1}}}(),function(){var b,c,d=y.createElement("div");for(b in{submit:!0,change:!0,focusin:!0})c="on"+b,(k[b+"Bubbles"]=c in a)||(d.setAttribute(c,"t"),k[b+"Bubbles"]=d.attributes[c].expando===!1);d=null}();var X=/^(?:input|select|textarea)$/i,Y=/^key/,Z=/^(?:mouse|pointer|contextmenu)|click/,$=/^(?:focusinfocus|focusoutblur)$/,_=/^([^.]*)(?:\.(.+)|)$/;function aa(){return!0}function ba(){return!1}function ca(){try{return y.activeElement}catch(a){}}m.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,n,o,p,q,r=m._data(a);if(r){c.handler&&(i=c,c=i.handler,e=i.selector),c.guid||(c.guid=m.guid++),(g=r.events)||(g=r.events={}),(k=r.handle)||(k=r.handle=function(a){return typeof m===K||a&&m.event.triggered===a.type?void 0:m.event.dispatch.apply(k.elem,arguments)},k.elem=a),b=(b||"").match(E)||[""],h=b.length;while(h--)f=_.exec(b[h])||[],o=q=f[1],p=(f[2]||"").split(".").sort(),o&&(j=m.event.special[o]||{},o=(e?j.delegateType:j.bindType)||o,j=m.event.special[o]||{},l=m.extend({type:o,origType:q,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&m.expr.match.needsContext.test(e),namespace:p.join(".")},i),(n=g[o])||(n=g[o]=[],n.delegateCount=0,j.setup&&j.setup.call(a,d,p,k)!==!1||(a.addEventListener?a.addEventListener(o,k,!1):a.attachEvent&&a.attachEvent("on"+o,k))),j.add&&(j.add.call(a,l),l.handler.guid||(l.handler.guid=c.guid)),e?n.splice(n.delegateCount++,0,l):n.push(l),m.event.global[o]=!0);a=null}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,n,o,p,q,r=m.hasData(a)&&m._data(a);if(r&&(k=r.events)){b=(b||"").match(E)||[""],j=b.length;while(j--)if(h=_.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o){l=m.event.special[o]||{},o=(d?l.delegateType:l.bindType)||o,n=k[o]||[],h=h[2]&&new RegExp("(^|\\.)"+p.join("\\.(?:.*\\.|)")+"(\\.|$)"),i=f=n.length;while(f--)g=n[f],!e&&q!==g.origType||c&&c.guid!==g.guid||h&&!h.test(g.namespace)||d&&d!==g.selector&&("**"!==d||!g.selector)||(n.splice(f,1),g.selector&&n.delegateCount--,l.remove&&l.remove.call(a,g));i&&!n.length&&(l.teardown&&l.teardown.call(a,p,r.handle)!==!1||m.removeEvent(a,o,r.handle),delete k[o])}else for(o in k)m.event.remove(a,o+b[j],c,d,!0);m.isEmptyObject(k)&&(delete r.handle,m._removeData(a,"events"))}},trigger:function(b,c,d,e){var f,g,h,i,k,l,n,o=[d||y],p=j.call(b,"type")?b.type:b,q=j.call(b,"namespace")?b.namespace.split("."):[];if(h=l=d=d||y,3!==d.nodeType&&8!==d.nodeType&&!$.test(p+m.event.triggered)&&(p.indexOf(".")>=0&&(q=p.split("."),p=q.shift(),q.sort()),g=p.indexOf(":")<0&&"on"+p,b=b[m.expando]?b:new m.Event(p,"object"==typeof b&&b),b.isTrigger=e?2:3,b.namespace=q.join("."),b.namespace_re=b.namespace?new RegExp("(^|\\.)"+q.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=d),c=null==c?[b]:m.makeArray(c,[b]),k=m.event.special[p]||{},e||!k.trigger||k.trigger.apply(d,c)!==!1)){if(!e&&!k.noBubble&&!m.isWindow(d)){for(i=k.delegateType||p,$.test(i+p)||(h=h.parentNode);h;h=h.parentNode)o.push(h),l=h;l===(d.ownerDocument||y)&&o.push(l.defaultView||l.parentWindow||a)}n=0;while((h=o[n++])&&!b.isPropagationStopped())b.type=n>1?i:k.bindType||p,f=(m._data(h,"events")||{})[b.type]&&m._data(h,"handle"),f&&f.apply(h,c),f=g&&h[g],f&&f.apply&&m.acceptData(h)&&(b.result=f.apply(h,c),b.result===!1&&b.preventDefault());if(b.type=p,!e&&!b.isDefaultPrevented()&&(!k._default||k._default.apply(o.pop(),c)===!1)&&m.acceptData(d)&&g&&d[p]&&!m.isWindow(d)){l=d[g],l&&(d[g]=null),m.event.triggered=p;try{d[p]()}catch(r){}m.event.triggered=void 0,l&&(d[g]=l)}return b.result}},dispatch:function(a){a=m.event.fix(a);var b,c,e,f,g,h=[],i=d.call(arguments),j=(m._data(this,"events")||{})[a.type]||[],k=m.event.special[a.type]||{};if(i[0]=a,a.delegateTarget=this,!k.preDispatch||k.preDispatch.call(this,a)!==!1){h=m.event.handlers.call(this,a,j),b=0;while((f=h[b++])&&!a.isPropagationStopped()){a.currentTarget=f.elem,g=0;while((e=f.handlers[g++])&&!a.isImmediatePropagationStopped())(!a.namespace_re||a.namespace_re.test(e.namespace))&&(a.handleObj=e,a.data=e.data,c=((m.event.special[e.origType]||{}).handle||e.handler).apply(f.elem,i),void 0!==c&&(a.result=c)===!1&&(a.preventDefault(),a.stopPropagation()))}return k.postDispatch&&k.postDispatch.call(this,a),a.result}},handlers:function(a,b){var c,d,e,f,g=[],h=b.delegateCount,i=a.target;if(h&&i.nodeType&&(!a.button||"click"!==a.type))for(;i!=this;i=i.parentNode||this)if(1===i.nodeType&&(i.disabled!==!0||"click"!==a.type)){for(e=[],f=0;h>f;f++)d=b[f],c=d.selector+" ",void 0===e[c]&&(e[c]=d.needsContext?m(c,this).index(i)>=0:m.find(c,this,null,[i]).length),e[c]&&e.push(d);e.length&&g.push({elem:i,handlers:e})}return h]","i"),ha=/^\s+/,ia=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,ja=/<([\w:]+)/,ka=/
\s*$/g,ra={option:[1,""],legend:[1,"
","
"],area:[1,"",""],param:[1,"",""],thead:[1,"
","
"],tr:[2,"","
"],col:[2,"","
"],td:[3,"","
"],_default:k.htmlSerialize?[0,"",""]:[1,"X
","
"]},sa=da(y),ta=sa.appendChild(y.createElement("div"));ra.optgroup=ra.option,ra.tbody=ra.tfoot=ra.colgroup=ra.caption=ra.thead,ra.th=ra.td;function ua(a,b){var c,d,e=0,f=typeof a.getElementsByTagName!==K?a.getElementsByTagName(b||"*"):typeof a.querySelectorAll!==K?a.querySelectorAll(b||"*"):void 0;if(!f)for(f=[],c=a.childNodes||a;null!=(d=c[e]);e++)!b||m.nodeName(d,b)?f.push(d):m.merge(f,ua(d,b));return void 0===b||b&&m.nodeName(a,b)?m.merge([a],f):f}function va(a){W.test(a.type)&&(a.defaultChecked=a.checked)}function wa(a,b){return m.nodeName(a,"table")&&m.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function xa(a){return a.type=(null!==m.find.attr(a,"type"))+"/"+a.type,a}function ya(a){var b=pa.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function za(a,b){for(var c,d=0;null!=(c=a[d]);d++)m._data(c,"globalEval",!b||m._data(b[d],"globalEval"))}function Aa(a,b){if(1===b.nodeType&&m.hasData(a)){var c,d,e,f=m._data(a),g=m._data(b,f),h=f.events;if(h){delete g.handle,g.events={};for(c in h)for(d=0,e=h[c].length;e>d;d++)m.event.add(b,c,h[c][d])}g.data&&(g.data=m.extend({},g.data))}}function Ba(a,b){var c,d,e;if(1===b.nodeType){if(c=b.nodeName.toLowerCase(),!k.noCloneEvent&&b[m.expando]){e=m._data(b);for(d in e.events)m.removeEvent(b,d,e.handle);b.removeAttribute(m.expando)}"script"===c&&b.text!==a.text?(xa(b).text=a.text,ya(b)):"object"===c?(b.parentNode&&(b.outerHTML=a.outerHTML),k.html5Clone&&a.innerHTML&&!m.trim(b.innerHTML)&&(b.innerHTML=a.innerHTML)):"input"===c&&W.test(a.type)?(b.defaultChecked=b.checked=a.checked,b.value!==a.value&&(b.value=a.value)):"option"===c?b.defaultSelected=b.selected=a.defaultSelected:("input"===c||"textarea"===c)&&(b.defaultValue=a.defaultValue)}}m.extend({clone:function(a,b,c){var d,e,f,g,h,i=m.contains(a.ownerDocument,a);if(k.html5Clone||m.isXMLDoc(a)||!ga.test("<"+a.nodeName+">")?f=a.cloneNode(!0):(ta.innerHTML=a.outerHTML,ta.removeChild(f=ta.firstChild)),!(k.noCloneEvent&&k.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||m.isXMLDoc(a)))for(d=ua(f),h=ua(a),g=0;null!=(e=h[g]);++g)d[g]&&Ba(e,d[g]);if(b)if(c)for(h=h||ua(a),d=d||ua(f),g=0;null!=(e=h[g]);g++)Aa(e,d[g]);else Aa(a,f);return d=ua(f,"script"),d.length>0&&za(d,!i&&ua(a,"script")),d=h=e=null,f},buildFragment:function(a,b,c,d){for(var e,f,g,h,i,j,l,n=a.length,o=da(b),p=[],q=0;n>q;q++)if(f=a[q],f||0===f)if("object"===m.type(f))m.merge(p,f.nodeType?[f]:f);else if(la.test(f)){h=h||o.appendChild(b.createElement("div")),i=(ja.exec(f)||["",""])[1].toLowerCase(),l=ra[i]||ra._default,h.innerHTML=l[1]+f.replace(ia,"<$1>")+l[2],e=l[0];while(e--)h=h.lastChild;if(!k.leadingWhitespace&&ha.test(f)&&p.push(b.createTextNode(ha.exec(f)[0])),!k.tbody){f="table"!==i||ka.test(f)?""!==l[1]||ka.test(f)?0:h:h.firstChild,e=f&&f.childNodes.length;while(e--)m.nodeName(j=f.childNodes[e],"tbody")&&!j.childNodes.length&&f.removeChild(j)}m.merge(p,h.childNodes),h.textContent="";while(h.firstChild)h.removeChild(h.firstChild);h=o.lastChild}else p.push(b.createTextNode(f));h&&o.removeChild(h),k.appendChecked||m.grep(ua(p,"input"),va),q=0;while(f=p[q++])if((!d||-1===m.inArray(f,d))&&(g=m.contains(f.ownerDocument,f),h=ua(o.appendChild(f),"script"),g&&za(h),c)){e=0;while(f=h[e++])oa.test(f.type||"")&&c.push(f)}return h=null,o},cleanData:function(a,b){for(var d,e,f,g,h=0,i=m.expando,j=m.cache,l=k.deleteExpando,n=m.event.special;null!=(d=a[h]);h++)if((b||m.acceptData(d))&&(f=d[i],g=f&&j[f])){if(g.events)for(e in g.events)n[e]?m.event.remove(d,e):m.removeEvent(d,e,g.handle);j[f]&&(delete j[f],l?delete d[i]:typeof d.removeAttribute!==K?d.removeAttribute(i):d[i]=null,c.push(f))}}}),m.fn.extend({text:function(a){return V(this,function(a){return void 0===a?m.text(this):this.empty().append((this[0]&&this[0].ownerDocument||y).createTextNode(a))},null,a,arguments.length)},append:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=wa(this,a);b.appendChild(a)}})},prepend:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=wa(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},remove:function(a,b){for(var c,d=a?m.filter(a,this):this,e=0;null!=(c=d[e]);e++)b||1!==c.nodeType||m.cleanData(ua(c)),c.parentNode&&(b&&m.contains(c.ownerDocument,c)&&za(ua(c,"script")),c.parentNode.removeChild(c));return this},empty:function(){for(var a,b=0;null!=(a=this[b]);b++){1===a.nodeType&&m.cleanData(ua(a,!1));while(a.firstChild)a.removeChild(a.firstChild);a.options&&m.nodeName(a,"select")&&(a.options.length=0)}return this},clone:function(a,b){return a=null==a?!1:a,b=null==b?a:b,this.map(function(){return m.clone(this,a,b)})},html:function(a){return V(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a)return 1===b.nodeType?b.innerHTML.replace(fa,""):void 0;if(!("string"!=typeof a||ma.test(a)||!k.htmlSerialize&&ga.test(a)||!k.leadingWhitespace&&ha.test(a)||ra[(ja.exec(a)||["",""])[1].toLowerCase()])){a=a.replace(ia,"<$1>");try{for(;d>c;c++)b=this[c]||{},1===b.nodeType&&(m.cleanData(ua(b,!1)),b.innerHTML=a);b=0}catch(e){}}b&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(){var a=arguments[0];return this.domManip(arguments,function(b){a=this.parentNode,m.cleanData(ua(this)),a&&a.replaceChild(b,this)}),a&&(a.length||a.nodeType)?this:this.remove()},detach:function(a){return this.remove(a,!0)},domManip:function(a,b){a=e.apply([],a);var c,d,f,g,h,i,j=0,l=this.length,n=this,o=l-1,p=a[0],q=m.isFunction(p);if(q||l>1&&"string"==typeof p&&!k.checkClone&&na.test(p))return this.each(function(c){var d=n.eq(c);q&&(a[0]=p.call(this,c,d.html())),d.domManip(a,b)});if(l&&(i=m.buildFragment(a,this[0].ownerDocument,!1,this),c=i.firstChild,1===i.childNodes.length&&(i=c),c)){for(g=m.map(ua(i,"script"),xa),f=g.length;l>j;j++)d=i,j!==o&&(d=m.clone(d,!0,!0),f&&m.merge(g,ua(d,"script"))),b.call(this[j],d,j);if(f)for(h=g[g.length-1].ownerDocument,m.map(g,ya),j=0;f>j;j++)d=g[j],oa.test(d.type||"")&&!m._data(d,"globalEval")&&m.contains(h,d)&&(d.src?m._evalUrl&&m._evalUrl(d.src):m.globalEval((d.text||d.textContent||d.innerHTML||"").replace(qa,"")));i=c=null}return this}}),m.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){m.fn[a]=function(a){for(var c,d=0,e=[],g=m(a),h=g.length-1;h>=d;d++)c=d===h?this:this.clone(!0),m(g[d])[b](c),f.apply(e,c.get());return this.pushStack(e)}});var Ca,Da={};function Ea(b,c){var d,e=m(c.createElement(b)).appendTo(c.body),f=a.getDefaultComputedStyle&&(d=a.getDefaultComputedStyle(e[0]))?d.display:m.css(e[0],"display");return e.detach(),f}function Fa(a){var b=y,c=Da[a];return c||(c=Ea(a,b),"none"!==c&&c||(Ca=(Ca||m("