@@ -16,6 +16,10 @@ HALT = FALSE
|
||||
; Timezone to use. Sync program with eve server time. (default: UTC)
|
||||
TZ = UTC
|
||||
|
||||
; Cache key prefix. Same for all cache values for this installation
|
||||
; CLI (cronjob) scripts use it for cache manipulation
|
||||
SEED = {{ md5(@SERVER.SERVER_NAME) }}
|
||||
|
||||
; Cache backend. Can handle Redis, Memcache module, APC, WinCache, XCache and a filesystem-based cache.
|
||||
; (default: folder=tmp/cache/)
|
||||
CACHE = folder=tmp/cache/
|
||||
|
||||
@@ -54,4 +54,7 @@ deleteAuthenticationData = Cron\CharacterUpdate->deleteAuthenticati
|
||||
deleteExpiredCacheData = Cron\Cache->deleteExpiredData, @downtime
|
||||
|
||||
; delete old statistics (activity log) data
|
||||
deleteStatisticsData = Cron\StatisticsUpdate->deleteStatisticsData, @weekly
|
||||
deleteStatisticsData = Cron\StatisticsUpdate->deleteStatisticsData, @weekly
|
||||
|
||||
; setup universe DB with static data from ESI
|
||||
; setup = Cron\Universe->setup, @instant
|
||||
@@ -1,5 +1,145 @@
|
||||
CHANGELOG
|
||||
|
||||
3.6.4 (19 April 2018)
|
||||
* NEW: Added Dependency Injection support with CONTAINER variable [#221](https://github.com/bcosca/fatfree-core/issues/221)
|
||||
* NEW: configurable LOGGABLE error codes [#1091](https://github.com/bcosca/fatfree/issues/1091#issuecomment-364674701)
|
||||
* NEW: JAR.lifetime option, [#178](https://github.com/bcosca/fatfree-core/issues/178)
|
||||
* Template: reduced Prefab calls
|
||||
* Template: optimized reflection for better derivative support, [bcosca/fatfree#1088](https://github.com/bcosca/fatfree/issues/1088)
|
||||
* Template: optimized parsing for template attributes and tokens
|
||||
* DB\Mongo: fixed logging with mongodb extention
|
||||
* DB\Jig: added lazy-loading [#7e1cd9b9b89](https://github.com/bcosca/fatfree-core/commit/7e1cd9b9b89c4175d0f6b86ced9d9bd49c04ac39)
|
||||
* DB\Jig\Mapper: Added group feature, bcosca/fatfree#616
|
||||
* DB\SQL\Mapper: fix PostgreSQL RETURNING ID when no pkey is available, [bcosca/fatfree#1069](https://github.com/bcosca/fatfree/issues/1069), [#230](https://github.com/bcosca/fatfree-core/issues/230)
|
||||
* DB\SQL\Mapper: disable order clause auto-quoting when it's already been quoted
|
||||
* Web->location: add failsafe for geoip_region_name_by_code() [#GB:Bxyn9xn9AgAJ](https://groups.google.com/d/msg/f3-framework/APau4wnwNzE/Bxyn9xn9AgAJ)
|
||||
* Web->request: Added proxy support [#e936361b](https://github.com/bcosca/fatfree-core/commit/e936361bc03010c4c7c38a396562e5e96a8a100d)
|
||||
* Web->mime: Added JFIF format
|
||||
* Markdown: handle line breaks in paragraph blocks, [bcosca/fatfree#1100](https://github.com/bcosca/fatfree/issues/1100)
|
||||
* config: reduced cast calls on parsing config sections
|
||||
* Patch empty SERVER_NAME [bcosca/fatfree#1084](https://github.com/bcosca/fatfree/issues/1084)
|
||||
* Bugfix: unreliable request headers in Web->request() response [bcosca/fatfree#1092](https://github.com/bcosca/fatfree/issues/1092)
|
||||
* Fixed, View->render: utilizing multiple UI paths, [bcosca/fatfree#1083](https://github.com/bcosca/fatfree/issues/1083)
|
||||
* Fixed URL parsing with PHP 5.4 [#247](https://github.com/bcosca/fatfree-core/issues/247)
|
||||
* Fixed PHP 7.2 warnings when session is active prematurely, [#238](https://github.com/bcosca/fatfree-core/issues/238)
|
||||
* Fixed setcookie $expire variable type [#240](https://github.com/bcosca/fatfree-core/issues/240)
|
||||
* Fixed expiration time when updating an existing cookie
|
||||
|
||||
3.6.3 (31 December 2017)
|
||||
* PHP7 fix: remove deprecated (unset) cast
|
||||
* Web->request: restricted follow_location to 3XX responses only
|
||||
* CLI mode: refactored arguments parsing
|
||||
* CLI mode: fixed query string encoding
|
||||
* SMTP: Refactor parsing of attachments
|
||||
* SMTP: clean-up mail headers for multipart messages, [#1065](https://github.com/bcosca/fatfree/issues/1065)
|
||||
* config: fixed performance issues on parsing config files
|
||||
* config: cast command parameters in config entries to php type & constant, [#1030](https://github.com/bcosca/fatfree/issues/1030)
|
||||
* config: reduced registry calls
|
||||
* config: skip hive escaping when resolving dynamic config vars, [#1030](https://github.com/bcosca/fatfree/issues/1030)
|
||||
* Bug fix: Incorrect cookie lifetime computation, [#1070](https://github.com/bcosca/fatfree/issues/1070), [#1016](https://github.com/bcosca/fatfree/issues/1016)
|
||||
* DB\SQL\Mapper: use RETURNING option instead of a sequence query to get lastInsertId in PostgreSQL, [#1069](https://github.com/bcosca/fatfree/issues/1069), [#230](https://github.com/bcosca/fatfree-core/issues/230)
|
||||
* DB\SQL\Session: check if _agent is too long for SQL based sessions [#236](https://github.com/bcosca/fatfree-core/issues/236)
|
||||
* DB\SQL\Session: fix Session handler table creation issue on SQL Server, [#899](https://github.com/bcosca/fatfree/issues/899)
|
||||
* DB\SQL: fix oracle db issue with empty error variable, [#1072](https://github.com/bcosca/fatfree/issues/1072)
|
||||
* DB\SQL\Mapper: fix sorting issues on SQL Server, [#1052](https://github.com/bcosca/fatfree/issues/1052) [#225](https://github.com/bcosca/fatfree-core/issues/225)
|
||||
* Prevent directory traversal attacks on filesystem based cache [#1073](https://github.com/bcosca/fatfree/issues/1073)
|
||||
* Bug fix, Template: PHP constants used in include with attribute, [#983](https://github.com/bcosca/fatfree/issues/983)
|
||||
* Bug fix, Template: Numeric value in expression alters PHP_EOL context
|
||||
* Template: use existing linefeed instead of PHP_EOL, [#1048](https://github.com/bcosca/fatfree/issues/1048)
|
||||
* Template: make newline interpolation handling configurable [#223](https://github.com/bcosca/fatfree-core/issues/223)
|
||||
* Template: add beforerender to Preview
|
||||
* fix custom FORMATS without modifiers
|
||||
* Cache: Refactor Cache->reset for XCache
|
||||
* Cache: loosen reset cache key pattern, [#1041](https://github.com/bcosca/fatfree/issues/1041)
|
||||
* XCache: suffix reset only works if xcache.admin.enable_auth is disabled
|
||||
* Added HTTP 103 as recently approved by the IETF
|
||||
* LDAP changes to for AD flexibility [#227](https://github.com/bcosca/fatfree-core/issues/227)
|
||||
* Hide debug trace from ajax errors when DEBUG=0 [#1071](https://github.com/bcosca/fatfree/issues/1071)
|
||||
* fix View->render using potentially wrong cache entry
|
||||
|
||||
3.6.2 (26 June 2017)
|
||||
* Return a status code > 0 when dying on error [#220](https://github.com/bcosca/fatfree-core/issues/220)
|
||||
* fix SMTP line width [#215](https://github.com/bcosca/fatfree-core/issues/215)
|
||||
* Allow using a custom field for ldap user id checking [#217](https://github.com/bcosca/fatfree-core/issues/217)
|
||||
* NEW: DB\SQL->exists: generic method to check if SQL table exists
|
||||
* Pass handler to route handler and hooks [#1035](https://github.com/bcosca/fatfree/issues/1035)
|
||||
* pass carriage return of multiline dictionary keys
|
||||
* Better Web->slug customization
|
||||
* fix incorrect header issue [#211](https://github.com/bcosca/fatfree-core/issues/211)
|
||||
* fix schema issue on databases with case-sensitive collation, fixes [#209](https://github.com/bcosca/fatfree-core/issues/209)
|
||||
* Add filter for deriving C-locale equivalent of a number
|
||||
* Bug fix: @LANGUAGE remains unchanged after override
|
||||
* abort: added Header pre-check
|
||||
* Assemble URL after ONREROUTE
|
||||
* Add reroute argument to skip script termination
|
||||
* Invoke ONREROUTE after headers are sent
|
||||
* SQLite switch to backtick as quote
|
||||
* Bug fix: Incorrect timing in SQL query logs
|
||||
* DB\SQL\Mapper: Cast return value of count to integer
|
||||
* Patched $_SERVER['REQUEST_URI'] to ensure it contains a relative URI
|
||||
* Tweak debug verbosity
|
||||
* fix php carriage return issue in preview->build [#205](https://github.com/bcosca/fatfree-core/pull/205)
|
||||
* fixed template string resolution [#205](https://github.com/bcosca/fatfree-core/pull/205)
|
||||
* Fixed unexpected default seed on CACHE set [#1028](https://github.com/bcosca/fatfree/issues/1028)
|
||||
* DB\SQL\Mapper: Optimized field escaping on options
|
||||
* Optimize template conversion to PHP file
|
||||
|
||||
3.6.1 (2 April 2017)
|
||||
* NEW: Recaptcha plugin [#194](https://github.com/bcosca/fatfree-core/pull/194)
|
||||
* NEW: MB variable for detecting multibyte support
|
||||
* NEW: DB\SQL: Cache parsed schema for the TTL duration
|
||||
* NEW: quick erase flag on Jig/Mongo/SQL mappers [#193](https://github.com/bcosca/fatfree-core/pull/193)
|
||||
* NEW: Allow OPTIONS method to return a response body [#171](https://github.com/bcosca/fatfree-core/pull/171)
|
||||
* NEW: Add support for Memcached (bcosca/fatfree#997)
|
||||
* NEW: Rudimentary preload resource (HTTP2 server) support via template push()
|
||||
* NEW: Add support for new MongoDB driver [#177](https://github.com/bcosca/fatfree-core/pull/177)
|
||||
* Changed: template filter are all lowercase now
|
||||
* Changed: Fix template lookup inconsistency: removed base dir from UI on render
|
||||
* Changed: count() method now has an options argument [#192](https://github.com/bcosca/fatfree-core/pull/192)
|
||||
* Changed: SMTP, Spit out error message if any
|
||||
* \DB\SQL\Mapper: refactored row count strategy
|
||||
* DB\SQL\Mapper: Allow non-scalar values to be assigned as mapper property
|
||||
* DB\SQL::PARAM_FLOAT: remove cast to float (#106 and bcosca/fatfree#984) (#191)
|
||||
* DB\SQL\mapper->erase: allow empty string
|
||||
* DB\SQL\mapper->insert: fields reset after successful INSERT
|
||||
* Add option to debounce Cursor->paginate subset [#195](https://github.com/bcosca/fatfree-core/pull/195)
|
||||
* View: Don't delete sandboxed variables (#198)
|
||||
* Preview: Optimize compilation of template expressions
|
||||
* Preview: Use shorthand tag for direct rendering
|
||||
* Preview->resolve(): new tweak to allow template persistence as option
|
||||
* Web: Expose diacritics translation table
|
||||
* SMTP: Enable logging of message body only when $log argument is 'verbose'
|
||||
* SMTP: Convert headers to camelcase for consistency
|
||||
* make cache seed more flexible, #164
|
||||
* Improve trace details for DEBUG>2
|
||||
* Enable config() to read from an array of input files
|
||||
* Improved alias and reroute regex
|
||||
* Make camelCase and snakeCase Unicode-aware
|
||||
* format: Provision for optional whitespaces
|
||||
* Break APCu-BC dependence
|
||||
* Old PHP 5.3 cleanup
|
||||
* Debug log must include HTTP query
|
||||
* Recognize X-Forwarded-Port header (bcosca/fatfree#1002)
|
||||
* Avoid use of deprecated mcrypt module
|
||||
* Return only the client's IP when using the `X-Forwarded-For` header to deduce an IP address
|
||||
* Remove orphan mutex locks on termination (#157)
|
||||
* Use 80 as default port number to avoid issues when `$_SERVER['SERVER_PORT']` is not existing
|
||||
* fread replaced with readfile() for simple send() usecase
|
||||
* Bug fix: request URI with multiple leading slashes, #203
|
||||
* Bug fix: Query generates wrong adhoc field value
|
||||
* Bug fix: SMTP stream context issue #200
|
||||
* Bug fix: child pseudo class selector in minify, bcosca/fatfree#1008
|
||||
* Bug fix: "Undefined index: CLI" error (#197)
|
||||
* Bug fix: cast Cache-Control expire time to int, bcosca/fatfree#1004
|
||||
* Bug fix: Avoid issuance of multiple Content-Type headers for nested templates
|
||||
* Bug fix: wildcard token issue with digits (bcosca/fatfree#996)
|
||||
* Bug fix: afterupdate ignored when row does not change
|
||||
* Bug fix: session handler read() method for PHP7 (need strict string) #184 #185
|
||||
* Bug fix: reroute mocking in CLI mode (#183)
|
||||
* Bug fix: Reroute authoritative relative references (#181)
|
||||
* Bug fix: locales order and charset hyphen
|
||||
* Bug fix: base stripped twice in router (#176)
|
||||
|
||||
3.6.0 (19 November 2016)
|
||||
* NEW: [cli] request type
|
||||
* NEW: console-friendly CLI mode
|
||||
@@ -89,6 +229,7 @@ CHANGELOG
|
||||
* Bug fix: encode CLI parameters
|
||||
* Bug fix: Close connection on abort explicitly (#162)
|
||||
* Bug fix: Image->identicon, Avoid double-size sprite rotation (and possible segfault)
|
||||
* Bug fix: Image->render and Image->dump, removed unnecessary 2nd argument (#146)
|
||||
* Bug fix: Magic->offsetset, access property as array element (#147)
|
||||
* Bug fix: multi-line custom template tag parsing (bcosca/fatfree#935)
|
||||
* Bug fix: cache headers on errors (bcosca/fatfree#885)
|
||||
107
app/lib/base.php
107
app/lib/base.php
@@ -45,7 +45,7 @@ final class Base extends Prefab implements ArrayAccess {
|
||||
//@{ Framework details
|
||||
const
|
||||
PACKAGE='Fat-Free Framework',
|
||||
VERSION='3.6.3-Release';
|
||||
VERSION='3.6.4-Release';
|
||||
//@}
|
||||
|
||||
//@{ HTTP status codes (RFC 2616)
|
||||
@@ -346,17 +346,16 @@ final class Base extends Prefab implements ArrayAccess {
|
||||
* @param $ttl int
|
||||
**/
|
||||
function set($key,$val,$ttl=0) {
|
||||
$time=$this->hive['TIME'];
|
||||
$time=(int)$this->hive['TIME'];
|
||||
if (preg_match('/^(GET|POST|COOKIE)\b(.+)/',$key,$expr)) {
|
||||
$this->set('REQUEST'.$expr[2],$val);
|
||||
if ($expr[1]=='COOKIE') {
|
||||
$parts=$this->cut($key);
|
||||
$jar=$this->unserialize($this->serialize($this->hive['JAR']));
|
||||
if (isset($_COOKIE[$parts[1]])) {
|
||||
$jar['expire']=0;
|
||||
unset($jar['lifetime']);
|
||||
if (isset($_COOKIE[$parts[1]]))
|
||||
call_user_func_array('setcookie',
|
||||
array_merge([$parts[1],NULL],$jar));
|
||||
}
|
||||
array_merge([$parts[1],NULL],['expire'=>0]+$jar));
|
||||
if ($ttl)
|
||||
$jar['expire']=$time+$ttl;
|
||||
call_user_func_array('setcookie',[$parts[1],$val]+$jar);
|
||||
@@ -395,9 +394,17 @@ final class Base extends Prefab implements ArrayAccess {
|
||||
$ref=&$this->ref($key);
|
||||
$ref=$val;
|
||||
if (preg_match('/^JAR\b/',$key)) {
|
||||
$jar=$this->unserialize($this->serialize($this->hive['JAR']));
|
||||
$jar['expire']-=$time;
|
||||
call_user_func_array('session_set_cookie_params',$jar);
|
||||
if ($key=='JAR.lifetime')
|
||||
$this->set('JAR.expire',$val==0?0:
|
||||
(is_int($val)?$time+$val:strtotime($val)));
|
||||
else {
|
||||
if ($key=='JAR.expire')
|
||||
$this->hive['JAR']['lifetime']=max(0,$val-$time);
|
||||
$jar=$this->unserialize($this->serialize($this->hive['JAR']));
|
||||
unset($jar['expire']);
|
||||
if (!headers_sent() && session_status()!=PHP_SESSION_ACTIVE)
|
||||
call_user_func_array('session_set_cookie_params',$jar);
|
||||
}
|
||||
}
|
||||
if ($ttl)
|
||||
// Persist the key-value pair
|
||||
@@ -442,6 +449,7 @@ final class Base extends Prefab implements ArrayAccess {
|
||||
if ($expr[1]=='COOKIE') {
|
||||
$parts=$this->cut($key);
|
||||
$jar=$this->hive['JAR'];
|
||||
unset($jar['lifetime']);
|
||||
$jar['expire']=0;
|
||||
call_user_func_array('setcookie',
|
||||
array_merge([$parts[1],NULL],$jar));
|
||||
@@ -1231,11 +1239,18 @@ final class Base extends Prefab implements ArrayAccess {
|
||||
$req.='?'.$this->hive['QUERY'];
|
||||
if (!$text)
|
||||
$text='HTTP '.$code.' ('.$req.')';
|
||||
error_log($text);
|
||||
$trace=$this->trace($trace);
|
||||
foreach (explode("\n",$trace) as $nexus)
|
||||
if ($nexus)
|
||||
error_log($nexus);
|
||||
$loggable=$this->hive['LOGGABLE'];
|
||||
if (!is_array($loggable))
|
||||
$loggable=$this->split($loggable);
|
||||
foreach ($loggable as $status)
|
||||
if ($status=='*' || preg_match('/^'.preg_replace('/\D/','\d',$status).'$/',$code)) {
|
||||
error_log($text);
|
||||
foreach (explode("\n",$trace) as $nexus)
|
||||
if ($nexus)
|
||||
error_log($nexus);
|
||||
break;
|
||||
}
|
||||
if ($highlight=!$this->hive['CLI'] && !$this->hive['AJAX'] &&
|
||||
$this->hive['HIGHLIGHT'] && is_file($css=__DIR__.'/'.self::CSS))
|
||||
$trace=$this->highlight($trace);
|
||||
@@ -1750,6 +1765,19 @@ final class Base extends Prefab implements ArrayAccess {
|
||||
if ($parts[2]=='->') {
|
||||
if (is_subclass_of($parts[1],'Prefab'))
|
||||
$parts[1]=call_user_func($parts[1].'::instance');
|
||||
elseif ($container=$this->get('CONTAINER')) {
|
||||
if (is_object($container) && is_callable([$container,'has'])
|
||||
&& $container->has($parts[1])) // PSR11
|
||||
$parts[1]=call_user_func([$container,'get'],$parts[1]);
|
||||
elseif (is_callable($container))
|
||||
$parts[1]=call_user_func($container,$parts[1],$args);
|
||||
elseif (is_string($container) && is_subclass_of($container,'Prefab'))
|
||||
$parts[1]=call_user_func($container.'::instance')->get($parts[1]);
|
||||
else
|
||||
user_error(sprintf(self::E_Class,
|
||||
$this->stringify($container)),
|
||||
E_USER_ERROR);
|
||||
}
|
||||
else {
|
||||
$ref=new ReflectionClass($parts[1]);
|
||||
$parts[1]=method_exists($parts[1],'__construct') && $args?
|
||||
@@ -1888,7 +1916,8 @@ final class Base extends Prefab implements ArrayAccess {
|
||||
call_user_func_array(
|
||||
[$this,$cmd[1]],
|
||||
array_merge([$match['lval']],
|
||||
str_getcsv($this->cast($match['rval'])))
|
||||
str_getcsv($cmd[1]=='config'?$this->cast($match['rval']):
|
||||
$match['rval']))
|
||||
);
|
||||
}
|
||||
else {
|
||||
@@ -2190,7 +2219,7 @@ final class Base extends Prefab implements ArrayAccess {
|
||||
$this->error(500,$text,NULL,$level);
|
||||
}
|
||||
);
|
||||
if (!isset($_SERVER['SERVER_NAME']))
|
||||
if (!isset($_SERVER['SERVER_NAME']) || $_SERVER['SERVER_NAME']==='')
|
||||
$_SERVER['SERVER_NAME']=gethostname();
|
||||
if ($cli=PHP_SAPI=='cli') {
|
||||
// Emulate HTTP request
|
||||
@@ -2266,14 +2295,14 @@ final class Base extends Prefab implements ArrayAccess {
|
||||
$base=rtrim($this->fixslashes(
|
||||
dirname($_SERVER['SCRIPT_NAME'])),'/');
|
||||
$uri=parse_url((preg_match('/^\w+:\/\//',$_SERVER['REQUEST_URI'])?'':
|
||||
'//'.$_SERVER['SERVER_NAME']).$_SERVER['REQUEST_URI']);
|
||||
$scheme.'://'.$_SERVER['SERVER_NAME']).$_SERVER['REQUEST_URI']);
|
||||
$_SERVER['REQUEST_URI']=$uri['path'].
|
||||
(isset($uri['query'])?'?'.$uri['query']:'').
|
||||
(isset($uri['fragment'])?'#'.$uri['fragment']:'');
|
||||
$path=preg_replace('/^'.preg_quote($base,'/').'/','',$uri['path']);
|
||||
session_cache_limiter('');
|
||||
$jar=[
|
||||
'expire'=>0,
|
||||
'lifetime'=>0,
|
||||
'path'=>$base?:'/',
|
||||
'domain'=>is_int(strpos($_SERVER['SERVER_NAME'],'.')) &&
|
||||
!filter_var($_SERVER['SERVER_NAME'],FILTER_VALIDATE_IP)?
|
||||
@@ -2281,7 +2310,6 @@ final class Base extends Prefab implements ArrayAccess {
|
||||
'secure'=>($scheme=='https'),
|
||||
'httponly'=>TRUE
|
||||
];
|
||||
call_user_func_array('session_set_cookie_params',$jar);
|
||||
$port=80;
|
||||
if (isset($headers['X-Forwarded-Port']))
|
||||
$port=$headers['X-Forwarded-Port'];
|
||||
@@ -2328,6 +2356,7 @@ final class Base extends Prefab implements ArrayAccess {
|
||||
$this->language($headers['Accept-Language']):
|
||||
$this->fallback,
|
||||
'LOCALES'=>'./',
|
||||
'LOGGABLE'=>'*',
|
||||
'LOGS'=>'./',
|
||||
'MB'=>extension_loaded('mbstring'),
|
||||
'ONERROR'=>NULL,
|
||||
@@ -2363,6 +2392,11 @@ final class Base extends Prefab implements ArrayAccess {
|
||||
'VERSION'=>self::VERSION,
|
||||
'XFRAME'=>'SAMEORIGIN'
|
||||
];
|
||||
if (!headers_sent() && session_status()!=PHP_SESSION_ACTIVE) {
|
||||
unset($jar['expire']);
|
||||
session_cache_limiter('');
|
||||
call_user_func_array('session_set_cookie_params',$jar);
|
||||
}
|
||||
if (PHP_SAPI=='cli-server' &&
|
||||
preg_match('/^'.preg_quote($base,'/').'$/',$this->hive['URI']))
|
||||
$this->reroute('/');
|
||||
@@ -2673,16 +2707,22 @@ class View extends Prefab {
|
||||
//! Nesting level
|
||||
$level=0;
|
||||
|
||||
/** @var \Base Framework instance */
|
||||
protected $fw;
|
||||
|
||||
function __construct() {
|
||||
$this->fw=\Base::instance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode characters to equivalent HTML entities
|
||||
* @return string
|
||||
* @param $arg mixed
|
||||
**/
|
||||
function esc($arg) {
|
||||
$fw=Base::instance();
|
||||
return $fw->recursive($arg,
|
||||
function($val) use($fw) {
|
||||
return is_string($val)?$fw->encode($val):$val;
|
||||
return $this->fw->recursive($arg,
|
||||
function($val) {
|
||||
return is_string($val)?$this->fw->encode($val):$val;
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -2693,10 +2733,9 @@ class View extends Prefab {
|
||||
* @param $arg mixed
|
||||
**/
|
||||
function raw($arg) {
|
||||
$fw=Base::instance();
|
||||
return $fw->recursive($arg,
|
||||
function($val) use($fw) {
|
||||
return is_string($val)?$fw->decode($val):$val;
|
||||
return $this->fw->recursive($arg,
|
||||
function($val) {
|
||||
return is_string($val)?$this->fw->decode($val):$val;
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -2708,7 +2747,7 @@ class View extends Prefab {
|
||||
* @param $mime string
|
||||
**/
|
||||
protected function sandbox(array $hive=NULL,$mime=NULL) {
|
||||
$fw=Base::instance();
|
||||
$fw=$this->fw;
|
||||
$implicit=FALSE;
|
||||
if (is_null($hive)) {
|
||||
$implicit=TRUE;
|
||||
@@ -2744,9 +2783,9 @@ class View extends Prefab {
|
||||
* @param $ttl int
|
||||
**/
|
||||
function render($file,$mime='text/html',array $hive=NULL,$ttl=0) {
|
||||
$fw=Base::instance();
|
||||
$fw=$this->fw;
|
||||
$cache=Cache::instance();
|
||||
foreach ($fw->split($fw->UI) as $dir)
|
||||
foreach ($fw->split($fw->UI) as $dir) {
|
||||
if ($cache->exists($hash=$fw->hash($dir.$file),$data))
|
||||
return $data;
|
||||
if (is_file($this->file=$fw->fixslashes($dir.$file))) {
|
||||
@@ -2762,6 +2801,7 @@ class View extends Prefab {
|
||||
$cache->set($hash,$data,$ttl);
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
user_error(sprintf(Base::E_Open,$file),E_USER_ERROR);
|
||||
}
|
||||
|
||||
@@ -2807,7 +2847,6 @@ class Preview extends View {
|
||||
* @param $val int|float
|
||||
**/
|
||||
function c($val) {
|
||||
$fw=Base::instance();
|
||||
$locale=setlocale(LC_NUMERIC,0);
|
||||
setlocale(LC_NUMERIC,'C');
|
||||
$out=(string)(float)$val;
|
||||
@@ -2821,13 +2860,13 @@ class Preview extends View {
|
||||
* @param $str string
|
||||
**/
|
||||
function token($str) {
|
||||
$fw = Base::instance();
|
||||
$fw=$this->fw;
|
||||
$str=trim(preg_replace('/\{\{(.+?)\}\}/s',trim('\1'),
|
||||
$fw->compile($str)));
|
||||
if (preg_match('/^(.+)(?<!\|)\|((?:\h*\w+(?:\h*[,;]?))+)$/s',
|
||||
$str,$parts)) {
|
||||
$str=trim($parts[1]);
|
||||
foreach ($fw->split($parts[2]) as $func)
|
||||
foreach ($fw->split(trim($parts[2],"\xC2\xA0")) as $func)
|
||||
$str=is_string($cmd=$this->filter($func))?
|
||||
$cmd.'('.$str.')':
|
||||
'Base::instance()->'.
|
||||
@@ -2890,7 +2929,7 @@ class Preview extends View {
|
||||
* @param $escape bool
|
||||
**/
|
||||
function resolve($node,array $hive=NULL,$ttl=0,$persist=FALSE,$escape=NULL) {
|
||||
$fw=Base::instance();
|
||||
$fw=$this->fw;
|
||||
$cache=Cache::instance();
|
||||
if ($escape!==NULL) {
|
||||
$esc=$fw->ESCAPE;
|
||||
@@ -2951,7 +2990,7 @@ class Preview extends View {
|
||||
* @param $ttl int
|
||||
**/
|
||||
function render($file,$mime='text/html',array $hive=NULL,$ttl=0) {
|
||||
$fw=Base::instance();
|
||||
$fw=$this->fw;
|
||||
$cache=Cache::instance();
|
||||
if (!is_dir($tmp=$fw->TEMP))
|
||||
mkdir($tmp,Base::MODE,TRUE);
|
||||
|
||||
@@ -18,8 +18,8 @@
|
||||
* https://github.com/ikkez/F3-Sugar/
|
||||
*
|
||||
* @package DB
|
||||
* @version 1.5.0
|
||||
* @date 30.06.2017
|
||||
* @version 1.6.0-dev
|
||||
* @date 25.04.2018
|
||||
* @since 24.04.2012
|
||||
*/
|
||||
|
||||
@@ -157,11 +157,19 @@ class Cortex extends Cursor {
|
||||
$f3->get('CORTEX.standardiseID') : TRUE;
|
||||
if(!empty($this->fieldConf))
|
||||
foreach($this->fieldConf as &$conf) {
|
||||
$conf=static::resolveRelationConf($conf);
|
||||
$conf=static::resolveRelationConf($conf,$this->primary);
|
||||
unset($conf);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* return raw mapper instance
|
||||
* @return Cursor
|
||||
*/
|
||||
public function getMapper() {
|
||||
return $this->mapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* get fields or set whitelist / blacklist of fields
|
||||
* @param array $fields
|
||||
@@ -512,9 +520,10 @@ class Cortex extends Cursor {
|
||||
/**
|
||||
* resolve relation field types
|
||||
* @param array $field
|
||||
* @param string $pkey
|
||||
* @return array
|
||||
*/
|
||||
protected static function resolveRelationConf($field) {
|
||||
protected static function resolveRelationConf($field,$pkey=NULL) {
|
||||
if (array_key_exists('belongs-to-one', $field)) {
|
||||
// find primary field definition
|
||||
if (!is_array($relConf = $field['belongs-to-one']))
|
||||
@@ -552,7 +561,10 @@ class Cortex extends Cursor {
|
||||
$field['has-many']['relField'] = $relConf[1];
|
||||
$field['has-many']['relFieldType'] = isset($rel['fieldConf'][$relConf[1]]['type']) ?
|
||||
$rel['fieldConf'][$relConf[1]]['type'] : Schema::DT_INT;
|
||||
$field['has-many']['relPK'] = isset($relConf[3])?$relConf[3]:$rel['primary'];
|
||||
$field['has-many']['relPK'] = isset($relConf['relPK'])?
|
||||
$relConf['relPK']:$rel['primary'];
|
||||
$field['has-many']['localKey'] = isset($relConf['localKey'])?
|
||||
$relConf['localKey']:($pkey?:'_id');
|
||||
} else {
|
||||
// has-many <> belongs-to-one (m:1)
|
||||
$field['has-many']['hasRel'] = 'belongs-to-one';
|
||||
@@ -713,7 +725,15 @@ class Cortex extends Cursor {
|
||||
}
|
||||
elseif ($result = $this->_hasRefsInMM($key,$has_filter,$has_options,$ttl))
|
||||
$addToFilter = array($id.' IN ?', $result);
|
||||
} // *-to-one
|
||||
}
|
||||
// *-to-one
|
||||
elseif ($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];
|
||||
}
|
||||
elseif ($result = $this->_hasRefsIn($key,$has_filter,$has_options,$ttl))
|
||||
$addToFilter = array($id.' IN ?', $result);
|
||||
break;
|
||||
@@ -754,7 +774,7 @@ class Cortex extends Cursor {
|
||||
$qtable = $this->db->quotekey($this->table);
|
||||
if (isset($options['order']) && $this->db->driver() == 'pgsql')
|
||||
// PostgreSQLism: sort NULL values to the end of a table
|
||||
$options['order'] = preg_replace('/\h+DESC/i',' DESC NULLS LAST',$options['order']);
|
||||
$options['order'] = preg_replace('/\h+DESC(?=\s*(?:$|,))/i',' DESC NULLS LAST',$options['order']);
|
||||
if ($hasJoin) {
|
||||
// assemble full sql query
|
||||
$adhoc='';
|
||||
@@ -838,7 +858,7 @@ class Cortex extends Cursor {
|
||||
}
|
||||
}
|
||||
if ($options) {
|
||||
$options = $this->queryParser->prepareOptions($options,$this->dbsType);
|
||||
$options = $this->queryParser->prepareOptions($options,$this->dbsType,$this->db);
|
||||
if ($count)
|
||||
unset($options['order']);
|
||||
}
|
||||
@@ -912,6 +932,22 @@ class Cortex extends Cursor {
|
||||
return empty($hasSetByRelId) ? false : $hasSetByRelId;
|
||||
}
|
||||
|
||||
/**
|
||||
* build sub query on relation
|
||||
* @param $key
|
||||
* @param $filter
|
||||
* @param $options
|
||||
* @return mixed
|
||||
*/
|
||||
protected function _refSubQuery($key, $filter, $options,$fields=null) {
|
||||
$type = $this->fieldConf[$key]['relType'];
|
||||
$fieldConf = $this->fieldConf[$key][$type];
|
||||
$rel = $this->getRelFromConf($fieldConf,$key);
|
||||
$filter[0]=$this->queryParser->sql_quoteCondition($filter[0],$this->db);
|
||||
return $rel->mapper->stringify(implode(',',array_map([$this->db,'quotekey'],
|
||||
$fields?:[$rel->primary])),$filter,$options);
|
||||
}
|
||||
|
||||
/**
|
||||
* return IDs of own mappers that match the given relation filter on pivot tables
|
||||
* @param string $key
|
||||
@@ -1164,30 +1200,69 @@ class Cortex extends Cursor {
|
||||
foreach($this->saveCsd as $key => $val) {
|
||||
if($fields[$key]['relType'] == 'has-many') {
|
||||
$relConf = $fields[$key]['has-many'];
|
||||
$mmTable = $this->mmTable($relConf,$key);
|
||||
$rel = $this->getRelInstance(null, array('db'=>$this->db, 'table'=>$mmTable));
|
||||
$id = $this->get($relConf['relPK'],true);
|
||||
$filter = [$relConf['relField'].' = ?',$id];
|
||||
if ($relConf['isSelf']) {
|
||||
$filter[0].= ' OR '.$relConf['relField'].'_ref = ?';
|
||||
$filter[] = $id;
|
||||
if ($relConf['hasRel'] == 'has-many') {
|
||||
$mmTable = $this->mmTable($relConf,$key);
|
||||
$mm = $this->getRelInstance(null, array('db'=>$this->db, 'table'=>$mmTable));
|
||||
$id = $this->get($relConf['localKey'],true);
|
||||
$filter = [$relConf['relField'].' = ?',$id];
|
||||
if ($relConf['isSelf']) {
|
||||
$filter[0].= ' OR '.$relConf['relField'].'_ref = ?';
|
||||
$filter[] = $id;
|
||||
}
|
||||
// delete all refs
|
||||
if (is_null($val))
|
||||
$mm->erase($filter);
|
||||
// update refs
|
||||
elseif (is_array($val)) {
|
||||
$mm->erase($filter);
|
||||
foreach($val as $v) {
|
||||
if ($relConf['isSelf'] && $v==$id)
|
||||
continue;
|
||||
$mm->set($key,$v);
|
||||
$mm->set($relConf['relField'].($relConf['isSelf']?'_ref':''),$id);
|
||||
$mm->save();
|
||||
$mm->reset();
|
||||
}
|
||||
}
|
||||
unset($mm);
|
||||
}
|
||||
// delete all refs
|
||||
if (is_null($val))
|
||||
$rel->erase($filter);
|
||||
// update refs
|
||||
elseif (is_array($val)) {
|
||||
$rel->erase($filter);
|
||||
foreach($val as $v) {
|
||||
if ($relConf['isSelf'] && $v==$id)
|
||||
continue;
|
||||
$rel->set($key,$v);
|
||||
$rel->set($relConf['relField'].($relConf['isSelf']?'_ref':''),$id);
|
||||
$rel->save();
|
||||
$rel->reset();
|
||||
elseif($relConf['hasRel'] == 'belongs-to-one') {
|
||||
$rel = $this->getRelInstance($relConf[0],$relConf,$key);
|
||||
// find existing relations
|
||||
$refs = $rel->find([$relConf[1].' = ?',$this->getRaw($relConf['relField'])]);
|
||||
if (is_null($val)) {
|
||||
foreach ($refs?:[] as $model) {
|
||||
$model->set($relConf[1],NULL);
|
||||
$model->save();
|
||||
}
|
||||
$this->fieldsCache[$key] = NULL;
|
||||
} else {
|
||||
if ($refs) {
|
||||
$ref_ids = $refs->getAll('_id');
|
||||
// unlink removed relations
|
||||
$remove_refs = array_diff($ref_ids,$val);
|
||||
foreach ($refs as $model)
|
||||
if (in_array($model->getRaw($relConf['relField']),$remove_refs)) {
|
||||
$model->set($relConf[1],null);
|
||||
$model->save();
|
||||
}
|
||||
// get new relation keys
|
||||
$val = array_diff($val,$ref_ids);
|
||||
} else
|
||||
$refs = new CortexCollection();
|
||||
if (!empty($val)) {
|
||||
// find models that need to be linked
|
||||
$new_refs = $rel->find([$relConf['relField'].' IN ?',$val]);
|
||||
foreach ($new_refs?:[] as $model) {
|
||||
// set relation to new models
|
||||
$model->set($relConf[1],$this->getRaw($relConf['relField']));
|
||||
$model->save();
|
||||
$refs->add($model);
|
||||
}
|
||||
}
|
||||
$this->fieldsCache[$key] = $refs;
|
||||
}
|
||||
}
|
||||
unset($rel);
|
||||
} elseif($fields[$key]['relType'] == 'has-one') {
|
||||
$val->save();
|
||||
}
|
||||
@@ -1372,7 +1447,9 @@ class Cortex extends Cursor {
|
||||
if (is_null($val))
|
||||
$val = NULL;
|
||||
elseif (is_object($val) &&
|
||||
!($this->dbsType=='mongo' && $val instanceof \MongoId)) {
|
||||
!($this->dbsType=='mongo' && (
|
||||
($this->db->legacy() && $val instanceof \MongoId) ||
|
||||
(!$this->db->legacy() && $val instanceof \MongoDB\BSON\ObjectId)))) {
|
||||
// fetch fkey from mapper
|
||||
if (!$val instanceof Cortex || $val->dry())
|
||||
trigger_error(self::E_INVALID_RELATION_OBJECT,E_USER_ERROR);
|
||||
@@ -1381,8 +1458,9 @@ class Cortex extends Cursor {
|
||||
$rel_field = (is_array($relConf) ? $relConf[1] : '_id');
|
||||
$val = $val->get($rel_field,true);
|
||||
}
|
||||
} elseif ($this->dbsType == 'mongo' && !$val instanceof \MongoId)
|
||||
$val = new \MongoId($val);
|
||||
} elseif ($this->dbsType == 'mongo' && (($this->db->legacy() && !$val instanceof \MongoId)
|
||||
|| (!$this->db->legacy() && !$val instanceof \MongoDB\BSON\ObjectId)))
|
||||
$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)) {
|
||||
@@ -1406,45 +1484,54 @@ class Cortex extends Cursor {
|
||||
$val = $this->getForeignKeysArray($val, $rel_field, $key);
|
||||
}
|
||||
elseif (isset($fields[$key]['has-many'])) {
|
||||
// many-to-many, bidirectional
|
||||
$relConf = $fields[$key]['has-many'];
|
||||
if ($relConf['hasRel'] == 'has-many') {
|
||||
// many-to-many, bidirectional
|
||||
// many-to-one, inverse
|
||||
if ($relConf['hasRel'] == 'has-many'
|
||||
|| $relConf['hasRel'] == 'belongs-to-one') {
|
||||
// custom setter
|
||||
$val = $this->emit('set_'.$key, $val);
|
||||
$val = $this->getForeignKeysArray($val,'_id',$key);
|
||||
if (empty($val) && is_array($val))
|
||||
$val=new CortexCollection();
|
||||
$this->saveCsd[$key] = $val; // array of keys
|
||||
$this->fieldsCache[$key] = $val;
|
||||
return $val;
|
||||
} elseif ($relConf['hasRel'] == 'belongs-to-one') {
|
||||
// TODO: many-to-one, bidirectional, inverse way
|
||||
trigger_error("not implemented",E_USER_ERROR);
|
||||
}
|
||||
}
|
||||
// convert array content
|
||||
if (is_array($val) && $this->dbsType == 'sql')
|
||||
if ($fields[$key]['type'] == self::DT_SERIALIZED)
|
||||
$val = serialize($val);
|
||||
elseif ($fields[$key]['type'] == self::DT_JSON)
|
||||
$val = json_encode($val);
|
||||
else
|
||||
trigger_error(sprintf(self::E_ARRAY_DATATYPE, $key),E_USER_ERROR);
|
||||
// add nullable polyfill
|
||||
if ($val === NULL && ($this->dbsType == 'jig' || $this->dbsType == 'mongo')
|
||||
&& array_key_exists('nullable', $fields[$key]) && $fields[$key]['nullable'] === false)
|
||||
trigger_error(sprintf(self::E_NULLABLE_COLLISION,$key),E_USER_ERROR);
|
||||
// MongoId shorthand
|
||||
if ($this->dbsType == 'mongo' && !$val instanceof \MongoId) {
|
||||
if ($this->dbsType == 'mongo' && (($this->db->legacy() && !$val instanceof \MongoId)
|
||||
|| (!$this->db->legacy() && !$val instanceof \MongoDB\BSON\ObjectId))) {
|
||||
if ($key == '_id')
|
||||
$val = new \MongoId($val);
|
||||
$val = $this->db->legacy() ? new \MongoId($val) : new \MongoDB\BSON\ObjectId($val);
|
||||
elseif (preg_match('/INT/i',$fields[$key]['type'])
|
||||
&& !isset($fields[$key]['relType']))
|
||||
$val = (int) $val;
|
||||
}
|
||||
// cast boolean
|
||||
if (preg_match('/BOOL/i',$fields[$key]['type'])) {
|
||||
$val = !$val || $val==='false' ? false : (bool) $val;
|
||||
if ($this->dbsType == 'sql')
|
||||
$val = (int) $val;
|
||||
}
|
||||
// custom setter
|
||||
$val = $this->emit('set_'.$key, $val);
|
||||
// convert array content
|
||||
if (is_array($val) && $this->dbsType == 'sql') {
|
||||
if ($fields[$key]['type']==self::DT_SERIALIZED)
|
||||
$val=serialize($val);
|
||||
elseif ($fields[$key]['type']==self::DT_JSON)
|
||||
$val=json_encode($val);
|
||||
else
|
||||
trigger_error(sprintf(self::E_ARRAY_DATATYPE,$key),E_USER_ERROR);
|
||||
}
|
||||
} else {
|
||||
// custom setter
|
||||
$val = $this->emit('set_'.$key, $val);
|
||||
}
|
||||
// fluid SQL
|
||||
if ($this->fluid && $this->dbsType == 'sql') {
|
||||
@@ -1474,8 +1561,6 @@ class Cortex extends Cursor {
|
||||
$this->mapper->schema($fields);
|
||||
}
|
||||
}
|
||||
// custom setter
|
||||
$val = $this->emit('set_'.$key, $val);
|
||||
return $this->mapper->set($key, $val);
|
||||
}
|
||||
|
||||
@@ -1555,6 +1640,8 @@ class Cortex extends Cursor {
|
||||
}
|
||||
if ($raw) {
|
||||
$out = $this->exists($key) ? $this->mapper->{$key} : NULL;
|
||||
if ($this->dbsType == 'mongo' && !$this->db->legacy() && $out instanceof \MongoDB\Model\BSONArray)
|
||||
$out = (array) $out;
|
||||
return $out;
|
||||
}
|
||||
if (!empty($fields) && isset($fields[$key]) && is_array($fields[$key])
|
||||
@@ -1692,7 +1779,7 @@ class Cortex extends Cursor {
|
||||
} // no collection
|
||||
else {
|
||||
// find foreign keys
|
||||
$fId=$this->get($fromConf['relPK'],true);
|
||||
$fId=$this->get($fromConf['localKey'],true);
|
||||
$filter = [$fromConf['relField'].' = ?',$fId];
|
||||
if ($fromConf['isSelf']) {
|
||||
$filter = [$fromConf['relField'].' = ?',$fId];
|
||||
@@ -1713,7 +1800,7 @@ class Cortex extends Cursor {
|
||||
unset($rel);
|
||||
$rel = $this->getRelInstance($fromConf[0],null,$key,true);
|
||||
// load foreign models
|
||||
$filter = array($toConf['relPK'].' IN ?', $fkeys);
|
||||
$filter = array($fromConf['relPK'].' IN ?', $fkeys);
|
||||
$filter = $this->mergeWithRelFilter($key, $filter);
|
||||
$this->fieldsCache[$key] = $rel->find($filter,
|
||||
$this->getRelFilterOption($key),$this->_ttl);
|
||||
@@ -1724,7 +1811,7 @@ class Cortex extends Cursor {
|
||||
elseif (isset($fields[$key]['belongs-to-many'])) {
|
||||
// many-to-many, unidirectional
|
||||
$fields[$key]['type'] = self::DT_JSON;
|
||||
$result = !$this->exists($key) ? null :$this->mapper->get($key);
|
||||
$result = $this->getRaw($key);
|
||||
if ($this->dbsType == 'sql')
|
||||
$result = json_decode($result, true);
|
||||
if (!is_array($result))
|
||||
@@ -1786,7 +1873,8 @@ class Cortex extends Cursor {
|
||||
// fetch cached value, if existing
|
||||
$val = array_key_exists($key,$this->fieldsCache) ? $this->fieldsCache[$key]
|
||||
: (($this->exists($key)) ? $this->mapper->{$key} : null);
|
||||
if ($this->dbsType == 'mongo' && $val instanceof \MongoId) {
|
||||
if ($this->dbsType == 'mongo' && (($this->db->legacy() && $val instanceof \MongoId) ||
|
||||
(!$this->db->legacy() && $val instanceof \MongoDB\BSON\ObjectId))) {
|
||||
// conversion to string makes further processing in template, etc. much easier
|
||||
$val = (string) $val;
|
||||
}
|
||||
@@ -1836,13 +1924,14 @@ class Cortex extends Cursor {
|
||||
$isMongo = ($this->dbsType == 'mongo');
|
||||
foreach ($val as &$item) {
|
||||
if (is_object($item) &&
|
||||
!($isMongo && $item instanceof \MongoId)) {
|
||||
!($isMongo && (($this->db->legacy() && $item instanceof \MongoId) ||
|
||||
(!$this->db->legacy() && $item instanceof \MongoDB\BSON\ObjectId)))) {
|
||||
if (!$item instanceof Cortex || $item->dry())
|
||||
trigger_error(self::E_INVALID_RELATION_OBJECT,E_USER_ERROR);
|
||||
else $item = $item->get($rel_field,true);
|
||||
}
|
||||
if ($isMongo && $rel_field == '_id' && is_string($item))
|
||||
$item = new \MongoId($item);
|
||||
$item = $this->db->legacy() ? new \MongoId($item) : new \MongoDB\BSON\ObjectId($item);
|
||||
if (is_numeric($item))
|
||||
$item = (int) $item;
|
||||
unset($item);
|
||||
@@ -1896,16 +1985,16 @@ class Cortex extends Cursor {
|
||||
|
||||
/**
|
||||
* get relation model from config
|
||||
* @param $fieldConf
|
||||
* @param $relConf
|
||||
* @param $key
|
||||
* @return Cortex
|
||||
*/
|
||||
protected function getRelFromConf(&$fieldConf, $key) {
|
||||
if (!is_array($fieldConf))
|
||||
$fieldConf = array($fieldConf, '_id');
|
||||
$rel = $this->getRelInstance($fieldConf[0],null,$key,true);
|
||||
if($this->dbsType=='sql' && $fieldConf[1] == '_id')
|
||||
$fieldConf[1] = $rel->primary;
|
||||
protected function getRelFromConf(&$relConf, $key) {
|
||||
if (!is_array($relConf))
|
||||
$relConf = array($relConf, '_id');
|
||||
$rel = $this->getRelInstance($relConf[0],null,$key,true);
|
||||
if($this->dbsType=='sql' && $relConf[1] == '_id')
|
||||
$relConf[1] = $rel->primary;
|
||||
return $rel;
|
||||
}
|
||||
|
||||
@@ -2097,10 +2186,11 @@ class Cortex extends Cursor {
|
||||
public function reset($mapper = true) {
|
||||
if ($mapper)
|
||||
$this->mapper->reset();
|
||||
$this->fieldsCache=array();
|
||||
$this->saveCsd=array();
|
||||
$this->countFields=array();
|
||||
$this->preBinds=array();
|
||||
$this->fieldsCache=[];
|
||||
$this->saveCsd=[];
|
||||
$this->countFields=[];
|
||||
$this->preBinds=[];
|
||||
$this->vFields=[];
|
||||
$this->grp_stack=null;
|
||||
// set default values
|
||||
if (($this->dbsType == 'jig' || $this->dbsType == 'mongo')
|
||||
@@ -2132,6 +2222,8 @@ class Cortex extends Cursor {
|
||||
* @return mixed
|
||||
*/
|
||||
public function changed($key=null) {
|
||||
if ($key=='_id')
|
||||
$key = $this->primary;
|
||||
if (method_exists($this->mapper,'changed'))
|
||||
return $this->mapper->changed($key);
|
||||
else
|
||||
@@ -2245,7 +2337,7 @@ class CortexQueryParser extends \Prefab {
|
||||
if (is_int(strpos($where, ':')))
|
||||
list($parts, $args) = $this->convertNamedParams($parts, $args);
|
||||
foreach ($parts as &$part) {
|
||||
$part = $this->_mongo_parse_relational_op($part, $args, $fieldConf);
|
||||
$part = $this->_mongo_parse_relational_op($part, $args, $db, $fieldConf);
|
||||
unset($part);
|
||||
}
|
||||
$ncond = $this->_mongo_parse_logical_op($parts);
|
||||
@@ -2341,18 +2433,19 @@ class CortexQueryParser extends \Prefab {
|
||||
// https://www.debuggex.com/r/6AXwJ1Y3Aac8aocQ/3
|
||||
// https://regex101.com/r/yM5vK4/1
|
||||
// this took me lots of sleepless nights
|
||||
return preg_replace_callback('/'.
|
||||
'(\w+\((?:[^)(]+|(?R))*\))|'. // exclude SQL function names "foo("
|
||||
$out = preg_replace_callback('/'.
|
||||
'\w+\((?:(?>[^()]+)|\((?:(?>[^()]+)|^(?R))*\))*\)|'. // exclude SQL function names "foo("
|
||||
'(?:(\b(?<!:)'. // exclude bind parameter ":foo"
|
||||
'[a-zA-Z_](?:[\w\-_.]+\.?))'. // match only identifier, exclude values
|
||||
'(?=[\s<>=!)]|$))/i', // only when part of condition or within brackets
|
||||
function($match) use($db) {
|
||||
if (!isset($match[2]))
|
||||
if (!isset($match[1]))
|
||||
return $match[0];
|
||||
if (preg_match('/\b(AND|OR|IN|LIKE|NOT)\b/i',$match[1]))
|
||||
return $match[1];
|
||||
if (preg_match('/\b(AND|OR|IN|LIKE|NOT)\b/i',$match[2]))
|
||||
return $match[2];
|
||||
return $db->quotekey($match[2]);
|
||||
return $db->quotekey($match[1]);
|
||||
}, $cond);
|
||||
return $out ?: $cond;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2362,8 +2455,8 @@ class CortexQueryParser extends \Prefab {
|
||||
* @return string
|
||||
*/
|
||||
public function sql_prependTableToFields($cond, $table) {
|
||||
return preg_replace_callback('/'.
|
||||
'(\w+\((?:[^)(]+|(?R))*\))|'.
|
||||
$out = preg_replace_callback('/'.
|
||||
'(\w+\((?:[^)(]+|\((?:[^)(]+|(?R))*\))*\))|'.
|
||||
'(?:(\s)|^|(?<=[(]))'.
|
||||
'([a-zA-Z_](?:[\w\-_]+))'.
|
||||
'(?=[\s<>=!)]|$)/i',
|
||||
@@ -2374,6 +2467,7 @@ class CortexQueryParser extends \Prefab {
|
||||
return $match[0];
|
||||
return $match[2].$table.'.'.$match[3];
|
||||
}, $cond);
|
||||
return $out ?: $cond;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2391,7 +2485,7 @@ class CortexQueryParser extends \Prefab {
|
||||
if (preg_match('/\s*\b(AND|OR)\b\s*/i',$part))
|
||||
continue;
|
||||
// prefix field names
|
||||
$part = preg_replace('/([a-z_-]+)/i', '@$1', $part, -1, $count);
|
||||
$part = preg_replace('/([a-z_-]+(?:[\w-]+))/i', '@$1', $part, -1, $count);
|
||||
// value comparison
|
||||
if (is_int(strpos($part, '?'))) {
|
||||
$val = array_shift($args);
|
||||
@@ -2401,7 +2495,7 @@ class CortexQueryParser extends \Prefab {
|
||||
if (is_int(strpos($upart = strtoupper($part), ' @LIKE '))) {
|
||||
if ($not = is_int($npos = strpos($upart, '@NOT')))
|
||||
$pos = $npos;
|
||||
$val = $this->_likeValueToRegEx($val);
|
||||
$val = '/'.$this->_likeValueToRegEx($val).'/iu';
|
||||
$part = ($not ? '!' : '').'preg_match(?,'.$match[0].')';
|
||||
} // find IN operator
|
||||
elseif (is_int($pos = strpos($upart, ' @IN '))) {
|
||||
@@ -2495,10 +2589,11 @@ class CortexQueryParser extends \Prefab {
|
||||
* find and convert relational operators
|
||||
* @param $part
|
||||
* @param $args
|
||||
* @param \DB\Mongo $db
|
||||
* @param null $fieldConf
|
||||
* @return array|null
|
||||
*/
|
||||
protected function _mongo_parse_relational_op($part, &$args, $fieldConf=null) {
|
||||
protected function _mongo_parse_relational_op($part, &$args, \DB\Mongo $db, $fieldConf=null) {
|
||||
if (is_null($part))
|
||||
return $part;
|
||||
if (preg_match('/\<\=|\>\=|\<\>|\<|\>|\!\=|\=\=|\=|like|not like|in|not in/i', $part, $match)) {
|
||||
@@ -2516,17 +2611,21 @@ class CortexQueryParser extends \Prefab {
|
||||
if ($key == '_id' || (isset($fieldConf[$key]) && isset($fieldConf[$key]['relType']))) {
|
||||
if (is_array($var))
|
||||
foreach ($var as &$id) {
|
||||
if (!$id instanceof \MongoId)
|
||||
if ($db->legacy() && !$id instanceof \MongoId)
|
||||
$id = new \MongoId($id);
|
||||
elseif (!$db->legacy() && !$id instanceof \MongoDB\BSON\ObjectId)
|
||||
$id = new \MongoDB\BSON\ObjectId($id);
|
||||
unset($id);
|
||||
}
|
||||
elseif(!$var instanceof \MongoId)
|
||||
elseif($db->legacy() && !$var instanceof \MongoId)
|
||||
$var = new \MongoId($var);
|
||||
elseif(!$db->legacy() && !$var instanceof \MongoDB\BSON\ObjectId)
|
||||
$var = new \MongoDB\BSON\ObjectId($var);
|
||||
}
|
||||
// find LIKE operator
|
||||
if (in_array($upart, array('LIKE','NOT LIKE'))) {
|
||||
$rgx = $this->_likeValueToRegEx($var);
|
||||
$var = new \MongoRegex($rgx);
|
||||
$var = $db->legacy() ? new \MongoRegex('/'.$rgx.'/iu') : new \MongoDB\BSON\Regex($rgx,'iu');
|
||||
if ($upart == 'NOT LIKE')
|
||||
$var = array('$not' => $var);
|
||||
} // find IN operator
|
||||
@@ -2561,7 +2660,7 @@ class CortexQueryParser extends \Prefab {
|
||||
// %var -> /var$/
|
||||
elseif ($var[0] == '%')
|
||||
$var = substr($var, 1).'$';
|
||||
return '/'.$var.'/iu';
|
||||
return $var;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2573,16 +2672,18 @@ class CortexQueryParser extends \Prefab {
|
||||
*
|
||||
* @param array $options
|
||||
* @param string $engine
|
||||
* @param object $db
|
||||
* @return array|null
|
||||
*/
|
||||
public function prepareOptions($options, $engine) {
|
||||
public function prepareOptions($options, $engine, $db) {
|
||||
if (empty($options) || !is_array($options))
|
||||
return null;
|
||||
switch ($engine) {
|
||||
case 'jig':
|
||||
if (array_key_exists('order', $options))
|
||||
$options['order'] = str_replace(array('asc', 'desc'),
|
||||
array('SORT_ASC', 'SORT_DESC'), strtolower($options['order']));
|
||||
$options['order'] = preg_replace(
|
||||
['/(?<=\h)(ASC)(?=\W|$)/i','/(?<=\h)(DESC)(?=\W|$)/i'],
|
||||
['SORT_ASC','SORT_DESC'],$options['order']);
|
||||
break;
|
||||
case 'mongo':
|
||||
if (array_key_exists('order', $options)) {
|
||||
@@ -2598,12 +2699,27 @@ class CortexQueryParser extends \Prefab {
|
||||
if (array_key_exists('group', $options) && is_string($options['group'])) {
|
||||
$keys = explode(',',$options['group']);
|
||||
$options['group']=array('keys'=>array(),'initial'=>array(),
|
||||
'reduce'=>'function (obj, prev) {}','finalize'=>'');
|
||||
'reduce'=>'function (obj, prev) {}','finalize'=>'');
|
||||
$keys = array_combine($keys,array_fill(0,count($keys),1));
|
||||
$options['group']['keys']=$keys;
|
||||
$options['group']['initial']=$keys;
|
||||
}
|
||||
break;
|
||||
case 'sql':
|
||||
$char=substr($db->quotekey(''),0,1);
|
||||
if (array_key_exists('order', $options) &&
|
||||
FALSE===strpos($options['order'],$char))
|
||||
$options['order']=preg_replace_callback(
|
||||
'/(\w+\h?\(|'. // skip function names
|
||||
'\b(?!\w+)(?:\s+\w+)+|' . // skip field args
|
||||
'\)\s+\w+)|'. // skip function args
|
||||
'(\b\d?[a-zA-Z_](?:[\w\-.])*)/i', // match table/field keys
|
||||
function($match) use($db) {
|
||||
if (!isset($match[2]))
|
||||
return $match[1];
|
||||
return $db->quotekey($match[2]);
|
||||
}, $options['order']);
|
||||
break;
|
||||
}
|
||||
return $options;
|
||||
}
|
||||
@@ -2702,7 +2818,7 @@ class CortexCollection extends \ArrayIterator {
|
||||
if (!$this->hasRelSet($prop) || !($relSet = $this->getRelSet($prop)))
|
||||
return null;
|
||||
foreach ($keys as &$key) {
|
||||
if ($key instanceof \MongoId)
|
||||
if ($key instanceof \MongoId || $key instanceof \MongoDB\BSON\ObjectId)
|
||||
$key = (string) $key;
|
||||
unset($key);
|
||||
}
|
||||
|
||||
@@ -41,7 +41,9 @@ class Jig {
|
||||
//! Jig log
|
||||
$log,
|
||||
//! Memory-held data
|
||||
$data;
|
||||
$data,
|
||||
//! lazy load/save files
|
||||
$lazy;
|
||||
|
||||
/**
|
||||
* Read data from memory/file
|
||||
@@ -54,6 +56,8 @@ class Jig {
|
||||
$this->data[$file]=[];
|
||||
return $this->data[$file];
|
||||
}
|
||||
if ($this->lazy && isset($this->data[$file]))
|
||||
return $this->data[$file];
|
||||
$fw=\Base::instance();
|
||||
$raw=$fw->read($dst);
|
||||
switch ($this->format) {
|
||||
@@ -75,7 +79,7 @@ class Jig {
|
||||
* @param $data array
|
||||
**/
|
||||
function write($file,array $data=NULL) {
|
||||
if (!$this->dir)
|
||||
if (!$this->dir || $this->lazy)
|
||||
return count($this->data[$file]=$data);
|
||||
$fw=\Base::instance();
|
||||
switch ($this->format) {
|
||||
@@ -131,6 +135,8 @@ class Jig {
|
||||
* @return NULL
|
||||
**/
|
||||
function drop() {
|
||||
if ($this->lazy) // intentional
|
||||
$this->data=[];
|
||||
if (!$this->dir)
|
||||
$this->data=[];
|
||||
elseif ($glob=@glob($this->dir.'/*',GLOB_NOSORT))
|
||||
@@ -147,11 +153,23 @@ class Jig {
|
||||
* @param $dir string
|
||||
* @param $format int
|
||||
**/
|
||||
function __construct($dir=NULL,$format=self::FORMAT_JSON) {
|
||||
function __construct($dir=NULL,$format=self::FORMAT_JSON,$lazy=FALSE) {
|
||||
if ($dir && !is_dir($dir))
|
||||
mkdir($dir,\Base::MODE,TRUE);
|
||||
$this->uuid=\Base::instance()->hash($this->dir=$dir);
|
||||
$this->format=$format;
|
||||
$this->lazy=$lazy;
|
||||
}
|
||||
|
||||
/**
|
||||
* save file on destruction
|
||||
**/
|
||||
function __destruct() {
|
||||
if ($this->lazy) {
|
||||
$this->lazy = FALSE;
|
||||
foreach ($this->data as $file => $data)
|
||||
$this->write($file,$data);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -33,7 +33,9 @@ class Mapper extends \DB\Cursor {
|
||||
//! Document identifier
|
||||
$id,
|
||||
//! Document contents
|
||||
$document=[];
|
||||
$document=[],
|
||||
//! field map-reduce handlers
|
||||
$_reduce;
|
||||
|
||||
/**
|
||||
* Return database type
|
||||
@@ -160,7 +162,8 @@ class Mapper extends \DB\Cursor {
|
||||
$options+=[
|
||||
'order'=>NULL,
|
||||
'limit'=>0,
|
||||
'offset'=>0
|
||||
'offset'=>0,
|
||||
'group'=>NULL,
|
||||
];
|
||||
$fw=\Base::instance();
|
||||
$cache=\Cache::instance();
|
||||
@@ -232,30 +235,46 @@ class Mapper extends \DB\Cursor {
|
||||
}
|
||||
);
|
||||
}
|
||||
if (isset($options['order'])) {
|
||||
$cols=$fw->split($options['order']);
|
||||
uasort(
|
||||
$data,
|
||||
function($val1,$val2) use($cols) {
|
||||
foreach ($cols as $col) {
|
||||
$parts=explode(' ',$col,2);
|
||||
$order=empty($parts[1])?
|
||||
SORT_ASC:
|
||||
constant($parts[1]);
|
||||
$col=$parts[0];
|
||||
if (!array_key_exists($col,$val1))
|
||||
$val1[$col]=NULL;
|
||||
if (!array_key_exists($col,$val2))
|
||||
$val2[$col]=NULL;
|
||||
list($v1,$v2)=[$val1[$col],$val2[$col]];
|
||||
if ($out=strnatcmp($v1,$v2)*
|
||||
(($order==SORT_ASC)*2-1))
|
||||
return $out;
|
||||
}
|
||||
return 0;
|
||||
if (isset($options['group'])) {
|
||||
$cols=array_reverse($fw->split($options['group']));
|
||||
// sort into groups
|
||||
$data=$this->sort($data,$options['group']);
|
||||
foreach($data as $i=>&$row) {
|
||||
if (!isset($prev)) {
|
||||
$prev=$row;
|
||||
$prev_i=$i;
|
||||
}
|
||||
$drop=false;
|
||||
foreach ($cols as $col)
|
||||
if ($prev_i!=$i && array_key_exists($col,$row) &&
|
||||
array_key_exists($col,$prev) && $row[$col]==$prev[$col])
|
||||
// reduce/modify
|
||||
$drop=!isset($this->_reduce[$col]) || call_user_func_array(
|
||||
$this->_reduce[$col][0],[&$prev,&$row])!==FALSE;
|
||||
elseif (isset($this->_reduce[$col])) {
|
||||
$null=null;
|
||||
// initial
|
||||
call_user_func_array($this->_reduce[$col][0],[&$row,&$null]);
|
||||
}
|
||||
if ($drop)
|
||||
unset($data[$i]);
|
||||
else {
|
||||
$prev=&$row;
|
||||
$prev_i=$i;
|
||||
}
|
||||
unset($row);
|
||||
}
|
||||
// finalize
|
||||
if ($this->_reduce[$col][1])
|
||||
foreach($data as $i=>&$row) {
|
||||
$row=call_user_func($this->_reduce[$col][1],$row);
|
||||
if (!$row)
|
||||
unset($data[$i]);
|
||||
unset($row);
|
||||
}
|
||||
);
|
||||
}
|
||||
if (isset($options['order']))
|
||||
$data=$this->sort($data,$options['order']);
|
||||
$data=array_slice($data,
|
||||
$options['offset'],$options['limit']?:NULL,TRUE);
|
||||
if ($fw->CACHE && $ttl)
|
||||
@@ -281,6 +300,48 @@ class Mapper extends \DB\Cursor {
|
||||
return $out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort a collection
|
||||
* @param $data
|
||||
* @param $cond
|
||||
* @return mixed
|
||||
*/
|
||||
protected function sort($data,$cond) {
|
||||
$cols=\Base::instance()->split($cond);
|
||||
uasort(
|
||||
$data,
|
||||
function($val1,$val2) use($cols) {
|
||||
foreach ($cols as $col) {
|
||||
$parts=explode(' ',$col,2);
|
||||
$order=empty($parts[1])?
|
||||
SORT_ASC:
|
||||
constant($parts[1]);
|
||||
$col=$parts[0];
|
||||
if (!array_key_exists($col,$val1))
|
||||
$val1[$col]=NULL;
|
||||
if (!array_key_exists($col,$val2))
|
||||
$val2[$col]=NULL;
|
||||
list($v1,$v2)=[$val1[$col],$val2[$col]];
|
||||
if ($out=strnatcmp($v1,$v2)*
|
||||
(($order==SORT_ASC)*2-1))
|
||||
return $out;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
);
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add reduce handler for grouped fields
|
||||
* @param $key string
|
||||
* @param $handler callback
|
||||
* @param $finalize callback
|
||||
*/
|
||||
function reduce($key,$handler,$finalize=null){
|
||||
$this->_reduce[$key]=[$handler,$finalize];
|
||||
}
|
||||
|
||||
/**
|
||||
* Count records that match criteria
|
||||
* @return int
|
||||
|
||||
@@ -68,8 +68,9 @@ class Mongo {
|
||||
$cursor=$this->db->selectcollection('system.profile')->find();
|
||||
foreach (iterator_to_array($cursor) as $frame)
|
||||
if (!preg_match('/\.system\..+$/',$frame['ns']))
|
||||
$this->log.=date('r',$frame['ts']->sec).' ('.
|
||||
sprintf('%.1f',$frame['millis']).'ms) '.
|
||||
$this->log.=date('r',$this->legacy() ?
|
||||
$frame['ts']->sec : (round((string)$frame['ts'])/1000)).
|
||||
' ('.sprintf('%.1f',$frame['millis']).'ms) '.
|
||||
$frame['ns'].' ['.$frame['op'].'] '.
|
||||
(empty($frame['query'])?
|
||||
'':json_encode($frame['query'])).
|
||||
|
||||
@@ -153,7 +153,7 @@ class Mapper extends \DB\Cursor {
|
||||
|
||||
/**
|
||||
* Convert array to mapper object
|
||||
* @return object
|
||||
* @return static
|
||||
* @param $row array
|
||||
**/
|
||||
protected function factory($row) {
|
||||
@@ -236,14 +236,15 @@ class Mapper extends \DB\Cursor {
|
||||
explode(',',$options['group'])));
|
||||
}
|
||||
if ($options['order']) {
|
||||
$order=' ORDER BY '.implode(',',array_map(
|
||||
function($str) use($db) {
|
||||
$char=substr($db->quotekey(''),0,1);// quoting char
|
||||
$order=' ORDER BY '.(FALSE===strpos($options['order'],$char)?
|
||||
implode(',',array_map(function($str) use($db) {
|
||||
return preg_match('/^\h*(\w+[._\-\w]*)(?:\h+((?:ASC|DESC)[\w\h]*))?\h*$/i',
|
||||
$str,$parts)?
|
||||
($db->quotekey($parts[1]).
|
||||
(isset($parts[2])?(' '.$parts[2]):'')):$str;
|
||||
},
|
||||
explode(',',$options['order'])));
|
||||
},explode(',',$options['order']))):
|
||||
$options['order']);
|
||||
}
|
||||
// SQL Server fixes
|
||||
if (preg_match('/mssql|sqlsrv|odbc/', $this->engine) &&
|
||||
@@ -285,7 +286,7 @@ class Mapper extends \DB\Cursor {
|
||||
|
||||
/**
|
||||
* Build query string and execute
|
||||
* @return object
|
||||
* @return static[]
|
||||
* @param $fields string
|
||||
* @param $filter string|array
|
||||
* @param $options array
|
||||
@@ -360,7 +361,7 @@ class Mapper extends \DB\Cursor {
|
||||
/**
|
||||
* Return record at specified offset using same criteria as
|
||||
* previous load() call and make it active
|
||||
* @return array
|
||||
* @return static
|
||||
* @param $ofs int
|
||||
**/
|
||||
function skip($ofs=1) {
|
||||
@@ -385,7 +386,7 @@ class Mapper extends \DB\Cursor {
|
||||
|
||||
/**
|
||||
* Insert new record
|
||||
* @return object
|
||||
* @return static
|
||||
**/
|
||||
function insert() {
|
||||
$args=[];
|
||||
@@ -424,8 +425,8 @@ class Mapper extends \DB\Cursor {
|
||||
}
|
||||
}
|
||||
if ($fields) {
|
||||
$add='';
|
||||
if ($this->engine=='pgsql') {
|
||||
$add=$aik='';
|
||||
if ($this->engine=='pgsql' && !empty($pkeys)) {
|
||||
$names=array_keys($pkeys);
|
||||
$aik=end($names);
|
||||
$add=' RETURNING '.$this->db->quotekey($aik);
|
||||
@@ -437,12 +438,12 @@ class Mapper extends \DB\Cursor {
|
||||
'INSERT INTO '.$this->table.' ('.$fields.') '.
|
||||
'VALUES ('.$values.')'.$add,$args
|
||||
);
|
||||
if ($this->engine=='pgsql' && $lID)
|
||||
if ($this->engine=='pgsql' && $lID && $aik)
|
||||
$this->_id=$lID[0][$aik];
|
||||
elseif ($this->engine!='oci')
|
||||
$this->_id=$this->db->lastinsertid();
|
||||
// Reload to obtain default and auto-increment field values
|
||||
if ($reload=($inc || $filter))
|
||||
if ($reload=(($inc && $this->_id) || $filter))
|
||||
$this->load($inc?
|
||||
[$inc.'=?',$this->db->value(
|
||||
$this->fields[$inc]['pdo_type'],$this->_id)]:
|
||||
@@ -463,7 +464,7 @@ class Mapper extends \DB\Cursor {
|
||||
|
||||
/**
|
||||
* Update current record
|
||||
* @return object
|
||||
* @return static
|
||||
**/
|
||||
function update() {
|
||||
$args=[];
|
||||
@@ -652,7 +653,7 @@ class Mapper extends \DB\Cursor {
|
||||
|
||||
/**
|
||||
* Instantiate class
|
||||
* @param $db object
|
||||
* @param $db \DB\SQL
|
||||
* @param $table string
|
||||
* @param $fields array|string
|
||||
* @param $ttl int|array
|
||||
|
||||
@@ -306,6 +306,7 @@ class Markdown extends Prefab {
|
||||
},
|
||||
$str
|
||||
);
|
||||
$str=preg_replace('/\s{2}\r?\n/','<br />',$str);
|
||||
return '<p>'.$this->scan($str).'</p>'."\n\n";
|
||||
}
|
||||
return '';
|
||||
|
||||
@@ -273,7 +273,7 @@ class Template extends Preview {
|
||||
// Build tree structure
|
||||
for ($ptr=0,$w=5,$len=strlen($text),$tree=[],$tmp='';$ptr<$len;)
|
||||
if (preg_match('/^(.{0,'.$w.'}?)<(\/?)(?:F3:)?'.
|
||||
'('.$this->tags.')\b((?:\s+[\w-]+'.
|
||||
'('.$this->tags.')\b((?:\s+[\w-.:@!]+'.
|
||||
'(?:\h*=\h*(?:"(?:.*?)"|\'(?:.*?)\'))?|'.
|
||||
'\h*\{\{.+?\}\})*)\h*(\/?)>/is',
|
||||
substr($text,$ptr),$match)) {
|
||||
@@ -303,19 +303,18 @@ class Template extends Preview {
|
||||
if ($match[4]) {
|
||||
// Process attributes
|
||||
preg_match_all(
|
||||
'/(?:\b([\w-]+)\h*'.
|
||||
'(?:=\h*(?:"(.*?)"|\'(.*?)\'))?|'.
|
||||
'(\{\{.+?\}\}))/s',
|
||||
'/(?:(\{\{.+?\}\})|([^\s\/"\'=]+))'.
|
||||
'\h*(?:=\h*(?:"(.*?)"|\'(.*?)\'))?/s',
|
||||
$match[4],$attr,PREG_SET_ORDER);
|
||||
foreach ($attr as $kv)
|
||||
if (isset($kv[4]))
|
||||
$node['@attrib'][]=$kv[4];
|
||||
if (!empty($kv[1]) && !isset($kv[3]) && !isset($kv[4]))
|
||||
$node['@attrib'][]=$kv[1];
|
||||
else
|
||||
$node['@attrib'][$kv[1]]=
|
||||
(isset($kv[2]) && $kv[2]!==''?
|
||||
$kv[2]:
|
||||
(isset($kv[3]) && $kv[3]!==''?
|
||||
$kv[3]:NULL));
|
||||
$node['@attrib'][$kv[1]?:$kv[2]]=
|
||||
(isset($kv[3]) && $kv[3]!==''?
|
||||
$kv[3]:
|
||||
(isset($kv[4]) && $kv[4]!==''?
|
||||
$kv[4]:NULL));
|
||||
}
|
||||
}
|
||||
$tmp='';
|
||||
@@ -342,12 +341,13 @@ class Template extends Preview {
|
||||
* return object
|
||||
**/
|
||||
function __construct() {
|
||||
$ref=new ReflectionClass(__CLASS__);
|
||||
$ref=new ReflectionClass(get_called_class());
|
||||
$this->tags='';
|
||||
foreach ($ref->getmethods() as $method)
|
||||
if (preg_match('/^_(?=[[:alpha:]])/',$method->name))
|
||||
$this->tags.=(strlen($this->tags)?'|':'').
|
||||
substr($method->name,1);
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ class Web extends Prefab {
|
||||
'hqx'=>'application/mac-binhex40',
|
||||
'html?'=>'text/html',
|
||||
'jar'=>'application/java-archive',
|
||||
'jpe?g'=>'image/jpeg',
|
||||
'jpe?g|jfif?'=>'image/jpeg',
|
||||
'js'=>'application/x-javascript',
|
||||
'midi'=>'audio/x-midi',
|
||||
'mp3'=>'audio/mpeg',
|
||||
@@ -281,6 +281,8 @@ class Web extends Prefab {
|
||||
curl_setopt($curl,CURLOPT_HTTPHEADER,$options['header']);
|
||||
if (isset($options['content']))
|
||||
curl_setopt($curl,CURLOPT_POSTFIELDS,$options['content']);
|
||||
if (isset($options['proxy']))
|
||||
curl_setopt($curl,CURLOPT_PROXY,$options['proxy']);
|
||||
curl_setopt($curl,CURLOPT_ENCODING,'gzip,deflate');
|
||||
$timeout=isset($options['timeout'])?
|
||||
$options['timeout']:
|
||||
@@ -333,6 +335,12 @@ class Web extends Prefab {
|
||||
**/
|
||||
protected function _stream($url,$options) {
|
||||
$eol="\r\n";
|
||||
if (isset($options['proxy'])) {
|
||||
$options['proxy']=preg_replace('/https?/i','tcp',$options['proxy']);
|
||||
$options['request_fulluri']=true;
|
||||
if (preg_match('/socks4?/i',$options['proxy']))
|
||||
return $this->_socket($url,$options);
|
||||
}
|
||||
$options['header']=implode($eol,$options['header']);
|
||||
$body=@file_get_contents($url,FALSE,
|
||||
stream_context_create(['http'=>$options]));
|
||||
@@ -378,25 +386,46 @@ class Web extends Prefab {
|
||||
$headers=[];
|
||||
$body='';
|
||||
$parts=parse_url($url);
|
||||
$empty=empty($parts['port']);
|
||||
if ($parts['scheme']=='https') {
|
||||
$hostname=$parts['host'];
|
||||
$proxy=false;
|
||||
if ($parts['scheme']=='https')
|
||||
$parts['host']='ssl://'.$parts['host'];
|
||||
if ($empty)
|
||||
$parts['port']=443;
|
||||
}
|
||||
elseif ($empty)
|
||||
$parts['port']=80;
|
||||
if (empty($parts['port']))
|
||||
$parts['port']=$parts['scheme']=='https'?443:80;
|
||||
if (empty($parts['path']))
|
||||
$parts['path']='/';
|
||||
if (empty($parts['query']))
|
||||
$parts['query']='';
|
||||
if ($socket=@fsockopen($parts['host'],$parts['port'],$code,$err)) {
|
||||
if (isset($options['proxy'])) {
|
||||
$req=$url;
|
||||
$pp=parse_url($options['proxy']);
|
||||
$proxy=$pp['scheme'];
|
||||
if ($pp['scheme']=='https')
|
||||
$pp['host']='ssl://'.$pp['host'];
|
||||
if (empty($pp['port']))
|
||||
$pp['port']=$pp['scheme']=='https'?443:80;
|
||||
$socket=@fsockopen($pp['host'],$pp['port'],$code,$err);
|
||||
} else {
|
||||
$req=$parts['path'].($parts['query']?('?'.$parts['query']):'');
|
||||
$socket=@fsockopen($parts['host'],$parts['port'],$code,$err);
|
||||
}
|
||||
if ($socket) {
|
||||
stream_set_blocking($socket,TRUE);
|
||||
stream_set_timeout($socket,isset($options['timeout'])?
|
||||
$options['timeout']:ini_get('default_socket_timeout'));
|
||||
fputs($socket,$options['method'].' '.$parts['path'].
|
||||
($parts['query']?('?'.$parts['query']):'').' HTTP/1.0'.$eol
|
||||
);
|
||||
if ($proxy=='socks4') {
|
||||
// SOCKS4; http://en.wikipedia.org/wiki/SOCKS#Protocol
|
||||
$packet="\x04\x01".pack("n", $parts['port']).
|
||||
pack("H*",dechex(ip2long(gethostbyname($hostname))))."\0";
|
||||
fputs($socket, $packet, strlen($packet));
|
||||
$response=fread($socket, 9);
|
||||
if (strlen($response)==8 && (ord($response[0])==0 || ord($response[0])==4)
|
||||
&& ord($response[1])==90) {
|
||||
$options['header'][]='Host: '.$hostname;
|
||||
} else
|
||||
$err='Socket Status '.ord($response[1]);
|
||||
}
|
||||
fputs($socket,$options['method'].' '.$req.' HTTP/1.0'.$eol);
|
||||
fputs($socket,implode($eol,$options['header']).$eol.$eol);
|
||||
if (isset($options['content']))
|
||||
fputs($socket,$options['content'].$eol);
|
||||
@@ -508,12 +537,6 @@ class Web extends Prefab {
|
||||
$this->engine();
|
||||
if ($this->wrapper!='stream') {
|
||||
// PHP streams can't cope with redirects when Host header is set
|
||||
foreach ($options['header'] as &$header)
|
||||
if (preg_match('/^Host:/',$header)) {
|
||||
$header='Host: '.$parts['host'];
|
||||
unset($header);
|
||||
break;
|
||||
}
|
||||
$this->subst($options['header'],'Host: '.$parts['host']);
|
||||
}
|
||||
$this->subst($options['header'],
|
||||
|
||||
@@ -64,8 +64,8 @@ class Geo extends \Prefab {
|
||||
$out=@geoip_record_by_name($ip)) {
|
||||
$out['request']=$ip;
|
||||
$out['region_code']=$out['region'];
|
||||
$out['region_name']=geoip_region_name_by_code(
|
||||
$out['country_code'],$out['region']);
|
||||
$out['region_name']=(!empty($out['country_code']) && !empty($out['region']))
|
||||
? geoip_region_name_by_code($out['country_code'],$out['region']) : '';
|
||||
unset($out['country_code3'],$out['region'],$out['postal_code']);
|
||||
return $out;
|
||||
}
|
||||
|
||||
@@ -6,7 +6,8 @@
|
||||
* Time: 17:42
|
||||
*/
|
||||
|
||||
namespace controller\api;
|
||||
namespace Controller\Api;
|
||||
|
||||
use Controller;
|
||||
use Model;
|
||||
|
||||
@@ -20,15 +21,14 @@ class Access extends Controller\AccessController {
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function search($f3, $params){
|
||||
|
||||
$accessData = [];
|
||||
|
||||
if(
|
||||
array_key_exists( 'arg1', $params) &&
|
||||
array_key_exists( 'arg2', $params)
|
||||
array_key_exists('arg1', $params) &&
|
||||
array_key_exists('arg2', $params)
|
||||
){
|
||||
$searchType = strtolower( $params['arg1'] );
|
||||
$searchToken = strtolower( $params['arg2'] );
|
||||
$searchType = strtolower($params['arg1']);
|
||||
$searchToken = strtolower($params['arg2']);
|
||||
|
||||
$accessModel = null;
|
||||
switch($searchType){
|
||||
@@ -59,13 +59,9 @@ class Access extends Controller\AccessController {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
echo json_encode($accessData);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -18,38 +18,60 @@ use Controller;
|
||||
*/
|
||||
class GitHub extends Controller\Controller {
|
||||
|
||||
protected function getBaseRequestOptions() : array {
|
||||
return [
|
||||
'timeout' => 3,
|
||||
'user_agent' => $this->getUserAgent(),
|
||||
'follow_location' => false // otherwise CURLOPT_FOLLOWLOCATION will fail
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* get HTTP request options for API (curl) request
|
||||
* @return array
|
||||
* @throws \Exception\PathfinderException
|
||||
*/
|
||||
protected function getRequestOptions(){
|
||||
$requestOptions = [
|
||||
'timeout' => 8,
|
||||
'method' => 'GET',
|
||||
'user_agent' => $this->getUserAgent(),
|
||||
'follow_location' => false // otherwise CURLOPT_FOLLOWLOCATION will fail
|
||||
protected function getRequestReleaseOptions() : array {
|
||||
$options = $this->getBaseRequestOptions();
|
||||
$options['method'] = 'GET';
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* get HTTP request options for API (curl) request
|
||||
* @param string $text
|
||||
* @return array
|
||||
* @throws \Exception\PathfinderException
|
||||
*/
|
||||
protected function getRequestMarkdownOptions(string $text) : array {
|
||||
$params = [
|
||||
'text' => $text,
|
||||
'mode' => 'gfm',
|
||||
'context' => 'exodus4d/pathfinder'
|
||||
];
|
||||
|
||||
return $requestOptions;
|
||||
$options = $this->getBaseRequestOptions();
|
||||
$options['method'] = 'POST';
|
||||
$options['content'] = json_encode($params, JSON_UNESCAPED_SLASHES);
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* get release information from GitHub
|
||||
* @param $f3
|
||||
* @param \Base $f3
|
||||
* @throws \Exception\PathfinderException
|
||||
*/
|
||||
public function releases($f3){
|
||||
public function releases(\Base $f3){
|
||||
$cacheKey = 'CACHE_GITHUB_RELEASES';
|
||||
$ttl = 60 * 30; // 30min
|
||||
$releaseCount = 4;
|
||||
|
||||
if( !$f3->exists($cacheKey) ){
|
||||
$apiPath = Config::getPathfinderData('api.git_hub') . '/repos/exodus4d/pathfinder/releases';
|
||||
if( !$f3->exists($cacheKey, $return) ){
|
||||
$apiReleasePath = Config::getPathfinderData('api.git_hub') . '/repos/exodus4d/pathfinder/releases';
|
||||
$apiMarkdownPath = Config::getPathfinderData('api.git_hub') . '/markdown';
|
||||
|
||||
// build request URL
|
||||
$options = $this->getRequestOptions();
|
||||
$apiResponse = \Web::instance()->request($apiPath, $options );
|
||||
$apiResponse = \Web::instance()->request($apiReleasePath, $this->getRequestReleaseOptions() );
|
||||
|
||||
if($apiResponse['body']){
|
||||
$return = (object) [];
|
||||
@@ -100,7 +122,17 @@ class GitHub extends Controller\Controller {
|
||||
// convert list style
|
||||
$body = str_replace(' - ', '* ', $body );
|
||||
|
||||
$releaseData->body = $md->convert( trim($body) );
|
||||
// convert Markdown to HTML -> use either gitHub API (in oder to create abs, issue links)
|
||||
// -> or F3´s markdown as fallback
|
||||
$markdownResponse = \Web::instance()->request($apiMarkdownPath, $this->getRequestMarkdownOptions($body) );
|
||||
|
||||
if($markdownResponse['body']){
|
||||
$body = $markdownResponse['body'];
|
||||
}else{
|
||||
$body = $md->convert( trim($body) );
|
||||
}
|
||||
|
||||
$releaseData->body = $body;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,7 +141,7 @@ class GitHub extends Controller\Controller {
|
||||
$f3->set($cacheKey, $return, $ttl);
|
||||
}else{
|
||||
// request failed -> cache failed result (respect API request limit)
|
||||
$f3->set($cacheKey, false, 60 * 5);
|
||||
$f3->set($cacheKey, false, 60 * 15);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -70,7 +70,6 @@ class Map extends Controller\AccessController {
|
||||
|
||||
// default map type config
|
||||
$mapsDefaultConfig = Config::getMapsDefaultConfig();
|
||||
|
||||
$mapTypeData = [];
|
||||
foreach((array)$rows as $rowData){
|
||||
$data = [
|
||||
@@ -81,7 +80,6 @@ class Map extends Controller\AccessController {
|
||||
'defaultConfig' => $mapsDefaultConfig[$rowData->name]
|
||||
];
|
||||
$mapTypeData[$rowData->name] = $data;
|
||||
|
||||
}
|
||||
$return->mapTypes = $mapTypeData;
|
||||
|
||||
@@ -143,8 +141,10 @@ class Map extends Controller\AccessController {
|
||||
$wormholes = Model\BasicModel::getNew('WormholeModel');
|
||||
$rows = $wormholes->find('id > 0', null, $expireTimeSQL);
|
||||
$wormholesData = [];
|
||||
foreach((array)$rows as $rowData){
|
||||
$wormholesData[$rowData->name] = $rowData->getData();
|
||||
if($rows){
|
||||
foreach((array)$rows as $rowData){
|
||||
$wormholesData[$rowData->name] = $rowData->getData();
|
||||
}
|
||||
}
|
||||
$return->wormholes = $wormholesData;
|
||||
|
||||
@@ -190,6 +190,17 @@ class Map extends Controller\AccessController {
|
||||
'status' => (bool)Config::getPathfinderData('discord.status')
|
||||
];
|
||||
|
||||
// structure status ---------------------------------------------------------------------------------------
|
||||
$structureStatus = Model\StructureStatusModel::getAll();
|
||||
$structureData = [];
|
||||
foreach($structureStatus as $status){
|
||||
$structureData[$status->_id] = $status->getData();
|
||||
}
|
||||
$return->structureStatus = $structureData;
|
||||
|
||||
// universe category data ---------------------------------------------------------------------------------
|
||||
$return->universeCategories = [65 => Model\Universe\BasicUniverseModel::getNew('CategoryModel')->getById(65)->getData()];
|
||||
|
||||
$f3->set(self::CACHE_KEY_INIT, $return, $expireTimeCache );
|
||||
}
|
||||
|
||||
@@ -202,11 +213,11 @@ class Map extends Controller\AccessController {
|
||||
|
||||
// get SSO error messages that should be shown immediately ----------------------------------------------------
|
||||
// -> e.g. errors while character switch from previous HTTP requests
|
||||
if( $f3->exists(Controller\Ccp\Sso::SESSION_KEY_SSO_ERROR) ){
|
||||
if($f3->exists(Controller\Ccp\Sso::SESSION_KEY_SSO_ERROR, $message)){
|
||||
$ssoError = (object) [];
|
||||
$ssoError->type = 'error';
|
||||
$ssoError->title = 'Login failed';
|
||||
$ssoError->message = $f3->get(Controller\Ccp\Sso::SESSION_KEY_SSO_ERROR);
|
||||
$ssoError->message = $message;
|
||||
$return->error[] = $ssoError;
|
||||
$f3->clear(Controller\Ccp\Sso::SESSION_KEY_SSO_ERROR);
|
||||
}
|
||||
@@ -888,7 +899,7 @@ class Map extends Controller\AccessController {
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* add new map connection based on current $character location
|
||||
* @param Model\CharacterModel $character
|
||||
* @param Model\MapModel $map
|
||||
* @return Model\MapModel
|
||||
@@ -938,7 +949,7 @@ class Map extends Controller\AccessController {
|
||||
$targetSystem = $map->getSystemByCCPId( $targetSystemId, ['active' => 1]);
|
||||
}
|
||||
|
||||
// if systems don´t already exists on map -> get "blank" systems
|
||||
// if systems don´t already exists on map -> get "blank" system
|
||||
// -> required for system type check (e.g. wormhole, k-space)
|
||||
if(
|
||||
!$sourceSystem &&
|
||||
@@ -960,126 +971,133 @@ class Map extends Controller\AccessController {
|
||||
$targetSystem = $map->getNewSystem($targetSystemId);
|
||||
}
|
||||
|
||||
$addSourceSystem = false;
|
||||
$addTargetSystem = false;
|
||||
$addConnection = false;
|
||||
// make sure we have system objects to work with
|
||||
// -> in case SDE does not have system they are null -> we can´t do anything
|
||||
if(
|
||||
$sourceSystem &&
|
||||
$targetSystem
|
||||
){
|
||||
$addSourceSystem = false;
|
||||
$addTargetSystem = false;
|
||||
$addConnection = false;
|
||||
|
||||
switch($mapScope->name){
|
||||
case 'all':
|
||||
if($sameSystem){
|
||||
$addSourceSystem = true;
|
||||
}else{
|
||||
$addSourceSystem = true;
|
||||
$addTargetSystem = true;
|
||||
$addConnection = true;
|
||||
}
|
||||
break;
|
||||
case 'k-space':
|
||||
if($sameSystem){
|
||||
if( !$sourceSystem->isWormhole() ){
|
||||
switch($mapScope->name){
|
||||
case 'all':
|
||||
if($sameSystem){
|
||||
$addSourceSystem = true;
|
||||
}
|
||||
}elseif(
|
||||
!$sourceSystem->isWormhole() ||
|
||||
!$targetSystem->isWormhole()
|
||||
){
|
||||
$addSourceSystem = true;
|
||||
$addTargetSystem = true;
|
||||
$addConnection = true;
|
||||
}
|
||||
break;
|
||||
case 'wh':
|
||||
default:
|
||||
if($sameSystem){
|
||||
if( $sourceSystem->isWormhole() ){
|
||||
$addSourceSystem = true;
|
||||
}
|
||||
}elseif(
|
||||
$sourceSystem->isWormhole() ||
|
||||
$targetSystem->isWormhole()
|
||||
){
|
||||
$addSourceSystem = true;
|
||||
$addTargetSystem = true;
|
||||
$addConnection = true;
|
||||
}elseif(
|
||||
!$sourceSystem->isWormhole() &&
|
||||
!$targetSystem->isWormhole()
|
||||
){
|
||||
// check distance between systems (in jumps)
|
||||
// -> if > 1 it is !very likely! a wormhole
|
||||
$routeController = new Route();
|
||||
$route = $routeController->searchRoute($sourceSystem->systemId, $targetSystem->systemId, 1);
|
||||
|
||||
if( !$route['routePossible'] ){
|
||||
}else{
|
||||
$addSourceSystem = true;
|
||||
$addTargetSystem = true;
|
||||
$addConnection = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 'k-space':
|
||||
if($sameSystem){
|
||||
if( !$sourceSystem->isWormhole() ){
|
||||
$addSourceSystem = true;
|
||||
}
|
||||
}elseif(
|
||||
!$sourceSystem->isWormhole() ||
|
||||
!$targetSystem->isWormhole()
|
||||
){
|
||||
$addSourceSystem = true;
|
||||
$addTargetSystem = true;
|
||||
$addConnection = true;
|
||||
}
|
||||
break;
|
||||
case 'wh':
|
||||
default:
|
||||
if($sameSystem){
|
||||
if( $sourceSystem->isWormhole() ){
|
||||
$addSourceSystem = true;
|
||||
}
|
||||
}elseif(
|
||||
$sourceSystem->isWormhole() ||
|
||||
$targetSystem->isWormhole()
|
||||
){
|
||||
$addSourceSystem = true;
|
||||
$addTargetSystem = true;
|
||||
$addConnection = true;
|
||||
}elseif(
|
||||
!$sourceSystem->isWormhole() &&
|
||||
!$targetSystem->isWormhole()
|
||||
){
|
||||
// check distance between systems (in jumps)
|
||||
// -> if > 1 it is !very likely! a wormhole
|
||||
$routeController = new Route();
|
||||
$route = $routeController->searchRoute($sourceSystem->systemId, $targetSystem->systemId, 1);
|
||||
|
||||
// save source system ---------------------------------------------------------------------------------
|
||||
if(
|
||||
$addSourceSystem &&
|
||||
$sourceSystem &&
|
||||
!$sourceExists
|
||||
){
|
||||
$sourceSystem = $map->saveSystem($sourceSystem, $character, $systemPosX, $systemPosY);
|
||||
// get updated maps object
|
||||
if($sourceSystem){
|
||||
$map = $sourceSystem->mapId;
|
||||
$sourceExists = true;
|
||||
$mapDataChanged = true;
|
||||
// increase system position (prevent overlapping)
|
||||
$systemPosX = $sourceSystem->posX + $systemOffsetX;
|
||||
$systemPosY = $sourceSystem->posY + $systemOffsetY;
|
||||
if( !$route['routePossible'] ){
|
||||
$addSourceSystem = true;
|
||||
$addTargetSystem = true;
|
||||
$addConnection = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// save target system ---------------------------------------------------------------------------------
|
||||
if(
|
||||
$addTargetSystem &&
|
||||
$targetSystem &&
|
||||
!$targetExists
|
||||
){
|
||||
$targetSystem = $map->saveSystem($targetSystem, $character, $systemPosX, $systemPosY);
|
||||
// get updated maps object
|
||||
if($targetSystem){
|
||||
$map = $targetSystem->mapId;
|
||||
$mapDataChanged = true;
|
||||
$targetExists = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(
|
||||
$sourceExists &&
|
||||
$targetExists &&
|
||||
$sourceSystem &&
|
||||
$targetSystem
|
||||
){
|
||||
$connection = $map->searchConnection( $sourceSystem, $targetSystem);
|
||||
|
||||
// save connection --------------------------------------------------------------------------------
|
||||
// save source system ---------------------------------------------------------------------------------
|
||||
if(
|
||||
$addConnection &&
|
||||
!$connection
|
||||
$addSourceSystem &&
|
||||
$sourceSystem &&
|
||||
!$sourceExists
|
||||
){
|
||||
$connection = $map->getNewConnection($sourceSystem, $targetSystem);
|
||||
$connection = $map->saveConnection($connection, $character);
|
||||
$sourceSystem = $map->saveSystem($sourceSystem, $character, $systemPosX, $systemPosY);
|
||||
// get updated maps object
|
||||
if($connection){
|
||||
$map = $connection->mapId;
|
||||
if($sourceSystem){
|
||||
$map = $sourceSystem->mapId;
|
||||
$sourceExists = true;
|
||||
$mapDataChanged = true;
|
||||
// increase system position (prevent overlapping)
|
||||
$systemPosX = $sourceSystem->posX + $systemOffsetX;
|
||||
$systemPosY = $sourceSystem->posY + $systemOffsetY;
|
||||
}
|
||||
}
|
||||
|
||||
// log jump mass ----------------------------------------------------------------------------------
|
||||
// save target system ---------------------------------------------------------------------------------
|
||||
if(
|
||||
$connection &&
|
||||
$connection->isWormhole()
|
||||
$addTargetSystem &&
|
||||
$targetSystem &&
|
||||
!$targetExists
|
||||
){
|
||||
$connection->logMass($log);
|
||||
$targetSystem = $map->saveSystem($targetSystem, $character, $systemPosX, $systemPosY);
|
||||
// get updated maps object
|
||||
if($targetSystem){
|
||||
$map = $targetSystem->mapId;
|
||||
$mapDataChanged = true;
|
||||
$targetExists = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(
|
||||
$sourceExists &&
|
||||
$targetExists &&
|
||||
$sourceSystem &&
|
||||
$targetSystem
|
||||
){
|
||||
$connection = $map->searchConnection( $sourceSystem, $targetSystem);
|
||||
|
||||
// save connection --------------------------------------------------------------------------------
|
||||
if(
|
||||
$addConnection &&
|
||||
!$connection
|
||||
){
|
||||
$connection = $map->getNewConnection($sourceSystem, $targetSystem);
|
||||
$connection = $map->saveConnection($connection, $character);
|
||||
// get updated maps object
|
||||
if($connection){
|
||||
$map = $connection->mapId;
|
||||
$mapDataChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
// log jump mass ----------------------------------------------------------------------------------
|
||||
if(
|
||||
$connection &&
|
||||
$connection->isWormhole()
|
||||
){
|
||||
$connection->logMass($log);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,12 @@ class Route extends Controller\AccessController {
|
||||
|
||||
const ROUTE_SEARCH_DEPTH_DEFAULT = 1;
|
||||
|
||||
/**
|
||||
* ESI route search can handle max 100 custom connections
|
||||
* -> each connection has a A->B and B->A entry. So we have 50 "real connections"
|
||||
*/
|
||||
const MAX_CONNECTION_COUNT = 100;
|
||||
|
||||
/**
|
||||
* cache time for static jump data (e.g. K-Space stargates)
|
||||
* @var int
|
||||
@@ -406,7 +412,9 @@ class Route extends Controller\AccessController {
|
||||
// search root by ESI API
|
||||
$routeData = $this->searchRouteESI($systemFromId, $systemToId, $searchDepth, $mapIds, $filterData);
|
||||
|
||||
if( !empty($routeData['error']) ){
|
||||
// Endpoint return http:404 in case no route find (e.g. from inside a wh)
|
||||
// we thread that error "no route found" as a valid response! -> no fallback to custom search
|
||||
if( !empty($routeData['error']) && strtolower($routeData['error']) !== 'no route found' ){
|
||||
// ESI route search has errors -> fallback to custom search implementation
|
||||
$routeData = $this->searchRouteCustom($systemFromId, $systemToId, $searchDepth, $mapIds, $filterData);
|
||||
}
|
||||
@@ -424,7 +432,7 @@ class Route extends Controller\AccessController {
|
||||
* @return array
|
||||
* @throws \Exception\PathfinderException
|
||||
*/
|
||||
public function searchRouteCustom(int $systemFromId, int $systemToId, $searchDepth = 0, array $mapIds = [], array $filterData = []) : array {
|
||||
private function searchRouteCustom(int $systemFromId, int $systemToId, $searchDepth = 0, array $mapIds = [], array $filterData = []) : array {
|
||||
// reset all previous set jump data
|
||||
$this->resetJumpData();
|
||||
|
||||
@@ -506,7 +514,7 @@ class Route extends Controller\AccessController {
|
||||
* @return array
|
||||
* @throws \Exception\PathfinderException
|
||||
*/
|
||||
public function searchRouteESI(int $systemFromId, int $systemToId, int $searchDepth = 0, array $mapIds = [], array $filterData = []) : array {
|
||||
private function searchRouteESI(int $systemFromId, int $systemToId, int $searchDepth = 0, array $mapIds = [], array $filterData = []) : array {
|
||||
// reset all previous set jump data
|
||||
$this->resetJumpData();
|
||||
|
||||
@@ -517,6 +525,11 @@ class Route extends Controller\AccessController {
|
||||
$routeData['searchType'] = 'esi';
|
||||
|
||||
if($systemFromId && $systemToId){
|
||||
// ESI route search can only handle 50 $connections (100 entries)
|
||||
// we want to add NON stargate connections ONLY for ESI route search
|
||||
// because ESI will use them anyways!
|
||||
$filterData['stargates'] = false;
|
||||
|
||||
// prepare search data ------------------------------------------------------------------------------------
|
||||
|
||||
// add map specific data
|
||||
@@ -542,7 +555,17 @@ class Route extends Controller\AccessController {
|
||||
|
||||
// systemIds exist and wer not removed before in filterJumpData()
|
||||
if($systemSourceId && $systemTargetId){
|
||||
$connections[] = [$systemSourceId, $systemTargetId];
|
||||
$jumpNode = [$systemSourceId, $systemTargetId];
|
||||
// jumpNode must be unique for ESI,
|
||||
// ... there can be multiple connections between same systems in Pathfinder
|
||||
if(!in_array($jumpNode, $connections)){
|
||||
$connections[] = [$systemSourceId, $systemTargetId];
|
||||
// check if connections limit is reached
|
||||
if(count($connections) >= self::MAX_CONNECTION_COUNT){
|
||||
// ESI API limit for custom "connections"
|
||||
break 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
212
app/main/controller/api/setup.php
Normal file
212
app/main/controller/api/setup.php
Normal file
@@ -0,0 +1,212 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: exodu
|
||||
* Date: 27.05.2018
|
||||
* Time: 14:17
|
||||
*/
|
||||
|
||||
namespace Controller\Api;
|
||||
|
||||
use Controller;
|
||||
use DB\Database;
|
||||
use Model;
|
||||
|
||||
class Setup extends Controller\Controller {
|
||||
|
||||
/**
|
||||
* build search index from existing data (e.g. Systems)
|
||||
* OR import data from ESI (e.g. Structures)
|
||||
* -> optional build/import smaller chunks of data
|
||||
* @param \Base $f3
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function buildIndex(\Base $f3){
|
||||
$postData = (array)$f3->get('POST');
|
||||
$type = (string)$postData['type'];
|
||||
$count = (int)$postData['count'];
|
||||
|
||||
$return = (object) [];
|
||||
$return->error = [];
|
||||
$return->type = $type;
|
||||
$return->count = $count;
|
||||
$return->countAll = 0;
|
||||
$return->countBuild = 0;
|
||||
$return->countBuildAll = 0;
|
||||
$return->progress = 0;
|
||||
|
||||
/**
|
||||
* sum array values
|
||||
* @param int $carry
|
||||
* @param int $value
|
||||
* @return int
|
||||
*/
|
||||
$sum = function(int $carry, int $value){
|
||||
$carry += $value;
|
||||
return $carry;
|
||||
};
|
||||
|
||||
/**
|
||||
* calc percent
|
||||
* @param int $countAll
|
||||
* @param int $count
|
||||
* @return int
|
||||
*/
|
||||
$percent = function(int $countAll, int $count){
|
||||
return $countAll ? floor((100/$countAll) * $count) : 0;
|
||||
};
|
||||
|
||||
$controller = new Controller\Ccp\Universe();
|
||||
switch($type){
|
||||
case 'Systems':
|
||||
$length = 200;
|
||||
$offset = $count * $length;
|
||||
$buildInfo = $controller->buildSystemsIndex($offset, $length);
|
||||
$return->countAll = $buildInfo['countAll'];
|
||||
$return->countBuild = $buildInfo['countBuild'];
|
||||
$return->countBuildAll = count($controller->getSystemsIndex());
|
||||
$return->progress = $percent($return->countAll, $offset + $length);
|
||||
break;
|
||||
case 'Structures':
|
||||
$categoryId = 65;
|
||||
$length = 2;
|
||||
$offset = $count * $length;
|
||||
$buildInfo = $controller->setupCategory($categoryId, $offset, $length);
|
||||
|
||||
$categoryUniverseModel = Model\Universe\BasicUniverseModel::getNew('CategoryModel');
|
||||
$return->countAll = (int)$f3->get('REQUIREMENTS.DATA.STRUCTURES');
|
||||
$return->countBuild = array_reduce($buildInfo, $sum, 0);
|
||||
$return->countBuildAll = $categoryUniverseModel->getById($categoryId, 0)->getTypesCount(false);
|
||||
$return->progress = $percent($return->countAll, $return->countBuildAll);
|
||||
break;
|
||||
case 'Ships':
|
||||
$categoryId = 6;
|
||||
$length = 2;
|
||||
$offset = $count * $length;
|
||||
$buildInfo = $controller->setupCategory($categoryId, $offset, $length);
|
||||
|
||||
$categoryUniverseModel = Model\Universe\BasicUniverseModel::getNew('CategoryModel');
|
||||
$return->countAll = (int)$f3->get('REQUIREMENTS.DATA.SHIPS');
|
||||
$return->countBuild = array_reduce($buildInfo, $sum, 0);
|
||||
$return->countBuildAll = $categoryUniverseModel->getById($categoryId, 0)->getTypesCount(false);
|
||||
$return->progress = $percent($return->countAll, $return->countBuildAll);
|
||||
break;
|
||||
case 'SystemNeighbourModel':
|
||||
// Becomes deprecated with new Universe DB!!!
|
||||
$this->setupSystemJumpTable();
|
||||
|
||||
$return->countAll = 5214;
|
||||
$return->countBuild = Database::instance()->getRowCount('system_neighbour');
|
||||
$return->countBuildAll = $return->countBuild;
|
||||
$return->progress = $percent($return->countAll, $return->countBuildAll);
|
||||
break;
|
||||
}
|
||||
|
||||
if($return->countBuildAll < $return->countAll){
|
||||
$return->count++;
|
||||
}
|
||||
|
||||
echo json_encode($return);
|
||||
}
|
||||
|
||||
/**
|
||||
* clear search index
|
||||
* @param \Base $f3
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function clearIndex(\Base $f3){
|
||||
$postData = (array)$f3->get('POST');
|
||||
$type = (string)$postData['type'];
|
||||
|
||||
$return = (object) [];
|
||||
$return->error = [];
|
||||
$return->type = $type;
|
||||
$return->count = 0;
|
||||
$return->countAll = 0;
|
||||
$return->countBuild = 0;
|
||||
$return->countBuildAll = 0;
|
||||
$return->progress = 0;
|
||||
|
||||
$controller = new Controller\Ccp\Universe();
|
||||
switch($type) {
|
||||
case 'Systems':
|
||||
$controller->clearSystemsIndex();
|
||||
$systemUniverseModel = Model\Universe\BasicUniverseModel::getNew('SystemModel');
|
||||
$return->countAll = Database::instance()->getRowCount($systemUniverseModel->getTable(), 'UNIVERSE');
|
||||
break;
|
||||
}
|
||||
|
||||
echo json_encode($return);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
protected function setupSystemJumpTable(){
|
||||
$pfDB = $this->getDB('PF');
|
||||
$ccpDB = $this->getDB('CCP');
|
||||
|
||||
$query = "SELECT
|
||||
map_sys.solarSystemID system_id,
|
||||
map_sys.regionID region_id,
|
||||
map_sys.constellationID constellation_id,
|
||||
map_sys.solarSystemName system_name,
|
||||
ROUND( map_sys.security, 4) system_security,
|
||||
(
|
||||
SELECT
|
||||
GROUP_CONCAT( NULLIF(map_sys_inner.solarSystemName, NULL) SEPARATOR ':')
|
||||
FROM
|
||||
mapSolarSystemJumps map_jump INNER JOIN
|
||||
mapSolarSystems map_sys_inner ON
|
||||
map_sys_inner.solarSystemID = map_jump.toSolarSystemID
|
||||
WHERE
|
||||
map_jump.fromSolarSystemID = map_sys.solarSystemID
|
||||
) system_neighbours
|
||||
FROM
|
||||
mapSolarSystems map_sys
|
||||
HAVING
|
||||
-- skip systems without neighbors (e.g. WHs)
|
||||
system_neighbours IS NOT NULL
|
||||
";
|
||||
|
||||
$rows = $ccpDB->exec($query);
|
||||
|
||||
if(count($rows) > 0){
|
||||
// switch DB back to pathfinder DB
|
||||
|
||||
// clear cache table
|
||||
$pfDB->exec("TRUNCATE system_neighbour");
|
||||
|
||||
foreach($rows as $row){
|
||||
$pfDB->exec("
|
||||
INSERT INTO
|
||||
system_neighbour(
|
||||
regionId,
|
||||
constellationId,
|
||||
systemName,
|
||||
systemId,
|
||||
jumpNodes,
|
||||
trueSec
|
||||
)
|
||||
VALUES(
|
||||
:regionId,
|
||||
:constellationId,
|
||||
:systemName,
|
||||
:systemId,
|
||||
:jumpNodes,
|
||||
:trueSec
|
||||
)",
|
||||
[
|
||||
':regionId' => $row['region_id'],
|
||||
':constellationId' => $row['constellation_id'],
|
||||
':systemName' => $row['system_name'],
|
||||
':systemId' => $row['system_id'],
|
||||
':jumpNodes' => $row['system_neighbours'],
|
||||
':trueSec' => $row['system_security']
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
111
app/main/controller/api/structure.php
Normal file
111
app/main/controller/api/structure.php
Normal file
@@ -0,0 +1,111 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: exodu
|
||||
* Date: 24.04.2018
|
||||
* Time: 22:23
|
||||
*/
|
||||
|
||||
namespace Controller\Api;
|
||||
|
||||
use Controller;
|
||||
use Model;
|
||||
use Exception;
|
||||
|
||||
class Structure extends Controller\AccessController {
|
||||
|
||||
/**
|
||||
* save/update structure
|
||||
* @param \Base $f3
|
||||
* @throws Exception
|
||||
*/
|
||||
public function save(\Base $f3){
|
||||
$requestData = (array)$f3->get('POST');
|
||||
$structuresData = (array)$requestData['structures'];
|
||||
$return = (object) [];
|
||||
$return->error = [];
|
||||
|
||||
if($structuresData){
|
||||
$activeCharacter = $this->getCharacter();
|
||||
|
||||
if($activeCharacter->hasCorporation()){
|
||||
// structures always belong to a corporation
|
||||
/**
|
||||
* @var $structure Model\StructureModel
|
||||
*/
|
||||
$structure = Model\BasicModel::getNew('StructureModel');
|
||||
foreach ($structuresData as $structureData){
|
||||
// reset on loop start because of potential "continue"
|
||||
$structure->reset();
|
||||
|
||||
if(!empty($structureData['id']) && $structureId = (int)$structureData['id']){
|
||||
// update specific structure
|
||||
$structure->getById($structureId);
|
||||
if( !$structure->hasAccess($activeCharacter) ){
|
||||
continue;
|
||||
}
|
||||
}elseif( !isset($structureData['id']) ){
|
||||
// from clipboard -> search by structure by name
|
||||
$structure->getByName($activeCharacter->getCorporation(), (string)$structureData['name']);
|
||||
}
|
||||
|
||||
$newStructure = $structure->dry();
|
||||
|
||||
try{
|
||||
$structure->copyfrom($structureData, ['structureId', 'corporationId', 'systemId', 'statusId', 'name', 'description']);
|
||||
$structure->save();
|
||||
|
||||
if($newStructure){
|
||||
$activeCharacter->getCorporation()->saveStructure($structure);
|
||||
}
|
||||
|
||||
// group all updated structures by corporation -> just for return
|
||||
$corporationsStructureData = $structure->getDataByCorporations();
|
||||
foreach($corporationsStructureData as $corporationId => $corporationStructureData){
|
||||
if(isset($return->structures[$corporationId])){
|
||||
$return->structures[$corporationId]['structures'] = array_merge(
|
||||
$return->structures[$corporationId]['structures'],
|
||||
$corporationStructureData['structures']
|
||||
);
|
||||
}else{
|
||||
$return->structures[$corporationId] = $corporationStructureData;
|
||||
}
|
||||
}
|
||||
}catch(Exception\ValidationException $e){
|
||||
$return->error[] = $e->getError();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
echo json_encode($return);
|
||||
}
|
||||
|
||||
/**
|
||||
* delete structure
|
||||
* @param \Base $f3
|
||||
* @throws Exception
|
||||
*/
|
||||
public function delete(\Base $f3){
|
||||
$structureData = (array)$f3->get('POST');
|
||||
$structureId = (int)$structureData['id'];
|
||||
|
||||
$return = (object) [];
|
||||
|
||||
if($structureId){
|
||||
$activeCharacter = $this->getCharacter();
|
||||
|
||||
/**
|
||||
* @var $structure Model\StructureModel
|
||||
*/
|
||||
$structure = Model\BasicModel::getNew('StructureModel');
|
||||
$structure->getById($structureId);
|
||||
if($structure->hasAccess($activeCharacter) && $structure->erase()){
|
||||
$return->deletedStructureIds = [$structureId];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
echo json_encode($return);
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@
|
||||
*/
|
||||
|
||||
namespace Controller\Api;
|
||||
|
||||
use Controller;
|
||||
use Data\Mapper as Mapper;
|
||||
use lib\Config;
|
||||
@@ -236,7 +237,9 @@ class System extends Controller\AccessController {
|
||||
$defaultStatusId = $systemModel->statusId;
|
||||
}
|
||||
|
||||
$systemModel->statusId = isset($systemData['statusId']) ? $systemData['statusId'] : $defaultStatusId;
|
||||
if( !is_null($systemModel) ){
|
||||
$systemModel->statusId = isset($systemData['statusId']) ? $systemData['statusId'] : $defaultStatusId;
|
||||
}
|
||||
|
||||
// map is not changeable for a system! (security)
|
||||
$systemData['mapId'] = $map;
|
||||
@@ -441,6 +444,30 @@ class System extends Controller\AccessController {
|
||||
echo json_encode($return);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Base $f3
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function getData(\Base $f3){
|
||||
$requestData = (array)$f3->get('POST');
|
||||
$mapId = (int)$requestData['mapId'];
|
||||
$systemId = (int)$requestData['systemId'];
|
||||
$activeCharacter = $this->getCharacter();
|
||||
|
||||
$return = (object) [];
|
||||
|
||||
if(
|
||||
!is_null($map = $activeCharacter->getMap($mapId)) &&
|
||||
!is_null($system = $map->getSystemById($systemId))
|
||||
){
|
||||
$return->system = $system->getData();
|
||||
$return->system->signatures = $system->getSignaturesData();
|
||||
$return->system->structures = $system->getStructuresData();
|
||||
}
|
||||
|
||||
echo json_encode($return);
|
||||
}
|
||||
|
||||
/**
|
||||
* delete systems and all its connections from map
|
||||
* -> set "active" flag
|
||||
|
||||
36
app/main/controller/api/universe.php
Normal file
36
app/main/controller/api/universe.php
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: exodu
|
||||
* Date: 21.04.2018
|
||||
* Time: 15:49
|
||||
*/
|
||||
|
||||
namespace Controller\Api;
|
||||
|
||||
use Controller;
|
||||
use Controller\Ccp as Ccp;
|
||||
|
||||
class Universe extends Controller\AccessController {
|
||||
|
||||
/**
|
||||
* search static Universe data by string within categories
|
||||
* @param \Base $f3
|
||||
* @param $params
|
||||
*/
|
||||
public function search(\Base $f3, $params){
|
||||
$postData = (array)$f3->get('POST');
|
||||
$categories = (array)$postData['categories'];
|
||||
$universeNameData = [];
|
||||
|
||||
if(
|
||||
array_key_exists('arg1', $params) &&
|
||||
!empty($search = strtolower($params['arg1'])) &&
|
||||
!empty($categories)
|
||||
){
|
||||
$universeNameData = Ccp\Universe::searchUniverseNameData($categories, $search);
|
||||
}
|
||||
|
||||
echo json_encode($universeNameData);
|
||||
}
|
||||
}
|
||||
@@ -42,6 +42,7 @@ class Sso extends Api\User{
|
||||
const ERROR_VERIFY_CHARACTER = 'Unable to verify character data. %s';
|
||||
const ERROR_LOGIN_FAILED = 'Failed authentication due to technical problems: %s';
|
||||
const ERROR_CHARACTER_VERIFICATION = 'Character verification failed by SSP SSO';
|
||||
const ERROR_CHARACTER_DATA = 'Failed to load characterData from ESI';
|
||||
const ERROR_CHARACTER_FORBIDDEN = 'Character "%s" is not authorized to log in. Reason: %s';
|
||||
const ERROR_SERVICE_TIMEOUT = 'CCP SSO service timeout (%ss). Try again later';
|
||||
const ERROR_COOKIE_LOGIN = 'Login from Cookie failed. Please retry by CCP SSO';
|
||||
@@ -112,7 +113,7 @@ class Sso extends Api\User{
|
||||
$this->setLoginCookie($character);
|
||||
|
||||
// route to "map"
|
||||
$f3->reroute(['map']);
|
||||
$f3->reroute(['map', ['*' => '']]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -207,7 +208,7 @@ class Sso extends Api\User{
|
||||
// verification available data. Data is needed for "ownerHash" check
|
||||
|
||||
// get character data from ESI
|
||||
$characterData = $this->getCharacterData($verificationCharacterData->CharacterID);
|
||||
$characterData = $this->getCharacterData((int)$verificationCharacterData->CharacterID);
|
||||
|
||||
if( isset($characterData->character) ){
|
||||
// add "ownerHash" and SSO tokens
|
||||
@@ -269,7 +270,7 @@ class Sso extends Api\User{
|
||||
if($rootAlias == 'admin'){
|
||||
$f3->reroute([$rootAlias, ['*' => '']]);
|
||||
}else{
|
||||
$f3->reroute(['map']);
|
||||
$f3->reroute(['map', ['*' => '']]);
|
||||
}
|
||||
}else{
|
||||
$f3->set(self::SESSION_KEY_SSO_ERROR, sprintf(self::ERROR_LOGIN_FAILED, $characterModel->name));
|
||||
@@ -281,6 +282,9 @@ class Sso extends Api\User{
|
||||
);
|
||||
}
|
||||
}
|
||||
}else{
|
||||
// failed to load characterData from API
|
||||
$f3->set(self::SESSION_KEY_SSO_ERROR, self::ERROR_CHARACTER_DATA);
|
||||
}
|
||||
}else{
|
||||
// failed to verify character by CCP SSO
|
||||
@@ -325,7 +329,7 @@ class Sso extends Api\User{
|
||||
$loginCheck = $this->loginByCharacter($character);
|
||||
if($loginCheck){
|
||||
// route to "map"
|
||||
$f3->reroute(['map']);
|
||||
$f3->reroute(['map', ['*' => '']]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -494,67 +498,44 @@ class Sso extends Api\User{
|
||||
/**
|
||||
* get character data
|
||||
* @param int $characterId
|
||||
* @return object
|
||||
* @return \stdClass
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function getCharacterData($characterId){
|
||||
public function getCharacterData(int $characterId) : \stdClass{
|
||||
$characterData = (object) [];
|
||||
|
||||
$characterDataBasic = $this->getF3()->ccpClient->getCharacterData($characterId);
|
||||
if($characterId){
|
||||
$characterDataBasic = $this->getF3()->ccpClient->getCharacterData($characterId);
|
||||
|
||||
if( !empty($characterDataBasic) ){
|
||||
// remove some "unwanted" data -> not relevant for Pathfinder
|
||||
$characterData->character = array_filter($characterDataBasic, function($key){
|
||||
return in_array($key, ['id', 'name', 'securityStatus']);
|
||||
}, ARRAY_FILTER_USE_KEY);
|
||||
if( !empty($characterDataBasic) ){
|
||||
// remove some "unwanted" data -> not relevant for Pathfinder
|
||||
$characterData->character = array_filter($characterDataBasic, function($key){
|
||||
return in_array($key, ['id', 'name', 'securityStatus']);
|
||||
}, ARRAY_FILTER_USE_KEY);
|
||||
|
||||
$characterData->corporation = null;
|
||||
$characterData->alliance = null;
|
||||
$characterData->corporation = null;
|
||||
$characterData->alliance = null;
|
||||
|
||||
if(isset($characterDataBasic['corporation'])){
|
||||
$corporationId = (int)$characterDataBasic['corporation']['id'];
|
||||
|
||||
/**
|
||||
* @var Model\CorporationModel $corporationModel
|
||||
*/
|
||||
$corporationModel = Model\BasicModel::getNew('CorporationModel');
|
||||
$corporationModel->getById($corporationId, 0);
|
||||
|
||||
if($corporationModel->dry()){
|
||||
// request corporation data
|
||||
$corporationData = $this->getF3()->ccpClient->getCorporationData($corporationId);
|
||||
|
||||
if( !empty($corporationData) ){
|
||||
// check for NPC corporation
|
||||
$corporationData['isNPC'] = $this->getF3()->ccpClient->isNpcCorporation($corporationId);
|
||||
|
||||
$corporationModel->copyfrom($corporationData, ['id', 'name', 'isNPC']);
|
||||
$characterData->corporation = $corporationModel->save();
|
||||
if($corporationId = (int)$characterDataBasic['corporation']['id']){
|
||||
/**
|
||||
* @var Model\CorporationModel $corporation
|
||||
*/
|
||||
$corporation = Model\BasicModel::getNew('CorporationModel');
|
||||
$corporation->getById($corporationId, 0);
|
||||
if( !$corporation->dry() ){
|
||||
$characterData->corporation = $corporation;
|
||||
}
|
||||
}else{
|
||||
$characterData->corporation = $corporationModel;
|
||||
}
|
||||
}
|
||||
|
||||
if(isset($characterDataBasic['alliance'])){
|
||||
$allianceId = (int)$characterDataBasic['alliance']['id'];
|
||||
|
||||
/**
|
||||
* @var Model\AllianceModel $allianceModel
|
||||
*/
|
||||
$allianceModel = Model\BasicModel::getNew('AllianceModel');
|
||||
$allianceModel->getById($allianceId, 0);
|
||||
|
||||
if($allianceModel->dry()){
|
||||
// request alliance data
|
||||
$allianceData = $this->getF3()->ccpClient->getAllianceData($allianceId);
|
||||
|
||||
if( !empty($allianceData) ){
|
||||
$allianceModel->copyfrom($allianceData, ['id', 'name']);
|
||||
$characterData->alliance = $allianceModel->save();
|
||||
if($allianceId = (int)$characterDataBasic['alliance']['id']){
|
||||
/**
|
||||
* @var Model\AllianceModel $allianceModel
|
||||
*/
|
||||
$alliance = Model\BasicModel::getNew('AllianceModel');
|
||||
$alliance->getById($allianceId, 0);
|
||||
if( !$alliance->dry() ){
|
||||
$characterData->alliance = $alliance;
|
||||
}
|
||||
}else{
|
||||
$characterData->alliance = $allianceModel;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -564,29 +545,29 @@ class Sso extends Api\User{
|
||||
|
||||
/**
|
||||
* update character
|
||||
* @param $characterData
|
||||
* @return \Model\CharacterModel
|
||||
* @param \stdClass $characterData
|
||||
* @return \Model\CharacterModel|null
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function updateCharacter($characterData){
|
||||
$characterModel = null;
|
||||
protected function updateCharacter(\stdClass $characterData){
|
||||
$character = null;
|
||||
|
||||
if( !empty($characterData->character) ){
|
||||
|
||||
/**
|
||||
* @var Model\CharacterModel $characterModel
|
||||
* @var Model\CharacterModel $character
|
||||
*/
|
||||
$characterModel = Model\BasicModel::getNew('CharacterModel');
|
||||
$characterModel->getById((int)$characterData->character['id'], 0);
|
||||
$characterModel->copyfrom($characterData->character, [
|
||||
$character = Model\BasicModel::getNew('CharacterModel');
|
||||
$character->getById((int)$characterData->character['id'], 0);
|
||||
$character->copyfrom($characterData->character, [
|
||||
'id', 'name', 'ownerHash', 'crestAccessToken', 'crestRefreshToken', 'esiScopes', 'securityStatus'
|
||||
]);
|
||||
$characterModel->corporationId = $characterData->corporation;
|
||||
$characterModel->allianceId = $characterData->alliance;
|
||||
$characterModel = $characterModel->save();
|
||||
|
||||
$character->corporationId = $characterData->corporation;
|
||||
$character->allianceId = $characterData->alliance;
|
||||
$character = $character->save();
|
||||
}
|
||||
|
||||
return $characterModel;
|
||||
return $character;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -10,7 +10,8 @@ namespace Controller\Ccp;
|
||||
|
||||
|
||||
use Controller\Controller;
|
||||
use Model\BasicModel;
|
||||
use lib\Util;
|
||||
use Model;
|
||||
|
||||
class Universe extends Controller {
|
||||
|
||||
@@ -20,77 +21,246 @@ class Universe extends Controller {
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function setupDB(\Base $f3){
|
||||
$this->setupRegions($f3);
|
||||
$this->setupConstellations($f3);
|
||||
//$this->setupWormholes($f3);
|
||||
//var_dump($this->getSystemsIndex());
|
||||
//var_dump($this->getSystemData(30000001));
|
||||
//var_dump($this->getSystemData(30000002));
|
||||
//var_dump($this->getSystemData('Lashesih'));
|
||||
}
|
||||
|
||||
/**
|
||||
* get all regions from CCP and store region data
|
||||
* @param \Base $f3
|
||||
* @throws \Exception
|
||||
*/
|
||||
private function setupRegions(\Base $f3){
|
||||
$this->getDB('UNIVERSE');
|
||||
$regionIds = $f3->ccpClient->getRegions();
|
||||
$regionModel = BasicModel::getNew('Universe\RegionModel');
|
||||
/* currently not used
|
||||
protected function setupRegions(\Base $f3){
|
||||
$regionsWhitelist = [
|
||||
10000002 // The Forge (13 constellations -> 93 systems)
|
||||
];
|
||||
$regionIds = $f3->ccpClient->getUniverseRegions();
|
||||
$regionIds = array_intersect ($regionsWhitelist, $regionIds);
|
||||
|
||||
$region = Model\Universe\BasicUniverseModel::getNew('RegionModel');
|
||||
foreach($regionIds as $regionId){
|
||||
$regionModel->getById($regionId);
|
||||
$region->loadById($regionId);
|
||||
$region->loadConstellationsData();
|
||||
|
||||
if($regionModel->dry()){
|
||||
$regionData = $f3->ccpClient->getRegionData($regionId);
|
||||
if( !empty($regionData) ){
|
||||
$regionModel->copyfrom($regionData, ['id', 'name', 'description']);
|
||||
$regionModel->save();
|
||||
}
|
||||
foreach((array)$region->constellations as $constellation){
|
||||
$constellation->loadSystemsData();
|
||||
}
|
||||
|
||||
$regionModel->reset();
|
||||
$region->reset();
|
||||
}
|
||||
}*/
|
||||
|
||||
/* currently not used
|
||||
protected function setupConstellations(\Base $f3){
|
||||
$constellationsWhitelist = [
|
||||
20000014 // Mal (11 systems)
|
||||
];
|
||||
$constellationIds = $f3->ccpClient->getUniverseConstellations();
|
||||
$constellationIds = array_intersect ($constellationsWhitelist, $constellationIds);
|
||||
$constellation = Model\Universe\BasicUniverseModel::getNew('ConstellationModel');
|
||||
foreach($constellationIds as $constellationId){
|
||||
$constellation->loadById($constellationId);
|
||||
$constellation->loadSystemsData();
|
||||
$constellation->reset();
|
||||
}
|
||||
}*/
|
||||
|
||||
/**
|
||||
* setup category + all dependencies (e.g. groups, types)
|
||||
* -> $length = 0 -> setup all groups
|
||||
* @param int $categoryId
|
||||
* @param int $offset
|
||||
* @param int $length
|
||||
* @return array
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function setupCategory(int $categoryId, int $offset = 0, int $length = 0){
|
||||
$return = [];
|
||||
if($categoryId){
|
||||
/**
|
||||
* @var $category Model\Universe\CategoryModel
|
||||
*/
|
||||
$category = Model\Universe\BasicUniverseModel::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();
|
||||
}
|
||||
}
|
||||
}
|
||||
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
|
||||
*/
|
||||
public function setupCategories(array $categoriesWhitelist = []){
|
||||
$return = [];
|
||||
$categoryIds = $this->getF3()->ccpClient->getUniverseCategories();
|
||||
$categoryIds = array_intersect ($categoriesWhitelist, $categoryIds);
|
||||
foreach($categoryIds as $categoryId){
|
||||
$return[$categoryId] = $this->setupCategory($categoryId);
|
||||
}
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* setup groups + all dependencies (e.g. types)
|
||||
* id 6 -> Sun (29 types)
|
||||
* id 7 -> Planet (9 types)
|
||||
* id 10 -> Stargate (17 types)
|
||||
* id 988 -> Wormhole (89 types)
|
||||
* @param array $groupsWhitelist
|
||||
* @return array
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function setupGroups(array $groupsWhitelist = []){
|
||||
$return = [];
|
||||
$groupIds = $this->getF3()->ccpClient->getUniverseGroups();
|
||||
$groupIds = array_intersect ($groupsWhitelist, $groupIds);
|
||||
/**
|
||||
* @var $group Model\Universe\GroupModel
|
||||
*/
|
||||
$group = Model\Universe\BasicUniverseModel::getNew('GroupModel');
|
||||
foreach($groupIds as $groupId){
|
||||
$group->loadById($groupId);
|
||||
$return[$group->_id] = $group->loadTypesData();
|
||||
$group->reset();
|
||||
}
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* build search index from all systems data
|
||||
* @param int $offset
|
||||
* @param int $length
|
||||
* @return array
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function buildSystemsIndex(int $offset = 0, int $length = 10){
|
||||
/**
|
||||
* @var $system Model\Universe\SystemModel
|
||||
*/
|
||||
$system = Model\Universe\BasicUniverseModel::getNew('SystemModel');
|
||||
$systems = $system->find();
|
||||
$systemIds = $systems->getAll('id', true);
|
||||
sort($systemIds, SORT_NUMERIC);
|
||||
$systemsAll = count($systemIds);
|
||||
$systemIds = array_slice($systemIds, $offset, $length);
|
||||
foreach($systemIds as $systemId){
|
||||
$system->getById($systemId);
|
||||
$system->buildIndex();
|
||||
$system->reset();
|
||||
}
|
||||
return ['countAll' => $systemsAll, 'countBuild' => count($systemIds)];
|
||||
}
|
||||
|
||||
/**
|
||||
* get complete system index (all systems)
|
||||
* @return array
|
||||
*/
|
||||
public function getSystemsIndex() : array {
|
||||
$index = [];
|
||||
$cacheKeyTable = Model\Universe\BasicUniverseModel::generateHashKeyTable('system');
|
||||
if($this->getF3()->exists($cacheKeyTable,$cacheKeys)){
|
||||
foreach((array)$cacheKeys as $cacheKeyRow){
|
||||
if(($data = $this->get($cacheKeyRow)) && is_object($data)){
|
||||
$index[$data->id] = $data;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $index;
|
||||
}
|
||||
|
||||
/**
|
||||
* clear complete systems search index for all systems
|
||||
*/
|
||||
public function clearSystemsIndex(){
|
||||
$cacheKeyTable = Model\Universe\BasicUniverseModel::generateHashKeyTable('system');
|
||||
if($this->getF3()->exists($cacheKeyTable,$cacheKeys)){
|
||||
foreach((array)$cacheKeys as $cacheKeyRow) {
|
||||
$this->clear($cacheKeyRow);
|
||||
}
|
||||
$this->getF3()->clear($cacheKeyTable);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* get all constellations from CCP and store constellation data
|
||||
* @param \Base $f3
|
||||
* @throws \Exception
|
||||
* look for existing systemData in index
|
||||
* -> id is either a valid systemId OR systemName
|
||||
* @param int|string $id
|
||||
* @return null|\stdClass
|
||||
*/
|
||||
private function setupConstellations(\Base $f3){
|
||||
$this->getDB('UNIVERSE');
|
||||
$constellationIds = $f3->ccpClient->getConstellations();
|
||||
$constellationModel = BasicModel::getNew('Universe\ConstellationModel');
|
||||
protected function getSystemData($id){
|
||||
$data = null;
|
||||
if($id){
|
||||
$cacheKeyRow = Model\Universe\BasicUniverseModel::generateHashKeyRow('system', $id);
|
||||
$data = $this->get($cacheKeyRow);
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
foreach($constellationIds as $constellationId){
|
||||
$constellationModel->getById($constellationId);
|
||||
|
||||
if($constellationModel->dry()){
|
||||
$constellationData = $f3->ccpClient->getConstellationData($constellationId);
|
||||
|
||||
if( !empty($constellationData) ){
|
||||
// $constellationModel->copyfrom($constellationData, ['id', 'name', 'regionId']);
|
||||
$constellationModel->copyfrom($constellationData, function($fields){
|
||||
// add position coordinates as separate columns
|
||||
if(is_array($fields['position'])){
|
||||
$position = $fields['position'];
|
||||
if(
|
||||
isset($position['x']) &&
|
||||
isset($position['y']) &&
|
||||
isset($position['z'])
|
||||
){
|
||||
$fields['x'] = $position['x'];
|
||||
$fields['y'] = $position['y'];
|
||||
$fields['z'] = $position['z'];
|
||||
}
|
||||
}
|
||||
|
||||
// filter relevant data for insert
|
||||
return array_intersect_key($fields, array_flip(['id', 'name', 'regionId', 'x', 'y', 'z']));
|
||||
});
|
||||
|
||||
$constellationModel->save();
|
||||
}
|
||||
/**
|
||||
* look for existing cacheKey data
|
||||
* @param string $cacheKey
|
||||
* @return null|\stdClass
|
||||
*/
|
||||
protected function get(string $cacheKey){
|
||||
$data = null;
|
||||
if($this->getF3()->exists($cacheKey,$value)) {
|
||||
if(is_string($value) && strpos($value, Model\Universe\BasicUniverseModel::CACHE_KEY_PREFIX) === 0) {
|
||||
// value references an other cacheKey that holds data
|
||||
return $this->get($value);
|
||||
}elseif( !empty((array)$value) ){
|
||||
// stdClass data is not empty
|
||||
$data = (object)$value;
|
||||
}
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
$constellationModel->reset();
|
||||
/**
|
||||
* clear cacheKey
|
||||
* @param string $cacheKey
|
||||
*/
|
||||
protected function clear(string $cacheKey){
|
||||
if($this->getF3()->exists($cacheKey,$value)) {
|
||||
if(is_string($value) && strpos($value, Model\Universe\BasicUniverseModel::CACHE_KEY_PREFIX) === 0) {
|
||||
// value references another cacheKey -> clear that one as well
|
||||
$this->clear($value);
|
||||
}
|
||||
$this->getF3()->clear($cacheKey);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* search universeName data by search term
|
||||
* @param array $categories
|
||||
* @param string $search
|
||||
* @param bool $strict
|
||||
* @return array
|
||||
*/
|
||||
public static function searchUniverseNameData(array $categories, string $search, bool $strict = false) : array {
|
||||
$f3 = \Base::instance();
|
||||
$universeNameData = [];
|
||||
if( !empty($categories) && !empty($search)){
|
||||
$universeIds = $f3->ccpClient->search($categories, $search, $strict);
|
||||
if(isset($universeIds['error'])){
|
||||
// ESI error
|
||||
$universeNameData = $universeIds;
|
||||
}elseif( !empty($universeIds) ){
|
||||
$universeIds = Util::arrayFlattenByValue($universeIds);
|
||||
$universeNameData = $f3->ccpClient->getUniverseNamesData($universeIds);
|
||||
}
|
||||
}
|
||||
return $universeNameData;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
namespace Controller;
|
||||
|
||||
use Controller\Ccp\Universe;
|
||||
use data\filesystem\Search;
|
||||
use DB;
|
||||
use DB\SQL;
|
||||
@@ -69,15 +70,18 @@ class Setup extends Controller {
|
||||
'Model\WormholeModel',
|
||||
'Model\RightModel',
|
||||
'Model\RoleModel',
|
||||
'Model\StructureModel',
|
||||
|
||||
'Model\CharacterStatusModel',
|
||||
'Model\ConnectionScopeModel',
|
||||
'Model\StructureStatusModel',
|
||||
|
||||
'Model\CharacterMapModel',
|
||||
'Model\AllianceMapModel',
|
||||
'Model\CorporationMapModel',
|
||||
|
||||
'Model\CorporationRightModel',
|
||||
'Model\CorporationStructureModel',
|
||||
|
||||
'Model\UserCharacterModel',
|
||||
'Model\CharacterModel',
|
||||
@@ -105,9 +109,17 @@ class Setup extends Controller {
|
||||
'info' => [],
|
||||
'models' => [
|
||||
'Model\Universe\TypeModel',
|
||||
'Model\Universe\GroupModel',
|
||||
'Model\Universe\CategoryModel',
|
||||
'Model\Universe\StructureModel',
|
||||
//'Model\Universe\RegionModel',
|
||||
//'Model\Universe\ConstellationModel'
|
||||
// 'Model\Universe\WormholeModel',
|
||||
// 'Model\Universe\StargateModel',
|
||||
// 'Model\Universe\StarModel',
|
||||
// 'Model\Universe\PlanetModel',
|
||||
// 'Model\Universe\SystemModel',
|
||||
// 'Model\Universe\ConstellationModel',
|
||||
// 'Model\Universe\RegionModel',
|
||||
// 'Model\Universe\SystemStaticModel'
|
||||
],
|
||||
'tables' => []
|
||||
],
|
||||
@@ -219,9 +231,6 @@ class Setup extends Controller {
|
||||
case 'fixCols':
|
||||
$fixColumns = true;
|
||||
break;
|
||||
case 'buildIndex':
|
||||
$this->setupSystemJumpTable();
|
||||
break;
|
||||
case 'importTable':
|
||||
$this->importTable($params['model']);
|
||||
break;
|
||||
@@ -262,7 +271,7 @@ class Setup extends Controller {
|
||||
$f3->set('socketInformation', $this->getSocketInformation());
|
||||
|
||||
// set index information
|
||||
$f3->set('indexInformation', $this->getIndexData());
|
||||
$f3->set('indexInformation', $this->getIndexData($f3));
|
||||
|
||||
// set cache size
|
||||
$f3->set('cacheSize', $this->getCacheData($f3));
|
||||
@@ -927,7 +936,7 @@ class Setup extends Controller {
|
||||
foreach($requiredTables as $requiredTableName => $data){
|
||||
|
||||
$tableExists = false;
|
||||
$tableEmpty = true;
|
||||
$tableRows = 0;
|
||||
// Check if table status is OK (no errors/warnings,..)
|
||||
$tableStatusCheckCount = 0;
|
||||
|
||||
@@ -939,8 +948,7 @@ class Setup extends Controller {
|
||||
$tableModifierTemp = new MySQL\TableModifier($requiredTableName, $schema);
|
||||
$currentColumns = $tableModifierTemp->getCols(true);
|
||||
// get row count
|
||||
$countRes = $db->exec("SELECT COUNT(*) `num` FROM " . $db->quotekey($requiredTableName) );
|
||||
$tableEmpty = $countRes[0]['num'] > 0 ? false : true;
|
||||
$tableRows = $this->dbLib->getRowCount($requiredTableName, $dbKey);
|
||||
}else{
|
||||
// table missing
|
||||
$dbStatusCheckCount++;
|
||||
@@ -1119,7 +1127,7 @@ class Setup extends Controller {
|
||||
}
|
||||
|
||||
$dbStatusCheckCount += $tableStatusCheckCount;
|
||||
$requiredTables[$requiredTableName]['empty'] = $tableEmpty;
|
||||
$requiredTables[$requiredTableName]['rows'] = $tableRows;
|
||||
$requiredTables[$requiredTableName]['exists'] = $tableExists;
|
||||
$requiredTables[$requiredTableName]['statusCheckCount'] = $tableStatusCheckCount;
|
||||
}
|
||||
@@ -1334,160 +1342,152 @@ class Setup extends Controller {
|
||||
return $socketInformation;
|
||||
}
|
||||
|
||||
/** get indexed (cache) data information
|
||||
/**
|
||||
* get indexed (cache) data information
|
||||
* @param \Base $f3
|
||||
* @return array
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function getIndexData(){
|
||||
protected function getIndexData(\Base $f3){
|
||||
// active DB and tables are required for obtain index data
|
||||
if(!$this->databaseHasError){
|
||||
$categoryUniverseModel = Model\Universe\BasicUniverseModel::getNew('CategoryModel');
|
||||
//$systemUniverseModel = Model\Universe\BasicUniverseModel::getNew('SystemModel');
|
||||
$systemNeighbourModel = Model\BasicModel::getNew('SystemNeighbourModel');
|
||||
$wormholeModel = Model\BasicModel::getNew('WormholeModel');
|
||||
$systemWormholeModel = Model\BasicModel::getNew('SystemWormholeModel');
|
||||
$constellationWormholeModel = Model\BasicModel::getNew('ConstellationWormholeModel');
|
||||
|
||||
$indexInfo = [
|
||||
'SystemNeighbourModel' => [
|
||||
/*
|
||||
'Systems' => [
|
||||
'task' => [
|
||||
[
|
||||
'action' => 'clearIndex',
|
||||
'label' => 'Clear',
|
||||
'icon' => 'fa-times',
|
||||
'btn' => 'btn-danger'
|
||||
],[
|
||||
'action' => 'buildIndex',
|
||||
'label' => 'build',
|
||||
'label' => 'Build',
|
||||
'icon' => 'fa-sync',
|
||||
'btn' => 'btn-primary'
|
||||
]
|
||||
],
|
||||
'table' => Model\BasicModel::getNew('SystemNeighbourModel')->getTable(),
|
||||
'count' => $this->dbLib->getRowCount( Model\BasicModel::getNew('SystemNeighbourModel')->getTable() )
|
||||
'label' => 'build systems index',
|
||||
'countBuild' => count((new Universe())->getSystemsIndex()),
|
||||
'countAll' => $this->dbLib->getRowCount($systemUniverseModel->getTable(), 'UNIVERSE'),
|
||||
'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' => $categoryUniverseModel->getById(65, 0)->getTypesCount(false),
|
||||
'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' => $categoryUniverseModel->getById(6, 0)->getTypesCount(false),
|
||||
'countAll' => (int)$f3->get('REQUIREMENTS.DATA.SHIPS'),
|
||||
'tooltip' => 'import all ships types from ESI. Runtime: ~2min'
|
||||
], */
|
||||
// All following rows become deprecated
|
||||
'SystemNeighbourModel' => [
|
||||
'task' => [
|
||||
[
|
||||
'action' => 'buildIndex',
|
||||
'label' => 'Build',
|
||||
'icon' => 'fa-sync',
|
||||
'btn' => 'btn-primary'
|
||||
]
|
||||
],
|
||||
'label' => 'system_neighbour',
|
||||
'countBuild' => $this->dbLib->getRowCount($systemNeighbourModel->getTable()),
|
||||
'countAll' => 5214
|
||||
],
|
||||
'WormholeModel' => [
|
||||
'task' => [
|
||||
[
|
||||
'action' => 'exportTable',
|
||||
'label' => 'export',
|
||||
'label' => 'Export',
|
||||
'icon' => 'fa-download',
|
||||
'btn' => 'btn-default'
|
||||
],[
|
||||
'action' => 'importTable',
|
||||
'label' => 'import',
|
||||
'label' => 'Import',
|
||||
'icon' => 'fa-upload',
|
||||
'btn' => 'btn-primary'
|
||||
]
|
||||
],
|
||||
'table' => Model\BasicModel::getNew('WormholeModel')->getTable(),
|
||||
'count' => $this->dbLib->getRowCount( Model\BasicModel::getNew('WormholeModel')->getTable() )
|
||||
'label' => 'wormhole',
|
||||
'countBuild' => $this->dbLib->getRowCount($wormholeModel->getTable()),
|
||||
'countAll' => 89
|
||||
],
|
||||
'SystemWormholeModel' => [
|
||||
'task' => [
|
||||
[
|
||||
'action' => 'exportTable',
|
||||
'label' => 'export',
|
||||
'label' => 'Export',
|
||||
'icon' => 'fa-download',
|
||||
'btn' => 'btn-default'
|
||||
],[
|
||||
'action' => 'importTable',
|
||||
'label' => 'import',
|
||||
'label' => 'Import',
|
||||
'icon' => 'fa-upload',
|
||||
'btn' => 'btn-primary'
|
||||
]
|
||||
],
|
||||
'table' => Model\BasicModel::getNew('SystemWormholeModel')->getTable(),
|
||||
'count' => $this->dbLib->getRowCount( Model\BasicModel::getNew('SystemWormholeModel')->getTable() )
|
||||
'label' => 'system_wormhole',
|
||||
'countBuild' => $this->dbLib->getRowCount($systemWormholeModel->getTable()),
|
||||
'countAll' => 234
|
||||
],
|
||||
'ConstellationWormholeModel' => [
|
||||
'task' => [
|
||||
[
|
||||
'action' => 'exportTable',
|
||||
'label' => 'export',
|
||||
'label' => 'Export',
|
||||
'icon' => 'fa-download',
|
||||
'btn' => 'btn-default'
|
||||
],[
|
||||
'action' => 'importTable',
|
||||
'label' => 'import',
|
||||
'label' => 'Import',
|
||||
'icon' => 'fa-upload',
|
||||
'btn' => 'btn-primary'
|
||||
]
|
||||
],
|
||||
'table' => Model\BasicModel::getNew('ConstellationWormholeModel')->getTable(),
|
||||
'count' => $this->dbLib->getRowCount( Model\BasicModel::getNew('ConstellationWormholeModel')->getTable() )
|
||||
'label' => 'constellation_wormhole',
|
||||
'countBuild' => $this->dbLib->getRowCount( $constellationWormholeModel->getTable() ),
|
||||
'countAll' => 461
|
||||
]
|
||||
];
|
||||
}else{
|
||||
$indexInfo = [
|
||||
'SystemNeighbourModel' => [
|
||||
'task' => [],
|
||||
'table' => 'Fix database errors first!'
|
||||
'label' => 'Fix database errors first!'
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
//var_dump($indexInfo); die();
|
||||
return $indexInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
protected function setupSystemJumpTable(){
|
||||
$pfDB = $this->getDB('PF');
|
||||
$ccpDB = $this->getDB('CCP');
|
||||
|
||||
$query = "SELECT
|
||||
map_sys.solarSystemID system_id,
|
||||
map_sys.regionID region_id,
|
||||
map_sys.constellationID constellation_id,
|
||||
map_sys.solarSystemName system_name,
|
||||
ROUND( map_sys.security, 4) system_security,
|
||||
(
|
||||
SELECT
|
||||
GROUP_CONCAT( NULLIF(map_sys_inner.solarSystemName, NULL) SEPARATOR ':')
|
||||
FROM
|
||||
mapSolarSystemJumps map_jump INNER JOIN
|
||||
mapSolarSystems map_sys_inner ON
|
||||
map_sys_inner.solarSystemID = map_jump.toSolarSystemID
|
||||
WHERE
|
||||
map_jump.fromSolarSystemID = map_sys.solarSystemID
|
||||
) system_neighbours
|
||||
FROM
|
||||
mapSolarSystems map_sys
|
||||
HAVING
|
||||
-- skip systems without neighbors (e.g. WHs)
|
||||
system_neighbours IS NOT NULL
|
||||
";
|
||||
|
||||
$rows = $ccpDB->exec($query);
|
||||
|
||||
if(count($rows) > 0){
|
||||
// switch DB back to pathfinder DB
|
||||
|
||||
// clear cache table
|
||||
$pfDB->exec("TRUNCATE system_neighbour");
|
||||
|
||||
foreach($rows as $row){
|
||||
$pfDB->exec("
|
||||
INSERT INTO
|
||||
system_neighbour(
|
||||
regionId,
|
||||
constellationId,
|
||||
systemName,
|
||||
systemId,
|
||||
jumpNodes,
|
||||
trueSec
|
||||
)
|
||||
VALUES(
|
||||
:regionId,
|
||||
:constellationId,
|
||||
:systemName,
|
||||
:systemId,
|
||||
:jumpNodes,
|
||||
:trueSec
|
||||
)",
|
||||
[
|
||||
':regionId' => $row['region_id'],
|
||||
':constellationId' => $row['constellation_id'],
|
||||
':systemName' => $row['system_name'],
|
||||
':systemId' => $row['system_id'],
|
||||
':jumpNodes' => $row['system_neighbours'],
|
||||
':trueSec' => $row['system_security']
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* import table data from existing dump file (e.g *.csv)
|
||||
* @param string $modelClass
|
||||
|
||||
246
app/main/cron/universe.php
Normal file
246
app/main/cron/universe.php
Normal file
@@ -0,0 +1,246 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: exodu
|
||||
* Date: 19.05.2018
|
||||
* Time: 03:46
|
||||
*/
|
||||
|
||||
namespace Cron;
|
||||
|
||||
use Model;
|
||||
|
||||
class Universe {
|
||||
|
||||
const LOG_TEXT = '%s type: %s %s/%s peak: %s total: %s msg: %s';
|
||||
|
||||
|
||||
/**
|
||||
* format counter for output (min length)
|
||||
* @param int $counter
|
||||
* @return string
|
||||
*/
|
||||
private function formatCounterValue(int $counter){
|
||||
return str_pad($counter, 4, ' ', STR_PAD_LEFT);
|
||||
}
|
||||
|
||||
/**
|
||||
* format id for output (min length)
|
||||
* @param int $counter
|
||||
* @return string
|
||||
*/
|
||||
private function formatIdValue(int $counter){
|
||||
return str_pad($counter, 10, ' ', STR_PAD_LEFT);
|
||||
}
|
||||
|
||||
/**
|
||||
* format Byte §size for output
|
||||
* @param int $size
|
||||
* @return string
|
||||
*/
|
||||
private function formatMemoryValue(int $size){
|
||||
$unit = ['B','KB','MB','GB','TB','PB'];
|
||||
return str_pad(number_format(@round($size/pow(1024,($i=floor(log($size,1024)))),2), 2, '.', '') . '' . $unit[$i], 9, ' ', STR_PAD_LEFT);
|
||||
}
|
||||
|
||||
/**
|
||||
* format seconds for output
|
||||
* @param float $time
|
||||
* @return string
|
||||
*/
|
||||
private function formatSeconds(float $time){
|
||||
$time = round($time, 5);
|
||||
$formatSeconds = function($seconds){
|
||||
return str_pad(number_format(round($seconds, 5), 5), 8, ' ', STR_PAD_LEFT);
|
||||
};
|
||||
|
||||
$formatted = $time < 60 ? $formatSeconds($time) . 's' : floor($time / 60) . 'm ' . $formatSeconds(fmod($time, 60)) . 's';
|
||||
return str_pad($formatted, 14, ' ', STR_PAD_LEFT);
|
||||
}
|
||||
|
||||
/**
|
||||
* flush output
|
||||
*/
|
||||
private function echoFlush(){
|
||||
flush();
|
||||
ob_flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* echo input parameters
|
||||
* @param string $type
|
||||
* @param int $paramOffset
|
||||
* @param int $paramLength
|
||||
*/
|
||||
private function echoParams(string $type, int $paramOffset, int $paramLength){
|
||||
echo 'params ───────────────────────────────────────────────────────────────────────────────────────────────────────' . PHP_EOL;
|
||||
echo 'type : ' . $type . PHP_EOL;
|
||||
echo 'offset : ' . $paramOffset . PHP_EOL;
|
||||
echo 'length : ' . $paramLength . PHP_EOL;
|
||||
$this->echoFlush();
|
||||
}
|
||||
|
||||
/**
|
||||
* echo configuration
|
||||
*/
|
||||
private function echoConfig(){
|
||||
echo 'config ───────────────────────────────────────────────────────────────────────────────────────────────────────' . PHP_EOL;
|
||||
echo 'max_execution_time : ' . ini_get('max_execution_time') . PHP_EOL;
|
||||
echo 'memory_limit : ' . ini_get('memory_limit') . PHP_EOL;
|
||||
$this->echoFlush();
|
||||
}
|
||||
|
||||
/**
|
||||
* echo information
|
||||
* @param int $total
|
||||
* @param int $offset
|
||||
* @param int $importCount
|
||||
* @param array $ids
|
||||
*/
|
||||
private function echoInfo(int $total, int $offset, int $importCount, array $ids){
|
||||
echo 'info ─────────────────────────────────────────────────────────────────────────────────────────────────────────' . PHP_EOL;
|
||||
echo 'all data : ' . $total . PHP_EOL;
|
||||
echo 'import offset : ' . $offset . PHP_EOL;
|
||||
echo 'import count : ' . $importCount . PHP_EOL;
|
||||
echo 'import chunk : ' . implode(',', $ids) . PHP_EOL;
|
||||
$this->echoFlush();
|
||||
}
|
||||
|
||||
/**
|
||||
* echo start
|
||||
*/
|
||||
private function echoStart(){
|
||||
echo 'start ────────────────────────────────────────────────────────────────────────────────────────────────────────' . PHP_EOL;
|
||||
$this->echoFlush();
|
||||
}
|
||||
|
||||
/**
|
||||
* echo loop start information
|
||||
* @param int $count
|
||||
* @param int $importCount
|
||||
* @param int $id
|
||||
*/
|
||||
private function echoLoading(int $count, int $importCount, int $id){
|
||||
echo '[' . date('H:i:s') . '] loading... ' . $this->formatCounterValue($count) . '/' . $importCount . ' id: ' . $this->formatIdValue($id) . PHP_EOL;
|
||||
$this->echoFlush();
|
||||
}
|
||||
|
||||
/**
|
||||
* echo loop finish information
|
||||
* @param int $importCount
|
||||
* @param int $id
|
||||
* @param float $timeLoopStart
|
||||
* @param float $timeTotalStart
|
||||
*/
|
||||
private function echoLoaded(int $importCount, int $id, float $timeLoopStart, float $timeTotalStart){
|
||||
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;
|
||||
$this->echoFlush();
|
||||
}
|
||||
|
||||
/**
|
||||
* echo finish information
|
||||
* @param int $count
|
||||
* @param int $importCount
|
||||
* @param float $timeTotalStart
|
||||
*/
|
||||
private function echoFinish(int $count, int $importCount, float $timeTotalStart){
|
||||
echo 'finished ─────────────────────────────────────────────────────────────────────────────────────────────────────' . PHP_EOL;
|
||||
echo '[' . date('H:i:s') . '] ' . $this->formatCounterValue($count) . '/' . $importCount .
|
||||
' peak: ' . $this->formatMemoryValue(memory_get_peak_usage ()) .
|
||||
' total: ' . $this->formatSeconds(microtime(true) - $timeTotalStart) . PHP_EOL;
|
||||
$this->echoFlush();
|
||||
}
|
||||
|
||||
/**
|
||||
* imports static universe data from ESI
|
||||
* >> php index.php "/cron/setup?type=system&offset=0&length=5"
|
||||
* @param \Base $f3
|
||||
* @throws \Exception
|
||||
*/
|
||||
function setup(\Base $f3){
|
||||
$params = (array)$f3->get('GET');
|
||||
$type = (string)$params['type'];
|
||||
$paramOffset = (int)$params['offset'];
|
||||
$paramLength = (int)$params['length'];
|
||||
$timeTotalStart = microtime(true);
|
||||
$msg = '';
|
||||
|
||||
$ids = [];
|
||||
$count = 0;
|
||||
$importCount = [];
|
||||
$modelClass = '';
|
||||
$setupModel = function(Model\Universe\BasicUniverseModel &$model, int $id){};
|
||||
|
||||
switch($type){
|
||||
case 'system':
|
||||
// load systems + dependencies (planets, star, types,...)
|
||||
$ids = $f3->ccpClient->getUniverseSystems();
|
||||
$modelClass = 'SystemModel';
|
||||
$setupModel = function(Model\Universe\SystemModel &$model, int $id){
|
||||
$model->loadById($id);
|
||||
$model->loadPlanetsData();
|
||||
};
|
||||
break;
|
||||
case 'stargate':
|
||||
// load all stargates. Systems must be present first!
|
||||
$ids = $f3->ccpClient->getUniverseSystems();
|
||||
$modelClass = 'SystemModel';
|
||||
$setupModel = function(Model\Universe\SystemModel &$model, int $id){
|
||||
$model->loadById($id);
|
||||
$model->loadStargatesData();
|
||||
};
|
||||
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->buildIndex();
|
||||
};
|
||||
break;
|
||||
default:
|
||||
$msg = 'Model is not valid';
|
||||
}
|
||||
|
||||
if($modelClass){
|
||||
$this->echoParams($type, $paramOffset, $paramLength);
|
||||
$this->echoConfig();
|
||||
|
||||
$total = count($ids);
|
||||
$offset = ($paramOffset < 0) ? 0 : (($paramOffset >= $total) ? $total : $paramOffset);
|
||||
$length = ($paramLength < 0) ? 0 : $paramLength;
|
||||
sort($ids, SORT_NUMERIC);
|
||||
$ids = array_slice($ids, $offset, $length);
|
||||
$importCount = count($ids);
|
||||
|
||||
$this->echoInfo($total, $offset, $importCount, $ids);
|
||||
$this->echoStart();
|
||||
|
||||
/**
|
||||
* @var $model Model\Universe\SystemModel
|
||||
*/
|
||||
$model = Model\Universe\BasicUniverseModel::getNew($modelClass);
|
||||
foreach($ids as $id){
|
||||
$timeLoopStart = microtime(true);
|
||||
$this->echoLoading(++$count, $importCount, $id);
|
||||
$setupModel($model, $id);
|
||||
$model->reset();
|
||||
$this->echoLoaded($importCount, $id, $timeLoopStart, $timeTotalStart);
|
||||
}
|
||||
|
||||
$this->echoFinish($count, $importCount, $timeTotalStart);
|
||||
}
|
||||
|
||||
// Log --------------------------------------------------------------------------------------------------------
|
||||
$log = new \Log('cron_' . __FUNCTION__ . '.log');
|
||||
$log->write( sprintf(self::LOG_TEXT, __FUNCTION__, $type,
|
||||
$this->formatCounterValue($count), $importCount, $this->formatMemoryValue(memory_get_peak_usage ()),
|
||||
$this->formatSeconds(microtime(true) - $timeTotalStart), $msg) );
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -65,8 +65,10 @@ class AbstractIterator extends \RecursiveArrayIterator {
|
||||
$mapValue = static::$map[$iterator->key()];
|
||||
|
||||
// check for mapping key
|
||||
if($iterator->hasChildren()){
|
||||
|
||||
if(
|
||||
$iterator->hasChildren() &&
|
||||
Util::is_assoc($iterator->current())
|
||||
){
|
||||
// recursive call for child elements
|
||||
$iterator->offsetSet($iterator->key(), forward_static_call(array('self', __METHOD__), $iterator->getChildren())->getArrayCopy());
|
||||
$iterator->next();
|
||||
|
||||
@@ -64,6 +64,12 @@ class CcpSystemsMapper extends AbstractIterator {
|
||||
$security = '';
|
||||
|
||||
if(
|
||||
$iterator['constellation']['id'] >= 22000001 &&
|
||||
$iterator['constellation']['id'] <= 22000025
|
||||
){
|
||||
// "Abyssal" system
|
||||
$security = 'A';
|
||||
}elseif(
|
||||
$iterator['security'] == 7 ||
|
||||
$iterator['security'] == 8 ||
|
||||
$iterator['security'] == 9
|
||||
@@ -105,13 +111,19 @@ class CcpSystemsMapper extends AbstractIterator {
|
||||
$type = 'w-space';
|
||||
$typeId = 1;
|
||||
if(
|
||||
$iterator['constellation']['id'] >= 22000001 &&
|
||||
$iterator['constellation']['id'] <= 22000025
|
||||
){
|
||||
// "Abyssal" system
|
||||
$type = 'a-space';
|
||||
$typeId = 3;
|
||||
}elseif(
|
||||
$iterator['security'] == 7 ||
|
||||
$iterator['security'] == 8 ||
|
||||
$iterator['security'] == 9
|
||||
){
|
||||
$type = 'k-space';
|
||||
$typeId = 2;
|
||||
|
||||
}
|
||||
|
||||
return [
|
||||
|
||||
@@ -35,6 +35,7 @@ class CcpClient extends \Prefab {
|
||||
$client->setDatasource( Config::getEnvironmentData('CCP_ESI_DATASOURCE') );
|
||||
$client->setUserAgent($this->getUserAgent());
|
||||
$client->setDebugLevel($f3->get('DEBUG'));
|
||||
//$client->setDebugLogRequests(true);
|
||||
}else{
|
||||
LogController::getLogger('ERROR')->write(sprintf(Config::ERROR_CLASS_NOT_EXISTS_COMPOSER, ApiClient::class));
|
||||
}
|
||||
|
||||
@@ -380,12 +380,12 @@ class Config extends \Prefab {
|
||||
|
||||
/**
|
||||
* @param string $key
|
||||
* @return mixed
|
||||
* @return null|mixed
|
||||
* @throws Exception\PathfinderException
|
||||
*/
|
||||
static function getPathfinderData($key = ''){
|
||||
$hiveKey = self::HIVE_KEY_PATHFINDER . ($key ? '.' . strtoupper($key) : '');
|
||||
|
||||
$data = null; // make sure it is always defined
|
||||
try{
|
||||
if( !\Base::instance()->exists($hiveKey, $data) ){
|
||||
throw new Exception\PathfinderException(sprintf(self::ERROR_CONF_PATHFINDER, $hiveKey));
|
||||
|
||||
@@ -29,12 +29,23 @@ class Util {
|
||||
}
|
||||
|
||||
/**
|
||||
* flatten multidimensional array
|
||||
* flatten multidimensional array ignore keys
|
||||
* @param array $array
|
||||
* @return array
|
||||
*/
|
||||
static function arrayFlattenByValue(array $array) : array {
|
||||
$return = [];
|
||||
array_walk_recursive($array, function($value) use (&$return) { $return[] = $value; });
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* flatten multidimensional array merge keys
|
||||
* -> overwrites duplicate keys!
|
||||
* @param array $array
|
||||
* @return array
|
||||
*/
|
||||
static function arrayFlatten(array $array) : array {
|
||||
static function arrayFlattenByKey(array $array) : array {
|
||||
$return = [];
|
||||
array_walk_recursive($array, function($value, $key) use (&$return) { $return[$key] = $value; });
|
||||
return $return;
|
||||
|
||||
@@ -47,32 +47,6 @@ abstract class AbstractMapTrackingModel extends BasicModel implements LogModelIn
|
||||
return array_merge(parent::getStaticFieldConf(), $this->trackingFieldConf);
|
||||
}
|
||||
|
||||
/**
|
||||
* validates a model field to be a valid relational model
|
||||
* @param $key
|
||||
* @param $val
|
||||
* @return bool
|
||||
* @throws \Exception\ValidationException
|
||||
*/
|
||||
protected function validate_notDry($key, $val): bool {
|
||||
$valid = true;
|
||||
if($colConf = $this->fieldConf[$key]){
|
||||
if(isset($colConf['belongs-to-one'])){
|
||||
if( (is_int($val) || ctype_digit($val)) && (int)$val > 0){
|
||||
$valid = true;
|
||||
}elseif( is_a($val, $colConf['belongs-to-one']) && !$val->dry() ){
|
||||
$valid = true;
|
||||
}else{
|
||||
$valid = false;
|
||||
$msg = 'Validation failed: "' . get_class($this) . '->' . $key . '" must be a valid instance of ' . $colConf['belongs-to-one'];
|
||||
$this->throwValidationException($key, $msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $valid;
|
||||
}
|
||||
|
||||
/**
|
||||
* log character activity create/update/delete events
|
||||
* @param string $action
|
||||
|
||||
@@ -27,6 +27,11 @@ class AllianceModel extends BasicModel {
|
||||
'nullable' => false,
|
||||
'default' => ''
|
||||
],
|
||||
'ticker' => [
|
||||
'type' => Schema::DT_VARCHAR128,
|
||||
'nullable' => false,
|
||||
'default' => ''
|
||||
],
|
||||
'shared' => [
|
||||
'type' => Schema::DT_BOOL,
|
||||
'nullable' => false,
|
||||
@@ -54,6 +59,21 @@ class AllianceModel extends BasicModel {
|
||||
return $allianceData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Event "Hook" function
|
||||
* return false will stop any further action
|
||||
* @param self $self
|
||||
* @param $pkeys
|
||||
* @return bool
|
||||
*/
|
||||
public function beforeUpdateEvent($self, $pkeys){
|
||||
// if model changed, 'update' col needs to be updated as well
|
||||
// -> data no longer "outdated"
|
||||
$this->touch('updated');
|
||||
|
||||
return parent::beforeUpdateEvent($self, $pkeys);
|
||||
}
|
||||
|
||||
/**
|
||||
* get all maps for this alliance
|
||||
* @return array|mixed
|
||||
@@ -87,9 +107,9 @@ class AllianceModel extends BasicModel {
|
||||
* get all characters in this alliance
|
||||
* @param array $characterIds
|
||||
* @param array $options
|
||||
* @return array
|
||||
* @return CharacterModel[]
|
||||
*/
|
||||
public function getCharacters($characterIds = [], $options = []){
|
||||
public function getCharacters($characterIds = [], $options = []) : array {
|
||||
$characters = [];
|
||||
$filter = ['active = ?', 1];
|
||||
|
||||
@@ -114,4 +134,21 @@ class AllianceModel extends BasicModel {
|
||||
|
||||
return $characters;
|
||||
}
|
||||
|
||||
public function getById(int $id, int $ttl = self::DEFAULT_SQL_TTL, bool $isActive = true){
|
||||
/**
|
||||
* @var AllianceModel $alliance
|
||||
*/
|
||||
$alliance = parent::getById($id, $ttl, $isActive);
|
||||
if($alliance->isOutdated()){
|
||||
// request alliance data
|
||||
$allianceData = self::getF3()->ccpClient->getAllianceData($id);
|
||||
if( !empty($allianceData) ){
|
||||
$alliance->copyfrom($allianceData, ['id', 'name', 'ticker']);
|
||||
$alliance->save();
|
||||
}
|
||||
}
|
||||
|
||||
return $alliance;
|
||||
}
|
||||
}
|
||||
@@ -104,9 +104,14 @@ abstract class BasicModel extends \DB\Cortex {
|
||||
*/
|
||||
const DEFAULT_CACHE_TTL = 120;
|
||||
|
||||
/**
|
||||
* default TTL for SQL query cache
|
||||
*/
|
||||
const DEFAULT_SQL_TTL = 3;
|
||||
|
||||
const ERROR_INVALID_MODEL_CLASS = 'Model class (%s) not found';
|
||||
|
||||
public function __construct($db = NULL, $table = NULL, $fluid = NULL, $ttl = 0){
|
||||
public function __construct($db = NULL, $table = NULL, $fluid = NULL, $ttl = self::DEFAULT_TTL){
|
||||
|
||||
$this->addStaticFieldConfig();
|
||||
|
||||
@@ -313,6 +318,32 @@ abstract class BasicModel extends \DB\Cortex {
|
||||
return $valid;
|
||||
}
|
||||
|
||||
/**
|
||||
* validates a model field to be a valid relational model
|
||||
* @param $key
|
||||
* @param $val
|
||||
* @return bool
|
||||
* @throws \Exception\ValidationException
|
||||
*/
|
||||
protected function validate_notDry($key, $val): bool {
|
||||
$valid = true;
|
||||
if($colConf = $this->fieldConf[$key]){
|
||||
if(isset($colConf['belongs-to-one'])){
|
||||
if( (is_int($val) || ctype_digit($val)) && (int)$val > 0){
|
||||
$valid = true;
|
||||
}elseif( is_a($val, $colConf['belongs-to-one']) && !$val->dry() ){
|
||||
$valid = true;
|
||||
}else{
|
||||
$valid = false;
|
||||
$msg = 'Validation failed: "' . get_class($this) . '->' . $key . '" must be a valid instance of ' . $colConf['belongs-to-one'];
|
||||
$this->throwValidationException($key, $msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $valid;
|
||||
}
|
||||
|
||||
/**
|
||||
* get key for for all objects in this table
|
||||
* @return string
|
||||
@@ -464,7 +495,7 @@ abstract class BasicModel extends \DB\Cortex {
|
||||
* @param bool $isActive
|
||||
* @return \DB\Cortex
|
||||
*/
|
||||
public function getById(int $id, int $ttl = 3, bool $isActive = true){
|
||||
public function getById(int $id, int $ttl = self::DEFAULT_SQL_TTL, bool $isActive = true){
|
||||
return $this->getByForeignKey('id', (int)$id, ['limit' => 1], $ttl, $isActive);
|
||||
}
|
||||
|
||||
@@ -614,15 +645,21 @@ abstract class BasicModel extends \DB\Cortex {
|
||||
/**
|
||||
* export and download table data as *.csv
|
||||
* this is primarily used for static tables
|
||||
* @param array $fields
|
||||
* @return bool
|
||||
*/
|
||||
public function exportData(){
|
||||
public function exportData(array $fields = []){
|
||||
$status = false;
|
||||
|
||||
if(static::$enableDataExport){
|
||||
$tableModifier = static::getTableModifier();
|
||||
$headers = $tableModifier->getCols();
|
||||
|
||||
if($fields){
|
||||
// columns to export -> reIndex keys
|
||||
$headers = array_values(array_intersect($headers, $fields));
|
||||
}
|
||||
|
||||
// just get the records with existing columns
|
||||
// -> no "virtual" fields or "new" columns
|
||||
$this->fields($headers);
|
||||
@@ -654,14 +691,16 @@ abstract class BasicModel extends \DB\Cortex {
|
||||
|
||||
/**
|
||||
* import table data from a *.csv file
|
||||
* @return bool
|
||||
* @return array|bool
|
||||
*/
|
||||
public function importData(){
|
||||
$status = false;
|
||||
|
||||
// rtrim(); for arrays (removes empty values) from the end
|
||||
$rtrim = function($array = []){
|
||||
return array_slice($array, 0, key(array_reverse(array_diff($array, ['']), 1))+1);
|
||||
$rtrim = function($array = [], $lengthMin = false){
|
||||
$length = key(array_reverse(array_diff($array, ['']), 1))+1;
|
||||
$length = $length < $lengthMin ? $lengthMin : $length;
|
||||
return array_slice($array, 0, $length);
|
||||
};
|
||||
|
||||
if(static::$enableDataImport){
|
||||
@@ -675,7 +714,7 @@ abstract class BasicModel extends \DB\Cortex {
|
||||
if(count($keys) > 0){
|
||||
$tableData = [];
|
||||
while (!feof($handle)) {
|
||||
$tableData[] = array_combine($keys, $rtrim(fgetcsv($handle, 0, ';')));
|
||||
$tableData[] = array_combine($keys, $rtrim(fgetcsv($handle, 0, ';'), count($keys)));
|
||||
}
|
||||
// import row data
|
||||
$status = $this->importStaticData($tableData);
|
||||
@@ -699,20 +738,22 @@ abstract class BasicModel extends \DB\Cortex {
|
||||
*/
|
||||
protected function importStaticData($tableData = []){
|
||||
$rowIDs = [];
|
||||
|
||||
$addedCount = 0;
|
||||
$updatedCount = 0;
|
||||
$deletedCount = 0;
|
||||
|
||||
$tableModifier = static::getTableModifier();
|
||||
$fields = $tableModifier->getCols();
|
||||
|
||||
foreach($tableData as $rowData){
|
||||
// search for existing record and update columns
|
||||
$this->getById($rowData['id']);
|
||||
$this->getById($rowData['id'], 0);
|
||||
if($this->dry()){
|
||||
$addedCount++;
|
||||
}else{
|
||||
$updatedCount++;
|
||||
}
|
||||
$this->copyfrom($rowData);
|
||||
$this->copyfrom($rowData, $fields);
|
||||
$this->save();
|
||||
$rowIDs[] = $this->id;
|
||||
$this->reset();
|
||||
@@ -763,6 +804,28 @@ abstract class BasicModel extends \DB\Cortex {
|
||||
return $this->validationError;
|
||||
}
|
||||
|
||||
/**
|
||||
* checks whether data is outdated and should be refreshed
|
||||
* @return bool
|
||||
*/
|
||||
protected function isOutdated(): bool {
|
||||
$outdated = true;
|
||||
if(!$this->dry()){
|
||||
$timezone = $this->getF3()->get('getTimeZone')();
|
||||
$currentTime = new \DateTime('now', $timezone);
|
||||
$updateTime = \DateTime::createFromFormat(
|
||||
'Y-m-d H:i:s',
|
||||
$this->updated,
|
||||
$timezone
|
||||
);
|
||||
$interval = $updateTime->diff($currentTime);
|
||||
if($interval->days < Universe\BasicUniverseModel::CACHE_MAX_DAYS){
|
||||
$outdated = false;
|
||||
}
|
||||
}
|
||||
return $outdated;
|
||||
}
|
||||
|
||||
public function save(){
|
||||
try{
|
||||
return parent::save();
|
||||
@@ -818,6 +881,21 @@ abstract class BasicModel extends \DB\Cortex {
|
||||
return \Base::instance();
|
||||
}
|
||||
|
||||
/**
|
||||
* stores data direct into the Cache backend (e.g. Redis)
|
||||
* $f3->set() used the same code. The difference is, that $f3->set()
|
||||
* also loads data into the Hive.
|
||||
* This can result in high RAM usage if a great number of key->values should be stored in Cache
|
||||
* (like the search index for system data)
|
||||
* @param string $key
|
||||
* @param $data
|
||||
* @param int $ttl
|
||||
*/
|
||||
public static function setCacheValue(string $key, $data, int $ttl = 0){
|
||||
$cache = \Cache::instance();
|
||||
$cache->set(self::getF3()->hash($key).'.var', $data, $ttl);
|
||||
}
|
||||
|
||||
/**
|
||||
* debug log function
|
||||
* @param string $text
|
||||
|
||||
@@ -32,6 +32,7 @@ class CharacterModel extends BasicModel {
|
||||
const AUTHORIZATION_STATUS = [
|
||||
'OK' => true, // success
|
||||
'UNKNOWN' => 'error', // general authorization error
|
||||
'CHARACTER' => 'failed to match character whitelist',
|
||||
'CORPORATION' => 'failed to match corporation whitelist',
|
||||
'ALLIANCE' => 'failed to match alliance whitelist',
|
||||
'KICKED' => 'character is kicked',
|
||||
@@ -180,7 +181,7 @@ class CharacterModel extends BasicModel {
|
||||
// no cached character data found
|
||||
|
||||
$characterData = (object) [];
|
||||
$characterData->id = $this->id;
|
||||
$characterData->id = $this->_id;
|
||||
$characterData->name = $this->name;
|
||||
$characterData->role = $this->roleId->getData();
|
||||
$characterData->shared = $this->shared;
|
||||
@@ -552,25 +553,39 @@ class CharacterModel extends BasicModel {
|
||||
// check whether character is banned or temp kicked
|
||||
if(is_null($this->banned)){
|
||||
if( !$this->isKicked() ){
|
||||
$whitelistCharacter = array_filter( array_map('trim', (array)Config::getPathfinderData('login.character') ) );
|
||||
$whitelistCorporations = array_filter( array_map('trim', (array)Config::getPathfinderData('login.corporation') ) );
|
||||
$whitelistAlliance = array_filter( array_map('trim', (array)Config::getPathfinderData('login.alliance') ) );
|
||||
|
||||
if(
|
||||
empty($whitelistCharacter) &&
|
||||
empty($whitelistCorporations) &&
|
||||
empty($whitelistAlliance)
|
||||
){
|
||||
// no corp/ally restrictions set -> any character is allowed to login
|
||||
$authStatus = 'OK';
|
||||
}else{
|
||||
// check if character is set in whitelist
|
||||
if(
|
||||
!empty($whitelistCharacter) &&
|
||||
in_array((int)$this->_id, $whitelistCharacter)
|
||||
){
|
||||
$authStatus = 'OK';
|
||||
}else{
|
||||
$authStatus = 'CHARACTER';
|
||||
}
|
||||
|
||||
// check if character corporation is set in whitelist
|
||||
if(
|
||||
$authStatus != 'OK' &&
|
||||
!empty($whitelistCorporations) &&
|
||||
$this->hasCorporation() &&
|
||||
in_array((int)$this->get('corporationId', true), $whitelistCorporations)
|
||||
$this->hasCorporation()
|
||||
){
|
||||
$authStatus = 'OK';
|
||||
}else{
|
||||
$authStatus = 'CORPORATION';
|
||||
if( in_array((int)$this->get('corporationId', true), $whitelistCorporations) ){
|
||||
$authStatus = 'OK';
|
||||
}else{
|
||||
$authStatus = 'CORPORATION';
|
||||
}
|
||||
}
|
||||
|
||||
// check if character alliance is set in whitelist
|
||||
@@ -752,7 +767,16 @@ class CharacterModel extends BasicModel {
|
||||
if( !empty($lookupUniverseIds) ){
|
||||
// get "more" information for some Ids (e.g. name)
|
||||
$universeData = self::getF3()->ccpClient->getUniverseNamesData($lookupUniverseIds, $additionalOptions);
|
||||
if( !empty($universeData) ){
|
||||
|
||||
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{
|
||||
// this is important! universe data is a MUST HAVE!
|
||||
|
||||
@@ -141,7 +141,7 @@ class ConnectionModel extends AbstractMapTrackingModel {
|
||||
* @return int|number
|
||||
*/
|
||||
public function set_type($type){
|
||||
$newTypes = (array)json_decode($type);
|
||||
$newTypes = (array)$type;
|
||||
|
||||
// set EOL timestamp
|
||||
if( !in_array('wh_eol', $newTypes) ){
|
||||
@@ -172,21 +172,30 @@ class ConnectionModel extends AbstractMapTrackingModel {
|
||||
|
||||
/**
|
||||
* set default connection type by search route between endpoints
|
||||
* @throws \Exception\PathfinderException
|
||||
*/
|
||||
public function setDefaultTypeData(){
|
||||
if(
|
||||
is_object($this->source) &&
|
||||
is_object($this->target)
|
||||
){
|
||||
$routeController = new Route();
|
||||
$route = $routeController->searchRoute($this->source->systemId, $this->target->systemId, 1);
|
||||
|
||||
if($route['routePossible']){
|
||||
$this->scope = 'stargate';
|
||||
$this->type = ['stargate'];
|
||||
if(
|
||||
$this->source->isAbyss() ||
|
||||
$this->target->isAbyss()
|
||||
){
|
||||
$this->scope = 'abyssal';
|
||||
$this->type = ['abyssal'];
|
||||
}else{
|
||||
$this->scope = 'wh';
|
||||
$this->type = ['wh_fresh'];
|
||||
$routeController = new Route();
|
||||
$route = $routeController->searchRoute($this->source->systemId, $this->target->systemId, 1);
|
||||
|
||||
if($route['routePossible']){
|
||||
$this->scope = 'stargate';
|
||||
$this->type = ['stargate'];
|
||||
}else{
|
||||
$this->scope = 'wh';
|
||||
$this->type = ['wh_fresh'];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -222,18 +231,19 @@ class ConnectionModel extends AbstractMapTrackingModel {
|
||||
}
|
||||
|
||||
/**
|
||||
* Event "Hook" function
|
||||
* Event "Hook" function
|
||||
* can be overwritten
|
||||
* return false will stop any further action
|
||||
* @param ConnectionModel $self
|
||||
* @param BasicModel $self
|
||||
* @param $pkeys
|
||||
* @return bool
|
||||
* @throws \Exception\DatabaseException
|
||||
* @throws \Exception\PathfinderException
|
||||
*/
|
||||
public function beforeInsertEvent($self, $pkeys){
|
||||
// check for "default" connection type and add them if missing
|
||||
// -> get() with "true" returns RAW data! important for JSON table column check!
|
||||
$types = (array)json_decode( $this->get('type', true) );
|
||||
$types = (array)json_decode($this->get('type', true));
|
||||
if(
|
||||
!$this->scope ||
|
||||
empty($types)
|
||||
@@ -379,7 +389,7 @@ class ConnectionModel extends AbstractMapTrackingModel {
|
||||
* get all connection log data linked to this connection
|
||||
* @return array
|
||||
*/
|
||||
public function getLogsData() : array{
|
||||
public function getLogsData() : array {
|
||||
$logsData = [];
|
||||
$logs = $this->getLogs();
|
||||
|
||||
@@ -411,6 +421,7 @@ class ConnectionModel extends AbstractMapTrackingModel {
|
||||
* @param null $table
|
||||
* @param null $fields
|
||||
* @return bool
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function setup($db=null, $table=null, $fields=null){
|
||||
$status = parent::setup($db,$table,$fields);
|
||||
|
||||
@@ -56,6 +56,12 @@ class ConnectionScopeModel extends BasicModel{
|
||||
'name' => 'jumpbridge',
|
||||
'label' => 'jumpbridge',
|
||||
'connectorDefinition' => '[ "Straight", { "stub": [5, 5], "gap": 0 } ]'
|
||||
],
|
||||
[
|
||||
'id' => 4,
|
||||
'name' => 'abyssal',
|
||||
'label' => 'abyssal',
|
||||
'connectorDefinition' => '[ "Straight", { "stub": [5, 5], "gap": 0 } ]'
|
||||
]
|
||||
|
||||
];
|
||||
|
||||
@@ -103,6 +103,16 @@ class CorporationModel extends BasicModel {
|
||||
'nullable' => false,
|
||||
'default' => ''
|
||||
],
|
||||
'ticker' => [
|
||||
'type' => Schema::DT_VARCHAR128,
|
||||
'nullable' => false,
|
||||
'default' => ''
|
||||
],
|
||||
'memberCount' => [
|
||||
'type' => Schema::DT_INT,
|
||||
'nullable' => false,
|
||||
'default' => 0
|
||||
],
|
||||
'shared' => [
|
||||
'type' => Schema::DT_BOOL,
|
||||
'nullable' => false,
|
||||
@@ -121,6 +131,12 @@ class CorporationModel extends BasicModel {
|
||||
],
|
||||
'corporationRights' => [
|
||||
'has-many' => ['Model\CorporationRightModel', 'corporationId']
|
||||
],
|
||||
'corporationStructures' => [
|
||||
'has-many' => ['Model\CorporationStructureModel', 'corporationId']
|
||||
],
|
||||
'structures' => [
|
||||
'has-many' => ['Model\StructureModel', 'corporationId']
|
||||
]
|
||||
];
|
||||
|
||||
@@ -145,6 +161,21 @@ class CorporationModel extends BasicModel {
|
||||
return $cooperationData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Event "Hook" function
|
||||
* return false will stop any further action
|
||||
* @param self $self
|
||||
* @param $pkeys
|
||||
* @return bool
|
||||
*/
|
||||
public function beforeUpdateEvent($self, $pkeys){
|
||||
// if model changed, 'update' col needs to be updated as well
|
||||
// -> data no longer "outdated"
|
||||
$this->touch('updated');
|
||||
|
||||
return parent::beforeUpdateEvent($self, $pkeys);
|
||||
}
|
||||
|
||||
/**
|
||||
* get all maps for this corporation
|
||||
* @param array $mapIds
|
||||
@@ -210,6 +241,38 @@ class CorporationModel extends BasicModel {
|
||||
return $characters;
|
||||
}
|
||||
|
||||
/**
|
||||
* get all structure data for this corporation
|
||||
* @param array $systemIds
|
||||
* @return array
|
||||
*/
|
||||
public function getStructuresData(array $systemIds = []) : array {
|
||||
$structuresData = [];
|
||||
|
||||
$this->filter('corporationStructures', ['active = ?', 1]);
|
||||
$this->has('corporationStructures.structureId', ['active = ?', 1]);
|
||||
|
||||
if($systemIds){
|
||||
if(count($systemIds) == 1){
|
||||
$filterSystems = 'systemId = ?';
|
||||
$filterSystemIds = reset($systemIds);
|
||||
}else{
|
||||
$filterSystems = 'systemId IN (?)';
|
||||
$filterSystemIds = $systemIds;
|
||||
}
|
||||
|
||||
$this->has('corporationStructures.structureId', [$filterSystems, $filterSystemIds]);
|
||||
}
|
||||
|
||||
if($this->corporationStructures) {
|
||||
foreach($this->corporationStructures as $corporationStructure){
|
||||
$structuresData[] = $corporationStructure->structureId->getData();
|
||||
}
|
||||
}
|
||||
|
||||
return $structuresData;
|
||||
}
|
||||
|
||||
/**
|
||||
* get roles for each character in this corp
|
||||
* -> CCP API call
|
||||
@@ -272,6 +335,46 @@ class CorporationModel extends BasicModel {
|
||||
return $corporationRights;
|
||||
}
|
||||
|
||||
/**
|
||||
* load corporation by Id either from DB or load data from API
|
||||
* @param int $id
|
||||
* @param int $ttl
|
||||
* @param bool $isActive
|
||||
* @return \DB\Cortex
|
||||
*/
|
||||
public function getById(int $id, int $ttl = self::DEFAULT_SQL_TTL, bool $isActive = true){
|
||||
/**
|
||||
* @var CorporationModel $corporation
|
||||
*/
|
||||
$corporation = parent::getById($id, $ttl, $isActive);
|
||||
if($corporation->isOutdated()){
|
||||
// request corporation data
|
||||
$corporationData = self::getF3()->ccpClient->getCorporationData($id);
|
||||
if( !empty($corporationData) ){
|
||||
// check for NPC corporation
|
||||
$corporationData['isNPC'] = self::getF3()->ccpClient->isNpcCorporation($id);
|
||||
|
||||
$corporation->copyfrom($corporationData, ['id', 'name', 'ticker', 'memberCount', 'isNPC']);
|
||||
$corporation->save();
|
||||
}
|
||||
}
|
||||
|
||||
return $corporation;
|
||||
}
|
||||
|
||||
/**
|
||||
* add new structure for this corporation
|
||||
* @param StructureModel $structure
|
||||
*/
|
||||
public function saveStructure(StructureModel $structure){
|
||||
if( !$structure->dry() ){
|
||||
$corporationStructure = $this->rel('corporationStructures');
|
||||
$corporationStructure->corporationId = $this;
|
||||
$corporationStructure->structureId = $structure;
|
||||
$corporationStructure->save();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* get all corporations
|
||||
* @param array $options
|
||||
|
||||
65
app/main/model/corporationstructuremodel.php
Normal file
65
app/main/model/corporationstructuremodel.php
Normal file
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: exodu
|
||||
* Date: 15.04.2018
|
||||
* Time: 19:23
|
||||
*/
|
||||
|
||||
namespace Model;
|
||||
|
||||
use DB\SQL\Schema;
|
||||
|
||||
class CorporationStructureModel extends BasicModel {
|
||||
|
||||
protected $table = 'corporation_structure';
|
||||
|
||||
protected $fieldConf = [
|
||||
'active' => [
|
||||
'type' => Schema::DT_BOOL,
|
||||
'nullable' => false,
|
||||
'default' => 1,
|
||||
'index' => true
|
||||
],
|
||||
'corporationId' => [
|
||||
'type' => Schema::DT_INT,
|
||||
'index' => true,
|
||||
'belongs-to-one' => 'Model\CorporationModel',
|
||||
'constraint' => [
|
||||
[
|
||||
'table' => 'corporation',
|
||||
'on-delete' => 'CASCADE'
|
||||
]
|
||||
]
|
||||
],
|
||||
'structureId' => [
|
||||
'type' => Schema::DT_INT,
|
||||
'index' => true,
|
||||
'belongs-to-one' => 'Model\StructureModel',
|
||||
'constraint' => [
|
||||
[
|
||||
'table' => 'structure',
|
||||
'on-delete' => 'CASCADE'
|
||||
]
|
||||
]
|
||||
]
|
||||
];
|
||||
|
||||
/**
|
||||
* 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){
|
||||
$status = parent::setup($db,$table,$fields);
|
||||
|
||||
if($status === true){
|
||||
$status = parent::setMultiColumnIndex(['corporationId', 'structureId'], true);
|
||||
}
|
||||
|
||||
return $status;
|
||||
}
|
||||
}
|
||||
@@ -197,8 +197,8 @@ class MapModel extends AbstractMapTrackingModel {
|
||||
}
|
||||
|
||||
/**
|
||||
* get map data
|
||||
* -> this includes system and connection data as well!
|
||||
* get data
|
||||
* -> this includes system and connection data as well
|
||||
* @return \stdClass
|
||||
* @throws PathfinderException
|
||||
* @throws \Exception
|
||||
@@ -473,20 +473,22 @@ class MapModel extends AbstractMapTrackingModel {
|
||||
public function getNewSystem($systemId){
|
||||
// check for "inactive" system
|
||||
$system = $this->getSystemByCCPId($systemId);
|
||||
if( is_null($system) ){
|
||||
if(is_null($system)){
|
||||
// get blank system
|
||||
$systemController = new System();
|
||||
$systems = $systemController->getSystemModelByIds([$systemId]);
|
||||
if( count($systems) ){
|
||||
if(count($systems)){
|
||||
$system = reset($systems);
|
||||
$system->mapId = $this->_id;
|
||||
}else{
|
||||
// should NEVER happen -> systemId does NOT exist in New Eden!!
|
||||
$this->getF3()->error(500, 'SystemId "' . $systemId . '"" does not exist in EVE!' );
|
||||
}
|
||||
|
||||
}
|
||||
$system->setActive(true);
|
||||
|
||||
if($system){
|
||||
$system->setActive(true);
|
||||
}
|
||||
|
||||
return $system;
|
||||
}
|
||||
@@ -649,7 +651,7 @@ class MapModel extends AbstractMapTrackingModel {
|
||||
* get all connection data in this map
|
||||
* @return \stdClass[]
|
||||
*/
|
||||
public function getConnectionData(){
|
||||
public function getConnectionData() : array {
|
||||
$connectionData = [];
|
||||
$connections = $this->getConnections();
|
||||
|
||||
@@ -663,6 +665,34 @@ class MapModel extends AbstractMapTrackingModel {
|
||||
return $connectionData;
|
||||
}
|
||||
|
||||
/**
|
||||
* get all structures data for this map
|
||||
* @param array $systemIds
|
||||
* @return array
|
||||
*/
|
||||
public function getStructuresData(array $systemIds = []) : array {
|
||||
$structuresData = [];
|
||||
$corporations = $this->getAllCorporations();
|
||||
|
||||
foreach($corporations as $corporation){
|
||||
// corporations should be unique
|
||||
if( !isset($structuresData[$corporation->_id]) ){
|
||||
// get all structures for current corporation
|
||||
$corporationStructuresData = $corporation->getStructuresData($systemIds);
|
||||
if( !empty($corporationStructuresData) ){
|
||||
// corporation has structures
|
||||
$structuresData[$corporation->_id] = [
|
||||
'id' => $corporation->_id,
|
||||
'name' => $corporation->name,
|
||||
'structures' => $corporationStructuresData
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $structuresData;
|
||||
}
|
||||
|
||||
/**
|
||||
* set map access for an object (character, corporation or alliance)
|
||||
* @param $obj
|
||||
@@ -767,7 +797,7 @@ class MapModel extends AbstractMapTrackingModel {
|
||||
* @return bool
|
||||
* @throws PathfinderException
|
||||
*/
|
||||
public function hasAccess(CharacterModel $characterModel){
|
||||
public function hasAccess(CharacterModel $characterModel) : bool {
|
||||
$hasAccess = false;
|
||||
|
||||
if( !$this->dry() ){
|
||||
@@ -809,20 +839,58 @@ class MapModel extends AbstractMapTrackingModel {
|
||||
return $characters;
|
||||
}
|
||||
|
||||
/**
|
||||
* get corporations that have access to this map
|
||||
* @return CorporationModel[]
|
||||
*/
|
||||
public function getCorporations() : array {
|
||||
$corporations = [];
|
||||
|
||||
if($this->isCorporation()){
|
||||
$this->filter('mapCorporations', ['active = ?', 1]);
|
||||
|
||||
if($this->mapCorporations){
|
||||
foreach($this->mapCorporations as $mapCorporation){
|
||||
$corporations[] = $mapCorporation->corporationId;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $corporations;
|
||||
}
|
||||
|
||||
/**
|
||||
* get alliances that have access to this map
|
||||
* @return AllianceModel[]
|
||||
*/
|
||||
public function getAlliances() : array {
|
||||
$alliances = [];
|
||||
|
||||
if($this->isAlliance()){
|
||||
$this->filter('mapAlliances', ['active = ?', 1]);
|
||||
|
||||
if($this->mapAlliances){
|
||||
foreach($this->mapAlliances as $mapAlliance){
|
||||
$alliances[] = $mapAlliance->allianceId;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $alliances;
|
||||
}
|
||||
|
||||
/**
|
||||
* get all character models that are currently online "viewing" this map
|
||||
* @param array $options filter options
|
||||
* @return CharacterModel[]
|
||||
*/
|
||||
private function getAllCharacters($options = []){
|
||||
private function getAllCharacters($options = []) : array {
|
||||
$characters = [];
|
||||
|
||||
if($this->isPrivate()){
|
||||
$activeCharacters = $this->getCharacters();
|
||||
|
||||
// add active character for each user
|
||||
foreach($activeCharacters as $activeCharacter){
|
||||
$characters[] = $activeCharacter;
|
||||
foreach($this->getCharacters() as $character){
|
||||
$characters[] = $character;
|
||||
}
|
||||
}elseif($this->isCorporation()){
|
||||
$corporations = $this->getCorporations();
|
||||
@@ -870,18 +938,35 @@ class MapModel extends AbstractMapTrackingModel {
|
||||
}
|
||||
|
||||
/**
|
||||
* get all corporations that have access to this map
|
||||
* get all corporations that have access
|
||||
* -> for private maps -> get corporations from characters
|
||||
* -> for corporation maps -> get corporations
|
||||
* -> for alliance maps -> get corporations from alliances
|
||||
* @return CorporationModel[]
|
||||
*/
|
||||
public function getCorporations(){
|
||||
public function getAllCorporations() : array {
|
||||
$corporations = [];
|
||||
|
||||
if($this->isCorporation()){
|
||||
$this->filter('mapCorporations', ['active = ?', 1]);
|
||||
|
||||
if($this->mapCorporations){
|
||||
foreach($this->mapCorporations as $mapCorporation){
|
||||
$corporations[] = $mapCorporation->corporationId;
|
||||
if($this->isPrivate()){
|
||||
foreach($this->getCharacters() as $character){
|
||||
if(
|
||||
$character->hasCorporation() &&
|
||||
!array_key_exists($character->get('corporationId', true), $corporations)
|
||||
){
|
||||
$corporations[$character->getCorporation()->_id] = $character->getCorporation();
|
||||
}
|
||||
}
|
||||
}elseif($this->isCorporation()){
|
||||
$corporations = $this->getCorporations();
|
||||
}elseif($this->isAlliance()){
|
||||
foreach($this->getAlliances() as $alliance){
|
||||
foreach($alliance->getCharacters() as $character){
|
||||
if(
|
||||
$character->hasCorporation() &&
|
||||
!array_key_exists($character->get('corporationId', true), $corporations)
|
||||
){
|
||||
$corporations[$character->getCorporation()->_id] = $character->getCorporation();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -889,26 +974,6 @@ class MapModel extends AbstractMapTrackingModel {
|
||||
return $corporations;
|
||||
}
|
||||
|
||||
/**
|
||||
* get all alliances that have access to this map
|
||||
* @return AllianceModel[]
|
||||
*/
|
||||
public function getAlliances(){
|
||||
$alliances = [];
|
||||
|
||||
if($this->isAlliance()){
|
||||
$this->filter('mapAlliances', ['active = ?', 1]);
|
||||
|
||||
if($this->mapAlliances){
|
||||
foreach($this->mapAlliances as $mapAlliance){
|
||||
$alliances[] = $mapAlliance->allianceId;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $alliances;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $action
|
||||
* @return Logging\LogInterface
|
||||
@@ -1306,7 +1371,6 @@ class MapModel extends AbstractMapTrackingModel {
|
||||
|
||||
// check if a system has active characters
|
||||
foreach($activeUserCharactersData as $key => $activeUserCharacterData){
|
||||
|
||||
if(isset($activeUserCharacterData->log)){
|
||||
// user as log data
|
||||
if($activeUserCharacterData->log->system->id == $systemData->systemId){
|
||||
|
||||
252
app/main/model/structuremodel.php
Normal file
252
app/main/model/structuremodel.php
Normal file
@@ -0,0 +1,252 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: exodu
|
||||
* Date: 15.04.2018
|
||||
* Time: 19:41
|
||||
*/
|
||||
|
||||
namespace Model;
|
||||
|
||||
use DB\SQL\Schema;
|
||||
|
||||
|
||||
class StructureModel extends BasicModel {
|
||||
|
||||
protected $table = 'structure';
|
||||
|
||||
/**
|
||||
* categoryId (from CCP´s SDE) that holds all "groups" with structure "types"
|
||||
*/
|
||||
const CATEGORY_STRUCTURE_ID = 65;
|
||||
|
||||
protected $fieldConf = [
|
||||
'active' => [
|
||||
'type' => Schema::DT_BOOL,
|
||||
'nullable' => false,
|
||||
'default' => 1,
|
||||
'index' => true
|
||||
],
|
||||
'structureId' => [
|
||||
'type' => Schema::DT_INT,
|
||||
'index' => true
|
||||
],
|
||||
'corporationId' => [
|
||||
'type' => Schema::DT_INT,
|
||||
'index' => true,
|
||||
'belongs-to-one' => 'Model\CorporationModel',
|
||||
'constraint' => [
|
||||
[
|
||||
'table' => 'corporation',
|
||||
'on-delete' => 'SET NULL'
|
||||
]
|
||||
]
|
||||
],
|
||||
'systemId' => [
|
||||
'type' => Schema::DT_INT,
|
||||
'index' => true,
|
||||
'validate' => true
|
||||
],
|
||||
'statusId' => [
|
||||
'type' => Schema::DT_INT,
|
||||
'default' => 1,
|
||||
'index' => true,
|
||||
'belongs-to-one' => 'Model\StructureStatusModel',
|
||||
'constraint' => [
|
||||
[
|
||||
'table' => 'structure_status',
|
||||
'on-delete' => 'SET NULL'
|
||||
]
|
||||
]
|
||||
],
|
||||
'name' => [
|
||||
'type' => Schema::DT_VARCHAR128,
|
||||
'nullable' => false,
|
||||
'default' => ''
|
||||
],
|
||||
'description' => [
|
||||
'type' => Schema::DT_VARCHAR512,
|
||||
'nullable' => false,
|
||||
'default' => ''
|
||||
],
|
||||
'structureCorporations' => [
|
||||
'has-many' => ['Model\CorporationStructureModel', 'structureId']
|
||||
]
|
||||
];
|
||||
|
||||
/**
|
||||
* get structure data
|
||||
* @return \stdClass
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function getData() : \stdClass {
|
||||
$structureData = (object) [];
|
||||
$structureData->id = $this->_id;
|
||||
$structureData->systemId = $this->systemId;
|
||||
$structureData->status = $this->statusId->getData();
|
||||
$structureData->name = $this->name;
|
||||
$structureData->description = $this->description;
|
||||
|
||||
if($this->structureId){
|
||||
$structureData->structure = $this->getUniverseTypeData($this->structureId);
|
||||
}
|
||||
|
||||
if($this->corporationId){
|
||||
$structureData->owner = (object) [];
|
||||
$structureData->owner->id = $this->corporationId->_id;
|
||||
$structureData->owner->name = $this->corporationId->name;
|
||||
}
|
||||
|
||||
$structureData->updated = (object) [];
|
||||
$structureData->updated->updated = strtotime($this->updated);
|
||||
|
||||
return $structureData;
|
||||
}
|
||||
|
||||
/**
|
||||
* set structureId (universeType) for this structure
|
||||
* @param $structureId
|
||||
* @return int|null
|
||||
*/
|
||||
public function set_structureId($structureId){
|
||||
$structureId = (int)$structureId;
|
||||
return $structureId ? : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* set corporationId (owner) for this structure
|
||||
* -> if corporation does not exists in DB -> load from API
|
||||
* @param $corporationId
|
||||
* @return int|null
|
||||
*/
|
||||
public function set_corporationId($corporationId){
|
||||
$oldCorporationId = $this->get('corporationId', true) ? : 0;
|
||||
$corporationId = !is_string($corporationId) ? : (int)$corporationId;
|
||||
|
||||
if($corporationId){
|
||||
if($corporationId !== $oldCorporationId){
|
||||
// make sure there is already corporation data stored for new corporationId
|
||||
/**
|
||||
* @var CorporationModel $corporation
|
||||
*/
|
||||
$corporation = $this->rel('corporationId');
|
||||
$corporation->getById($corporationId);
|
||||
if($corporation->dry()){
|
||||
$corporationId = null;
|
||||
}
|
||||
}
|
||||
}else{
|
||||
$corporationId = null;
|
||||
}
|
||||
|
||||
return $corporationId;
|
||||
}
|
||||
/**
|
||||
* validates systemId
|
||||
* @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;
|
||||
}
|
||||
|
||||
/**
|
||||
* check whether this model is valid or not
|
||||
* @return bool
|
||||
*/
|
||||
public function isValid(): bool {
|
||||
if($valid = parent::isValid()){
|
||||
// structure always belongs to a systemId
|
||||
if(!(int)$this->systemId){
|
||||
$valid = false;
|
||||
}
|
||||
}
|
||||
|
||||
return $valid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Event "Hook" function
|
||||
* can be overwritten
|
||||
* return false will stop any further action
|
||||
* @param BasicModel $self
|
||||
* @param $pkeys
|
||||
* @return bool
|
||||
*/
|
||||
public function beforeInsertEvent($self, $pkeys){
|
||||
return $this->isValid() ? parent::beforeInsertEvent($self, $pkeys) : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* check access by chraacter
|
||||
* @param CharacterModel $characterModel
|
||||
* @return bool
|
||||
*/
|
||||
public function hasAccess(CharacterModel $characterModel) : bool {
|
||||
$access = false;
|
||||
if($characterModel->hasCorporation()){
|
||||
$this->filter('structureCorporations', ['active = ?', 1]);
|
||||
$this->has('structureCorporations.corporationId', ['active = ?', 1]);
|
||||
$this->has('structureCorporations.corporationId', ['id = ?', $characterModel->get('corporationId', true)]);
|
||||
|
||||
if($this->structureCorporations){
|
||||
$access = true;
|
||||
}
|
||||
}
|
||||
|
||||
return $access;
|
||||
}
|
||||
|
||||
/**
|
||||
* get structure data grouped by corporations
|
||||
* @return array
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function getDataByCorporations() : array {
|
||||
$structuresData = [];
|
||||
foreach((array)$this->structureCorporations as $structureCorporation){
|
||||
if($structureCorporation->isActive() && $structureCorporation->corporationId->isActive()){
|
||||
$structuresData[$structureCorporation->corporationId->_id] = [
|
||||
'id' => $structureCorporation->corporationId->_id,
|
||||
'name' => $structureCorporation->corporationId->name,
|
||||
'structures' => [$this->getData()]
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $structuresData;
|
||||
}
|
||||
|
||||
public function getByName(CorporationModel $corporation, string $name) {
|
||||
if( !$corporation->dry() && $name){
|
||||
$this->has('structureCorporations', ['corporationId = :corporationId', ':corporationId' => $corporation->_id]);
|
||||
$this->load(['name = :name AND active = :active',
|
||||
':name' => $name,
|
||||
':active' => 1
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* get universe type data for structureId
|
||||
* @param int $structureId
|
||||
* @return \stdClass
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function getUniverseTypeData(int $structureId) : \stdClass {
|
||||
/**
|
||||
* @var $type Universe\TypeModel
|
||||
*/
|
||||
$type = Universe\BasicUniverseModel::getNew('TypeModel')->getById($structureId);
|
||||
return $type->dry() ? (object)[] : $type->getData();
|
||||
}
|
||||
|
||||
}
|
||||
92
app/main/model/structurestatusmodel.php
Normal file
92
app/main/model/structurestatusmodel.php
Normal file
@@ -0,0 +1,92 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: exodu
|
||||
* Date: 15.04.2018
|
||||
* Time: 20:13
|
||||
*/
|
||||
|
||||
namespace Model;
|
||||
|
||||
use DB\SQL\Schema;
|
||||
|
||||
|
||||
class StructureStatusModel extends BasicModel {
|
||||
|
||||
protected $table = 'structure_status';
|
||||
|
||||
protected $fieldConf = [
|
||||
'active' => [
|
||||
'type' => Schema::DT_BOOL,
|
||||
'nullable' => false,
|
||||
'default' => 1,
|
||||
'index' => true
|
||||
],
|
||||
'name' => [
|
||||
'type' => Schema::DT_VARCHAR128,
|
||||
'nullable' => false,
|
||||
'default' => ''
|
||||
],
|
||||
'label' => [
|
||||
'type' => Schema::DT_VARCHAR128,
|
||||
'nullable' => false,
|
||||
'default' => ''
|
||||
],
|
||||
'class' => [
|
||||
'type' => Schema::DT_VARCHAR128,
|
||||
'nullable' => false,
|
||||
'default' => ''
|
||||
],
|
||||
'structures' => [
|
||||
'has-many' => ['Model\StructureModel', 'statusId']
|
||||
]
|
||||
];
|
||||
|
||||
protected static $tableData = [
|
||||
[
|
||||
'id' => 1,
|
||||
'name' => 'unknown',
|
||||
'label' => '',
|
||||
'class' => 'pf-structure-status-unknown'
|
||||
],
|
||||
[
|
||||
'id' => 2,
|
||||
'name' => 'online',
|
||||
'label' => 'online',
|
||||
'class' => 'pf-structure-status-online'
|
||||
],
|
||||
[
|
||||
'id' => 3,
|
||||
'name' => 'offline',
|
||||
'label' => 'offline',
|
||||
'class' => 'pf-structure-status-offline'
|
||||
]
|
||||
];
|
||||
|
||||
/**
|
||||
* get structure status data
|
||||
* @return \stdClass
|
||||
*/
|
||||
public function getData() : \stdClass {
|
||||
$statusData = (object) [];
|
||||
$statusData->id = $this->_id;
|
||||
$statusData->name = $this->name;
|
||||
$statusData->label = $this->label;
|
||||
$statusData->class = $this->class;
|
||||
|
||||
return $statusData;
|
||||
}
|
||||
|
||||
/**
|
||||
* get all status options
|
||||
* @return \DB\CortexCollection
|
||||
*/
|
||||
public static function getAll(){
|
||||
$query = [
|
||||
'active = :active',
|
||||
':active' => 1
|
||||
];
|
||||
|
||||
return (new self())->find($query);
|
||||
}
|
||||
}
|
||||
@@ -203,7 +203,7 @@ class SystemModel extends AbstractMapTrackingModel {
|
||||
// no cached system data found
|
||||
|
||||
$systemData = (object) [];
|
||||
$systemData->id = $this->id;
|
||||
$systemData->id = $this->_id;
|
||||
$systemData->mapId = is_object($this->mapId) ? $this->get('mapId', true) : 0;
|
||||
$systemData->systemId = $this->systemId;
|
||||
$systemData->name = $this->name;
|
||||
@@ -242,13 +242,13 @@ class SystemModel extends AbstractMapTrackingModel {
|
||||
|
||||
$systemData->created = (object) [];
|
||||
$systemData->created->created = strtotime($this->created);
|
||||
if( is_object($this->createdCharacterId) ){
|
||||
if(is_object($this->createdCharacterId)){
|
||||
$systemData->created->character = $this->createdCharacterId->getData();
|
||||
}
|
||||
|
||||
$systemData->updated = (object) [];
|
||||
$systemData->updated->updated = strtotime($this->updated);
|
||||
if( is_object($this->updatedCharacterId) ){
|
||||
if(is_object($this->updatedCharacterId)){
|
||||
$systemData->updated->character = $this->updatedCharacterId->getData();
|
||||
}
|
||||
|
||||
@@ -485,7 +485,6 @@ class SystemModel extends AbstractMapTrackingModel {
|
||||
public function getSignatures(){
|
||||
$signatures = [];
|
||||
$this->filter('signatures', ['active = ?', 1], ['order' => 'name']);
|
||||
|
||||
if($this->signatures){
|
||||
$signatures = $this->signatures;
|
||||
}
|
||||
@@ -494,13 +493,12 @@ class SystemModel extends AbstractMapTrackingModel {
|
||||
}
|
||||
|
||||
/**
|
||||
* get all data for all Signatures in this system
|
||||
* get data for all Signatures in this system
|
||||
* @return \stdClass[]
|
||||
*/
|
||||
public function getSignaturesData(){
|
||||
$signaturesData = [];
|
||||
$signatures = $this->getSignatures();
|
||||
|
||||
foreach($signatures as $signature){
|
||||
$signaturesData[] = $signature->getData();
|
||||
}
|
||||
@@ -516,7 +514,6 @@ class SystemModel extends AbstractMapTrackingModel {
|
||||
*/
|
||||
public function getSignatureById(CharacterModel $characterModel, $id){
|
||||
$signature = null;
|
||||
|
||||
if($this->hasAccess($characterModel)){
|
||||
$this->filter('signatures', ['active = ? AND id = ?', 1, $id]);
|
||||
if($this->signatures){
|
||||
@@ -535,7 +532,6 @@ class SystemModel extends AbstractMapTrackingModel {
|
||||
*/
|
||||
public function getSignatureByName(CharacterModel $characterModel, $name){
|
||||
$signature = null;
|
||||
|
||||
if($this->hasAccess($characterModel)){
|
||||
$this->filter('signatures', ['active = ? AND name = ?', 1, $name]);
|
||||
if($this->signatures){
|
||||
@@ -546,22 +542,38 @@ class SystemModel extends AbstractMapTrackingModel {
|
||||
return $signature;
|
||||
}
|
||||
|
||||
/**
|
||||
* get data for all structures in this system
|
||||
* @return \stdClass[]
|
||||
*/
|
||||
public function getStructuresData() : array {
|
||||
return $this->getMap()->getStructuresData([$this->systemId]);
|
||||
}
|
||||
|
||||
/**
|
||||
* check whether this system is a wormhole
|
||||
* @return bool
|
||||
*/
|
||||
public function isWormhole(){
|
||||
public function isWormhole() : bool {
|
||||
return ($this->typeId->id === 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* check whether this syste is a shattered wormhole
|
||||
* check whether this system is a shattered wormhole
|
||||
* @return bool
|
||||
*/
|
||||
public function isShatteredWormhole(){
|
||||
public function isShatteredWormhole() : bool {
|
||||
return ($this->isWormhole() && $this->security === 'SH');
|
||||
}
|
||||
|
||||
/**
|
||||
* check whether this system is an Abyss system
|
||||
* @return bool
|
||||
*/
|
||||
public function isAbyss() : bool {
|
||||
return ($this->typeId->id === 3 && $this->security === 'A');
|
||||
}
|
||||
|
||||
/**
|
||||
* send rally point poke to various "APIs"
|
||||
* -> send to a Slack channel
|
||||
@@ -697,11 +709,11 @@ class SystemModel extends AbstractMapTrackingModel {
|
||||
}
|
||||
|
||||
/**
|
||||
* 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){
|
||||
$status = parent::setup($db,$table,$fields);
|
||||
|
||||
@@ -31,11 +31,15 @@ class SystemTypeModel extends BasicModel {
|
||||
protected static $tableData = [
|
||||
[
|
||||
'id' => 1,
|
||||
'name' => 'w-space'
|
||||
'name' => 'w-space' // Wormhole Space
|
||||
],
|
||||
[
|
||||
'id' => 2,
|
||||
'name' => 'k-space'
|
||||
'name' => 'k-space' // Known Space
|
||||
],
|
||||
[
|
||||
'id' => 3,
|
||||
'name' => 'a-space' // Abyss Space
|
||||
]
|
||||
];
|
||||
|
||||
|
||||
@@ -18,10 +18,178 @@ abstract class BasicUniverseModel extends BasicModel {
|
||||
* data from Universe tables is static and does not change frequently
|
||||
* -> refresh static data after X days
|
||||
*/
|
||||
const CACHE_MAX_DAYS = 7;
|
||||
const CACHE_MAX_DAYS = 60;
|
||||
|
||||
const CACHE_KEY_PREFIX = 'index_universe_';
|
||||
|
||||
/**
|
||||
* cache key for model data -> should "never" expire
|
||||
* -> until manual remove and or global cache clear
|
||||
*/
|
||||
const CACHE_INDEX_EXPIRE_KEY = 86400 * 356 * 5;
|
||||
|
||||
protected $db = 'DB_UNIVERSE';
|
||||
|
||||
/**
|
||||
* get model data -> should be overwritten
|
||||
* @return null
|
||||
*/
|
||||
public function getData(){
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Event "Hook" function
|
||||
* return false will stop any further action
|
||||
* @param self $self
|
||||
* @param $pkeys
|
||||
* @return bool
|
||||
*/
|
||||
public function beforeUpdateEvent($self, $pkeys){
|
||||
// if model changed, 'update' col needs to be updated as well
|
||||
// -> data no longer "outdated"
|
||||
$this->touch('updated');
|
||||
|
||||
return parent::beforeUpdateEvent($self, $pkeys);
|
||||
}
|
||||
|
||||
/**
|
||||
* get hashKey for search index build
|
||||
* -> used by the cache backend
|
||||
* @param string $column
|
||||
* @return bool|string
|
||||
*/
|
||||
public function getHashKey(string $column = '_id'){
|
||||
$key = false;
|
||||
if( !$this->dry() && $this->exists($column) ){
|
||||
$key = self::generateHashKeyRow($this->getTable(), $this->$column);
|
||||
}
|
||||
return $key;
|
||||
}
|
||||
|
||||
/**
|
||||
* calculate time period (in seconds) from now on, until data get expired
|
||||
* @return int
|
||||
*/
|
||||
/*
|
||||
public function calcTtl() : int {
|
||||
$ttl = 0;
|
||||
if(!$this->dry()){
|
||||
$timezone = $this->getF3()->get('getTimeZone')();
|
||||
$currentTime = new \DateTime('now', $timezone);
|
||||
$updateTime = \DateTime::createFromFormat(
|
||||
'Y-m-d H:i:s',
|
||||
$this->updated,
|
||||
$timezone
|
||||
);
|
||||
// add expire period to last updated timestamp
|
||||
$updateTime->modify('+' . self::CACHE_MAX_DAYS . ' day');
|
||||
|
||||
$seconds = $updateTime->getTimestamp() - $currentTime->getTimestamp();
|
||||
if($seconds > 0){
|
||||
$ttl = $seconds;
|
||||
}
|
||||
}
|
||||
|
||||
return $ttl;
|
||||
}
|
||||
*/
|
||||
/**
|
||||
* build up a "search" index for this model
|
||||
* -> stores getData() result into Cache (RAM) for faster access
|
||||
*/
|
||||
public function buildIndex(){
|
||||
if($hashKeyId = $this->getHashKey()){
|
||||
$f3 = self::getF3();
|
||||
$hashKeyTable = self::generateHashKeyTable($this->getTable());
|
||||
|
||||
if( !$f3->exists($hashKeyTable, $cachedData) ){
|
||||
$cachedData = [];
|
||||
}
|
||||
|
||||
if( !in_array($hashKeyId, $cachedData) ){
|
||||
$cachedData[] = $hashKeyId;
|
||||
}
|
||||
|
||||
// value update does not update ttl -> delete key from cache and add again
|
||||
$f3->clear($hashKeyId);
|
||||
$f3->clear($hashKeyTable);
|
||||
|
||||
// straight into cache (no $f->set() ), no sync with hive here -> save ram
|
||||
self::setCacheValue($hashKeyId, $this->getData(), self::CACHE_INDEX_EXPIRE_KEY);
|
||||
self::setCacheValue($hashKeyTable, $cachedData, self::CACHE_INDEX_EXPIRE_KEY);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* load object by $id
|
||||
* -> if $id not exists in DB -> query API
|
||||
* @param int $id
|
||||
* @param string $accessToken
|
||||
* @param array $additionalOptions
|
||||
*/
|
||||
public function loadById(int $id, string $accessToken = '', array $additionalOptions = []){
|
||||
/**
|
||||
* @var $model self
|
||||
*/
|
||||
$model = $this->getById($id, 0);
|
||||
if($model->isOutdated()){
|
||||
$model->loadData($id, $accessToken, $additionalOptions);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* load data by foreign key or other column than "id"
|
||||
* @param string $key
|
||||
* @param $value
|
||||
*/
|
||||
public function loadByKey(string $key, $value){
|
||||
/**
|
||||
* @var $model self
|
||||
*/
|
||||
$model = $this->getByForeignKey($key, $value, ['limit' => 1]);
|
||||
if($model->isOutdated()){
|
||||
$model->loadDataByKey($key, $value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* load data from API into $this and save $this
|
||||
* @param int $id
|
||||
* @param string $accessToken
|
||||
* @param array $additionalOptions
|
||||
*/
|
||||
abstract protected function loadData(int $id, string $accessToken = '', array $additionalOptions = []);
|
||||
|
||||
protected function loadDataByKey(string $key, $value){}
|
||||
|
||||
/**
|
||||
* generate hashKey for a table row data for search index build
|
||||
* @param string $table
|
||||
* @param $value
|
||||
* @return string
|
||||
*/
|
||||
public static function generateHashKeyRow(string $table, $value){
|
||||
return self::generateHashKeyTable($table) . '_' . md5(strtolower((string)$value));
|
||||
}
|
||||
|
||||
/**
|
||||
* generate hashKey for a complete table
|
||||
* -> should hold hashKeys for multiple rows
|
||||
* @param string $table
|
||||
* @return string
|
||||
*/
|
||||
public static function generateHashKeyTable(string $table){
|
||||
return self::CACHE_KEY_PREFIX . strtolower($table);
|
||||
}
|
||||
|
||||
/**
|
||||
* factory for all UniverseModels
|
||||
* @param string $model
|
||||
* @param int $ttl
|
||||
* @return BasicModel|null
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function getNew($model, $ttl = self::DEFAULT_TTL){
|
||||
$class = null;
|
||||
|
||||
@@ -35,51 +203,4 @@ abstract class BasicUniverseModel extends BasicModel {
|
||||
|
||||
return $class;
|
||||
}
|
||||
|
||||
/**
|
||||
* load data from API into $this and save $this
|
||||
* @param int $id
|
||||
* @param string $accessToken
|
||||
* @param array $additionalOptions
|
||||
*/
|
||||
abstract protected function loadData(int $id, string $accessToken = '', array $additionalOptions = []);
|
||||
|
||||
/**
|
||||
* load object by $id
|
||||
* -> if $id not exists in DB -> query API
|
||||
* @param int $id
|
||||
* @param string $accessToken
|
||||
* @param array $additionalOptions
|
||||
*/
|
||||
public function loadById(int $id, string $accessToken = '', array $additionalOptions = []){
|
||||
/**
|
||||
* @var $model self
|
||||
*/
|
||||
$model = $this->getById($id);
|
||||
if($model->isOutdated()){
|
||||
$model->loadData($id, $accessToken, $additionalOptions);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* checks whether data is outdated and should be refreshed
|
||||
* @return bool
|
||||
*/
|
||||
protected function isOutdated(): bool {
|
||||
$outdated = true;
|
||||
if(!$this->dry()){
|
||||
$timezone = $this->getF3()->get('getTimeZone')();
|
||||
$currentTime = new \DateTime('now', $timezone);
|
||||
$updateTime = \DateTime::createFromFormat(
|
||||
'Y-m-d H:i:s',
|
||||
$this->updated,
|
||||
$timezone
|
||||
);
|
||||
$interval = $updateTime->diff($currentTime);
|
||||
if($interval->days < self::CACHE_MAX_DAYS ){
|
||||
$outdated = false;
|
||||
}
|
||||
}
|
||||
return $outdated;
|
||||
}
|
||||
}
|
||||
145
app/main/model/universe/categorymodel.php
Normal file
145
app/main/model/universe/categorymodel.php
Normal file
@@ -0,0 +1,145 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: exodu
|
||||
* Date: 13.04.2018
|
||||
* Time: 23:49
|
||||
*/
|
||||
|
||||
namespace Model\Universe;
|
||||
|
||||
use DB\SQL\Schema;
|
||||
|
||||
class CategoryModel extends BasicUniverseModel {
|
||||
|
||||
protected $table = 'category';
|
||||
|
||||
protected $fieldConf = [
|
||||
'name' => [
|
||||
'type' => Schema::DT_VARCHAR128,
|
||||
'nullable' => false,
|
||||
'default' => ''
|
||||
],
|
||||
'published' => [
|
||||
'type' => Schema::DT_BOOL,
|
||||
'nullable' => false,
|
||||
'default' => 1,
|
||||
'index' => true
|
||||
],
|
||||
'groups' => [
|
||||
'has-many' => ['Model\Universe\GroupModel', 'categoryId']
|
||||
]
|
||||
];
|
||||
|
||||
/**
|
||||
* get category data
|
||||
* @return object
|
||||
*/
|
||||
public function getData(){
|
||||
$categoryData = (object) [];
|
||||
$categoryData->id = $this->id;
|
||||
$categoryData->name = $this->name;
|
||||
|
||||
if($groupsData = $this->getGroupsData()){
|
||||
$categoryData->groups = $groupsData;
|
||||
}
|
||||
|
||||
return $categoryData;
|
||||
}
|
||||
|
||||
/**
|
||||
* get all groups for this category
|
||||
* @param bool $published
|
||||
* @return array|mixed
|
||||
*/
|
||||
protected function getGroups(bool $published = true){
|
||||
$groups = [];
|
||||
if($published){
|
||||
$this->filter('groups', [
|
||||
'published = :published',
|
||||
':published' => 1
|
||||
]);
|
||||
}
|
||||
|
||||
if($this->groups){
|
||||
$groups = $this->groups;
|
||||
}
|
||||
|
||||
return $groups;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
protected function getGroupsData() : array {
|
||||
$groupsData = [];
|
||||
$groups = $this->getGroups();
|
||||
|
||||
foreach($groups as $group){
|
||||
$groupsData[] = $group->getData();
|
||||
}
|
||||
|
||||
return $groupsData;
|
||||
}
|
||||
|
||||
/**
|
||||
* count all types that belong to groups in this category
|
||||
* @param bool $published
|
||||
* @return int
|
||||
*/
|
||||
public function getTypesCount(bool $published = true) : int {
|
||||
$count = 0;
|
||||
if( !$this->dry() ){
|
||||
/**
|
||||
* @var $group GroupModel
|
||||
*/
|
||||
foreach($groups = $this->getGroups($published) as $group){
|
||||
$count += $group->getTypesCount($published);
|
||||
}
|
||||
}
|
||||
return $count;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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->getUniverseCategoryData($id);
|
||||
if(!empty($data)){
|
||||
$this->copyfrom($data, ['id', 'name', 'published']);
|
||||
$this->save();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* load groups data for this category
|
||||
* @param int $offset
|
||||
* @param int $length 0 -> all groups
|
||||
* @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();
|
||||
}
|
||||
}
|
||||
}
|
||||
return $groupIds;
|
||||
}
|
||||
}
|
||||
@@ -14,12 +14,6 @@ class ConstellationModel extends BasicUniverseModel {
|
||||
|
||||
protected $table = 'constellation';
|
||||
|
||||
/**
|
||||
* No static columns added
|
||||
* @var bool
|
||||
*/
|
||||
protected $addStaticFields = false;
|
||||
|
||||
protected $fieldConf = [
|
||||
'name' => [
|
||||
'type' => Schema::DT_VARCHAR128,
|
||||
@@ -35,32 +29,94 @@ class ConstellationModel extends BasicUniverseModel {
|
||||
'table' => 'region',
|
||||
'on-delete' => 'CASCADE'
|
||||
]
|
||||
]
|
||||
],
|
||||
'validate' => 'validate_notDry'
|
||||
],
|
||||
'x' => [
|
||||
'type' => Schema::DT_INT8,
|
||||
'type' => Schema::DT_BIGINT,
|
||||
'nullable' => false,
|
||||
'default' => 0
|
||||
],
|
||||
'y' => [
|
||||
'type' => Schema::DT_INT8,
|
||||
'type' => Schema::DT_BIGINT,
|
||||
'nullable' => false,
|
||||
'default' => 0
|
||||
],
|
||||
'z' => [
|
||||
'type' => Schema::DT_INT8,
|
||||
'type' => Schema::DT_BIGINT,
|
||||
'nullable' => false,
|
||||
'default' => 0
|
||||
],
|
||||
'systems' => [
|
||||
'has-many' => ['Model\Universe\SystemModel', 'constellationId']
|
||||
]
|
||||
];
|
||||
|
||||
/**
|
||||
* get data
|
||||
* @return \stdClass
|
||||
*/
|
||||
public function getData(){
|
||||
$constellationData = (object) [];
|
||||
$constellationData->id = $this->_id;
|
||||
$constellationData->name = $this->name;
|
||||
$constellationData->region = $this->regionId->getData();
|
||||
|
||||
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
|
||||
* @param array $additionalOptions
|
||||
*/
|
||||
protected function loadData(int $id, string $accessToken = '', array $additionalOptions = []){
|
||||
$data = self::getF3()->ccpClient->getUniverseConstellationData($id);
|
||||
if(!empty($data)){
|
||||
/**
|
||||
* @var $region RegionModel
|
||||
*/
|
||||
$region = $this->rel('regionId');
|
||||
$region->loadById($data['regionId'], $accessToken, $additionalOptions);
|
||||
$data['regionId'] = $region;
|
||||
|
||||
$this->copyfrom($data, ['id', 'name', 'regionId', 'position']);
|
||||
$this->save();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* load systems data for this constellation
|
||||
*/
|
||||
public function loadSystemsData(){
|
||||
if( !$this->dry() ){
|
||||
$data = self::getF3()->ccpClient->getUniverseConstellationData($this->_id);
|
||||
if(!empty($data)){
|
||||
foreach((array)$data['systems'] as $systemId){
|
||||
/**
|
||||
* @var $system SystemModel
|
||||
*/
|
||||
$system = $this->rel('systems');
|
||||
$system->loadById($systemId);
|
||||
$system->reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
148
app/main/model/universe/groupmodel.php
Normal file
148
app/main/model/universe/groupmodel.php
Normal file
@@ -0,0 +1,148 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: exodu
|
||||
* Date: 13.04.2018
|
||||
* Time: 23:58
|
||||
*/
|
||||
|
||||
namespace Model\Universe;
|
||||
|
||||
use DB\SQL\Schema;
|
||||
|
||||
class GroupModel extends BasicUniverseModel {
|
||||
|
||||
protected $table = 'group';
|
||||
|
||||
protected $fieldConf = [
|
||||
'name' => [
|
||||
'type' => Schema::DT_VARCHAR128,
|
||||
'nullable' => false,
|
||||
'default' => ''
|
||||
],
|
||||
'published' => [
|
||||
'type' => Schema::DT_BOOL,
|
||||
'nullable' => false,
|
||||
'default' => 1,
|
||||
'index' => true
|
||||
],
|
||||
'categoryId' => [
|
||||
'type' => Schema::DT_INT,
|
||||
'index' => true,
|
||||
'belongs-to-one' => 'Model\Universe\CategoryModel',
|
||||
'constraint' => [
|
||||
[
|
||||
'table' => 'category',
|
||||
'on-delete' => 'CASCADE'
|
||||
]
|
||||
],
|
||||
'validate' => 'validate_notDry'
|
||||
],
|
||||
'types' => [
|
||||
'has-many' => ['Model\Universe\TypeModel', 'groupId']
|
||||
]
|
||||
];
|
||||
|
||||
/**
|
||||
* get group data
|
||||
* @return object
|
||||
*/
|
||||
public function getData(){
|
||||
$groupData = (object) [];
|
||||
$groupData->id = $this->id;
|
||||
$groupData->name = $this->name;
|
||||
|
||||
if($typesData = $this->getTypesData()){
|
||||
$groupData->types = $typesData;
|
||||
}
|
||||
|
||||
return $groupData;
|
||||
}
|
||||
|
||||
/**
|
||||
* get all types for this group
|
||||
* @param bool $published
|
||||
* @return array|mixed
|
||||
*/
|
||||
protected function getTypes(bool $published = true){
|
||||
$types = [];
|
||||
if($published){
|
||||
$this->filter('types', [
|
||||
'published = :published',
|
||||
':published' => 1
|
||||
]);
|
||||
}
|
||||
|
||||
if($this->types){
|
||||
$types = $this->types;
|
||||
}
|
||||
|
||||
return $types;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
protected function getTypesData() : array {
|
||||
$typesData = [];
|
||||
$types = $this->getTypes();
|
||||
|
||||
foreach($types as $type){
|
||||
$typesData[] = $type->getData();
|
||||
}
|
||||
|
||||
return $typesData;
|
||||
}
|
||||
|
||||
/**
|
||||
* count all types in this group
|
||||
* @param bool $published
|
||||
* @return int
|
||||
*/
|
||||
public function getTypesCount(bool $published = true) : int {
|
||||
return $this->dry() ? 0 : count($this->getTypes($published));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $id
|
||||
* @param string $accessToken
|
||||
* @param array $additionalOptions
|
||||
*/
|
||||
protected function loadData(int $id, string $accessToken = '', array $additionalOptions = []){
|
||||
$data = self::getF3()->ccpClient->getUniverseGroupData($id);
|
||||
if(!empty($data)){
|
||||
/**
|
||||
* @var $category CategoryModel
|
||||
*/
|
||||
$category = $this->rel('categoryId');
|
||||
$category->loadById($data['categoryId'], $accessToken, $additionalOptions);
|
||||
$data['categoryId'] = $category;
|
||||
|
||||
$this->copyfrom($data, ['id', 'name', 'published', 'categoryId']);
|
||||
$this->save();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* load types data for this group
|
||||
* @return int
|
||||
*/
|
||||
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++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $count;
|
||||
}
|
||||
}
|
||||
123
app/main/model/universe/planetmodel.php
Normal file
123
app/main/model/universe/planetmodel.php
Normal file
@@ -0,0 +1,123 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: exodu
|
||||
* Date: 19.05.2018
|
||||
* Time: 01:12
|
||||
*/
|
||||
|
||||
namespace Model\Universe;
|
||||
|
||||
use DB\SQL\Schema;
|
||||
|
||||
class PlanetModel extends BasicUniverseModel {
|
||||
|
||||
protected $table = 'planet';
|
||||
|
||||
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' => 'validate_notDry'
|
||||
],
|
||||
'typeId' => [
|
||||
'type' => Schema::DT_INT,
|
||||
'index' => true,
|
||||
'belongs-to-one' => 'Model\Universe\TypeModel',
|
||||
'constraint' => [
|
||||
[
|
||||
'table' => 'type',
|
||||
'on-delete' => 'SET NULL'
|
||||
]
|
||||
],
|
||||
'validate' => 'validate_notDry'
|
||||
],
|
||||
'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(){
|
||||
$planetData = (object) [];
|
||||
$planetData->id = $this->_id;
|
||||
$planetData->name = $this->name;
|
||||
|
||||
$planetData->position = (object) [];
|
||||
$planetData->position->x = $this->x;
|
||||
$planetData->position->y = $this->y;
|
||||
$planetData->position->z = $this->z;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $id
|
||||
* @param string $accessToken
|
||||
* @param array $additionalOptions
|
||||
*/
|
||||
protected function loadData(int $id, string $accessToken = '', array $additionalOptions = []){
|
||||
$data = self::getF3()->ccpClient->getUniversePlanetData($id);
|
||||
if(!empty($data)){
|
||||
/**
|
||||
* @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;
|
||||
|
||||
$this->copyfrom($data, ['id', 'name', 'systemId', 'typeId', 'position']);
|
||||
$this->save();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -14,12 +14,6 @@ class RegionModel extends BasicUniverseModel {
|
||||
|
||||
protected $table = 'region';
|
||||
|
||||
/**
|
||||
* No static columns added
|
||||
* @var bool
|
||||
*/
|
||||
protected $addStaticFields = false;
|
||||
|
||||
protected $fieldConf = [
|
||||
'name' => [
|
||||
'type' => Schema::DT_VARCHAR128,
|
||||
@@ -34,12 +28,47 @@ class RegionModel extends BasicUniverseModel {
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* get data
|
||||
* @return \stdClass
|
||||
*/
|
||||
public function getData(){
|
||||
$regionData = (object) [];
|
||||
$regionData->id = $this->_id;
|
||||
$regionData->name = $this->name;
|
||||
|
||||
return $regionData;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $id
|
||||
* @param string $accessToken
|
||||
* @param array $additionalOptions
|
||||
*/
|
||||
protected function loadData(int $id, string $accessToken = '', array $additionalOptions = []){
|
||||
$data = self::getF3()->ccpClient->getUniverseRegionData($id);
|
||||
if(!empty($data)){
|
||||
$this->copyfrom($data, ['id', 'name', 'description']);
|
||||
$this->save();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* load constellations data for this region
|
||||
*/
|
||||
public function loadConstellationsData(){
|
||||
if( !$this->dry() ){
|
||||
$data = self::getF3()->ccpClient->getUniverseRegionData($this->_id);
|
||||
if(!empty($data)){
|
||||
foreach((array)$data['constellations'] as $constellationsId){
|
||||
/**
|
||||
* @var $constellation ConstellationModel
|
||||
*/
|
||||
$constellation = $this->rel('constellations');
|
||||
$constellation->loadById($constellationsId);
|
||||
$constellation->reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
164
app/main/model/universe/stargatemodel.php
Normal file
164
app/main/model/universe/stargatemodel.php
Normal file
@@ -0,0 +1,164 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: exodu
|
||||
* Date: 19.05.2018
|
||||
* Time: 04:30
|
||||
*/
|
||||
|
||||
namespace Model\Universe;
|
||||
|
||||
use DB\SQL\Schema;
|
||||
|
||||
class StargateModel extends BasicUniverseModel {
|
||||
|
||||
protected $table = 'stargate';
|
||||
|
||||
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' => 'validate_notDry'
|
||||
],
|
||||
'typeId' => [
|
||||
'type' => Schema::DT_INT,
|
||||
'index' => true,
|
||||
'belongs-to-one' => 'Model\Universe\TypeModel',
|
||||
'constraint' => [
|
||||
[
|
||||
'table' => 'type',
|
||||
'on-delete' => 'SET NULL'
|
||||
]
|
||||
],
|
||||
'validate' => 'validate_notDry'
|
||||
],
|
||||
'destinationSystemId' => [
|
||||
'type' => Schema::DT_INT,
|
||||
'index' => true,
|
||||
'belongs-to-one' => 'Model\Universe\SystemModel',
|
||||
'constraint' => [
|
||||
[
|
||||
'table' => 'system',
|
||||
'on-delete' => 'CASCADE'
|
||||
]
|
||||
],
|
||||
'validate' => 'validate_notDry'
|
||||
],
|
||||
'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
|
||||
]
|
||||
];
|
||||
|
||||
public function getData(){
|
||||
|
||||
$stargateData = (object) [];
|
||||
$stargateData->id = $this->_id;
|
||||
$stargateData->type = $this->typeId->name;
|
||||
$stargateData->destination = $this->destinationSystemId->name;
|
||||
|
||||
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
|
||||
* @param array $additionalOptions
|
||||
*/
|
||||
protected function loadData(int $id, string $accessToken = '', array $additionalOptions = []){
|
||||
$data = self::getF3()->ccpClient->getUniverseStargateData($id);
|
||||
|
||||
if(!empty($data)){
|
||||
|
||||
if($this->get('systemId', true) !== $data['systemId']){
|
||||
// new stargate or system changed
|
||||
/**
|
||||
* @var $system SystemModel
|
||||
*/
|
||||
$system = $this->rel('systemId');
|
||||
$system->loadById($data['systemId'], $accessToken, $additionalOptions);
|
||||
$data['systemId'] = $system;
|
||||
}
|
||||
|
||||
if($this->get('typeId', true) !== $data['typeId']){
|
||||
/**
|
||||
* @var $type TypeModel
|
||||
*/
|
||||
$type = $this->rel('typeId');
|
||||
$type->loadById($data['typeId'], $accessToken, $additionalOptions);
|
||||
$data['typeId'] = $type;
|
||||
}
|
||||
|
||||
if($this->get('destinationSystemId', true) !== $data['destination']->system_id){
|
||||
// new stargate or destinationSystem changed
|
||||
/**
|
||||
* @var $destinationSystem SystemModel
|
||||
*/
|
||||
$destinationSystem = $this->rel('destinationSystemId');
|
||||
// no loadById() here! we don´t want to insert/update systems that do not exist yet
|
||||
$destinationSystem->getById($data['destination']->system_id, 0);
|
||||
|
||||
if( !$destinationSystem->dry() ){
|
||||
$data['destinationSystemId'] = $destinationSystem;
|
||||
$this->copyfrom($data, ['id', 'name', 'position', 'systemId', 'typeId', 'destinationSystemId']);
|
||||
$this->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param null $db
|
||||
* @param null $table
|
||||
* @param null $fields
|
||||
* @return bool
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function setup($db=null, $table=null, $fields=null){
|
||||
$status = parent::setup($db,$table,$fields);
|
||||
|
||||
if($status === true){
|
||||
$status = parent::setMultiColumnIndex(['systemId', 'destinationSystemId'], true);
|
||||
}
|
||||
|
||||
return $status;
|
||||
}
|
||||
}
|
||||
96
app/main/model/universe/starmodel.php
Normal file
96
app/main/model/universe/starmodel.php
Normal file
@@ -0,0 +1,96 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: exodu
|
||||
* Date: 18.05.2018
|
||||
* Time: 23:52
|
||||
*/
|
||||
|
||||
namespace Model\Universe;
|
||||
|
||||
use DB\SQL\Schema;
|
||||
|
||||
class StarModel extends BasicUniverseModel {
|
||||
|
||||
protected $table = 'star';
|
||||
|
||||
protected $fieldConf = [
|
||||
'name' => [
|
||||
'type' => Schema::DT_VARCHAR128,
|
||||
'nullable' => false,
|
||||
'default' => ''
|
||||
],
|
||||
'typeId' => [
|
||||
'type' => Schema::DT_INT,
|
||||
'index' => true,
|
||||
'belongs-to-one' => 'Model\Universe\TypeModel',
|
||||
'constraint' => [
|
||||
[
|
||||
'table' => 'type',
|
||||
'on-delete' => 'SET NULL'
|
||||
]
|
||||
],
|
||||
'validate' => 'validate_notDry'
|
||||
],
|
||||
'age' => [
|
||||
'type' => Schema::DT_BIGINT,
|
||||
'nullable' => true,
|
||||
'default' => null
|
||||
],
|
||||
'radius' => [
|
||||
'type' => Schema::DT_BIGINT,
|
||||
'nullable' => true,
|
||||
'default' => null
|
||||
],
|
||||
'temperature' => [
|
||||
'type' => Schema::DT_INT,
|
||||
'nullable' => true,
|
||||
'default' => null
|
||||
],
|
||||
'luminosity' => [
|
||||
'type' => Schema::DT_FLOAT,
|
||||
'nullable' => true,
|
||||
'default' => null
|
||||
],
|
||||
'spectralClass' => [
|
||||
'type' => Schema::DT_VARCHAR128,
|
||||
'nullable' => true,
|
||||
'default' => null
|
||||
],
|
||||
'system' => [
|
||||
'has-one' => ['Model\Universe\SystemModel', 'starId']
|
||||
]
|
||||
];
|
||||
|
||||
/**
|
||||
* get data
|
||||
* @return \stdClass
|
||||
*/
|
||||
public function getData(){
|
||||
$starData = (object) [];
|
||||
$starData->id = $this->_id;
|
||||
$starData->name = $this->typeId->name;
|
||||
|
||||
return $starData;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $id
|
||||
* @param string $accessToken
|
||||
* @param array $additionalOptions
|
||||
*/
|
||||
protected function loadData(int $id, string $accessToken = '', array $additionalOptions = []){
|
||||
$data = self::getF3()->ccpClient->getUniverseStarData($id);
|
||||
if(!empty($data)){
|
||||
/**
|
||||
* @var $type TypeModel
|
||||
*/
|
||||
$type = $this->rel('typeId');
|
||||
$type->loadById($data['typeId'], $accessToken, $additionalOptions);
|
||||
$data['typeId'] = $type;
|
||||
|
||||
$this->copyfrom($data, ['id', 'name', 'typeId', 'age', 'radius', 'temperature', 'luminosity', 'spectralClass']);
|
||||
$this->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,10 +8,8 @@
|
||||
|
||||
namespace Model\Universe;
|
||||
|
||||
|
||||
use DB\SQL;
|
||||
use DB\SQL\Schema;
|
||||
use lib\Util;
|
||||
|
||||
class StructureModel extends BasicUniverseModel {
|
||||
|
||||
@@ -38,7 +36,8 @@ class StructureModel extends BasicUniverseModel {
|
||||
'table' => 'type',
|
||||
'on-delete' => 'CASCADE'
|
||||
]
|
||||
]
|
||||
],
|
||||
'validate' => 'validate_notDry'
|
||||
],
|
||||
'x' => [
|
||||
'type' => Schema::DT_FLOAT,
|
||||
@@ -80,32 +79,25 @@ class StructureModel extends BasicUniverseModel {
|
||||
protected function loadData(int $id, string $accessToken = '', array $additionalOptions = []){
|
||||
$data = self::getF3()->ccpClient->getUniverseStructureData($id, $accessToken, $additionalOptions);
|
||||
if(!empty($data)){
|
||||
/**
|
||||
* @var $type TypeModel
|
||||
*/
|
||||
$type = $this->rel('typeId');
|
||||
$type->loadById($data['typeId'], $accessToken, $additionalOptions);
|
||||
$data['typeId'] = $type;
|
||||
|
||||
$this->copyfrom($data);
|
||||
$this->copyfrom($data, ['id', 'name', 'systemId', 'typeId', 'position']);
|
||||
$this->save();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array|string $key
|
||||
* @param null $fields
|
||||
* @return NULL
|
||||
*/
|
||||
public function copyfrom($key, $fields = null){
|
||||
// flatten array (e.g. "position" key)
|
||||
$key = Util::arrayFlatten((array)$key);
|
||||
parent::copyfrom($key, $fields);
|
||||
}
|
||||
|
||||
/**
|
||||
* overwrites parent
|
||||
* @param null|SQL $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)){
|
||||
|
||||
391
app/main/model/universe/systemmodel.php
Normal file
391
app/main/model/universe/systemmodel.php
Normal file
@@ -0,0 +1,391 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: exodu
|
||||
* Date: 14.05.2018
|
||||
* Time: 19:29
|
||||
*/
|
||||
|
||||
namespace Model\Universe;
|
||||
|
||||
use DB\SQL\Schema;
|
||||
|
||||
class SystemModel extends BasicUniverseModel {
|
||||
|
||||
protected $table = 'system';
|
||||
|
||||
const ERROR_INVALID_WORMHOLE = 'Invalid wormhole name "%s" for system: "%s"';
|
||||
|
||||
protected $fieldConf = [
|
||||
'name' => [
|
||||
'type' => Schema::DT_VARCHAR128,
|
||||
'nullable' => false,
|
||||
'default' => ''
|
||||
],
|
||||
'constellationId' => [
|
||||
'type' => Schema::DT_INT,
|
||||
'index' => true,
|
||||
'belongs-to-one' => 'Model\Universe\ConstellationModel',
|
||||
'constraint' => [
|
||||
[
|
||||
'table' => 'constellation',
|
||||
'on-delete' => 'CASCADE'
|
||||
]
|
||||
],
|
||||
'validate' => 'validate_notDry'
|
||||
],
|
||||
'starId' => [
|
||||
'type' => Schema::DT_INT,
|
||||
'index' => true,
|
||||
'belongs-to-one' => 'Model\Universe\StarModel',
|
||||
'constraint' => [
|
||||
[
|
||||
'table' => 'star',
|
||||
'on-delete' => 'CASCADE'
|
||||
]
|
||||
],
|
||||
'validate' => 'validate_notDry'
|
||||
],
|
||||
'security' => [
|
||||
'type' => Schema::DT_VARCHAR128
|
||||
],
|
||||
'trueSec' => [
|
||||
'type' => Schema::DT_FLOAT,
|
||||
'nullable' => false,
|
||||
'default' => 1
|
||||
],
|
||||
'securityStatus' => [
|
||||
'type' => Schema::DT_DOUBLE,
|
||||
'nullable' => false,
|
||||
'default' => 1
|
||||
],
|
||||
'securityClass' => [
|
||||
'type' => Schema::DT_VARCHAR128,
|
||||
],
|
||||
'effect' => [
|
||||
'type' => Schema::DT_VARCHAR128
|
||||
],
|
||||
'shattered' => [
|
||||
'type' => Schema::DT_BOOL,
|
||||
'nullable' => false,
|
||||
'default' => 0
|
||||
],
|
||||
'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
|
||||
],
|
||||
'planets' => [
|
||||
'has-many' => ['Model\Universe\PlanetModel', 'systemId']
|
||||
],
|
||||
'statics' => [
|
||||
'has-many' => ['Model\Universe\SystemStaticModel', 'systemId']
|
||||
],
|
||||
'stargates' => [
|
||||
'has-many' => ['Model\Universe\StargateModel', 'systemId']
|
||||
]
|
||||
];
|
||||
|
||||
/**
|
||||
* get system data
|
||||
* -> this includes constellation, region, star, planets as well
|
||||
* @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 = $this->trueSec;
|
||||
$systemData->effect = $this->effect;
|
||||
$systemData->shattered = $this->shattered;
|
||||
|
||||
if($this->starId){
|
||||
$systemData->star = $this->starId->getData();
|
||||
}
|
||||
|
||||
if( !empty($planetsData = $this->getPlanetsData()) ){
|
||||
$systemData->planets = $planetsData;
|
||||
}
|
||||
|
||||
if( !empty($staticsData = $this->getStaticsData()) ){
|
||||
$systemData->statics = $staticsData;
|
||||
}
|
||||
|
||||
if( !empty($stargatesData = $this->getStargatesData()) ){
|
||||
$systemData->stargates = $stargatesData;
|
||||
}
|
||||
|
||||
return $systemData;
|
||||
}
|
||||
|
||||
/**
|
||||
* setter for system name
|
||||
* @param $name
|
||||
* @return mixed
|
||||
*/
|
||||
public function set_name($name){
|
||||
// name should never change
|
||||
// -> important for "Abyssal" systems where ESI don´t have correct system name
|
||||
if(!empty($this->name)){
|
||||
$name = $this->name;
|
||||
}
|
||||
return $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* setter for row (un-formatted) trueSec
|
||||
* @param $secStatus
|
||||
* @return double
|
||||
*/
|
||||
public function set_securityStatus($secStatus){
|
||||
$secStatus = (double)$secStatus;
|
||||
// round for trueSec
|
||||
$positive = ($secStatus > 0);
|
||||
$trueSec = round($secStatus, 1, PHP_ROUND_HALF_DOWN);
|
||||
if($positive && $trueSec <= 0){
|
||||
$trueSec = 0.1;
|
||||
}
|
||||
$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
|
||||
){
|
||||
// "Abyssal" system
|
||||
$security = 'A';
|
||||
}else{
|
||||
// k-space system
|
||||
if($trueSec <= 0){
|
||||
$security = '0.0';
|
||||
}elseif($trueSec < 0.5){
|
||||
$security = 'L';
|
||||
}else{
|
||||
$security = 'H';
|
||||
}
|
||||
}
|
||||
|
||||
$this->security = $security;
|
||||
}
|
||||
return $secStatus;
|
||||
}
|
||||
|
||||
/**
|
||||
* setter for wormhole effect name
|
||||
* @param $effect
|
||||
* @return string|null
|
||||
*/
|
||||
public function set_effect($effect){
|
||||
$effect = (string)$effect;
|
||||
return $effect ? : 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* setter for static systems (wormholes)
|
||||
* -> comma separated string or array
|
||||
* @param $staticNames
|
||||
* @return null
|
||||
*/
|
||||
public function set_staticNames($staticNames){
|
||||
$staticNames = array_unique(is_string($staticNames) ? explode(',', $staticNames) : (array)$staticNames);
|
||||
$this->virtual('staticNames', array_map('strtoupper', $staticNames));
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Event "Hook" function
|
||||
* return false will stop any further action
|
||||
* @param self $self
|
||||
* @param $pkeys
|
||||
* @throws \Exception\ValidationException
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* get data from all planets
|
||||
* @return array
|
||||
*/
|
||||
protected function getPlanetsData(){
|
||||
$planetsData = [];
|
||||
|
||||
if($this->planets){
|
||||
foreach($this->planets as &$planet){
|
||||
/**
|
||||
* @var $planet PlanetModel
|
||||
*/
|
||||
$planetsData[] = $planet->getData();
|
||||
}
|
||||
}
|
||||
return $planetsData;
|
||||
}
|
||||
|
||||
/**
|
||||
* get data from all static wormholes
|
||||
* @return array
|
||||
*/
|
||||
protected function getStaticsData(){
|
||||
$staticsData = [];
|
||||
|
||||
if($this->statics){
|
||||
foreach($this->statics as &$static){
|
||||
/**
|
||||
* @var $static SystemStaticModel
|
||||
*/
|
||||
$staticsData[] = $static->getData();
|
||||
}
|
||||
}
|
||||
return $staticsData;
|
||||
}
|
||||
|
||||
/**
|
||||
* get data from all stargates
|
||||
* @return array
|
||||
*/
|
||||
protected function getStargatesData(){
|
||||
$stargatesData = [];
|
||||
|
||||
if($this->stargates){
|
||||
foreach($this->stargates as &$stargate){
|
||||
/**
|
||||
* @var $stargate StargateModel
|
||||
*/
|
||||
$stargatesData[] = $stargate->getData();
|
||||
}
|
||||
}
|
||||
return $stargatesData;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $id
|
||||
* @param string $accessToken
|
||||
* @param array $additionalOptions
|
||||
*/
|
||||
protected function loadData(int $id, string $accessToken = '', array $additionalOptions = []){
|
||||
$data = self::getF3()->ccpClient->getUniverseSystemData($id);
|
||||
|
||||
if(!empty($data)){
|
||||
/**
|
||||
* @var $constellation ConstellationModel
|
||||
*/
|
||||
$constellation = $this->rel('constellationId');
|
||||
$constellation->loadById($data['constellationId'], $accessToken, $additionalOptions);
|
||||
$data['constellationId'] = $constellation;
|
||||
|
||||
// starId is optional since ESI v4 (e.g. Abyssal systems)
|
||||
if($data['starId']){
|
||||
/**
|
||||
* @var $star StarModel
|
||||
*/
|
||||
$star = $this->rel('starId');
|
||||
$star->loadById($data['starId'], $accessToken, $additionalOptions);
|
||||
$data['starId'] = $star;
|
||||
}
|
||||
|
||||
$this->copyfrom($data, ['id', 'name', 'constellationId', 'starId', 'securityStatus', 'securityClass', 'position']);
|
||||
$this->save();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* load planets data for this system
|
||||
*/
|
||||
public function loadPlanetsData(){
|
||||
if( !$this->dry() ){
|
||||
$data = self::getF3()->ccpClient->getUniverseSystemData($this->_id);
|
||||
if($data['planets']){
|
||||
// planets are optional since ESI v4 (e.g. Abyssal systems)
|
||||
foreach((array)$data['planets'] as $planetData){
|
||||
/**
|
||||
* @var $planet PlanetModel
|
||||
*/
|
||||
$planet = $this->rel('planets');
|
||||
$planet->loadById($planetData->planet_id);
|
||||
$planet->reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* load stargates for this system
|
||||
* -> stargates to destination system which is not in DB get ignored
|
||||
*/
|
||||
public function loadStargatesData(){
|
||||
if( !$this->dry() ){
|
||||
$data = self::getF3()->ccpClient->getUniverseSystemData($this->_id);
|
||||
if(!empty($data)){
|
||||
foreach((array)$data['stargates'] as $stargateId){
|
||||
/**
|
||||
* @var $stargate StargateModel
|
||||
*/
|
||||
$stargate = $this->rel('stargates');
|
||||
$stargate->loadById($stargateId);
|
||||
$stargate->reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
76
app/main/model/universe/systemstaticmodel.php
Normal file
76
app/main/model/universe/systemstaticmodel.php
Normal file
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: exodu
|
||||
* Date: 18.05.2018
|
||||
* Time: 17:50
|
||||
*/
|
||||
|
||||
namespace Model\Universe;
|
||||
|
||||
use DB\SQL\Schema;
|
||||
|
||||
class SystemStaticModel extends BasicUniverseModel {
|
||||
|
||||
protected $table = 'system_static';
|
||||
|
||||
protected $fieldConf = [
|
||||
'systemId' => [
|
||||
'type' => Schema::DT_INT,
|
||||
'index' => true,
|
||||
'belongs-to-one' => 'Model\Universe\SystemModel',
|
||||
'constraint' => [
|
||||
[
|
||||
'table' => 'system',
|
||||
'on-delete' => 'CASCADE'
|
||||
]
|
||||
],
|
||||
'validate' => 'validate_notDry'
|
||||
],
|
||||
'wormholeId' => [
|
||||
'type' => Schema::DT_INT,
|
||||
'index' => true,
|
||||
'belongs-to-one' => 'Model\Universe\WormholeModel',
|
||||
'constraint' => [
|
||||
[
|
||||
'table' => 'wormhole',
|
||||
'on-delete' => 'CASCADE'
|
||||
]
|
||||
],
|
||||
'validate' => 'validate_notDry'
|
||||
]
|
||||
];
|
||||
|
||||
/**
|
||||
* No static columns added
|
||||
* @var bool
|
||||
*/
|
||||
protected $addStaticFields = false;
|
||||
|
||||
/**
|
||||
* get static data
|
||||
* @return null|string
|
||||
*/
|
||||
public function getData(){
|
||||
return $this->wormholeId ? $this->wormholeId->name : null;
|
||||
}
|
||||
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){
|
||||
$status = parent::setup($db,$table,$fields);
|
||||
|
||||
if($status === true){
|
||||
$status = parent::setMultiColumnIndex(['systemId', 'wormholeId'], true);
|
||||
}
|
||||
|
||||
return $status;
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,6 @@
|
||||
|
||||
namespace Model\Universe;
|
||||
|
||||
|
||||
use DB\SQL\Schema;
|
||||
|
||||
class TypeModel extends BasicUniverseModel {
|
||||
@@ -52,9 +51,15 @@ class TypeModel extends BasicUniverseModel {
|
||||
],
|
||||
'groupId' => [
|
||||
'type' => Schema::DT_INT,
|
||||
'nullable' => false,
|
||||
'default' => 0,
|
||||
'index' => true
|
||||
'index' => true,
|
||||
'belongs-to-one' => 'Model\Universe\GroupModel',
|
||||
'constraint' => [
|
||||
[
|
||||
'table' => 'group',
|
||||
'on-delete' => 'CASCADE'
|
||||
]
|
||||
],
|
||||
'validate' => 'validate_notDry',
|
||||
],
|
||||
'marketGroupId' => [
|
||||
'type' => Schema::DT_INT,
|
||||
@@ -80,9 +85,30 @@ class TypeModel extends BasicUniverseModel {
|
||||
],
|
||||
'structures' => [
|
||||
'has-many' => ['Model\Universe\StructureModel', 'typeId']
|
||||
],
|
||||
'planets' => [
|
||||
'has-many' => ['Model\Universe\PlanetModel', 'typeId']
|
||||
],
|
||||
'stars' => [
|
||||
'has-many' => ['Model\Universe\StarModel', 'typeId']
|
||||
],
|
||||
'wormholes' => [
|
||||
'has-many' => ['Model\Universe\WormholeModel', 'typeId']
|
||||
]
|
||||
];
|
||||
|
||||
/**
|
||||
* get type data
|
||||
* @return object
|
||||
*/
|
||||
public function getData(){
|
||||
$typeData = (object) [];
|
||||
$typeData->id = $this->id;
|
||||
$typeData->name = $this->name;
|
||||
|
||||
return $typeData;
|
||||
}
|
||||
|
||||
/**
|
||||
* get shipData from object
|
||||
* -> more fields can be added in here if needed
|
||||
@@ -107,6 +133,10 @@ class TypeModel extends BasicUniverseModel {
|
||||
protected function loadData(int $id, string $accessToken = '', array $additionalOptions = []){
|
||||
$data = self::getF3()->ccpClient->getUniverseTypesData($id, $additionalOptions);
|
||||
if(!empty($data)){
|
||||
$group = $this->rel('groupId');
|
||||
$group->loadById($data['groupId'], $accessToken, $additionalOptions);
|
||||
$data['groupId'] = $group;
|
||||
|
||||
$this->copyfrom($data);
|
||||
$this->save();
|
||||
}
|
||||
|
||||
179
app/main/model/universe/wormholemodel.php
Normal file
179
app/main/model/universe/wormholemodel.php
Normal file
@@ -0,0 +1,179 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: exodu
|
||||
* Date: 13.05.2018
|
||||
* Time: 18:36
|
||||
*/
|
||||
|
||||
namespace Model\Universe;
|
||||
|
||||
use DB\SQL\Schema;
|
||||
|
||||
class WormholeModel extends BasicUniverseModel {
|
||||
|
||||
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' => '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']
|
||||
]
|
||||
];
|
||||
|
||||
/**
|
||||
* 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']
|
||||
){
|
||||
return parent::exportData($fields);
|
||||
}
|
||||
|
||||
protected function loadData(int $id, string $accessToken = '', array $additionalOptions = []){
|
||||
var_dump('loadData');
|
||||
var_dump($id);
|
||||
/*
|
||||
$data = self::getF3()->ccpClient->getUniverseTypesData($id, $additionalOptions);
|
||||
if(!empty($data)){
|
||||
$group = $this->rel('groupId');
|
||||
$group->loadById($data['groupId'], $accessToken, $additionalOptions);
|
||||
$data['groupId'] = $group;
|
||||
|
||||
$this->copyfrom($data);
|
||||
$this->save();
|
||||
} */
|
||||
}
|
||||
|
||||
protected function loadDataByKey(string $key, $value){
|
||||
var_dump('loadData');
|
||||
var_dump($key);
|
||||
var_dump($value);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -50,6 +50,11 @@ class WormholeModel extends BasicModel {
|
||||
'nullable' => false,
|
||||
'default' => 1,
|
||||
'index' => true,
|
||||
],
|
||||
'signatureStrength' => [
|
||||
'type' => Schema::DT_FLOAT,
|
||||
'nullable' => true,
|
||||
'default' => null
|
||||
]
|
||||
];
|
||||
|
||||
@@ -59,6 +64,18 @@ class WormholeModel extends BasicModel {
|
||||
*/
|
||||
protected $addStaticFields = false;
|
||||
|
||||
/**
|
||||
* setter for "signatureStrength"
|
||||
* @param $value
|
||||
* @return mixed|null|string
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function set_signatureStrength($value){
|
||||
$value = (float)$value ? $value : null;
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* get wormhole data as object
|
||||
* @return object
|
||||
@@ -82,6 +99,11 @@ class WormholeModel extends BasicModel {
|
||||
$systemStaticData->massRegeneration = $this->massRegeneration;
|
||||
}
|
||||
|
||||
// signature strength as defined by http://wiki.eve-inspiracy.com/index.php?title=Wormhole_Signature_Strength_List
|
||||
if($this->signatureStrength){
|
||||
$systemStaticData->signatureStrength = $this->signatureStrength;
|
||||
}
|
||||
|
||||
return $systemStaticData;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
[PATHFINDER]
|
||||
NAME = Pathfinder
|
||||
; installed version (used for CSS/JS cache busting)
|
||||
VERSION = v1.3.4
|
||||
VERSION = v1.3.5
|
||||
; contact information [optional]
|
||||
CONTACT = https://github.com/exodus4d
|
||||
; public contact email [optional]
|
||||
@@ -32,6 +32,7 @@ COOKIE_EXPIRE = 30
|
||||
MODE_MAINTENANCE = 0
|
||||
|
||||
; restrict login to specific corporations/alliances by id (e.g. 1000166,1000080)
|
||||
CHARACTER =
|
||||
CORPORATION =
|
||||
ALLIANCE =
|
||||
|
||||
|
||||
@@ -72,3 +72,6 @@ FOREIGN_KEY_CHECKS = ON
|
||||
NODE = 6.0
|
||||
NPM = 3.10.0
|
||||
|
||||
[REQUIREMENTS.DATA]
|
||||
STRUCTURES = 33
|
||||
SHIPS = 490
|
||||
|
||||
@@ -27,6 +27,6 @@
|
||||
"monolog/monolog": "1.*",
|
||||
"websoftwares/monolog-zmq-handler": "0.2.*",
|
||||
"swiftmailer/swiftmailer": "^6.0",
|
||||
"exodus4d/pathfinder_esi": "dev-master#v1.2.3"
|
||||
"exodus4d/pathfinder_esi": "dev-master#v1.2.4"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,81 +1,90 @@
|
||||
"Id";"Name";"Security";"MassTotal";"MassIndividual";"MassRegeneration";"MaxStableTime";
|
||||
"1";"A009";"L";"500000000";"20000000";;"16";
|
||||
"2";"A239";"L";"2000000000";"300000000";;"24";
|
||||
"3";"A641";"H";"2000000000";"1000000000";;"16";
|
||||
"4";"A982";"C6";"3000000000";"300000000";;"24";
|
||||
"5";"B041";"C6";"5000000000";"300000000";"500000000";"48";
|
||||
"6";"B274";"H";"2000000000";"300000000";;"24";
|
||||
"7";"B449";"H";"2000000000";"1000000000";;"16";
|
||||
"8";"B520";"H";"5000000000";"300000000";"500000000";"24";
|
||||
"9";"C008";"C5";;"5000000";"1";"16";
|
||||
"10";"C125";"C2";"1000000000";"20000000";;"16";
|
||||
"11";"C140";"L";"3000000000";"1350000000";;"24";
|
||||
"12";"C247";"C3";"2000000000";"300000000";;"16";
|
||||
"13";"C248";"0.0";"5000000000";"1800000000";"500000000";"24";
|
||||
"14";"C391";"L";"5000000000";"1800000000";"500000000";"24";
|
||||
"15";"D364";"C2";"1000000000";"300000000";;"16";
|
||||
"16";"D382";"C2";"2000000000";"300000000";;"16";
|
||||
"17";"D792";"H";"3000000000";"1000000000";;"24";
|
||||
"18";"D845";"H";"5000000000";"300000000";"500000000";"24";
|
||||
"19";"E004";"C1";;"5000000";"1";"16";
|
||||
"20";"E175";"C4";"2000000000";"300000000";;"16";
|
||||
"21";"E545";"0.0";"2000000000";"300000000";;"24";
|
||||
"22";"G009";"C6";;"5000000";;"16";
|
||||
"23";"G024";"C2";"2000000000";"300000000";;"16";
|
||||
"24";"H121";"C1";"500000000";"20000000";;"16";
|
||||
"25";"H296";"C5";"3000000000";"1350000000";;"24";
|
||||
"26";"H900";"C5";"3000000000";"300000000";;"24";
|
||||
"27";"I182";"C2";"2000000000";"300000000";;"16";
|
||||
"28";"J244";"L";"1000000000";"20000000";;"24";
|
||||
"29";"K329";"0.0";"5000000000";"1800000000";"500000000";"24";
|
||||
"30";"K346";"0.0";"3000000000";"300000000";;"24";
|
||||
"31";"L005";"C2";;"5000000";"1";"16";
|
||||
"32";"L477";"C3";"2000000000";"300000000";;"16";
|
||||
"33";"L614";"C5";"1000000000";"20000000";;"24";
|
||||
"34";"M006";"C4";;"5000000";"1";"16";
|
||||
"35";"M267";"C3";"1000000000";"300000000";;"16";
|
||||
"36";"M555";"C5";"3000000000";"1000000000";;"24";
|
||||
"37";"M609";"C4";"1000000000";"20000000";;"16";
|
||||
"38";"N062";"C5";"3000000000";"300000000";;"24";
|
||||
"39";"N110";"H";"1000000000";"20000000";;"24";
|
||||
"40";"N290";"L";"5000000000";"1800000000";"500000000";"24";
|
||||
"41";"N432";"C5";"3000000000";"1350000000";;"24";
|
||||
"42";"N766";"C2";"2000000000";"300000000";;"16";
|
||||
"43";"N770";"C5";"3000000000";"300000000";;"24";
|
||||
"44";"N944";"L";"3000000000";"1350000000";;"24";
|
||||
"45";"N968";"C3";"2000000000";"300000000";;"16";
|
||||
"46";"O128";"C4";"1000000000";"300000000";"100000000";"24";
|
||||
"47";"O477";"C3";"2000000000";"300000000";;"16";
|
||||
"48";"O883";"C3";"1000000000";"20000000";;"16";
|
||||
"49";"P060";"C1";"500000000";"20000000";;"16";
|
||||
"50";"Q003";"0.0";;"5000000";"1";"16";
|
||||
"51";"Q317";"C1";"500000000";"20000000";;"16";
|
||||
"52";"R051";"L";"3000000000";"1000000000";;"16";
|
||||
"53";"R474";"C6";"3000000000";"300000000";;"24";
|
||||
"54";"R943";"C2";"750000000";"300000000";;"16";
|
||||
"55";"S047";"H";"3000000000";"300000000";;"24";
|
||||
"56";"S199";"0.0";"3000000000";"1350000000";;"24";
|
||||
"57";"S804";"C6";"1000000000";"20000000";;"24";
|
||||
"58";"T405";"C4";"2000000000";"300000000";;"16";
|
||||
"59";"U210";"L";"3000000000";"300000000";;"24";
|
||||
"60";"U319";"C6";"3000000000";"1800000000";"500000000";"48";
|
||||
"61";"U574";"C6";"3000000000";"300000000";;"24";
|
||||
"62";"V283";"0.0";"3000000000";"1000000000";;"24";
|
||||
"63";"V301";"C1";"500000000";"20000000";;"16";
|
||||
"64";"V753";"C6";"3000000000";"1350000000";;"24";
|
||||
"65";"V911";"C5";"3000000000";"1350000000";;"24";
|
||||
"66";"W237";"C6";"3000000000";"1350000000";;"24";
|
||||
"67";"X702";"C3";"1000000000";"300000000";;"24";
|
||||
"68";"X877";"C4";"2000000000";"300000000";;"16";
|
||||
"69";"Y683";"C4";"2000000000";"300000000";;"16";
|
||||
"70";"Y790";"C1";"500000000";"20000000";;"16";
|
||||
"71";"Z006";"C3";;"5000000";"1";"16";
|
||||
"72";"Z060";"0.0";"1000000000";"20000000";;"24";
|
||||
"73";"Z142";"0.0";"3000000000";"1350000000";;"24";
|
||||
"74";"Z457";"C4";"2000000000";"300000000";;"16";
|
||||
"75";"Z647";"C1";"500000000";"20000000";;"16";
|
||||
"76";"Z971";"C1";"100000000";"20000000";;"16";
|
||||
"80";"M001";"C4";;"5000000";"1";"16";
|
||||
"81";"E587";"0.0";"3000000000";"1000000000";;"16";
|
||||
"82";"V898";"L";"2000000000";"300000000";;"16";
|
||||
"83";"Q063";"H";"500000000";"20000000";;"16";
|
||||
"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";;
|
||||
|
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@@ -30,7 +30,7 @@ requirejs.config({
|
||||
text: 'lib/requirejs/text', // v2.0.12 A RequireJS/AMD loader plugin for loading text resources.
|
||||
mustache: 'lib/mustache.min', // v1.0.0 Javascript template engine - http://mustache.github.io
|
||||
localForage: 'lib/localforage.min', // v1.4.2 localStorage library - https://mozilla.github.io/localForage
|
||||
velocity: 'lib/velocity.min', // v1.4.1 animation engine - http://julian.com/research/velocity
|
||||
velocity: 'lib/velocity.min', // v1.5.1 animation engine - http://julian.com/research/velocity
|
||||
velocityUI: 'lib/velocity.ui.min', // v5.2.0 plugin for velocity - http://julian.com/research/velocity/#uiPack
|
||||
slidebars: 'lib/slidebars', // v0.10 Slidebars - side menu plugin http://plugins.adchsm.me/slidebars
|
||||
jsPlumb: 'lib/dom.jsPlumb-1.7.6', // v1.7.6 jsPlumb (Vanilla)- main map draw plugin https://jsplumbtoolkit.com
|
||||
@@ -44,7 +44,7 @@ requirejs.config({
|
||||
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.minified', // v1.8.0 Hover intention - http://cherne.net/brian/resources/jquery.hoverIntent.html
|
||||
hoverIntent: 'lib/jquery.hoverIntent.min', // v1.9.0 Hover intention - http://cherne.net/brian/resources/jquery.hoverIntent.html
|
||||
fullScreen: 'lib/jquery.fullscreen.min', // v0.6.0 Full screen mode - https://github.com/private-face/jquery.fullscreen
|
||||
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
|
||||
@@ -153,7 +153,7 @@ requirejs.config({
|
||||
deps : ['jquery']
|
||||
},
|
||||
select2: {
|
||||
deps : ['jquery'],
|
||||
deps : ['jquery', 'mousewheel'],
|
||||
exports: 'Select2'
|
||||
},
|
||||
validator: {
|
||||
|
||||
@@ -101,7 +101,7 @@ define(['jquery'], function($) {
|
||||
4: 'M609 - C4',
|
||||
5: 'L614 - C5',
|
||||
6: 'S804 - C6',
|
||||
7: 'F135 - Thera'
|
||||
7: 'F353 - Thera'
|
||||
},
|
||||
6: { // ORE
|
||||
1: 'Ordinary Perimeter Deposit', //*
|
||||
|
||||
@@ -17,19 +17,19 @@ define([], function() {
|
||||
effect: 'Damage',
|
||||
value: '+30%'
|
||||
},{
|
||||
effect: 'Missile explosion radius',
|
||||
effect: 'Missile exp. radius',
|
||||
value: '+15%'
|
||||
},{
|
||||
effect: 'Drone Tracking',
|
||||
effect: 'Drone tracking',
|
||||
value: '-15%'
|
||||
},{
|
||||
effect: 'Targeting Range',
|
||||
effect: 'Targeting range',
|
||||
value: '-15%'
|
||||
},{
|
||||
effect: 'Tracking Speed',
|
||||
effect: 'Tracking speed',
|
||||
value: '-15%'
|
||||
},{
|
||||
effect: 'Target Painter Strength',
|
||||
effect: 'Target Painter strength',
|
||||
value: '-15%'
|
||||
}
|
||||
],
|
||||
@@ -38,19 +38,19 @@ define([], function() {
|
||||
effect: 'Damage',
|
||||
value: '+44%'
|
||||
},{
|
||||
effect: 'Missile explosion radius',
|
||||
effect: 'Missile exp. radius',
|
||||
value: '+22%'
|
||||
},{
|
||||
effect: 'Drone Tracking',
|
||||
effect: 'Drone tracking',
|
||||
value: '-22%'
|
||||
},{
|
||||
effect: 'Targeting Range',
|
||||
effect: 'Targeting range',
|
||||
value: '-22%'
|
||||
},{
|
||||
effect: 'Tracking Speed',
|
||||
effect: 'Tracking speed',
|
||||
value: '-22%'
|
||||
},{
|
||||
effect: 'Target Painter Strength',
|
||||
effect: 'Target Painter strength',
|
||||
value: '-22%'
|
||||
}
|
||||
],
|
||||
@@ -59,19 +59,19 @@ define([], function() {
|
||||
effect: 'Damage',
|
||||
value: '+58%'
|
||||
},{
|
||||
effect: 'Missile explosion radius',
|
||||
effect: 'Missile exp. radius',
|
||||
value: '+29%'
|
||||
},{
|
||||
effect: 'Drone Tracking',
|
||||
effect: 'Drone tracking',
|
||||
value: '-29%'
|
||||
},{
|
||||
effect: 'Targeting Range',
|
||||
effect: 'Targeting range',
|
||||
value: '-29%'
|
||||
},{
|
||||
effect: 'Tracking Speed',
|
||||
effect: 'Tracking speed',
|
||||
value: '-29%'
|
||||
},{
|
||||
effect: 'Target Painter Strength',
|
||||
effect: 'Target Painter strength',
|
||||
value: '-29%'
|
||||
}
|
||||
],
|
||||
@@ -80,19 +80,19 @@ define([], function() {
|
||||
effect: 'Damage',
|
||||
value: '+72%'
|
||||
},{
|
||||
effect: 'Missile explosion radius',
|
||||
effect: 'Missile exp. radius',
|
||||
value: '+36%'
|
||||
},{
|
||||
effect: 'Drone Tracking',
|
||||
effect: 'Drone tracking',
|
||||
value: '-36%'
|
||||
},{
|
||||
effect: 'Targeting Range',
|
||||
effect: 'Targeting range',
|
||||
value: '-36%'
|
||||
},{
|
||||
effect: 'Tracking Speed',
|
||||
effect: 'Tracking speed',
|
||||
value: '-36%'
|
||||
},{
|
||||
effect: 'Target Painter Strength',
|
||||
effect: 'Target Painter strength',
|
||||
value: '-36%'
|
||||
}
|
||||
],
|
||||
@@ -101,19 +101,19 @@ define([], function() {
|
||||
effect: 'Damage',
|
||||
value: '+86%'
|
||||
},{
|
||||
effect: 'Missile explosion radius',
|
||||
effect: 'Missile exp. radius',
|
||||
value: '+43%'
|
||||
},{
|
||||
effect: 'Drone Tracking',
|
||||
effect: 'Drone tracking',
|
||||
value: '-43%'
|
||||
},{
|
||||
effect: 'Targeting Range',
|
||||
effect: 'Targeting range',
|
||||
value: '-43%'
|
||||
},{
|
||||
effect: 'Tracking Speed',
|
||||
effect: 'Tracking speed',
|
||||
value: '-43%'
|
||||
},{
|
||||
effect: 'Target Painter Strength',
|
||||
effect: 'Target Painter strength',
|
||||
value: '-43%'
|
||||
}
|
||||
],
|
||||
@@ -122,19 +122,19 @@ define([], function() {
|
||||
effect: 'Damage',
|
||||
value: '+100%'
|
||||
},{
|
||||
effect: 'Missile explosion radius',
|
||||
effect: 'Missile exp. radius',
|
||||
value: '+50%'
|
||||
},{
|
||||
effect: 'Drone Tracking',
|
||||
effect: 'Drone tracking',
|
||||
value: '-50%'
|
||||
},{
|
||||
effect: 'Targeting Range',
|
||||
effect: 'Targeting range',
|
||||
value: '-50%'
|
||||
},{
|
||||
effect: 'Tracking Speed',
|
||||
effect: 'Tracking speed',
|
||||
value: '-50%'
|
||||
},{
|
||||
effect: 'Target Painter Strength',
|
||||
effect: 'Target Painter strength',
|
||||
value: '-50%'
|
||||
}
|
||||
]
|
||||
@@ -142,109 +142,109 @@ define([], function() {
|
||||
redGiant: {
|
||||
1: [
|
||||
{
|
||||
effect: 'Heat Damage',
|
||||
effect: 'Heat damage',
|
||||
value: '+15%'
|
||||
},{
|
||||
effect: 'Overload Bonus',
|
||||
effect: 'Overload bonus',
|
||||
value: '+30%'
|
||||
},{
|
||||
effect: 'Smart Bomb Range',
|
||||
effect: 'Smart Bomb range',
|
||||
value: '+30%'
|
||||
},{
|
||||
effect: 'Smart Bomb Damage',
|
||||
effect: 'Smart Bomb damage',
|
||||
value: '+30%'
|
||||
},{
|
||||
effect: 'Bomb Damage',
|
||||
effect: 'Bomb damage',
|
||||
value: '+30%'
|
||||
}
|
||||
],
|
||||
2: [
|
||||
{
|
||||
effect: 'Heat Damage',
|
||||
effect: 'Heat damage',
|
||||
value: '+22%'
|
||||
},{
|
||||
effect: 'Overload Bonus',
|
||||
effect: 'Overload bonus',
|
||||
value: '+44%'
|
||||
},{
|
||||
effect: 'Smart Bomb Range',
|
||||
effect: 'Smart Bomb range',
|
||||
value: '+44%'
|
||||
},{
|
||||
effect: 'Smart Bomb Damage',
|
||||
effect: 'Smart Bomb damage',
|
||||
value: '+44%'
|
||||
},{
|
||||
effect: 'Bomb Damage',
|
||||
effect: 'Bomb damage',
|
||||
value: '+44%'
|
||||
}
|
||||
],
|
||||
3: [
|
||||
{
|
||||
effect: 'Heat Damage',
|
||||
effect: 'Heat damage',
|
||||
value: '+29%'
|
||||
},{
|
||||
effect: 'Overload Bonus',
|
||||
effect: 'Overload bonus',
|
||||
value: '+58%'
|
||||
},{
|
||||
effect: 'Smart Bomb Range',
|
||||
effect: 'Smart Bomb range',
|
||||
value: '+58%'
|
||||
},{
|
||||
effect: 'Smart Bomb Damage',
|
||||
effect: 'Smart Bomb damage',
|
||||
value: '+58%'
|
||||
},{
|
||||
effect: 'Bomb Damage',
|
||||
effect: 'Bomb damage',
|
||||
value: '+58%'
|
||||
}
|
||||
],
|
||||
4: [
|
||||
{
|
||||
effect: 'Heat Damage',
|
||||
effect: 'Heat damage',
|
||||
value: '+36%'
|
||||
},{
|
||||
effect: 'Overload Bonus',
|
||||
effect: 'Overload bonus',
|
||||
value: '+72%'
|
||||
},{
|
||||
effect: 'Smart Bomb Range',
|
||||
effect: 'Smart Bomb range',
|
||||
value: '+72%'
|
||||
},{
|
||||
effect: 'Smart Bomb Damage',
|
||||
effect: 'Smart Bomb damage',
|
||||
value: '+72%'
|
||||
},{
|
||||
effect: 'Bomb Damage',
|
||||
effect: 'Bomb damage',
|
||||
value: '+72%'
|
||||
}
|
||||
],
|
||||
5: [
|
||||
{
|
||||
effect: 'Heat Damage',
|
||||
effect: 'Heat damage',
|
||||
value: '+43%'
|
||||
},{
|
||||
effect: 'Overload Bonus',
|
||||
effect: 'Overload bonus',
|
||||
value: '+86%'
|
||||
},{
|
||||
effect: 'Smart Bomb Range',
|
||||
effect: 'Smart Bomb range',
|
||||
value: '+86%'
|
||||
},{
|
||||
effect: 'Smart Bomb Damage',
|
||||
effect: 'Smart Bomb damage',
|
||||
value: '+86%'
|
||||
},{
|
||||
effect: 'Bomb Damage',
|
||||
effect: 'Bomb damage',
|
||||
value: '+86%'
|
||||
}
|
||||
],
|
||||
6: [
|
||||
{
|
||||
effect: 'Heat Damage',
|
||||
effect: 'Heat damage',
|
||||
value: '+50%'
|
||||
},{
|
||||
effect: 'Overload Bonus',
|
||||
effect: 'Overload bonus',
|
||||
value: '+100%'
|
||||
},{
|
||||
effect: 'Smart Bomb Range',
|
||||
effect: 'Smart Bomb range',
|
||||
value: '+100%'
|
||||
},{
|
||||
effect: 'Smart Bomb Damage',
|
||||
effect: 'Smart Bomb damage',
|
||||
value: '+100%'
|
||||
},{
|
||||
effect: 'Bomb Damage',
|
||||
effect: 'Bomb damage',
|
||||
value: '+100%'
|
||||
}
|
||||
]
|
||||
@@ -255,7 +255,7 @@ define([], function() {
|
||||
effect: 'Shield HP',
|
||||
value: '+30%'
|
||||
},{
|
||||
effect: 'Armor Resists',
|
||||
effect: 'Armor resist',
|
||||
value: '-15%'
|
||||
},{
|
||||
effect: 'Capacitor recharge',
|
||||
@@ -264,7 +264,7 @@ define([], function() {
|
||||
effect: 'Signature',
|
||||
value: '+30%'
|
||||
},{
|
||||
effect: 'NOS / Neut Drain Amount',
|
||||
effect: 'NOS/Neut drain',
|
||||
value: '+30%'
|
||||
}
|
||||
],
|
||||
@@ -273,7 +273,7 @@ define([], function() {
|
||||
effect: 'Shield HP',
|
||||
value: '+44%'
|
||||
},{
|
||||
effect: 'Armor Resists',
|
||||
effect: 'Armor resist',
|
||||
value: '-22%'
|
||||
},{
|
||||
effect: 'Capacitor recharge',
|
||||
@@ -282,7 +282,7 @@ define([], function() {
|
||||
effect: 'Signature',
|
||||
value: '+44%'
|
||||
},{
|
||||
effect: 'NOS / Neut Drain Amount',
|
||||
effect: 'NOS/Neut drain',
|
||||
value: '+44%'
|
||||
}
|
||||
],
|
||||
@@ -291,7 +291,7 @@ define([], function() {
|
||||
effect: 'Shield HP',
|
||||
value: '+58%'
|
||||
},{
|
||||
effect: 'Armor Resists',
|
||||
effect: 'Armor resist',
|
||||
value: '-29%'
|
||||
},{
|
||||
effect: 'Capacitor recharge',
|
||||
@@ -300,7 +300,7 @@ define([], function() {
|
||||
effect: 'Signature',
|
||||
value: '+58%'
|
||||
},{
|
||||
effect: 'NOS / Neut Drain Amount',
|
||||
effect: 'NOS/Neut drain',
|
||||
value: '+58%'
|
||||
}
|
||||
],
|
||||
@@ -309,7 +309,7 @@ define([], function() {
|
||||
effect: 'Shield HP',
|
||||
value: '+72%'
|
||||
},{
|
||||
effect: 'Armor Resists',
|
||||
effect: 'Armor resist',
|
||||
value: '-36%'
|
||||
},{
|
||||
effect: 'Capacitor recharge',
|
||||
@@ -318,7 +318,7 @@ define([], function() {
|
||||
effect: 'Signature',
|
||||
value: '+72%'
|
||||
},{
|
||||
effect: 'NOS / Neut Drain Amount',
|
||||
effect: 'NOS/Neut drain',
|
||||
value: '+72%'
|
||||
}
|
||||
],
|
||||
@@ -327,7 +327,7 @@ define([], function() {
|
||||
effect: 'Shield HP',
|
||||
value: '+86%'
|
||||
},{
|
||||
effect: 'Armor Resists',
|
||||
effect: 'Armor resist',
|
||||
value: '-43%'
|
||||
},{
|
||||
effect: 'Capacitor recharge',
|
||||
@@ -336,7 +336,7 @@ define([], function() {
|
||||
effect: 'Signature',
|
||||
value: '+86%'
|
||||
},{
|
||||
effect: 'NOS / Neut Drain Amount',
|
||||
effect: 'NOS/Neut drain',
|
||||
value: '+86%'
|
||||
}
|
||||
],
|
||||
@@ -345,7 +345,7 @@ define([], function() {
|
||||
effect: 'Shield HP',
|
||||
value: '+100%'
|
||||
},{
|
||||
effect: 'Armor Resists',
|
||||
effect: 'Armor resist',
|
||||
value: '-50%'
|
||||
},{
|
||||
effect: 'Capacitor recharge',
|
||||
@@ -354,7 +354,7 @@ define([], function() {
|
||||
effect: 'Signature',
|
||||
value: '+100%'
|
||||
},{
|
||||
effect: 'NOS / Neut Drain Amount',
|
||||
effect: 'NOS/Neut drain',
|
||||
value: '+100%'
|
||||
}
|
||||
]
|
||||
@@ -365,13 +365,13 @@ define([], function() {
|
||||
effect: 'Armor HP',
|
||||
value: '+30%'
|
||||
},{
|
||||
effect: 'Shield Resist',
|
||||
effect: 'Shield resist',
|
||||
value: '-15%'
|
||||
},{
|
||||
effect: 'Small Weapon Damage',
|
||||
effect: 'Small Weapon damage',
|
||||
value: '+60%'
|
||||
},{
|
||||
effect: 'Signature Size',
|
||||
effect: 'Signature size',
|
||||
value: '-15%'
|
||||
}
|
||||
],
|
||||
@@ -380,13 +380,13 @@ define([], function() {
|
||||
effect: 'Armor HP',
|
||||
value: '+44%'
|
||||
},{
|
||||
effect: 'Shield Resist',
|
||||
effect: 'Shield resist',
|
||||
value: '-22%'
|
||||
},{
|
||||
effect: 'Small Weapon Damage',
|
||||
effect: 'Small Weapon damage',
|
||||
value: '+88%'
|
||||
},{
|
||||
effect: 'Signature Size',
|
||||
effect: 'Signature size',
|
||||
value: '-22%'
|
||||
}
|
||||
],
|
||||
@@ -395,13 +395,13 @@ define([], function() {
|
||||
effect: 'Armor HP',
|
||||
value: '+58%'
|
||||
},{
|
||||
effect: 'Shield Resist',
|
||||
effect: 'Shield resist',
|
||||
value: '-29%'
|
||||
},{
|
||||
effect: 'Small Weapon Damage',
|
||||
effect: 'Small Weapon damage',
|
||||
value: '+116%'
|
||||
},{
|
||||
effect: 'Signature Size',
|
||||
effect: 'Signature size',
|
||||
value: '-29%'
|
||||
}
|
||||
],
|
||||
@@ -410,13 +410,13 @@ define([], function() {
|
||||
effect: 'Armor HP',
|
||||
value: '+72%'
|
||||
},{
|
||||
effect: 'Shield Resist',
|
||||
effect: 'Shield resist',
|
||||
value: '-36%'
|
||||
},{
|
||||
effect: 'Small Weapon Damage',
|
||||
effect: 'Small Weapon damage',
|
||||
value: '+144%'
|
||||
},{
|
||||
effect: 'Signature Size',
|
||||
effect: 'Signature size',
|
||||
value: '-36%'
|
||||
}
|
||||
],
|
||||
@@ -425,13 +425,13 @@ define([], function() {
|
||||
effect: 'Armor HP',
|
||||
value: '+86%'
|
||||
},{
|
||||
effect: 'Shield Resist',
|
||||
effect: 'Shield resist',
|
||||
value: '-43%'
|
||||
},{
|
||||
effect: 'Small Weapon Damage',
|
||||
effect: 'Small Weapon damage',
|
||||
value: '+172%'
|
||||
},{
|
||||
effect: 'Signature Size',
|
||||
effect: 'Signature size',
|
||||
value: '-43%'
|
||||
}
|
||||
],
|
||||
@@ -440,13 +440,13 @@ define([], function() {
|
||||
effect: 'Armor HP',
|
||||
value: '+100%'
|
||||
},{
|
||||
effect: 'Shield Resist',
|
||||
effect: 'Shield resist',
|
||||
value: '-50%'
|
||||
},{
|
||||
effect: 'Small Weapon Damage',
|
||||
effect: 'Small Weapon damage',
|
||||
value: '+200%'
|
||||
},{
|
||||
effect: 'Signature Size',
|
||||
effect: 'Signature size',
|
||||
value: '-50%'
|
||||
}
|
||||
]
|
||||
@@ -603,13 +603,13 @@ define([], function() {
|
||||
effect: 'Missile velocity',
|
||||
value: '+15%'
|
||||
},{
|
||||
effect: 'Missile Explosion Velocity',
|
||||
effect: 'Missile exp. velocity',
|
||||
value: '+30%'
|
||||
},{
|
||||
effect: 'Ship velocity',
|
||||
value: '+30%'
|
||||
},{
|
||||
effect: 'Stasis Webifier Strength',
|
||||
effect: 'Stasis Webifier strength',
|
||||
value: '-15%'
|
||||
},{
|
||||
effect: 'Inertia',
|
||||
@@ -624,13 +624,13 @@ define([], function() {
|
||||
effect: 'Missile velocity',
|
||||
value: '+22%'
|
||||
},{
|
||||
effect: 'Missile Explosion Velocity',
|
||||
effect: 'Missile exp. velocity',
|
||||
value: '+44%'
|
||||
},{
|
||||
effect: 'Ship velocity',
|
||||
value: '+44%'
|
||||
},{
|
||||
effect: 'Stasis Webifier Strength',
|
||||
effect: 'Stasis Webifier strength',
|
||||
value: '-22%'
|
||||
},{
|
||||
effect: 'Inertia',
|
||||
@@ -645,13 +645,13 @@ define([], function() {
|
||||
effect: 'Missile velocity',
|
||||
value: '+29%'
|
||||
},{
|
||||
effect: 'Missile Explosion Velocity',
|
||||
effect: 'Missile exp. velocity',
|
||||
value: '+58%'
|
||||
},{
|
||||
effect: 'Ship velocity',
|
||||
value: '+58%'
|
||||
},{
|
||||
effect: 'Stasis Webifier Strength',
|
||||
effect: 'Stasis Webifier strength',
|
||||
value: '-29%'
|
||||
},{
|
||||
effect: 'Inertia',
|
||||
@@ -666,13 +666,13 @@ define([], function() {
|
||||
effect: 'Missile velocity',
|
||||
value: '+36%'
|
||||
},{
|
||||
effect: 'Missile Explosion Velocity',
|
||||
effect: 'Missile exp. velocity',
|
||||
value: '+72%'
|
||||
},{
|
||||
effect: 'Ship velocity',
|
||||
value: '+72%'
|
||||
},{
|
||||
effect: 'Stasis Webifier Strength',
|
||||
effect: 'Stasis Webifier strength',
|
||||
value: '-36%'
|
||||
},{
|
||||
effect: 'Inertia',
|
||||
@@ -687,13 +687,13 @@ define([], function() {
|
||||
effect: 'Missile velocity',
|
||||
value: '+43%'
|
||||
},{
|
||||
effect: 'Missile Explosion Velocity',
|
||||
effect: 'Missile exp. velocity',
|
||||
value: '+86%'
|
||||
},{
|
||||
effect: 'Ship velocity',
|
||||
value: '+86%'
|
||||
},{
|
||||
effect: 'Stasis Webifier Strength',
|
||||
effect: 'Stasis Webifier strength',
|
||||
value: '-43%'
|
||||
},{
|
||||
effect: 'Inertia',
|
||||
@@ -708,13 +708,13 @@ define([], function() {
|
||||
effect: 'Missile velocity',
|
||||
value: '+50%'
|
||||
},{
|
||||
effect: 'Missile Explosion Velocity',
|
||||
effect: 'Missile exp. velocity',
|
||||
value: '+100%'
|
||||
},{
|
||||
effect: 'Ship velocity',
|
||||
value: '+100%'
|
||||
},{
|
||||
effect: 'Stasis Webifier Strength',
|
||||
effect: 'Stasis Webifier strength',
|
||||
value: '-50%'
|
||||
},{
|
||||
effect: 'Inertia',
|
||||
|
||||
@@ -67,9 +67,10 @@ define([
|
||||
*/
|
||||
$.fn.destroyTimestampCounter = function(){
|
||||
return this.each(function(){
|
||||
let element = $(this);
|
||||
element.find('[data-counter="init"]').each(function(){
|
||||
let interval = $(this).data('interval');
|
||||
let parentElement = $(this);
|
||||
parentElement.find('[data-counter="init"]').each(function(){
|
||||
let element = $(this);
|
||||
let interval = element.data('interval');
|
||||
if(interval){
|
||||
clearInterval(interval);
|
||||
element.removeAttr('data-counter')
|
||||
|
||||
@@ -33,6 +33,7 @@ define(['jquery'], ($) => {
|
||||
getMapConnectionData: '/api/map/getConnectionData', // ajax URL - get connection data
|
||||
getMapLogData: '/api/map/getLogData', // ajax URL - get logs data
|
||||
// system API
|
||||
getSystemData: '/api/system/getData', // ajax URL - get system data
|
||||
searchSystem: '/api/system/search', // ajax URL - search system by name
|
||||
saveSystem: '/api/system/save', // ajax URL - saves system to map
|
||||
deleteSystem: '/api/system/delete', // ajax URL - delete system from map
|
||||
@@ -47,10 +48,15 @@ define(['jquery'], ($) => {
|
||||
getSignatures: '/api/signature/getAll', // ajax URL - get all signature data for system
|
||||
saveSignatureData: '/api/signature/save', // ajax URL - save signature data for system
|
||||
deleteSignatureData: '/api/signature/delete', // ajax URL - delete signature data for system
|
||||
// structure API
|
||||
saveStructureData: '/api/structure/save', // ajax URL - save structure data
|
||||
deleteStructureData: '/api/structure/delete', // ajax URL - delete structure data
|
||||
// route API
|
||||
searchRoute: '/api/route/search', // ajax URL - search system routes
|
||||
// stats API
|
||||
getStatisticsData: '/api/statistic/getData', // ajax URL - get statistics data (activity log)
|
||||
// universe API
|
||||
searchUniverseData: '/api/universe/search', // ajax URL - search universe data
|
||||
// GitHub API
|
||||
gitHubReleases: '/api/github/releases' // ajax URL - get release info from GitHub
|
||||
},
|
||||
@@ -194,6 +200,9 @@ define(['jquery'], ($) => {
|
||||
security: {
|
||||
class: 'pf-system-sec'
|
||||
},
|
||||
'A': {
|
||||
class: 'pf-system-sec-abyssal'
|
||||
},
|
||||
'SH': {
|
||||
class: 'pf-system-sec-unknown'
|
||||
},
|
||||
@@ -278,6 +287,12 @@ define(['jquery'], ($) => {
|
||||
defaultMapScope: 'wh', // default scope for connection
|
||||
// map connection types
|
||||
connectionTypes: {
|
||||
abyssal: {
|
||||
cssClass: 'pf-map-connection-abyssal',
|
||||
paintStyle: {
|
||||
dashstyle: '0.5 2' // dotted line
|
||||
}
|
||||
},
|
||||
jumpbridge: {
|
||||
cssClass: 'pf-map-connection-jumpbridge',
|
||||
paintStyle: {
|
||||
|
||||
@@ -25,11 +25,11 @@ define([
|
||||
label: 'Reload tab',
|
||||
keyNames: ['CONTROL', 'R']
|
||||
},
|
||||
signaturePaste: {
|
||||
clipboardPaste: {
|
||||
group: 'global',
|
||||
label: 'Paste signatures from clipboard',
|
||||
label: 'Update signatures/D-Scan from clipboard',
|
||||
keyNames: ['CONTROL', 'V'],
|
||||
alias: 'paste'
|
||||
alias: 'paste'
|
||||
},
|
||||
|
||||
// map ----------------------------------------------------------------------------------------------
|
||||
|
||||
@@ -105,6 +105,17 @@ define([
|
||||
let ssoButtonElement = $('.' + config.ssoButtonClass);
|
||||
let cookieHintElement = $('#' + config.cookieHintId);
|
||||
|
||||
$(document).on('click', '.' + config.characterSelectionClass + ' a', function(){
|
||||
$('.' + config.splashOverlayClass).showSplashOverlay();
|
||||
});
|
||||
|
||||
$(document).on('click', '.' + config.ssoButtonClass , function(){
|
||||
if(Util.getCookie('cookie') === '1'){
|
||||
// ... cookies accepted no "confirm" shown
|
||||
$('.' + config.splashOverlayClass).showSplashOverlay();
|
||||
}
|
||||
});
|
||||
|
||||
// cookie hint --------------------------------------------------------
|
||||
cookieHintElement.find('.btn-success').on('click', function(){
|
||||
setAcceptCookie();
|
||||
@@ -265,6 +276,7 @@ define([
|
||||
titleProperty: 'imgTitle',
|
||||
transitionSpeed: 600,
|
||||
slideshowInterval: 5000,
|
||||
preloadRange: 1,
|
||||
onopened: function () {
|
||||
// Callback function executed when the Gallery has been initialized
|
||||
// and the initialization transition has been completed.
|
||||
@@ -616,6 +628,7 @@ define([
|
||||
case 'UNKNOWN':
|
||||
label = 'ERROR';
|
||||
break;
|
||||
case 'CHARACTER':
|
||||
case 'CORPORATION':
|
||||
case 'ALLIANCE':
|
||||
label = 'INVALID';
|
||||
@@ -682,11 +695,6 @@ define([
|
||||
let content = Mustache.render(template, data);
|
||||
this.characterElement.html(content);
|
||||
|
||||
// lock character selection on click (prevent click spamming)
|
||||
this.characterElement.find('a').on('click', function(){
|
||||
$('.' + config.splashOverlayClass).showSplashOverlay();
|
||||
});
|
||||
|
||||
// show character panel (animation settings)
|
||||
initCharacterAnimation(this.characterElement.find('.' + config.characterImageWrapperClass));
|
||||
}else{
|
||||
|
||||
@@ -14,7 +14,6 @@ define([
|
||||
'app/map/magnetizing',
|
||||
'app/map/scrollbar',
|
||||
'dragToSelect',
|
||||
'select2',
|
||||
'app/map/contextmenu',
|
||||
'app/map/overlay',
|
||||
'app/map/local'
|
||||
@@ -37,14 +36,13 @@ define([
|
||||
systemHeadClass: 'pf-system-head', // class for system head
|
||||
systemHeadNameClass: 'pf-system-head-name', // class for system name
|
||||
systemHeadExpandClass: 'pf-system-head-expand', // class for system head expand arrow
|
||||
systemHeadInfoClass: 'pf-system-head-info', // class for system info
|
||||
systemBodyClass: 'pf-system-body', // class for system body
|
||||
systemBodyItemHeight: 16, // px of a system body entry
|
||||
systemBodyItemClass: 'pf-system-body-item', // class for a system body entry
|
||||
systemBodyItemStatusClass: 'pf-user-status', // class for player status in system body
|
||||
systemBodyItemNameClass: 'pf-system-body-item-name', // class for player name in system body
|
||||
systemBodyRightClass: 'pf-system-body-right', // class for player ship name in system body
|
||||
systemTooltipInnerClass: 'pf-system-tooltip-inner', // class for system tooltip content
|
||||
systemTooltipInnerIdPrefix: 'pf-system-tooltip-inner-', // id prefix for system tooltip content
|
||||
dynamicElementWrapperId: 'pf-dialog-wrapper', // wrapper div for dynamic content (dialogs, context-menus,...)
|
||||
|
||||
// endpoint classes
|
||||
@@ -60,6 +58,8 @@ define([
|
||||
systemDialogId: 'pf-system-dialog', // id for system dialog
|
||||
systemDialogSelectClass: 'pf-system-dialog-select', // class for system select Element
|
||||
|
||||
popoverTriggerClass: 'pf-popover-trigger', // class for "popover" trigger elements
|
||||
|
||||
// system security classes
|
||||
systemSec: 'pf-system-sec'
|
||||
};
|
||||
@@ -83,7 +83,9 @@ define([
|
||||
let effectClass = MapUtil.getEffectInfoForSystem('effect', 'class');
|
||||
return (
|
||||
target.hasClass(config.systemHeadNameClass) ||
|
||||
target.hasClass(effectClass)
|
||||
target.hasClass(effectClass) ||
|
||||
target.hasClass(config.systemHeadExpandClass) ||
|
||||
target.hasClass(config.systemHeadInfoClass)
|
||||
);
|
||||
};
|
||||
|
||||
@@ -120,8 +122,6 @@ define([
|
||||
connectionTypes: Init.connectionTypes
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* updates a system with current information
|
||||
* @param map
|
||||
@@ -129,7 +129,6 @@ define([
|
||||
* @param currentUserIsHere boolean - if the current user is in this system
|
||||
*/
|
||||
$.fn.updateSystemUserData = function(map, data, currentUserIsHere){
|
||||
|
||||
let system = $(this);
|
||||
let systemId = system.attr('id');
|
||||
|
||||
@@ -208,21 +207,17 @@ define([
|
||||
// user count changed -> change tooltip content
|
||||
|
||||
// set tooltip color
|
||||
let highlight = false;
|
||||
let tooltipIconClass = '';
|
||||
let highlight = '';
|
||||
if(userCounter > oldUserCount){
|
||||
highlight = 'good';
|
||||
tooltipIconClass = 'fa-caret-up';
|
||||
}else if(userCounter < oldUserCount){
|
||||
highlight = 'bad';
|
||||
tooltipIconClass = 'fa-caret-down';
|
||||
}
|
||||
|
||||
let tooltipOptions = {
|
||||
trigger: 'manual',
|
||||
id: systemId,
|
||||
systemId: systemId,
|
||||
highlight: highlight,
|
||||
title: '<i class="fas ' + tooltipIconClass + '"></i> ' + userCounter
|
||||
userCount: userCounter
|
||||
};
|
||||
|
||||
// show system head
|
||||
@@ -232,13 +227,12 @@ define([
|
||||
duration: 50,
|
||||
display: 'inline-block',
|
||||
progress: function(){
|
||||
//revalidate element size and repaint
|
||||
//re-validate element size and repaint
|
||||
map.revalidate( systemId );
|
||||
},
|
||||
complete: function(){
|
||||
// show system body
|
||||
system.toggleBody(true, map, {complete: function(system){
|
||||
// complete callback function
|
||||
// show active user tooltip
|
||||
system.toggleSystemTooltip('show', tooltipOptions);
|
||||
}});
|
||||
@@ -323,133 +317,6 @@ define([
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* show/hide systems tooltip
|
||||
* @param show
|
||||
* @param options
|
||||
*/
|
||||
$.fn.toggleSystemTooltip = function(show, options){
|
||||
|
||||
// tooltip colors
|
||||
let colorClasses = {
|
||||
good: 'txt-color-green',
|
||||
bad: 'txt-color-red'
|
||||
};
|
||||
|
||||
return this.each(function(){
|
||||
let system = $(this);
|
||||
let tooltipId = 0;
|
||||
let tooltipClassHighlight = false;
|
||||
|
||||
// do not update tooltips while a system is dragged
|
||||
if(system.hasClass('jsPlumb_dragged')){
|
||||
// skip system
|
||||
return true;
|
||||
}
|
||||
|
||||
if(show === 'destroy'){
|
||||
system.tooltip( show );
|
||||
system.removeAttr('data-original-title');
|
||||
}else if(show === 'hide'){
|
||||
system.tooltip( show );
|
||||
} else if(show === 'toggle'){
|
||||
system.tooltip( show );
|
||||
}else if(show === 'show'){
|
||||
|
||||
// check if tooltip is currently visible
|
||||
let tooltipActive = (system.attr('aria-describedby') !== undefined);
|
||||
|
||||
if(options === undefined){
|
||||
options = {};
|
||||
}
|
||||
|
||||
// optional color highlight
|
||||
if(colorClasses.hasOwnProperty( options.highlight )){
|
||||
tooltipClassHighlight = colorClasses[ options.highlight ];
|
||||
}
|
||||
|
||||
if(
|
||||
tooltipActive === false &&
|
||||
options.id
|
||||
){
|
||||
|
||||
// init new tooltip
|
||||
tooltipId = config.systemTooltipInnerIdPrefix + options.id;
|
||||
|
||||
let template = '<div class="tooltip" role="tooltip">' +
|
||||
'<div class="tooltip-arrow"></div>' +
|
||||
'<div id="' + tooltipId + '" class="tooltip-inner txt-color ' + config.systemTooltipInnerClass + '"></div>' +
|
||||
'</div>';
|
||||
|
||||
options.placement = getSystemTooltipPlacement(system);
|
||||
options.html = true;
|
||||
options.animation = true;
|
||||
options.template = template;
|
||||
options.viewport = system.parent('.' + config.mapClass);
|
||||
|
||||
system.attr('title', options.title);
|
||||
|
||||
system.tooltip(options);
|
||||
|
||||
system.tooltip(show);
|
||||
|
||||
if(tooltipClassHighlight !== false){
|
||||
// set tooltip observer and set new class after open -> due to transition effect
|
||||
|
||||
system.on('shown.bs.tooltip', function() {
|
||||
$('#' + tooltipId).addClass( tooltipClassHighlight );
|
||||
// remove observer -> color should not be changed every time a tooltip toggles e.g. dragging system
|
||||
$(this).off('shown.bs.tooltip');
|
||||
});
|
||||
}
|
||||
}else{
|
||||
// update/change/toggle tooltip text or color without tooltip reload
|
||||
|
||||
let tooltipInner = false;
|
||||
if(
|
||||
options.title ||
|
||||
tooltipClassHighlight !== false
|
||||
){
|
||||
tooltipInner = system.tooltip('fixTitle')
|
||||
.data('bs.tooltip')
|
||||
.$tip.find('.tooltip-inner');
|
||||
|
||||
if(options.title){
|
||||
tooltipInner.html( options.title );
|
||||
}
|
||||
|
||||
if(tooltipClassHighlight !== false){
|
||||
tooltipInner.removeClass( colorClasses.good + ' ' + colorClasses.bad).addClass(tooltipClassHighlight);
|
||||
}
|
||||
}
|
||||
|
||||
// update tooltip placement based on system position
|
||||
if (system.data('bs.tooltip')) {
|
||||
system.data('bs.tooltip').options.placement = getSystemTooltipPlacement(system);
|
||||
}
|
||||
|
||||
// show() can be forced
|
||||
if(options.show === true){
|
||||
system.tooltip('show');
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* get tooltip position based on current system position
|
||||
* @param system
|
||||
* @returns {string}
|
||||
*/
|
||||
let getSystemTooltipPlacement = (system) => {
|
||||
let offsetParent = system.parent().offset();
|
||||
let offsetSystem = system.offset();
|
||||
|
||||
return (offsetSystem.top - offsetParent.top < 27) ? 'bottom' : 'top';
|
||||
};
|
||||
|
||||
/**
|
||||
* set or change the status of a system
|
||||
* @param status
|
||||
@@ -490,7 +357,6 @@ define([
|
||||
if(!system){
|
||||
// set system name or alias
|
||||
let systemName = data.name;
|
||||
|
||||
if(
|
||||
data.alias &&
|
||||
data.alias !== ''
|
||||
@@ -498,6 +364,12 @@ define([
|
||||
systemName = data.alias;
|
||||
}
|
||||
|
||||
let systemHeadClasses = [config.systemHeadNameClass];
|
||||
// Abyssal system
|
||||
if(data.type.id === 3){
|
||||
systemHeadClasses.push(Util.config.fontTriglivianClass);
|
||||
}
|
||||
|
||||
// get system info classes
|
||||
let effectBasicClass = MapUtil.getEffectInfoForSystem('effect', 'class');
|
||||
let effectName = MapUtil.getEffectInfoForSystem(data.effect, 'name');
|
||||
@@ -505,41 +377,35 @@ define([
|
||||
let secClass = Util.getSecurityClassForSystem(data.security);
|
||||
|
||||
system = $('<div>', {
|
||||
// system
|
||||
id: systemId,
|
||||
class: config.systemClass
|
||||
}).append(
|
||||
// system head
|
||||
$('<div>', {
|
||||
class: config.systemHeadClass
|
||||
}).append(
|
||||
// System name is editable
|
||||
$('<span>', {
|
||||
class: config.systemHeadNameClass
|
||||
}).attr('data-value', systemName)
|
||||
).append(
|
||||
// System locked status
|
||||
$('<i>', {
|
||||
class: ['fas', 'fa-lock', 'fa-fw'].join(' ')
|
||||
}).attr('title', 'locked')
|
||||
).append(
|
||||
// System effect color
|
||||
$('<i>', {
|
||||
class: ['fas', 'fa-square ', 'fa-fw', effectBasicClass, effectClass].join(' ')
|
||||
}).attr('title', effectName)
|
||||
).append(
|
||||
// expand option
|
||||
$('<i>', {
|
||||
class: ['fas', 'fa-angle-down ', config.systemHeadExpandClass].join(' ')
|
||||
})
|
||||
).prepend(
|
||||
$('<span>', {
|
||||
class: [config.systemSec, secClass].join(' '),
|
||||
text: data.security
|
||||
})
|
||||
)
|
||||
).append(
|
||||
// system body
|
||||
}),
|
||||
// System name is editable
|
||||
$('<span>', {
|
||||
class: systemHeadClasses.join(' '),
|
||||
}).attr('data-value', systemName),
|
||||
// System locked status
|
||||
$('<i>', {
|
||||
class: ['fas', 'fa-lock', 'fa-fw'].join(' ')
|
||||
}).attr('title', 'locked'),
|
||||
// System effect color
|
||||
$('<i>', {
|
||||
class: ['fas', 'fa-square', 'fa-fw', effectBasicClass, effectClass, config.popoverTriggerClass].join(' ')
|
||||
}),
|
||||
// expand option
|
||||
$('<i>', {
|
||||
class: ['fas', 'fa-angle-down', config.systemHeadExpandClass].join(' ')
|
||||
}),
|
||||
// info element (new line) (optional)
|
||||
System.getHeadInfoElement(data)
|
||||
),
|
||||
$('<div>', {
|
||||
class: config.systemBodyClass
|
||||
})
|
||||
@@ -574,6 +440,9 @@ define([
|
||||
// hide system tooltip
|
||||
$(system).toggleSystemTooltip('hide', {});
|
||||
|
||||
// destroy popovers
|
||||
$(system).destroyPopover(true);
|
||||
|
||||
// move them to the "top"
|
||||
$(system).updateSystemZIndex();
|
||||
},
|
||||
@@ -749,7 +618,7 @@ define([
|
||||
}
|
||||
}.bind(connection);
|
||||
|
||||
connectionCanvas.singleDoubleClick(single, () => {});
|
||||
Util.singleDoubleClick(connectionCanvas, single, () => {});
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -1009,7 +878,6 @@ define([
|
||||
|
||||
checkMapSize(mapWrapper[0]);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -1506,7 +1374,7 @@ define([
|
||||
* @param systemData
|
||||
* @param connectedSystem
|
||||
*/
|
||||
let drawSystem = function(map, systemData, connectedSystem){
|
||||
let drawSystem = (map, systemData, connectedSystem) => {
|
||||
|
||||
// check if systemData is valid
|
||||
if(isValidSystem(systemData)){
|
||||
@@ -1552,7 +1420,7 @@ define([
|
||||
* @param requestData
|
||||
* @param context
|
||||
*/
|
||||
let saveSystem = function(requestData, context){
|
||||
let saveSystem = (requestData, context) => {
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: Init.path.saveSystem,
|
||||
@@ -1963,7 +1831,6 @@ define([
|
||||
dataType: 'json',
|
||||
context: connections
|
||||
}).done(function(data){
|
||||
|
||||
// remove connections from map
|
||||
removeConnections(this);
|
||||
|
||||
@@ -1971,7 +1838,6 @@ define([
|
||||
if(callback){
|
||||
callback();
|
||||
}
|
||||
|
||||
}).fail(function( jqXHR, status, error) {
|
||||
let reason = status + ' ' + error;
|
||||
Util.showNotify({title: jqXHR.status + ': deleteSystem', text: reason, type: 'warning'});
|
||||
@@ -1998,7 +1864,8 @@ define([
|
||||
{icon: 'fa-filter', action: 'filter_scope', text: 'filter scope', subitems: [
|
||||
{subIcon: '', subAction: 'filter_wh', subText: 'wormhole'},
|
||||
{subIcon: '', subAction: 'filter_stargate', subText: 'stargate'},
|
||||
{subIcon: '', subAction: 'filter_jumpbridge', subText: 'jumpbridge'}
|
||||
{subIcon: '', subAction: 'filter_jumpbridge', subText: 'jumpbridge'},
|
||||
{subIcon: '', subAction: 'filter_abyssal', subText: 'abyssal'}
|
||||
]},
|
||||
{icon: 'fa-sitemap', action: 'map', text: 'map', subitems: [
|
||||
{subIcon: 'fa-edit', subAction: 'map_edit', subText: 'edit map'},
|
||||
@@ -2040,7 +1907,7 @@ define([
|
||||
{subIcon: 'fa-circle', subAction: 'status_critical', subText: 'stage 3 (critical)'}
|
||||
|
||||
]},
|
||||
{divider: true, action: 'delete_connection'} ,
|
||||
{divider: true, action: 'separator'} ,
|
||||
{icon: 'fa-unlink', action: 'delete_connection', text: 'detach'}
|
||||
]
|
||||
};
|
||||
@@ -2106,7 +1973,14 @@ define([
|
||||
|
||||
let scope = component.scope;
|
||||
|
||||
if(scope === 'stargate'){
|
||||
if(scope === 'abyssal'){
|
||||
hiddenOptions.push('frigate');
|
||||
hiddenOptions.push('preserve_mass');
|
||||
hiddenOptions.push('change_status');
|
||||
|
||||
hiddenOptions.push('change_scope');
|
||||
hiddenOptions.push('separator');
|
||||
}else if(scope === 'stargate'){
|
||||
hiddenOptions.push('frigate');
|
||||
hiddenOptions.push('preserve_mass');
|
||||
hiddenOptions.push('change_status');
|
||||
@@ -2173,6 +2047,9 @@ define([
|
||||
if(component.data('filter_scope') === 'jumpbridge'){
|
||||
activeOptions.push('filter_jumpbridge');
|
||||
}
|
||||
if(component.data('filter_scope') === 'abyssal'){
|
||||
activeOptions.push('filter_abyssal');
|
||||
}
|
||||
}else if( component.hasClass(config.systemClass) ){
|
||||
// active system menu entries
|
||||
if(component.data('locked') === true){
|
||||
@@ -2191,7 +2068,7 @@ define([
|
||||
* @param map
|
||||
* @param system
|
||||
*/
|
||||
let setSystemObserver = function(map, system){
|
||||
let setSystemObserver = (map, system) => {
|
||||
system = $(system);
|
||||
|
||||
// get map container
|
||||
@@ -2235,6 +2112,9 @@ define([
|
||||
// hide tooltip
|
||||
$(selectedSystems).toggleSystemTooltip('hide', {});
|
||||
|
||||
// destroy popovers
|
||||
$(selectedSystems).destroyPopover(true);
|
||||
|
||||
// move them to the "top"
|
||||
$(selectedSystems).updateSystemZIndex();
|
||||
},
|
||||
@@ -2292,6 +2172,8 @@ define([
|
||||
}
|
||||
|
||||
// init system tooltips =======================================================================================
|
||||
// TODO check this code:
|
||||
/*
|
||||
let systemTooltipOptions = {
|
||||
toggle: 'tooltip',
|
||||
placement: 'right',
|
||||
@@ -2300,79 +2182,7 @@ define([
|
||||
};
|
||||
|
||||
system.find('.fas').tooltip(systemTooltipOptions);
|
||||
|
||||
// init system body expand ====================================================================================
|
||||
systemHeadExpand.hoverIntent(function(e){
|
||||
// hover in
|
||||
let hoverSystem = $(this).parents('.' + config.systemClass);
|
||||
let hoverSystemId = hoverSystem.attr('id');
|
||||
|
||||
// bring system in front (increase zIndex)
|
||||
hoverSystem.updateSystemZIndex();
|
||||
|
||||
// get ship counter and calculate expand height
|
||||
let userCount = parseInt( hoverSystem.data('userCount') );
|
||||
|
||||
let expandHeight = userCount * config.systemBodyItemHeight;
|
||||
|
||||
systemBody.velocity('stop').velocity(
|
||||
{
|
||||
height: expandHeight + 'px',
|
||||
width: 150,
|
||||
'min-width': '150px'
|
||||
},{
|
||||
easing: 'easeInOutQuart',
|
||||
duration: 150,
|
||||
progress: function(){
|
||||
// repaint connections of current system
|
||||
map.revalidate( hoverSystemId );
|
||||
},
|
||||
complete: function(){
|
||||
map.revalidate( hoverSystemId );
|
||||
|
||||
// extend player name element
|
||||
$(this).find('.' + config.systemBodyItemNameClass).css({width: '80px'});
|
||||
|
||||
$(this).find('.' + config.systemBodyRightClass).velocity('stop').velocity({
|
||||
opacity: 1
|
||||
},{
|
||||
duration: 150,
|
||||
display: 'auto'
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
}, function(e){
|
||||
// hover out
|
||||
let hoverSystem = $(this).parents('.' + config.systemClass);
|
||||
let hoverSystemId = hoverSystem.attr('id');
|
||||
|
||||
// stop animation (prevent visual bug if user spams hover-icon [in - out])
|
||||
systemBody.velocity('stop');
|
||||
|
||||
// reduce player name element back to "normal" size (css class width is used)
|
||||
systemBody.find('.' + config.systemBodyItemNameClass).css({width: ''});
|
||||
|
||||
systemBody.find('.' + config.systemBodyRightClass).velocity('stop').velocity( {
|
||||
opacity: 0,
|
||||
'min-width': '0px'
|
||||
},{
|
||||
easing: 'easeInOutQuart',
|
||||
duration: 150,
|
||||
display: 'none',
|
||||
complete: function(){
|
||||
systemBody.velocity('stop').velocity('reverse', {
|
||||
complete: function(){
|
||||
// overwrite "complete" function from first "hover"-open
|
||||
map.revalidate( hoverSystemId );
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
*/
|
||||
// context menu ===============================================================================================
|
||||
|
||||
// trigger context menu
|
||||
@@ -2482,7 +2292,6 @@ define([
|
||||
};
|
||||
|
||||
let single = function(e){
|
||||
|
||||
// check if click was performed on "popover" (x-editable)
|
||||
let popoverClick = false;
|
||||
if( $(e.target).parents('.popover').length ){
|
||||
@@ -2511,7 +2320,7 @@ define([
|
||||
|
||||
};
|
||||
|
||||
system.singleDoubleClick(single, double);
|
||||
Util.singleDoubleClick(system, single, double);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -2698,6 +2507,11 @@ define([
|
||||
return false;
|
||||
}
|
||||
|
||||
// switch connection type to "abyss" in case source OR target system belongs to "a-space"
|
||||
if(sourceSystem.data('typeId') === 3 || targetSystem.data('typeId') === 3){
|
||||
setConnectionScope(connection, 'abyssal');
|
||||
}
|
||||
|
||||
// set "default" connection status only for NEW connections
|
||||
if(!connection.suspendedElement){
|
||||
MapUtil.setConnectionWHStatus(connection, MapUtil.getDefaultConnectionTypeByScope(connection.scope) );
|
||||
@@ -2823,6 +2637,7 @@ define([
|
||||
case 'filter_wh':
|
||||
case 'filter_stargate':
|
||||
case 'filter_jumpbridge':
|
||||
case 'filter_abyssal':
|
||||
// filter (show/hide)
|
||||
let filterScope = action.split('_')[1];
|
||||
|
||||
@@ -2919,6 +2734,119 @@ define([
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// system body expand -----------------------------------------------------------------------------------------
|
||||
mapContainer.hoverIntent({
|
||||
over: function(e){
|
||||
let system = $(this).closest('.' + config.systemClass);
|
||||
let map = MapUtil.getMapInstance(system.attr('data-mapid'));
|
||||
let systemId = system.attr('id');
|
||||
let systemBody = system.find('.' + config.systemBodyClass);
|
||||
|
||||
// bring system in front (increase zIndex)
|
||||
system.updateSystemZIndex();
|
||||
|
||||
// get ship counter and calculate expand height
|
||||
let userCount = parseInt(system.data('userCount'));
|
||||
let expandHeight = userCount * config.systemBodyItemHeight;
|
||||
|
||||
// calculate width
|
||||
let width = system[0].clientWidth;
|
||||
let minWidth = 150;
|
||||
let newWidth = width > minWidth ? width : minWidth; // in case of big systems
|
||||
|
||||
systemBody.velocity('stop').velocity(
|
||||
{
|
||||
height: expandHeight + 'px',
|
||||
width: newWidth,
|
||||
'min-width': minWidth + 'px'
|
||||
},{
|
||||
easing: 'easeInOutQuart',
|
||||
duration: 120,
|
||||
progress: function(){
|
||||
// repaint connections of current system
|
||||
map.revalidate(systemId);
|
||||
},
|
||||
complete: function(){
|
||||
map.revalidate(systemId);
|
||||
|
||||
// extend player name element
|
||||
let systemBody = $(this);
|
||||
let systemBodyItemNameWidth = newWidth - 50 - 10 - 20; // - bodyRight - icon - somePadding
|
||||
systemBody.find('.' + config.systemBodyItemNameClass).css({width: systemBodyItemNameWidth + 'px'});
|
||||
systemBody.find('.' + config.systemBodyRightClass).show();
|
||||
}
|
||||
}
|
||||
);
|
||||
},
|
||||
out: function(e){
|
||||
let system = $(this).closest('.' + config.systemClass);
|
||||
let map = MapUtil.getMapInstance(system.attr('data-mapid'));
|
||||
let systemId = system.attr('id');
|
||||
let systemBody = system.find('.' + config.systemBodyClass);
|
||||
|
||||
// stop animation (prevent visual bug if user spams hover-icon [in - out])
|
||||
systemBody.velocity('stop');
|
||||
|
||||
// reduce player name element back to "normal" size (css class width is used)
|
||||
systemBody.find('.' + config.systemBodyRightClass).hide();
|
||||
systemBody.find('.' + config.systemBodyItemNameClass).css({width: ''});
|
||||
|
||||
systemBody.velocity('reverse', {
|
||||
complete: function(){
|
||||
// overwrite "complete" function from first "hover"-open
|
||||
// set animated "with" back to default "100%" important in case of system with change (e.g. longer name)
|
||||
$(this).css({width: ''});
|
||||
|
||||
map.revalidate(systemId);
|
||||
}
|
||||
});
|
||||
},
|
||||
selector: '.' + config.systemClass + ' .' + config.systemHeadExpandClass
|
||||
});
|
||||
|
||||
// system "effect" popover ------------------------------------------------------------------------------------
|
||||
// -> event delegation to system elements, popup only if needed (hover)
|
||||
mapContainer.hoverIntent({
|
||||
over: function(e){
|
||||
let effectElement = $(this);
|
||||
let systemElement = effectElement.closest('.' + config.systemClass);
|
||||
let security = systemElement.data('security');
|
||||
let effect = systemElement.data('effect');
|
||||
|
||||
effectElement.addSystemEffectTooltip(security, effect, {
|
||||
trigger: 'manual',
|
||||
placement: 'right'
|
||||
}).setPopoverSmall().popover('show');
|
||||
},
|
||||
out: function(e){
|
||||
$(this).destroyPopover();
|
||||
},
|
||||
selector: '.' + config.systemClass + ' .' + MapUtil.getEffectInfoForSystem('effect', 'class')
|
||||
});
|
||||
|
||||
// system "statics" popover -----------------------------------------------------------------------------------
|
||||
// -> event delegation to system elements, popup only if needed (hover)
|
||||
mapContainer.hoverIntent({
|
||||
over: function(e){
|
||||
let staticWormholeElement = $(this);
|
||||
let wormholeName = staticWormholeElement.attr('data-name');
|
||||
let wormholeData = Util.getObjVal(Init, 'wormholes.' + wormholeName);
|
||||
if(wormholeData){
|
||||
staticWormholeElement.addWormholeInfoTooltip(wormholeData, {
|
||||
trigger: 'manual',
|
||||
placement: 'right',
|
||||
smaller: true,
|
||||
show: true
|
||||
});
|
||||
}
|
||||
},
|
||||
out: function(e){
|
||||
$(this).destroyPopover();
|
||||
},
|
||||
selector: '.' + config.systemHeadInfoClass + ' span[class^="pf-system-sec-"]'
|
||||
});
|
||||
|
||||
// catch events ===============================================================================================
|
||||
|
||||
// toggle global map option (e.g. "grid snap", "magnetization")
|
||||
@@ -2962,7 +2890,6 @@ define([
|
||||
// delete map option
|
||||
MapUtil.deleteLocalData('map', this.mapElement.data('id'), this.mapOption.option );
|
||||
}else{
|
||||
|
||||
// toggle button class
|
||||
button.addClass('active');
|
||||
|
||||
@@ -3102,7 +3029,6 @@ define([
|
||||
default:
|
||||
systemInfo.push('bad system query');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if(systemInfo.length === 1){
|
||||
@@ -3119,7 +3045,6 @@ define([
|
||||
* @returns {boolean}
|
||||
*/
|
||||
$.fn.updateUserData = function(userData){
|
||||
|
||||
let returnStatus = true;
|
||||
|
||||
// get new map instance or load existing
|
||||
@@ -3213,7 +3138,7 @@ define([
|
||||
|
||||
// users who are not in any map system --------------------------------------------------------------------
|
||||
for(let i = 0; i < userData.data.systems.length; i++){
|
||||
// users without location are grouped in systemid: 0
|
||||
// users without location are grouped in systemId: 0
|
||||
if(userData.data.systems[i].id){
|
||||
headerUpdateData.userCountOutside += userData.data.systems[i].user.length;
|
||||
}else{
|
||||
|
||||
@@ -19,7 +19,14 @@ define([
|
||||
y: 0
|
||||
},
|
||||
|
||||
mapClass: 'pf-map', // class for all maps
|
||||
|
||||
popoverTriggerClass: 'pf-popover-trigger', // class for "popover" trigger elements
|
||||
|
||||
systemHeadInfoClass: 'pf-system-head-info', // class for system info
|
||||
systemActiveClass: 'pf-system-active', // class for an active system on a map
|
||||
systemTooltipInnerIdPrefix: 'pf-system-tooltip-inner-', // id prefix for system tooltip content
|
||||
systemTooltipInnerClass: 'pf-system-tooltip-inner', // class for system tooltip content
|
||||
|
||||
dialogRallyId: 'pf-rally-dialog', // id for "Rally point" dialog
|
||||
|
||||
@@ -219,6 +226,139 @@ define([
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* toggle system tooltip (current pilot count)
|
||||
* @param show
|
||||
* @param tooltipOptions
|
||||
* @returns {*}
|
||||
*/
|
||||
$.fn.toggleSystemTooltip = function(show, tooltipOptions){
|
||||
let highlightData = {
|
||||
good: {
|
||||
colorClass: 'txt-color-green',
|
||||
iconClass: 'fa-caret-up'
|
||||
},
|
||||
bad: {
|
||||
colorClass: 'txt-color-red',
|
||||
iconClass: 'fa-caret-down'
|
||||
}
|
||||
};
|
||||
|
||||
let getHighlightClass = (highlight) => {
|
||||
return Util.getObjVal(highlightData, highlight + '.colorClass') || '';
|
||||
};
|
||||
|
||||
let getHighlightIcon = (highlight) => {
|
||||
return Util.getObjVal(highlightData, highlight + '.iconClass') || '';
|
||||
};
|
||||
|
||||
let getTitle = (userCounter, highlight) => {
|
||||
return '<i class="fas ' + getHighlightIcon(highlight) + '"></i> ' + userCounter + '';
|
||||
};
|
||||
|
||||
return this.each(function(){
|
||||
let system = $(this);
|
||||
switch(show){
|
||||
case 'destroy':
|
||||
//destroy tooltip and remove some attributes which are not deleted by 'destroy'
|
||||
system.tooltip('destroy').removeAttr('title data-original-title');
|
||||
break;
|
||||
case 'hide':
|
||||
system.tooltip('hide');
|
||||
break;
|
||||
case 'show':
|
||||
// initial "show" OR "update" open tooltip
|
||||
// -> do not update tooltips while a system is dragged
|
||||
let showTooltip = !system.hasClass('jsPlumb_dragged');
|
||||
|
||||
if(system.data('bs.tooltip')){
|
||||
// tooltip initialized but could be hidden
|
||||
// check for title update
|
||||
if(
|
||||
tooltipOptions.hasOwnProperty('userCount') &&
|
||||
tooltipOptions.hasOwnProperty('highlight')
|
||||
){
|
||||
let currentTitle = system.attr('data-original-title');
|
||||
let newTitle = getTitle(tooltipOptions.userCount, tooltipOptions.highlight);
|
||||
|
||||
if(currentTitle !== newTitle){
|
||||
// update tooltip
|
||||
let tooltipInner = system.attr('title', newTitle).tooltip('fixTitle').data('bs.tooltip').$tip.find('.tooltip-inner');
|
||||
tooltipInner.html(newTitle);
|
||||
|
||||
// change highlight class
|
||||
let highlightClass = getHighlightClass(tooltipOptions.highlight);
|
||||
if( !tooltipInner.hasClass(highlightClass) ){
|
||||
tooltipInner.removeClass( getHighlightClass('good') + ' ' + getHighlightClass('bad')).addClass(highlightClass);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let tip = system.data('bs.tooltip').tip();
|
||||
if( !tip.hasClass('in') && showTooltip){
|
||||
// update tooltip placement based on system position
|
||||
system.data('bs.tooltip').options.placement = getSystemTooltipPlacement(system);
|
||||
|
||||
system.tooltip('show');
|
||||
}
|
||||
}else{
|
||||
// no tooltip initialized
|
||||
// "some" config data is required
|
||||
if(
|
||||
tooltipOptions.hasOwnProperty('systemId') &&
|
||||
tooltipOptions.hasOwnProperty('userCount') &&
|
||||
tooltipOptions.hasOwnProperty('highlight')
|
||||
){
|
||||
let innerTooltipId = config.systemTooltipInnerIdPrefix + tooltipOptions.systemId;
|
||||
|
||||
let template = '<div class="tooltip" role="tooltip">' +
|
||||
'<div class="tooltip-arrow"></div>' +
|
||||
'<div id="' + innerTooltipId + '" class="tooltip-inner txt-color ' + config.systemTooltipInnerClass + '"></div>' +
|
||||
'</div>';
|
||||
|
||||
let options = {
|
||||
trigger: 'manual',
|
||||
placement: getSystemTooltipPlacement(system),
|
||||
html: true,
|
||||
animation: true,
|
||||
template: template,
|
||||
viewport: system.closest('.' + config.mapClass)
|
||||
};
|
||||
|
||||
// init new tooltip -> Do not show automatic maybe system is currently dragged
|
||||
system.attr('title', getTitle(tooltipOptions.userCount, tooltipOptions.highlight));
|
||||
system.tooltip(options);
|
||||
|
||||
system.one('shown.bs.tooltip', function() {
|
||||
// set highlight only on FIRST show
|
||||
$('#' + this.innerTooltipId).addClass(this.highlightClass);
|
||||
}.bind({
|
||||
highlightClass: getHighlightClass(tooltipOptions.highlight),
|
||||
innerTooltipId: innerTooltipId
|
||||
}));
|
||||
|
||||
if(showTooltip){
|
||||
system.tooltip('show');
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* get tooltip position (top, bottom) based on system position
|
||||
* @param system
|
||||
* @returns {string}
|
||||
*/
|
||||
let getSystemTooltipPlacement = (system) => {
|
||||
let offsetParent = system.parent().offset();
|
||||
let offsetSystem = system.offset();
|
||||
|
||||
return (offsetSystem.top - offsetParent.top < 27) ? 'bottom' : 'top';
|
||||
};
|
||||
|
||||
/**
|
||||
* delete system(s) with all their connections
|
||||
* (ajax call) remove system from DB
|
||||
@@ -266,7 +406,6 @@ define([
|
||||
* @param systems
|
||||
*/
|
||||
let removeSystems = (map, systems) => {
|
||||
|
||||
let removeSystemCallbak = function(deleteSystem){
|
||||
map.remove(deleteSystem);
|
||||
};
|
||||
@@ -285,8 +424,9 @@ define([
|
||||
// do not fire a "connectionDetached" event
|
||||
map.detachAllConnections(system, {fireEvent: false});
|
||||
|
||||
// hide tooltip
|
||||
// destroy tooltip/popover
|
||||
system.toggleSystemTooltip('destroy', {});
|
||||
system.destroyPopover(true);
|
||||
|
||||
// remove system
|
||||
system.velocity('transition.whirlOut', {
|
||||
@@ -299,7 +439,6 @@ define([
|
||||
/**
|
||||
* calculate the x/y coordinates for a new system - relativ to a source system
|
||||
* @param sourceSystem
|
||||
* @param grid
|
||||
* @returns {{x: *, y: *}}
|
||||
*/
|
||||
let calculateNewSystemPosition = function(sourceSystem){
|
||||
@@ -343,9 +482,42 @@ define([
|
||||
return newPosition;
|
||||
};
|
||||
|
||||
/**
|
||||
* get new dom element for systemData that shows "info" data (additional data)
|
||||
* -> this is show below the system base data on map
|
||||
* @param data
|
||||
* @returns {*}
|
||||
*/
|
||||
let getHeadInfoElement = (data) => {
|
||||
let headInfo = null;
|
||||
|
||||
// check systemData if headInfo element is needed
|
||||
if(data.statics && data.statics.length){
|
||||
// format wh statics
|
||||
let statics = [];
|
||||
for(let staticData of data.statics){
|
||||
statics.push(
|
||||
'<span class="' +
|
||||
Util.getSecurityClassForSystem(staticData.security) + ' ' +
|
||||
config.popoverTriggerClass + '" data-name="' + staticData.name +
|
||||
'">' + staticData.security + '</span>'
|
||||
);
|
||||
}
|
||||
|
||||
headInfo = $('<div>', {
|
||||
class: config.systemHeadInfoClass
|
||||
}).append(
|
||||
statics.join(' ')
|
||||
);
|
||||
}
|
||||
|
||||
return headInfo;
|
||||
};
|
||||
|
||||
return {
|
||||
deleteSystems: deleteSystems,
|
||||
removeSystems: removeSystems,
|
||||
calculateNewSystemPosition: calculateNewSystemPosition
|
||||
calculateNewSystemPosition: calculateNewSystemPosition,
|
||||
getHeadInfoElement: getHeadInfoElement
|
||||
};
|
||||
});
|
||||
@@ -692,6 +692,9 @@ define([
|
||||
case'stargate':
|
||||
type = 'stargate';
|
||||
break;
|
||||
case'abyssal':
|
||||
type = 'abyssal';
|
||||
break;
|
||||
default:
|
||||
console.error('Connection scope "' + scope + '" unknown!');
|
||||
}
|
||||
@@ -990,16 +993,53 @@ define([
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* add system effect tooltip
|
||||
* @param security
|
||||
* @param effect
|
||||
* @returns {*}
|
||||
*/
|
||||
$.fn.addSystemEffectTooltip = function(security, effect, options){
|
||||
let effectClass = getEffectInfoForSystem(effect, 'class');
|
||||
let systemEffectData = Util.getSystemEffectData(security, effect);
|
||||
|
||||
let title = '<i class="fas fa-square fa-fw ' + effectClass + '"></i> ' +
|
||||
getEffectInfoForSystem(effect, 'name') +
|
||||
'<span class="pull-right ' + Util.getSecurityClassForSystem(security) + '">' + security + '</span>';
|
||||
|
||||
let content = Util.getSystemEffectTable(systemEffectData);
|
||||
|
||||
let defaultOptions = {
|
||||
placement: 'top',
|
||||
html: true,
|
||||
trigger: 'hover',
|
||||
container: 'body',
|
||||
title: title,
|
||||
content: content,
|
||||
delay: {
|
||||
show: 150,
|
||||
hide: 0
|
||||
},
|
||||
};
|
||||
|
||||
options = $.extend({}, defaultOptions, options);
|
||||
|
||||
return this.each(function(){
|
||||
$(this).popover(options);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* add a wormhole tooltip with wh specific data to elements
|
||||
* @param tooltipData
|
||||
* @returns {*}
|
||||
*/
|
||||
$.fn.addWormholeInfoTooltip = function(tooltipData){
|
||||
return this.each(function() {
|
||||
$.fn.addWormholeInfoTooltip = function(tooltipData, options){
|
||||
return this.each(function(){
|
||||
let element = $(this);
|
||||
|
||||
requirejs(['text!templates/tooltip/wormhole_info.html', 'mustache'], function (template, Mustache) {
|
||||
requirejs(['text!templates/tooltip/wormhole_info.html', 'mustache'], (template, Mustache) => {
|
||||
|
||||
// format tooltip data
|
||||
let data = {};
|
||||
if(tooltipData.massTotal){
|
||||
@@ -1014,28 +1054,48 @@ define([
|
||||
if(tooltipData.maxStableTime){
|
||||
data.maxStableTime = tooltipData.maxStableTime + ' h';
|
||||
}
|
||||
if(tooltipData.signatureStrength){
|
||||
data.signatureStrength = parseFloat(tooltipData.signatureStrength).toLocaleString() + ' %';
|
||||
}else{
|
||||
data.signatureStrength = 'unknown';
|
||||
}
|
||||
if(!tooltipData.class){
|
||||
tooltipData.class = Util.getSecurityClassForSystem(tooltipData.security);
|
||||
}
|
||||
|
||||
let title = tooltipData.name +
|
||||
'<span class="pull-right ' + tooltipData.class +'">' + tooltipData.security + '</span>';
|
||||
let content = Mustache.render(template, data);
|
||||
|
||||
element.popover({
|
||||
let defaultOptions = {
|
||||
placement: 'top',
|
||||
html: true,
|
||||
trigger: 'hover',
|
||||
content: '',
|
||||
container: 'body',
|
||||
title: title,
|
||||
content: '',
|
||||
delay: {
|
||||
show: 150,
|
||||
hide: 0
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
options = $.extend({}, defaultOptions, options);
|
||||
|
||||
element.popover(options);
|
||||
|
||||
// set new popover content
|
||||
let popover = element.data('bs.popover');
|
||||
popover.options.title = title;
|
||||
popover.options.content = content;
|
||||
|
||||
if(options.smaller){
|
||||
element.setPopoverSmall();
|
||||
}
|
||||
|
||||
if(options.show){
|
||||
element.popover('show');
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
@@ -32,6 +32,9 @@ define([
|
||||
// set default dialog config
|
||||
Util.initDefaultBootboxConfig();
|
||||
|
||||
// set default select2 config
|
||||
Util.initDefaultSelect2Config();
|
||||
|
||||
// load page
|
||||
// load info (maintenance) info panel (if scheduled)
|
||||
$('body').loadPageStructure().setGlobalShortcuts();
|
||||
@@ -128,6 +131,8 @@ define([
|
||||
Init.url = response.url;
|
||||
Init.slack = response.slack;
|
||||
Init.discord = response.discord;
|
||||
Init.structureStatus = response.structureStatus;
|
||||
Init.universeCategories = response.universeCategories;
|
||||
Init.routeSearch = response.routeSearch;
|
||||
Init.programMode = response.programMode;
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ define([
|
||||
'app/ui/system_graph',
|
||||
'app/ui/system_signature',
|
||||
'app/ui/system_route',
|
||||
'app/ui/system_intel',
|
||||
'app/ui/system_killboard',
|
||||
'app/ui/connection_info',
|
||||
'app/counter'
|
||||
@@ -23,6 +24,7 @@ define([
|
||||
SystemGraphModule,
|
||||
SystemSignatureModule,
|
||||
SystemRouteModule,
|
||||
SystemIntelModule,
|
||||
SystemKillboardModule,
|
||||
ConnectionInfoModule
|
||||
) => {
|
||||
@@ -276,6 +278,9 @@ define([
|
||||
// draw system routes module
|
||||
drawModule(secondCell, SystemRouteModule, currentSystemData.mapId, currentSystemData.systemData);
|
||||
|
||||
// draw system intel module
|
||||
drawModule(secondCell, SystemIntelModule, currentSystemData.mapId, currentSystemData.systemData);
|
||||
|
||||
// draw system killboard module
|
||||
drawModule(secondCell, SystemKillboardModule, currentSystemData.mapId, currentSystemData.systemData);
|
||||
});
|
||||
|
||||
@@ -69,7 +69,8 @@ define([
|
||||
dynamicElementWrapperId: 'pf-dialog-wrapper',
|
||||
|
||||
// system signature module
|
||||
systemSigModuleClass: 'pf-sig-table-module', // module wrapper (signatures)
|
||||
systemSignatureModuleClass: 'pf-signature-table-module', // module wrapper (signatures)
|
||||
systemIntelModuleClass: 'pf-system-intel-module', // module wrapper (intel)
|
||||
};
|
||||
|
||||
let programStatusCounter = 0; // current count down in s until next status change is possible
|
||||
@@ -137,13 +138,18 @@ define([
|
||||
location.reload();
|
||||
});
|
||||
|
||||
body.watchKey('signaturePaste', (e) => {
|
||||
body.watchKey('clipboardPaste', (e) => {
|
||||
// just send event to the current active map
|
||||
let activeMap = Util.getMapModule().getActiveMap();
|
||||
if(activeMap){
|
||||
// look for active signature module (active system)
|
||||
let signatureModuleElement = MapUtil.getTabContentElementByMapElement(activeMap).find('.' + config.systemSigModuleClass);
|
||||
if(signatureModuleElement.length){
|
||||
let mapContentElement = MapUtil.getTabContentElementByMapElement(activeMap);
|
||||
let signatureModuleElement = mapContentElement.find('.' + config.systemSignatureModuleClass);
|
||||
let intelModuleElement = mapContentElement.find('.' + config.systemIntelModuleClass);
|
||||
if(
|
||||
signatureModuleElement.length ||
|
||||
intelModuleElement.length
|
||||
){
|
||||
e = e.originalEvent;
|
||||
let targetElement = $(e.target);
|
||||
// do not read clipboard if pasting into form elements
|
||||
@@ -155,6 +161,7 @@ define([
|
||||
){
|
||||
let clipboard = (e.originalEvent || e).clipboardData.getData('text/plain');
|
||||
signatureModuleElement.trigger('pf:updateSystemSignatureModuleByClipboard', [clipboard]);
|
||||
intelModuleElement.trigger('pf:updateIntelModuleByClipboard', [clipboard]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,24 +14,71 @@ define([
|
||||
splashOverlayClass: 'pf-splash' // class for "splash" overlay
|
||||
};
|
||||
|
||||
/**
|
||||
* send ajax request for index build
|
||||
* @param url
|
||||
* @param requestData
|
||||
* @param context
|
||||
* @param callback
|
||||
*/
|
||||
let sendRequest = (url, requestData, context, callback) => {
|
||||
if(requestData.count === 0){
|
||||
// first iteration
|
||||
context.target.button('loading');
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
url: url,
|
||||
type: 'POST',
|
||||
dataType: 'json',
|
||||
data: requestData,
|
||||
context: context
|
||||
}).done(function(data){
|
||||
callback(this, data);
|
||||
}).fail(function( jqXHR, status, error) {
|
||||
let reason = status + ' ' + error;
|
||||
Util.showNotify({title: jqXHR.status + ': Failed. Please retry', text: reason, type: 'warning'});
|
||||
this.target.button('reset');
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* set page observer
|
||||
*/
|
||||
let setPageObserver = () => {
|
||||
let body = $('body');
|
||||
|
||||
// collapse ---------------------------------------
|
||||
// collapse ---------------------------------------------------------------------------------------------------
|
||||
body.find('[data-toggle="collapse"]').css({cursor: 'pointer'}).on('click', function(){
|
||||
$(this).find('.pf-animate-rotate').toggleClass('right');
|
||||
});
|
||||
|
||||
// buttons ----------------------------------------
|
||||
// buttons ----------------------------------------------------------------------------------------------------
|
||||
// exclude "download" && "navigation" buttons
|
||||
body.find('.btn').not('.navbar-fixed-bottom .btn').not('[href^="?export"]').on('click', function(e){
|
||||
body.find('.btn')
|
||||
.not('.navbar-fixed-bottom .btn')
|
||||
.not('[data-action="clearIndex"]')
|
||||
.not('[data-action="buildIndex"]')
|
||||
.not('[href^="?export"]').on('click', function(e){
|
||||
$('.' + config.splashOverlayClass).showSplashOverlay();
|
||||
});
|
||||
|
||||
// tooltips ---------------------------------------
|
||||
// build/clear index buttons ----------------------------------------------------------------------------------
|
||||
// clear index buttons ----------------------------------------------------------------------------------------
|
||||
body.find('.btn[data-action="buildIndex"], .btn[data-action="clearIndex"]').on('click', function(e){
|
||||
e.preventDefault();
|
||||
let element = $(this);
|
||||
let url = '/api/setup/' + element.attr('data-action');
|
||||
sendRequest(url, {
|
||||
type: element.attr('data-type'),
|
||||
count: 0
|
||||
}, {
|
||||
target: element,
|
||||
url: url
|
||||
}, updateIndexCount);
|
||||
});
|
||||
|
||||
// tooltips ---------------------------------------------------------------------------------------------------
|
||||
body.initTooltips();
|
||||
|
||||
// change url (remove logout parameter)
|
||||
@@ -40,6 +87,42 @@ define([
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* update data count label for "indexed data"
|
||||
* @param context
|
||||
* @param responseData
|
||||
*/
|
||||
let updateIndexCount = (context, responseData) => {
|
||||
let countElement = context.target.closest('.row').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){
|
||||
countElement.addClass('txt-color-success');
|
||||
}else if(responseData.countBuildAll > 0){
|
||||
countElement.addClass('txt-color-warning');
|
||||
}else{
|
||||
countElement.addClass('txt-color-danger');
|
||||
}
|
||||
|
||||
context.target.find('.btn-progress').html(' ' + responseData.progress + '%').css('width', responseData.progress + '%');
|
||||
|
||||
// send next chunk of rows -> import only
|
||||
if(
|
||||
context.target.attr('data-action') === 'buildIndex' &&
|
||||
responseData.countBuildAll < responseData.countAll
|
||||
){
|
||||
sendRequest(context.url, {
|
||||
type: responseData.type,
|
||||
count: responseData.count
|
||||
}, {
|
||||
target: context.target,
|
||||
url: context.url
|
||||
}, updateIndexCount);
|
||||
}else{
|
||||
context.target.button('reset');
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* perform a basic check if Clients (browser) can connect to the webSocket server
|
||||
*/
|
||||
@@ -165,10 +248,10 @@ define([
|
||||
*/
|
||||
$(function(){
|
||||
|
||||
// show app information in browser console --------
|
||||
// show app information in browser console --------------------------------------------------------------------
|
||||
Util.showVersionInfo();
|
||||
|
||||
// hide splash loading animation ------------------
|
||||
// hide splash loading animation ------------------------------------------------------------------------------
|
||||
$('.' + config.splashOverlayClass).hideSplashOverlay();
|
||||
|
||||
setPageObserver();
|
||||
|
||||
@@ -44,7 +44,13 @@ define([
|
||||
}.bind(this);
|
||||
}.bind({
|
||||
Util: Util
|
||||
})
|
||||
}),
|
||||
sigStrengthValue: function(){
|
||||
return function(value, render){
|
||||
let float = render(value);
|
||||
return float.length ? parseFloat(float).toLocaleString() + ' %' : 'unknown';
|
||||
};
|
||||
}
|
||||
};
|
||||
let content = Mustache.render(template, data);
|
||||
|
||||
|
||||
@@ -346,7 +346,6 @@ define([
|
||||
let formValid = form.isValidForm();
|
||||
|
||||
if(formValid === true){
|
||||
|
||||
// lock dialog
|
||||
let dialogContent = mapInfoDialog.find('.modal-content');
|
||||
dialogContent.showLoadingAnimation();
|
||||
@@ -391,7 +390,6 @@ define([
|
||||
data: requestData,
|
||||
dataType: 'json'
|
||||
}).done(function(responseData){
|
||||
|
||||
if(responseData.error.length){
|
||||
form.showFormMessage(responseData.error);
|
||||
}else{
|
||||
|
||||
@@ -7,10 +7,67 @@ define([
|
||||
'app/init',
|
||||
'app/util',
|
||||
'app/map/util'
|
||||
], function($, Init, Util, MapUtil) {
|
||||
|
||||
], ($, Init, Util, MapUtil) => {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* format result data
|
||||
* @param data
|
||||
* @returns {*}
|
||||
*/
|
||||
let formatCategoryTypeResultData = (data) => {
|
||||
if(data.loading) return data.text;
|
||||
if(data.placeholder) return data.placeholder;
|
||||
|
||||
let markup = '<div class="clearfix">';
|
||||
|
||||
if(data.hasOwnProperty('children')){
|
||||
// category group label
|
||||
markup += '<div class="col-xs-9">' + data.text + '</div>';
|
||||
markup += '<div class="col-xs-3 text-right">(' + data.children.length + ')</div>';
|
||||
}else{
|
||||
let imagePath = '';
|
||||
let iconName = '';
|
||||
let thumb = '';
|
||||
|
||||
switch(data.categoryType){
|
||||
case 'character':
|
||||
imagePath = Init.url.ccpImageServer + '/Character/' + data.id + '_32.jpg';
|
||||
break;
|
||||
case 'corporation':
|
||||
imagePath = Init.url.ccpImageServer + '/Corporation/' + data.id + '_32.png';
|
||||
break;
|
||||
case 'alliance':
|
||||
imagePath = Init.url.ccpImageServer + '/Alliance/' + data.id + '_32.png';
|
||||
break;
|
||||
case 'inventoryType':
|
||||
imagePath = Init.url.ccpImageServer + '/Type/' + data.id + '_32.png';
|
||||
break;
|
||||
case 'render':
|
||||
imagePath = Init.url.ccpImageServer + '/Render/' + data.id + '_32.png';
|
||||
break;
|
||||
case 'station':
|
||||
iconName = 'fa-home';
|
||||
break;
|
||||
case 'system':
|
||||
iconName = 'fa-sun';
|
||||
break;
|
||||
}
|
||||
|
||||
if(imagePath){
|
||||
thumb = '<img src="' + imagePath + '" style="max-width: 100%" />';
|
||||
}else if(iconName){
|
||||
thumb = '<i class="fas fa-fw ' + iconName + '" ></i>';
|
||||
}
|
||||
|
||||
markup += '<div class="col-xs-2 text-center">' + thumb + '</div>';
|
||||
markup += '<div class="col-xs-10">' + data.text + '</div>';
|
||||
}
|
||||
markup += '</div>';
|
||||
|
||||
return markup;
|
||||
};
|
||||
|
||||
/**
|
||||
* init a select element as "select2" for map selection
|
||||
*/
|
||||
@@ -20,7 +77,6 @@ define([
|
||||
$.when(
|
||||
selectElement.select2({
|
||||
dropdownParent: selectElement.parents('.modal-body'),
|
||||
theme: 'pathfinder',
|
||||
maximumSelectionLength: 5
|
||||
})
|
||||
);
|
||||
@@ -133,8 +189,7 @@ define([
|
||||
}
|
||||
},
|
||||
dropdownParent: selectElement.parents('.modal-body'),
|
||||
theme: 'pathfinder',
|
||||
minimumInputLength: 2,
|
||||
minimumInputLength: 3,
|
||||
templateResult: formatResultData,
|
||||
placeholder: 'System name',
|
||||
allowClear: true,
|
||||
@@ -147,7 +202,7 @@ define([
|
||||
// select changed
|
||||
}).on('select2:open', function(){
|
||||
// clear selected system (e.g. default system)
|
||||
// => improves usability (not necessary). There is a small "x" whe it could be cleared manually
|
||||
// => improves usability (not necessary). There is a small "x" if field can be cleared manually
|
||||
if(
|
||||
options.maxSelectionLength === 1 &&
|
||||
$(this).val() !== null
|
||||
@@ -171,52 +226,8 @@ define([
|
||||
$.fn.initAccessSelect = function(options){
|
||||
|
||||
return this.each(function(){
|
||||
|
||||
let selectElement = $(this);
|
||||
|
||||
// format result data
|
||||
function formatResultData (data) {
|
||||
|
||||
if (data.loading){
|
||||
return data.text;
|
||||
}
|
||||
|
||||
// check if an option is already selected
|
||||
// do not show the same result twice
|
||||
let currentValues = selectElement.val();
|
||||
|
||||
if(
|
||||
currentValues &&
|
||||
currentValues.indexOf( data.id.toString() ) !== -1
|
||||
){
|
||||
return ;
|
||||
}
|
||||
|
||||
let imagePath = '';
|
||||
let previewContent = '';
|
||||
|
||||
switch(options.type){
|
||||
case 'character':
|
||||
imagePath = Init.url.ccpImageServer + '/Character/' + data.id + '_32.jpg';
|
||||
previewContent = '<img src="' + imagePath + '" style="max-width: 100%" />';
|
||||
break;
|
||||
case 'corporation':
|
||||
imagePath = Init.url.ccpImageServer + '/Corporation/' + data.id + '_32.png';
|
||||
previewContent = '<img src="' + imagePath + '" style="max-width: 100%" />';
|
||||
break;
|
||||
case 'alliance':
|
||||
imagePath = Init.url.ccpImageServer + '/Alliance/' + data.id + '_32.png';
|
||||
previewContent = '<img src="' + imagePath + '" style="max-width: 100%" />';
|
||||
break;
|
||||
}
|
||||
|
||||
let markup = '<div class="clearfix">';
|
||||
markup += '<div class="col-sm-2">' + previewContent + '</div>';
|
||||
markup += '<div class="col-sm-10">' + data.text + '</div></div>';
|
||||
|
||||
return markup;
|
||||
}
|
||||
|
||||
// format selection data
|
||||
function formatSelectionData (data){
|
||||
|
||||
@@ -251,7 +262,8 @@ define([
|
||||
results: data.map( function(item){
|
||||
return {
|
||||
id: item.id,
|
||||
text: item.name
|
||||
text: item.name,
|
||||
categoryType: options.type
|
||||
};
|
||||
})
|
||||
};
|
||||
@@ -266,12 +278,11 @@ define([
|
||||
}
|
||||
},
|
||||
dropdownParent: selectElement.parents('.modal-body'),
|
||||
theme: 'pathfinder',
|
||||
minimumInputLength: 3,
|
||||
placeholder: options.type + ' names',
|
||||
allowClear: false,
|
||||
maximumSelectionLength: options.maxSelectionLength,
|
||||
templateResult: formatResultData,
|
||||
templateResult: formatCategoryTypeResultData,
|
||||
templateSelection: formatSelectionData,
|
||||
escapeMarkup: function(markup){
|
||||
// let our custom formatter work
|
||||
@@ -288,4 +299,190 @@ define([
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* init a select element as an ajax based "select2" object for universeTypes
|
||||
* e.g. 'alliance', 'corporation', 'character', ...
|
||||
* @param options
|
||||
* @returns {*}
|
||||
*/
|
||||
$.fn.initUniverseSearch = function(options) {
|
||||
|
||||
let showErrorNotification = (reason) => {
|
||||
Util.showNotify({title: 'Search failed', text: reason + ' deleted', type: 'warning'});
|
||||
};
|
||||
|
||||
/**
|
||||
* format selection data
|
||||
* @param data
|
||||
* @returns {*}
|
||||
*/
|
||||
function formatSelectionData (data){
|
||||
if(data.loading) return data.text;
|
||||
if(data.placeholder) return data.placeholder;
|
||||
|
||||
let markup = '<div class="clearfix">';
|
||||
markup += '<div class="col-sm-10">' + data.text + '</div></div>';
|
||||
|
||||
return markup;
|
||||
}
|
||||
|
||||
return this.each(function() {
|
||||
let selectElement = $(this);
|
||||
|
||||
$.when(
|
||||
selectElement.select2({
|
||||
ajax: {
|
||||
type: 'POST',
|
||||
url: function(params){
|
||||
// add params to URL
|
||||
return Init.path.searchUniverseData + '/' + params.term;
|
||||
},
|
||||
dataType: 'json',
|
||||
delay: 250,
|
||||
timeout: 5000,
|
||||
cache: true,
|
||||
data: function(params){
|
||||
return {
|
||||
categories: options.categoryNames
|
||||
};
|
||||
},
|
||||
processResults: function(result, page) {
|
||||
let data = {results: []};
|
||||
if(result.hasOwnProperty('error')){
|
||||
showErrorNotification(result.error);
|
||||
}else{
|
||||
let mapChildren = function(item){
|
||||
return {
|
||||
id: item.id,
|
||||
text: item.name,
|
||||
categoryType: this
|
||||
};
|
||||
};
|
||||
|
||||
for(let category in result){
|
||||
// skip custom functions in case result = [] (array functions)
|
||||
if(result.hasOwnProperty(category)){
|
||||
data.results.push({
|
||||
text: category,
|
||||
children: result[category].map(mapChildren, category)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
},
|
||||
error: function (jqXHR, status, error) {
|
||||
if( !Util.isXHRAborted(jqXHR) ){
|
||||
let reason = status + ' ' + jqXHR.status + ': ' + error;
|
||||
showErrorNotification(reason);
|
||||
}
|
||||
}
|
||||
},
|
||||
dropdownParent: selectElement.parents('.modal-body') ,
|
||||
minimumInputLength: 3,
|
||||
placeholder: '',
|
||||
allowClear: options.maxSelectionLength <= 1,
|
||||
maximumSelectionLength: options.maxSelectionLength,
|
||||
templateResult: formatCategoryTypeResultData,
|
||||
// templateSelection: formatSelectionData, // some issues with "clear" selection on single selects (empty option is needed)
|
||||
escapeMarkup: function(markup){
|
||||
// let our custom formatter work
|
||||
return markup;
|
||||
}
|
||||
}).on('change', function(e){
|
||||
// select changed
|
||||
}).on('select2:open', function(){
|
||||
// clear selected system (e.g. default system)
|
||||
// => improves usability (not necessary). There is a small "x" if field can be cleared manually
|
||||
if(
|
||||
options.maxSelectionLength === 1 &&
|
||||
$(this).val() !== null
|
||||
){
|
||||
$(this).val('').trigger('change');
|
||||
}
|
||||
})
|
||||
).done(function(){
|
||||
// after init finish
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
$.fn.initUniverseTypeSelect = function(options) {
|
||||
|
||||
/**
|
||||
* get select option data by categoryIds
|
||||
* @param categoryIds
|
||||
* @returns {{results: Array}}
|
||||
*/
|
||||
let getOptionsData = categoryIds => {
|
||||
let data = [];
|
||||
|
||||
let mapChildren = function(type){
|
||||
return {
|
||||
id: type.id,
|
||||
text: type.name,
|
||||
groupId: this.groupId,
|
||||
categoryId: this.categoryId,
|
||||
categoryType: this.categoryType
|
||||
};
|
||||
};
|
||||
|
||||
for(let categoryId of categoryIds){
|
||||
let categoryData = Util.getObjVal(Init, 'universeCategories.' + categoryId);
|
||||
if(categoryData && categoryData.groups){
|
||||
// categoryId data exists and has groups...
|
||||
for(let groupData of categoryData.groups){
|
||||
if(groupData && groupData.types){
|
||||
// groupData exists and has types...
|
||||
data.push({
|
||||
text: groupData.name,
|
||||
children: groupData.types.map(mapChildren, {
|
||||
groupId: groupData.id,
|
||||
categoryId: categoryData.id,
|
||||
categoryType: 'inventoryType',
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
};
|
||||
|
||||
return this.each(function() {
|
||||
let selectElement = $(this);
|
||||
|
||||
$.when(
|
||||
selectElement.select2({
|
||||
data: getOptionsData(options.categoryIds),
|
||||
dropdownParent: selectElement.parents('.modal-body'),
|
||||
minimumInputLength: 0, // minimum number of characters required to start a search
|
||||
maximumInputLength: 100, // maximum number of characters that may be provided for a search term
|
||||
placeholder: '',
|
||||
allowClear: options.maxSelectionLength <= 1,
|
||||
multiple: options.maxSelectionLength > 1,
|
||||
maximumSelectionLength: options.maxSelectionLength,
|
||||
// maximumSelectionLength: options.maxSelectionLength > 1 ? options.maxSelectionLength > 1 : 0,
|
||||
// minimumResultsForSearch: 5, // minimum number of results required to display the search box
|
||||
templateResult: formatCategoryTypeResultData,
|
||||
escapeMarkup: function(markup){
|
||||
return markup;
|
||||
}
|
||||
}).on('select2:open', function(){
|
||||
// clear selected system (e.g. default system)
|
||||
// => improves usability (not necessary). There is a small "x" if field can be cleared manually
|
||||
if(
|
||||
options.maxSelectionLength === 1 &&
|
||||
$(this).val() !== null
|
||||
){
|
||||
$(this).val('').trigger('change');
|
||||
}
|
||||
}).val(options.selected).trigger('change')
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
});
|
||||
|
||||
@@ -39,7 +39,10 @@ define([
|
||||
tableToolsActionClass: 'pf-table-tools-action', // class for "edit" action
|
||||
|
||||
descriptionTextareaElementClass: 'pf-system-info-description', // class for "description" textarea element (xEditable)
|
||||
descriptionTextareaCharCounter: 'pf-form-field-char-count' // class for "character counter" element for form field
|
||||
descriptionTextareaCharCounter: 'pf-form-field-char-count', // class for "character counter" element for form field
|
||||
|
||||
// fonts
|
||||
fontTriglivianClass: 'pf-triglivian' // class for "Triglivian" names (e.g. Abyssal systems)
|
||||
};
|
||||
|
||||
// disable Module update temporary (until. some requests/animations) are finished
|
||||
@@ -54,7 +57,7 @@ define([
|
||||
/**
|
||||
* set module observer and look for relevant system data to update
|
||||
*/
|
||||
let setModuleObserver = function(moduleElement){
|
||||
let setModuleObserver = (moduleElement) => {
|
||||
$(document).off('pf:updateSystemInfoModule').on('pf:updateSystemInfoModule', function(e, data){
|
||||
if(data){
|
||||
moduleElement.updateSystemInfoModule(data);
|
||||
@@ -66,7 +69,7 @@ define([
|
||||
* shows the tool action element by animation
|
||||
* @param toolsActionElement
|
||||
*/
|
||||
let showToolsActionElement = function(toolsActionElement){
|
||||
let showToolsActionElement = (toolsActionElement) => {
|
||||
toolsActionElement.velocity('stop').velocity({
|
||||
opacity: 1,
|
||||
height: '100%'
|
||||
@@ -81,7 +84,7 @@ define([
|
||||
* hides the tool action element by animation
|
||||
* @param toolsActionElement
|
||||
*/
|
||||
let hideToolsActionElement = function(toolsActionElement){
|
||||
let hideToolsActionElement = (toolsActionElement) => {
|
||||
toolsActionElement.velocity('stop').velocity('reverse', {
|
||||
display: 'none',
|
||||
visibility: 'hidden'
|
||||
@@ -169,40 +172,13 @@ define([
|
||||
moduleElement.find('.' + config.descriptionArea).hideLoadingAnimation();
|
||||
};
|
||||
|
||||
/**
|
||||
* update a character counter field with current value length - maxCharLength
|
||||
* @param field
|
||||
* @param charCounterElement
|
||||
* @param maxCharLength
|
||||
*/
|
||||
let updateCounter = function(field, charCounterElement, maxCharLength){
|
||||
let value = field.val();
|
||||
let inputLength = value.length;
|
||||
|
||||
// line breaks are 2 characters!
|
||||
let newLines = value.match(/(\r\n|\n|\r)/g);
|
||||
let addition = 0;
|
||||
if (newLines != null) {
|
||||
addition = newLines.length;
|
||||
}
|
||||
inputLength += addition;
|
||||
|
||||
charCounterElement.text(maxCharLength - inputLength);
|
||||
|
||||
if(maxCharLength <= inputLength){
|
||||
charCounterElement.toggleClass('txt-color-red', true);
|
||||
}else{
|
||||
charCounterElement.toggleClass('txt-color-red', false);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* get module element
|
||||
* @param parentElement
|
||||
* @param mapId
|
||||
* @param systemData
|
||||
*/
|
||||
let getModule = function(parentElement, mapId, systemData){
|
||||
let getModule = (parentElement, mapId, systemData) => {
|
||||
|
||||
// create new module container
|
||||
let moduleElement = $('<div>');
|
||||
@@ -263,7 +239,6 @@ define([
|
||||
inputclass: config.descriptionTextareaElementClass,
|
||||
tpl: '<textarea maxlength="' + maxDescriptionLength + '"></textarea>',
|
||||
params: function(params){
|
||||
|
||||
params.mapData = {
|
||||
id: mapId
|
||||
};
|
||||
@@ -319,10 +294,10 @@ define([
|
||||
textarea.parent().next().append(charCounter);
|
||||
|
||||
// update character counter
|
||||
updateCounter(textarea, charCounter, maxDescriptionLength);
|
||||
Util.updateCounter(textarea, charCounter, maxDescriptionLength);
|
||||
|
||||
textarea.on('keyup', function(){
|
||||
updateCounter($(this), charCounter, maxDescriptionLength);
|
||||
Util.updateCounter($(this), charCounter, maxDescriptionLength);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -358,30 +333,7 @@ define([
|
||||
tooltipElements.tooltip();
|
||||
|
||||
// init system effect popover ----------------------------------------------------------------
|
||||
let infoEffectElement = $(moduleElement).find('.' + config.systemInfoEffectInfoClass);
|
||||
|
||||
if(infoEffectElement.length){
|
||||
// effect row exists -> get effect data
|
||||
let systemEffectData = Util.getSystemEffectData( systemData.security, systemData.effect);
|
||||
|
||||
if(systemEffectData !== false){
|
||||
// transform data into table
|
||||
let systemEffectTable = Util.getSystemEffectTable( systemEffectData );
|
||||
|
||||
infoEffectElement.popover({
|
||||
html: true,
|
||||
trigger: 'hover',
|
||||
placement: 'top',
|
||||
delay: 200,
|
||||
title: 'System effects',
|
||||
container: 'body',
|
||||
content: systemEffectTable
|
||||
});
|
||||
}else{
|
||||
// effect data not found (e.g. !unknown! shattered system) -> hide "popover" icon icon
|
||||
infoEffectElement.children().hide();
|
||||
}
|
||||
}
|
||||
$(moduleElement).find('.' + config.systemInfoEffectInfoClass).addSystemEffectTooltip(systemData.security, systemData.effect);
|
||||
|
||||
// init static wormhole information ----------------------------------------------------------
|
||||
if(systemData.statics){
|
||||
@@ -408,7 +360,6 @@ define([
|
||||
popoverElement = $(popoverElement);
|
||||
let popover = popoverElement.data('bs.popover');
|
||||
|
||||
|
||||
$.ajax({
|
||||
url: popoverElement.data('url'),
|
||||
success: function(data){
|
||||
@@ -434,6 +385,7 @@ define([
|
||||
statusInfoClass: config.systemInfoStatusLabelClass,
|
||||
|
||||
systemTypeName: MapUtil.getSystemTypeInfo(systemData.type.id, 'name'),
|
||||
systemIsWormhole: MapUtil.getSystemTypeInfo(systemData.type.id, 'name') === 'w-space',
|
||||
systemStatusId: systemData.status.id,
|
||||
systemStatusClass: Util.getStatusInfoForSystem(systemData.status.id, 'class'),
|
||||
systemStatusLabel: Util.getStatusInfoForSystem(systemData.status.id, 'label'),
|
||||
@@ -446,6 +398,11 @@ define([
|
||||
descriptionButtonClass: config.addDescriptionButtonClass,
|
||||
tableToolsActionClass: config.tableToolsActionClass,
|
||||
descriptionTextareaClass: config.descriptionTextareaElementClass,
|
||||
systemNameClass: () => {
|
||||
return (val, render) => {
|
||||
return render(val) === 'A' ? config.fontTriglivianClass : '';
|
||||
};
|
||||
},
|
||||
|
||||
shatteredWormholeInfo: shatteredWormholeInfo,
|
||||
|
||||
@@ -468,7 +425,7 @@ define([
|
||||
* @param mapId
|
||||
* @param systemData
|
||||
*/
|
||||
let initModule = function(moduleElement, mapId, systemData){
|
||||
let initModule = (moduleElement, mapId, systemData) => {
|
||||
// set module observer
|
||||
setModuleObserver(moduleElement);
|
||||
|
||||
|
||||
791
js/app/ui/system_intel.js
Normal file
791
js/app/ui/system_intel.js
Normal file
@@ -0,0 +1,791 @@
|
||||
/**
|
||||
* system route module
|
||||
*/
|
||||
|
||||
define([
|
||||
'jquery',
|
||||
'app/init',
|
||||
'app/util',
|
||||
'bootbox'
|
||||
], ($, Init, Util, bootbox) => {
|
||||
'use strict';
|
||||
|
||||
let config = {
|
||||
// module info
|
||||
modulePosition: 1,
|
||||
moduleName: 'systemIntel',
|
||||
moduleHeadClass: 'pf-module-head', // class for module header
|
||||
moduleHandlerClass: 'pf-module-handler-drag', // class for "drag" handler
|
||||
moduleTypeClass: 'pf-system-intel-module', // class for this module
|
||||
|
||||
// headline toolbar
|
||||
moduleHeadlineIconClass: 'pf-module-icon-button', // class for toolbar icons in the head
|
||||
moduleHeadlineIconAddClass: 'pf-module-icon-button-add', // class for "add structure" icon
|
||||
moduleHeadlineIconReaderClass: 'pf-module-icon-button-reader', // class for "dScan reader" icon
|
||||
moduleHeadlineIconRefreshClass: 'pf-module-icon-button-refresh', // class for "refresh" icon
|
||||
|
||||
// system intel module
|
||||
systemStructuresTableClass: 'pf-system-structure-table', // class for route tables
|
||||
|
||||
// structure dialog
|
||||
structureDialogId: 'pf-structure-dialog', // id for "structure" dialog
|
||||
statusSelectId: 'pf-structure-dialog-status-select', // id for "status" select
|
||||
typeSelectId: 'pf-structure-dialog-type-select', // id for "type" select
|
||||
corporationSelectId: 'pf-structure-dialog-corporation-select', // id for "corporation" select
|
||||
descriptionTextareaId: 'pf-structure-dialog-description-textarea', // id for "description" textarea
|
||||
descriptionTextareaCharCounter: 'pf-form-field-char-count', // class for "character counter" element for form field
|
||||
|
||||
// dataTable
|
||||
tableRowIdPrefix: 'pf-structure-row_', // id prefix for table rows
|
||||
tableCellImageClass: 'pf-table-image-smaller-cell', // class for table "image" cells
|
||||
tableCellCounterClass: 'pf-table-counter-cell', // class for table "counter" cells
|
||||
tableCellEllipsisClass: 'pf-table-cell-ellipses-auto', // class for table "ellipsis" cells
|
||||
dataTableActionCellClass: 'pf-table-action-cell' // class for "action" cells
|
||||
};
|
||||
|
||||
let maxDescriptionLength = 512;
|
||||
|
||||
/**
|
||||
* get status icon for structure
|
||||
* @param statusData
|
||||
* @returns {string}
|
||||
*/
|
||||
let getStatusData = (statusData) => {
|
||||
return '<i class="fas fa-fw fa-circle ' + statusData.class + '" title="' + statusData.label + '"></i>';
|
||||
};
|
||||
|
||||
/**
|
||||
* get <tr> DOM id by id
|
||||
* @param tableApi
|
||||
* @param id
|
||||
* @returns {*}
|
||||
*/
|
||||
let getRowId = (tableApi, id) => {
|
||||
return tableApi.rows().ids().toArray().find(rowId => rowId === config.tableRowIdPrefix + id);
|
||||
};
|
||||
|
||||
/**
|
||||
* callback -> add structure rows from responseData
|
||||
* @param context
|
||||
* @param responseData
|
||||
*/
|
||||
let callbackAddStructureRows = (context, responseData) => {
|
||||
let systemData = Util.getObjVal(responseData, 'system');
|
||||
callbackUpdateStructureRows(context, systemData);
|
||||
};
|
||||
|
||||
/**
|
||||
* callback -> add structure rows from systemData
|
||||
* @param context
|
||||
* @param systemData
|
||||
*/
|
||||
let callbackUpdateStructureRows = (context, systemData) => {
|
||||
let touchedRows = [];
|
||||
let hadData = context.tableApi.rows().any();
|
||||
let notificationCounter = {
|
||||
added: 0,
|
||||
changed: 0,
|
||||
deleted: 0
|
||||
};
|
||||
|
||||
if(systemData){
|
||||
let corporations = Util.getObjVal(systemData, 'structures');
|
||||
if(corporations) {
|
||||
for (let [corporationId, corporationData] of Object.entries(corporations)){
|
||||
if(corporationData.structures && corporationData.structures.length){
|
||||
for(let structureData of corporationData.structures){
|
||||
let rowId = getRowId(context.tableApi, structureData.id);
|
||||
|
||||
// add corporation data
|
||||
structureData.corporation = {
|
||||
id: corporationData.id,
|
||||
name: corporationData.name
|
||||
};
|
||||
|
||||
if(rowId){
|
||||
// update row
|
||||
let api = context.tableApi.row('#' + rowId).data(structureData);
|
||||
api.nodes().to$().data('animationStatus', 'changed').destroyTimestampCounter();
|
||||
|
||||
touchedRows.push(api.id());
|
||||
notificationCounter.changed++;
|
||||
}else{
|
||||
// insert new row
|
||||
//context.tableApi.row.add(structureData).nodes().to$().data('animationStatus', 'added');
|
||||
let api = context.tableApi.row.add(structureData);
|
||||
api.nodes().to$().data('animationStatus', 'added');
|
||||
|
||||
touchedRows.push(api.id());
|
||||
notificationCounter.added++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(context.removeMissing){
|
||||
notificationCounter.deleted += context.tableApi.rows((idx, data, node) => !touchedRows.includes(node.id)).remove().ids().count();
|
||||
}
|
||||
|
||||
context.tableApi.draw();
|
||||
|
||||
// show notification ------------------------------------------------------------------------------------------
|
||||
let notification = '';
|
||||
notification += notificationCounter.added > 0 ? notificationCounter.added + ' added<br>' : '';
|
||||
notification += notificationCounter.changed > 0 ? notificationCounter.changed + ' changed<br>' : '';
|
||||
notification += notificationCounter.deleted > 0 ? notificationCounter.deleted + ' deleted<br>' : '';
|
||||
|
||||
if(hadData && notification.length){
|
||||
Util.showNotify({title: 'Structures updated', text: notification, type: 'success'});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* callback -> delete structure rows
|
||||
* @param context
|
||||
* @param responseData
|
||||
*/
|
||||
let callbackDeleteStructures = (context, responseData) => {
|
||||
let structureIds = Util.getObjVal(responseData, 'deletedStructureIds');
|
||||
let deletedCounter = 0;
|
||||
if(structureIds && structureIds.length){
|
||||
for(let structureId of structureIds){
|
||||
let rowId = getRowId(context.tableApi, structureId);
|
||||
if(rowId){
|
||||
context.tableApi.row('#' + rowId).remove();
|
||||
deletedCounter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(deletedCounter){
|
||||
context.tableApi.draw();
|
||||
Util.showNotify({title: 'Structure deleted', text: deletedCounter + ' deleted', type: 'success'});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* send ajax request
|
||||
* @param url
|
||||
* @param requestData
|
||||
* @param context
|
||||
* @param callback
|
||||
*/
|
||||
let sendRequest = (url, requestData, context, callback) => {
|
||||
context.moduleElement.showLoadingAnimation();
|
||||
|
||||
$.ajax({
|
||||
url: url,
|
||||
type: 'POST',
|
||||
dataType: 'json',
|
||||
data: requestData,
|
||||
context: context
|
||||
}).done(function(data){
|
||||
callback(this, data);
|
||||
}).fail(function( jqXHR, status, error) {
|
||||
let reason = status + ' ' + error;
|
||||
Util.showNotify({title: jqXHR.status + ': System intel data', text: reason, type: 'warning'});
|
||||
$(document).setProgramStatus('problem');
|
||||
}).always(function(){
|
||||
// hide loading animation
|
||||
this.moduleElement.hideLoadingAnimation();
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* requests system data
|
||||
* @param requestData
|
||||
* @param context
|
||||
* @param callback
|
||||
*/
|
||||
let getStructureData = (requestData, context, callback) => {
|
||||
sendRequest(Init.path.getSystemData, requestData, context, callback);
|
||||
};
|
||||
|
||||
/**
|
||||
* save structure data
|
||||
* @param requestData
|
||||
* @param context
|
||||
* @param callback
|
||||
*/
|
||||
let saveStructureData = (requestData, context, callback) => {
|
||||
sendRequest(Init.path.saveStructureData, requestData, context, callback);
|
||||
};
|
||||
|
||||
/**
|
||||
* delete structure
|
||||
* @param requestData
|
||||
* @param context
|
||||
* @param callback
|
||||
*/
|
||||
let deleteStructure = (requestData, context, callback) => {
|
||||
sendRequest(Init.path.deleteStructureData, requestData, context, callback);
|
||||
};
|
||||
|
||||
/**
|
||||
* show structure dialog
|
||||
* @param moduleElement
|
||||
* @param tableApi
|
||||
* @param systemId
|
||||
* @param structureData
|
||||
*/
|
||||
let showStructureDialog = (moduleElement, tableApi, systemId, structureData) => {
|
||||
let structureStatusData = Util.getObjVal(Init, 'structureStatus');
|
||||
let structureTypeData = Util.getObjVal(Init, 'structureStatus');
|
||||
|
||||
let data = {
|
||||
id: config.structureDialogId,
|
||||
structureData: structureData,
|
||||
structureStatus: Object.keys(structureStatusData).map((k) => {
|
||||
let data = structureStatusData[k];
|
||||
data.selected = data.id === Util.getObjVal(structureData, 'status.id');
|
||||
return data;
|
||||
}),
|
||||
statusSelectId: config.statusSelectId,
|
||||
typeSelectId: config.typeSelectId,
|
||||
corporationSelectId: config.corporationSelectId,
|
||||
descriptionTextareaId: config.descriptionTextareaId,
|
||||
descriptionTextareaCharCounter: config.descriptionTextareaCharCounter,
|
||||
maxDescriptionLength: maxDescriptionLength
|
||||
};
|
||||
|
||||
requirejs(['text!templates/dialog/structure.html', 'mustache'], (template, Mustache) => {
|
||||
let content = Mustache.render(template, data);
|
||||
|
||||
let structureDialog = bootbox.dialog({
|
||||
title: 'Structure',
|
||||
message: content,
|
||||
show: true,
|
||||
buttons: {
|
||||
close: {
|
||||
label: 'cancel',
|
||||
className: 'btn-default'
|
||||
},
|
||||
success: {
|
||||
label: '<i class="fas fa-fw fa-check"></i> save',
|
||||
className: 'btn-success',
|
||||
callback: function (){
|
||||
let form = this.find('form');
|
||||
|
||||
// validate form
|
||||
form.validator('validate');
|
||||
|
||||
// check whether the form is valid
|
||||
let formValid = form.isValidForm();
|
||||
|
||||
if(formValid){
|
||||
// get form data
|
||||
let formData = form.getFormValues();
|
||||
formData.id = Util.getObjVal(structureData, 'id') | 0;
|
||||
formData.structureId = Util.getObjVal(formData, 'structureId') | 0;
|
||||
formData.corporationId = Util.getObjVal(formData, 'corporationId') | 0;
|
||||
formData.systemId = systemId | 0;
|
||||
|
||||
saveStructureData({
|
||||
structures: [formData]
|
||||
}, {
|
||||
moduleElement: moduleElement,
|
||||
tableApi: tableApi
|
||||
}, callbackUpdateStructureRows);
|
||||
}else{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
structureDialog.on('shown.bs.modal', function(e) {
|
||||
// init type select live search
|
||||
let selectElementType = $(this).find('#' + config.typeSelectId);
|
||||
selectElementType.initUniverseTypeSelect({
|
||||
categoryIds: [65],
|
||||
maxSelectionLength: 1,
|
||||
selected: [Util.getObjVal(structureData, 'structure.id')]
|
||||
});
|
||||
|
||||
// init corporation select live search
|
||||
let selectElementCorporation = $(this).find('#' + config.corporationSelectId);
|
||||
selectElementCorporation.initUniverseSearch({
|
||||
categoryNames: ['corporation'],
|
||||
maxSelectionLength: 1
|
||||
});
|
||||
|
||||
$(this).find('#' + config.statusSelectId).select2({
|
||||
minimumResultsForSearch: -1
|
||||
});
|
||||
|
||||
// init character counter
|
||||
let textarea = $(this).find('#' + config.descriptionTextareaId);
|
||||
let charCounter = $(this).find('.' + config.descriptionTextareaCharCounter);
|
||||
Util.updateCounter(textarea, charCounter, maxDescriptionLength);
|
||||
|
||||
textarea.on('keyup', function(){
|
||||
Util.updateCounter($(this), charCounter, maxDescriptionLength);
|
||||
});
|
||||
|
||||
// set form validator (after select2 init finish)
|
||||
$(this).find('form').initFormValidation();
|
||||
});
|
||||
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* show D-Scan reader dialog
|
||||
* @param moduleElement
|
||||
* @param tableApi
|
||||
* @param systemData
|
||||
*/
|
||||
let showDscanReaderDialog = (moduleElement, tableApi, systemData) => {
|
||||
requirejs(['text!templates/dialog/dscan_reader.html', 'mustache'], (template, Mustache) => {
|
||||
let structureDialog = bootbox.dialog({
|
||||
title: 'D-Scan reader',
|
||||
message: Mustache.render(template, {}),
|
||||
show: true,
|
||||
buttons: {
|
||||
close: {
|
||||
label: 'cancel',
|
||||
className: 'btn-default'
|
||||
},
|
||||
success: {
|
||||
label: '<i class="fas fa-fw fa-paste fa-fw"></i> update intel',
|
||||
className: 'btn-success',
|
||||
callback: function (){
|
||||
let form = this.find('form');
|
||||
let formData = form.getFormValues();
|
||||
|
||||
updateStructureTableByClipboard(systemData, formData.clipboard, {
|
||||
moduleElement: moduleElement,
|
||||
tableApi: tableApi
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// dialog shown event
|
||||
structureDialog.on('shown.bs.modal', function(e) {
|
||||
// set focus on textarea
|
||||
structureDialog.find('textarea').focus();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* get module element
|
||||
* @param parentElement
|
||||
* @param mapId
|
||||
* @param systemData
|
||||
* @returns {jQuery}
|
||||
*/
|
||||
let getModule = (parentElement, mapId, systemData) => {
|
||||
let corporationId = Util.getCurrentUserInfo('corporationId');
|
||||
|
||||
// create new module container
|
||||
let moduleElement = $('<div>').append(
|
||||
$('<div>', {
|
||||
class: config.moduleHeadClass
|
||||
}).append(
|
||||
$('<h5>', {
|
||||
class: config.moduleHandlerClass
|
||||
}),
|
||||
$('<h5>', {
|
||||
class: 'pull-right'
|
||||
}).append(
|
||||
$('<i>', {
|
||||
class: ['fas', 'fa-fw', 'fa-plus', config.moduleHeadlineIconClass, config.moduleHeadlineIconAddClass].join(' '),
|
||||
title: 'add'
|
||||
}).attr('data-html', 'true').attr('data-toggle', 'tooltip'),
|
||||
$('<i>', {
|
||||
class: ['fas', 'fa-fw', 'fa-paste', config.moduleHeadlineIconClass, config.moduleHeadlineIconReaderClass].join(' '),
|
||||
title: 'D-Scan reader'
|
||||
}).attr('data-html', 'true').attr('data-toggle', 'tooltip'),
|
||||
$('<i>', {
|
||||
class: ['fas', 'fa-fw', 'fa-sync', config.moduleHeadlineIconClass, config.moduleHeadlineIconRefreshClass].join(' '),
|
||||
title: 'refresh all'
|
||||
}).attr('data-html', 'true').attr('data-toggle', 'tooltip')
|
||||
),
|
||||
$('<h5>', {
|
||||
text: 'Intel'
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
let table = $('<table>', {
|
||||
class: ['compact', 'stripe', 'order-column', 'row-border', 'pf-table-fixed', config.systemStructuresTableClass].join(' ')
|
||||
});
|
||||
moduleElement.append(table);
|
||||
|
||||
let structureTable = table.DataTable({
|
||||
paging: false,
|
||||
lengthChange: false,
|
||||
ordering: true,
|
||||
order: [[ 10, 'desc' ], [ 0, 'asc' ]],
|
||||
info: false,
|
||||
searching: false,
|
||||
hover: false,
|
||||
autoWidth: false,
|
||||
rowId: rowData => config.tableRowIdPrefix + rowData.id,
|
||||
language: {
|
||||
emptyTable: 'No structures recorded',
|
||||
info: '_START_ to _END_ of _MAX_',
|
||||
infoEmpty: ''
|
||||
},
|
||||
rowGroup: {
|
||||
enable: true,
|
||||
dataSrc: 'systemId'
|
||||
},
|
||||
columnDefs: [
|
||||
{
|
||||
targets: 0,
|
||||
title: '',
|
||||
width: 2,
|
||||
class: 'text-center',
|
||||
data: 'status',
|
||||
render: {
|
||||
display: data => getStatusData(data),
|
||||
sort: data => data.id
|
||||
},
|
||||
createdCell: function(cell, cellData, rowData, rowIndex, colIndex){
|
||||
$(cell).find('i').tooltip();
|
||||
}
|
||||
},{
|
||||
targets: 1,
|
||||
title: '',
|
||||
width: 26,
|
||||
orderable: false,
|
||||
className: [config.tableCellImageClass, 'text-center'].join(' '),
|
||||
data: 'structure.id',
|
||||
defaultContent: '<i class="fas fa-question txt-color txt-color-orangeDark"></i>',
|
||||
render: {
|
||||
_: function(data, type, row, meta){
|
||||
let value = data;
|
||||
if(type === 'display' && value){
|
||||
value = '<img src="' + Init.url.ccpImageServer + '/Type/' + value + '_32.png" />';
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
},{
|
||||
targets: 2,
|
||||
title: 'type',
|
||||
width: 30,
|
||||
className: [config.tableCellEllipsisClass].join(' '),
|
||||
data: 'structure.name',
|
||||
defaultContent: '<i class="fas fa-question txt-color txt-color-orangeDark"></i>',
|
||||
},{
|
||||
targets: 3,
|
||||
title: 'name',
|
||||
width: 60,
|
||||
className: [config.tableCellEllipsisClass].join(' '),
|
||||
data: 'name'
|
||||
},{
|
||||
targets: 4,
|
||||
title: '',
|
||||
width: 26,
|
||||
orderable: false,
|
||||
className: [config.tableCellImageClass, 'text-center'].join(' '),
|
||||
data: 'owner.id',
|
||||
defaultContent: '<i class="fas fa-question txt-color txt-color-orangeDark"></i>',
|
||||
render: {
|
||||
_: function(data, type, row, meta){
|
||||
let value = data;
|
||||
if(type === 'display' && value){
|
||||
value = '<img src="' + Init.url.ccpImageServer + '/Corporation/' + value + '_32.png" />';
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
},{
|
||||
targets: 5,
|
||||
title: 'owner',
|
||||
width: 50,
|
||||
className: [config.tableCellEllipsisClass].join(' '),
|
||||
data: 'owner.name',
|
||||
defaultContent: '<i class="fas fa-question txt-color txt-color-orangeDark"></i>',
|
||||
},{
|
||||
targets: 6,
|
||||
title: 'note',
|
||||
className: [config.tableCellEllipsisClass].join(' '),
|
||||
data: 'description'
|
||||
},{
|
||||
targets: 7,
|
||||
title: 'updated',
|
||||
width: 80,
|
||||
className: ['text-right', config.tableCellCounterClass].join(' '),
|
||||
data: 'updated.updated'
|
||||
},{
|
||||
targets: 8,
|
||||
title: '',
|
||||
orderable: false,
|
||||
width: 10,
|
||||
class: ['text-center', config.dataTableActionCellClass, config.moduleHeadlineIconClass].join(' '),
|
||||
data: null,
|
||||
render: {
|
||||
display: data => {
|
||||
let icon = '<i class="fas fa-pencil-alt"></i>';
|
||||
if(data.corporation.id !== corporationId){
|
||||
icon = '';
|
||||
}
|
||||
return icon;
|
||||
}
|
||||
},
|
||||
createdCell: function(cell, cellData, rowData, rowIndex, colIndex){
|
||||
let tableApi = this.api();
|
||||
|
||||
if($(cell).is(':empty')){
|
||||
$(cell).removeClass(config.dataTableActionCellClass + ' ' + config.moduleHeadlineIconClass);
|
||||
}else{
|
||||
$(cell).on('click', function(e) {
|
||||
// get current row data (important!)
|
||||
// -> "rowData" param is not current state, values are "on createCell()" state
|
||||
rowData = tableApi.row( $(cell).parents('tr')).data();
|
||||
showStructureDialog(moduleElement, tableApi, systemData.systemId, rowData);
|
||||
});
|
||||
}
|
||||
}
|
||||
},{
|
||||
targets: 9,
|
||||
title: '',
|
||||
orderable: false,
|
||||
width: 10,
|
||||
class: ['text-center', config.dataTableActionCellClass].join(' '),
|
||||
data: null,
|
||||
render: {
|
||||
display: data => {
|
||||
let icon = '<i class="fas fa-times txt-color txt-color-redDarker"></i>';
|
||||
if(data.corporation.id !== corporationId){
|
||||
icon = '<i class="fas fa-ban txt-color txt-color-grayLight" title="restricted" data-placement="left"></i>';
|
||||
}
|
||||
return icon;
|
||||
}
|
||||
},
|
||||
createdCell: function(cell, cellData, rowData, rowIndex, colIndex){
|
||||
let tableApi = this.api();
|
||||
|
||||
if($(cell).find('.fa-ban').length){
|
||||
$(cell).removeClass(config.dataTableActionCellClass + ' ' + config.moduleHeadlineIconClass);
|
||||
$(cell).find('i').tooltip();
|
||||
}else{
|
||||
let confirmationSettings = {
|
||||
container: 'body',
|
||||
placement: 'left',
|
||||
btnCancelClass: 'btn btn-sm btn-default',
|
||||
btnCancelLabel: 'cancel',
|
||||
btnCancelIcon: 'fas fa-fw fa-ban',
|
||||
title: 'delete structure',
|
||||
btnOkClass: 'btn btn-sm btn-danger',
|
||||
btnOkLabel: 'delete',
|
||||
btnOkIcon: 'fas fa-fw fa-times',
|
||||
onConfirm : function(e, target){
|
||||
// get current row data (important!)
|
||||
// -> "rowData" param is not current state, values are "on createCell()" state
|
||||
rowData = tableApi.row( $(cell).parents('tr')).data();
|
||||
|
||||
// let deleteRowElement = $(cell).parents('tr');
|
||||
// tableApi.rows(deleteRowElement).remove().draw();
|
||||
deleteStructure({
|
||||
id: rowData.id
|
||||
},{
|
||||
moduleElement: moduleElement,
|
||||
tableApi: tableApi
|
||||
}, callbackDeleteStructures);
|
||||
}
|
||||
};
|
||||
|
||||
// init confirmation dialog
|
||||
$(cell).confirmation(confirmationSettings);
|
||||
}
|
||||
}
|
||||
},{
|
||||
targets: 10,
|
||||
name: 'corporation',
|
||||
data: 'corporation',
|
||||
visible: false,
|
||||
render: {
|
||||
sort: function(data){
|
||||
return data.name;
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
drawCallback: function (settings){
|
||||
let tableApi = this.api();
|
||||
let columnCount = tableApi.columns(':visible').count();
|
||||
let rows = tableApi.rows( {page:'current'} ).nodes();
|
||||
let last= null;
|
||||
|
||||
tableApi.column('corporation:name', {page:'current'} ).data().each( function ( group, i ) {
|
||||
if ( !last || last.id !== group.id ) {
|
||||
$(rows).eq(i).before(
|
||||
'<tr class="group">' +
|
||||
'<td></td>' +
|
||||
'<td class="' + config.tableCellImageClass + '">' +
|
||||
'<img src="' + Init.url.ccpImageServer + '/Corporation/' + group.id + '_32.png" />' +
|
||||
'</td>' +
|
||||
'<td colspan="' + (columnCount - 2 ) + '">' + group.name + '</td>' +
|
||||
'</tr>'
|
||||
);
|
||||
last = group;
|
||||
}
|
||||
});
|
||||
|
||||
rows.to$().find('.' + config.tableCellCounterClass + ':not([data-counter])').initTimestampCounter();
|
||||
|
||||
let animationRows = rows.to$().filter(function() {
|
||||
return (
|
||||
$(this).data('animationStatus') ||
|
||||
$(this).data('animationTimer')
|
||||
);
|
||||
});
|
||||
|
||||
for(let i = 0; i < animationRows.length; i++){
|
||||
let animationRow = $(animationRows[i]);
|
||||
animationRow.pulseTableRow(animationRow.data('animationStatus'));
|
||||
animationRow.removeData('animationStatus');
|
||||
}
|
||||
},
|
||||
initComplete: function(settings){
|
||||
let tableApi = this.api();
|
||||
|
||||
// initial structure data request
|
||||
getStructureData({
|
||||
mapId: mapId,
|
||||
systemId: systemData.id
|
||||
},{
|
||||
moduleElement: moduleElement,
|
||||
tableApi: tableApi
|
||||
}, callbackAddStructureRows);
|
||||
}
|
||||
});
|
||||
|
||||
// init tooltips for this module
|
||||
let tooltipElements = moduleElement.find('[data-toggle="tooltip"]');
|
||||
tooltipElements.tooltip({
|
||||
container: 'body'
|
||||
});
|
||||
|
||||
return moduleElement;
|
||||
};
|
||||
|
||||
/**
|
||||
* get universe typeIds for given categoryIds
|
||||
* @param categoryIds
|
||||
* @returns {Array}
|
||||
*/
|
||||
let getUniverseTypeIdsByCategoryIds = (categoryIds) => {
|
||||
let typeIds = [];
|
||||
let mapIds = type => type.id;
|
||||
for(let categoryId of categoryIds){
|
||||
let categoryData = Util.getObjVal(Init, 'universeCategories.' + categoryId);
|
||||
if(categoryData && categoryData.groups){
|
||||
for(let groupData of categoryData.groups){
|
||||
if(groupData && groupData.types){
|
||||
typeIds = typeIds.concat(groupData.types.map(mapIds));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return typeIds;
|
||||
};
|
||||
|
||||
/**
|
||||
* parse a copy&paste string from ingame dScan windows
|
||||
* @param systemData
|
||||
* @param clipboard
|
||||
* @returns {Array}
|
||||
*/
|
||||
let parseDscanString = (systemData, clipboard) => {
|
||||
let dScanData = [];
|
||||
let structureTypeIds = getUniverseTypeIdsByCategoryIds([65]);
|
||||
|
||||
if(clipboard.length){
|
||||
let dScanRows = clipboard.split(/\r\n|\r|\n/g);
|
||||
|
||||
for(let rowData of dScanRows){
|
||||
rowData = rowData.split(/\t/g);
|
||||
|
||||
if(rowData.length === 4){
|
||||
rowData[0] = parseInt(rowData[0]);
|
||||
// valid dScan result
|
||||
if(structureTypeIds.indexOf( rowData[0] ) !== -1){
|
||||
dScanData.push({
|
||||
structureId: rowData[0],
|
||||
name: rowData[1],
|
||||
systemId: systemData.systemId
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return dScanData;
|
||||
};
|
||||
|
||||
/**
|
||||
* parse clipboard data for structures and update table
|
||||
* @param systemData
|
||||
* @param clipboard
|
||||
* @param context
|
||||
*/
|
||||
let updateStructureTableByClipboard = (systemData, clipboard, context) => {
|
||||
let structureData = parseDscanString(systemData, clipboard);
|
||||
if(structureData.length){
|
||||
saveStructureData({
|
||||
structures: structureData
|
||||
}, context, callbackUpdateStructureRows);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* init intel module
|
||||
* @param moduleElement
|
||||
* @param mapId
|
||||
* @param systemData
|
||||
*/
|
||||
let initModule = (moduleElement, mapId, systemData) => {
|
||||
|
||||
let structureTableElement = moduleElement.find('.' + config.systemStructuresTableClass);
|
||||
let tableApi = structureTableElement.DataTable();
|
||||
|
||||
// init structure dialog --------------------------------------------------------------------------------------
|
||||
moduleElement.find('.' + config.moduleHeadlineIconAddClass).on('click', function(e){
|
||||
showStructureDialog(moduleElement, tableApi, systemData.systemId);
|
||||
});
|
||||
|
||||
// init structure dialog --------------------------------------------------------------------------------------
|
||||
moduleElement.find('.' + config.moduleHeadlineIconReaderClass).on('click', function(e){
|
||||
showDscanReaderDialog(moduleElement, tableApi, systemData);
|
||||
});
|
||||
|
||||
// init refresh button ----------------------------------------------------------------------------------------
|
||||
moduleElement.find('.' + config.moduleHeadlineIconRefreshClass).on('click', function(e){
|
||||
getStructureData({
|
||||
mapId: mapId,
|
||||
systemId: systemData.id
|
||||
},{
|
||||
moduleElement: moduleElement,
|
||||
tableApi: tableApi,
|
||||
removeMissing: true
|
||||
}, callbackAddStructureRows);
|
||||
});
|
||||
|
||||
// init listener for global "past" dScan into this page -------------------------------------------------------
|
||||
moduleElement.on('pf:updateIntelModuleByClipboard', function(e, clipboard){
|
||||
updateStructureTableByClipboard(systemData, clipboard, {
|
||||
moduleElement: moduleElement,
|
||||
tableApi: tableApi
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
config: config,
|
||||
getModule: getModule,
|
||||
initModule: initModule
|
||||
};
|
||||
|
||||
});
|
||||
@@ -3,7 +3,7 @@ define([
|
||||
'app/init',
|
||||
'app/util',
|
||||
'morris'
|
||||
], function($, Init, Util, Morris) {
|
||||
], ($, Init, Util, Morris) => {
|
||||
'use strict';
|
||||
|
||||
let config = {
|
||||
@@ -38,7 +38,7 @@ define([
|
||||
* @param options
|
||||
* @returns {jQuery}
|
||||
*/
|
||||
let getLabel = function(text, options){
|
||||
let getLabel = (text, options) => {
|
||||
let label = $('<span>', {
|
||||
class: ['label', options.type, options.align].join(' ')
|
||||
}).text( text );
|
||||
@@ -46,7 +46,12 @@ define([
|
||||
return label;
|
||||
};
|
||||
|
||||
let showKillmails = function(moduleElement, killboardData){
|
||||
/**
|
||||
* show killMails
|
||||
* @param moduleElement
|
||||
* @param killboardData
|
||||
*/
|
||||
let showKillmails = (moduleElement, killboardData) => {
|
||||
|
||||
// show number of killMails
|
||||
let killMailCounterMax = 20;
|
||||
@@ -384,7 +389,7 @@ define([
|
||||
* minify the killboard graph element e.g. if no kills where found, or on error
|
||||
* @param killboardGraphElement
|
||||
*/
|
||||
let minifyKillboardGraphElement = function(killboardGraphElement){
|
||||
let minifyKillboardGraphElement = (killboardGraphElement) => {
|
||||
killboardGraphElement.velocity({
|
||||
height: '20px',
|
||||
marginBottom: '0px'
|
||||
@@ -433,8 +438,9 @@ define([
|
||||
/**
|
||||
* get module element
|
||||
* @param parentElement
|
||||
* @param mapId
|
||||
* @param systemData
|
||||
* @returns {*|jQuery|HTMLElement}
|
||||
* @returns {jQuery}
|
||||
*/
|
||||
let getModule = (parentElement, mapId, systemData) => {
|
||||
// create new module container
|
||||
|
||||
@@ -8,7 +8,7 @@ define([
|
||||
'app/util',
|
||||
'bootbox',
|
||||
'app/map/util'
|
||||
], function($, Init, Util, bootbox, MapUtil) {
|
||||
], ($, Init, Util, bootbox, MapUtil) => {
|
||||
'use strict';
|
||||
|
||||
let config = {
|
||||
@@ -92,10 +92,15 @@ define([
|
||||
* get a unique cache key name for "source"/"target"-name
|
||||
* @param sourceName
|
||||
* @param targetName
|
||||
* @returns {string}
|
||||
* @returns {*}
|
||||
*/
|
||||
let getConnectionDataCacheKey = (sourceName, targetName) => {
|
||||
return [sourceName.toLowerCase(), targetName.toLowerCase()].sort().join('###');
|
||||
let key = false;
|
||||
if(sourceName && targetName){
|
||||
// names can be "undefined" in case system is currently on drag/drop
|
||||
key = [sourceName.toLowerCase(), targetName.toLowerCase()].sort().join('###');
|
||||
}
|
||||
return key;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -103,7 +108,7 @@ define([
|
||||
* @param context
|
||||
* @param routesData
|
||||
*/
|
||||
let callbackAddRouteRow = (context, routesData) => {
|
||||
let callbackAddRouteRows = (context, routesData) => {
|
||||
|
||||
if(routesData.length > 0){
|
||||
for(let i = 0; i < routesData.length; i++){
|
||||
@@ -170,7 +175,6 @@ define([
|
||||
return rowElement;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* requests route data from eveCentral API and execute callback
|
||||
* @param requestData
|
||||
@@ -210,7 +214,7 @@ define([
|
||||
routeData.push( getRouteRequestDataFromRowData( this.data() ));
|
||||
});
|
||||
|
||||
getRouteData({routeData: routeData}, context, callbackAddRouteRow);
|
||||
getRouteData({routeData: routeData}, context, callbackAddRouteRows);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -261,7 +265,7 @@ define([
|
||||
mapSelectOptions: mapSelectOptions
|
||||
};
|
||||
|
||||
requirejs(['text!templates/dialog/route.html', 'mustache'], function(template, Mustache) {
|
||||
requirejs(['text!templates/dialog/route.html', 'mustache'], (template, Mustache) => {
|
||||
|
||||
let content = Mustache.render(template, data);
|
||||
|
||||
@@ -326,7 +330,7 @@ define([
|
||||
}]
|
||||
};
|
||||
|
||||
getRouteData(requestData, context, callbackAddRouteRow);
|
||||
getRouteData(requestData, context, callbackAddRouteRows);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -418,7 +422,7 @@ define([
|
||||
routeData: requestRouteData
|
||||
};
|
||||
|
||||
getRouteData(requestData, contextData, callbackAddRouteRow);
|
||||
getRouteData(requestData, contextData, callbackAddRouteRows);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -432,7 +436,7 @@ define([
|
||||
let showSettingsDialog = (dialogData, moduleElement, systemFromData, routesTable) => {
|
||||
|
||||
let promiseStore = MapUtil.getLocaleData('map', dialogData.mapId);
|
||||
promiseStore.then(function(dataStore) {
|
||||
promiseStore.then(dataStore => {
|
||||
// selected systems (if already stored)
|
||||
let systemSelectOptions = [];
|
||||
if(
|
||||
@@ -902,7 +906,6 @@ define([
|
||||
)
|
||||
);
|
||||
|
||||
// crate new route table
|
||||
let table = $('<table>', {
|
||||
class: ['compact', 'stripe', 'order-column', 'row-border', config.systemInfoRoutesTableClass].join(' ')
|
||||
});
|
||||
@@ -919,7 +922,7 @@ define([
|
||||
autoWidth: false,
|
||||
rowId: 'systemTo',
|
||||
language: {
|
||||
emptyTable: 'No routes added'
|
||||
emptyTable: 'No routes added'
|
||||
},
|
||||
columnDefs: [
|
||||
{
|
||||
@@ -1039,7 +1042,7 @@ define([
|
||||
routeData: [routeData]
|
||||
};
|
||||
|
||||
getRouteData(requestData, context, callbackAddRouteRow);
|
||||
getRouteData(requestData, context, callbackAddRouteRows);
|
||||
});
|
||||
}
|
||||
},{
|
||||
@@ -1074,7 +1077,7 @@ define([
|
||||
routeData: [routeData]
|
||||
};
|
||||
|
||||
getRouteData(requestData, context, callbackAddRouteRow);
|
||||
getRouteData(requestData, context, callbackAddRouteRows);
|
||||
});
|
||||
}
|
||||
},{
|
||||
@@ -1122,10 +1125,10 @@ define([
|
||||
});
|
||||
|
||||
for(let i = 0; i < animationRows.length; i++){
|
||||
$(animationRows[i]).pulseTableRow($(animationRows[i]).data('animationStatus'));
|
||||
$(animationRows[i]).removeData('animationStatus');
|
||||
let animationRow = $(animationRows[i]);
|
||||
animationRow.pulseTableRow(animationRow.data('animationStatus'));
|
||||
animationRow.removeData('animationStatus');
|
||||
}
|
||||
|
||||
},
|
||||
initComplete: function(settings, json){
|
||||
// click on "fake connection" -------------------------------------------------------------------------
|
||||
@@ -1231,7 +1234,6 @@ define([
|
||||
};
|
||||
|
||||
let routesTableElement = moduleElement.find('.' + config.systemInfoRoutesTableClass);
|
||||
|
||||
let routesTable = routesTableElement.DataTable();
|
||||
|
||||
// init refresh routes --------------------------------------------------------------------
|
||||
|
||||
@@ -23,15 +23,12 @@ define([
|
||||
moduleClass: 'pf-module', // class for each module
|
||||
|
||||
// system signature module
|
||||
moduleTypeClass: 'pf-sig-table-module', // module wrapper
|
||||
moduleTypeClass: 'pf-signature-table-module', // module wrapper
|
||||
|
||||
// tables
|
||||
tableToolsClass: 'pf-table-tools', // class for table toolbar
|
||||
tableToolsActionClass: 'pf-table-tools-action', // class for table toolbar action
|
||||
|
||||
// dialogs
|
||||
signatureReaderDialogId: 'pf-signature-reader-dialog', // id for signature reader dialog
|
||||
|
||||
// signature progress bar
|
||||
signatureScannedProgressBarClass: 'pf-system-progress-scanned', // class for signature progress bar
|
||||
|
||||
@@ -340,10 +337,11 @@ define([
|
||||
// and add "new" row
|
||||
let changedRowElement = addSignatureRow(signatureTableApi, currentSystemData.systemData, signatureData[i], false);
|
||||
|
||||
// highlight
|
||||
changedRowElement.pulseTableRow('changed');
|
||||
|
||||
notificationCounter.changed++;
|
||||
if(changedRowElement){
|
||||
// highlight
|
||||
changedRowElement.pulseTableRow('changed');
|
||||
notificationCounter.changed++;
|
||||
}
|
||||
}
|
||||
|
||||
// remove signature data -> all left signatures will be added
|
||||
@@ -383,13 +381,13 @@ define([
|
||||
// and add "new" row
|
||||
let newRowElement = addSignatureRow(signatureTableApi, currentSystemData.systemData, signatureData[k], false);
|
||||
|
||||
// highlight
|
||||
newRowElement.pulseTableRow('added');
|
||||
|
||||
notificationCounter.added++;
|
||||
if(newRowElement){
|
||||
// highlight
|
||||
newRowElement.pulseTableRow('added');
|
||||
notificationCounter.added++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// show notification ------------------------------------------------------------------------------------------
|
||||
if(
|
||||
notificationCounter.added > 0 ||
|
||||
@@ -399,9 +397,8 @@ define([
|
||||
// update signature bar
|
||||
moduleElement.updateScannedSignaturesBar({showNotice: true});
|
||||
|
||||
// show Notification
|
||||
let notification = notificationCounter.added + ' added<br>';
|
||||
notification += notificationCounter.changed + ' changed<br>';
|
||||
notification += notificationCounter.changed + ' updated<br>';
|
||||
notification += notificationCounter.deleted + ' deleted<br>';
|
||||
Util.showNotify({title: 'Signatures updated', text: notification, type: 'success'});
|
||||
|
||||
@@ -499,7 +496,6 @@ define([
|
||||
Util.showNotify({title: 'System is scanned', text: notification, type: 'success'});
|
||||
}
|
||||
}
|
||||
|
||||
}, 100);
|
||||
};
|
||||
|
||||
@@ -508,36 +504,23 @@ define([
|
||||
* @param systemData
|
||||
*/
|
||||
$.fn.showSignatureReaderDialog = function(systemData){
|
||||
|
||||
let moduleElement = $(this);
|
||||
|
||||
let data = {
|
||||
id: config.signatureReaderDialogId
|
||||
};
|
||||
|
||||
requirejs(['text!templates/dialog/signature_reader.html', 'mustache'], function(template, Mustache) {
|
||||
|
||||
let content = Mustache.render(template, data);
|
||||
|
||||
requirejs(['text!templates/dialog/signature_reader.html', 'mustache'], (template, Mustache) => {
|
||||
let signatureReaderDialog = bootbox.dialog({
|
||||
title: 'Signature reader',
|
||||
message: content,
|
||||
message: Mustache.render(template, {}),
|
||||
buttons: {
|
||||
close: {
|
||||
label: 'cancel',
|
||||
className: 'btn-default',
|
||||
callback: function(){
|
||||
$(signatureReaderDialog).modal('hide');
|
||||
}
|
||||
className: 'btn-default'
|
||||
},
|
||||
success: {
|
||||
label: '<i class="fas fa-paste fa-fw"></i> update signatures',
|
||||
className: 'btn-success',
|
||||
callback: function () {
|
||||
// get form Values
|
||||
let form = $('#' + config.signatureReaderDialogId).find('form');
|
||||
let formData = $(form).getFormValues();
|
||||
|
||||
let form = this.find('form');
|
||||
let formData = form.getFormValues();
|
||||
let signatureOptions = {
|
||||
deleteOld: (formData.deleteOld) ? 1 : 0
|
||||
};
|
||||
@@ -554,8 +537,6 @@ define([
|
||||
// set focus on sig-input textarea
|
||||
signatureReaderDialog.find('textarea').focus();
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
};
|
||||
|
||||
@@ -607,10 +588,8 @@ define([
|
||||
|
||||
// check if copy&paste is enabled
|
||||
if( !disableCopyFromClipboard ){
|
||||
|
||||
// parse input stream
|
||||
let signatureData = parseSignatureString(systemData, clipboard);
|
||||
|
||||
if(signatureData.length > 0){
|
||||
// valid signature data parsed
|
||||
|
||||
@@ -641,7 +620,7 @@ define([
|
||||
};
|
||||
|
||||
/**
|
||||
* parses a copy&paste string from ingame scanning window and parses it
|
||||
* parses a copy&paste string from ingame scanning window
|
||||
* @param systemData
|
||||
* @param clipboard
|
||||
* @returns {Array}
|
||||
@@ -656,9 +635,7 @@ define([
|
||||
|
||||
for(let i = 0; i < signatureRows.length; i++){
|
||||
let rowData = signatureRows[i].split(/\t/g);
|
||||
|
||||
if(rowData.length === 6){
|
||||
|
||||
// check if sig Type = anomaly or combat site
|
||||
if(validSignatureNames.indexOf( rowData[1] ) !== -1){
|
||||
|
||||
@@ -702,8 +679,6 @@ define([
|
||||
}else{
|
||||
invalidSignatures++;
|
||||
}
|
||||
}else{
|
||||
invalidSignatures++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -875,7 +850,7 @@ define([
|
||||
* @param options
|
||||
* @returns {*|jQuery}
|
||||
*/
|
||||
let getLabledButton = function(options){
|
||||
let getLabeledButton = function(options){
|
||||
|
||||
let buttonClasses = ['btn', 'btn-sm', 'btn-labeled'];
|
||||
|
||||
@@ -1002,7 +977,7 @@ define([
|
||||
let tableToolbar = $('<div>', {
|
||||
class: config.tableToolsClass
|
||||
}).append(
|
||||
getLabledButton({
|
||||
getLabeledButton({
|
||||
type: 'primary',
|
||||
label: 'add',
|
||||
icon: 'fa-plus',
|
||||
@@ -1026,7 +1001,7 @@ define([
|
||||
}
|
||||
})
|
||||
).append(
|
||||
getLabledButton({
|
||||
getLabeledButton({
|
||||
type: 'primary',
|
||||
label: 'signature reader',
|
||||
icon: 'fa-paste',
|
||||
@@ -1035,7 +1010,7 @@ define([
|
||||
}
|
||||
})
|
||||
).append(
|
||||
getLabledButton({
|
||||
getLabeledButton({
|
||||
type: 'default',
|
||||
label: 'select all',
|
||||
icon: 'fa-check-square',
|
||||
@@ -1063,7 +1038,7 @@ define([
|
||||
value: 1,
|
||||
}).attr('data-toggle', 'toggle')
|
||||
).append(
|
||||
getLabledButton({
|
||||
getLabeledButton({
|
||||
type: 'danger',
|
||||
classes: [config.sigTableClearButtonClass, 'pull-right'],
|
||||
label: 'delete',
|
||||
@@ -1812,7 +1787,7 @@ define([
|
||||
// update connection conflicts
|
||||
checkConnectionConflicts();
|
||||
|
||||
Util.showNotify({title: 'Signature deleted', text: signatureCount + ' signatures deleted', type: 'success'});
|
||||
Util.showNotify({title: 'Signature deleted', text: signatureCount + ' deleted', type: 'success'});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1843,25 +1818,25 @@ define([
|
||||
* @param animate
|
||||
* @returns {*}
|
||||
*/
|
||||
let addSignatureRow = function(signatureTableApi, systemData, signatureData, animate){
|
||||
let newSignatureData = formatSignatureData(systemData, [signatureData], fullSignatureOptions);
|
||||
let addSignatureRow = (signatureTableApi, systemData, signatureData, animate) => {
|
||||
let newRowElement = null;
|
||||
if(signatureTableApi){
|
||||
let newSignatureData = formatSignatureData(systemData, [signatureData], fullSignatureOptions);
|
||||
let newRowNode = signatureTableApi.row.add(newSignatureData.shift()).draw().nodes();
|
||||
newRowElement = newRowNode.to$();
|
||||
|
||||
let newRowNode = signatureTableApi.row.add(newSignatureData.shift()).draw().nodes();
|
||||
let newRowElement = newRowNode.to$();
|
||||
|
||||
if(animate === true){
|
||||
newRowElement.hide();
|
||||
|
||||
newRowElement.toggleTableRow(function(newRowElement){
|
||||
// make new row editable
|
||||
if(animate === true){
|
||||
newRowElement.hide();
|
||||
newRowElement.toggleTableRow(newRowElement => {
|
||||
// make new row editable
|
||||
newRowElement.makeEditable(signatureTableApi, systemData);
|
||||
|
||||
// update scan progress bar
|
||||
newRowElement.parents('.' + config.moduleClass).updateScannedSignaturesBar({showNotice: true});
|
||||
});
|
||||
}else{
|
||||
newRowElement.makeEditable(signatureTableApi, systemData);
|
||||
|
||||
// update scan progress bar
|
||||
newRowElement.parents('.' + config.moduleClass).updateScannedSignaturesBar({showNotice: true});
|
||||
});
|
||||
}else{
|
||||
newRowElement.makeEditable(signatureTableApi, systemData);
|
||||
}
|
||||
}
|
||||
|
||||
return newRowElement;
|
||||
@@ -2270,20 +2245,22 @@ define([
|
||||
|
||||
let newRowElement = addSignatureRow(primaryTableApi, systemData, data.signatures[0], true);
|
||||
|
||||
// highlight
|
||||
newRowElement.pulseTableRow('added');
|
||||
if(newRowElement){
|
||||
// highlight
|
||||
newRowElement.pulseTableRow('added');
|
||||
|
||||
// prepare "add signature" table for new entry -> reset -------------------
|
||||
let signatureData = formatSignatureData(systemData, [emptySignatureData], emptySignatureOptions);
|
||||
let newAddRowElement = secondaryTableApi.clear().row.add(signatureData.shift()).draw().nodes();
|
||||
// prepare "add signature" table for new entry -> reset -------------------
|
||||
let signatureData = formatSignatureData(systemData, [emptySignatureData], emptySignatureOptions);
|
||||
let newAddRowElement = secondaryTableApi.clear().row.add(signatureData.shift()).draw().nodes();
|
||||
|
||||
newAddRowElement.to$().makeEditable(secondaryTableApi, systemData);
|
||||
newAddRowElement.to$().makeEditable(secondaryTableApi, systemData);
|
||||
|
||||
Util.showNotify({
|
||||
title: 'Signature added',
|
||||
text: 'Name: ' + data.name,
|
||||
type: 'success'
|
||||
});
|
||||
Util.showNotify({
|
||||
title: 'Signature added',
|
||||
text: 'Name: ' + data.name,
|
||||
type: 'success'
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
239
js/app/util.js
239
js/app/util.js
@@ -15,7 +15,8 @@ define([
|
||||
'easyPieChart',
|
||||
'hoverIntent',
|
||||
'bootstrapConfirmation',
|
||||
'bootstrapToggle'
|
||||
'bootstrapToggle',
|
||||
'select2'
|
||||
], ($, Init, SystemEffect, SignatureType, bootbox, localforage) => {
|
||||
|
||||
'use strict';
|
||||
@@ -55,14 +56,19 @@ define([
|
||||
mapWrapperClass: 'pf-map-wrapper', // wrapper div (scrollable)
|
||||
mapClass: 'pf-map' , // class for all maps
|
||||
|
||||
// select2
|
||||
select2Class: 'pf-select2', // class for all "Select2" <select> elements
|
||||
|
||||
// animation
|
||||
animationPulseSuccessClass: 'pf-animation-pulse-success', // animation class
|
||||
animationPulseWarningClass: 'pf-animation-pulse-warning', // animation class
|
||||
|
||||
// popover
|
||||
popoverTriggerClass: 'pf-popover-trigger' // class for "popover" trigger elements
|
||||
popoverSmallClass: 'pf-popover-small', // class for "small" popover
|
||||
popoverTriggerClass: 'pf-popover-trigger', // class for "popover" trigger elements
|
||||
|
||||
// fonts
|
||||
fontTriglivianClass: 'pf-triglivian' // class for "Triglivian" names (e.g. Abyssal systems)
|
||||
};
|
||||
|
||||
let stopTimerCache = {}; // cache for stopwatch timer
|
||||
@@ -389,7 +395,6 @@ define([
|
||||
let valid = false;
|
||||
|
||||
let errorElements = form.find('.has-error');
|
||||
|
||||
if(errorElements.length === 0){
|
||||
valid = true;
|
||||
}
|
||||
@@ -402,7 +407,6 @@ define([
|
||||
* @returns {Array}
|
||||
*/
|
||||
$.fn.isInViewport = function(){
|
||||
|
||||
let visibleElement = [];
|
||||
|
||||
this.each(function(){
|
||||
@@ -436,12 +440,10 @@ define([
|
||||
* init the map-update-counter as "easy-pie-chart"
|
||||
*/
|
||||
$.fn.initMapUpdateCounter = function(){
|
||||
|
||||
let counterChart = $(this);
|
||||
|
||||
counterChart.easyPieChart({
|
||||
barColor: function(percent){
|
||||
|
||||
let color = '#568a89';
|
||||
if(percent <= 30){
|
||||
color = '#d9534f';
|
||||
@@ -450,7 +452,6 @@ define([
|
||||
}
|
||||
|
||||
return color;
|
||||
|
||||
},
|
||||
trackColor: '#2b2b2b',
|
||||
size: 30,
|
||||
@@ -657,6 +658,24 @@ define([
|
||||
});
|
||||
};
|
||||
|
||||
$.fn.destroyPopover = function(recursive){
|
||||
return this.each(function() {
|
||||
let element = $(this);
|
||||
let popoverSelector = '.' + config.popoverTriggerClass;
|
||||
let popoverElements = element.filter(popoverSelector);
|
||||
if(recursive){
|
||||
popoverElements = popoverElements.add(element.find(popoverSelector));
|
||||
}
|
||||
|
||||
popoverElements.each(function() {
|
||||
let popoverElement = $(this);
|
||||
if(popoverElement.data('bs.popover')){
|
||||
popoverElement.popover('destroy');
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* set "popover" close action on clicking "somewhere" on the <body>
|
||||
* @param eventNamespace
|
||||
@@ -689,6 +708,20 @@ define([
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* adds the small-class to a tooltip
|
||||
* @returns {*}
|
||||
*/
|
||||
$.fn.setPopoverSmall = function(){
|
||||
return this.each(function() {
|
||||
let element = $(this);
|
||||
let popover = element.data('bs.popover');
|
||||
if(popover){
|
||||
popover.tip().addClass(config.popoverSmallClass);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* display a custom message (info/warning/error) to a container element
|
||||
* check: $.fn.showFormMessage() for an other way of showing messages
|
||||
@@ -741,38 +774,6 @@ define([
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* wrapper function for onClick() || onDblClick() events in order to distinguish between this two types of events
|
||||
* @param singleClickCallback
|
||||
* @param doubleClickCallback
|
||||
* @param timeout
|
||||
* @returns {any|JQuery|*}
|
||||
*/
|
||||
$.fn.singleDoubleClick = function(singleClickCallback, doubleClickCallback, timeout) {
|
||||
return this.each(function(){
|
||||
let clicks = 0, self = this;
|
||||
|
||||
// prevent default behaviour (e.g. open <a>-tag link)
|
||||
$(this).off('click').on('click', function(e){
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
$(this).off('mouseup').on('mouseup', function(e){
|
||||
clicks++;
|
||||
if (clicks === 1) {
|
||||
setTimeout(function(){
|
||||
if(clicks === 1) {
|
||||
singleClickCallback.call(self, e);
|
||||
} else {
|
||||
doubleClickCallback.call(self, e);
|
||||
}
|
||||
clicks = 0;
|
||||
}, timeout || Init.timer.DBL_CLICK);
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* highlight jquery elements
|
||||
* add/remove css class for keyframe animation
|
||||
@@ -962,14 +963,78 @@ define([
|
||||
};
|
||||
|
||||
/**
|
||||
* set default configuration for "Bootbox" dialogs
|
||||
* set default configuration for "Bootbox"
|
||||
*/
|
||||
let initDefaultBootboxConfig = function(){
|
||||
let initDefaultBootboxConfig = () => {
|
||||
bootbox.setDefaults({
|
||||
onEscape: true // enables close dialogs on ESC key
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* set default configuration for "Select2"
|
||||
*/
|
||||
let initDefaultSelect2Config = () => {
|
||||
$.fn.select2.defaults.set('theme', 'pathfinder');
|
||||
|
||||
let initScrollbar = (resultsWrapper) => {
|
||||
// default 'mousewheel' event set by select2 needs to be disabled
|
||||
// in order to make mCustomScrollbar mouseWheel enable works correctly
|
||||
$(resultsWrapper).find('ul.select2-results__options').off('mousewheel');
|
||||
|
||||
resultsWrapper.mCustomScrollbar({
|
||||
mouseWheel: {
|
||||
enable: true,
|
||||
scrollAmount: 'auto',
|
||||
axis: 'y',
|
||||
preventDefault: true
|
||||
},
|
||||
keyboard: {
|
||||
enable: false,
|
||||
scrollType: 'stepless',
|
||||
scrollAmount: 'auto'
|
||||
},
|
||||
scrollbarPosition: 'inside',
|
||||
autoDraggerLength: true,
|
||||
autoHideScrollbar: false,
|
||||
advanced: {
|
||||
updateOnContentResize: true
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
let getResultsWrapper = (selectElement) => {
|
||||
let wrapper = null;
|
||||
let selectElementId = selectElement.getAttribute('data-select2-id');
|
||||
if(selectElementId){
|
||||
let resultsOptions = $('#select2-' + selectElementId + '-results');
|
||||
if(resultsOptions.length) {
|
||||
let resultsWrapper = resultsOptions.parents('.select2-results');
|
||||
if(resultsWrapper.length){
|
||||
wrapper = resultsWrapper;
|
||||
}
|
||||
}
|
||||
}
|
||||
return wrapper;
|
||||
};
|
||||
|
||||
// global open event
|
||||
$(document).on('select2:open', '.' + config.select2Class, function(e){
|
||||
let resultsWrapper = getResultsWrapper(this);
|
||||
if(resultsWrapper){
|
||||
initScrollbar(resultsWrapper);
|
||||
}
|
||||
});
|
||||
|
||||
// global select2:closing event
|
||||
$(document).on('select2:closing', '.' + config.select2Class, function(e){
|
||||
let resultsWrapper = getResultsWrapper(this);
|
||||
if(resultsWrapper){
|
||||
resultsWrapper.mCustomScrollbar('destroy');
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* get the current main trigger delay for the main trigger functions
|
||||
* optional in/decrease the delay
|
||||
@@ -1104,6 +1169,33 @@ define([
|
||||
return duration;
|
||||
};
|
||||
|
||||
/**
|
||||
* update a character counter field with current value length - maxCharLength
|
||||
* @param field
|
||||
* @param charCounterElement
|
||||
* @param maxCharLength
|
||||
*/
|
||||
let updateCounter = function(field, charCounterElement, maxCharLength){
|
||||
let value = field.val();
|
||||
let inputLength = value.length;
|
||||
|
||||
// line breaks are 2 characters!
|
||||
let newLines = value.match(/(\r\n|\n|\r)/g);
|
||||
let addition = 0;
|
||||
if (newLines != null) {
|
||||
addition = newLines.length;
|
||||
}
|
||||
inputLength += addition;
|
||||
|
||||
charCounterElement.text(maxCharLength - inputLength);
|
||||
|
||||
if(maxCharLength <= inputLength){
|
||||
charCounterElement.toggleClass('txt-color-red', true);
|
||||
}else{
|
||||
charCounterElement.toggleClass('txt-color-red', false);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* trigger main logging event with log information
|
||||
* @param logKey
|
||||
@@ -2284,6 +2376,68 @@ define([
|
||||
return dateString + ' ' + timeString;
|
||||
};
|
||||
|
||||
/**
|
||||
* check an element for attached event by name
|
||||
* -> e.g. eventName = 'click.myNamespace'
|
||||
* @param element
|
||||
* @param eventName
|
||||
* @returns {boolean}
|
||||
*/
|
||||
let hasEvent = (element, eventName) => {
|
||||
let exists = false;
|
||||
let parts = eventName.split('.');
|
||||
let name = parts[0];
|
||||
let namespace = parts.length === 2 ? parts[1] : false;
|
||||
let events = $._data( element[0], 'events')[name];
|
||||
if(events){
|
||||
if(namespace){
|
||||
// seach events by namespace
|
||||
for(let event of events){
|
||||
if(event.namespace === namespace){
|
||||
exists = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}else{
|
||||
// at least ONE event of the given name found
|
||||
exists = true;
|
||||
}
|
||||
}
|
||||
return exists;
|
||||
};
|
||||
|
||||
/**
|
||||
* wrapper function for onClick() || onDblClick() events in order to distinguish between this two types of events
|
||||
* @param element
|
||||
* @param singleClickCallback
|
||||
* @param doubleClickCallback
|
||||
* @param timeout
|
||||
*/
|
||||
let singleDoubleClick = (element, singleClickCallback, doubleClickCallback, timeout) => {
|
||||
let eventName = 'mouseup.singleDouble';
|
||||
if(!hasEvent(element, eventName)){
|
||||
let clicks = 0;
|
||||
// prevent default behaviour (e.g. open <a>-tag link)
|
||||
element.off('click').on('click', function(e){
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
element.off(eventName).on(eventName, function(e){
|
||||
clicks++;
|
||||
if (clicks === 1) {
|
||||
setTimeout(element => {
|
||||
if(clicks === 1) {
|
||||
singleClickCallback.call(element, e);
|
||||
} else {
|
||||
doubleClickCallback.call(element, e);
|
||||
}
|
||||
clicks = 0;
|
||||
}, timeout || Init.timer.DBL_CLICK, this);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* get deep json object value if exists
|
||||
* -> e.g. key = 'first.last.third' string
|
||||
@@ -2413,12 +2567,14 @@ define([
|
||||
showVersionInfo: showVersionInfo,
|
||||
initPrototypes: initPrototypes,
|
||||
initDefaultBootboxConfig: initDefaultBootboxConfig,
|
||||
initDefaultSelect2Config: initDefaultSelect2Config,
|
||||
getCurrentTriggerDelay: getCurrentTriggerDelay,
|
||||
getServerTime: getServerTime,
|
||||
convertTimestampToServerTime: convertTimestampToServerTime,
|
||||
getTimeDiffParts: getTimeDiffParts,
|
||||
timeStart: timeStart,
|
||||
timeStop: timeStop,
|
||||
updateCounter: updateCounter,
|
||||
log: log,
|
||||
showNotify: showNotify,
|
||||
stopTabBlink: stopTabBlink,
|
||||
@@ -2470,6 +2626,7 @@ define([
|
||||
getLocalStorage: getLocalStorage,
|
||||
clearSessionStorage: clearSessionStorage,
|
||||
getBrowserTabId: getBrowserTabId,
|
||||
singleDoubleClick: singleDoubleClick,
|
||||
getObjVal: getObjVal,
|
||||
redirect: redirect,
|
||||
logout: logout,
|
||||
|
||||
9
js/lib/jquery.hoverIntent.min.js
vendored
Normal file
9
js/lib/jquery.hoverIntent.min.js
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
/*!
|
||||
* hoverIntent v1.9.0 // 2017.09.01 // jQuery v1.7.0+
|
||||
* http://briancherne.github.io/jquery-hoverIntent/
|
||||
*
|
||||
* You may use hoverIntent under the terms of the MIT license. Basically that
|
||||
* means you are free to use hoverIntent as long as this header is left intact.
|
||||
* Copyright 2007-2017 Brian Cherne
|
||||
*/
|
||||
!function(factory){"use strict";"function"==typeof define&&define.amd?define(["jquery"],factory):jQuery&&!jQuery.fn.hoverIntent&&factory(jQuery)}(function($){"use strict";var cX,cY,_cfg={interval:100,sensitivity:6,timeout:0},INSTANCE_COUNT=0,track=function(ev){cX=ev.pageX,cY=ev.pageY},compare=function(ev,$el,s,cfg){if(Math.sqrt((s.pX-cX)*(s.pX-cX)+(s.pY-cY)*(s.pY-cY))<cfg.sensitivity)return $el.off(s.event,track),delete s.timeoutId,s.isActive=!0,ev.pageX=cX,ev.pageY=cY,delete s.pX,delete s.pY,cfg.over.apply($el[0],[ev]);s.pX=cX,s.pY=cY,s.timeoutId=setTimeout(function(){compare(ev,$el,s,cfg)},cfg.interval)},delay=function(ev,$el,s,out){return delete $el.data("hoverIntent")[s.id],out.apply($el[0],[ev])};$.fn.hoverIntent=function(handlerIn,handlerOut,selector){var instanceId=INSTANCE_COUNT++,cfg=$.extend({},_cfg);$.isPlainObject(handlerIn)?(cfg=$.extend(cfg,handlerIn),$.isFunction(cfg.out)||(cfg.out=cfg.over)):cfg=$.isFunction(handlerOut)?$.extend(cfg,{over:handlerIn,out:handlerOut,selector:selector}):$.extend(cfg,{over:handlerIn,out:handlerIn,selector:handlerOut});var handleHover=function(e){var ev=$.extend({},e),$el=$(this),hoverIntentData=$el.data("hoverIntent");hoverIntentData||$el.data("hoverIntent",hoverIntentData={});var state=hoverIntentData[instanceId];state||(hoverIntentData[instanceId]=state={id:instanceId}),state.timeoutId&&(state.timeoutId=clearTimeout(state.timeoutId));var mousemove=state.event="mousemove.hoverIntent.hoverIntent"+instanceId;if("mouseenter"===e.type){if(state.isActive)return;state.pX=ev.pageX,state.pY=ev.pageY,$el.off(mousemove,track).on(mousemove,track),state.timeoutId=setTimeout(function(){compare(ev,$el,state,cfg)},cfg.interval)}else{if(!state.isActive)return;$el.off(mousemove,track),state.timeoutId=setTimeout(function(){delay(ev,$el,state,cfg.out)},cfg.timeout)}};return this.on({"mouseenter.hoverIntent":handleHover,"mouseleave.hoverIntent":handleHover},cfg.selector)}});
|
||||
4
js/lib/select2.min.js
vendored
4
js/lib/select2.min.js
vendored
File diff suppressed because one or more lines are too long
6
js/lib/velocity.min.js
vendored
6
js/lib/velocity.min.js
vendored
File diff suppressed because one or more lines are too long
2
js/lib/velocity.ui.min.js
vendored
2
js/lib/velocity.ui.min.js
vendored
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
BIN
public/css/v1.3.5/pathfinder.css.br
Normal file
BIN
public/css/v1.3.5/pathfinder.css.br
Normal file
Binary file not shown.
7
public/css/v1.3.5/pathfinder.css.map
Normal file
7
public/css/v1.3.5/pathfinder.css.map
Normal file
File diff suppressed because one or more lines are too long
BIN
public/fonts/triglavian-regular.eot
Normal file
BIN
public/fonts/triglavian-regular.eot
Normal file
Binary file not shown.
BIN
public/fonts/triglavian-regular.ttf
Normal file
BIN
public/fonts/triglavian-regular.ttf
Normal file
Binary file not shown.
BIN
public/fonts/triglavian-regular.woff
Normal file
BIN
public/fonts/triglavian-regular.woff
Normal file
Binary file not shown.
BIN
public/fonts/triglavian-regular.woff2
Normal file
BIN
public/fonts/triglavian-regular.woff2
Normal file
Binary file not shown.
BIN
public/img/icons/anoik_logo.png
Normal file
BIN
public/img/icons/anoik_logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.2 KiB |
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user