Merge pull request #861 from exodus4d/develop

v1.5.4
This commit is contained in:
Mark Friedrich
2019-10-12 20:59:32 +02:00
committed by GitHub
244 changed files with 11554 additions and 3144 deletions

View File

@@ -14,7 +14,7 @@
"node": true,
// Allow ES6.
"esversion": 6,
"esversion": 7,
/*
* ENFORCING OPTIONS

View File

@@ -18,7 +18,8 @@ Mapping tool for [*EVE ONLINE*](https://www.eveonline.com)
- [wiki](https://github.com/exodus4d/pathfinder/wiki)
- Developer [Slack](https://slack.com) chat:
- https://pathfinder-eve-online.slack.com
- Please send me a mail for invite: pathfinder@exodus4d.de
- Join channel [pathfinder-eve-online.slack.com](https://join.slack.com/t/pathfinder-eve-online/shared_invite/enQtMzMyOTkyMjczMTA3LWI2NGE1OTY5ODBmNDZlMDY3MDIzYjk5ZTljM2JjZjIwNDRkNzMyMTEwMDUzOGQwM2E3ZjE1NGEwNThlMzYzY2Y)
- Can´t join? pathfinder@exodus4d.de
**Feel free to check the code for bugs and security issues.
Issues should be reported in the [Issue](https://github.com/exodus4d/pathfinder/issues) section.**
@@ -49,7 +50,7 @@ Issues should be reported in the [Issue](https://github.com/exodus4d/pathfinder/
|-- app.js --> require.js config (!required for production!)
|-- [0777] logs/ --> log files
|-- ...
| -- node_modules/ --> node.js modules (not used for production)
|-- node_modules/ --> node.js modules (not used for production)
|-- ...
|-- [0755] public/ --> frontend source
|-- css/ --> CSS dist/build folder (minified)

View File

@@ -59,8 +59,13 @@ deleteStatisticsData = Cron\StatisticsUpdate->deleteStatisticsD
; truncate map history log files
truncateMapHistoryLogFiles = Cron\MapHistory->truncateFiles, @halfHour
; updates small amount of static system data from CCP API
; sync "sovereignty" and "faction warfare" data from CCP´s ESI API
updateSovereigntyData = Cron\Universe->updateSovereigntyData, @halfPastHour
; sync static system data from CCP´s ESI API
; -> Job is WIP!
;updateUniverseSystems = Cron\Universe->updateUniverseSystems, @instant
; setup universe DB with static data from ESI
; bootstrap job for "eve_universe" DB from CCP´s ESI API
; -> Only for development! This job is used to build the initial export/sql/eve_universe.sql
;setup = Cron\Universe->setup, @instant

View File

@@ -213,7 +213,7 @@ class Cortex extends Cursor {
} else
$this->whitelist=$fields;
$id=$this->dbsType=='sql'?$this->primary:'_id';
if (!in_array($id,$this->whitelist))
if (!in_array($id,$this->whitelist) && !($exclude && in_array($id,$fields)))
$this->whitelist[]=$id;
$this->applyWhitelist();
return $this->whitelist;
@@ -595,7 +595,7 @@ class Cortex extends Cursor {
* @param array|null $filter
* @param array|null $options
* @param int $ttl
* @return CortexCollection
* @return CortexCollection|false
*/
public function find($filter = NULL, array $options = NULL, $ttl = 0) {
$sort=false;
@@ -731,12 +731,12 @@ class Cortex extends Cursor {
$addToFilter = array($id.' IN ?', $result);
}
// *-to-one
elseif ($this->dbsType == 'sql') {
elseif (!$deep && $this->dbsType == 'sql') {
// use sub-query inclusion
$has_filter=$this->mergeFilter([$has_filter,
[$this->rel($key)->getTable().'.'.$fromConf[1].'='.$this->getTable().'.'.$id]]);
$result = $this->_refSubQuery($key,$has_filter,$has_options);
$addToFilter = ['exists('.$result[0].')']+$result[1];
$addToFilter = array_merge(['exists('.$result[0].')'],$result[1]);
}
elseif ($result = $this->_hasRefsIn($key,$has_filter,$has_options,$ttl))
$addToFilter = array($id.' IN ?', $result);
@@ -781,11 +781,11 @@ class Cortex extends Cursor {
$options['order'] = preg_replace('/\h+DESC(?=\s*(?:$|,))/i',' DESC NULLS LAST',$options['order']);
// assemble full sql query for joined queries
if ($hasJoin) {
$adhoc=[];
// when in count-mode and grouping is active, wrap the query later
// otherwise add a an adhoc counter field here
if (!($subquery_mode=($options && !empty($options['group']))) && $count)
$this->adhoc['_rows']=['expr'=>'COUNT(*)','value'=>NULL];
$adhoc=[];
$adhoc[]='(COUNT(*)) as _rows';
if (!$count)
// add bind parameters for filters in adhoc fields
if ($this->preBinds) {
@@ -1224,7 +1224,7 @@ class Cortex extends Cursor {
// m:m save cascade
if (!empty($this->saveCsd)) {
foreach($this->saveCsd as $key => $val) {
if($fields[$key]['relType'] == 'has-many') {
if ($fields[$key]['relType'] == 'has-many') {
$relConf = $fields[$key]['has-many'];
if ($relConf['hasRel'] == 'has-many') {
$mmTable = $this->mmTable($relConf,$key);
@@ -1236,12 +1236,12 @@ class Cortex extends Cursor {
$filter[] = $id;
}
// delete all refs
if (is_null($val))
if (empty($val))
$mm->erase($filter);
// update refs
elseif (is_array($val)) {
$mm->erase($filter);
foreach($val as $v) {
foreach(array_unique($val) as $v) {
if ($relConf['isSelf'] && $v==$id)
continue;
$mm->set($key,$v);
@@ -1256,7 +1256,7 @@ class Cortex extends Cursor {
$rel = $this->getRelInstance($relConf[0],$relConf,$key);
// find existing relations
$refs = $rel->find([$relConf[1].' = ?',$this->getRaw($relConf['relField'])]);
if (is_null($val)) {
if (empty($val)) {
foreach ($refs?:[] as $model) {
$model->set($relConf[1],NULL);
$model->save();
@@ -1470,7 +1470,7 @@ class Cortex extends Cursor {
// handle relations
if (isset($fields[$key]['belongs-to-one'])) {
// one-to-many, one-to-one
if (is_null($val))
if (empty($val))
$val = NULL;
elseif (is_object($val) &&
!($this->dbsType=='mongo' && (
@@ -1489,7 +1489,7 @@ class Cortex extends Cursor {
$val = $this->db->legacy() ? new \MongoId($val) : new \MongoDB\BSON\ObjectId($val);
} elseif (isset($fields[$key]['has-one'])){
$relConf = $fields[$key]['has-one'];
if (is_null($val)) {
if (empty($val)) {
$val = $this->get($key);
$val->set($relConf[1],NULL);
} else {
@@ -2554,7 +2554,7 @@ class CortexQueryParser extends \Prefab {
function($match) use($db) {
if (!isset($match[1]))
return $match[0];
if (preg_match('/\b(AND|OR|IN|LIKE|NOT)\b/i',$match[1]))
if (preg_match('/\b(AND|OR|IN|LIKE|NOT|HAVING|SELECT|FROM|WHERE)\b/i',$match[1]))
return $match[1];
return $db->quotekey($match[1]);
}, $cond);
@@ -2576,7 +2576,7 @@ class CortexQueryParser extends \Prefab {
function($match) use($table) {
if (!isset($match[3]))
return $match[1];
if (preg_match('/\b(AND|OR|IN|LIKE|NOT)\b/i',$match[3]))
if (preg_match('/\b(AND|OR|IN|LIKE|NOT|HAVING|SELECT|FROM|WHERE)\b/i',$match[3]))
return $match[0];
return $match[2].$table.'.'.$match[3];
}, $cond);

View File

@@ -36,14 +36,15 @@ class AccessController extends Controller {
}
/**
* get current character and check if it is a valid character
* check login status and look or a valid character
* @param \Base $f3
* @return string
* @throws \Exception
*/
protected function isLoggedIn(\Base $f3) : string {
$loginStatus = 'UNKNOWN';
if($character = $this->getCharacter()){
// disable ttl cache time here. Further getCharacter() calls should use a short ttl
if($character = $this->getCharacter(0)){
if($character->checkLoginTimer()){
if(( $authStatus = $character->isAuthorized()) === 'OK'){
$loginStatus = 'OK';

View File

@@ -89,7 +89,7 @@ class Admin extends Controller{
protected function getAdminCharacter(\Base $f3){
$adminCharacter = null;
if( !$f3->exists(Sso::SESSION_KEY_SSO_ERROR) ){
if( $character = $this->getCharacter() ){
if( $character = $this->getCharacter(0) ){
if(in_array($character->roleId->name, ['SUPER', 'CORPORATION'], true)){
// current character is admin
$adminCharacter = $character;
@@ -288,7 +288,7 @@ class Admin extends Controller{
$characters = [];
// check if kickCharacters belong to same Corp as admin character
// -> remove admin char from valid characters...
if( !empty($characterIds = array_diff( [$characterId], [$character->_id])) ){
if( !empty($characterIds = array_diff([$characterId], [$character->_id])) ){
if($character->roleId->name === 'SUPER'){
if($filterCharacters = CharacterModel::getAll($characterIds)){
$characters = $filterCharacters;
@@ -337,7 +337,7 @@ class Admin extends Controller{
$maps = $filterMaps;
}
}else{
$maps = $character->getCorporation()->getMaps([$mapId], ['addInactive' => true, 'ignoreMapCount' => true]);
$maps = $character->getCorporation()->getMaps($mapId, ['addInactive' => true, 'ignoreMapCount' => true]);
}
return $maps;
@@ -404,7 +404,7 @@ class Admin extends Controller{
$corporations = $this->getAccessibleCorporations($character);
foreach($corporations as $corporation){
if($maps = $corporation->getMaps([], ['addInactive' => true, 'ignoreMapCount' => true])){
if($maps = $corporation->getMaps(null, ['addInactive' => true, 'ignoreMapCount' => true])){
$data->corpMaps[$corporation->name] = $maps;
}
}

View File

@@ -52,13 +52,10 @@ class Map extends Controller\AccessController {
* @throws Exception
*/
public function initData(\Base $f3){
// expire time in seconds
$expireTimeCache = 60 * 60;
if(!$f3->exists(self::CACHE_KEY_INIT, $return)){
// response should not be cached if invalid -> e.g. missing static data
$validInitData = true;
$validInitData = true;
$ttl = 60 * 60;
if(!$exists = $f3->exists(self::CACHE_KEY_INIT, $return)){
$return = (object) [];
$return->error = [];
@@ -179,7 +176,10 @@ class Map extends Controller\AccessController {
// get third party APIs -----------------------------------------------------------------------------------
$return->url = [
'ccpImageServer' => Config::getPathfinderData('api.ccp_image_server'),
'zKillboard' => Config::getPathfinderData('api.z_killboard')
'zKillboard' => Config::getPathfinderData('api.z_killboard'),
'eveeye' => Config::getPathfinderData('api.eveeye'),
'dotlan' => Config::getPathfinderData('api.dotlan'),
'anoik' => Config::getPathfinderData('api.anoik')
];
// Character default config -------------------------------------------------------------------------------
@@ -199,29 +199,33 @@ class Map extends Controller\AccessController {
// structure status ---------------------------------------------------------------------------------------
$structureStatus = Pathfinder\StructureStatusModel::getAll();
$structureData = [];
$structureStatusData = [];
foreach($structureStatus as $status){
$structureData[$status->_id] = $status->getData();
$structureStatusData[$status->_id] = $status->getData();
}
$return->structureStatus = $structureData;
$return->structureStatus = $structureStatusData;
$validInitData = $validInitData ? !empty($structureData) : $validInitData;
$validInitData = $validInitData ? !empty($structureStatusData) : $validInitData;
// get available wormhole types ---------------------------------------------------------------------------
/**
* @var $wormhole Universe\WormholeModel
* @var $groupUniverseModel Universe\GroupModel
*/
$wormhole = Universe\AbstractUniverseModel::getNew('WormholeModel');
$groupUniverseModel = Universe\AbstractUniverseModel::getNew('GroupModel');
$groupUniverseModel->getById(Config::ESI_GROUP_WORMHOLE_ID);
$wormholesData = [];
if($rows = $wormhole->find(null, ['order' => 'name asc'])){
foreach($rows as $rowData){
$wormholesData[$rowData->name] = $rowData->getData();
/**
* @var $typeModel Universe\TypeModel
*/
foreach($types = $groupUniverseModel->getTypes(false) as $typeModel){
if(
($wormholeData = $typeModel->getWormholeData()) &&
mb_strlen((string)$wormholeData->name) === 4
){
$wormholesData[$wormholeData->name] = $wormholeData;
}
$wormhole->reset();
$wormhole->name = 'K162';
$wormholesData[$wormhole->name] = $wormhole->getData();
}
ksort($wormholesData);
$return->wormholes = $wormholesData;
$validInitData = $validInitData ? !empty($wormholesData) : $validInitData;
@@ -231,20 +235,20 @@ class Map extends Controller\AccessController {
* @var $categoryUniverseModel Universe\CategoryModel
*/
$categoryUniverseModel = Universe\AbstractUniverseModel::getNew('CategoryModel');
$categoryUniverseModel->getById(6);
$shipData = $categoryUniverseModel->getData(['mass']);
$categoryUniverseModel->getById(65);
$structureData = $categoryUniverseModel->getData();
$return->universeCategories = [
6 => $shipData,
65 => $structureData
Config::ESI_CATEGORY_SHIP_ID =>
($categoryUniverseModel->getById(Config::ESI_CATEGORY_SHIP_ID) && $categoryUniverseModel->valid()) ? $categoryUniverseModel->getData(['mass']) : null,
Config::ESI_CATEGORY_STRUCTURE_ID =>
($categoryUniverseModel->getById(Config::ESI_CATEGORY_STRUCTURE_ID) && $categoryUniverseModel->valid()) ? $categoryUniverseModel->getData() : null,
];
$validInitData = $validInitData ? !empty($return->universeCategories[65]) : $validInitData;
$validInitData = $validInitData ? !count(array_filter($return->universeCategories, function($v){
return empty(array_filter((array)$v->groups));
})) : $validInitData;
// response should not be cached if invalid -> e.g. missing static data
if($validInitData){
$f3->set(self::CACHE_KEY_INIT, $return, $expireTimeCache );
$f3->set(self::CACHE_KEY_INIT, $return, $ttl);
}
}
@@ -257,6 +261,9 @@ class Map extends Controller\AccessController {
$ssoError->message = $message;
$return->error[] = $ssoError;
$f3->clear(Controller\Ccp\Sso::SESSION_KEY_SSO_ERROR);
}elseif($validInitData){
// no errors and valid data -> send Cache header
$f3->expire(Config::ttlLeft($exists, $ttl));
}
echo json_encode($return);
@@ -871,6 +878,7 @@ class Map extends Controller\AccessController {
$getMapUserData = (bool)$postData['getMapUserData'];
$mapTracking = (bool)$postData['mapTracking'];
$systemData = (array)$postData['systemData'];
$newSystemPositions = (array)$postData['newSystemPositions'];
$activeCharacter = $this->getCharacter();
$return = (object)[];
@@ -886,7 +894,7 @@ class Map extends Controller\AccessController {
if( !is_null($map = $activeCharacter->getMap($mapId)) ){
// check character log (current system) and manipulate map (e.g. add new system)
if($mapTracking){
$map = $this->updateMapByCharacter($map, $activeCharacter);
$map = $this->updateMapByCharacter($map, $activeCharacter, $newSystemPositions);
}
// mapUserData ----------------------------------------------------------------------------------------
@@ -906,14 +914,13 @@ class Map extends Controller\AccessController {
// systemData -----------------------------------------------------------------------------------------
if(
$mapId === (int)$systemData['mapId'] &&
!is_null($system = $map->getSystemById((int)$systemData['systemData']['id']))
!is_null($system = $map->getSystemById((int)$systemData['id']))
){
// data for currently selected system
$return->system = $system->getData();
$return->system->signatures = $system->getSignaturesData();
$return->system->sigHistory = $system->getSignaturesHistory();
$return->system->structures = $system->getStructuresData();
}
}
}
@@ -932,10 +939,11 @@ class Map extends Controller\AccessController {
* update map connections/systems based on $character´s location logs
* @param Pathfinder\MapModel $map
* @param Pathfinder\CharacterModel $character
* @param array $newSystemPositions
* @return Pathfinder\MapModel
* @throws Exception
*/
protected function updateMapByCharacter(Pathfinder\MapModel $map, Pathfinder\CharacterModel $character) : Pathfinder\MapModel {
protected function updateMapByCharacter(Pathfinder\MapModel $map, Pathfinder\CharacterModel $character, array $newSystemPositions = []) : Pathfinder\MapModel {
// map changed. update cache (system/connection) changed
$mapDataChanged = false;
@@ -952,6 +960,9 @@ class Map extends Controller\AccessController {
$sourceSystemId = (int)$sourceLog->systemId;
if($sourceSystemId){
$defaultPositions = (array)$newSystemPositions['defaults'];
$currentPosition = (array)$newSystemPositions['location'];
$sourceSystem = null;
$targetSystem = null;
@@ -963,8 +974,8 @@ class Map extends Controller\AccessController {
// system coordinates for system tha might be added next
$systemOffsetX = 130;
$systemOffsetY = 0;
$systemPosX = 0;
$systemPosY = 30;
$systemPosX = ((int)$defaultPositions[0]['x']) ? : 0;
$systemPosY = ((int)$defaultPositions[0]['y']) ? : 30;
// check if previous (solo) system is already on the map ----------------------------------------------
$sourceSystem = $map->getSystemByCCPId($sourceSystemId, [AbstractModel::getFilter('active', true)]);
@@ -972,12 +983,10 @@ class Map extends Controller\AccessController {
// if systems don´t already exists on map -> get "blank" system
// -> required for system type check (e.g. wormhole, k-space)
if($sourceSystem){
// system exists
$sourceExists = true;
// system exists -> add target to the "right"
$systemPosX = $sourceSystem->posX + $systemOffsetX;
$systemPosY = $sourceSystem->posY + $systemOffsetY;
}else{
// system not exists -> get"blank" system
$sourceSystem = $map->getNewSystem($sourceSystemId);
}
@@ -992,6 +1001,11 @@ class Map extends Controller\AccessController {
if($targetSystem){
$targetExists = true;
if($targetSystemId === (int)$currentPosition['systemId']){
$systemPosX = (int)$currentPosition['position']['x'];
$systemPosY = (int)$currentPosition['position']['y'];
}
}else{
$targetSystem = $map->getNewSystem($targetSystemId);
}
@@ -1062,6 +1076,24 @@ class Map extends Controller\AccessController {
break;
}
// check for "abyss" systems =====================================================================
if(!$map->trackAbyssalJumps){
if(
$sourceSystem->isAbyss() ||
$targetSystem->isAbyss()
){
$addConnection = false;
if($sourceSystem->isAbyss()){
$addSourceSystem = false;
}
if($targetSystem->isAbyss()){
$addTargetSystem = false;
}
}
}
// save source system =============================================================================
if(
$addSourceSystem &&
@@ -1074,9 +1106,15 @@ class Map extends Controller\AccessController {
$map = $sourceSystem->mapId;
$sourceExists = true;
$mapDataChanged = true;
// increase system position (prevent overlapping)
$systemPosX = $sourceSystem->posX + $systemOffsetX;
$systemPosY = $sourceSystem->posY + $systemOffsetY;
if(!empty($defaultPositions[1])){
$systemPosX = (int)$defaultPositions[1]['x'];
$systemPosY = (int)$defaultPositions[1]['y'];
}else{
// increase system position (prevent overlapping)
$systemPosX = $sourceSystem->posX + $systemOffsetX;
$systemPosY = $sourceSystem->posY + $systemOffsetY;
}
}
}

View File

@@ -56,7 +56,7 @@ class Signature extends AbstractRestController {
$data['groupId'] == 5 ||
$data['typeId'] == 0
){
unset( $data['typeId'] );
unset($data['typeId']);
}
// "sig reader" should not overwrite signature group information
@@ -65,6 +65,7 @@ class Signature extends AbstractRestController {
$signature->groupId > 0
){
unset($data['groupId']);
unset($data['typeId']);
}
}
@@ -78,13 +79,20 @@ class Signature extends AbstractRestController {
// delete "old" signatures ----------------------------------------------------------------------------
if((bool)$requestData['deleteOld']){
// if linked ConnectionModels should be deleted as well
$deleteConnectionId = (bool)$requestData['deleteConnection'];
$updatedSignatureIds = array_column($signaturesData, 'id');
$signatures = $system->getSignatures();
foreach($signatures as $signature){
if(!in_array($signature->_id, $updatedSignatureIds)){
// set if potential linked ConnectionModel should be deleted as well
$signature->virtual('connectionIdDeleteCascade', $deleteConnectionId);
if($signature->delete()){
$updateSignaturesHistory = true;
}
// clear temp virtual field as well
$signature->clearVirtual('connectionIdDeleteCascade');
}
}
}
@@ -190,6 +198,9 @@ class Signature extends AbstractRestController {
$system->getById($systemId);
if($system->hasAccess($activeCharacter)){
// if linked ConnectionModels should be deleted as well
$deleteConnectionId = (bool)$requestData['deleteConnection'];
// if there is any changed/deleted/updated signature
// -> we need to update signature history data for the system
$updateSignaturesHistory = false;
@@ -202,11 +213,15 @@ class Signature extends AbstractRestController {
$signature->getById($signatureId);
// make sure signature belongs to main system (user has access)
if($signature->get('systemId', true) == $systemId){
// set if potential linked ConnectionModel should be deleted as well
$signature->virtual('connectionIdDeleteCascade', $deleteConnectionId);
if($signature->delete()){
$deletedSignatureIds[] = $signatureId;
$updateSignaturesHistory = true;
}
$signature->reset();
// clear temp virtual field as well
$signature->clearVirtual('connectionIdDeleteCascade');
}
}

View File

@@ -38,6 +38,7 @@ class System extends AbstractRestController {
$systemData->signatures = $system->getSignaturesData();
$systemData->sigHistory = $system->getSignaturesHistory();
$systemData->structures = $system->getStructuresData();
$systemData->stations = $system->getStationsData();
}
}

View File

@@ -91,14 +91,16 @@ class Route extends Controller\AccessController {
* -> this data is equal for EACH route search (does not depend on map data)
*/
private function setStaticJumpData(){
$query = "SELECT * FROM system_neighbour";
$rows = $this->getDB()->exec($query, null, $this->staticJumpDataCacheTime);
if($universeDB = $this->getDB('UNIVERSE')){
$query = "SELECT * FROM system_neighbour";
$rows = $universeDB->exec($query, null, $this->staticJumpDataCacheTime);
if(count($rows) > 0){
array_walk($rows, function(&$row){
$row['jumpNodes'] = array_map('intval', explode(':', $row['jumpNodes']));
});
$this->updateJumpData($rows);
if(count($rows) > 0){
array_walk($rows, function(&$row){
$row['jumpNodes'] = array_map('intval', explode(':', $row['jumpNodes']));
});
$this->updateJumpData($rows);
}
}
}

View File

@@ -9,6 +9,7 @@
namespace Controller\Api;
use Controller;
use lib\Config;
use Model;
class Setup extends Controller\Controller {
@@ -23,6 +24,7 @@ class Setup extends Controller\Controller {
public function buildIndex(\Base $f3){
$postData = (array)$f3->get('POST');
$type = (string)$postData['type'];
$countAll = (int)$postData['countAll'];
$count = (int)$postData['count'];
$offset = (int)$postData['offset'];
@@ -31,7 +33,7 @@ class Setup extends Controller\Controller {
$return->type = $type;
$return->count = $count;
$return->offset = $offset;
$return->countAll = 0;
$return->countAll = $countAll;
$return->countBuild = 0;
$return->countBuildAll = 0;
$return->progress = 0;
@@ -42,7 +44,7 @@ class Setup extends Controller\Controller {
* @param int $value
* @return int
*/
$sum = function(int $carry, int $value){
$sum = function(int $carry, int $value) : int {
$carry += $value;
return $carry;
};
@@ -62,49 +64,72 @@ class Setup extends Controller\Controller {
case 'Systems':
$length = 100;
$buildInfo = $controller->buildSystemsIndex($offset, $length);
$return->offset = $buildInfo['offset'];
$return->countAll = $buildInfo['countAll'];
$return->countBuild = $buildInfo['countBuild'];
$return->countBuildAll = $offset;
$return->progress = $percent($return->countAll, $offset);
$return->countBuildAll = $return->offset;
break;
case 'Wormholes':
$groupId = Config::ESI_GROUP_WORMHOLE_ID;
$length = 10;
$buildInfo = $controller->setupGroup($groupId, $offset, $length, true);
$return->offset = $buildInfo['offset'];
$return->countAll = $buildInfo['countAll'];
$return->countBuild = $buildInfo['count'];
$return->countBuildAll = $return->offset;
break;
case 'Structures':
$categoryId = 65;
$length = 2;
$offset = $count * $length;
$categoryId = Config::ESI_CATEGORY_STRUCTURE_ID;
$length = 1;
$buildInfo = $controller->setupCategory($categoryId, $offset, $length);
$categoryUniverseModel = Model\Universe\AbstractUniverseModel::getNew('CategoryModel');
$categoryUniverseModel->getById($categoryId, 0);
$return->countAll = (int)$f3->get('REQUIREMENTS.DATA.STRUCTURES');
$return->countBuild = array_reduce($buildInfo, $sum, 0);
$return->countBuildAll = $categoryUniverseModel->getTypesCount(false);
$return->progress = $percent($return->countAll, $return->countBuildAll);
$return->offset = $buildInfo['offset'];
$return->countBuild = $buildInfo['count'];
$return->countBuildAll = $return->offset;
$return->subCount = [
'countBuildAll' => $categoryUniverseModel->getTypesCount(false)
];
break;
case 'Ships':
$categoryId = 6;
$categoryId = Config::ESI_CATEGORY_SHIP_ID;
$length = 2;
$offset = $count * $length;
$buildInfo = $controller->setupCategory($categoryId, $offset, $length);
$categoryUniverseModel = Model\Universe\AbstractUniverseModel::getNew('CategoryModel');
$categoryUniverseModel->getById($categoryId, 0);
$return->countAll = (int)$f3->get('REQUIREMENTS.DATA.SHIPS');
$return->countBuild = array_reduce($buildInfo, $sum, 0);
$return->countBuildAll = $categoryUniverseModel->getTypesCount(false);
$return->progress = $percent($return->countAll, $return->countBuildAll);
$return->offset = $buildInfo['offset'];
$return->countBuild = $buildInfo['count'];
$return->countBuildAll = $return->offset;
$return->subCount = [
'countBuildAll' => $categoryUniverseModel->getTypesCount(false)
];
break;
case 'SystemStatic':
$length = 300;
$buildInfo = $this->setupSystemStaticTable($offset, $length);
$return->offset = $buildInfo['offset'];
$return->countAll = $buildInfo['countAll'];
$return->countBuild = $buildInfo['count'];
$return->countBuildAll = $return->offset;
break;
case 'SystemNeighbour':
// Becomes deprecated with new Universe DB!!!
$this->setupSystemJumpTable();
$length = 1500;
$buildInfo = $this->setupSystemJumpTable($offset, $length);
$return->countAll = (int)$f3->get('REQUIREMENTS.DATA.NEIGHBOURS');
$return->countBuild = $f3->DB->getDB('PF')->getRowCount('system_neighbour');
$return->countBuildAll = $return->countBuild;
$return->progress = $percent($return->countAll, $return->countBuildAll);
$return->offset = $buildInfo['offset'];
$return->countAll = $buildInfo['countAll'];
$return->countBuild = $buildInfo['count'];
$return->countBuildAll = $return->offset;
break;
}
$return->progress = $percent($return->countAll, $return->countBuildAll);
if($return->countBuildAll < $return->countAll){
$return->count++;
}
@@ -135,21 +160,87 @@ class Setup extends Controller\Controller {
case 'Systems':
$controller->clearSystemsIndex();
$systemUniverseModel = Model\Universe\AbstractUniverseModel::getNew('SystemModel');
$return->countAll = $f3->DB->getDB('UNIVERSE')->getRowCount($systemUniverseModel->getTable());
$return->countAll = $systemUniverseModel->getRowCount();
break;
case 'SystemNeighbour':
$systemNeighbourModel = Model\Universe\AbstractUniverseModel::getNew('SystemNeighbourModel');
$systemNeighbourModel->truncate();
$return->countAll = (int)$f3->get('REQUIREMENTS.DATA.NEIGHBOURS');
break;
}
echo json_encode($return);
}
/**
* import static 'system_static` table data from *.csv
* @param int $offset
* @param int $length
* @return array
* @throws \Exception
*/
protected function setupSystemStaticTable(int $offset = 0, int $length = 0) : array {
$info = ['countAll' => 0, 'countChunk' => 0, 'count' => 0, 'offset' => $offset];
/**
* @var $systemStaticModel Model\Universe\SystemStaticModel
*/
$systemStaticModel = Model\Universe\AbstractUniverseModel::getNew('SystemStaticModel');
if(!empty($csvData = $systemStaticModel::getCSVData($systemStaticModel->getTable()))){
$info['countAll'] = count($csvData);
if($length){
$csvData = array_slice($csvData, $offset, $length);
}
$info['countChunk'] = count($csvData);
$cols = ['typeId' => [], 'systemId' => []];
foreach($csvData as $data){
$validColCount = 0;
$systemStaticModel->getById((int)$data['id'], 0);
$systemStaticModel->id = (int)$data['id'];
foreach($cols as $col => &$invalidIds){
if($systemStaticModel->exists($col)){
$colVal = (int)$data[$col];
if(!in_array($colVal, $invalidIds)){
$relModel = $systemStaticModel->rel($col);
$relModel->getById($colVal, 0);
if($relModel->valid()){
$systemStaticModel->$col = $relModel;
$validColCount++;
}else{
$invalidIds[] = $colVal;
break;
}
}else{
break;
}
}
}
if($validColCount == count($cols)){
$systemStaticModel->save();
}
$systemStaticModel->reset();
$info['count']++;
$info['offset']++;
}
}
return $info;
}
/**
* This function is just for setting up the cache table 'system_neighbour' which is used
* for system jump calculation. Call this function manually when CCP adds Systems/Stargates
* @param int $offset
* @param int $length
* @return array
*/
protected function setupSystemJumpTable(){
$universeDB = $this->getDB('UNIVERSE');
protected function setupSystemJumpTable(int $offset = 0, int $length = 0) : array {
$info = ['countAll' => 0, 'countChunk' => 0, 'count' => 0, 'offset' => $offset];
$universeDB = $this->getDB('UNIVERSE');
$query = "SELECT
$query = "SELECT SQL_CALC_FOUND_ROWS
`system`.`id` `systemId`,
`system`.`name` `systemName`,
`system`.`constellationId` `constellationId`,
@@ -178,58 +269,73 @@ class Setup extends Controller\Controller {
`system`.`security` = :ls OR
`system`.`security` = :hs
)
HAVING
`jumpNodes` IS NOT NULL
";
$rows = $universeDB->exec($query, [
$args = [
':regionIdJove1' => 10000017,
':regionIdJove2' => 10000019,
':regionIdJove3' => 10000004,
':ns' => '0.0',
':ls' => 'L',
':hs' => 'H'
]);
];
if(count($rows)){
$pfDB = $this->getDB('PF');
if($length){
$query .= ' LIMIT :limit';
$args[':limit'] = $length;
// clear cache table
$pfDB->exec("TRUNCATE system_neighbour");
if($offset){
$query .= ' OFFSET :offset';
$args[':offset'] = $offset;
}
}
$rows = $universeDB->exec($query, $args);
if(!empty($countRes = $universeDB->exec("SELECT FOUND_ROWS() `count`")) && isset($countRes[0]['count'])){
$info['countAll'] = (int)$countRes[0]['count'];
}
if($info['countChunk'] = count($rows)){
$placeholderStr = function(string $str) : string {
return ':' . $str;
};
$updateRule = function(string $str) : string {
return $str . " = VALUES(" . $str . ")";
};
$universeDB->begin();
foreach($rows as $row){
$info['count']++;
$info['offset']++;
if(!$row['jumpNodes']){
// should never happen!
continue;
}
$pfDB->exec("
INSERT INTO
system_neighbour(
regionId,
constellationId,
systemName,
systemId,
jumpNodes,
trueSec
)
VALUES(
:regionId,
:constellationId,
:systemName,
:systemId,
:jumpNodes,
:trueSec
)",
[
':regionId' => $row['regionId'],
':constellationId' => $row['constellationId'],
':systemName' => $row['systemName'],
':systemId' => $row['systemId'],
':jumpNodes' => $row['jumpNodes'],
':trueSec' => $row['trueSec']
]
);
$columns = array_keys($row);
$columnsQuoted = array_map($universeDB->quotekey, $columns);
$placeholder = array_map($placeholderStr, $columns);
$args = array_combine($placeholder, $row);
$updateSql = array_map($updateRule, $columns);
$sql = "INSERT INTO
system_neighbour(" . implode(', ', $columnsQuoted) . ")
VALUES(" . implode(', ', $placeholder) . ")
ON DUPLICATE KEY UPDATE
" . implode(', ', $updateSql);
$universeDB->exec($sql, $args);
}
$universeDB->commit();
}
return $info;
}
}

View File

@@ -111,7 +111,7 @@ class System extends Controller\AccessController {
}
/**
* set destination for specific systemIds
* set destination for system, station or structure
* @param \Base $f3
* @throws \Exception
*/
@@ -120,25 +120,25 @@ class System extends Controller\AccessController {
$return = (object) [];
$return->error = [];
$return->systemData = [];
$return->destData = [];
if( !empty($postData['systemData'] )){
if(!empty($destData = (array)$postData['destData'])){
$activeCharacter = $this->getCharacter();
$return->clearOtherWaypoints = (bool)$postData['clearOtherWaypoints'];
$return->first = (bool)$postData['first'];
if( $accessToken = $activeCharacter->getAccessToken() ){
if($accessToken = $activeCharacter->getAccessToken()){
$options = [
'clearOtherWaypoints' => $return->clearOtherWaypoints,
'addToBeginning' => $return->first,
];
foreach($postData['systemData'] as $systemData){
$response = $f3->ccpClient()->setWaypoint($systemData['systemId'], $accessToken, $options);
foreach($destData as $data){
$response = $f3->ccpClient()->setWaypoint((int)$data['id'], $accessToken, $options);
if(empty($response)){
$return->systemData[] = $systemData;
$return->destData[] = $data;
}else{
$error = (object) [];
$error->type = 'error';

View File

@@ -112,7 +112,7 @@ class Universe extends Controller\AccessController {
$constellation = Model\Universe\AbstractUniverseModel::getNew('ConstellationModel');
$constellation->getById($constellationId);
if( !$constellation->dry() && $constellation->systems){
if($constellation->valid() && $constellation->systems){
/**
* @var Model\Universe\SystemModel $system
*/

View File

@@ -128,7 +128,7 @@ class User extends Controller\Controller{
// character is valid and allowed to login
$return->character = reset($characters)->getData();
// get Session status for character
if($activeCharacter = $this->getCharacter()){
if($activeCharacter = $this->getCharacter(0)){
if($activeUser = $activeCharacter->getUser()){
if($sessionCharacterData = $activeUser->findSessionCharacterData($return->character->id)){
$return->character->hasActiveSession = true;

View File

@@ -81,7 +81,7 @@ class Sso extends Api\User{
// check if character is valid and exists
if(
!$character->dry() &&
$character->valid() &&
$character->hasUserCharacter() &&
($activeCharacter->getUser()->_id === $character->getUser()->_id)
){
@@ -217,9 +217,9 @@ class Sso extends Api\User{
$characterModel = $characterModel->updateLog();
// connect character with current user
if( is_null($user = $this->getUser()) ){
if(is_null($user = $this->getUser())){
// connect character with existing user (no changes)
if( is_null( $user = $characterModel->getUser()) ){
if(is_null($user = $characterModel->getUser())){
// no user found (new character) -> create new user and connect to character
/**
* @var $user Pathfinder\UserModel
@@ -234,7 +234,7 @@ class Sso extends Api\User{
* @var $userCharactersModel Pathfinder\UserCharacterModel
*/
if( is_null($userCharactersModel = $characterModel->userCharacter) ){
$userCharactersModel = Pathfinder\AbstractPathfinderModel::getNew('UserCharacterModel');
$userCharactersModel = $characterModel->rel('userCharacter');
$userCharactersModel->characterId = $characterModel;
}

View File

@@ -53,6 +53,25 @@ class Universe extends Controller {
}
}*/
/**
* setup categories + all dependencies (e.g. groups, types)
* id 2 -> Celestial (>100 groups -> >1000 types)
* id 6 -> Ship (46 groups -> 4xx types)
* id 65 -> Structure (10 groups -> 33 types)
* @param array $categoriesWhitelist
* @return array
* @throws \Exception
*/
protected function setupCategories(array $categoriesWhitelist = []) : array {
$info = [];
$categoryIds = Model\Universe\CategoryModel::getUniverseCategories();
$categoryIds = array_intersect($categoriesWhitelist, $categoryIds);
foreach($categoryIds as $categoryId){
$info[$categoryId] = $this->setupCategory($categoryId);
}
return $info;
}
/**
* setup category + all dependencies (e.g. groups, types)
* -> $length = 0 -> setup all groups
@@ -63,41 +82,18 @@ class Universe extends Controller {
* @throws \Exception
*/
public function setupCategory(int $categoryId, int $offset = 0, int $length = 0) : array {
$return = [];
$info = ['countAll' => 0, 'countChunk' => 0, 'count' => 0, 'offset' => $offset, 'groupTypes' => []];
if($categoryId){
/**
* @var $category Model\Universe\CategoryModel
*/
$category = Model\Universe\AbstractUniverseModel::getNew('CategoryModel');
$category->loadById($categoryId);
$groupIds = $category->loadGroupsData($offset, $length);
foreach((array)$category->groups as $group){
// only load types for changed groups (not all)
if(in_array($group->_id, $groupIds)){
$return[$group->_id] = $group->loadTypesData();
}
}
$info = $category->loadGroupsData($offset, $length);
}
return $return;
}
/**
* setup categories + all dependencies (e.g. groups, types)
* id 2 -> Celestial (>100 groups -> >1000 types)
* id 6 -> Ship (45 groups -> 490 types)
* id 65 -> Structure (10 groups -> 33 types)
* @param array $categoriesWhitelist
* @return array
* @throws \Exception
*/
protected function setupCategories(array $categoriesWhitelist = []) : array {
$return = [];
$categoryIds = $this->getF3()->ccpClient()->getUniverseCategories();
$categoryIds = array_intersect($categoriesWhitelist, $categoryIds);
foreach($categoryIds as $categoryId){
$return[$categoryId] = $this->setupCategory($categoryId);
}
return $return;
return $info;
}
/**
@@ -111,19 +107,38 @@ class Universe extends Controller {
* @throws \Exception
*/
protected function setupGroups(array $groupsWhitelist = []) : array {
$return = [];
$groupIds = $this->getF3()->ccpClient()->getUniverseGroups();
$info = [];
$groupIds = Model\Universe\GroupModel::getUniverseGroups();
$groupIds = array_intersect($groupsWhitelist, $groupIds);
/**
* @var $group Model\Universe\GroupModel
*/
$group = Model\Universe\AbstractUniverseModel::getNew('GroupModel');
foreach($groupIds as $groupId){
$group->loadById($groupId);
$return[$group->_id] = $group->loadTypesData();
$group->reset();
$info[$groupId] = $this->setupGroup($groupId);
}
return $return;
return $info;
}
/**
* setup group + all dependencies (e.g. types)
* @param int $groupId
* @param int $offset
* @param int $length
* @param bool $storeDogmaAttributes
* @return array
* @throws \Exception
*/
public function setupGroup(int $groupId, int $offset = 0, int $length = 0, bool $storeDogmaAttributes = false) : array {
$info = ['countAll' => 0, 'countChunk' => 0, 'count' => 0, 'offset' => $offset];
if($groupId){
/**
* @var $group Model\Universe\GroupModel
*/
$group = Model\Universe\AbstractUniverseModel::getNew('GroupModel');
$group->storeDogmaAttributes = $storeDogmaAttributes;
$group->loadById($groupId);
$info = $group->loadTypesData($offset, $length);
}
return $info;
}
// system search index methods ====================================================================================
@@ -146,10 +161,11 @@ class Universe extends Controller {
$system = Model\Universe\AbstractUniverseModel::getNew('SystemModel');
$indexData = [];
foreach($systemIds as $systemId){
$system->getById($systemId);
$system->getById($systemId, 0);
if($hashKeyId = $system->getHashKey()){
$indexData[$hashKeyId] = $system->getData();
}
$system->reset();
// offset must increase otherwise we get a endless loop
// -> see /setup ajax build loop function
$offset++;
@@ -165,12 +181,14 @@ class Universe extends Controller {
/**
* get systemIds for all systems
* @param bool $ignoreCache
* @return array
* @throws \Exception
*/
public function getSystemIds() : array {
public function getSystemIds(bool $ignoreCache = false) : array {
$f3 = $this->getF3();
if( !$f3->exists(self::SESSION_KEY_SYSTEM_IDS, $systemIds) ){
$systemIds = [];
if($ignoreCache || !$f3->exists(self::SESSION_KEY_SYSTEM_IDS, $systemIds)){
/**
* @var $system Model\Universe\SystemModel
*/
@@ -184,7 +202,7 @@ class Universe extends Controller {
}
}
return (array)$systemIds;
return $systemIds ? : [];
}
/**
@@ -225,20 +243,20 @@ class Universe extends Controller {
* @return null|\stdClass
* @throws \Exception
*/
public function getSystemData(int $systemId){
public function getSystemData(int $systemId) : ?\stdClass {
$data = null;
if($systemId){
// ...check index for data
$cacheKeyRow = Model\Universe\AbstractUniverseModel::generateHashKeyRow('system', $systemId);
$data = $this->get($cacheKeyRow);
if(!$data){
if(!$data = $this->get($cacheKeyRow)){
// .. try to build index
/**
* @var $system Model\Universe\SystemModel
*/
$system = Model\Universe\AbstractUniverseModel::getNew('SystemModel');
$system->getById($systemId);
$data = $system->buildIndex();
if($system->getById($systemId)){
$data = $system->buildIndex();
}
}
}
return $data;
@@ -249,7 +267,7 @@ class Universe extends Controller {
* @param string $cacheKey
* @return null|\stdClass
*/
private function get(string $cacheKey){
private function get(string $cacheKey) : ?\stdClass {
$data = null;
if($this->getF3()->exists($cacheKey,$value)) {
if(is_string($value) && strpos($value, Model\Universe\AbstractUniverseModel::CACHE_KEY_PREFIX) === 0) {

View File

@@ -16,6 +16,7 @@ use lib\db\SQL;
use lib\Resource;
use lib\Monolog;
use lib\Util;
use Model\AbstractModel;
use Model\Pathfinder;
use DB;
@@ -381,13 +382,14 @@ class Controller {
}
/**
* get current character data from session
* @return array
* get current character from session data
* @param int $ttl
* @return Pathfinder\CharacterModel|null
* @throws \Exception
*/
public function getSessionCharacterData() : array {
$data = [];
if($user = $this->getUser()){
protected function getSessionCharacter(int $ttl = AbstractModel::DEFAULT_SQL_TTL) : ?Pathfinder\CharacterModel {
$character = null;
if($user = $this->getUser($ttl)){
$header = self::getRequestHeaders();
$requestedCharacterId = (int)$header['Pf-Character'];
if( !$this->getF3()->get('AJAX') ){
@@ -400,10 +402,10 @@ class Controller {
}
}
$data = $user->getSessionCharacterData($requestedCharacterId);
$character = $user->getSessionCharacter($requestedCharacterId, $ttl);
}
return $data;
return $character;
}
/**
@@ -412,24 +414,8 @@ class Controller {
* @return Pathfinder\CharacterModel|null
* @throws \Exception
*/
public function getCharacter(int $ttl = 0) : ?Pathfinder\CharacterModel {
$character = null;
if(!empty($characterData = $this->getSessionCharacterData())){
/**
* @var $characterModel Pathfinder\CharacterModel
*/
$characterModel = Pathfinder\AbstractPathfinderModel::getNew('CharacterModel');
$characterModel->getById((int)$characterData['ID'], $ttl);
if(
!$characterModel->dry() &&
$characterModel->hasUserCharacter()
){
$character = &$characterModel;
}
}
return $character;
public function getCharacter(int $ttl = AbstractModel::DEFAULT_SQL_TTL) : ?Pathfinder\CharacterModel {
return $this->getSessionCharacter($ttl);
}
/**
@@ -438,7 +424,7 @@ class Controller {
* @return Pathfinder\UserModel|null
* @throws \Exception
*/
public function getUser($ttl = 0) : ?Pathfinder\UserModel {
public function getUser($ttl = AbstractModel::DEFAULT_SQL_TTL) : ?Pathfinder\UserModel {
$user = null;
if($this->getF3()->exists(Api\User::SESSION_KEY_USER_ID, $userId)){
@@ -452,7 +438,7 @@ class Controller {
!$userModel->dry() &&
$userModel->hasUserCharacters()
){
$user = &$userModel;
$user = $userModel;
}
}
@@ -534,10 +520,11 @@ class Controller {
* @throws \Exception
*/
public function getEveServerStatus(\Base $f3){
$ttl = 60;
$esiStatusVersion = 'latest';
$cacheKey = 'eve_server_status';
if( !$f3->exists($cacheKey, $return) ){
if(!$exists = $f3->exists($cacheKey, $return)){
$return = (object) [];
$return->error = [];
@@ -593,10 +580,10 @@ class Controller {
// find top status
$status = 'OK';
$color = 'green';
foreach($apiStatus['status'] as $statusData){
foreach($apiStatus['status'] as &$statusData){
if('red' == $statusData['status']){
$status = 'unstable';
$color = $statusData['status'];
$color = $statusData['status'] = 'orange'; // red is already in use for fatal API errors (e.g. no response at all, or offline)
break;
}
if('yellow' == $statusData['status']){
@@ -613,11 +600,15 @@ class Controller {
}
if(empty($return->error)){
$f3->set($cacheKey, $return, 60);
$f3->set($cacheKey, $return, $ttl);
}
}
}
if(empty($return->error)){
$f3->expire(Config::ttlLeft($exists, $ttl));
}
echo json_encode($return);
}

View File

@@ -118,13 +118,13 @@ class LogController extends \Prefab {
$updateSql = array_map($updateRule, $columnsForUpdate);
$sql = "INSERT DELAYED INTO
activity_log (" . implode(', ', $columnsQuoted) . ") values(
" . implode(', ', $placeholder) . "
)
ON DUPLICATE KEY UPDATE
updated = NOW(),
" . implode(', ', $updateSql) . "
";
activity_log (" . implode(', ', $columnsQuoted) . ") VALUES(
" . implode(', ', $placeholder) . "
)
ON DUPLICATE KEY UPDATE
updated = NOW(),
" . implode(', ', $updateSql) . "
";
$db->exec($sql, $args);
}

View File

@@ -67,7 +67,6 @@ class Setup extends Controller {
'Model\Pathfinder\MapTypeModel',
'Model\Pathfinder\SystemTypeModel',
'Model\Pathfinder\SystemStatusModel',
'Model\Pathfinder\SystemNeighbourModel',
'Model\Pathfinder\RightModel',
'Model\Pathfinder\RoleModel',
'Model\Pathfinder\StructureModel',
@@ -105,19 +104,27 @@ class Setup extends Controller {
'UNIVERSE' => [
'info' => [],
'models' => [
'Model\Universe\DogmaAttributeModel',
'Model\Universe\TypeAttributeModel',
'Model\Universe\TypeModel',
'Model\Universe\GroupModel',
'Model\Universe\CategoryModel',
'Model\Universe\FactionModel',
'Model\Universe\AllianceModel',
'Model\Universe\CorporationModel',
'Model\Universe\RaceModel',
'Model\Universe\StationModel',
'Model\Universe\StructureModel',
'Model\Universe\WormholeModel',
'Model\Universe\StargateModel',
'Model\Universe\StarModel',
'Model\Universe\PlanetModel',
'Model\Universe\SystemModel',
'Model\Universe\ConstellationModel',
'Model\Universe\RegionModel',
'Model\Universe\SystemStaticModel'
'Model\Universe\SystemNeighbourModel',
'Model\Universe\SystemStaticModel',
'Model\Universe\SovereigntyMapModel',
'Model\Universe\FactionWarSystemModel'
]
]
];
@@ -1665,18 +1672,111 @@ class Setup extends Controller {
* @var $categoryUniverseModel Universe\CategoryModel
*/
$categoryUniverseModel = Universe\AbstractUniverseModel::getNew('CategoryModel');
$categoryUniverseModel->getById(65, 0);
$structureCount = $categoryUniverseModel->getTypesCount(false);
$categoryUniverseModel->getById(Config::ESI_CATEGORY_STRUCTURE_ID, 0);
$groupsCountStructure = $categoryUniverseModel->getGroupsCount(false);
$typesCountStructure = $categoryUniverseModel->getTypesCount(false);
$categoryUniverseModel->getById(6, 0);
$shipCount = $categoryUniverseModel->getTypesCount(false);
$categoryUniverseModel->getById(Config::ESI_CATEGORY_SHIP_ID, 0);
$groupsCountShip = $categoryUniverseModel->getGroupsCount(false);
$typesCountShip = $categoryUniverseModel->getTypesCount(false);
/**
* @var $systemNeighbourModel Pathfinder\SystemNeighbourModel
* @var $groupUniverseModel Universe\GroupModel
*/
$systemNeighbourModel = Pathfinder\AbstractPathfinderModel::getNew('SystemNeighbourModel');
$groupUniverseModel = Universe\AbstractUniverseModel::getNew('GroupModel');
$groupUniverseModel->getById(Config::ESI_GROUP_WORMHOLE_ID, 0);
$wormholeCount = $groupUniverseModel->getTypesCount(false);
/**
* @var $systemNeighbourModel Universe\SystemNeighbourModel
*/
$systemNeighbourModel = Universe\AbstractUniverseModel::getNew('SystemNeighbourModel');
/**
* @var $systemStaticModel Universe\SystemStaticModel
*/
$systemStaticModel = Universe\AbstractUniverseModel::getNew('SystemStaticModel');
if(empty($systemCountAll = count(($universeController = new UniverseController())->getSystemIds(true)))){
// no systems found in 'universe' DB. Clear potential existing system cache
$universeController->clearSystemsIndex();
}
$sum = function(int $carry, int $value) : int {
return $carry + $value;
};
$indexInfo = [
'Wormholes' => [
'task' => [
[
'action' => 'buildIndex',
'label' => 'Import',
'icon' => 'fa-sync',
'btn' => 'btn-primary'
]
],
'label' => 'Wormholes data',
'countBuild' => $wormholeCount,
'countAll' => count(Universe\GroupModel::getUniverseGroupTypes(Config::ESI_GROUP_WORMHOLE_ID)),
'tooltip' => 'import all wormhole types (e.g. L031) from ESI. Runtime: ~25s'
],
'Structures' => [
'task' => [
[
'action' => 'buildIndex',
'label' => 'Import',
'icon' => 'fa-sync',
'btn' => 'btn-primary'
]
],
'label' => 'Structures data',
'countBuild' => $groupsCountStructure,
'countAll' => count(Universe\CategoryModel::getUniverseCategoryGroups(Config::ESI_CATEGORY_STRUCTURE_ID)),
'tooltip' => 'import all structure types (e.g. Citadels) from ESI. Runtime: ~15s',
'subCount' => [
'countBuild' => $typesCountStructure,
'countAll' => array_reduce(array_map('count', Universe\CategoryModel::getUniverseCategoryTypes(Config::ESI_CATEGORY_STRUCTURE_ID)), $sum, 0),
]
],
'Ships' => [
'task' => [
[
'action' => 'buildIndex',
'label' => 'Import',
'icon' => 'fa-sync',
'btn' => 'btn-primary'
]
],
'label' => 'Ships data',
'countBuild' => $groupsCountShip,
'countAll' => count(Universe\CategoryModel::getUniverseCategoryGroups(Config::ESI_CATEGORY_SHIP_ID)),
'tooltip' => 'import all ships from ESI. Runtime: ~2min',
'subCount' => [
'countBuild' => $typesCountShip,
'countAll' => array_reduce(array_map('count', Universe\CategoryModel::getUniverseCategoryTypes(Config::ESI_CATEGORY_SHIP_ID)), $sum, 0),
]
],
'SystemStatic' => [
'task' => [
[
'action' => 'buildIndex',
'label' => 'Import',
'icon' => 'fa-sync',
'btn' => 'btn-primary'
]
],
'label' => 'Wormhole statics data',
'countBuild' => $systemStaticModel->getRowCount(),
'countAll' => 3772,
'tooltip' => 'import all static wormholes for systems. Runtime: ~25s'
],
[
'label' => 'Build search index',
'icon' => 'fa-search',
'tooltip' => 'Search indexes are build from static EVE universe data (e.g. systems, stargate connections,…). Re-build if underlying data was updated.'
],
'Systems' => [
'task' => [
[
@@ -1691,81 +1791,36 @@ class Setup extends Controller {
'btn' => 'btn-primary'
]
],
'label' => 'build systems index',
'countBuild' => count((new UniverseController())->getSystemsIndex()),
'countAll' => count((new UniverseController())->getSystemIds()),
'tooltip' => 'build up a static search index over all systems found on DB. Do not refresh page until import is complete (check progress)! Runtime: ~5min'
],
'Structures' => [
'task' => [
[
'action' => 'buildIndex',
'label' => 'Import',
'icon' => 'fa-sync',
'btn' => 'btn-primary'
]
],
'label' => 'import structures data',
'countBuild' => $structureCount,
'countAll' => (int)$f3->get('REQUIREMENTS.DATA.STRUCTURES'),
'tooltip' => 'import all structure types (e.g. Citadels) from ESI. Runtime: ~15s'
],
'Ships' => [
'task' => [
[
'action' => 'buildIndex',
'label' => 'Import',
'icon' => 'fa-sync',
'btn' => 'btn-primary'
]
],
'label' => 'import ships data',
'countBuild' => $shipCount,
'countAll' => (int)$f3->get('REQUIREMENTS.DATA.SHIPS'),
'tooltip' => 'import all ships types from ESI. Runtime: ~2min'
'label' => 'Systems data index',
'countBuild' => count($universeController->getSystemsIndex()),
'countAll' => $systemCountAll,
'tooltip' => 'Build up a static search index over all systems, found on DB. Runtime: ~5min'
],
'SystemNeighbour' => [
'task' => [
[
'action' => 'clearIndex',
'label' => 'Clear',
'icon' => 'fa-trash',
'btn' => 'btn-danger'
],[
'action' => 'buildIndex',
'label' => 'Build',
'icon' => 'fa-sync',
'btn' => 'btn-primary'
]
],
'label' => 'build neighbour index',
'countBuild' => $f3->DB->getDB('PF')->getRowCount($systemNeighbourModel->getTable()),
'label' => 'Systems neighbour index',
'countBuild' => $systemNeighbourModel->getRowCount(),
'countAll' => (int)$f3->get('REQUIREMENTS.DATA.NEIGHBOURS'),
'tooltip' => 'build up a static search index for route search. This is used as fallback in case ESI is down. Runtime: ~30s'
],
// All following rows become deprecated
/*
'WormholeModel' => [
'task' => [
[
'action' => 'exportTable',
'label' => 'Export',
'icon' => 'fa-download',
'btn' => 'btn-default'
],[
'action' => 'importTable',
'label' => 'Import',
'icon' => 'fa-upload',
'btn' => 'btn-primary'
]
],
'label' => 'wormhole',
'countBuild' => $f3->DB->getDB('PF')->getRowCount($wormholeModel->getTable()),
'countAll' => 89
'tooltip' => 'Build up a static search index for route search. This is used as fallback in case ESI is down. Runtime: ~10s'
]
*/
];
}else{
$indexInfo = [
'SystemNeighbour' => [
'task' => [],
'label' => 'Fix database errors first!'
[
'label' => 'Fix database errors first!',
'class' => 'txt-color-danger text-center'
]
];
}

View File

@@ -10,13 +10,21 @@ namespace cron;
abstract class AbstractCron {
// default max_execution_time for cronJobs
/**
* default max_execution_time for cronJobs
// -> should be less then execution period
*/
const DEFAULT_MAX_EXECUTION_TIME = 50;
/**
* set max execution time for cronjobs
* -> Default CLI execution time is "0" -> infinite!
* default threshold time in seconds before a running script (e.g. a large loop) should stop
* -> so there is some time or e.g. logging,... left
*/
const DEFAULT_EXECUTION_TIME_THRESHOLD = 3;
/**
* set max execution time for cronJbs
* -> Default CLI execution time is 0 == infinite!
* php.ini settings are ignored! http://php.net/manual/en/info.configuration.php#ini.max-execution-time
* @param int $time
*/
@@ -24,4 +32,33 @@ abstract class AbstractCron {
ini_set('max_execution_time', $time);
}
/**
* get max execution time
* -> 0 means == infinite!
* @return int
*/
protected function getMaxExecutionTime() : int {
return (int)ini_get('max_execution_time');
}
/**
* checks execution time of a "long" running script
* -> returns false if execution time is close to maxExecutionTime
* @param float $timeTotalStart
* @param float|null $timeCheck
* @param int $timeThreshold
* @return bool
*/
protected function isExecutionTimeLeft(float $timeTotalStart, float $timeCheck = null, int $timeThreshold = self::DEFAULT_EXECUTION_TIME_THRESHOLD) : bool {
$timeLeft = true;
if($timeTotalMax = $this->getMaxExecutionTime()){
$timeTotalMaxThreshold = $timeTotalStart + $timeTotalMax - $timeThreshold;
$timeCheck = $timeCheck ? : microtime(true);
if($timeCheck >= $timeTotalMaxThreshold){
$timeLeft = false;
}
}
return $timeLeft;
}
}

View File

@@ -65,7 +65,7 @@ class CharacterUpdate extends AbstractCron {
*/
if(is_object($characterLog->characterId)){
if($accessToken = $characterLog->characterId->getAccessToken()){
if($this->isOnline($accessToken)){
if($characterLog->characterId->isOnline($accessToken)){
// force characterLog as "updated" even if no changes were made
$characterLog->touch('updated');
$characterLog->save();

View File

@@ -13,6 +13,7 @@ use Model;
class Universe extends AbstractCron {
const LOG_TEXT = '%s type: %s %s/%s peak: %s total: %s msg: %s';
const LOG_TEXT_SOV_FW = '%s %4s/%-4s checked, %s peak, %s total, %4s updated [%4s sovChanges, %4s fwChanges], msg: %s';
/**
@@ -133,10 +134,11 @@ class Universe extends AbstractCron {
* @param float $timeTotalStart
*/
private function echoLoaded(int $importCount, int $id, float $timeLoopStart, float $timeTotalStart){
$time = microtime(true);
echo '[' . date('H:i:s') . '] loaded ' . str_pad('', strlen($importCount), ' ') . ' id: ' . $this->formatIdValue($id) .
' memory: ' . $this->formatMemoryValue(memory_get_usage()) .
' time: ' . $this->formatSeconds(microtime(true) - $timeLoopStart) .
' total: ' . $this->formatSeconds(microtime(true) - $timeTotalStart) . PHP_EOL;
' time: ' . $this->formatSeconds($time - $timeLoopStart) .
' total: ' . $this->formatSeconds($time - $timeTotalStart) . PHP_EOL;
$this->echoFlush();
}
@@ -169,8 +171,8 @@ class Universe extends AbstractCron {
$msg = '';
$ids = [];
$importCount = 0;
$count = 0;
$importCount = [];
$modelClass = '';
$setupModel = function(Model\Universe\AbstractUniverseModel &$model, int $id){};
@@ -193,12 +195,51 @@ class Universe extends AbstractCron {
$model->loadStargatesData();
};
break;
case 'station':
$ids = $f3->ccpClient()->getUniverseSystems();
$modelClass = 'SystemModel';
$setupModel = function(Model\Universe\SystemModel &$model, int $id){
if($model->getById($id)){
$model->loadStationsData();
}else{
echo 'NOT VALID ' . $id . PHP_EOL;
die();
}
};
break;
case 'sovereignty':
// load sovereignty map data. Systems must be present first!
$sovData = $f3->ccpClient()->getSovereigntyMap();
$ids = !empty($sovData = $sovData['map']) ? array_keys($sovData): [];
$modelClass = 'SystemModel';
$setupModel = function(Model\Universe\SystemModel &$model, int $id) use ($sovData) {
if($model->getById($id)){
$model->updateSovereigntyData($sovData[$id]);
}else{
echo 'NOT VALID ' . $id . PHP_EOL;
die();
}
};
break;
case 'faction_war_systems':
$fwSystems = $f3->ccpClient()->getFactionWarSystems();
$ids = !empty($fwSystems = $fwSystems['systems']) ? array_keys($fwSystems): [];
$modelClass = 'SystemModel';
$setupModel = function(Model\Universe\SystemModel &$model, int $id) use ($fwSystems) {
if($model->getById($id)){
$model->updateFactionWarData($fwSystems[$id]);
}else{
echo 'NOT VALID ' . $id . PHP_EOL;
die();
}
};
break;
case 'index_system':
// setup system index, Systems must be present first!
$ids = $f3->ccpClient()->getUniverseSystems();
$modelClass = 'SystemModel';
$setupModel = function(Model\Universe\SystemModel &$model, int $id){
$model->getById($id); // no loadById() here! would take "forever" when system not exists and build up first...
$model->getById($id); // no loadById() here! would take "forever" when system not exists and must be build up first...
$model->buildIndex();
};
break;
@@ -216,6 +257,7 @@ class Universe extends AbstractCron {
sort($ids, SORT_NUMERIC);
$ids = array_slice($ids, $offset, $length);
$importCount = count($ids);
$count = 0;
$this->echoInfo($total, $offset, $importCount, $ids);
$this->echoStart();
@@ -237,9 +279,83 @@ class Universe extends AbstractCron {
// Log --------------------------------------------------------------------------------------------------------
$log = new \Log('cron_' . __FUNCTION__ . '.log');
$log->write( sprintf(self::LOG_TEXT, __FUNCTION__, $type,
$log->write(sprintf(self::LOG_TEXT, __FUNCTION__, $type,
$this->formatCounterValue($count), $importCount, $this->formatMemoryValue(memory_get_peak_usage ()),
$this->formatSeconds(microtime(true) - $timeTotalStart), $msg) );
$this->formatSeconds(microtime(true) - $timeTotalStart), $msg));
}
/**
* update Sovereignty system data from ESI
* -> this updates Faction warfare data as well
* >> php index.php "/cron/updateSovereigntyData"
* @param \Base $f3
* @throws \Exception
*/
function updateSovereigntyData(\Base $f3){
$this->setMaxExecutionTime();
$timeTotalStart = microtime(true);
$msg = '';
/**
* @var $system Model\Universe\SystemModel
*/
$system = Model\Universe\AbstractUniverseModel::getNew('SystemModel');
$sovData = $f3->ccpClient()->getSovereigntyMap();
$fwSystems = $f3->ccpClient()->getFactionWarSystems();
$fwSystems = $fwSystems['systems'];
$ids = !empty($sovData = $sovData['map']) ? array_keys($sovData): [];
sort($ids, SORT_NUMERIC);
$importCount = count($ids);
$count = 0;
$changes = [];
foreach($ids as $id){
$count++;
// skip wormhole systems -> can not have sov data
// -> even though they are returned from sovereignty/map endpoint?!
if(
$system->getById($id, 0) &&
strpos($system->security, 'C') === false
){
if($changedSovData = $system->updateSovereigntyData($sovData[$id])){
$changes['sovereignty'][] = $id;
}
$changedFwData = false;
if(is_array($fwSystems[$id])){
if($changedFwData = $system->updateFactionWarData($fwSystems[$id])){
$changes['factionWarfare'][] = $id;
}
}
if($changedSovData || $changedFwData){
$system->buildIndex();
}
}
$system->reset();
// stop loop if runtime gets close to "max_execution_time"
// -> we need some time for writing *.log file
if(!$this->isExecutionTimeLeft($timeTotalStart)){
$msg = 'Script execution stopped due to "max_execution_time" limit reached';
// TODO store current loop index and use it as new "offset" for next call
break;
}
}
$changedIds = array_reduce($changes, function(array $reducedIds, array $changedIds) : array {
return array_unique(array_merge($reducedIds, $changedIds));
}, []);
// Log ------------------------
$log = new \Log('cron_' . __FUNCTION__ . '.log');
$log->write(sprintf(self::LOG_TEXT_SOV_FW, __FUNCTION__,
$count, $importCount, $this->formatMemoryValue(memory_get_peak_usage ()),
$this->formatSeconds(microtime(true) - $timeTotalStart),
count($changedIds), count($changes['sovereignty'] ? : []), count($changes['factionWarfare'] ? : []),
$msg));
}
/**
@@ -251,9 +367,12 @@ class Universe extends AbstractCron {
*/
function updateUniverseSystems(\Base $f3){
$this->setMaxExecutionTime();
$system = Model\Universe\AbstractUniverseModel::getNew('SystemModel');
$systems = $system->find( null, ['order' => 'updated', 'limit' => 2]);
/**
* @var $systemModel Model\Universe\SystemModel
* @var $system Model\Universe\SystemModel
*/
$systemModel = Model\Universe\AbstractUniverseModel::getNew('SystemModel');
$systems = $systemModel->find( null, ['order' => 'updated', 'limit' => 2]);
if($systems){
foreach ($systems as $system){
$system->updateModel();

View File

@@ -0,0 +1,124 @@
<?php
namespace lib;
class PriorityCacheStore {
/**
* default max entry limit before store gets truncated
*/
const DEFAULT_ENTRY_LIMIT = 100;
/**
* default cleanup interval
* -> truncate store after 10 inserts. Max store entries:
* DEFAULT_ENTRY_LIMIT + DEFAULT_CLEANUP_INTERVAL - 1
*/
const DEFAULT_CLEANUP_INTERVAL = 10;
/**
* @var int
*/
protected $entryLimit;
/**
* @var int
*/
protected $cleanupInterval;
/**
* @var array
*/
protected $store;
/**
* @var \SplPriorityQueue
*/
protected $priorityQueue;
/**
* @var int
*/
protected $priority = 0;
/**
* PriorityCacheStore constructor.
* @param int $entryLimit
* @param int $cleanupInterval
*/
function __construct(int $entryLimit = self::DEFAULT_ENTRY_LIMIT, int $cleanupInterval = self::DEFAULT_CLEANUP_INTERVAL){
$this->cleanupInterval = $cleanupInterval;
$this->entryLimit = $entryLimit;
$this->store = [];
$this->priorityQueue = new \SplPriorityQueue ();
$this->priorityQueue->setExtractFlags(\SplPriorityQueue::EXTR_BOTH);
}
/**
* @param $key
* @param $data
*/
public function set($key, $data){
if(!$this->exists($key)){
$this->priorityQueue->insert($key, $this->priority--);
}
$this->store[$key] = $data;
// check cleanup interval and cleanup Store
$this->cleanupInterval();
}
/**
* @param $key
* @return mixed|null
*/
public function get($key){
return $this->exists($key) ? $this->store[$key] : null;
}
/**
* @param $key
* @return bool
*/
public function exists($key){
return isset($this->store[$key]);
}
public function cleanupInterval() : void {
if(
!$this->priorityQueue->isEmpty() && $this->cleanupInterval &&
($this->priorityQueue->count() % $this->cleanupInterval === 0)
){
$this->cleanup();
}
}
public function cleanup(){
while(
$this->entryLimit < $this->priorityQueue->count() &&
$this->priorityQueue->valid()
){
if($this->exists($key = $this->priorityQueue->extract()['data'])){
unset($this->store[$key]);
}
}
}
public function clear(){
$limit = $this->entryLimit;
$this->entryLimit = 0;
$this->cleanup();
// restore entryLimit for next data
$this->entryLimit = $limit;
}
/**
* @return string
*/
public function __toString(){
return 'Store count: ' . count($this->store) . ' priorityQueue count: ' . $this->priorityQueue->count();
}
}

View File

@@ -82,6 +82,30 @@ class Config extends \Prefab {
*/
const DOWNTIME_BUFFER = 1;
// ================================================================================================================
// ESI API id´s
// ================================================================================================================
/**
* ESI categoryId or 'Structure's
*/
const ESI_CATEGORY_STRUCTURE_ID = 65;
/**
* ESI categoryId or 'Ship's
*/
const ESI_CATEGORY_SHIP_ID = 6;
/**
* ESI groupId or 'Wormhole's
*/
const ESI_GROUP_WORMHOLE_ID = 988;
/**
* ESI dogmaAttributeId for 'scanWormholeStrength's
*/
const ESI_DOGMA_ATTRIBUTE_SCANWHSTRENGTH_ID = 1908;
/**
* error message for missing Composer dependency class
*/
@@ -166,7 +190,7 @@ class Config extends \Prefab {
* @return array|null
*/
protected function getAllEnvironmentData(\Base $f3){
if( !$f3->exists(self::HIVE_KEY_ENVIRONMENT, $environmentData) ){
if(!$f3->exists(self::HIVE_KEY_ENVIRONMENT, $environmentData)){
$environmentData = $this->setAllEnvironmentData($f3);
}
@@ -320,7 +344,7 @@ class Config extends \Prefab {
if($config['SCHEME'] == 'mysql'){
$options[\PDO::MYSQL_ATTR_COMPRESS] = true;
$options[\PDO::MYSQL_ATTR_INIT_COMMAND] = implode(',', [
"SET NAMES " . strtolower(str_replace('-','', $f3->ENCODING)),
"SET NAMES " . self::getRequiredDbVars($f3, $config['SCHEME'])['CHARACTER_SET_CONNECTION'] . " COLLATE " . self::getRequiredDbVars($f3, $config['SCHEME'])['COLLATION_CONNECTION'],
"@@session.time_zone = '+00:00'",
"@@session.default_storage_engine = " . self::getRequiredDbVars($f3, $config['SCHEME'])['DEFAULT_STORAGE_ENGINE']
]);
@@ -616,4 +640,18 @@ class Config extends \Prefab {
return $format;
}
static function ttlLeft($fromExists, int $ttlMax) : int {
$ttlMax = max($ttlMax, 0);
if($fromExists){
// == true || array
if(is_array($fromExists)){
return max(min((int)ceil(round(array_sum($fromExists) - microtime(true), 4)), $ttlMax), 0);
}else{
return 0;
}
}else{
// == false
return $ttlMax;
}
}
}

View File

@@ -110,8 +110,8 @@ class Pool extends \Prefab {
$schema = new Schema($db);
if(!in_array($newDbName, $schema->getDatabases())){
$db->exec("CREATE DATABASE IF NOT EXISTS
`" . $newDbName . "` DEFAULT CHARACTER SET utf8
COLLATE utf8_general_ci;");
`" . $newDbName . "` DEFAULT CHARACTER SET utf8mb4
COLLATE utf8mb4_unicode_ci;");
$db->exec("USE `" . $newDbName . "`");
// check if DB create was successful

View File

@@ -51,12 +51,30 @@ class Util {
return $return;
}
/**
* transforms array with assoc. arrays as values
* into assoc. array where $key column data is used for its key
* @param array $array
* @param string $key
* @param bool $unsetKey
* @return array
*/
static function arrayGetBy(array $array, string $key, bool $unsetKey = true) : array {
// we can remove $key from nested arrays
return array_map(function($val) use ($key, $unsetKey) : array {
if($unsetKey){
unset($val[$key]);
}
return $val;
}, array_column($array, null, $key));
}
/**
* checks whether an array is associative or not (sequential)
* @param mixed $array
* @return bool
*/
static function is_assoc($array): bool {
static function is_assoc($array) : bool {
$isAssoc = false;
if(
is_array($array) &&
@@ -109,7 +127,7 @@ class Util {
* @param int $maxHideChars
* @return string
*/
static function obscureString(string $string, int $maxHideChars = 10): string {
static function obscureString(string $string, int $maxHideChars = 10) : string {
$formatted = '';
$length = mb_strlen((string)$string);
if($length > 0){
@@ -125,7 +143,7 @@ class Util {
* @param array $scopes
* @return string
*/
static function getHashFromScopes($scopes){
static function getHashFromScopes($scopes) : string {
$scopes = (array)$scopes;
sort($scopes);
return md5(serialize($scopes));

View File

@@ -11,6 +11,7 @@ namespace Model;
use DB\Cortex;
use DB\CortexCollection;
use DB\SQL\Schema;
use lib\Util;
use lib\logging;
use Controller;
use Exception\ValidationException;
@@ -44,6 +45,14 @@ abstract class AbstractModel extends Cortex {
*/
protected $addStaticFields = true;
/**
* enables table truncate
* -> see truncate();
* -> CAUTION! if set to true truncate() will clear ALL rows!
* @var bool
*/
protected $allowTruncate = false;
/**
* enables change for "active" column
* -> see setActive();
@@ -89,6 +98,23 @@ abstract class AbstractModel extends Cortex {
*/
const DEFAULT_CACHE_TTL = 120;
/**
* default TTL or temp table data read from *.csv file
* -> used during data import
*/
const DEFAULT_CACHE_CSV_TTL = 120;
/**
* cache key prefix name for "full table" indexing
* -> used e.g. for a "search" index; or "import" index for *.csv imports
*/
const CACHE_KEY_PREFIX = 'INDEX';
/**
* cache key name for temp data import from *.csv files per table
*/
const CACHE_KEY_CSV_PREFIX = 'CSV';
/**
* default TTL for SQL query cache
*/
@@ -495,7 +521,7 @@ abstract class AbstractModel extends Cortex {
* @return bool
*/
public function getById(int $id, int $ttl = self::DEFAULT_SQL_TTL, bool $isActive = true) : bool {
return $this->getByForeignKey('id', $id, ['limit' => 1], $ttl, $isActive);
return $this->getByForeignKey($this->primary, $id, ['limit' => 1], $ttl, $isActive);
}
/**
@@ -508,10 +534,7 @@ abstract class AbstractModel extends Cortex {
* @return bool
*/
public function getByForeignKey(string $key, $value, array $options = [], int $ttl = 0, bool $isActive = true) : bool {
$filters = [];
if($this->exists($key)){
$filters[] = [$key . ' = :' . $key, ':' . $key => $value];
}
$filters = [self::getFilter($key, $value)];
if($isActive && $this->exists('active')){
$filters[] = self::getFilter('active', true);
@@ -658,6 +681,24 @@ abstract class AbstractModel extends Cortex {
return true;
}
/**
* get row count in this table
* @return int
*/
public function getRowCount() : int {
return is_object($this->db) ? $this->db->getRowCount($this->getTable()) : 0;
}
/**
* truncate all table rows
* -> Use with Caution!!!
*/
public function truncate(){
if($this->allowTruncate && is_object($this->db)){
$this->db->exec("TRUNCATE " . $this->getTable());
}
}
/**
* format dateTime column
* @param $column
@@ -716,43 +757,78 @@ abstract class AbstractModel extends Cortex {
}
/**
* import table data from a *.csv file
* @return array|bool
* read *.csv file for a $table name
* -> 'group' by $getByKey column name and return array
* @param string $table
* @param string $getByKey
* @return array
*/
public function importData(){
$status = false;
public static function getCSVData(string $table, string $getByKey = 'id') : array {
$hashKeyTableCSV = static::generateHashKeyTable($table, static::CACHE_KEY_PREFIX . '_' . self::CACHE_KEY_CSV_PREFIX);
if(
!self::getF3()->exists($hashKeyTableCSV, $tableData) &&
!empty($tableData = Util::arrayGetBy(self::loadCSV($table), $getByKey, false))
){
self::getF3()->set($hashKeyTableCSV, $tableData, self::DEFAULT_CACHE_CSV_TTL);
}
return $tableData;
}
/**
* load data from *.csv file
* @param string $fileName
* @return array
*/
protected static function loadCSV(string $fileName) : array {
$tableData = [];
// rtrim(); for arrays (removes empty values) from the end
$rtrim = function($array = [], $lengthMin = false){
$rtrim = function($array = [], $lengthMin = false) : array {
$length = key(array_reverse(array_diff($array, ['']), 1))+1;
$length = $length < $lengthMin ? $lengthMin : $length;
return array_slice($array, 0, $length);
};
if(static::$enableDataImport){
$filePath = $this->getF3()->get('EXPORT') . 'csv/' . $this->getTable() . '.csv';
if($fileName){
$filePath = self::getF3()->get('EXPORT') . 'csv/' . $fileName . '.csv';
if(is_file($filePath)){
$handle = @fopen($filePath, 'r');
$keys = array_map('lcfirst', fgetcsv($handle, 0, ';'));
$keys = $rtrim($keys);
if(count($keys) > 0){
$tableData = [];
while (!feof($handle)) {
$tableData[] = array_combine($keys, $rtrim(fgetcsv($handle, 0, ';'), count($keys)));
}
// import row data
$status = $this->importStaticData($tableData);
$this->getF3()->status(202);
}else{
$this->getF3()->error(500, 'File could not be read');
self::getF3()->error(500, 'File could not be read');
}
}else{
$this->getF3()->error(404, 'File not found: ' . $filePath);
self::getF3()->error(404, 'File not found: ' . $filePath);
}
}
return $tableData;
}
/**
* import table data from a *.csv file
* @return array|bool
*/
public function importData(){
$status = false;
if(
static::$enableDataImport &&
!empty($tableData = self::loadCSV($this->getTable()))
){
// import row data
$status = $this->importStaticData($tableData);
$this->getF3()->status(202);
}
return $status;
}
@@ -896,6 +972,15 @@ abstract class AbstractModel extends Cortex {
return \Base::instance();
}
/**
* get model data as array
* @param $data
* @return array
*/
public static function toArray($data) : array {
return json_decode(json_encode($data), true);
}
/**
* get new filter array representation
* -> $suffix can be used fore unique placeholder,
@@ -1019,6 +1104,17 @@ abstract class AbstractModel extends Cortex {
return $model;
}
/**
* generate hashKey for a complete table
* -> should hold hashKeys for multiple rows
* @param string $table
* @param string $prefix
* @return string
*/
public static function generateHashKeyTable(string $table, string $prefix = self::CACHE_KEY_PREFIX ) : string {
return $prefix . '_' . strtolower($table);
}
/**
* overwrites parent
* @param null $db

View File

@@ -12,8 +12,14 @@ use DB\SQL\Schema;
class AllianceMapModel extends AbstractPathfinderModel {
/**
* @var string
*/
protected $table = 'alliance_map';
/**
* @var array
*/
protected $fieldConf = [
'active' => [
'type' => Schema::DT_BOOL,

View File

@@ -13,8 +13,14 @@ use lib\Config;
class AllianceModel extends AbstractPathfinderModel {
/**
* @var string
*/
protected $table = 'alliance';
/**
* @var array
*/
protected $fieldConf = [
'active' => [
'type' => Schema::DT_BOOL,
@@ -145,7 +151,7 @@ class AllianceModel extends AbstractPathfinderModel {
if($this->isOutdated()){
// request alliance data
$allianceData = self::getF3()->ccpClient()->getAllianceData($id);
if( !empty($allianceData) ){
if(!empty($allianceData) && !isset($allianceData['error'])){
$this->copyfrom($allianceData, ['id', 'name', 'ticker']);
$this->save();
}

View File

@@ -96,6 +96,15 @@ class CharacterLogModel extends AbstractPathfinderModel {
'default' => '',
'activity-log' => true
],
'structureTypeId' => [
'type' => Schema::DT_INT,
'index' => true
],
'structureTypeName' => [
'type' => Schema::DT_VARCHAR128,
'nullable' => false,
'default' => ''
],
'structureId' => [
'type' => Schema::DT_BIGINT,
'index' => true,
@@ -146,9 +155,13 @@ class CharacterLogModel extends AbstractPathfinderModel {
}
if( isset($logData['structure']) ){
$this->structureTypeId = (int)$logData['structure']['type']['id'];
$this->structureTypeName = $logData['structure']['type']['name'];
$this->structureId = (int)$logData['structure']['id'];
$this->structureName = $logData['structure']['name'];
}else{
$this->structureTypeId = null;
$this->structureTypeName = '';
$this->structureId = null;
$this->structureName = '';
}
@@ -178,20 +191,15 @@ class CharacterLogModel extends AbstractPathfinderModel {
$logData->station->name = $this->stationName;
$logData->structure = (object) [];
$logData->structure->type = (object) [];
$logData->structure->type->id = $this->structureTypeId;
$logData->structure->type->name = $this->structureTypeName;
$logData->structure->id = (int)$this->structureId;
$logData->structure->name = $this->structureName;
return $logData;
}
/**
* get 'character log' data as array
* @return array
*/
public function getDataAsArray() : array {
return json_decode(json_encode($this->getData()), true);
}
/**
* Event "Hook" function
* return false will stop any further action

View File

@@ -499,14 +499,7 @@ class CharacterModel extends AbstractPathfinderModel {
* @return UserModel|null
*/
public function getUser() : ?UserModel {
$user = null;
if($this->hasUserCharacter()){
/**
* @var $user UserModel
*/
$user = $this->userCharacter->userId;
}
return $user;
return $this->hasUserCharacter() ? $this->userCharacter->userId : null;
}
/**
@@ -864,23 +857,22 @@ class CharacterModel extends AbstractPathfinderModel {
if($this->isOnline($accessToken)){
$locationData = self::getF3()->ccpClient()->getCharacterLocationData($this->_id, $accessToken);
if( !empty($locationData['system']['id']) ){
if(!empty($locationData['system']['id'])){
// character is currently in-game
// get current $characterLog or get new ---------------------------------------------------
if( !($characterLog = $this->getLog()) ){
// get current $characterLog or get new -------------------------------------------------------
if(!$characterLog = $this->getLog()){
// create new log
$characterLog = $this->rel('characterLog');
}
// get current log data and modify on change
$logData = $characterLog->getDataAsArray();
$logData = $characterLog::toArray($characterLog->getData());
// check system and station data for changes ----------------------------------------------
// check system and station data for changes --------------------------------------------------
// IDs for "systemId", "stationId" that require more data
$lookupUniverseIds = [];
if(
empty($logData['system']['name']) ||
$logData['system']['id'] !== $locationData['system']['id']
@@ -889,33 +881,18 @@ class CharacterModel extends AbstractPathfinderModel {
$lookupUniverseIds[] = $locationData['system']['id'];
}
if( !empty($locationData['station']['id']) ){
if(
empty($logData['station']['name']) ||
$logData['station']['id'] !== $locationData['station']['id']
){
// station changed -> request "station name" for current station
$lookupUniverseIds[] = $locationData['station']['id'];
}
}else{
unset($logData['station']);
}
$logData = array_replace_recursive($logData, $locationData);
// get "more" data for systemId and/or stationId -----------------------------------------
if( !empty($lookupUniverseIds) ){
// get "more" data for systemId ---------------------------------------------------------------
if(!empty($lookupUniverseIds)){
// get "more" information for some Ids (e.g. name)
$universeData = self::getF3()->ccpClient()->getUniverseNamesData($lookupUniverseIds);
if( !empty($universeData) && !isset($universeData['error']) ){
if(!empty($universeData) && !isset($universeData['error'])){
// We expect max ONE system AND/OR station data, not an array of e.g. systems
if(!empty($universeData['system'])){
$universeData['system'] = reset($universeData['system']);
}
if(!empty($universeData['station'])){
$universeData['station'] = reset($universeData['station']);
}
$logData = array_replace_recursive($logData, $universeData);
}else{
@@ -924,32 +901,63 @@ class CharacterModel extends AbstractPathfinderModel {
}
}
// check structure data for changes -------------------------------------------------------
// check station data for changes -------------------------------------------------------------
if(!$deleteLog){
// IDs for "stationId" that require more data
$lookupStationId = 0;
if(!empty($locationData['station']['id'])){
if(
empty($logData['station']['name']) ||
$logData['station']['id'] !== $locationData['station']['id']
){
// station changed -> request station data
$lookupStationId = $locationData['station']['id'];
}
}else{
unset($logData['station']);
}
// get "more" data for stationId
if($lookupStationId > 0){
/**
* @var $stationModel Universe\StationModel
*/
$stationModel = Universe\AbstractUniverseModel::getNew('StationModel');
$stationModel->loadById($lookupStationId, $accessToken, $additionalOptions);
if($stationModel->valid()){
$stationData['station'] = $stationModel::toArray($stationModel->getData());
$logData = array_replace_recursive($logData, $stationData);
}else{
unset($logData['station']);
}
}
}
// check structure data for changes -----------------------------------------------------------
if(!$deleteLog){
// IDs for "structureId" that require more data
$lookupStructureId = 0;
if( !empty($locationData['structure']['id']) ){
if(!empty($locationData['structure']['id'])){
if(
empty($logData['structure']['name']) ||
$logData['structure']['id'] !== $locationData['structure']['id']
){
// structure changed -> request "structure name" for current station
// structure changed -> request structure data
$lookupStructureId = $locationData['structure']['id'];
}
}else{
unset($logData['structure']);
}
// get "more" data for structureId ---------------------------------------------------
// get "more" data for structureId
if($lookupStructureId > 0){
/**
* @var $structureModel Universe\StructureModel
*/
$structureModel = Universe\AbstractUniverseModel::getNew('StructureModel');
$structureModel->loadById($lookupStructureId, $accessToken, $additionalOptions);
if(!$structureModel->dry()){
$structureData['structure'] = (array)$structureModel->getData();
if($structureModel->valid()){
$structureData['structure'] = $structureModel::toArray($structureModel->getData());
$logData = array_replace_recursive($logData, $structureData);
}else{
unset($logData['structure']);
@@ -957,13 +965,13 @@ class CharacterModel extends AbstractPathfinderModel {
}
}
// check ship data for changes ------------------------------------------------------------
if( !$deleteLog ){
// check ship data for changes ----------------------------------------------------------------
if(!$deleteLog){
$shipData = self::getF3()->ccpClient()->getCharacterShipData($this->_id, $accessToken);
// IDs for "shipTypeId" that require more data
$lookupShipTypeId = 0;
if( !empty($shipData['ship']['typeId']) ){
if(!empty($shipData['ship']['typeId'])){
if(
empty($logData['ship']['typeName']) ||
$logData['ship']['typeId'] !== $shipData['ship']['typeId']
@@ -980,7 +988,7 @@ class CharacterModel extends AbstractPathfinderModel {
$invalidResponse = true;
}
// get "more" data for shipTypeId ----------------------------------------------------
// get "more" data for shipTypeId
if($lookupShipTypeId > 0){
/**
* @var $typeModel Universe\TypeModel
@@ -997,7 +1005,7 @@ class CharacterModel extends AbstractPathfinderModel {
}
}
if( !$deleteLog ){
if(!$deleteLog){
// mark log as "updated" even if no changes were made
if($additionalOptions['markUpdated'] === true){
$characterLog->touch('updated');
@@ -1086,7 +1094,7 @@ class CharacterModel extends AbstractPathfinderModel {
){
$task = 'add';
$mapIds = [];
$historyLog = $characterLog->getDataAsArray();
$historyLog = $characterLog::toArray($characterLog->getData());
if($logHistoryData = $this->getLogsHistory()){
// skip logging if no relevant fields changed

View File

@@ -14,8 +14,14 @@ use lib\logging;
class ConnectionModel extends AbstractMapTrackingModel {
/**
* @var string
*/
protected $table = 'connection';
/**
* @var array
*/
protected $fieldConf = [
'active' => [
'type' => Schema::DT_BOOL,

View File

@@ -12,8 +12,14 @@ use DB\SQL\Schema;
class CorporationMapModel extends AbstractPathfinderModel {
/**
* @var string
*/
protected $table = 'corporation_map';
/**
* @var array
*/
protected $fieldConf = [
'active' => [
'type' => Schema::DT_BOOL,

View File

@@ -13,6 +13,9 @@ use lib\Config;
class CorporationModel extends AbstractPathfinderModel {
/**
* @var string
*/
protected $table = 'corporation';
/**
@@ -91,6 +94,9 @@ class CorporationModel extends AbstractPathfinderModel {
'map_export'
];
/**
* @var array
*/
protected $fieldConf = [
'active' => [
'type' => Schema::DT_BOOL,
@@ -181,17 +187,19 @@ class CorporationModel extends AbstractPathfinderModel {
/**
* get all maps for this corporation
* @param array $mapIds
* @param int|null $mapId
* @param array $options
* @return array
*/
public function getMaps($mapIds = [], $options = []) : array {
public function getMaps(?int $mapId = null, $options = []) : array {
$maps = [];
$this->filterRel();
if(!empty($mapIds)){
$filters = [];
$filters[] = ['mapId IN (:mapId)', ':mapId' => $mapIds];
if($mapId){
$filters = [
self::getFilter('mapId', $mapId)
];
$this->filter('mapCorporations', $this->mergeWithRelFilter('mapCorporations', $this->mergeFilter($filters)), $this->getRelFilterOption('mapCorporations'));
}
@@ -217,7 +225,7 @@ class CorporationModel extends AbstractPathfinderModel {
* @param array $options
* @return CharacterModel[]
*/
public function getCharacters($characterIds = [], $options = []){
public function getCharacters($characterIds = [], $options = []) : array {
$characters = [];
$filter = ['active = ?', 1];
@@ -244,30 +252,28 @@ class CorporationModel extends AbstractPathfinderModel {
/**
* get all structure data for this corporation
* @param array $systemIds
* @param int $systemId
* @return array
*/
public function getStructuresData(array $systemIds = []) : array {
public function getStructuresData(int $systemId) : array {
$structuresData = [];
$structure = $this->rel('structures');
$this->filter('corporationStructures', ['active = ?', 1]);
$this->has('corporationStructures.structureId', ['active = ?', 1]);
$filters = [
self::getFilter('corporationId', $this->id),
self::getFilter('active', true)
];
if($systemIds){
if(count($systemIds) == 1){
$filterSystems = 'systemId = ?';
$filterSystemIds = reset($systemIds);
}else{
$filterSystems = 'systemId IN (?)';
$filterSystemIds = $systemIds;
}
$structure->has('structureCorporations', $this->mergeFilter($filters));
$this->has('corporationStructures.structureId', [$filterSystems, $filterSystemIds]);
}
$filters = [
self::getFilter('systemId', $systemId),
self::getFilter('active', true)
];
if($this->corporationStructures) {
foreach($this->corporationStructures as $corporationStructure){
$structuresData[] = $corporationStructure->structureId->getData();
if($structures = $structure->find($this->mergeFilter($filters))){
foreach($structures as $structure){
$structuresData[] = $structure->getData();
}
}
@@ -351,7 +357,7 @@ class CorporationModel extends AbstractPathfinderModel {
if($this->isOutdated()){
// request corporation data
$corporationData = self::getF3()->ccpClient()->getCorporationData($id);
if( !empty($corporationData) ){
if(!empty($corporationData) && !isset($corporationData['error'])){
// check for NPC corporation
$corporationData['isNPC'] = self::getF3()->ccpClient()->isNpcCorporation($id);

View File

@@ -12,8 +12,14 @@ use DB\SQL\Schema;
class CorporationStructureModel extends AbstractPathfinderModel {
/**
* @var string
*/
protected $table = 'corporation_structure';
/**
* @var array
*/
protected $fieldConf = [
'active' => [
'type' => Schema::DT_BOOL,

View File

@@ -17,6 +17,9 @@ use lib\logging;
class MapModel extends AbstractMapTrackingModel {
/**
* @var string
*/
protected $table = 'map';
/**
@@ -27,6 +30,9 @@ class MapModel extends AbstractMapTrackingModel {
const ERROR_SLACK_CHANNEL = 'Invalid #Slack channel column [%s]';
const ERROR_DISCORD_CHANNEL = 'Invalid #Discord channel column [%s]';
/**
* @var array
*/
protected $fieldConf = [
'active' => [
'type' => Schema::DT_BOOL,
@@ -98,6 +104,12 @@ class MapModel extends AbstractMapTrackingModel {
'default' => 1,
'activity-log' => true
],
'trackAbyssalJumps' => [
'type' => Schema::DT_BOOL,
'nullable' => false,
'default' => 1,
'activity-log' => true
],
'logActivity' => [
'type' => Schema::DT_BOOL,
'nullable' => false,
@@ -223,6 +235,7 @@ class MapModel extends AbstractMapTrackingModel {
$mapData->deleteEolConnections = $this->deleteEolConnections;
$mapData->persistentAliases = $this->persistentAliases;
$mapData->persistentSignatures = $this->persistentSignatures;
$mapData->trackAbyssalJumps = $this->trackAbyssalJumps;
// map scope
$mapData->scope = (object) [];
@@ -563,41 +576,37 @@ class MapModel extends AbstractMapTrackingModel {
}
/**
* get either all system models in this map
* @return SystemModel[]
* get systems in this map
* @return CortexCollection|array
*/
protected function getSystems(){
$systems = [];
$filters = [
self::getFilter('active', true)
];
// orderBy x-Coordinate for smoother frontend animation (left to right)
$this->filter('systems', ['active = 1'],
['order' => 'posX']
);
if($this->systems){
$systems = $this->systems;
}
return $systems;
return $this->relFind('systems', $this->mergeFilter($filters)) ? : [];
}
/**
* get all system data for all systems in this map
* @return \stdClass[]
* @throws \Exception
*/
public function getSystemsData() : array{
$systemData = [];
$systems = $this->getSystems();
public function getSystemsData() : array {
$systemsData = [];
foreach($systems as $system){
foreach($this->getSystems() as $system){
/**
* @var $system SystemModel
*/
$systemData[] = $system->getData();
$systemsData[] = $system->getData();
}
return $systemData;
// orderBy x-Coordinate for smoother frontend animation (left to right)
usort($systemsData, function($sysDataA, $sysDataB){
return $sysDataA->position->x <=> $sysDataB->position->x;
});
return $systemsData;
}
/**
@@ -650,25 +659,24 @@ class MapModel extends AbstractMapTrackingModel {
* @return \stdClass[]
*/
public function getConnectionsData() : array {
$connectionData = [];
$connections = $this->getConnections();
$connectionsData = [];
foreach($connections as $connection){
foreach($this->getConnections() as $connection){
/**
* @var $connection ConnectionModel
*/
$connectionData[] = $connection->getData(true);
$connectionsData[] = $connection->getData(true);
}
return $connectionData;
return $connectionsData;
}
/**
* get all structures data for this map
* @param array $systemIds
* @param int $systemId
* @return array
*/
public function getStructuresData(array $systemIds = []) : array {
public function getStructuresData(int $systemId) : array {
$structuresData = [];
$corporations = $this->getAllCorporations();
@@ -676,7 +684,7 @@ class MapModel extends AbstractMapTrackingModel {
// corporations should be unique
if( !isset($structuresData[$corporation->_id]) ){
// get all structures for current corporation
$corporationStructuresData = $corporation->getStructuresData($systemIds);
$corporationStructuresData = $corporation->getStructuresData($systemId);
if( !empty($corporationStructuresData) ){
// corporation has structures
$structuresData[$corporation->_id] = [
@@ -819,11 +827,6 @@ class MapModel extends AbstractMapTrackingModel {
$characters = [];
$filter = ['active = ?', 1];
if( !empty($characterIds) ){
$filter[0] .= ' AND id IN (?)';
$filter[] = $characterIds;
}
$this->filter('mapCharacters', $filter);
if($this->mapCharacters){
@@ -839,7 +842,7 @@ class MapModel extends AbstractMapTrackingModel {
* get corporations that have access to this map
* @return CorporationModel[]
*/
public function getCorporations() : array {
private function getCorporations() : array {
$corporations = [];
if($this->isCorporation()){
@@ -847,7 +850,7 @@ class MapModel extends AbstractMapTrackingModel {
if($this->mapCorporations){
foreach($this->mapCorporations as $mapCorporation){
$corporations[] = $mapCorporation->corporationId;
$corporations[$mapCorporation->corporationId->_id] = $mapCorporation->corporationId;
}
}
}

View File

@@ -12,8 +12,14 @@ use DB\SQL\Schema;
class RightModel extends AbstractPathfinderModel {
/**
* @var string
*/
protected $table = 'right';
/**
* @var array
*/
protected $fieldConf = [
'active' => [
'type' => Schema::DT_BOOL,
@@ -24,7 +30,9 @@ class RightModel extends AbstractPathfinderModel {
'name' => [
'type' => Schema::DT_VARCHAR128,
'nullable' => false,
'default' => ''
'default' => '',
'index' => true,
'unique' => true
],
'label' => [
'type' => Schema::DT_VARCHAR128,
@@ -41,6 +49,9 @@ class RightModel extends AbstractPathfinderModel {
]
];
/**
* @var array
*/
protected static $tableData = [
[
'id' => 1,

View File

@@ -19,11 +19,6 @@ class StructureModel extends AbstractPathfinderModel {
*/
protected $table = 'structure';
/**
* categoryId (from ESI) that holds all "groups" with structure "types"
*/
const CATEGORY_STRUCTURE_ID = 65;
/**
* @var array
*/
@@ -130,15 +125,15 @@ class StructureModel extends AbstractPathfinderModel {
/**
* set corporationId (owner) for this structure
* -> if corporation does not exists in DB -> load from API
* @param int $corporationId
* @param int|null $corporationId
* @return int|null
*/
public function set_corporationId(int $corporationId) : ?int {
public function set_corporationId(?int $corporationId) : ?int {
$oldCorporationId = $this->get('corporationId', true) ? : 0;
if($corporationId){
if($corporationId !== $oldCorporationId){
// make sure there is already corporation data stored for new corporationId
// make sure there is already corporation data available for new corporationId
/**
* @var CorporationModel $corporation
*/
@@ -156,26 +151,20 @@ class StructureModel extends AbstractPathfinderModel {
}
/**
* validates systemId
* -> a structure always belongs to the same system
* @param string $key
* @param string $val
* @return bool
*/
protected function validate_systemId(string $key, string $val): bool {
$valid = true;
if( !$this->dry() && $this->systemId !== (int)$val ){
// structure always belongs to the same system
$valid = false;
}
return $valid;
protected function validate_systemId(string $key, string $val) : bool {
return !($this->valid() && $this->systemId !== (int)$val);
}
/**
* check whether this model is valid or not
* @return bool
*/
public function isValid(): bool {
public function isValid() : bool {
if($valid = parent::isValid()){
// structure always belongs to a systemId
if(!(int)$this->systemId){
@@ -244,7 +233,7 @@ class StructureModel extends AbstractPathfinderModel {
* @param string $name
* @param int $systemId
*/
public function getByName(CorporationModel $corporation, string $name, int $systemId) {
public function getByName(CorporationModel $corporation, string $name, int $systemId){
if( !$corporation->dry() && $name){
$this->has('structureCorporations', ['corporationId = :corporationId', ':corporationId' => $corporation->_id]);
$this->load(['name = :name AND systemId = :systemId AND active = :active',

View File

@@ -13,8 +13,14 @@ use DB\SQL\Schema;
class StructureStatusModel extends AbstractPathfinderModel {
/**
* @var string
*/
protected $table = 'structure_status';
/**
* @var array
*/
protected $fieldConf = [
'active' => [
'type' => Schema::DT_BOOL,
@@ -42,6 +48,9 @@ class StructureStatusModel extends AbstractPathfinderModel {
]
];
/**
* @var array
*/
protected static $tableData = [
[
'id' => 1,

View File

@@ -10,6 +10,7 @@ namespace Model\Pathfinder;
use DB\SQL\Schema;
use lib\logging;
use lib\PriorityCacheStore;
use Controller\Ccp\Universe;
class SystemModel extends AbstractMapTrackingModel {
@@ -39,16 +40,16 @@ class SystemModel extends AbstractMapTrackingModel {
*/
const DATA_CACHE_KEY_SIGNATURES_HISTORY = 'HISTORY_SIGNATURES';
/**
* @var PriorityCacheStore
*/
protected static $priorityCacheStore;
/**
* @var string
*/
protected $table = 'system';
/**
* @var array
*/
protected $staticSystemDataCache = [];
/**
* @var array
*/
@@ -161,80 +162,79 @@ class SystemModel extends AbstractMapTrackingModel {
/**
* get map data as object
* @return \stdClass
* @throws \Exception
*/
public function getData(){
// check if there is cached data
$systemData = $this->getCacheData();
if(is_null($systemData)){
// no cached system data found
$systemData = (object) [];
$systemData->id = $this->_id;
$systemData->mapId = is_object($this->mapId) ? $this->get('mapId', true) : 0;
$systemData->systemId = $this->systemId;
$systemData->alias = $this->alias;
if(is_null($data = $this->getCacheData())){
$data = (object) [];
$data->id = $this->_id;
$data->mapId = is_object($this->mapId) ? $this->get('mapId', true) : 0;
$data->systemId = $this->systemId;
$data->alias = $this->alias;
if(is_object($this->typeId)){
$systemData->type = $this->typeId->getData();
$data->type = $this->typeId->getData();
}
if(is_object($this->statusId)){
$systemData->status = $this->statusId->getData();
$data->status = $this->statusId->getData();
}
$systemData->locked = $this->locked;
$systemData->rallyUpdated = strtotime($this->rallyUpdated);
$systemData->rallyPoke = $this->rallyPoke;
$systemData->description = $this->description ? : '';
$data->locked = $this->locked;
$data->drifter = $this->isDrifter();
$data->rallyUpdated = strtotime($this->rallyUpdated);
$data->rallyPoke = $this->rallyPoke;
$data->description = $this->description ? : '';
$systemData->position = (object) [];
$systemData->position->x = $this->posX;
$systemData->position->y = $this->posY;
$data->position = (object) [];
$data->position->x = $this->posX;
$data->position->y = $this->posY;
$systemData->created = (object) [];
$systemData->created->created = strtotime($this->created);
$data->created = (object) [];
$data->created->created = strtotime($this->created);
if(is_object($this->createdCharacterId)){
$systemData->created->character = $this->createdCharacterId->getData();
$data->created->character = $this->createdCharacterId->getData();
}
$systemData->updated = (object) [];
$systemData->updated->updated = strtotime($this->updated);
$data->updated = (object) [];
$data->updated->updated = strtotime($this->updated);
if(is_object($this->updatedCharacterId)){
$systemData->updated->character = $this->updatedCharacterId->getData();
$data->updated->character = $this->updatedCharacterId->getData();
}
// static system data -------------------------------------------------------------------------------------
$systemData->name = $this->name;
$systemData->security = $this->security;
$systemData->trueSec = $this->trueSec;
$systemData->effect = $this->effect;
$systemData->shattered = $this->shattered;
$data->name = $this->name;
$data->security = $this->security;
$data->trueSec = $this->trueSec;
$data->effect = $this->effect;
$data->shattered = $this->shattered;
$systemData->constellation = (object) [];
$systemData->constellation->id = $this->constellationId;
$systemData->constellation->name = $this->constellation;
$data->constellation = (object) [];
$data->constellation->id = $this->constellationId;
$data->constellation->name = $this->constellation;
$systemData->region = (object) [];
$systemData->region->id = $this->regionId;
$systemData->region->name = $this->region;
$data->region = (object) [];
$data->region->id = $this->regionId;
$data->region->name = $this->region;
$systemData->planets = $this->planets ? : [];
$systemData->statics = $this->statics ? : [];
$data->planets = $this->planets ? : [];
$data->statics = $this->statics ? : [];
if(is_object($this->faction)){
$systemData->faction = $this->faction;
if(is_object($sovereignty = $this->sovereignty)){
$data->sovereignty = $sovereignty;
}
if(is_object($factionWar = $this->factionWar)){
$data->factionWar = $factionWar;
}
// max caching time for a system
// the cached date has to be cleared manually on any change
// this includes system, connection,... changes (all dependencies)
$this->updateCacheData($systemData);
$this->updateCacheData($data);
}
return $systemData;
return $data;
}
/**
@@ -244,14 +244,19 @@ class SystemModel extends AbstractMapTrackingModel {
*/
private function getStaticSystemData(){
$staticData = null;
if( !empty($this->staticSystemDataCache[$this->systemId]) ){
$staticData = $this->staticSystemDataCache[$this->systemId];
if(!is_object(self::$priorityCacheStore)){
self::$priorityCacheStore = new PriorityCacheStore();
}
if(self::$priorityCacheStore->exists($this->systemId)){
$staticData = self::$priorityCacheStore->get($this->systemId);
}else{
$staticData = (new Universe())->getSystemData($this->systemId);
if($staticData){
$this->staticSystemDataCache = [$this->systemId => $staticData];
self::$priorityCacheStore->set($this->systemId, $staticData);
}
}
return $staticData;
}
@@ -435,10 +440,6 @@ class SystemModel extends AbstractMapTrackingModel {
return ($constellationData && $constellationData->region) ? $constellationData->region->name : null;
}
public function get_faction(){
return $this->getStaticSystemValue('faction');
}
public function get_security(){
return $this->getStaticSystemValue('security');
}
@@ -463,6 +464,18 @@ class SystemModel extends AbstractMapTrackingModel {
return $this->getStaticSystemValue('planets');
}
public function get_stations(){
return $this->getStaticSystemValue('stations');
}
public function get_sovereignty(){
return $this->getStaticSystemValue('sovereignty');
}
public function get_factionWar(){
return $this->getStaticSystemValue('factionWar');
}
/**
* Event "Hook" function
* @param self $self
@@ -652,7 +665,15 @@ class SystemModel extends AbstractMapTrackingModel {
* @return \stdClass[]
*/
public function getStructuresData() : array {
return $this->getMap()->getStructuresData([$this->systemId]);
return $this->getMap()->getStructuresData($this->systemId);
}
/**
* get data for all stations in this system
* @return array
*/
public function getStationsData() : array {
return $this->stations ? : [];
}
/**
@@ -679,6 +700,14 @@ class SystemModel extends AbstractMapTrackingModel {
return ($this->typeId->id === 3 && $this->security === 'A');
}
/**
* check whether this system is in drifter-space
* @return bool
*/
public function isDrifter() : bool {
return in_array($this->security, ['C14', 'C15', 'C16', 'C17', 'C18']);
}
/**
* send rally point poke to various "APIs"
* -> send to a Slack channel

View File

@@ -1,49 +0,0 @@
<?php
/**
* Created by PhpStorm.
* User: Exodus
* Date: 24.12.2015
* Time: 00:59
*/
namespace Model\Pathfinder;
use DB\SQL\Schema;
class SystemNeighbourModel extends AbstractPathfinderModel {
protected $table = 'system_neighbour';
protected $fieldConf = [
'regionId' => [
'type' => Schema::DT_INT,
'index' => true
],
'constellationId' => [
'type' => Schema::DT_INT,
'index' => true
],
'systemName' => [
'type' => Schema::DT_VARCHAR128,
'default' => ''
],
'systemId' => [
'type' => Schema::DT_INT,
'index' => true
],
'jumpNodes' => [
'type' => Schema::DT_VARCHAR512,
'default' => ''
],
'trueSec' => [
'type' => Schema::DT_DECIMAL,
'default' => 0
]
];
/**
* No static columns added
* @var bool
*/
protected $addStaticFields = false;
}

View File

@@ -13,8 +13,14 @@ use lib\logging;
class SystemSignatureModel extends AbstractMapTrackingModel {
/**
* @var string
*/
protected $table = 'system_signature';
/**
* @var array
*/
protected $fieldConf = [
'active' => [
'type' => Schema::DT_BOOL,
@@ -278,6 +284,13 @@ class SystemSignatureModel extends AbstractMapTrackingModel {
*/
public function afterEraseEvent($self, $pkeys){
$self->logActivity('signatureDelete');
if(
$self->connectionIdDeleteCascade === true &&
($connection = $self->getConnection())
){
$connection->erase();
}
}
/**

View File

@@ -12,8 +12,14 @@ use DB\SQL\Schema;
class SystemTypeModel extends AbstractPathfinderModel {
/**
* @var string
*/
protected $table = 'system_type';
/**
* @var array
*/
protected $fieldConf = [
'active' => [
'type' => Schema::DT_BOOL,
@@ -28,6 +34,9 @@ class SystemTypeModel extends AbstractPathfinderModel {
]
];
/**
* @var array
*/
protected static $tableData = [
[
'id' => 1,

View File

@@ -202,16 +202,15 @@ class UserModel extends AbstractPathfinderModel {
}
/**
* get current character data from session
* get current character from session data
* -> if $characterId == 0 -> get first character data (random)
* @param int $characterId
* @param bool $objectCheck
* @return array
* @param int $ttl
* @return CharacterModel|null
* @throws Exception
*/
public function getSessionCharacterData($characterId = 0, $objectCheck = true) : array {
public function getSessionCharacter(int $characterId = 0, int $ttl = self::DEFAULT_SQL_TTL) : ?CharacterModel {
$data = [];
$characterId = (int)$characterId;
$currentSessionUser = (array)$this->getF3()->get(User::SESSION_KEY_USER);
if($this->_id === $currentSessionUser['ID']){
@@ -228,28 +227,22 @@ class UserModel extends AbstractPathfinderModel {
}
}
if(
$objectCheck === true &&
!empty($data)
){
if($characterId = (int)$data['ID']){
// check if character still exists on DB (e.g. was manually removed in the meantime)
// -> This should NEVER happen just for security and "local development"
/**
* @var $character CharacterModel
*/
$character = AbstractPathfinderModel::getNew('CharacterModel');
$character->getById((int)$data['ID']);
$character->getById($characterId, $ttl);
if(
$character->dry() ||
!$character->hasUserCharacter()
){
// character data is invalid!
$data = [];
if($character->valid() && $character->hasUserCharacter()){
// character data is valid!
return $character;
}
}
return $data;
return null;
}
/**
@@ -259,10 +252,9 @@ class UserModel extends AbstractPathfinderModel {
*/
public function findSessionCharacterData(int $characterId) : array {
$data = [];
if($characterId){
$sessionCharacters = (array)$this->getF3()->get(User::SESSION_KEY_CHARACTERS);
if($characterId && $this->getF3()->exists(User::SESSION_KEY_CHARACTERS, $sessionCharacters)){
// search for specific characterData
foreach($sessionCharacters as $characterData){
foreach((array)$sessionCharacters as $characterData){
if($characterId === (int)$characterData['ID']){
$data = $characterData;
break;

View File

@@ -20,7 +20,7 @@ abstract class AbstractUniverseModel extends AbstractModel {
/**
*
*/
const CACHE_KEY_PREFIX = 'index_universe_';
const CACHE_KEY_PREFIX = parent::CACHE_KEY_PREFIX . '_' . self::DB_ALIAS;
/**
* cache key for model data -> should "never" expire
@@ -36,6 +36,21 @@ abstract class AbstractUniverseModel extends AbstractModel {
return null;
}
/**
* setter for positions array (x/y/z)
* @param $position
* @return null
*/
public function set_position($position){
$position = (array)$position;
if(count($position) === 3){
$this->x = $position['x'];
$this->y = $position['y'];
$this->z = $position['z'];
}
return null;
}
/**
* Event "Hook" function
* return false will stop any further action
@@ -46,7 +61,7 @@ abstract class AbstractUniverseModel extends AbstractModel {
public function beforeUpdateEvent($self, $pkeys) : bool {
// if model changed, 'update' col needs to be updated as well
// -> data no longer "outdated"
$this->touch('updated');
$self->touch('updated');
return parent::beforeUpdateEvent($self, $pkeys);
}
@@ -59,7 +74,7 @@ abstract class AbstractUniverseModel extends AbstractModel {
*/
public function getHashKey(string $column = '_id'){
$key = false;
if( !$this->dry() && $this->exists($column) ){
if($this->valid() && $this->exists($column)){
$key = self::generateHashKeyRow($this->getTable(), $this->$column);
}
return $key;
@@ -110,21 +125,6 @@ abstract class AbstractUniverseModel extends AbstractModel {
return $data;
}
/**
* add $rowKeys (hashKeys) to a search index that holds all rowKeys of a table
* @param AbstractUniverseModel $model
* @param array $rowKeys
*/
public static function buildTableIndex(AbstractUniverseModel $model, array $rowKeys = []){
$hashKeyTable = self::generateHashKeyTable($model->getTable());
if( !self::getF3()->exists($hashKeyTable, $cachedData) ){
$cachedData = [];
}
$cachedData = array_unique(array_merge($cachedData, $rowKeys));
self::getF3()->set($hashKeyTable, $cachedData, self::CACHE_INDEX_EXPIRE_KEY);
}
/**
* get data from "search" index for this model
* -> if data not found -> try to build up index for this model
@@ -166,6 +166,45 @@ abstract class AbstractUniverseModel extends AbstractModel {
*/
abstract protected function loadData(int $id, string $accessToken = '', array $additionalOptions = []);
/**
* convert CCPs ids for system security into Pathfinder security label
* -> used e.g. in "Dogma Attributes" (wormholeTargetSystemClass) for wormhole types
* @param int $id
* @return string|null
*/
public static function getSystemSecurityFromId(int $id) : ?string {
$security = null;
if(
($id >= 1 && $id <= 6) ||
($id >= 12 && $id <= 18)
){
$security = 'C' . $id;
}elseif($id == 7){
$security = 'H';
}elseif($id == 8){
$security = 'L';
}elseif($id == 9){
$security = '0.0';
}
return $security;
}
/**
* add $rowKeys (hashKeys) to a search index that holds all rowKeys of a table
* @param AbstractUniverseModel $model
* @param array $rowKeys
*/
public static function buildTableIndex(AbstractUniverseModel $model, array $rowKeys = []){
$hashKeyTable = static::generateHashKeyTable($model->getTable());
if( !self::getF3()->exists($hashKeyTable, $cachedData) ){
$cachedData = [];
}
$cachedData = array_unique(array_merge($cachedData, $rowKeys));
self::getF3()->set($hashKeyTable, $cachedData, self::CACHE_INDEX_EXPIRE_KEY);
}
/**
* generate hashKey for a table row data for search index build
* @param string $table
@@ -173,16 +212,17 @@ abstract class AbstractUniverseModel extends AbstractModel {
* @return string
*/
public static function generateHashKeyRow(string $table, $value) : string {
return self::generateHashKeyTable($table) . '_' . md5(strtolower((string)$value));
return static::generateHashKeyTable($table) . '_' . md5(strtolower((string)$value));
}
/**
* generate hashKey for a complete table
* -> should hold hashKeys for multiple rows
* @param string $table
* @param string $prefix
* @return string
*/
public static function generateHashKeyTable(string $table) : string {
return self::CACHE_KEY_PREFIX . strtolower($table);
public static function generateHashKeyTable(string $table, string $prefix = self::CACHE_KEY_PREFIX) : string {
return parent::generateHashKeyTable($table, $prefix);
}
}

View File

@@ -0,0 +1,103 @@
<?php
namespace Model\Universe;
use DB\SQL\Schema;
class AllianceModel extends AbstractUniverseModel {
/**
* @var string
*/
protected $table = 'alliance';
/**
* @var array
*/
protected $fieldConf = [
'name' => [
'type' => Schema::DT_VARCHAR128,
'nullable' => false,
'default' => ''
],
'ticker' => [
'type' => Schema::DT_VARCHAR128,
'nullable' => false,
'default' => ''
],
'dateFounded' => [
'type' => Schema::DT_DATETIME,
'default' => null
],
'factionId' => [
'type' => Schema::DT_INT,
'index' => true,
'belongs-to-one' => 'Model\Universe\FactionModel',
'constraint' => [
[
'table' => 'faction',
'on-delete' => 'SET NULL'
]
]
],
'corporations' => [
'has-many' => ['Model\Universe\CorporationModel', 'allianceId']
],
'sovereigntySystems' => [
'has-many' => ['Model\Universe\SovereigntyMapModel', 'allianceId']
]
];
/**
* get data
* @return \stdClass
*/
public function getData(){
$data = (object) [];
$data->id = $this->_id;
$data->name = $this->name;
$data->ticker = $this->ticker;
return $data;
}
/**
* @param $date
* @return string|null
*/
public function set_dateFounded($date){
if(is_string($date) && !empty($date)){
try{
$dateTime = new \DateTime($date);
$date = $dateTime->format('Y-m-d H:i:s');
}catch(\Exception $e){
$date = null;
}
}
return $date;
}
/**
* load alliance by Id either from DB or load data from API
* @param int $id
* @param string $accessToken
* @param array $additionalOptions
*/
protected function loadData(int $id, string $accessToken = '', array $additionalOptions = []){
$data = self::getF3()->ccpClient()->getAllianceData($id);
if(!empty($data) && !isset($data['error'])){
if($data['factionId']){
/**
* @var $faction FactionModel
*/
$faction = $this->rel('factionId');
$faction->loadById($data['factionId'], $accessToken, $additionalOptions);
$data['factionId'] = $faction;
}
$this->copyfrom($data, ['id', 'name', 'ticker', 'dateFounded', 'factionId']);
$this->save();
}
}
}

View File

@@ -38,7 +38,7 @@ class CategoryModel extends AbstractUniverseModel {
*/
public function getData(array $additionalData = []){
$categoryData = (object) [];
$categoryData->id = $this->id;
$categoryData->id = $this->_id;
$categoryData->name = $this->name;
if($groupsData = $this->getGroupsData($additionalData)){
@@ -77,6 +77,9 @@ class CategoryModel extends AbstractUniverseModel {
$groupsData = [];
$groups = $this->getGroups();
/**
* @var $group GroupModel
*/
foreach($groups as $group){
$groupsData[] = $group->getData($additionalData);
}
@@ -84,6 +87,15 @@ class CategoryModel extends AbstractUniverseModel {
return $groupsData;
}
/**
* get groups count
* @param bool $published
* @return int
*/
public function getGroupsCount(bool $published = true) : int {
return $this->valid() ? count($this->getGroups($published)) : 0;
}
/**
* count all types that belong to groups in this category
* @param bool $published
@@ -91,7 +103,7 @@ class CategoryModel extends AbstractUniverseModel {
*/
public function getTypesCount(bool $published = true) : int {
$count = 0;
if( !$this->dry() ){
if($this->valid()){
/**
* @var $group GroupModel
*/
@@ -109,8 +121,7 @@ class CategoryModel extends AbstractUniverseModel {
* @param array $additionalOptions
*/
protected function loadData(int $id, string $accessToken = '', array $additionalOptions = []){
$data = self::getF3()->ccpClient()->getUniverseCategoryData($id);
if(!empty($data)){
if(!empty($data = self::getUniverseCategoryData($id))){
$this->copyfrom($data, ['id', 'name', 'published']);
$this->save();
}
@@ -123,25 +134,71 @@ class CategoryModel extends AbstractUniverseModel {
* @return array
*/
public function loadGroupsData(int $offset = 0, int $length = 0) : array {
$groupIds = [];
if( !$this->dry() ){
$data = self::getF3()->ccpClient()->getUniverseCategoryData($this->_id);
if(!empty($data)){
array_multisort($data['groups'], SORT_ASC, SORT_NUMERIC);
if($length){
$data['groups'] = array_slice($data['groups'], $offset, $length);
}
foreach($data['groups'] as $groupId){
/**
* @var $group GroupModel
*/
$group = $this->rel('groups');
$group->loadById($groupId);
$groupIds[] = $groupId;
$group->reset();
}
$info = ['countAll' => 0, 'countChunk' => 0, 'count' => 0, 'offset' => $offset, 'groupTypes' => []];
if(
$this->valid() &&
!empty($data = self::getUniverseCategoryData($this->_id))
){
$info['countAll'] = count($data['groups']);
array_multisort($data['groups'], SORT_ASC, SORT_NUMERIC);
if($length){
$data['groups'] = array_slice($data['groups'], $offset, $length);
}
$info['countChunk'] = count($data['groups']);
foreach($data['groups'] as $groupId){
/**
* @var $group GroupModel
*/
$group = $this->rel('groups');
$group->loadById($groupId);
$info['groupTypes'][$groupId] = $group->loadTypesData();
$group->reset();
$info['count']++;
$info['offset']++;
}
}
return $groupIds;
return $info;
}
/**
* @param int $id
* @return array
*/
public static function getUniverseCategoryData(int $id) : array {
return self::getF3()->ccpClient()->getUniverseCategoryData($id);
}
/**
* @return array
*/
public static function getUniverseCategories() : array {
return self::getF3()->ccpClient()->getUniverseCategories();
}
/**
* @param int $id
* @return array
*/
public static function getUniverseCategoryGroups(int $id) : array {
return empty($data = self::getUniverseCategoryData($id)) ? [] : $data['groups'];
}
/**
* @param int $id
* @return array
*/
public static function getUniverseCategoryTypes(int $id) : array {
$types = [];
foreach($groupIds = self::getUniverseCategoryGroups($id) as $groupId){
$types[$groupId] = GroupModel::getUniverseGroupTypes($groupId);
}
return $types;
}
}

View File

@@ -55,6 +55,9 @@ class ConstellationModel extends AbstractUniverseModel {
],
'systems' => [
'has-many' => ['Model\Universe\SystemModel', 'constellationId']
],
'systemNeighbours' => [
'has-many' => ['Model\Universe\SystemNeighbourModel', 'constellationId']
]
];
@@ -71,21 +74,6 @@ class ConstellationModel extends AbstractUniverseModel {
return $constellationData;
}
/**
* setter for positions array (x/y/z)
* @param $position
* @return null
*/
public function set_position($position){
$position = (array)$position;
if(count($position) === 3){
$this->x = $position['x'];
$this->y = $position['y'];
$this->z = $position['z'];
}
return null;
}
/**
* @param int $id
* @param string $accessToken

View File

@@ -0,0 +1,135 @@
<?php
namespace Model\Universe;
use DB\SQL\Schema;
class CorporationModel extends AbstractUniverseModel {
/**
* @var string
*/
protected $table = 'corporation';
/**
* @var array
*/
protected $fieldConf = [
'name' => [
'type' => Schema::DT_VARCHAR128,
'nullable' => false,
'default' => ''
],
'ticker' => [
'type' => Schema::DT_VARCHAR128,
'nullable' => false,
'default' => ''
],
'dateFounded' => [
'type' => Schema::DT_DATETIME,
'default' => null
],
'memberCount' => [
'type' => Schema::DT_INT,
'nullable' => false,
'default' => 0
],
'isNPC' => [
'type' => Schema::DT_BOOL,
'nullable' => false,
'default' => 0
],
'factionId' => [
'type' => Schema::DT_INT,
'index' => true,
'belongs-to-one' => 'Model\Universe\FactionModel',
'constraint' => [
[
'table' => 'faction',
'on-delete' => 'SET NULL'
]
]
],
'allianceId' => [
'type' => Schema::DT_INT,
'index' => true,
'belongs-to-one' => 'Model\Universe\AllianceModel',
'constraint' => [
[
'table' => 'alliance',
'on-delete' => 'SET NULL'
]
]
],
'sovereigntySystems' => [
'has-many' => ['Model\Universe\SovereigntyMapModel', 'corporationId']
],
'stations' => [
'has-many' => ['Model\Universe\StationModel', 'corporationId']
]
];
/**
* get data
* @return \stdClass
*/
public function getData(){
$data = (object) [];
$data->id = $this->_id;
$data->name = $this->name;
return $data;
}
/**
* @param $date
* @return string|null
*/
public function set_dateFounded($date){
if(is_string($date) && !empty($date)){
try{
$dateTime = new \DateTime($date);
$date = $dateTime->format('Y-m-d H:i:s');
}catch(\Exception $e){
$date = null;
}
}
return $date;
}
/**
* load corporation by Id either from DB or load data from API
* @param int $id
* @param string $accessToken
* @param array $additionalOptions
*/
protected function loadData(int $id, string $accessToken = '', array $additionalOptions = []){
$data = self::getF3()->ccpClient()->getCorporationData($id);
if(!empty($data) && !isset($data['error'])){
// check for NPC corporation
$data['isNPC'] = self::getF3()->ccpClient()->isNpcCorporation($id);
if($data['factionId']){
/**
* @var $faction FactionModel
*/
$faction = $this->rel('factionId');
$faction->loadById($data['factionId'], $accessToken, $additionalOptions);
$data['factionId'] = $faction;
}
if($data['allianceId']){
/**
* @var $alliance AllianceModel
*/
$alliance = $this->rel('allianceId');
$alliance->loadById($data['allianceId'], $accessToken, $additionalOptions);
$data['allianceId'] = $alliance;
}
$this->copyfrom($data, ['id', 'name', 'ticker', 'dateFounded', 'memberCount', 'isNPC', 'factionId', 'allianceId']);
$this->save();
}
}
}

View File

@@ -0,0 +1,97 @@
<?php
/**
* Created by PhpStorm.
* User: Exodus4D
* Date: 15.08.2019
* Time: 22:00
*/
namespace Model\Universe;
use DB\SQL\Schema;
class DogmaAttributeModel extends AbstractUniverseModel {
/**
* @var string
*/
protected $table = 'dogma_attribute';
/**
* @var array
*/
protected $fieldConf = [
'name' => [
'type' => Schema::DT_VARCHAR128,
'nullable' => false,
'default' => ''
],
'displayName' => [
'type' => Schema::DT_VARCHAR128,
'nullable' => true,
'default' => null
],
'description' => [
'type' => Schema::DT_TEXT
],
'published' => [
'type' => Schema::DT_BOOL,
'nullable' => true,
'default' => null
],
'stackable' => [
'type' => Schema::DT_BOOL,
'nullable' => true,
'default' => null
],
'highIsGood' => [
'type' => Schema::DT_BOOL,
'nullable' => true,
'default' => null
],
'defaultValue' => [
'type' => Schema::DT_FLOAT,
'nullable' => false,
'default' => 0
],
'iconId' => [
'type' => Schema::DT_INT,
'nullable' => true,
'default' => null
],
'unitId' => [
'type' => Schema::DT_INT,
'nullable' => true,
'default' => null
],
'attributeTypes' => [
'has-many' => ['Model\Universe\TypeAttributeModel', 'attributeId']
]
];
/**
* get data
* @return \stdClass
*/
public function getData(){
$attributeData = (object) [];
$attributeData->id = $this->_id;
$attributeData->name = $this->name;
$attributeData->description = $this->description;
return $attributeData;
}
/**
* @param int $id
* @param string $accessToken
* @param array $additionalOptions
*/
protected function loadData(int $id, string $accessToken = '', array $additionalOptions = []){
$data = self::getF3()->ccpClient()->getDogmaAttributeData($id);
if(!empty($data) && !isset($data['error'])){
$this->copyfrom($data, ['id', 'name', 'displayName', 'description', 'published', 'stackable', 'highIsGood', 'defaultValue', 'iconId', 'unitId']);
$this->save();
}
}
}

View File

@@ -46,8 +46,23 @@ class FactionModel extends AbstractUniverseModel {
'nullable' => false,
'default' => 0
],
'systems' => [
'has-many' => ['Model\Universe\SystemModel', 'factionId']
'race' => [ // faction API endpoint dont have "raceId" data, but race API endpoint has
'has-one' => ['Model\Universe\RaceModel', 'factionId']
],
'alliances' => [
'has-many' => ['Model\Universe\AllianceModel', 'factionId']
],
'corporations' => [
'has-many' => ['Model\Universe\CorporationModel', 'factionId']
],
'sovereigntySystems' => [
'has-many' => ['Model\Universe\SovereigntyMapModel', 'factionId']
],
'factionWarSystemOwners' => [
'has-many' => ['Model\Universe\FactionWarSystemModel', 'ownerFactionId']
],
'factionWarSystemOccupiers' => [
'has-many' => ['Model\Universe\FactionWarSystemModel', 'occupierFactionId']
]
];
@@ -70,7 +85,7 @@ class FactionModel extends AbstractUniverseModel {
*/
protected function loadData(int $id, string $accessToken = '', array $additionalOptions = []){
$data = self::getF3()->ccpClient()->getUniverseFactionData($id);
if(!empty($data)){
if(!empty($data) && !isset($data['error'])){
$this->copyfrom($data, ['id', 'name', 'description', 'sizeFactor', 'stationCount', 'stationSystemCount']);
$this->save();
}

View File

@@ -0,0 +1,120 @@
<?php
namespace Model\Universe;
use DB\SQL\Schema;
class FactionWarSystemModel extends AbstractUniverseModel {
/**
* @var string
*/
protected $table = 'faction_war_system';
/**
* @var array
*/
protected $fieldConf = [
'systemId' => [
'type' => Schema::DT_INT,
'index' => true,
'unique' => true,
'belongs-to-one' => 'Model\Universe\SystemModel',
'constraint' => [
[
'table' => 'system',
'on-delete' => 'CASCADE'
]
],
'validate' => 'notDry'
],
'ownerFactionId' => [
'type' => Schema::DT_INT,
'index' => true,
'belongs-to-one' => 'Model\Universe\FactionModel',
'constraint' => [
[
'table' => 'faction',
'on-delete' => 'CASCADE'
]
]
],
'occupierFactionId' => [
'type' => Schema::DT_INT,
'index' => true,
'belongs-to-one' => 'Model\Universe\FactionModel',
'constraint' => [
[
'table' => 'faction',
'on-delete' => 'CASCADE'
]
]
],
'contested' => [
'type' => Schema::DT_VARCHAR128,
'nullable' => false,
'default' => ''
],
'victoryPoints' => [
'type' => Schema::DT_INT,
'nullable' => false,
'default' => 0
],
'victoryPointsThreshold' => [
'type' => Schema::DT_INT,
'nullable' => false,
'default' => 0
]
];
/**
* No static columns added
* @var bool
*/
protected $addStaticFields = false;
/**
* get data
* @return \stdClass
*/
public function getData(){
$data = (object) [];
$data->contested = $this->contested;
if($this->ownerFactionId){
$data->ownerFaction = $this->ownerFactionId->getData();
$data->victoryPercentage = $this->getVictoryPercentage();
if(
$this->occupierFactionId &&
$this->get('occupierFactionId', true) !== $this->get('ownerFactionId', true)
){
$data->occupierFaction = $this->occupierFactionId->getData();
}
}
return $data;
}
/**
* calculate victory progress in percent
* @return int
*/
protected function getVictoryPercentage() : int {
$percent = 0;
if($this->victoryPoints && $this->victoryPointsThreshold){
$percent = floor((100 / $this->victoryPointsThreshold) * $this->victoryPoints);
}
return $percent;
}
/**
* @param int $id
* @param string $accessToken
* @param array $additionalOptions
*/
protected function loadData(int $id, string $accessToken = '', array $additionalOptions = []){}
}

View File

@@ -12,8 +12,19 @@ use DB\SQL\Schema;
class GroupModel extends AbstractUniverseModel {
protected $table = 'group';
/**
* @var string
*/
protected $table = 'group';
/**
* @var bool
*/
public $storeDogmaAttributes = TypeModel::DEFAULT_STORE_DOGMA_ATTRIBUTES;
/**
* @var array
*/
protected $fieldConf = [
'name' => [
'type' => Schema::DT_VARCHAR128,
@@ -50,7 +61,7 @@ class GroupModel extends AbstractUniverseModel {
*/
public function getData(array $additionalData = []){
$groupData = (object) [];
$groupData->id = $this->id;
$groupData->id = $this->_id;
$groupData->name = $this->name;
if($typesData = $this->getTypesData($additionalData)){
@@ -65,7 +76,7 @@ class GroupModel extends AbstractUniverseModel {
* @param bool $published
* @return array|mixed
*/
protected function getTypes(bool $published = true){
public function getTypes(bool $published = true){
$types = [];
if($published){
$this->filter('types', [
@@ -102,7 +113,7 @@ class GroupModel extends AbstractUniverseModel {
* @return int
*/
public function getTypesCount(bool $published = true) : int {
return $this->dry() ? 0 : count($this->getTypes($published));
return $this->valid() ? count($this->getTypes($published)) : 0;
}
/**
@@ -111,8 +122,7 @@ class GroupModel extends AbstractUniverseModel {
* @param array $additionalOptions
*/
protected function loadData(int $id, string $accessToken = '', array $additionalOptions = []){
$data = self::getF3()->ccpClient()->getUniverseGroupData($id);
if(!empty($data)){
if(!empty($data = self::getUniverseGroupData($id))){
/**
* @var $category CategoryModel
*/
@@ -127,24 +137,62 @@ class GroupModel extends AbstractUniverseModel {
/**
* load types data for this group
* @return int
* @param int $offset
* @param int $length 0 -> all types
* @return array
*/
public function loadTypesData(){
$count = 0;
if( !$this->dry() ){
$data = self::getF3()->ccpClient()->getUniverseGroupData($this->_id);
if(!empty($data)){
foreach((array)$data['types'] as $typeId){
/**
* @var $type TypeModel
*/
$type = $this->rel('types');
$type->loadById($typeId);
$type->reset();
$count++;
}
public function loadTypesData(int $offset = 0, int $length = 0) : array {
$info = ['countAll' => 0, 'countChunk' => 0, 'count' => 0, 'offset' => $offset];
if(
$this->valid() &&
!empty($data = self::getUniverseGroupData($this->_id))
){
$info['countAll'] = count($data['types']);
array_multisort($data['types'], SORT_ASC, SORT_NUMERIC);
if($length){
$data['types'] = array_slice($data['types'], $offset, $length);
}
$info['countChunk'] = count($data['types']);
foreach($data['types'] as $typeId){
/**
* @var $type TypeModel
*/
$type = $this->rel('types');
$type->storeDogmaAttributes = $this->storeDogmaAttributes;
$type->loadById($typeId);
$type->reset();
$info['count']++;
$info['offset']++;
}
}
return $count;
return $info;
}
/**
* @param int $id
* @return array
*/
public static function getUniverseGroupData(int $id) : array {
return self::getF3()->ccpClient()->getUniverseGroupData($id);
}
/**
* @return array
*/
public static function getUniverseGroups() : array {
return self::getF3()->ccpClient()->getUniverseGroups();
}
/**
* @param int $id
* @return array
*/
public static function getUniverseGroupTypes(int $id) : array {
return empty($data = self::getUniverseGroupData($id)) ? [] : $data['types'];
}
}

View File

@@ -66,28 +66,13 @@ class PlanetModel extends AbstractUniverseModel {
* @return \stdClass
*/
public function getData(){
$planetData = (object) [];
$planetData->name = $this->name;
$data = (object) [];
$data->name = $this->name;
$planetData->type = (object) [];
$planetData->type->name = $this->typeId->name;
$data->type = (object) [];
$data->type->name = $this->typeId->name;
return $planetData;
}
/**
* setter for positions array (x/y/z)
* @param $position
* @return null
*/
public function set_position($position){
$position = (array)$position;
if(count($position) === 3){
$this->x = $position['x'];
$this->y = $position['y'];
$this->z = $position['z'];
}
return null;
return $data;
}
/**

View File

@@ -0,0 +1,82 @@
<?php
/**
* Created by PhpStorm.
* User: Exodus 4D
* Date: 22.10.2019
* Time: 03:21
*/
namespace Model\Universe;
use DB\SQL\Schema;
class RaceModel extends AbstractUniverseModel {
/**
* @var string
*/
protected $table = 'race';
/**
* @var array
*/
protected $fieldConf = [
'name' => [
'type' => Schema::DT_VARCHAR128,
'nullable' => false,
'default' => ''
],
'description' => [
'type' => Schema::DT_TEXT
],
'factionId' => [
'type' => Schema::DT_INT,
'index' => true,
'belongs-to-one' => 'Model\Universe\FactionModel',
'constraint' => [
[
'table' => 'faction',
'on-delete' => 'CASCADE'
]
],
'validate' => 'notDry'
],
'stations' => [
'has-many' => ['Model\Universe\StationModel', 'raceId']
]
];
/**
* get data
* @return \stdClass
*/
public function getData(){
$data = (object) [];
$data->id = $this->_id;
$data->name = $this->name;
$data->faction = $this->factionId->getData();
return $data;
}
/**
* load data from API into $this and save $this
* @param int $id
* @param string $accessToken
* @param array $additionalOptions
*/
protected function loadData(int $id, string $accessToken = '', array $additionalOptions = []){
$data = self::getF3()->ccpClient()->getUniverseRaceData($id);
if(!empty($data) && !isset($data['error'])){
/**
* @var $faction FactionModel
*/
$faction = $this->rel('factionId');
$faction->loadById($data['factionId'], $accessToken, $additionalOptions);
$data['factionId'] = $faction;
$this->copyfrom($data, ['id', 'name', 'description', 'factionId']);
$this->save();
}
}
}

View File

@@ -32,6 +32,9 @@ class RegionModel extends AbstractUniverseModel {
],
'constellations' => [
'has-many' => ['Model\Universe\ConstellationModel', 'regionId']
],
'systemNeighbours' => [
'has-many' => ['Model\Universe\SystemNeighbourModel', 'regionId']
]
];

View File

@@ -0,0 +1,101 @@
<?php
namespace Model\Universe;
use DB\SQL\Schema;
class SovereigntyMapModel extends AbstractUniverseModel {
/**
* @var string
*/
protected $table = 'sovereignty_map';
/**
* @var array
*/
protected $fieldConf = [
'systemId' => [
'type' => Schema::DT_INT,
'index' => true,
'unique' => true,
'belongs-to-one' => 'Model\Universe\SystemModel',
'constraint' => [
[
'table' => 'system',
'on-delete' => 'CASCADE'
]
],
'validate' => 'notDry'
],
'factionId' => [
'type' => Schema::DT_INT,
'index' => true,
'belongs-to-one' => 'Model\Universe\FactionModel',
'constraint' => [
[
'table' => 'faction',
'on-delete' => 'CASCADE'
]
]
],
'allianceId' => [
'type' => Schema::DT_INT,
'index' => true,
'belongs-to-one' => 'Model\Universe\AllianceModel',
'constraint' => [
[
'table' => 'alliance',
'on-delete' => 'CASCADE'
]
]
],
'corporationId' => [
'type' => Schema::DT_INT,
'index' => true,
'belongs-to-one' => 'Model\Universe\CorporationModel',
'constraint' => [
[
'table' => 'corporation',
'on-delete' => 'CASCADE'
]
]
]
];
/**
* No static columns added
* @var bool
*/
protected $addStaticFields = false;
/**
* get data
* @return \stdClass
*/
public function getData(){
$data = (object) [];
if($this->factionId){
$data->faction = $this->factionId->getData();
}else{
if($this->allianceId){
$data->alliance = $this->allianceId->getData();
}
if($this->corporationId){
$data->corporation = $this->corporationId->getData();
}
}
return $data;
}
/**
* @param int $id
* @param string $accessToken
* @param array $additionalOptions
*/
protected function loadData(int $id, string $accessToken = '', array $additionalOptions = []){}
}

View File

@@ -74,7 +74,6 @@ class StargateModel extends AbstractUniverseModel {
];
public function getData(){
$stargateData = (object) [];
$stargateData->id = $this->_id;
$stargateData->type = $this->typeId->name;
@@ -83,21 +82,6 @@ class StargateModel extends AbstractUniverseModel {
return $stargateData;
}
/**
* setter for positions array (x/y/z)
* @param $position
* @return null
*/
public function set_position($position){
$position = (array)$position;
if(count($position) === 3){
$this->x = $position['x'];
$this->y = $position['y'];
$this->z = $position['z'];
}
return null;
}
/**
* @param int $id
* @param string $accessToken

View File

@@ -12,8 +12,14 @@ use DB\SQL\Schema;
class StarModel extends AbstractUniverseModel {
/**
* @var string
*/
protected $table = 'star';
/**
* @var array
*/
protected $fieldConf = [
'name' => [
'type' => Schema::DT_VARCHAR128,

View File

@@ -0,0 +1,165 @@
<?php
/**
* Created by PhpStorm.
* User: Exodus 4D
* Date: 22.10.2019
* Time: 03:00
*/
namespace Model\Universe;
use DB\SQL\Schema;
class StationModel extends AbstractUniverseModel {
/**
* @var string
*/
protected $table = 'station';
/**
* @var array
*/
protected $fieldConf = [
'name' => [
'type' => Schema::DT_VARCHAR128,
'nullable' => false,
'default' => ''
],
'systemId' => [
'type' => Schema::DT_INT,
'index' => true,
'belongs-to-one' => 'Model\Universe\SystemModel',
'constraint' => [
[
'table' => 'system',
'on-delete' => 'CASCADE'
]
],
'validate' => 'notDry'
],
'typeId' => [
'type' => Schema::DT_INT,
'index' => true,
'belongs-to-one' => 'Model\Universe\TypeModel',
'constraint' => [
[
'table' => 'type',
'on-delete' => 'CASCADE'
]
],
'validate' => 'notDry'
],
'corporationId' => [
'type' => Schema::DT_INT,
'index' => true,
'belongs-to-one' => 'Model\Universe\CorporationModel',
'constraint' => [
[
'table' => 'corporation',
'on-delete' => 'CASCADE'
]
]
],
'raceId' => [
'type' => Schema::DT_INT,
'index' => true,
'belongs-to-one' => 'Model\Universe\RaceModel',
'constraint' => [
[
'table' => 'race',
'on-delete' => 'CASCADE'
]
]
],
'services' => [
'type' => self::DT_JSON
],
'x' => [
'type' => Schema::DT_BIGINT,
'nullable' => false,
'default' => 0
],
'y' => [
'type' => Schema::DT_BIGINT,
'nullable' => false,
'default' => 0
],
'z' => [
'type' => Schema::DT_BIGINT,
'nullable' => false,
'default' => 0
]
];
/**
* get data
* @return \stdClass
*/
public function getData(){
$data = (object) [];
$data->id = $this->_id;
$data->name = $this->name;
$data->type = $this->typeId->getData();
$data->services = $this->services ? : [];
// according to ESIs Swagger conf, "raceId" AND "corporationId"(= "owner") are optional
// -> I haven´t seen any imported NPC station data where "raceId" OR "corporationId" not exist
if($this->corporationId){
$data->corporation = $this->corporationId->getData();
}
if($this->raceId){
$data->race = $this->raceId->getData();
}
return $data;
}
/**
* load data from API into $this and save $this
* @param int $id
* @param string $accessToken
* @param array $additionalOptions
*/
protected function loadData(int $id, string $accessToken = '', array $additionalOptions = []){
$data = self::getF3()->ccpClient()->getUniverseStationData($id);
if(!empty($data) && !isset($data['error'])){
/**
* @var $system SystemModel
*/
$system = $this->rel('systemId');
$system->loadById($data['systemId'], $accessToken, $additionalOptions);
$data['systemId'] = $system;
/**
* @var $type TypeModel
*/
$type = $this->rel('typeId');
$type->loadById($data['typeId'], $accessToken, $additionalOptions);
$data['typeId'] = $type;
if($data['corporationId']){
/**
* @var $faction CorporationModel
*/
$corporation = $this->rel('corporationId');
$corporation->loadById($data['corporationId'], $accessToken, $additionalOptions);
$data['corporationId'] = $corporation;
}
if($data['raceId']){
/**
* @var $race RaceModel
*/
$race = $this->rel('raceId');
$race->loadById($data['raceId'], $accessToken, $additionalOptions);
$data['raceId'] = $race;
}
$this->copyfrom($data, ['id', 'name', 'systemId', 'typeId', 'corporationId', 'raceId', 'services', 'position']);
$this->save();
}
}
}

View File

@@ -13,8 +13,14 @@ use DB\SQL\Schema;
class StructureModel extends AbstractUniverseModel {
/**
* @var string
*/
protected $table = 'structure';
/**
* @var array
*/
protected $fieldConf = [
'name' => [
'type' => Schema::DT_VARCHAR128,
@@ -23,9 +29,15 @@ class StructureModel extends AbstractUniverseModel {
],
'systemId' => [
'type' => Schema::DT_INT,
'nullable' => false,
'default' => 0,
'index' => true
'index' => true,
'belongs-to-one' => 'Model\Universe\SystemModel',
'constraint' => [
[
'table' => 'system',
'on-delete' => 'CASCADE'
]
],
'validate' => 'notDry'
],
'typeId' => [
'type' => Schema::DT_INT,
@@ -63,9 +75,10 @@ class StructureModel extends AbstractUniverseModel {
*/
public function getData(): \stdClass {
$data = (object) [];
if(!$this->dry()){
$data->id = $this->_id;
if($this->valid()){
$data->id = $this->_id;
$data->name = $this->name;
$data->type = $this->typeId->getData();
}
return $data;
}
@@ -78,7 +91,7 @@ class StructureModel extends AbstractUniverseModel {
*/
protected function loadData(int $id, string $accessToken = '', array $additionalOptions = []){
$data = self::getF3()->ccpClient()->getUniverseStructureData($id, $accessToken);
if(!empty($data)){
if(!empty($data) && !isset($data['error'])){
/**
* @var $type TypeModel
*/

View File

@@ -17,11 +17,6 @@ class SystemModel extends AbstractUniverseModel {
*/
protected $table = 'system';
/**
*
*/
const ERROR_INVALID_WORMHOLE = 'Invalid wormhole name "%s" for system: "%s"';
/**
* @var array
*/
@@ -55,18 +50,6 @@ class SystemModel extends AbstractUniverseModel {
],
'validate' => 'notDry'
],
'factionId' => [
'type' => Schema::DT_INT,
'index' => true,
'belongs-to-one' => 'Model\Universe\FactionModel',
'constraint' => [
[
'table' => 'faction',
'on-delete' => 'CASCADE'
]
],
'validate' => 'notDry'
],
'security' => [
'type' => Schema::DT_VARCHAR128
],
@@ -86,11 +69,6 @@ class SystemModel extends AbstractUniverseModel {
'effect' => [
'type' => Schema::DT_VARCHAR128
],
'shattered' => [
'type' => Schema::DT_BOOL,
'nullable' => false,
'default' => 0
],
'x' => [
'type' => Schema::DT_BIGINT,
'nullable' => false,
@@ -114,6 +92,21 @@ class SystemModel extends AbstractUniverseModel {
],
'stargates' => [
'has-many' => ['Model\Universe\StargateModel', 'systemId']
],
'stations' => [
'has-many' => ['Model\Universe\StationModel', 'systemId']
],
'structures' => [
'has-many' => ['Model\Universe\StructureModel', 'systemId']
],
'neighbour' => [
'has-one' => ['Model\Universe\SystemNeighbourModel', 'systemId']
],
'sovereignty' => [
'has-one' => ['Model\Universe\SovereigntyMapModel', 'systemId']
],
'factionWar' => [
'has-one' => ['Model\Universe\FactionWarSystemModel', 'systemId']
]
];
@@ -123,37 +116,52 @@ class SystemModel extends AbstractUniverseModel {
* @return \stdClass
*/
public function getData(){
$systemData = (object) [];
$systemData->id = $this->_id;
$systemData->name = $this->name;
$systemData->constellation = $this->constellationId->getData();
$systemData->security = $this->security;
$systemData->trueSec = (float)$this->trueSec;
$systemData->effect = $this->effect;
$systemData->shattered = (bool)$this->shattered;
$data = (object) [];
$data->id = $this->_id;
$data->name = $this->name;
$data->constellation = $this->constellationId->getData();
$data->security = $this->security;
$data->trueSec = (float)$this->trueSec;
$data->effect = $this->effect;
$data->shattered = false;
if($this->starId){
$systemData->star = $this->starId->getData();
$data->star = $this->starId->getData();
}
if($this->factionId){
$systemData->faction = $this->factionId->getData();
if($this->sovereignty){
$data->sovereignty = $this->sovereignty->getData();
}
if($this->factionWar){
$data->factionWar = $this->factionWar->getData();
}
if( !empty($planetsData = $this->getPlanetsData()) ){
$systemData->planets = $planetsData;
$data->planets = $planetsData;
// 'Shattered' systems have ONLY planets named with '(shattered)'
// -> system 'Thera' has '(shattered)' AND other planets -> not shattered.
// -> system 'J164104, 'J115422' - the only non-shattered wormholes which have a shattered planet -> not shattered.
$data->shattered = count(array_filter($planetsData, function(object $planetData){
return property_exists($planetData, 'type') &&
(strpos(strtolower($planetData->type->name), '(shattered)') !== false);
})) == count($planetsData);
}
if( !empty($staticsData = $this->getStaticsData()) ){
$systemData->statics = $staticsData;
$data->statics = $staticsData;
}
if( !empty($stargatesData = $this->getStargatesData()) ){
$systemData->stargates = $stargatesData;
$data->stargates = $stargatesData;
}
return $systemData;
if( !empty($stationsData = $this->getStationsData()) ){
$data->stations = $stationsData;
}
return $data;
}
/**
@@ -185,11 +193,18 @@ class SystemModel extends AbstractUniverseModel {
}
$this->trueSec = $trueSec;
// set 'security' for NON wormhole systems! -> those get updated from csv import
if(!preg_match('/^j\d+$/i', $this->name)){
// check for "Abyssal" system
if(
$this->get('constellationId', true) >= 22000001 &&
$this->get('constellationId', true) <= 22000025
// 'J1226-0' is also a wormhole with a '-' in the name! (single system)
if(
!preg_match('/^j(\d{6}|\d{4}-\d)$/i', $this->name) &&
$this->name != 'Thera'
){
$constellationId = (int)$this->get('constellationId', true);
if($constellationId == 23000001){
// "Pocket" system
$security = 'P';
}elseif(
$constellationId >= 22000001 &&
$constellationId <= 22000025
){
// "Abyssal" system
$security = 'A';
@@ -220,30 +235,141 @@ class SystemModel extends AbstractUniverseModel {
}
/**
* setter for positions array (x/y/z)
* @param $position
* @return null
* @param array $sovData
* @return bool true if sovereignty data changed
*/
public function set_position($position){
$position = (array)$position;
if(count($position) === 3){
$this->x = $position['x'];
$this->y = $position['y'];
$this->z = $position['z'];
public function updateSovereigntyData(array $sovData = []) : bool {
$hasChanged = false;
$systemId = (int)$sovData['systemId'];
$factionId = (int)$sovData['factionId'];
$allianceId = (int)$sovData['allianceId'];
$corporationId = (int)$sovData['corporationId'];
if($this->valid()){
if($systemId === $this->_id){
// sov data belongs to this system
$validSovData = (bool)max($factionId, $allianceId, $corporationId);
if($validSovData){
// at least one of these Ids must exist for a sovereignty relation
/**
* @var $sovereignty SovereigntyMapModel
*/
if(!$sovereignty = $this->sovereignty){
// insert new sovereignty data
$sovereignty = $this->rel('sovereignty');
}
$sovData['systemId'] = $this;
if($factionId){
// HS, L - systems have "faction war"
$sovData['allianceId'] = null;
$sovData['corporationId'] = null;
/**
* @var $faction FactionModel
*/
$faction = $sovereignty->rel('factionId');
$faction->loadById($factionId);
$sovData['factionId'] = $faction;
}else{
// 0.0 - systems have sovereignty data by corp and/or ally
$sovData['factionId'] = null;
/**
* @var $alliance AllianceModel|null
*/
$alliance = null;
if($allianceId){
$alliance = $sovereignty->rel('allianceId');
$alliance->loadById($allianceId);
}
/**
* @var $corporation CorporationModel|null
*/
$corporation = null;
if($corporationId){
$corporation = $sovereignty->rel('corporationId');
$corporation->loadById($corporationId);
}
$sovData['allianceId'] = $alliance;
$sovData['corporationId'] = $corporation;
}
$sovereignty->copyfrom($sovData, ['systemId', 'factionId', 'allianceId', 'corporationId']);
// must be called before save(). Changed fields get reset after save() is called!
if($sovereignty->changed()){
$hasChanged = true;
}
$sovereignty->save();
}elseif($this->sovereignty){
// delete existing sov data
// -> hint: WH - systems never have sovereignty data
$this->sovereignty->erase();
$hasChanged = true;
}
}
}
return null;
return $hasChanged;
}
/**
* setter for static systems (wormholes)
* -> comma separated string or array
* @param $staticNames
* @return null
* @param array $fwData
* @return bool true if faction warfare data changed
*/
public function set_staticNames($staticNames){
$staticNames = array_unique(is_string($staticNames) ? explode(',', $staticNames) : (array)$staticNames);
$this->virtual('staticNames', array_map('strtoupper', $staticNames));
return null;
public function updateFactionWarData(array $fwData = []) : bool {
$hasChanged = false;
$systemId = (int)$fwData['systemId'];
$ownerFactionId = (int)$fwData['ownerFactionId'];
$occupierFactionId = (int)$fwData['occupierFactionId'];
if($this->valid()){
if($systemId === $this->_id){
/**
* @var $factionWar FactionWarSystemModel
*/
if(!$factionWar = $this->factionWar){
// insert new faction war data
$factionWar = $this->rel('factionWar');
}
$fwData['systemId'] = $this;
if($ownerFactionId){
/**
* @var $ownerFaction FactionModel
*/
$ownerFaction = $factionWar->rel('ownerFactionId');
$ownerFaction->loadById($ownerFactionId);
$fwData['ownerFactionId'] = $ownerFaction;
}
if($occupierFactionId){
/**
* @var $occupierFaction FactionModel
*/
$occupierFaction = $factionWar->rel('occupierFactionId');
$occupierFaction->loadById($occupierFactionId);
$fwData['occupierFactionId'] = $occupierFaction;
}
$factionWar->copyfrom($fwData, ['systemId', 'ownerFactionId', 'occupierFactionId', 'contested', 'victoryPoints', 'victoryPointsThreshold']);
// must be called before save(). Changed fields get reset after save() is called!
if($factionWar->changed()){
$hasChanged = true;
}
$factionWar->save();
}
}
return $hasChanged;
}
/**
@@ -253,38 +379,8 @@ class SystemModel extends AbstractUniverseModel {
* @param $pkeys
*/
public function afterUpdateEvent($self, $pkeys){
$staticNames = (array)$self->staticNames;
if(
count($staticNames) > 0 && // make sure statics are set. In case a wh system get updated without statics are set
preg_match('/^c\d+$/i', $self->security) // make sure it is a wormhole
){
foreach((array)$self->statics as $static){
if(in_array($static->wormholeId->name, $staticNames)){
unset($staticNames[array_search($static->wormholeId->name, $staticNames)]);
}else{
$static->erase();
}
}
// add new statics
foreach($staticNames as $staticName){
$static = $self->rel('statics');
/**
* @var $wormhole WormholeModel
*/
$wormhole = $static->rel('wormholeId')->getByForeignKey('name', $staticName, ['limit' => 1]);
if( !$wormhole->dry() ){
$static->systemId = $self;
$static->wormholeId = $wormhole;
$static->save();
}
}
}
// build search index
$self->buildIndex();
return parent::afterUpdateEvent($self, $pkeys);
}
@@ -292,14 +388,14 @@ class SystemModel extends AbstractUniverseModel {
* get data from all planets
* @return array
*/
protected function getPlanetsData(){
protected function getPlanetsData() : array {
$planetsData = [];
if($this->planets){
/**
* @var $planet PlanetModel
*/
foreach($this->planets as &$planet){
/**
* @var $planet PlanetModel
*/
$planetsData[] = $planet->getData();
}
}
@@ -310,14 +406,14 @@ class SystemModel extends AbstractUniverseModel {
* get data from all static wormholes
* @return array
*/
protected function getStaticsData(){
protected function getStaticsData() : array {
$staticsData = [];
if($this->statics){
/**
* @var $static SystemStaticModel
*/
foreach($this->statics as &$static){
/**
* @var $static SystemStaticModel
*/
$staticsData[] = $static->getData();
}
}
@@ -328,25 +424,62 @@ class SystemModel extends AbstractUniverseModel {
* get data from all stargates
* @return array
*/
protected function getStargatesData(){
protected function getStargatesData() : array {
$stargatesData = [];
if($this->stargates){
/**
* @var $stargate StargateModel
*/
foreach($this->stargates as &$stargate){
/**
* @var $stargate StargateModel
*/
$stargatesData[] = $stargate->getData();
}
}
return $stargatesData;
}
/**
* get data from all stations
* @return array
*/
protected function getStationsData() : array {
$stationsData = [];
if($this->stations){
/**
* @var $station StationModel
*/
foreach($this->stations as &$station){
$data = $station->getData();
if(!$data->race){
// should never happen NPC stations always have a owning race
$data->race = (object) [];
$data->race->id = 0;
$data->race->name = 'unknown';
$data->race->faction = (object) [];
$data->race->faction->id = 0;
$data->race->faction->name = 'unknown';
}
if(!array_key_exists($data->race->faction->id, $stationsData)){
$stationsData[$data->race->faction->id] = [
'id' => $data->race->faction->id,
'name' => $data->race->name,
'stations' => []
];
}
$stationsData[$data->race->faction->id]['stations'][] = $data;
}
}
return $stationsData;
}
/**
* update system from ESI
*/
public function updateModel(){
if( !$this->dry() ){
if($this->valid()){
$this->loadData($this->_id);
$this->loadPlanetsData();
}
@@ -387,7 +520,7 @@ class SystemModel extends AbstractUniverseModel {
* load planets data for this system
*/
public function loadPlanetsData(){
if( !$this->dry() ){
if($this->valid()){
$data = self::getF3()->ccpClient()->getUniverseSystemData($this->_id);
if($data['planets']){
// planets are optional since ESI v4 (e.g. Abyssal systems)
@@ -408,9 +541,9 @@ class SystemModel extends AbstractUniverseModel {
* -> stargates to destination system which is not in DB get ignored
*/
public function loadStargatesData(){
if( !$this->dry() ){
if($this->valid()){
$data = self::getF3()->ccpClient()->getUniverseSystemData($this->_id);
if(!empty($data)){
if($data['stargates']){
foreach((array)$data['stargates'] as $stargateId){
/**
* @var $stargate StargateModel
@@ -422,4 +555,23 @@ class SystemModel extends AbstractUniverseModel {
}
}
}
/**
* load NPC owned stations for this system
*/
public function loadStationsData(){
if($this->valid()){
$data = self::getF3()->ccpClient()->getUniverseSystemData($this->_id);
if($data['stations']){
foreach((array)$data['stations'] as $stationId){
/**
* @var $station SystemModel
*/
$station = $this->rel('stations');
$station->loadById($stationId);
$station->reset();
}
}
}
}
}

View File

@@ -0,0 +1,92 @@
<?php
namespace Model\Universe;
use DB\SQL\Schema;
class SystemNeighbourModel extends AbstractUniverseModel {
/**
* @var string
*/
protected $table = 'system_neighbour';
/**
* allow table truncate
* -> used on /setup page or index build
* @var bool
*/
protected $allowTruncate = true;
/**
* @var array
*/
protected $fieldConf = [
'regionId' => [
'type' => Schema::DT_INT,
'index' => true,
'belongs-to-one' => 'Model\Universe\RegionModel',
'constraint' => [
[
'table' => 'region',
'on-delete' => 'CASCADE'
]
],
'validate' => 'notDry'
],
'constellationId' => [
'type' => Schema::DT_INT,
'index' => true,
'belongs-to-one' => 'Model\Universe\ConstellationModel',
'constraint' => [
[
'table' => 'constellation',
'on-delete' => 'CASCADE'
]
],
'validate' => 'notDry'
],
'systemId' => [
'type' => Schema::DT_INT,
'index' => true,
'unique' => true,
'belongs-to-one' => 'Model\Universe\SystemModel',
'constraint' => [
[
'table' => 'system',
'on-delete' => 'CASCADE'
]
],
'validate' => 'notDry'
],
'systemName' => [
'type' => Schema::DT_VARCHAR128,
'nullable' => false,
'default' => ''
],
'jumpNodes' => [
'type' => Schema::DT_VARCHAR512,
'nullable' => false,
'default' => ''
],
'trueSec' => [
'type' => Schema::DT_DECIMAL,
'nullable' => false,
'default' => 0
]
];
/**
* No static columns added
* @var bool
*/
protected $addStaticFields = false;
/**
* @param int $id
* @param string $accessToken
* @param array $additionalOptions
*/
protected function loadData(int $id, string $accessToken = '', array $additionalOptions = []){}
}

View File

@@ -12,8 +12,14 @@ use DB\SQL\Schema;
class SystemStaticModel extends AbstractUniverseModel {
/**
* @var string
*/
protected $table = 'system_static';
/**
* @var array
*/
protected $fieldConf = [
'systemId' => [
'type' => Schema::DT_INT,
@@ -27,13 +33,13 @@ class SystemStaticModel extends AbstractUniverseModel {
],
'validate' => 'notDry'
],
'wormholeId' => [
'typeId' => [
'type' => Schema::DT_INT,
'index' => true,
'belongs-to-one' => 'Model\Universe\WormholeModel',
'belongs-to-one' => 'Model\Universe\TypeModel',
'constraint' => [
[
'table' => 'wormhole',
'table' => 'type',
'on-delete' => 'CASCADE'
]
],
@@ -52,8 +58,14 @@ class SystemStaticModel extends AbstractUniverseModel {
* @return null|string
*/
public function getData(){
return $this->wormholeId ? $this->wormholeId->name : null;
return $this->typeId ? $this->typeId->getWormholeName() : null;
}
/**
* @param int $id
* @param string $accessToken
* @param array $additionalOptions
*/
protected function loadData(int $id, string $accessToken = '', array $additionalOptions = []){}
/**
@@ -66,7 +78,7 @@ class SystemStaticModel extends AbstractUniverseModel {
*/
public static function setup($db = null, $table = null, $fields = null){
if($status = parent::setup($db, $table, $fields)){
$status = parent::setMultiColumnIndex(['systemId', 'wormholeId'], true);
$status = parent::setMultiColumnIndex(['systemId', 'typeId'], true);
}
return $status;
}

View File

@@ -0,0 +1,92 @@
<?php
/**
* Created by PhpStorm.
* User: Exodus4D
* Date: 15.08.2019
* Time: 22:00
*/
namespace Model\Universe;
use DB\SQL\Schema;
class TypeAttributeModel extends AbstractUniverseModel {
/**
* @var string
*/
protected $table = 'type_attribute';
/**
* @var array
*/
protected $fieldConf = [
'typeId' => [
'type' => Schema::DT_INT,
'index' => true,
'belongs-to-one' => 'Model\Universe\TypeModel',
'constraint' => [
[
'table' => 'type',
'on-delete' => 'CASCADE'
]
],
'validate' => 'notDry'
],
'attributeId' => [
'type' => Schema::DT_INT,
'index' => true,
'belongs-to-one' => 'Model\Universe\DogmaAttributeModel',
'constraint' => [
[
'table' => 'dogma_attribute',
'on-delete' => 'CASCADE'
]
],
'validate' => 'notDry'
],
'value' => [
'type' => Schema::DT_FLOAT,
'nullable' => false,
'default' => 0
]
];
/**
* No static columns added
* @var bool
*/
protected $addStaticFields = false;
/**
* @return \stdClass
*/
public function getData(){
$typeAttributeData = $this->attributeId->getData();
$typeAttributeData->value = (float)$this->value;
return $typeAttributeData;
}
/**
* @param int $id
* @param string $accessToken
* @param array $additionalOptions
*/
protected function loadData(int $id, string $accessToken = '', array $additionalOptions = []){}
/**
* overwrites parent
* @param null $db
* @param null $table
* @param null $fields
* @return bool
* @throws \Exception
*/
public static function setup($db = null, $table = null, $fields = null){
if($status = parent::setup($db, $table, $fields)){
$status = parent::setMultiColumnIndex(['typeId', 'attributeId'], true);
}
return $status;
}
}

View File

@@ -9,11 +9,31 @@
namespace Model\Universe;
use DB\SQL\Schema;
use lib\Config;
use lib\Util;
class TypeModel extends AbstractUniverseModel {
protected $table = 'type';
/**
* @var string
*/
protected $table = 'type';
/**
* default store option for 'dogma' typeAttributes data
* -> set to true will store all typeAttributes from ESI for a type
* -> should be enabled for specific types, where data is used by Pathfinder
*/
const DEFAULT_STORE_DOGMA_ATTRIBUTES = false;
/**
* @var bool
*/
public $storeDogmaAttributes = self::DEFAULT_STORE_DOGMA_ATTRIBUTES;
/**
* @var array
*/
protected $fieldConf = [
'name' => [
'type' => Schema::DT_VARCHAR128,
@@ -83,6 +103,9 @@ class TypeModel extends AbstractUniverseModel {
'default' => 0,
'index' => true
],
'stations' => [
'has-many' => ['Model\Universe\StationModel', 'typeId']
],
'structures' => [
'has-many' => ['Model\Universe\StructureModel', 'typeId']
],
@@ -92,11 +115,46 @@ class TypeModel extends AbstractUniverseModel {
'stars' => [
'has-many' => ['Model\Universe\StarModel', 'typeId']
],
'wormholes' => [
'has-many' => ['Model\Universe\WormholeModel', 'typeId']
'attributes' => [
'has-many' => ['Model\Universe\TypeAttributeModel', 'typeId']
],
'stargates' => [
'has-many' => ['Model\Universe\StargateModel', 'typeId']
],
'statics' => [
'has-many' => ['Model\Universe\SystemStaticModel', 'typeId']
]
];
/**
* set 'dogma_attributes' during ESI import process to a virtual field
* -> 'dogma_attributes' get imported after type is saved
* @see loadData()
* @param $dogmaAttributesData
* @return null
*/
public function set_dogma_attributes($dogmaAttributesData){
$this->virtual('dogmaAttributes', (array)$dogmaAttributesData);
return null;
}
/**
* special getter for 'wormhole' types
* @return string|null
*/
public function getWormholeName(){
return self::formatWormholeName($this->name);
}
/**
* @param bool $mapper
* @return NULL|void
*/
public function reset($mapper = true){
$this->clearVirtual('dogmaAttributes');
parent::reset($mapper);
}
/**
* get type data
* @param array $additionalData
@@ -104,24 +162,70 @@ class TypeModel extends AbstractUniverseModel {
*/
public function getData(array $additionalData = []){
$typeData = (object) [];
$typeData->id = $this->id;
$typeData->id = $this->_id;
$typeData->name = $this->name;
foreach($additionalData as $key){
$typeData->$key = $this->$key;
if($key == 'attributes'){
// add 'dogma' typeAttributes data
$typeData->$key = $this->getAttributesData();
}elseif($this->exists($key)){
$typeData->$key = $this->$key;
}
}
return $typeData;
}
/**
* get wormholeData from object
* @return \stdClass
*/
public function getWormholeData() : \stdClass {
$wormholeData = (object) [];
if($this->valid()){
$wormholeData->name = $this->getWormholeName();
$wormholeData->static = $this->statics ? (bool)count($this->statics) : false;
$wormholeData->security = '';
$wormholeData->massTotal = null;
$wormholeData->massIndividual = null;
$wormholeData->maxStableTime = null;
foreach($this->getAttributesData() as $id => $attributesData){
switch($id){
case 1381: // 'wormholeTargetSystemClass' -> 'security'
$wormholeData->security = self::getSystemSecurityFromId((int)$attributesData['value']);
break;
case 1383: // 'wormholeMaxStableMass' -> 'massTotal'
$wormholeData->massTotal = $attributesData['value'];
break;
case 1385: // 'wormholeMaxJumpMass' -> 'massIndividual'
$wormholeData->massIndividual = $attributesData['value'];
break;
case 1384: // 'wormholeMassRegeneration' -> 'massRegeneration'
if($attributesData['value']){
$wormholeData->massRegeneration = $attributesData['value'];
}
break;
case 1382: // 'wormholeMaxStableTime' -> 'maxStableTime'
$wormholeData->maxStableTime = $attributesData['value'] / 60;
break;
case Config::ESI_DOGMA_ATTRIBUTE_SCANWHSTRENGTH_ID: // 'scanWormholeStrength' -> 'scanWormholeStrength'
$wormholeData->scanWormholeStrength = $attributesData['value'];
break;
}
}
}
return $wormholeData;
}
/**
* get shipData from object
* -> more fields can be added in here if needed
* @return \stdClass
*/
public function getShipData(): \stdClass {
public function getShipData() : \stdClass {
$shipData = (object) [];
if(!$this->dry()){
if($this->valid()){
$shipData->typeId = $this->_id;
$shipData->typeName = $this->name;
$shipData->mass = $this->mass;
@@ -129,6 +233,118 @@ class TypeModel extends AbstractUniverseModel {
return $shipData;
}
/**
* @return array
*/
protected function getAttributesData() : array {
$attributesData = [];
if($this->attributes){
foreach($this->attributes as $typeAttribute){
/**
* @var $typeAttribute TypeAttributeModel
*/
$attributesData[] = get_object_vars($typeAttribute->getData());
}
}
return Util::arrayGetBy($attributesData, 'id');
}
/**
* Event "Hook" function
* return false will stop any further action
* @param self $self
* @param $pkeys
*/
public function afterInsertEvent($self, $pkeys){
$self->syncDogmaAttributes();
return parent::afterInsertEvent($self, $pkeys);
}
/**
* Event "Hook" function
* @param self $self
* @param $pkeys
*/
public function afterUpdateEvent($self, $pkeys){
$self->syncDogmaAttributes();
return parent::afterUpdateEvent($self, $pkeys);
}
/**
* sync existing 'dogma' typeAttributes data with "new/updated" typeAttributes
* -> $this->dogmaAttributes must be set before calling this method
*/
protected function syncDogmaAttributes(){
if(
$this->storeDogmaAttributes &&
!empty($dogmaAttributesData = (array)$this->dogmaAttributes)
){
foreach((array)$this->attributes as $typeAttribute){
$key = array_search($typeAttribute->get('attributeId', true), array_column($dogmaAttributesData, 'attributeId'));
if($key !== false){
// attribute still belongs to this 'type' -> update value
$typeAttribute->copyfrom($dogmaAttributesData[$key], ['value']);
$typeAttribute->save();
unset($dogmaAttributesData[$key]);
$dogmaAttributesData = array_values($dogmaAttributesData);
}else{
// attribute no longer belongs to this 'type'
$typeAttribute->erase();
}
}
// add new dogmaTypes
foreach($dogmaAttributesData as $dogmaAttributeData){
/**
* @var $typeAttribute TypeAttributeModel
* @var $dogmaAttribute DogmaAttributeModel
*/
$typeAttribute = $this->rel('attributes');
$dogmaAttribute = $typeAttribute->rel('attributeId');
$dogmaAttribute->loadById($dogmaAttributeData['attributeId']);
if($dogmaAttribute->valid()){
$typeAttribute->typeId = $this;
$typeAttribute->attributeId = $dogmaAttribute;
$typeAttribute->value = $dogmaAttributeData['value'];
$typeAttribute->save();
}
}
}
}
/**
* manipulate 'dogma_attributes' array be reference
* -> used to inject custom attributes (not available from ESI)
* @param array $data
*/
private function manipulateDogmaAttributes(array &$data){
if(!$this->storeDogmaAttributes){
// attributes should not get saved
unset($data['dogma_attributes']);
}elseif(!empty($data['dogma_attributes'])){
switch($data['groupId']){
case Config::ESI_GROUP_WORMHOLE_ID:
if(
!empty($wormholesCSVData = static::getCSVData('wormhole', 'name')) &&
!empty($wormholeCSVData = $wormholesCSVData[self::formatWormholeName($data['name'])])
){
// found relevant wormhole data in *.csv for current type
if(!empty($scanWormholeStrength = (float)$wormholeCSVData['scanWormholeStrength'])){
$data['dogma_attributes'][] = [
'attributeId' => Config::ESI_DOGMA_ATTRIBUTE_SCANWHSTRENGTH_ID,
'value' => $scanWormholeStrength
];
}
}
break;
}
}
}
/**
* load data from API into $this and save $this
* @param int $id
@@ -138,6 +354,8 @@ class TypeModel extends AbstractUniverseModel {
protected function loadData(int $id, string $accessToken = '', array $additionalOptions = []){
$data = self::getF3()->ccpClient()->getUniverseTypesData($id);
if(!empty($data)){
$this->manipulateDogmaAttributes($data);
/**
* @var $group GroupModel
*/
@@ -149,4 +367,12 @@ class TypeModel extends AbstractUniverseModel {
$this->save();
}
}
/**
* @param string|null $name
* @return string|null
*/
public static function formatWormholeName(?string $name) : ?string {
return (!empty($name) && !empty($format = @end(explode(' ', $name)))) ? $format : null;
}
}

View File

@@ -1,192 +0,0 @@
<?php
/**
* Created by PhpStorm.
* User: Exodus 4D
* Date: 13.05.2018
* Time: 18:36
*/
namespace Model\Universe;
use DB\SQL\Schema;
class WormholeModel extends AbstractUniverseModel {
protected $table = 'wormhole';
public static $enableDataExport = true;
public static $enableDataImport = true;
protected $fieldConf = [
'name' => [
'type' => Schema::DT_VARCHAR128,
'nullable' => false,
'default' => '',
'index' => true,
'unique' => true
],
'typeId' => [
'type' => Schema::DT_INT,
'index' => true,
'belongs-to-one' => 'Model\Universe\TypeModel',
'constraint' => [
[
'table' => 'type',
'on-delete' => 'SET NULL'
]
],
'validate' => 'notDry'
],
'static' => [
'type' => Schema::DT_BOOL,
'nullable' => false,
'default' => 0
],
'security' => [
'type' => Schema::DT_VARCHAR128,
'nullable' => false,
'default' => ''
],
'massTotal' => [
'type' => Schema::DT_BIGINT,
'nullable' => true,
'default' => null
],
'massIndividual' => [
'type' => Schema::DT_BIGINT,
'nullable' => true,
'default' => null
],
'massRegeneration' => [
'type' => Schema::DT_BIGINT,
'nullable' => true,
'default' => null
],
'maxStableTime' => [
'type' => Schema::DT_TINYINT,
'nullable' => true,
'default' => null
],
'signatureStrength' => [
'type' => Schema::DT_FLOAT,
'nullable' => true,
'default' => null
],
'systems' => [
'has-many' => ['Model\Universe\SystemStaticModel', 'wormholeId']
]
];
/**
* get wormhole data
* @return \stdClass
*/
public function getData(){
$wormholeData = (object) [];
$wormholeData->name = $this->name;
$wormholeData->static = $this->static;
$wormholeData->security = $this->security;
$wormholeData->massTotal = $this->massTotal;
$wormholeData->massIndividual = $this->massIndividual;
if($this->massRegeneration){
$wormholeData->massRegeneration = $this->massRegeneration;
}
$wormholeData->maxStableTime = $this->maxStableTime;
// signature strength as defined by http://wiki.eve-inspiracy.com/index.php?title=Wormhole_Signature_Strength_List
if($this->signatureStrength){
$wormholeData->signatureStrength = $this->signatureStrength;
}
return $wormholeData;
}
/**
* setter for typeId
* @param string $typeId
* @return string|int|null
*/
public function set_typeId($typeId){
if(!is_object($typeId)){
/**
* @var $type TypeModel
*/
$type = $this->rel('typeId');
$type->loadById((int)$typeId);
$typeId = $type->dry() ? null : $type->_id;
}
return $typeId;
}
/**
* setter for massTotal
* @param string $mass
* @return int|null
*/
public function set_massTotal($mass){
$mass = (int)$mass;
return $mass ? : null;
}
/**
* setter for massIndividual
* @param string $mass
* @return string|null
*/
public function set_massIndividual($mass){
$mass = (int)$mass;
return $mass ? : null;
}
/**
* setter for massRegeneration
* @param $mass
* @return int|null
*/
public function set_massRegeneration($mass){
$mass = (int)$mass;
return $mass ? : null;
}
/**
* setter for maxStableTime
* @param string $hours
* @return int|null
*/
public function set_maxStableTime($hours){
$hours = (int)$hours;
return $hours ? : null;
}
/**
* setter for signatureStrength
* @param string $strength
* @return float|null
*/
public function set_signatureStrength($strength){
$strength = (float)$strength;
return $strength ? : null;
}
/**
* @param array $fields
* @return bool
*/
public function exportData(array $fields = [
'id', 'name', 'typeId', 'static', 'security', 'massTotal', 'massIndividual',
'massRegeneration', 'maxStableTime', 'signatureStrength']
) : bool {
return parent::exportData($fields);
}
/**
* @param int $id
* @param string $accessToken
* @param array $additionalOptions
*/
protected function loadData(int $id, string $accessToken = '', array $additionalOptions = []){}
}

View File

@@ -13,8 +13,8 @@ NAME = Pathfinder
; Version is used for CSS/JS cache busting and is part of the URL for static resources:
; e.g. public/js/vX.X.X/app.js
; Syntax: String (current version)
; Default: v1.5.0
VERSION = v1.5.3
; Default: v1.5.4
VERSION = v1.5.4
; Contact information [optional]
; Shown on 'licence', 'contact' page.
@@ -377,6 +377,9 @@ LOG_LINES = 1000
[PATHFINDER.API]
CCP_IMAGE_SERVER = https://image.eveonline.com
Z_KILLBOARD = https://zkillboard.com/api
EVEEYE = https://eveeye.com
DOTLAN = http://evemaps.dotlan.net
ANOIK = http://anoik.is
; GitHub Developer API
GIT_HUB = https://api.github.com

View File

@@ -56,12 +56,13 @@ PDO_TIMEOUT = 2
; MySql variables. Values are auto. set as 'SESSION' vars
; https://dev.mysql.com/doc/refman/5.5/en/show-variables.html
DEFAULT_STORAGE_ENGINE = InnoDB
CHARACTER_SET_DATABASE = utf8
CHARACTER_SET_CLIENT = utf8
CHARACTER_SET_RESULTS = utf8
CHARACTER_SET_CONNECTION = utf8
COLLATION_DATABASE = utf8_general_ci
COLLATION_CONNECTION = utf8_general_ci
CHARACTER_SET_SERVER = utf8mb4
CHARACTER_SET_DATABASE = utf8mb4
CHARACTER_SET_CLIENT = utf8mb4
CHARACTER_SET_RESULTS = utf8mb4
CHARACTER_SET_CONNECTION = utf8mb4
COLLATION_DATABASE = utf8mb4_unicode_ci
COLLATION_CONNECTION = utf8mb4_unicode_ci
FOREIGN_KEY_CHECKS = ON
INNODB_FILE_PER_TABLE = ON
WAIT_TIMEOUT = 28800
@@ -80,6 +81,4 @@ NODE = 6.0
NPM = 3.10.0
[REQUIREMENTS.DATA]
STRUCTURES = 33
SHIPS = 491
NEIGHBOURS = 5201

View File

@@ -38,7 +38,7 @@
"react/socket": "1.2.*",
"react/promise-stream": "1.1.*",
"clue/ndjson-react": "1.0.*",
"exodus4d/pathfinder_esi": "v1.3.1"
"exodus4d/pathfinder_esi": "v1.3.2"
},
"suggest": {
"ext-redis": "Redis can be used as cache backend."

169
composer.lock generated
View File

@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "ad512ee5d1242b7e46b3620b1b05a2a2",
"content-hash": "cd6689b64084cbe1bdbf74b1165fd472",
"packages": [
{
"name": "cache/adapter-common",
@@ -55,7 +55,7 @@
{
"name": "Tobias Nyholm",
"email": "tobias.nyholm@gmail.com",
"homepage": "https://github.com/nyholm"
"homepage": "https://github.com/Nyholm"
}
],
"description": "Common classes for PSR-6 adapters",
@@ -123,7 +123,7 @@
{
"name": "Tobias Nyholm",
"email": "tobias.nyholm@gmail.com",
"homepage": "https://github.com/nyholm"
"homepage": "https://github.com/Nyholm"
}
],
"description": "A PSR-6 cache implementation using a php array. This implementation supports tags",
@@ -191,7 +191,7 @@
{
"name": "Tobias Nyholm",
"email": "tobias.nyholm@gmail.com",
"homepage": "https://github.com/nyholm"
"homepage": "https://github.com/Nyholm"
}
],
"description": "A PSR-6 cache implementation using filesystem. This implementation supports tags",
@@ -253,7 +253,7 @@
{
"name": "Tobias Nyholm",
"email": "tobias.nyholm@gmail.com",
"homepage": "https://github.com/nyholm"
"homepage": "https://github.com/Nyholm"
}
],
"description": "A helper trait and interface to your PSR-6 cache to support hierarchical keys.",
@@ -311,7 +311,7 @@
{
"name": "Tobias Nyholm",
"email": "tobias.nyholm@gmail.com",
"homepage": "https://github.com/nyholm"
"homepage": "https://github.com/Nyholm"
}
],
"description": "A decorator that makes your cache support namespaces",
@@ -381,7 +381,7 @@
{
"name": "Tobias Nyholm",
"email": "tobias.nyholm@gmail.com",
"homepage": "https://github.com/nyholm"
"homepage": "https://github.com/Nyholm"
}
],
"description": "A PSR-6 cache implementation using Redis (PhpRedis). This implementation supports tags",
@@ -432,7 +432,7 @@
{
"name": "Tobias Nyholm",
"email": "tobias.nyholm@gmail.com",
"homepage": "https://github.com/nyholm"
"homepage": "https://github.com/Nyholm"
},
{
"name": "Nicolas Grekas",
@@ -505,7 +505,7 @@
{
"name": "Tobias Nyholm",
"email": "tobias.nyholm@gmail.com",
"homepage": "https://github.com/nyholm"
"homepage": "https://github.com/Nyholm"
}
],
"description": "A PSR-6 cache implementation using Void. This implementation supports tags",
@@ -629,28 +629,30 @@
},
{
"name": "doctrine/lexer",
"version": "1.0.2",
"version": "1.1.0",
"source": {
"type": "git",
"url": "https://github.com/doctrine/lexer.git",
"reference": "1febd6c3ef84253d7c815bed85fc622ad207a9f8"
"reference": "e17f069ede36f7534b95adec71910ed1b49c74ea"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/doctrine/lexer/zipball/1febd6c3ef84253d7c815bed85fc622ad207a9f8",
"reference": "1febd6c3ef84253d7c815bed85fc622ad207a9f8",
"url": "https://api.github.com/repos/doctrine/lexer/zipball/e17f069ede36f7534b95adec71910ed1b49c74ea",
"reference": "e17f069ede36f7534b95adec71910ed1b49c74ea",
"shasum": ""
},
"require": {
"php": ">=5.3.2"
"php": "^7.2"
},
"require-dev": {
"phpunit/phpunit": "^4.5"
"doctrine/coding-standard": "^6.0",
"phpstan/phpstan": "^0.11.8",
"phpunit/phpunit": "^8.2"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
"dev-master": "1.1.x-dev"
}
},
"autoload": {
@@ -663,14 +665,14 @@
"MIT"
],
"authors": [
{
"name": "Roman Borschel",
"email": "roman@code-factory.org"
},
{
"name": "Guilherme Blanco",
"email": "guilhermeblanco@gmail.com"
},
{
"name": "Roman Borschel",
"email": "roman@code-factory.org"
},
{
"name": "Johannes Schmitt",
"email": "schmittjoh@gmail.com"
@@ -685,20 +687,20 @@
"parser",
"php"
],
"time": "2019-06-08T11:03:04+00:00"
"time": "2019-07-30T19:33:28+00:00"
},
{
"name": "egulias/email-validator",
"version": "2.1.9",
"version": "2.1.11",
"source": {
"type": "git",
"url": "https://github.com/egulias/EmailValidator.git",
"reference": "128cc721d771ec2c46ce59698f4ca42b73f71b25"
"reference": "92dd169c32f6f55ba570c309d83f5209cefb5e23"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/egulias/EmailValidator/zipball/128cc721d771ec2c46ce59698f4ca42b73f71b25",
"reference": "128cc721d771ec2c46ce59698f4ca42b73f71b25",
"url": "https://api.github.com/repos/egulias/EmailValidator/zipball/92dd169c32f6f55ba570c309d83f5209cefb5e23",
"reference": "92dd169c32f6f55ba570c309d83f5209cefb5e23",
"shasum": ""
},
"require": {
@@ -708,7 +710,8 @@
"require-dev": {
"dominicsayers/isemail": "dev-master",
"phpunit/phpunit": "^4.8.35||^5.7||^6.0",
"satooshi/php-coveralls": "^1.0.1"
"satooshi/php-coveralls": "^1.0.1",
"symfony/phpunit-bridge": "^4.4@dev"
},
"suggest": {
"ext-intl": "PHP Internationalization Libraries are required to use the SpoofChecking validation"
@@ -716,7 +719,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.0.x-dev"
"dev-master": "2.1.x-dev"
}
},
"autoload": {
@@ -742,7 +745,7 @@
"validation",
"validator"
],
"time": "2019-06-23T10:14:27+00:00"
"time": "2019-08-13T17:33:27+00:00"
},
{
"name": "evenement/evenement",
@@ -789,16 +792,16 @@
},
{
"name": "exodus4d/pathfinder_esi",
"version": "v1.3.1",
"version": "v1.3.2",
"source": {
"type": "git",
"url": "https://github.com/exodus4d/pathfinder_esi.git",
"reference": "392cb81f1efbd9bf69d7d943f4aefdcf3f0a617c"
"reference": "7eecfe27bb15fab5142aceeee30e07355d660773"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/exodus4d/pathfinder_esi/zipball/392cb81f1efbd9bf69d7d943f4aefdcf3f0a617c",
"reference": "392cb81f1efbd9bf69d7d943f4aefdcf3f0a617c",
"url": "https://api.github.com/repos/exodus4d/pathfinder_esi/zipball/7eecfe27bb15fab5142aceeee30e07355d660773",
"reference": "7eecfe27bb15fab5142aceeee30e07355d660773",
"shasum": ""
},
"require": {
@@ -824,9 +827,9 @@
],
"description": "ESI API library for Pathfinder",
"support": {
"source": "https://github.com/exodus4d/pathfinder_esi/tree/v1.3.1"
"source": "https://github.com/exodus4d/pathfinder_esi/tree/v1.3.2"
},
"time": "2019-05-07T11:53:44+00:00"
"time": "2019-10-08T10:20:37+00:00"
},
{
"name": "guzzlehttp/guzzle",
@@ -1017,16 +1020,16 @@
},
{
"name": "league/flysystem",
"version": "1.0.53",
"version": "1.0.55",
"source": {
"type": "git",
"url": "https://github.com/thephpleague/flysystem.git",
"reference": "08e12b7628f035600634a5e76d95b5eb66cea674"
"reference": "33c91155537c6dc899eacdc54a13ac6303f156e6"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/thephpleague/flysystem/zipball/08e12b7628f035600634a5e76d95b5eb66cea674",
"reference": "08e12b7628f035600634a5e76d95b5eb66cea674",
"url": "https://api.github.com/repos/thephpleague/flysystem/zipball/33c91155537c6dc899eacdc54a13ac6303f156e6",
"reference": "33c91155537c6dc899eacdc54a13ac6303f156e6",
"shasum": ""
},
"require": {
@@ -1097,20 +1100,20 @@
"sftp",
"storage"
],
"time": "2019-06-18T20:09:29+00:00"
"time": "2019-08-24T11:17:19+00:00"
},
{
"name": "league/html-to-markdown",
"version": "4.8.1",
"version": "4.8.2",
"source": {
"type": "git",
"url": "https://github.com/thephpleague/html-to-markdown.git",
"reference": "250d1bf45f80d15594fb6b316df777d6d4c97ad1"
"reference": "e747489191f8e9144a7270eb61f8b9516e99e413"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/thephpleague/html-to-markdown/zipball/250d1bf45f80d15594fb6b316df777d6d4c97ad1",
"reference": "250d1bf45f80d15594fb6b316df777d6d4c97ad1",
"url": "https://api.github.com/repos/thephpleague/html-to-markdown/zipball/e747489191f8e9144a7270eb61f8b9516e99e413",
"reference": "e747489191f8e9144a7270eb61f8b9516e99e413",
"shasum": ""
},
"require": {
@@ -1142,17 +1145,17 @@
"MIT"
],
"authors": [
{
"name": "Nick Cernis",
"email": "nick@cern.is",
"homepage": "http://modernnerd.net",
"role": "Original Author"
},
{
"name": "Colin O'Dell",
"email": "colinodell@gmail.com",
"homepage": "https://www.colinodell.com",
"role": "Lead Developer"
},
{
"name": "Nick Cernis",
"email": "nick@cern.is",
"homepage": "http://modernnerd.net",
"role": "Original Author"
}
],
"description": "An HTML-to-markdown conversion helper for PHP",
@@ -1161,20 +1164,20 @@
"html",
"markdown"
],
"time": "2018-12-24T17:21:44+00:00"
"time": "2019-08-02T11:57:39+00:00"
},
{
"name": "monolog/monolog",
"version": "1.24.0",
"version": "1.25.1",
"source": {
"type": "git",
"url": "https://github.com/Seldaek/monolog.git",
"reference": "bfc9ebb28f97e7a24c45bdc3f0ff482e47bb0266"
"reference": "70e65a5470a42cfec1a7da00d30edb6e617e8dcf"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Seldaek/monolog/zipball/bfc9ebb28f97e7a24c45bdc3f0ff482e47bb0266",
"reference": "bfc9ebb28f97e7a24c45bdc3f0ff482e47bb0266",
"url": "https://api.github.com/repos/Seldaek/monolog/zipball/70e65a5470a42cfec1a7da00d30edb6e617e8dcf",
"reference": "70e65a5470a42cfec1a7da00d30edb6e617e8dcf",
"shasum": ""
},
"require": {
@@ -1239,7 +1242,7 @@
"logging",
"psr-3"
],
"time": "2018-11-05T09:00:11+00:00"
"time": "2019-09-06T13:49:17+00:00"
},
{
"name": "psr/cache",
@@ -1911,16 +1914,16 @@
},
{
"name": "symfony/polyfill-iconv",
"version": "v1.11.0",
"version": "v1.12.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-iconv.git",
"reference": "f037ea22acfaee983e271dd9c3b8bb4150bd8ad7"
"reference": "685968b11e61a347c18bf25db32effa478be610f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-iconv/zipball/f037ea22acfaee983e271dd9c3b8bb4150bd8ad7",
"reference": "f037ea22acfaee983e271dd9c3b8bb4150bd8ad7",
"url": "https://api.github.com/repos/symfony/polyfill-iconv/zipball/685968b11e61a347c18bf25db32effa478be610f",
"reference": "685968b11e61a347c18bf25db32effa478be610f",
"shasum": ""
},
"require": {
@@ -1932,7 +1935,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.11-dev"
"dev-master": "1.12-dev"
}
},
"autoload": {
@@ -1966,20 +1969,20 @@
"portable",
"shim"
],
"time": "2019-02-06T07:57:58+00:00"
"time": "2019-08-06T08:03:45+00:00"
},
{
"name": "symfony/polyfill-intl-idn",
"version": "v1.11.0",
"version": "v1.12.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-intl-idn.git",
"reference": "c766e95bec706cdd89903b1eda8afab7d7a6b7af"
"reference": "6af626ae6fa37d396dc90a399c0ff08e5cfc45b2"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/c766e95bec706cdd89903b1eda8afab7d7a6b7af",
"reference": "c766e95bec706cdd89903b1eda8afab7d7a6b7af",
"url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/6af626ae6fa37d396dc90a399c0ff08e5cfc45b2",
"reference": "6af626ae6fa37d396dc90a399c0ff08e5cfc45b2",
"shasum": ""
},
"require": {
@@ -1993,7 +1996,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.9-dev"
"dev-master": "1.12-dev"
}
},
"autoload": {
@@ -2009,13 +2012,13 @@
"MIT"
],
"authors": [
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
},
{
"name": "Laurent Bassin",
"email": "laurent@bassin.info"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony polyfill for intl's idn_to_ascii and idn_to_utf8 functions",
@@ -2028,20 +2031,20 @@
"portable",
"shim"
],
"time": "2019-03-04T13:44:35+00:00"
"time": "2019-08-06T08:03:45+00:00"
},
{
"name": "symfony/polyfill-mbstring",
"version": "v1.11.0",
"version": "v1.12.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-mbstring.git",
"reference": "fe5e94c604826c35a32fa832f35bd036b6799609"
"reference": "b42a2f66e8f1b15ccf25652c3424265923eb4f17"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/fe5e94c604826c35a32fa832f35bd036b6799609",
"reference": "fe5e94c604826c35a32fa832f35bd036b6799609",
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/b42a2f66e8f1b15ccf25652c3424265923eb4f17",
"reference": "b42a2f66e8f1b15ccf25652c3424265923eb4f17",
"shasum": ""
},
"require": {
@@ -2053,7 +2056,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.11-dev"
"dev-master": "1.12-dev"
}
},
"autoload": {
@@ -2087,20 +2090,20 @@
"portable",
"shim"
],
"time": "2019-02-06T07:57:58+00:00"
"time": "2019-08-06T08:03:45+00:00"
},
{
"name": "symfony/polyfill-php72",
"version": "v1.11.0",
"version": "v1.12.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php72.git",
"reference": "ab50dcf166d5f577978419edd37aa2bb8eabce0c"
"reference": "04ce3335667451138df4307d6a9b61565560199e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/ab50dcf166d5f577978419edd37aa2bb8eabce0c",
"reference": "ab50dcf166d5f577978419edd37aa2bb8eabce0c",
"url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/04ce3335667451138df4307d6a9b61565560199e",
"reference": "04ce3335667451138df4307d6a9b61565560199e",
"shasum": ""
},
"require": {
@@ -2109,7 +2112,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.11-dev"
"dev-master": "1.12-dev"
}
},
"autoload": {
@@ -2142,7 +2145,7 @@
"portable",
"shim"
],
"time": "2019-02-06T07:57:58+00:00"
"time": "2019-08-06T08:03:45+00:00"
}
],
"packages-dev": [],

3773
export/csv/system_static.csv Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,90 +1,90 @@
"Id";"Name";"Security";"MassTotal";"MassIndividual";"MassRegeneration";"MaxStableTime";"SignatureStrength";
"1";"A009";"C13";"500000000";"5000000";"3000000000";"16";;
"2";"A239";"L";"2000000000";"300000000";;"24";"5";
"3";"A641";"H";"2000000000";"1000000000";;"16";"10";
"4";"A982";"C6";"3000000000";"300000000";;"24";"2.22";
"5";"B041";"C6";"3000000000";"300000000";"500000000";"48";;
"6";"B274";"H";"2000000000";"300000000";;"24";"10";
"7";"B449";"H";"2000000000";"1000000000";;"16";"2.5";
"8";"B520";"H";"3000000000";"300000000";"500000000";"24";;
"9";"C008";"C5";"1000000000";"5000000";"3000000000";"16";;
"10";"C125";"C2";"1000000000";"20000000";;"16";"6.67";
"11";"C140";"L";"3000000000";"1350000000";;"24";"5";
"12";"C247";"C3";"2000000000";"300000000";;"16";"10";
"13";"C248";"0.0";"3000000000";"1350000000";"500000000";"24";;
"14";"C391";"L";"3000000000";"1000000000";"500000000";"24";;
"15";"D364";"C2";"1000000000";"300000000";;"16";"1.25";
"16";"D382";"C2";"2000000000";"300000000";;"16";"6.67";
"17";"D792";"H";"3000000000";"1000000000";;"24";"2.5";
"18";"D845";"H";"5000000000";"300000000";"500000000";"24";"5";
"19";"E004";"C1";"1000000000";"5000000";"3000000000";"16";;
"20";"E175";"C4";"2000000000";"300000000";;"16";"5";
"21";"E545";"0.0";"2000000000";"300000000";;"24";"2.5";
"23";"G024";"C2";"2000000000";"300000000";;"16";"1.25";
"24";"H121";"C1";"500000000";"20000000";;"16";"10";
"25";"H296";"C5";"3000000000";"1350000000";;"24";"10";
"26";"H900";"C5";"3000000000";"300000000";;"24";"2.5";
"27";"I182";"C2";"2000000000";"300000000";;"16";"4";
"28";"J244";"L";"1000000000";"20000000";;"24";"5";
"29";"K329";"0.0";"5000000000";"1800000000";"500000000";"24";;
"30";"K346";"0.0";"3000000000";"300000000";;"24";"2.5";
"31";"L005";"C2";"1000000000";"5000000";"3000000000";"16";;
"32";"L477";"C3";"2000000000";"300000000";;"16";"5";
"33";"L614";"C5";"1000000000";"20000000";;"24";"2.5";
"35";"M267";"C3";"1000000000";"300000000";;"16";"1.25";
"36";"M555";"C5";"3000000000";"1000000000";;"24";"2.5";
"37";"M609";"C4";"1000000000";"20000000";;"16";"4";
"38";"N062";"C5";"3000000000";"300000000";;"24";"2.5";
"39";"N110";"H";"1000000000";"20000000";;"24";"10";
"40";"N290";"L";"3000000000";"1350000000";"500000000";"24";;
"41";"N432";"C5";"3000000000";"1350000000";;"24";"10";
"42";"N766";"C2";"2000000000";"300000000";;"16";"4";
"43";"N770";"C5";"3000000000";"300000000";;"24";"2.5";
"44";"N944";"L";"3000000000";"1350000000";;"24";"10";
"45";"N968";"C3";"2000000000";"300000000";;"16";"10";
"46";"O128";"C4";"1000000000";"300000000";"100000000";"24";;
"47";"O477";"C3";"2000000000";"300000000";;"16";"5";
"48";"O883";"C3";"1000000000";"20000000";;"16";"5";
"49";"P060";"C1";"500000000";"20000000";;"16";"5";
"50";"Q003";"0.0";"1000000000";"5000000";"3000000000";"16";;
"51";"Q317";"C1";"500000000";"20000000";;"16";"2.5";
"52";"R051";"L";"3000000000";"1000000000";;"16";"5";
"53";"R474";"C6";"3000000000";"300000000";;"24";"2.22";
"54";"R943";"C2";"750000000";"300000000";;"16";"6.67";
"55";"S047";"H";"3000000000";"300000000";;"24";;
"56";"S199";"0.0";"3000000000";"1350000000";;"24";"10";
"57";"S804";"C6";"1000000000";"20000000";;"24";"1.25";
"58";"T405";"C4";"2000000000";"300000000";;"16";"6.67";
"59";"U210";"L";"3000000000";"300000000";;"24";"10";
"60";"U319";"C6";"3000000000";"1350000000";"500000000";"48";;
"61";"U574";"C6";"3000000000";"300000000";;"24";"1.25";
"62";"V283";"0.0";"3000000000";"1000000000";;"24";"2.5";
"63";"V301";"C1";"500000000";"20000000";;"16";"5";
"64";"V753";"C6";"3000000000";"1350000000";;"24";"6.67";
"65";"V911";"C5";"3000000000";"1350000000";;"24";"10";
"66";"W237";"C6";"3000000000";"1350000000";;"24";"6.67";
"67";"X702";"C3";"1000000000";"300000000";;"24";"10";
"68";"X877";"C4";"2000000000";"300000000";;"16";"6.67";
"69";"Y683";"C4";"2000000000";"300000000";;"16";"4";
"70";"Y790";"C1";"500000000";"20000000";;"16";"2.5";
"71";"Z006";"C3";"1000000000";"5000000";"3000000000";"16";;
"72";"Z060";"0.0";"1000000000";"20000000";;"24";"2.5";
"73";"Z142";"0.0";"3000000000";"1350000000";;"24";"10";
"74";"Z457";"C4";"2000000000";"300000000";;"16";"4";
"75";"Z647";"C1";"500000000";"20000000";;"16";"10";
"76";"Z971";"C1";"100000000";"20000000";;"16";"10";
"80";"M001";"C4";"1000000000";"5000000";"3000000000";"16";;
"81";"E587";"0.0";"3000000000";"1000000000";;"16";;
"82";"V898";"L";"2000000000";"300000000";;"16";;
"83";"Q063";"H";"500000000";"20000000";;"16";;
"84";"G008";"C6";"1000000000";"5000000";"3000000000";"16";;
"85";"F353";"C12";"100000000";"20000000";;"16";;
"86";"F135";"C12";"750000000";"300000000";;"16";;
"87";"T458";"C12";"500000000";"20000000";;"16";;
"88";"M164";"C12";"2000000000";"300000000";;"16";;
"89";"L031";"C12";"3000000000";"1000000000";;"16";;
"90";"S877";"C14";"750000000";"300000000";;"16";;
"91";"B735";"C15";"750000000";"300000000";;"16";;
"92";"V928";"C16";"750000000";"300000000";;"16";;
"93";"C414";"C17";"750000000";"300000000";;"16";;
"94";"R259";"C18";"750000000";"300000000";;"16";;
Id;Name;scanWormholeStrength
1;A009;
2;A239;5
3;A641;10
4;A982;2.22
5;B041;
6;B274;10
7;B449;2.5
8;B520;
9;C008;
10;C125;6.67
11;C140;5
12;C247;10
13;C248;
14;C391;
15;D364;1.25
16;D382;6.67
17;D792;2.5
18;D845;5
19;E004;
20;E175;5
21;E545;2.5
23;G024;1.25
24;H121;10
25;H296;10
26;H900;2.5
27;I182;4
28;J244;5
29;K329;
30;K346;2.5
31;L005;
32;L477;5
33;L614;2.5
35;M267;1.25
36;M555;2.5
37;M609;4
38;N062;2.5
39;N110;10
40;N290;
41;N432;10
42;N766;4
43;N770;2.5
44;N944;10
45;N968;10
46;O128;
47;O477;5
48;O883;5
49;P060;5
50;Q003;
51;Q317;2.5
52;R051;5
53;R474;2.22
54;R943;6.67
55;S047;
56;S199;10
57;S804;1.25
58;T405;6.67
59;U210;10
60;U319;
61;U574;1.25
62;V283;2.5
63;V301;5
64;V753;6.67
65;V911;10
66;W237;6.67
67;X702;10
68;X877;6.67
69;Y683;4
70;Y790;2.5
71;Z006;
72;Z060;2.5
73;Z142;10
74;Z457;4
75;Z647;10
76;Z971;10
80;M001;
81;E587;
82;V898;
83;Q063;
84;G008;
85;F353;
86;F135;
87;T458;
88;M164;
89;L031;
90;S877;
91;B735;
92;V928;
93;C414;
94;R259;
1 Id Name Security SignatureStrength scanWormholeStrength MassTotal MassIndividual MassRegeneration MaxStableTime
2 1 A009 C13 500000000 5000000 3000000000 16
3 2 A239 L 5 2000000000 300000000 24
4 3 A641 H 10 2000000000 1000000000 16
5 4 A982 C6 2.22 3000000000 300000000 24
6 5 B041 C6 3000000000 300000000 500000000 48
7 6 B274 H 10 2000000000 300000000 24
8 7 B449 H 2.5 2000000000 1000000000 16
9 8 B520 H 3000000000 300000000 500000000 24
10 9 C008 C5 1000000000 5000000 3000000000 16
11 10 C125 C2 6.67 1000000000 20000000 16
12 11 C140 L 5 3000000000 1350000000 24
13 12 C247 C3 10 2000000000 300000000 16
14 13 C248 0.0 3000000000 1350000000 500000000 24
15 14 C391 L 3000000000 1000000000 500000000 24
16 15 D364 C2 1.25 1000000000 300000000 16
17 16 D382 C2 6.67 2000000000 300000000 16
18 17 D792 H 2.5 3000000000 1000000000 24
19 18 D845 H 5 5000000000 300000000 500000000 24
20 19 E004 C1 1000000000 5000000 3000000000 16
21 20 E175 C4 5 2000000000 300000000 16
22 21 E545 0.0 2.5 2000000000 300000000 24
23 23 G024 C2 1.25 2000000000 300000000 16
24 24 H121 C1 10 500000000 20000000 16
25 25 H296 C5 10 3000000000 1350000000 24
26 26 H900 C5 2.5 3000000000 300000000 24
27 27 I182 C2 4 2000000000 300000000 16
28 28 J244 L 5 1000000000 20000000 24
29 29 K329 0.0 5000000000 1800000000 500000000 24
30 30 K346 0.0 2.5 3000000000 300000000 24
31 31 L005 C2 1000000000 5000000 3000000000 16
32 32 L477 C3 5 2000000000 300000000 16
33 33 L614 C5 2.5 1000000000 20000000 24
34 35 M267 C3 1.25 1000000000 300000000 16
35 36 M555 C5 2.5 3000000000 1000000000 24
36 37 M609 C4 4 1000000000 20000000 16
37 38 N062 C5 2.5 3000000000 300000000 24
38 39 N110 H 10 1000000000 20000000 24
39 40 N290 L 3000000000 1350000000 500000000 24
40 41 N432 C5 10 3000000000 1350000000 24
41 42 N766 C2 4 2000000000 300000000 16
42 43 N770 C5 2.5 3000000000 300000000 24
43 44 N944 L 10 3000000000 1350000000 24
44 45 N968 C3 10 2000000000 300000000 16
45 46 O128 C4 1000000000 300000000 100000000 24
46 47 O477 C3 5 2000000000 300000000 16
47 48 O883 C3 5 1000000000 20000000 16
48 49 P060 C1 5 500000000 20000000 16
49 50 Q003 0.0 1000000000 5000000 3000000000 16
50 51 Q317 C1 2.5 500000000 20000000 16
51 52 R051 L 5 3000000000 1000000000 16
52 53 R474 C6 2.22 3000000000 300000000 24
53 54 R943 C2 6.67 750000000 300000000 16
54 55 S047 H 3000000000 300000000 24
55 56 S199 0.0 10 3000000000 1350000000 24
56 57 S804 C6 1.25 1000000000 20000000 24
57 58 T405 C4 6.67 2000000000 300000000 16
58 59 U210 L 10 3000000000 300000000 24
59 60 U319 C6 3000000000 1350000000 500000000 48
60 61 U574 C6 1.25 3000000000 300000000 24
61 62 V283 0.0 2.5 3000000000 1000000000 24
62 63 V301 C1 5 500000000 20000000 16
63 64 V753 C6 6.67 3000000000 1350000000 24
64 65 V911 C5 10 3000000000 1350000000 24
65 66 W237 C6 6.67 3000000000 1350000000 24
66 67 X702 C3 10 1000000000 300000000 24
67 68 X877 C4 6.67 2000000000 300000000 16
68 69 Y683 C4 4 2000000000 300000000 16
69 70 Y790 C1 2.5 500000000 20000000 16
70 71 Z006 C3 1000000000 5000000 3000000000 16
71 72 Z060 0.0 2.5 1000000000 20000000 24
72 73 Z142 0.0 10 3000000000 1350000000 24
73 74 Z457 C4 4 2000000000 300000000 16
74 75 Z647 C1 10 500000000 20000000 16
75 76 Z971 C1 10 100000000 20000000 16
76 80 M001 C4 1000000000 5000000 3000000000 16
77 81 E587 0.0 3000000000 1000000000 16
78 82 V898 L 2000000000 300000000 16
79 83 Q063 H 500000000 20000000 16
80 84 G008 C6 1000000000 5000000 3000000000 16
81 85 F353 C12 100000000 20000000 16
82 86 F135 C12 750000000 300000000 16
83 87 T458 C12 500000000 20000000 16
84 88 M164 C12 2000000000 300000000 16
85 89 L031 C12 3000000000 1000000000 16
86 90 S877 C14 750000000 300000000 16
87 91 B735 C15 750000000 300000000 16
88 92 V928 C16 750000000 300000000 16
89 93 C414 C17 750000000 300000000 16
90 94 R259 C18 750000000 300000000 16

Binary file not shown.

View File

@@ -27,7 +27,7 @@ requirejs.config({
admin: './app/admin', // initial start "admin page" view
notification: './app/notification', // "notification" view
jquery: 'lib/jquery-3.3.1.min', // v3.3.1 jQuery
jquery: 'lib/jquery-3.4.1.min', // v3.4.1 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', // v3.0.1 Javascript template engine - http://mustache.github.io
@@ -42,11 +42,11 @@ requirejs.config({
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.2.8 Raphaël - required for morris - https://dmitrybaranovskiy.github.io/raphael
bootbox: 'lib/bootbox.min', // v4.4.0 Bootbox.js - custom dialogs - http://bootboxjs.com
bootbox: 'lib/bootbox.min', // v5.2.0 Bootbox.js - custom dialogs - http://bootboxjs.com
easyPieChart: 'lib/jquery.easypiechart.min', // v2.1.6 Easy Pie Chart - HTML 5 pie charts - http://rendro.github.io/easy-pie-chart
peityInlineChart: 'lib/jquery.peity.min', // v3.2.1 Inline Chart - http://benpickles.github.io/peity/
dragToSelect: 'lib/jquery.dragToSelect', // v1.1 Drag to Select - http://andreaslagerkvist.com/jquery/drag-to-select
hoverIntent: 'lib/jquery.hoverIntent.min', // v1.9.0 Hover intention - http://cherne.net/brian/resources/jquery.hoverIntent.html
hoverIntent: 'lib/jquery.hoverIntent.min', // v1.10.0 Hover intention - http://cherne.net/brian/resources/jquery.hoverIntent.html
select2: 'lib/select2.min', // v4.0.3 Drop Down customization - https://select2.github.io
validator: 'lib/validator.min', // v0.10.1 Validator for Bootstrap 3 - https://github.com/1000hz/bootstrap-validator
lazylinepainter: 'lib/jquery.lazylinepainter-1.5.1.min', // v1.5.1 SVG line animation plugin - http://lazylinepainter.info

View File

@@ -383,6 +383,51 @@ define(['jquery'], ($) => {
30: 'K346 - 0.0',
31: 'Z060 - 0.0'
}
},
14: { // Drifter Sentinel WH
1: { // Combat
1: 'Monolith',
2: 'Wormhole in Rock Circle',
3: 'Opposing Spatial Rifts',
4: 'Sleeper Enclave Debris',
5: 'Crystal Resource'
}
},
15: { // Drifter Barbican WH
1: { // Combat
1: 'Wrecked Ships',
2: 'Unstable Wormhole',
3: 'Spatial Rift',
4: 'Heavily Guarded Spatial Rift',
5: 'Crystals'
}
},
16: { // Drifter Vidette WH
1: { // Combat
1: 'Ship Graveyard',
2: 'Sleeper Engineering Station',
3: 'Spatial Rift',
4: 'Sleeper Enclave in Coral Rock',
5: 'Crystals and Stone Circle'
}
},
17: { // Drifter Conflux WH
1: { // Combat
1: 'Monolith',
2: 'Caged Wormhole',
3: 'Rock Formation and Wormhole',
4: 'Particle Acceleration Array',
5: 'Guarded Asteroid Station'
}
},
18: { // Drifter Redoubt WH
1: { // Combat
1: 'Ship Graveyard',
2: 'Caged Wormhole',
3: 'Spatial Rift Generator',
4: 'Sleeper Enclave',
5: 'Hollow Asteroid'
}
}
}, // system type (k-space)
2: {

File diff suppressed because it is too large Load Diff

View File

@@ -137,7 +137,8 @@ define([
// mark as init
tableElement.attr('data-counter', 'init');
let refreshIntervalId = window.setInterval(() => {
let updateTableCount = () => {
tableApi.cells(null, columnSelector).every(function(rowIndex, colIndex, tableLoopCount, cellLoopCount){
let cell = this;
let node = cell.node();
@@ -148,7 +149,9 @@ define([
updateDateDiff( cell.nodes().to$(), date, round);
}
});
}, 500);
};
let refreshIntervalId = window.setInterval(updateTableCount, 500);
tableElement.data('interval', refreshIntervalId);
};

View File

@@ -45,6 +45,7 @@ define([
table.destroyTimestampCounter(true);
});
// Status Plugin ==============================================================================================
let StatusTable = function(settings){
let me = this;
me.statusContainer = $('<div>', {

View File

@@ -244,6 +244,21 @@ define([], () => {
},
'C12': {
class: 'pf-system-sec-special'
},
'C14': {
class: 'pf-system-sec-drifter'
},
'C15': {
class: 'pf-system-sec-drifter'
},
'C16': {
class: 'pf-system-sec-drifter'
},
'C17': {
class: 'pf-system-sec-drifter'
},
'C18': {
class: 'pf-system-sec-drifter'
}
},
// true sec
@@ -627,6 +642,14 @@ define([], () => {
8: 'A009 - C13'
}
},
// Drifter wormholes (can only appear in k-space)
drifterWormholes: {
1: 'S877 - C14 Sentinel',
2: 'B735 - C15 Barbican',
3: 'V928 - C16 Vidette',
4: 'C414 - C17 Conflux',
5: 'R259 - C18 Redoubt'
},
// incoming wormholes
incomingWormholes: {
1: 'K162 - C1/2/3 (unknown)',

View File

@@ -141,15 +141,14 @@ define([
// show Cookie accept hint on SSO login button
let confirmationSettings = {
container: 'body',
placement: 'bottom',
btnOkClass: 'btn btn-sm btn-default',
btnOkLabel: 'dismiss',
btnOkIcon: 'fas fa-fw fa-sign-in-alt',
title: 'Accept cookies',
btnCancelClass: 'btn btn-sm btn-success',
btnCancelLabel: 'accept',
btnCancelIcon: 'fas fa-fw fa-check',
btnOkClass: 'btn btn-sm btn-default',
btnOkLabel: 'dismiss',
btnOkIcon: 'fas fa-fw fa-sign-in-alt',
onCancel: function(e, target){
// "Accept cookies"
setAcceptCookie();
@@ -314,7 +313,6 @@ define([
}
});
});
}
});
};
@@ -406,13 +404,13 @@ define([
* init scrollSpy for navigation bar
*/
let initScrollSpy = () => {
// init scrollspy
let scrollElement = window;
let timeout;
// show elements that are currently in the viewport
let showVisibleElements = () => {
// find all elements that should be animated
let visibleElements = $('.' + config.animateElementClass).isInViewport();
let visibleElements = Util.findInViewport($('.' + config.animateElementClass));
$(visibleElements).removeClass( config.animateElementClass );
$(visibleElements).velocity('transition.flipXIn', {
@@ -431,16 +429,23 @@ define([
});
};
$( window ).scroll(() => {
// check for new visible elements
showVisibleElements();
});
let scrollHandler = () => {
// If there's a timer, cancel it
if(timeout){
window.cancelAnimationFrame(timeout);
}
timeout = window.requestAnimationFrame(showVisibleElements);
};
scrollElement.addEventListener('scroll', scrollHandler, false);
// initial check for visible elements
showVisibleElements();
// event listener for navigation links
Util.initPageScroll('#' + config.navigationElementId);
Util.initScrollSpy(document.getElementById(config.navigationElementId), scrollElement, {
offset: 150
});
};
/**
@@ -449,11 +454,18 @@ define([
*/
let initServerStatus = () => {
$.ajax({
type: 'POST',
type: 'GET',
url: Init.path.getServerStatus,
dataType: 'json'
}).done(function(responseData, textStatus, request){
let dateLastModified = new Date(request.getResponseHeader('Last-Modified') || Date.now());
let dateExpires = new Date(request.getResponseHeader('Expires') || Date.now());
var options = { hour: '2-digit', minute: '2-digit', hour12: false, timeZone: 'UTC', timeZoneName: 'short' };
responseData.api.cache = dateLastModified.toLocaleTimeString('en-US', options);
responseData.api.cacheExpire = 'TTL ' + (dateExpires - dateLastModified) / 1000 + 's';
let data = {
stickyPanelServerId: config.stickyPanelServerId,
stickyPanelClass: config.stickyPanelClass,
@@ -466,7 +478,8 @@ define([
case 'online':
case 'green': return 'txt-color-green';
case 'vip':
case 'yellow': return 'txt-color-orange';
case 'yellow': return 'txt-color-yellow';
case 'orange': return 'txt-color-orange';
case 'offline':
case 'red': return 'txt-color-red';
default: return '';

View File

@@ -163,7 +163,7 @@ define([
{icon: 'fa-route', action: 'find_route', text: 'find route'},
{icon: 'fa-object-group', action: 'select_connections', text: 'select connections'},
{icon: 'fa-reply fa-rotate-180', text: 'waypoints', subitems: [
{subIcon: 'fa-flag-checkered', subAction: 'set_destination', subText: 'set destination'},
{subIcon: 'fa-flag', subAction: 'set_destination', subText: 'set destination'},
{subDivider: true, action: ''},
{subIcon: 'fa-step-backward', subAction: 'add_first_waypoint', subText: 'add new [start]'},
{subIcon: 'fa-step-forward', subAction: 'add_last_waypoint', subText: 'add new [end]'}

View File

@@ -6,41 +6,50 @@ define(() => {
constructor(config){
this._defaultConfig = {
container: null, // parent DOM container element
center: null, // DOM elements that works as center
center: null, // DOM element OR [x,y] coordinates that works as center
elementClass: 'pf-system', // class for all elements
defaultSteps: 8, // how many potential dimensions are checked on en ellipsis around the center
defaultGapX: 50,
defaultGapY: 50,
gapX: 50, // leave gap between elements (x-axis)
gapY: 50, // leave gap between elements (y-axis)
minX: 0, // min x for valid elements
minY: 0, // min y for valid elements
spacingX: 20, // spacing x between elements
spacingY: 10, // spacing y between elements
loops: 2, // max loops around "center" for search
grid: false, // set to [20, 20] to force grid snapping
newElementWidth: 100, // width for new element
newElementHeight: 22, // height for new element
mirrorSearch: false, // if true coordinates are "mirrored" for an "alternating" search
debug: false, // render debug elements
debugOk: false, // if true, only not overlapped dimensions are rendered for debug
debugElementClass: 'pf-system-debug' // class for debug elements
};
this._config = Object.assign({}, this._defaultConfig, config);
this._config.dimensionCache = {};
this._cacheKey = (dim, depth) => ['dim', dim.left, dim.top, dim.width, dim.height, depth].join('_');
/**
* convert degree into radial unit
* @param deg
* @returns {number}
* @private
*/
this._degToRad = (deg) => {
return deg * Math.PI / 180;
};
this._degToRad = deg => deg * Math.PI / 180;
/**
* get element dimension/position of a DOM element
* @param element
* @returns {*}
* @param spacingX
* @param spacingY
* @returns {{a: *, b: *, top: *, left: *, width: *, height: *}}
* @private
*/
this._getElementDimension = element => {
let dim = null;
this._getElementDimension = (element, spacingX = 0, spacingY = 0) => {
let left = 0;
let top = 0;
let a = 0;
@@ -49,7 +58,7 @@ define(() => {
let height = this._config.newElementHeight;
if(Array.isArray(element)){
// xy coordinates
// x/y coordinates
let point = [
element[0] ? parseInt(element[0], 10) : 0,
element[1] ? parseInt(element[1], 10) : 0
@@ -63,14 +72,21 @@ define(() => {
top = point[1];
a = this._config.gapX;
b = this._config.gapY;
}else if(element){
}else if(element instanceof Element){
// DOM element
left = element.style.left ? parseInt(element.style.left, 10) : 0;
top = element.style.top ? parseInt(element.style.top, 10) : 0;
a = parseInt((element.offsetWidth / 2).toString(), 10) + this._config.gapX;
b = parseInt((element.offsetHeight / 2).toString(), 10) + this._config.gapY;
width = element.offsetWidth;
height = element.offsetHeight;
left = (element.style.left ? parseInt(element.style.left, 10) : 0) - spacingX;
top = (element.style.top ? parseInt(element.style.top, 10) : 0) - spacingY;
a = parseInt((element.offsetWidth / 2).toString(), 10) + spacingX + this._config.gapX;
b = parseInt((element.offsetHeight / 2).toString(), 10) + spacingY + this._config.gapY;
width = element.offsetWidth + 2 * spacingX;
height = element.offsetHeight + 2 * spacingY;
}else if(element instanceof Object){
left = element.left - spacingX;
top = element.top - spacingY;
a = parseInt((element.width / 2).toString(), 10) + spacingX + this._config.gapX;
b = parseInt((element.height / 2).toString(), 10) + spacingY + this._config.gapY;
width = element.width + 2 * spacingX;
height = element.height + 2 * spacingY;
}
// add "gap" to a and b in order to have some space around elements
@@ -121,7 +137,7 @@ define(() => {
let dimensions = [];
let surroundingElements = this._getContainer().getElementsByClassName(this._config.elementClass);
for(let element of surroundingElements){
dimensions.push(this._getElementDimension(element));
dimensions.push(this._getElementDimension(element, this._config.spacingX, this._config.spacingY));
}
return dimensions;
};
@@ -132,7 +148,7 @@ define(() => {
* @returns {*}
* @private
*/
this._transformPointToGrid = (point) => {
this._transformPointToGrid = point => {
point[0] = Math.floor(point[0] / this._config.grid[0]) * this._config.grid[0];
point[1] = Math.floor(point[1] / this._config.grid[1]) * this._config.grid[1];
return point;
@@ -231,31 +247,44 @@ define(() => {
return percent;
};
/**
* checks whether dim11 has valid x/y coordinate
* -> coordinates are >= "minX/Y" limit
* @param dim1
* @returns {*|boolean}
* @private
*/
this._valid = dim1 => dim1 && dim1.left >= this._config.minX && dim1.top >= this._config.minY;
/**
* checks whether dim1 is partially overlapped by any other element
* @param dim1
* @param dimensionContainer
* @param allDimensions
* @param depth
* @returns {boolean}
* @private
*/
this._isOverlapping = (dim1, dimensionContainer, allDimensions) => {
this._isOverlapping = (dim1, dimensionContainer, allDimensions, depth) => {
let isOverlapping = false;
if(dim1){
if(this._percentCovered(dimensionContainer, dim1 ) === 100){
let cacheKey = this._cacheKey(dim1, depth);
// check cache first (e.g. if grid is active some dimensions would be checked multiple times)
if(this._config.dimensionCache[cacheKey]){
return true;
}else if(this._percentCovered(dimensionContainer, dim1) === 100){
// element is within parent container
for(let dim2 of allDimensions){
let percentCovered = this._percentCovered(dim1, dim2);
if(percentCovered){
isOverlapping = true;
// render debug element
this._showDebugElement(dim1, percentCovered);
this._config.dimensionCache[cacheKey] = percentCovered;
break;
}
}
}else{
isOverlapping = true;
this._showDebugElement(dim1, 100);
this._config.dimensionCache[cacheKey] = 100;
}
}else{
isOverlapping = true;
@@ -264,36 +293,63 @@ define(() => {
return isOverlapping;
};
/**
*
* @param dim1
* @returns {boolean}
* @private
*/
this._existDimension = function(dim1){
return (
dim1.left === this.left &&
dim1.top === this.top &&
dim1.width === this.width &&
dim1.height === this.height
);
};
/**
* find all dimensions around a centerDimension that are not overlapped by other elements
* @param maxResults
* @param steps
* @param allDimensions
* @param depth
* @param loops
* @returns {Array}
* @private
*/
this._findDimensions = (maxResults, steps, allDimensions, loops) => {
this._findDimensions = (maxResults, steps, allDimensions, depth, loops) => {
steps = steps || 1;
loops = loops || 1;
let dimensions = [];
let start = 0;
let end = 360;
let angle = end / steps;
// as default coordinates get checked clockwise Q4 -> Q3 -> Q2 -> Q1
// we could also check "mirrored" coordinates Q4+Q1 -> Q3+Q2
if(this._config.mirrorSearch){
end /= end;
}
let dimensionContainer = this._getElementDimension(this._getContainer());
steps = steps || 1;
loops = loops || 1;
if(loops === 1){
// check center element
let centerDimension = this._getElementDimension(this._config.center);
if(!this._isOverlapping(centerDimension, dimensionContainer, allDimensions)){
if(
this._valid(centerDimension) &&
!dimensions.some(this._existDimension, centerDimension) &&
!this._isOverlapping(centerDimension, dimensionContainer, allDimensions, depth)
){
dimensions.push({
left: centerDimension.left,
top: centerDimension.top,
width: centerDimension.width,
height: centerDimension.height
});
// render debug element
this._showDebugElement(centerDimension, 0);
this._config.dimensionCache[this._cacheKey(centerDimension, depth)] = 0;
maxResults--;
}
@@ -308,27 +364,38 @@ define(() => {
while(maxResults > 0 && start < end){
// get all potential coordinates on an eclipse around a given "centerElementDimension"
let coordinate = this._getEllipseCoordinates(centerDimension, end);
// transform relative x/y coordinate into a absolute 2D area
let checkDimension = this._transformCoordinate(centerDimension, coordinate);
if(!this._isOverlapping(checkDimension, dimensionContainer, allDimensions)){
dimensions.push({
left: checkDimension.left,
top: checkDimension.top,
width: checkDimension.width,
height: checkDimension.height
});
// render debug element
this._showDebugElement(checkDimension, 0);
maxResults--;
let coordinates = [coordinate];
if(this._config.mirrorSearch && coordinate){
coordinates.push({x: coordinate.x, y: coordinate.y * -1 });
}
for(let coordinateTemp of coordinates){
// transform relative x/y coordinate into a absolute 2D area
let checkDimension = this._transformCoordinate(centerDimension, coordinateTemp);
if(
this._valid(checkDimension) &&
!dimensions.some(this._existDimension, checkDimension) &&
!this._isOverlapping(checkDimension, dimensionContainer, allDimensions, depth)
){
dimensions.push({
left: checkDimension.left,
top: checkDimension.top,
width: checkDimension.width,
height: checkDimension.height
});
this._config.dimensionCache[this._cacheKey(checkDimension, depth)] = 0;
maxResults--;
}
}
end -= angle;
}
if(maxResults > 0 && loops < this._config.loops){
loops++;
steps *= 2;
dimensions = dimensions.concat(this._findDimensions(maxResults, steps, allDimensions, loops));
dimensions = dimensions.concat(this._findDimensions(maxResults, steps, allDimensions, depth, loops));
}
return dimensions;
@@ -346,21 +413,33 @@ define(() => {
/**
* render debug element into parent container
* -> checks overlapping dimension with other elements
* @param dimension
* @param percentCovered
* @private
*/
this._showDebugElement = (dimension, percentCovered) => {
this._showDebugElements = () => {
if(this._config.debug){
let element = document.createElement('div');
element.style.left = dimension.left + 'px';
element.style.top = dimension.top + 'px';
element.style.width = dimension.width + 'px';
element.style.height = dimension.height + 'px';
element.style.backgroundColor = Boolean(percentCovered) ? 'rgba(255,0,0,0.1)' : 'rgba(0,255,0,0.1)';
element.innerHTML = Math.round(percentCovered * 100) / 100 + '%';
element.classList.add(this._config.debugElementClass);
this._getContainer().appendChild(element);
let documentFragment = document.createDocumentFragment();
for(let [cacheKey, percentCovered] of Object.entries(this._config.dimensionCache)){
if(this._config.debugOk && percentCovered){
continue;
}
let element = document.createElement('div');
let dimParts = cacheKey.split('_');
element.style.left = dimParts[1] + 'px';
element.style.top = dimParts[2] + 'px';
element.style.width = dimParts[3] + 'px';
element.style.height = dimParts[4] + 'px';
element.style.backgroundColor = Boolean(percentCovered) ? 'rgba(255,0,0,0.1)' : 'rgba(0,255,0,0.4)';
element.style.opacity = Boolean(percentCovered) ? 0.5 : 1;
element.style.zIndex = Boolean(percentCovered) ? 1000 : 2000;
element.style.border = Boolean(percentCovered) ? 'none' : '1px solid rgba(0,255,0,0.3)';
element.innerHTML = Math.round(percentCovered * 100) / 100 + '';
element.classList.add(this._config.debugElementClass);
element.setAttribute('data-depth', dimParts[5]);
documentFragment.appendChild(element);
}
this._getContainer().appendChild(documentFragment);
}
};
@@ -381,15 +460,45 @@ define(() => {
/**
* search for surrounding, non overlapping dimensions
* @param maxResults
* @param steps
* @param findChain
* @returns {Array}
*/
this.findNonOverlappingDimensions = (maxResults, steps) => {
this.findNonOverlappingDimensions = (maxResults, findChain = false) => {
this._hideDebugElements();
this._config.dimensionCache = {};
// element dimensions that exist and should be checked for overlapping
let allDimensions = this._getAllElementDimensions();
let dimensions = [];
let depth = 1;
let maxDepth = findChain ? maxResults : 1;
maxResults = findChain ? 1 : maxResults;
while(depth <= maxDepth){
let dimensionsTemp = this._findDimensions(maxResults, this._config.defaultSteps, allDimensions, depth);
return this._findDimensions(maxResults, steps, allDimensions);
if(dimensionsTemp.length){
dimensions = dimensions.concat(dimensionsTemp);
if(findChain){
// if depth > 0 we have 2D dimension as "center" (not a x/y coordinate)
// -> increase the gap
this._config.defaultGapX = 10;
this._config.defaultGapY = 10;
this._config.gapX = 50;
this._config.gapY = 50;
this._config.center = dimensionsTemp[0];
allDimensions = allDimensions.concat([this._getElementDimension(dimensionsTemp[0], this._config.spacingX, this._config.spacingY)]);
}
depth++;
}else{
break;
}
}
this._showDebugElements();
return dimensions;
};
}
}

View File

@@ -422,7 +422,7 @@ define([
_: (data, type, row, meta) => {
let value = data.typeName;
if(type === 'display'){
value = '<img src="' + Init.url.ccpImageServer + '/Render/' + data.typeId + '_32.png"/>';
value = '<img src="' + Util.eveImageUrl('render', data.typeId) + '"/>';
}
return value;
}

View File

@@ -546,13 +546,16 @@ define([
system.data('region', data.region.name);
system.data('constellationId', parseInt(data.constellation.id));
system.data('constellation', data.constellation.name);
system.data('faction', data.faction);
system.data('planets', data.planets);
system.data('shattered', data.shattered);
system.data('drifter', data.drifter);
system.data('statics', data.statics);
system.data('updated', parseInt(data.updated.updated));
system.data('changed', false);
system.attr('data-mapid', parseInt(mapContainer.data('id')));
if(data.sovereignty){
system.data('sovereignty', data.sovereignty);
}
// locked system
if( Boolean(system.data('locked')) !== data.locked ){
@@ -660,20 +663,10 @@ define([
case 'add_system':
// add new system dialog
let position = Layout.getEventCoordinates(e);
let grid = [MapUtil.config.mapSnapToGridDimension, MapUtil.config.mapSnapToGridDimension];
let positionFinder = new Layout.Position({
container: mapElement[0],
center: [position.x, position.y],
loops: 5,
defaultGapX: 10,
defaultGapY: 10,
grid: mapElement.hasClass(MapUtil.config.mapGridClass) ? grid : false,
debug: false
let dimensions = MapUtil.newSystemPositionByCoordinates(mapElement, {
center: [position.x, position.y]
});
let dimensions = positionFinder.findNonOverlappingDimensions(1, 8);
if(dimensions.length){
position.x = dimensions[0].left;
position.y = dimensions[0].top;
@@ -1212,7 +1205,7 @@ define([
mapConfig.map.setContainer(mapContainer);
// init custom scrollbars and add overlay
parentElement.initMapScrollbar();
initMapScrollbar(mapWrapper);
// set map observer
setMapObserver(mapConfig.map);
@@ -2708,7 +2701,7 @@ define([
if(select){
let mapWrapper = mapContainer.closest('.' + config.mapWrapperClass);
Scrollbar.scrollToSystem(mapWrapper, MapUtil.getSystemPosition(system));
Scrollbar.scrollToCenter(mapWrapper, system);
// select system
MapUtil.showSystemInfo(map, system);
}
@@ -3046,47 +3039,59 @@ define([
*/
$.fn.getSystemData = function(minimal = false){
let system = $(this);
let data = system.data();
let systemData = {
id: parseInt(system.data('id')),
id: parseInt(data.id),
updated: {
updated: parseInt(system.data('updated'))
updated: parseInt(data.updated)
}
};
if(!minimal){
systemData = Object.assign(systemData, {
systemId: parseInt(system.data('systemId')),
name: system.data('name'),
let systemDataComplete = {
systemId: parseInt(data.systemId),
name: data.name,
alias: system.getSystemInfo(['alias']),
effect: system.data('effect'),
effect: data.effect,
type: {
id: system.data('typeId')
id: data.typeId
},
security: system.data('security'),
trueSec: system.data('trueSec'),
security: data.security,
trueSec: data.trueSec,
region: {
id: system.data('regionId'),
name: system.data('region')
id: data.regionId,
name: data.region
},
constellation: {
id: system.data('constellationId'),
name: system.data('constellation')
id: data.constellationId,
name: data.constellation
},
status: {
id: system.data('statusId')
id: data.statusId
},
locked: system.data('locked') ? 1 : 0,
rallyUpdated: system.data('rallyUpdated') || 0,
rallyPoke: system.data('rallyPoke') ? 1 : 0,
currentUser: system.data('currentUser'), // if user is currently in this system
faction: system.data('faction'),
planets: system.data('planets'),
shattered: system.data('shattered') ? 1 : 0,
statics: system.data('statics'),
userCount: (system.data('userCount') ? parseInt(system.data('userCount')) : 0),
locked: data.locked ? 1 : 0,
rallyUpdated: data.rallyUpdated || 0,
rallyPoke: data.rallyPoke ? 1 : 0,
currentUser: data.currentUser, // if user is currently in this system
planets: data.planets,
shattered: data.shattered ? 1 : 0,
drifter: data.drifter ? 1 : 0,
statics: data.statics,
userCount: parseInt(data.userCount) || 0,
position: MapUtil.getSystemPosition(system)
});
};
let optionalDataKeys = ['sovereignty'];
for(let dataKey of optionalDataKeys){
let value = system.data(dataKey);
if(value !== null && value !== undefined){
systemDataComplete[dataKey] = value;
}
}
systemData = Object.assign(systemData, systemDataComplete);
}
return systemData;
@@ -3166,15 +3171,13 @@ define([
/**
* init scrollbar for Map element
* @param mapWrapper
*/
$.fn.initMapScrollbar = function(){
// get Map Scrollbar
let mapTabContentElement = $(this);
let mapWrapperElement = mapTabContentElement.find('.' + config.mapWrapperClass);
let mapElement = mapTabContentElement.find('.' + config.mapClass);
let initMapScrollbar = mapWrapper => {
let mapElement = mapWrapper.find('.' + config.mapClass);
let mapId = mapElement.data('id');
mapWrapperElement.initCustomScrollbar({
Scrollbar.initScrollbar(mapWrapper, {
callbacks: {
onInit: function(){
// init 'space' key + 'mouse' down for map scroll -------------------------------------------------
@@ -3291,8 +3294,8 @@ define([
// ------------------------------------------------------------------------------------------------------------
// add map overlays after scrollbar is initialized
// because of its absolute position
mapWrapperElement.initMapOverlays();
mapWrapperElement.initLocalOverlay(mapId);
mapWrapper.initMapOverlays();
mapWrapper.initLocalOverlay(mapId);
};
return {

View File

@@ -370,15 +370,16 @@ define([
// init popover if not already exists
if(!systemHead.data('bs.popover')){
let system = systemHead.parent();
let systemData = system.data();
systemHead.popover({
placement: 'right',
placement: 'bottom',
html: true,
trigger: 'manual',
container: mapElement,
title: false,
content: Util.getSystemRegionTable(
system.data('region'),
system.data('faction') || null
Util.getObjVal(systemData, 'region'),
Util.getObjVal(systemData, 'sovereignty')
)
});
}

View File

@@ -1,73 +1,129 @@
/**
* Created by Exodus on 26.06.2016.
*/
define([
'jquery',
'app/init',
'app/util',
'mousewheel',
'customScrollbar'
], ($, Init, Util) => {
], ($) => {
'use strict';
let defaultConfig = {
axis: 'yx',
theme: 'light-3' ,
scrollInertia: 200,
autoExpandScrollbar: false,
scrollButtons:{
enable: true,
scrollAmount: 30,
scrollType: 'stepless'
},
callbacks: {
onTotalScrollOffset: 0,
onTotalScrollBackOffset: 0,
alwaysTriggerOffsets: true
},
advanced: {
autoUpdateTimeout: 120, // auto-update timeout (default: 60)
updateOnContentResize: true,
autoExpandHorizontalScroll: false, // on resize css scale() scroll content should not change
//autoExpandHorizontalScroll: 2,
autoScrollOnFocus: 'div',
},
mouseWheel: {
enable: false, // scroll wheel currently disabled
scrollAmount: 'auto',
axis: 'x',
preventDefault: true
},
keyboard: {
enable: false, // not working with pathfinder "shortcuts"
scrollType: 'stepless',
scrollAmount: 'auto'
},
scrollbarPosition: 'inside',
autoDraggerLength: true,
autoHideScrollbar: false
};
/**
* init map scrollbar
* @param scrollWrapper
* @param config
*/
$.fn.initCustomScrollbar = function(config){
// default config -------------------------------------------------------------------------
let defaultConfig = {
axis: 'yx',
theme: 'light-3' ,
scrollInertia: 300,
autoExpandScrollbar: false,
scrollButtons:{
enable: true,
scrollAmount: 30,
scrollType: 'stepless'
},
callbacks: {
onTotalScrollOffset: 0,
onTotalScrollBackOffset: 0,
alwaysTriggerOffsets: true
},
advanced: {
autoUpdateTimeout: 120, // auto-update timeout (default: 60)
updateOnContentResize: true,
autoExpandHorizontalScroll: false, // on resize css scale() scroll content should not change
//autoExpandHorizontalScroll: 2,
autoScrollOnFocus: 'div',
},
mouseWheel: {
enable: false, // scroll wheel currently disabled
scrollAmount: 'auto',
axis: 'x',
preventDefault: true
},
keyboard: {
enable: false, // not working with pathfinder "shortcuts"
scrollType: 'stepless',
scrollAmount: 'auto'
},
scrollbarPosition: 'inside',
autoDraggerLength: true,
autoHideScrollbar: false
};
// init -----------------------------------------------------------------------------------
let initScrollbar = (scrollWrapper, config) => {
config = $.extend(true, {}, defaultConfig, config);
return this.each(function(){
let mapWrapperElement = $(this);
scrollWrapper.mCustomScrollbar(config);
};
// prevent multiple initialization
mapWrapperElement.mCustomScrollbar('destroy');
/**
* get mCustomScrollbar container
* @param element
* @returns {*|[]}
*/
let getContainer = element => element.parents('.mCSB_container');
// init custom scrollbars
mapWrapperElement.mCustomScrollbar(config);
});
/**
*
* @param container
* @param element
* @returns {{x: number, y: number}}
*/
let getElementPos = (container, element) => {
return {
x: element.offset().left - container.offset().left,
y: element.offset().top - container.offset().top
};
};
/**
* @param element
* @returns {{x: number, y: number}}
*/
let getElementDim = element => {
return {
x: element.outerWidth(false),
y: element.outerHeight(false)
};
};
/**
* check if an element is 100% visible
* -> scrolled into viewport
* @param element
* @returns {boolean}
*/
let isInView = element => {
let container = getContainer(element);
let wrapper = container.parent();
let cPos = {x: container[0].offsetLeft, y: container[0].offsetTop};
let ePos = getElementPos(container, element);
let eDim = getElementDim(element);
return cPos.y + ePos.y >= 0 &&
cPos.y + ePos.y < wrapper.height() - eDim.y &&
cPos.x + ePos.x >= 0 &&
cPos.x + ePos.x < wrapper.width() - eDim.x;
};
/**
* get new scrollTo coordinates to center element in viewport
* @param element
* @returns {{x: number, y: number}}
*/
let getCenterScrollPosition = element => {
let container = getContainer(element);
let wrapper = container.parent();
let cDim = getElementDim(container);
let wDim = {x: wrapper.width(), y: wrapper.height()};
let eDim = getElementDim(element);
let ePos = getElementPos(container, element);
let eOff = {
x: (-wDim.x / 2) + (eDim.x / 2),
y: (-wDim.y / 2) + (eDim.y / 2)
};
return adjustPos(addOffset(ePos, eOff), cDim);
};
/**
@@ -82,33 +138,55 @@ define([
};
/**
* scroll to a specific system on map
* scroll to center an element
* -> subtract some offset for tooltips/connections
* @param scrollWrapper
* @param position
* @param options
* @param element
*/
let scrollToSystem = (scrollWrapper, position, options) => {
position = getOffsetPosition(position, {x: -15, y: -35});
scrollToPosition(scrollWrapper, position, options);
let scrollToCenter = (scrollWrapper, element) => {
// no scroll if element is already FULL visible in scrollable viewport
if(!isInView(element)){
// get scrollTo position for centered element
scrollToPosition(scrollWrapper, getCenterScrollPosition(element));
}
};
/**
* add/subtract offset coordinates from position
* -> no negative values returned
* @param position
* @param offset
* @param {{x: number, y: number}} position
* @param {{x: number, y: number}} offset
* @returns {{x: number, y: number}}
*/
let getOffsetPosition = (position, offset) => {
return {
x: Math.max(0, position.x + offset.x),
y: Math.max(0, position.y + offset.y)
};
};
let addOffset = (position, offset) => mapObject(position, (v, k) => v + offset[k]);
/**
* round position
* @param {{x: number, y: number}} position
* @returns {{x: number, y: number}}
*/
let roundPos = position => mapObject(position, Math.round);
/**
*
* @param {{x: number, y: number}} position
* @param {{x: number, y: number}} dimension
* @returns {{x: number, y: number}}
*/
let adjustPos = (position, dimension) => mapObject(roundPos(position), (v, k) => Math.max(1, Math.min(dimension[k], v)) );
/**
* like Array.map() for objects
* -> callback f is called for each property
* @see https://stackoverflow.com/a/38829074/4329969
* @param o
* @param f
* @returns {Object}
*/
let mapObject = (o, f) => Object.assign(...Object.entries(o).map(([k, v]) => ({[k]: f(v, k) })));
return {
initScrollbar: initScrollbar,
scrollToPosition: scrollToPosition,
scrollToSystem: scrollToSystem
scrollToCenter: scrollToCenter
};
});

View File

@@ -9,9 +9,8 @@ define([
'app/util',
'bootbox',
'app/map/util',
'app/map/layout',
'app/map/magnetizing'
], ($, Init, Util, bootbox, MapUtil, Layout, Magnetizer) => {
], ($, Init, Util, bootbox, MapUtil, Magnetizer) => {
'use strict';
let config = {
@@ -245,7 +244,7 @@ define([
sourceSystem = options.sourceSystem;
// get new position
newPosition = calculateNewSystemPosition(sourceSystem);
newPosition = newSystemPositionBySystem(sourceSystem);
}else if(options.position){
// check mouse cursor position (add system to map)
newPosition = {
@@ -730,25 +729,16 @@ define([
};
/**
* calculate the x/y coordinates for a new system - relativ to a source system
* calculate the x/y coordinates for a new system - relative to a source system
* -> in case no coordinates found -> return default calculated coordinates
* @param sourceSystem
* @returns {{x: *, y: *}}
*/
let calculateNewSystemPosition = sourceSystem => {
let mapContainer = sourceSystem.parent();
let grid = [MapUtil.config.mapSnapToGridDimension, MapUtil.config.mapSnapToGridDimension];
let newSystemPositionBySystem = sourceSystem => {
let x = 0;
let y = 0;
let positionFinder = new Layout.Position({
container: mapContainer[0],
center: sourceSystem[0],
loops: 4,
grid: mapContainer.hasClass(MapUtil.config.mapGridClass) ? grid : false,
debug: false
});
let dimensions = positionFinder.findNonOverlappingDimensions(1, 16);
let dimensions = MapUtil.newSystemPositionBySystem(sourceSystem);
if(dimensions.length){
//... empty map space found
x = dimensions[0].left;
@@ -780,8 +770,12 @@ define([
let headInfoLeft = [];
let headInfoRight = [];
if(data.drifter){
headInfoLeft.push('<i class="fas fa-fw fa-wave-square ' + Util.getSecurityClassForSystem(data.security) + '" title="drifter"></i>');
}
if(data.shattered){
headInfoLeft.push('<i class="fas fa-fw fa-skull ' + Util.getSecurityClassForSystem('SH') + '" title="shattered"></i>');
headInfoLeft.push('<i class="fas fa-fw fa-chart-pie ' + Util.getSecurityClassForSystem('SH') + '" title="shattered"></i>');
}
// check systemData if headInfo element is needed

View File

@@ -6,9 +6,10 @@ define([
'jquery',
'app/init',
'app/util',
'app/map/layout',
'app/map/scrollbar',
'app/map/overlay/util'
], ($, Init, Util, Scrollbar, MapOverlayUtil) => {
], ($, Init, Util, Layout, Scrollbar, MapOverlayUtil) => {
'use strict';
let config = {
@@ -806,10 +807,9 @@ define([
// collect all required data from map module to update the info element
// store them global and assessable for each module
Util.setCurrentSystemData({
systemData: system.getSystemData(),
mapId: parseInt( system.attr('data-mapid') )
});
let systemData = system.getSystemData();
systemData.mapId = parseInt(system.attr('data-mapid')) || 0;
Util.setCurrentSystemData(systemData);
};
/**
@@ -1764,6 +1764,91 @@ define([
});
};
/**
* add station services tooltip
* @param services
* @param options
* @returns {*}
*/
$.fn.addStationServiceTooltip = function(services, options){
let getServiceIcon = service => {
switch(service){
case 'bounty-missions': return false;
case 'assasination-missions': return false;
case 'courier-missions': return false;
case 'interbus': return false;
case 'reprocessing-plant': return 'reprocess';
case 'refinery': return false;
case 'market': return 'market';
case 'black-market': return false;
case 'stock-exchange': return false;
case 'cloning': return 'clonebay';
case 'surgery': return false;
case 'dna-therapy': return false;
case 'repair-facilities': return 'repairshop';
case 'factory': return 'industry';
case 'labratory': return 'research';
case 'gambling': return false;
case 'fitting': return 'fitting';
case 'paintshop': return 'skins';
case 'news': return false;
case 'storage': return false;
case 'insurance': return 'insurance';
case 'docking': return 'docking';
case 'office-rental': return false;
case 'jump-clone-facility': return 'jumpclones';
case 'loyalty-point-store': return 'lpstore';
case 'navy-offices': return 'factionalwarfare';
case 'security-offices': return 'concord';
default: return false;
}
};
let getStationServicesTable = services => {
let content = '';
for(let i = 0; i < services.length; i++){
let icon = getServiceIcon(services[i]);
if(icon){
content += '<img class="' + Util.config.popoverListIconClass + '" src="/public/img/icons/client/ui/window/' + icon + '.png" alt="' + services[i] + '">';
}
}
return content;
};
let content = getStationServicesTable(services);
let title = '<i class="fas fa-tools fa-fw"></i>&nbsp;Services';
let defaultOptions = {
placement: 'top',
html: true,
trigger: 'hover',
container: 'body',
title: title,
content: '',
delay: {
show: 150,
hide: 0
},
};
options = $.extend({}, defaultOptions, options);
return this.each(function(){
let element = $(this);
element.popover(options);
// set new popover content
let popover = element.data('bs.popover');
popover.options.content = content;
popover.tip().addClass(Util.config.popoverClass);
if(options.show){
element.popover('show');
}
});
};
/**
* add system effect tooltip
* @param security
@@ -1774,9 +1859,11 @@ define([
$.fn.addSystemEffectTooltip = function(security, effect, options){
let effectClass = getEffectInfoForSystem(effect, 'class');
let systemEffectData = Util.getSystemEffectData(security, effect);
let areaId = Util.getAreaIdBySecurity(security);
let title = '<i class="fas fa-square fa-fw ' + effectClass + '"></i>&nbsp;' +
getEffectInfoForSystem(effect, 'name') +
getEffectInfoForSystem(effect, 'name') + '&nbsp;&nbsp;' +
'<kbd>' + Util.getSystemEffectMultiplierByAreaId(parseInt(areaId)) + 'x' + '</kbd>' +
'<span class="pull-right ' + Util.getSecurityClassForSystem(security) + '">' + security + '</span>';
let content = Util.getSystemEffectTable(systemEffectData);
@@ -1808,7 +1895,6 @@ define([
* @returns {*}
*/
$.fn.addSystemPlanetsTooltip = function(planets, options){
let content = Util.getSystemPlanetsTable(planets);
let defaultOptions = {
@@ -1869,10 +1955,10 @@ define([
if(tooltipData.maxStableTime){
data.maxStableTime = tooltipData.maxStableTime + ' h';
}
if(tooltipData.signatureStrength){
data.signatureStrength = parseFloat(tooltipData.signatureStrength).toLocaleString() + '&nbsp;&#37;';
if(tooltipData.scanWormholeStrength){
data.scanWormholeStrength = parseFloat(tooltipData.scanWormholeStrength).toLocaleString() + '&nbsp;&#37;';
}else{
data.signatureStrength = '<span class="txt-color txt-color-grayLight">unknown</span>';
data.scanWormholeStrength = '<span class="txt-color txt-color-grayLight">unknown</span>';
}
let title = tooltipData.name;
@@ -2029,6 +2115,132 @@ define([
return hasAccess;
};
/**
*
* @param options
* @param maxResults
* @param findChain
* @returns {Array}
*/
let findNonOverlappingDimensions = (options = {}, maxResults = 1, findChain = false) => {
let defaultOptions = {
center: [0, 30],
loops: 4,
debug: false
};
options = Object.assign({}, defaultOptions, options);
let positionFinder = new Layout.Position(Object.assign({}, defaultOptions, options));
return positionFinder.findNonOverlappingDimensions(maxResults, findChain);
};
/**
* calculate the x/y coordinates for a new system - relative to a source system
* @param sourceSystem
* @returns {Array}
*/
let newSystemPositionBySystem = sourceSystem => {
let mapContainer = sourceSystem.parent();
let grid = [config.mapSnapToGridDimension, config.mapSnapToGridDimension];
let options = {
container: mapContainer[0],
center: sourceSystem[0],
grid: mapContainer.hasClass(config.mapGridClass) ? grid : false
};
return findNonOverlappingDimensions(options);
};
/**
* calculate the x/y coordinates for a new system - relative to x/y position
* @param mapContainer
* @param options
* @param maxResults
* @param findChain
* @returns {Array}
*/
let newSystemPositionByCoordinates = (mapContainer, options = {}, maxResults = 1, findChain = false) => {
let grid = [config.mapSnapToGridDimension, config.mapSnapToGridDimension];
let defaultOptions = {
container: mapContainer[0],
center: [0, 0],
grid: mapContainer.hasClass(config.mapGridClass) ? grid : false,
loops: 10,
defaultGapX: 10,
defaultGapY: 10,
//debugOk: true,
//debug: true
};
options = Object.assign({}, defaultOptions, options);
return findNonOverlappingDimensions(options, maxResults, findChain);
};
/**
*
* @param mapContainer
*/
let newSystemPositionsByMap = mapContainer => {
let positions = {};
if(mapContainer){
let mapId = mapContainer.data('id');
let scrollPosition = {
x: Math.abs(parseInt(mapContainer.attr('data-scroll-left')) || 0),
y: Math.abs(parseInt(mapContainer.attr('data-scroll-top')) || 0)
};
// space new positions from map top (e.g. used for tooltips)
scrollPosition.y = Math.max(scrollPosition.y, 30);
// default position -> current map section top/left -------------------------------------------------------
positions.defaults = [scrollPosition];
// check default position for overlapping -----------------------------------------------------------------
let dimensions = newSystemPositionByCoordinates(mapContainer, {
center: [scrollPosition.x, scrollPosition.y],
minX: scrollPosition.x,
minY: scrollPosition.y
}, 2, true);
if(dimensions.length){
positions.defaults = dimensions.map(dim => ({
x: parseInt(dim.left) || 0,
y: parseInt(dim.top) || 0
}));
}
// -> calc possible coordinates for new system that should be used based on current user location ---------
let currentLocationData = Util.getCurrentLocationData();
if(currentLocationData.id){
// ... we need to the PF systemId for 'SelectSystem' trigger
let systemData = getSystemData(mapId, currentLocationData.id, 'systemId');
if(systemData){
let currentSystem = $('#' + getSystemId(mapId, systemData.id));
if(currentSystem.length){
let dimensions = newSystemPositionBySystem(currentSystem);
if(dimensions.length){
//... empty map space found
positions.location = {
systemId: currentLocationData.id,
position: {
x: dimensions[0].left,
y: dimensions[0].top
}
};
}
}
}
}
}
return Object.keys(positions).length ? positions : null;
};
/**
* get a unique map url for deeplinking
* @param mapId
@@ -2108,6 +2320,9 @@ define([
initWormholeInfoTooltip: initWormholeInfoTooltip,
getSystemId: getSystemId,
checkRight: checkRight,
newSystemPositionBySystem: newSystemPositionBySystem,
newSystemPositionByCoordinates: newSystemPositionByCoordinates,
newSystemPositionsByMap: newSystemPositionsByMap,
getMapDeeplinkUrl: getMapDeeplinkUrl
};
});

View File

@@ -9,10 +9,11 @@ define([
'app/logging',
'app/page',
'app/map/worker',
'app/map/util',
'app/module_map',
'app/key',
'app/ui/form_element'
], ($, Init, Util, Logging, Page, MapWorker, ModuleMap) => {
], ($, Init, Util, Logging, Page, MapWorker, MapUtil, ModuleMap) => {
'use strict';
@@ -31,6 +32,9 @@ define([
// set default dialog config
Util.initDefaultBootboxConfig();
// set default confirmation popover config
Util.initDefaultConfirmationConfig();
// set default select2 config
Util.initDefaultSelect2Config();
@@ -444,7 +448,7 @@ define([
// start user update trigger after map loaded
updateTimeouts.userUpdate = setTimeout(() => {
triggerUserUpdatePing();
}, 1000);
}, 500);
}
});
}
@@ -461,11 +465,13 @@ define([
// IMPORTANT: Get user data for ONE map that is currently visible
// On later releases this can be easy changed to "full update" all maps for a user
//
let mapIds = [];
let newSystemPositions = null;
let activeMap = Util.getMapModule().getActiveMap();
if(activeMap){
mapIds = [ activeMap.data('id') ];
mapIds = [activeMap.data('id')];
newSystemPositions = MapUtil.newSystemPositionsByMap(activeMap);
}
let updatedUserData = {
@@ -475,6 +481,10 @@ define([
systemData: Util.getCurrentSystemData()
};
if(newSystemPositions){
updatedUserData.newSystemPositions = newSystemPositions;
}
Util.timeStart(logKeyServerUserData);
$.ajax({

View File

@@ -353,7 +353,7 @@ define([
// request "additional" system data (e.g. Structures, Description)
// -> this is used to update some modules after initial draw
let promiseRequestData = Util.request('GET', 'system', currentSystemData.systemData.id, {mapId: currentSystemData.mapId});
let promiseRequestData = Util.request('GET', 'system', currentSystemData.id, {mapId: currentSystemData.mapId});
// draw modules -------------------------------------------------------------------------------------------
@@ -361,22 +361,22 @@ define([
let secondCell = tabContentElement.find('.' + config.mapTabContentCellSecond);
// draw system info module
let promiseInfo = drawModule(firstCell, SystemInfoModule, currentSystemData.mapId, currentSystemData.systemData);
let promiseInfo = drawModule(firstCell, SystemInfoModule, currentSystemData.mapId, currentSystemData);
// draw system graph module
drawModule(firstCell, SystemGraphModule, currentSystemData.mapId, currentSystemData.systemData);
drawModule(firstCell, SystemGraphModule, currentSystemData.mapId, currentSystemData);
// draw signature table module
let promiseSignature = drawModule(firstCell, SystemSignatureModule, currentSystemData.mapId, currentSystemData.systemData);
let promiseSignature = drawModule(firstCell, SystemSignatureModule, currentSystemData.mapId, currentSystemData);
// draw system routes module
drawModule(secondCell, SystemRouteModule, currentSystemData.mapId, currentSystemData.systemData);
drawModule(secondCell, SystemRouteModule, currentSystemData.mapId, currentSystemData);
// draw system intel module
let promiseIntel = drawModule(secondCell, SystemIntelModule, currentSystemData.mapId, currentSystemData.systemData);
let promiseIntel = drawModule(secondCell, SystemIntelModule, currentSystemData.mapId, currentSystemData);
// draw system killboard module
drawModule(secondCell, SystemKillboardModule, currentSystemData.mapId, currentSystemData.systemData);
drawModule(secondCell, SystemKillboardModule, currentSystemData.mapId, currentSystemData);
// update some modules ------------------------------------------------------------------------------------
promiseDrawAll.push(promiseRequestData, promiseInfo, promiseSignature, promiseIntel);
@@ -479,7 +479,7 @@ define([
if(
currentSystemData &&
systemData.id === currentSystemData.systemData.id
systemData.id === currentSystemData.id
){
// trigger system update events
let tabContentElement = $('#' + config.mapTabIdPrefix + systemData.mapId + '.' + config.mapTabContentClass);

View File

@@ -11,7 +11,6 @@ define([
'app/map/util',
'app/map/contextmenu',
'slidebars',
'text!img/logo.svg!strip',
'text!templates/layout/header_map.html',
'text!templates/layout/footer_map.html',
'dialog/notification',
@@ -27,7 +26,7 @@ define([
'dialog/credit',
'xEditable',
'app/module_map'
], ($, Init, Util, Logging, Mustache, MapUtil, MapContextMenu, SlideBars, TplLogo, TplHead, TplFooter) => {
], ($, Init, Util, Logging, Mustache, MapUtil, MapContextMenu, SlideBars, TplHead, TplFooter) => {
'use strict';
@@ -152,6 +151,7 @@ define([
loadFooter(pageElement),
loadLeftMenu(pageMenuLeftElement),
loadRightMenu(pageMenuRightElement),
loadSVGs()
]).then(payload => Promise.all([
setMenuObserver(payload[2].data),
setMenuObserver(payload[3].data),
@@ -413,6 +413,35 @@ define([
return new Promise(executor);
};
/**
* load standalone <svg>´s into DOM
* -> SVGs can be used by referencing its ID e.g.:
* <svg width="12px" height="12px"><use xlink:href="#pf-svg-swords"/></svg>
* @returns {Promise<any>}
*/
let loadSVGs = () => {
let executor = resolve => {
let parentElement = $('body');
let svgPaths = [
'img/svg/logo.svg',
'img/svg/swords.svg'
].map(path => 'text!' + path + '!strip');
requirejs(svgPaths, (...SVGs) => {
parentElement.append.apply(parentElement, SVGs);
resolve({
action: 'loadSVGs',
data: {}
});
});
};
return new Promise(executor);
};
/**
* load page header
* @param pageElement
@@ -423,7 +452,6 @@ define([
let executor = resolve => {
let moduleData = {
id: config.pageHeaderId,
logo: () => Mustache.render(TplLogo, {}),
brandLogo: config.menuHeadMenuLogoClass,
popoverTriggerClass: Util.config.popoverTriggerClass,
userCharacterClass: config.headUserCharacterClass,
@@ -853,6 +881,10 @@ define([
let modalElement = $(e.target);
modalElement.destroyTimestampCounter(true);
// destroy all form validators
// -> does not work properly. validation functions still used (js error) after 'destroy'
//modalElement.find('form').filter((i, form) => $(form).data('bs.validator')).validator('destroy');
// destroy all popovers
modalElement.find('.' + Util.config.popoverTriggerClass).popover('destroy');
@@ -980,7 +1012,13 @@ define([
if(changes.charactersIds){
updateTasks.push(updateHeaderCharacterSwitch(userData, changes.characterId));
}
if(changes.characterSystemId || changes.characterShipType || changes.characterLogHistory){
if(
changes.characterSystemId ||
changes.characterShipType ||
changes.characterStationId ||
changes.characterStructureId ||
changes.characterLogHistory
){
updateTasks.push(updateHeaderCharacterLocation(userData, changes.characterShipType));
}
@@ -1022,7 +1060,7 @@ define([
if(changedCharacter){
// current character changed
userInfoElement.find('span').text(Util.getObjVal(userData, 'character.name'));
userInfoElement.find('img').attr('src', Init.url.ccpImageServer + '/Character/' + Util.getObjVal(userData, 'character.id') + '_32.jpg');
userInfoElement.find('img').attr('src', Util.eveImageUrl('character', Util.getObjVal(userData, 'character.id')));
}
// init "character switch" popover
userInfoElement.initCharacterSwitchPopover(userData);
@@ -1055,6 +1093,16 @@ define([
let shipTypeId = Util.getObjVal(shipData, 'typeId') || 0;
let shipTypeName = Util.getObjVal(shipData, 'typeName') || '';
let stationData = Util.getObjVal(userData, 'character.log.station');
let stationId = Util.getObjVal(stationData, 'id') || 0;
let stationName = Util.getObjVal(stationData, 'name') || '';
let structureData = Util.getObjVal(userData, 'character.log.structure');
let structureTypeId = Util.getObjVal(structureData, 'type.id') || 0;
let structureTypeName = Util.getObjVal(structureData, 'type.name') || '';
let structureId = Util.getObjVal(structureData, 'id') || 0;
let structureName = Util.getObjVal(structureData, 'name') || '';
logDataAll.push(logData);
// check for log history data as well
@@ -1084,15 +1132,20 @@ define([
if(isCurrentLocation){
breadcrumbHtml += '<i class="fas fa-fw fa-map-marker-alt" title="current location"></i>';
if(stationId > 0){
breadcrumbHtml += '<i class="fas fa-home" title="' + stationName + '"></i>';
}else if(structureId > 0){
breadcrumbHtml += '<i class="fas fa-industry" title="' + structureTypeName + ' &quot;' + structureName + '&quot;"></i>';
}
}
breadcrumbHtml += systemName;
if(isCurrentLocation && shipTypeId){
// show ship image
let shipSrc = Init.url.ccpImageServer + '/Render/' + shipTypeId + '_32.png';
breadcrumbHtml += '<img class="pf-head-image --right" ';
breadcrumbHtml += 'src="' + shipSrc + '" ';
breadcrumbHtml += 'src="' + Util.eveImageUrl('render', shipTypeId) + '" ';
breadcrumbHtml += 'title="' + shipTypeName + '" ';
breadcrumbHtml += '>';
}

View File

@@ -13,6 +13,10 @@ define([
let config = {
splashOverlayClass: 'pf-splash', // class for "splash" overlay
// navigation
navigationElementId: 'pf-navbar', // id for navbar element
webSocketStatsId: 'pf-setup-webSocket-stats', // id for webSocket "stats" panel
webSocketRefreshStatsId: 'pf-setup-webSocket-stats-refresh' // class for "reload stats" button
};
@@ -64,7 +68,9 @@ define([
let body = $('body');
// navigation (scroll) ----------------------------------------------------------------------------------------
Util.initPageScroll(body);
Util.initScrollSpy(document.getElementById(config.navigationElementId), window, {
offset: 300
});
// collapse ---------------------------------------------------------------------------------------------------
setCollapseObserver(body, '[data-toggle="collapse"]');
@@ -87,6 +93,7 @@ define([
let url = '/api/setup/' + element.attr('data-action');
sendRequest(url, {
type: element.attr('data-type'),
countAll: element.attr('data-countall'),
count: 0,
offset: 0
}, {
@@ -110,10 +117,10 @@ define([
* @param responseData
*/
let updateIndexCount = (context, responseData) => {
let countElement = context.target.closest('.row').children().eq(1).find('kbd');
let countElement = context.target.closest('tr').children().eq(1).find('kbd');
countElement.text(responseData.countBuildAll + '/' + responseData.countAll);
countElement.removeClass('txt-color-success txt-color-danger txt-color-warning');
if(responseData.countBuildAll >=responseData.countAll){
if(responseData.countBuildAll >= responseData.countAll){
countElement.addClass('txt-color-success');
}else if(responseData.countBuildAll > 0){
countElement.addClass('txt-color-warning');
@@ -121,6 +128,12 @@ define([
countElement.addClass('txt-color-danger');
}
// update 'subCount' element (shows e.g. invType count)
if(responseData.subCount){
let subCountElement = context.target.closest('tr').children().eq(2).find('kbd');
subCountElement.text(responseData.subCount.countBuildAll + '/' + subCountElement.attr('data-countall'));
}
context.target.find('.btn-progress').html('&nbsp;&nbsp;' + responseData.progress + '%').css('width', responseData.progress + '%');
// send next chunk of rows -> import only
@@ -130,6 +143,7 @@ define([
){
sendRequest(context.url, {
type: responseData.type,
countAll: responseData.countAll,
count: responseData.count,
offset: responseData.offset
}, {
@@ -188,8 +202,8 @@ define([
* @param data
*/
let updateWebSocketPanel = (data) => {
let badgeSocketWarning = $('.navbar a[data-anchor="#pf-setup-socket"] .txt-color-warning');
let badgeSocketDanger = $('.navbar a[data-anchor="#pf-setup-socket"] .txt-color-danger');
let badgeSocketWarning = $('.navbar a[data-target="pf-setup-socket"] .txt-color-warning');
let badgeSocketDanger = $('.navbar a[data-target="pf-setup-socket"] .txt-color-danger');
let socketWarningCount = parseInt(badgeSocketWarning.text()) || 0;
let socketDangerCount = parseInt(badgeSocketDanger.text()) || 0;

View File

@@ -77,11 +77,9 @@ define([
// show "discard" changes confirmation
let confirmationSettings = {
container: 'body',
placement: 'top',
btnCancelClass: 'btn btn-sm btn-default',
btnCancelLabel: 'cancel',
title: 'discard changes',
btnCancelIcon: '',
btnOkClass: 'btn btn-sm btn-warning',
btnOkLabel: 'discard',
btnOkIcon: 'fas fa-fw fa-ban',

View File

@@ -38,7 +38,8 @@ define([
switch(render(val)){
case 'green': return 'ok';
case 'yellow': return 'degraded: Slow or potentially dropping requests';
case 'red': return 'bad: Most requests are not succeeding and/or are very slow (5s+) on average';
case 'orange': return 'bad: Most requests are not succeeding and/or are very slow (5s+) on average';
case 'red': return 'error: Status data not available. Either offline or any other fatal error';
default: return 'unknown';
}
};

View File

@@ -29,12 +29,39 @@ define([
*/
$.fn.showJumpInfoDialog = function(){
requirejs(['text!templates/dialog/jump_info.html', 'mustache', 'datatables.loader'], (template, Mustache) => {
let iconShattered = '<i class="fas fa-fw fa-chart-pie pf-system-sec-unknown"></i>';
let iconDrifter = '<i class="fas fa-fw fa-wave-square pf-system-sec-drifter"></i>';
let staticsMatrixHead = [
let formatTableBodyData = (head, matrixBody) => {
return matrixBody.map((row, rowIndex) => {
return row.map((label, colIndex) => {
// get security name from "matrix Head" data if NOT first column
let secName = colIndex ? head[0][colIndex] : label;
return {
label: label,
class: Util.getSecurityClassForSystem(secName),
hasPopover: colIndex && label.length
};
});
});
};
// Statics table first ------------------------------------------------------------------------------------
let headGroupFirst = [
[
{label: '', class: 'separator-right', style: 'width: 55px;'},
{colspan: 6, label: 'W-space', class: 'separator-right'},
{colspan: 3, label: 'K-space', class: 'separator-right'},
{label: 'Thera', class: 'separator-right'},
{label: iconShattered}
]
];
let headFirst = [
['From╲To', 'C1', 'C2', 'C3', 'C4', 'C5', 'C6', 'H', 'L', '0.0', 'C12', 'C13']
];
let staticsMatrixBody = [
let matrixBodyFirst = [
['C1', 'H121', 'C125', 'O883', 'M609', 'L614', 'S804', 'N110', 'J244', 'Z060', 'F353', ''],
['C2', 'Z647', 'D382', 'O477', 'Y683', 'N062', 'R474', 'B274', 'A239', 'E545', 'F135', ''],
['C3', 'V301', 'I182', 'N968', 'T405', 'N770', 'A982', 'D845', 'U210', 'K346', 'F135', ''],
@@ -48,22 +75,48 @@ define([
['?', 'E004', 'L005', 'Z006', 'M001', 'C008', 'G008', '' , '' , 'Q003', '' , 'A009']
];
let staticsTableDataFirst = {
headGroup: headGroupFirst,
head: headFirst,
body: formatTableBodyData(headFirst, matrixBodyFirst)
};
// Statics table second -----------------------------------------------------------------------------------
let headGroupSecond = [
[
{label: '', class: 'separator-right', style: 'width: 55px;'},
{label: iconDrifter + '&nbsp;&nbsp;' + 'Sentinel', class: 'separator-right'},
{label: iconDrifter + '&nbsp;&nbsp;' + 'Barbican', class: 'separator-right'},
{label: iconDrifter + '&nbsp;&nbsp;' + 'Vidette', class: 'separator-right'},
{label: iconDrifter + '&nbsp;&nbsp;' + 'Conflux', class: 'separator-right'},
{label: iconDrifter + '&nbsp;&nbsp;' + 'Redoubt'}
]
];
let headSecond = [
['From╲To', 'C14', 'C15', 'C16', 'C17', 'C18']
];
let matrixBodySecond = [
['?', 'S877', 'B735', 'V928', 'C414', 'R259']
];
let staticsTableDataSecond = {
headline: 'Drifter W-space',
headGroup: headGroupSecond,
head: headSecond,
body: formatTableBodyData(headSecond, matrixBodySecond)
};
let staticsTablesData = [staticsTableDataFirst, staticsTableDataSecond];
let data = {
config: config,
popoverTriggerClass: Util.config.popoverTriggerClass,
wormholes: Object.keys(Init.wormholes).map(function(k){ return Init.wormholes[k]; }), // convert Json to array
staticsMatrixHead: staticsMatrixHead,
staticsMatrixBody: staticsMatrixBody.map((row, rowIndex) => {
return row.map((label, colIndex) => {
// get security name from "matrix Head" data if NOT first column
let secName = colIndex ? staticsMatrixHead[0][colIndex] : label;
return {
label: label,
class: Util.getSecurityClassForSystem(secName),
hasPopover: colIndex && label.length
};
});
}),
staticsTablesData: staticsTablesData,
massValue: function(){
return function(value, render){
let mass = render(value);

View File

@@ -26,7 +26,6 @@ define([
$.fn.showMapManual = function(){
requirejs(['text!templates/dialog/map_manual.html', 'mustache'], (template, Mustache) => {
let data = {
dialogNavigationClass: config.dialogNavigationClass,
dialogNavLiClass: config.dialogNavigationListItemClass,
@@ -67,8 +66,7 @@ define([
let scrollspyElement = $('#' + config.mapManualScrollspyId);
let whileScrolling = function(){
let whileScrolling = () => {
if(disableOnScrollEvent === false){
for(let i = 0; i < scrollBreakpointElements.length; i++){
let offset = $(scrollBreakpointElements[i]).offset().top;
@@ -124,11 +122,9 @@ define([
let mainNavigationLiElement = $(this).parent('.' + config.dialogNavigationListItemClass);
whileScrolling();
// if link is a main navigation link (not an anchor link)
if(mainNavigationLiElement.length > 0){
// remove all active classes
scrollNavLiElements.removeClass('active');
@@ -138,7 +134,6 @@ define([
}
});
},
onScroll: function(){
disableOnScrollEvent = false;

View File

@@ -56,18 +56,6 @@ define([
}
};
// confirmation dialog settings (e.g. delete row)
let confirmationSettings = {
container: 'body',
placement: 'left',
btnCancelClass: 'btn btn-sm btn-default',
btnCancelLabel: 'cancel',
btnCancelIcon: 'fas fa-fw fa-ban',
btnOkClass: 'btn btn-sm btn-danger',
btnOkLabel: 'delete',
btnOkIcon: 'fas fa-fw fa-times'
};
/**
* get icon that marks a table cell as clickable
* @returns {string}
@@ -217,7 +205,7 @@ define([
let systemsElement = $(this).empty();
let systemTable = $('<table>', {
id: Util.getTableId(config.tableId, mapData.config.id, '', 'systems'),
id: Util.getTableId(config.tableId, 'systems', mapData.config.id, ''),
class: ['compact', 'stripe', 'order-column', 'row-border'].join(' ')
});
systemsElement.append(systemTable);
@@ -237,7 +225,7 @@ define([
paging: true,
lengthMenu: [[5, 10, 20, 50, -1], [5, 10, 20, 50, 'All']],
ordering: true,
order: [14, 'desc'],
order: [15, 'desc'],
hover: false,
data: mapData.data.systems,
columnDefs: [],
@@ -285,7 +273,7 @@ define([
}
},{
name: 'shattered',
title: '<i class="fas fa-skull" title="shattered" data-toggle="tooltip"></i>',
title: '<i class="fas fa-chart-pie" title="shattered" data-toggle="tooltip"></i>',
width: 10,
className: ['text-center', 'min-screen-l'].join(' '),
searchable: false,
@@ -294,7 +282,7 @@ define([
display: (cellData, type, rowData, meta) => {
let value = '';
if(cellData){
value = '<i class="fas fa-skull fa-fw ' + Util.getSecurityClassForSystem('SH') + '"></i>';
value = '<i class="fas fa-chart-pie fa-fw ' + Util.getSecurityClassForSystem('SH') + '"></i>';
}
return value;
}
@@ -324,6 +312,22 @@ define([
title: 'region',
data: 'region.name',
className: 'min-screen-l',
},{
name: 'sovereignty',
title: 'sov.',
width: 30,
className: 'text-center',
data: 'sovereignty.alliance.ticker',
defaultContent: '',
render: {
display: (cellData, type, rowData, meta) => {
let value = '';
if(cellData){
value = '&lt;' + cellData + '&gt;';
}
return value;
}
}
},{
name: 'planets',
title: '<i class="fas fa-circle" title="planets" data-toggle="tooltip"></i>',
@@ -456,44 +460,49 @@ define([
createdCell: function(cell, cellData, rowData, rowIndex, colIndex){
let tempTableElement = this;
let tempConfirmationSettings = confirmationSettings;
tempConfirmationSettings.title = 'Delete system';
tempConfirmationSettings.onConfirm = function(e, target){
let deleteRowElement = $(target).parents('tr');
let confirmationSettings = {
placement: 'left',
title: 'Delete system',
template: Util.getConfirmationTemplate(null, {
size: 'small',
noTitle: true
}),
onConfirm: function(e, target){
let deleteRowElement = $(target).parents('tr');
let activeMap = Util.getMapModule().getActiveMap();
let systemElement = $('#' + MapUtil.getSystemId(mapData.config.id, rowData.id) );
let activeMap = Util.getMapModule().getActiveMap();
let systemElement = $('#' + MapUtil.getSystemId(mapData.config.id, rowData.id) );
if(systemElement.length){
// trigger system delete event
activeMap.trigger('pf:deleteSystems', [{
systems: [systemElement[0]],
callback: function(deletedSystems){
// callback function after ajax "delete" success
// check if system was deleted
if(deletedSystems.length === 1){
// remove table row
tempTableElement.DataTable().rows(deleteRowElement).remove().draw();
if(systemElement.length){
// trigger system delete event
activeMap.trigger('pf:deleteSystems', [{
systems: [systemElement[0]],
callback: function(deletedSystems){
// callback function after ajax "delete" success
// check if system was deleted
if(deletedSystems.length === 1){
// remove table row
tempTableElement.DataTable().rows(deleteRowElement).remove().draw();
Util.showNotify({title: 'System deleted', text: rowData.name, type: 'success'});
Util.showNotify({title: 'System deleted', text: rowData.name, type: 'success'});
// refresh connection table (connections might have changed) --------------
let connectionsElement = $('#' + config.mapInfoConnectionsId);
let mapDataNew = activeMap.getMapDataFromClient(['hasId']);
// refresh connection table (connections might have changed) --------------
let connectionsElement = $('#' + config.mapInfoConnectionsId);
let mapDataNew = activeMap.getMapDataFromClient(['hasId']);
connectionsElement.initConnectionInfoTable(mapDataNew);
}else{
// error
Util.showNotify({title: 'Failed to delete system', text: rowData.name, type: 'error'});
connectionsElement.initConnectionInfoTable(mapDataNew);
}else{
// error
Util.showNotify({title: 'Failed to delete system', text: rowData.name, type: 'error'});
}
}
}
}]);
}]);
}
}
};
// init confirmation dialog
$(cell).confirmation(tempConfirmationSettings);
$(cell).confirmation(confirmationSettings);
}
}
],
@@ -652,23 +661,29 @@ define([
createdCell: function(cell, cellData, rowData, rowIndex, colIndex){
let tempTableElement = this;
let tempConfirmationSettings = confirmationSettings;
tempConfirmationSettings.title = 'Delete connection';
tempConfirmationSettings.onConfirm = function(e, target){
let deleteRowElement = $(target).parents('tr');
let confirmationSettings = {
placement: 'left',
title: 'Delete connection',
template: Util.getConfirmationTemplate(null, {
size: 'small',
noTitle: true
}),
onConfirm: function(e, target){
let deleteRowElement = $(target).parents('tr');
// deleteSignatures(row);
let connection = $().getConnectionById(mapData.config.id, rowData.id);
// deleteSignatures(row);
let connection = $().getConnectionById(mapData.config.id, rowData.id);
MapUtil.deleteConnections([connection], () => {
// callback function after ajax "delete" success
// remove table row
tempTableElement.DataTable().rows(deleteRowElement).remove().draw();
});
MapUtil.deleteConnections([connection], () => {
// callback function after ajax "delete" success
// remove table row
tempTableElement.DataTable().rows(deleteRowElement).remove().draw();
});
}
};
// init confirmation dialog
$(cell).confirmation(tempConfirmationSettings);
$(cell).confirmation(confirmationSettings);
}
}
],
@@ -749,7 +764,7 @@ define([
_: function(data, type, row, meta){
let value = data;
if(data && type === 'display'){
value = '<img src="' + Init.url.ccpImageServer + '/Render/' + value.typeId + '_32.png" title="' + value.typeName + '" data-toggle="tooltip" />';
value = '<img src="' + Util.eveImageUrl('render', value.typeId) + '" title="' + value.typeName + '" data-toggle="tooltip" />';
}
return value;
}
@@ -787,7 +802,7 @@ define([
_: function(data, type, row, meta){
let value = data;
if(type === 'display'){
value = '<img src="' + Init.url.ccpImageServer + '/Character/' + value + '_32.jpg" />';
value = '<img src="' + Util.eveImageUrl('character', value) + '"/>';
}
return value;
}
@@ -827,7 +842,7 @@ define([
_: function(data, type, row, meta){
let value = data;
if(type === 'display'){
value = '<img src="' + Init.url.ccpImageServer + '/Corporation/' + value.id + '_32.png" />';
value = '<img src="' + Util.eveImageUrl('corporation', value.id) + '"/>';
}
return value;
}
@@ -849,7 +864,7 @@ define([
}
},
createdCell: function(cell, cellData, rowData, rowIndex, colIndex){
// open character information window (ingame)
// open corporation information window (ingame)
$(cell).on('click', { tableApi: this.api() }, function(e){
let cellData = e.data.tableApi.cell(this).data();
Util.openIngameWindow(cellData.id);
@@ -1110,7 +1125,7 @@ define([
_: function(data, type, row, meta){
let value = data;
if(type === 'display'){
value = '<img src="' + Init.url.ccpImageServer + '/Character/' + value + '_32.jpg" />';
value = '<img src="' + Util.eveImageUrl('character', value) + '"/>';
}
return value;
}

View File

@@ -36,6 +36,7 @@ define([
deleteEolConnectionsId: 'pf-map-dialog-delete-connections-eol', // id for "deleteEOLConnections" checkbox
persistentAliasesId: 'pf-map-dialog-persistent-aliases', // id for "persistentAliases" checkbox
persistentSignaturesId: 'pf-map-dialog-persistent-signatures', // id for "persistentSignatures" checkbox
trackAbyssalJumpsId: 'pf-map-dialog-track-abyss-jumps', // id for "trackAbyssalJumps" checkbox
logHistoryId: 'pf-map-dialog-history', // id for "history logging" checkbox
logActivityId: 'pf-map-dialog-activity', // id for "activity" checkbox
@@ -159,6 +160,7 @@ define([
let deleteEolConnections = true;
let persistentAliases = true;
let persistentSignatures = true;
let trackAbyssalJumps = true;
let logActivity = true;
let logHistory = true;
@@ -197,6 +199,7 @@ define([
deleteEolConnections = mapData.config.deleteEolConnections;
persistentAliases = mapData.config.persistentAliases;
persistentSignatures = mapData.config.persistentSignatures;
trackAbyssalJumps = mapData.config.trackAbyssalJumps;
logActivity = mapData.config.logging.activity;
logHistory = mapData.config.logging.history;
@@ -255,10 +258,12 @@ define([
deleteEolConnectionsId : config.deleteEolConnectionsId,
persistentAliasesId : config.persistentAliasesId,
persistentSignaturesId : config.persistentSignaturesId,
trackAbyssalJumpsId : config.trackAbyssalJumpsId,
deleteExpiredConnections: deleteExpiredConnections,
deleteEolConnections: deleteEolConnections,
persistentAliases: persistentAliases,
persistentSignatures: persistentSignatures,
trackAbyssalJumps: trackAbyssalJumps,
logHistoryId: config.logHistoryId,
logActivityId: config.logActivityId,
@@ -398,6 +403,9 @@ define([
if( form.find('#' + config.persistentSignaturesId).length ){
formData.persistentSignatures = formData.hasOwnProperty('persistentSignatures') ? parseInt( formData.persistentSignatures ) : 0;
}
if( form.find('#' + config.trackAbyssalJumpsId).length ){
formData.trackAbyssalJumps = formData.hasOwnProperty('trackAbyssalJumps') ? parseInt( formData.trackAbyssalJumps ) : 0;
}
if( form.find('#' + config.logHistoryId).length ){
formData.logHistory = formData.hasOwnProperty('logHistory') ? parseInt( formData.logHistory ) : 0;
}

View File

@@ -130,7 +130,7 @@ define([
data: 'character',
render: {
_: function(data, type, row, meta){
return '<img src="' + Init.url.ccpImageServer + '/Character/' + data.id + '_32.jpg" />';
return '<img src="' + Util.eveImageUrl('character', parseInt(data.id)) + '"/>';
}
}
},{

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