- new "logging" system for map/system/signature/connection changes, closed #271
- new map change log to Slack channel - new "rally point" logging to Slack channel - new "rally point" poke options (e.g. custom message), closed #295 - new log options for WebSocket installations - added ship "mass" logging (backend only), #313 - added map logging to Slack, #326 - added "ESI error rate" limit detection - added "Monolog" as new logging library (Composer dependency) - added "Swiftmailer" as new eMail library (Composer dependency) - added Support for Redis session hander (performance boost) - improved character select panels (visible "online" status) - improved "activity logging" (more DB columns added to check) - improved eMail logging (HTML template support) - improved "delete map" now become "inactive" for some days before delete - improved character logout handling - improved /setup page for DB bootstrap (new button for DB create if not exists) - fixed broken ship tracking (ship name re-added) - fixed broken ship tracking for multiple chars on different browser tabs - fixed broken cursor coordinates, closed #518 - fixed null pointer "charactermodel.php->isActive():925" closed #529 - fixed broken "scroll offset", closed #533 closed #534 - Updated "validation" library JS v0.10.1 -> v0.11.9 - Updated ORM Mapper _Cortex_ v1.5.0-dev -> v1.5.0 - and many more....
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -55,3 +55,4 @@ Temporary Items
|
||||
/node_modules/
|
||||
/public/js/vX.X.X/
|
||||
/vendor/
|
||||
/history/
|
||||
|
||||
52
README.md
52
README.md
@@ -16,7 +16,7 @@ Mapping tool for [*EVE ONLINE*](https://www.eveonline.com)
|
||||
- Database will be cleared from time to time
|
||||
- Installation guide:
|
||||
- [wiki](https://github.com/exodus4d/pathfinder/wiki)
|
||||
- Developer chat [Slack](https://slack.com) :
|
||||
- Developer [Slack](https://slack.com) chat:
|
||||
- https://pathfinder-eve-online.slack.com
|
||||
- Please send me a mail for invite: pathfinder@exodus4d.de
|
||||
|
||||
@@ -28,44 +28,46 @@ Issues should be reported in the [Issue](https://github.com/exodus4d/pathfinder/
|
||||
### Project structure
|
||||
|
||||
```
|
||||
|-- (0755) app --> backend [*.php]
|
||||
|-- app --> "Fat Free Framework" extensions
|
||||
|-- lib --> "Fat Free Framework"
|
||||
|-- main --> "PATHFINDER" root
|
||||
|-- [0755] app/ --> backend [*.php]
|
||||
|-- app/ --> "Fat Free Framework" extensions
|
||||
|-- lib/ --> "Fat Free Framework"
|
||||
|-- main/ --> "PATHFINDER" root
|
||||
|-- config.ini --> config "f3" framework
|
||||
|-- cron.ini --> config - cronjobs
|
||||
|-- environment.ini --> config - system environment
|
||||
|-- pathfinder.ini --> config - pathfinder
|
||||
|-- requirements.ini --> config - system requirements
|
||||
|-- routes.ini --> config - routes
|
||||
|-- (0755) export --> DB export data
|
||||
|-- sql --> static DB data for import (pathfinder.sql)
|
||||
|-- (0755) favicon --> Favicons
|
||||
|-- (0755) js --> JS source files (raw)
|
||||
|-- app --> "PASTHFINDER" core files (not used for production)
|
||||
|-- lib --> 3rd partie extension/library (not used for production)
|
||||
|-- [0755] export/ --> static data
|
||||
|-- csv/ --> *.csv used by /setup page
|
||||
|-- json/ --> *.json used by /setup page
|
||||
|-- sql/ --> DB dump for import (pathfinder.sql)
|
||||
|-- [0755] favicon/ --> Favicons
|
||||
|-- [0777] history/ --> log files (map history logs) [optional]
|
||||
|-- [0755] js/ --> JS source files (raw)
|
||||
|-- app/ --> "PASTHFINDER" core files (not used for production)
|
||||
|-- lib/ --> 3rd partie extension/library (not used for production)
|
||||
|-- app.js --> require.js config (!required for production!)
|
||||
|-- (0777) logs --> log files
|
||||
|-- [0777] logs/ --> log files
|
||||
|-- ...
|
||||
| -- node_modules --> node.js modules (not used for production)
|
||||
| -- node_modules/ --> node.js modules (not used for production)
|
||||
|-- ...
|
||||
|-- (0755) public --> frontend source
|
||||
|-- css --> CSS dist/build folder (minified)
|
||||
|-- fonts --> (icon)-Fonts
|
||||
|-- img --> images
|
||||
|-- js --> JS dist/build folder and source maps (minified, uglified)
|
||||
|-- templates --> templates
|
||||
|-- sass --> SCSS source (not used for production)
|
||||
|-- ...
|
||||
|-- (0777) tmp --> cache folder
|
||||
|-- ...
|
||||
|-- (0755) .htaccess --> reroute/caching rules ("Apache" only!)
|
||||
|-- (0755) index.php
|
||||
|-- [0755] public/ --> frontend source
|
||||
|-- css/ --> CSS dist/build folder (minified)
|
||||
|-- fonts/ --> (icon)-Fonts
|
||||
|-- img/ --> images
|
||||
|-- js/ --> JS dist/build folder and source maps (minified, uglified)
|
||||
|-- templates/ --> templates
|
||||
|-- sass/ --> SCSS source (not used for production)
|
||||
|-- [0777] tmp/ --> cache folder
|
||||
|-- [0755] .htaccess --> reroute/caching rules ("Apache" only!)
|
||||
|-- [0755] index.php
|
||||
|
||||
--------------------------
|
||||
CI/CD config files:
|
||||
--------------------------
|
||||
|-- .jshintrc --> "JSHint" config (not used for production)
|
||||
|-- composer.json --> Composer package definition
|
||||
|-- config.rb --> "Compass" config (not used for production)
|
||||
|-- gulpfile.js --> "Gulp" task config (not used for production )
|
||||
|-- package.json --> "Node.js" dependency config (not used for production)
|
||||
|
||||
@@ -34,23 +34,23 @@ class Schema extends Controller
|
||||
$this->f3->set('CACHE', false);
|
||||
|
||||
$dbs = array(
|
||||
/*'mysql' => new \DB\SQL(
|
||||
'mysql' => new \DB\SQL(
|
||||
'mysql:host=localhost;port=3306;dbname=fatfree', 'fatfree', ''
|
||||
),*/
|
||||
),
|
||||
'sqlite' => new \DB\SQL(
|
||||
'sqlite::memory:'
|
||||
// 'sqlite:db/sqlite.db'
|
||||
// 'sqlite:db/sqlite.db'
|
||||
),
|
||||
/*'pgsql' => new \DB\SQL(
|
||||
'pgsql' => new \DB\SQL(
|
||||
'pgsql:host=localhost;dbname=fatfree', 'fatfree', 'fatfree'
|
||||
),*/
|
||||
/*'sqlsrv2012' => new \DB\SQL(
|
||||
'sqlsrv:SERVER=LOCALHOST\SQLEXPRESS2012;Database=fatfree','fatfree', 'fatfree'
|
||||
),*/
|
||||
/*'sqlsrv2008' => new \DB\SQL(
|
||||
'sqlsrv:SERVER=LOCALHOST\SQLEXPRESS2008;Database=fatfree','fatfree', 'fatfree'
|
||||
)*/
|
||||
);
|
||||
),
|
||||
// 'sqlsrv2012' => new \DB\SQL(
|
||||
// 'sqlsrv:SERVER=LOCALHOST\SQLEXPRESS2012;Database=fatfree','fatfree', 'fatfree'
|
||||
// ),
|
||||
// 'sqlsrv2008' => new \DB\SQL(
|
||||
// 'sqlsrv:SERVER=LOCALHOST\SQLEXPRESS2008;Database=fatfree','fatfree', 'fatfree'
|
||||
// )
|
||||
);
|
||||
|
||||
$this->roundTime = microtime(TRUE) - \Base::instance()->get('timer');
|
||||
$this->tname = 'test_table';
|
||||
@@ -117,10 +117,27 @@ class Schema extends Controller
|
||||
$this->getTestDesc('adding column ['.$field.'], nullable')
|
||||
);
|
||||
}
|
||||
|
||||
$r1 = $table->getCols(true);
|
||||
foreach (array_keys($schema->dataTypes) as $index => $field) {
|
||||
if (isset($r1['column_'.$index])) {
|
||||
$datType=$schema->findQuery($schema->dataTypes[$field]);
|
||||
$compatible = $schema->isCompatible($field,$r1['column_'.$index]['type']);
|
||||
$this->test->expect(
|
||||
$compatible,
|
||||
$this->getTestDesc('reverse lookup compatible: '.
|
||||
($compatible?'YES':'NO').
|
||||
', '.$field.': ['.$datType.' > '.$r1['column_'.$index]['type'].']')
|
||||
);
|
||||
}
|
||||
}
|
||||
unset($r1);
|
||||
|
||||
|
||||
// adding some testing data
|
||||
$mapper = new \DB\SQL\Mapper($db, $this->tname);
|
||||
$mapper->column_5 = 123.456;
|
||||
$mapper->column_6 = 123456.789012;
|
||||
$mapper->column_7 = 'hello world';
|
||||
$mapper->save();
|
||||
$mapper->reset();
|
||||
@@ -130,11 +147,33 @@ class Schema extends Controller
|
||||
$result['column_7'] == 'hello world',
|
||||
$this->getTestDesc('mapping dummy data')
|
||||
);
|
||||
$this->test->expect(
|
||||
$result['column_5'] == 123.456,
|
||||
$this->getTestDesc('testing float value: '.$result['column_5'])
|
||||
);
|
||||
$this->test->expect(
|
||||
$result['column_6'] == 123456.789012,
|
||||
$this->getTestDesc('testing decimal value: '.$result['column_6'])
|
||||
);
|
||||
|
||||
|
||||
$mapper = new \DB\SQL\Mapper($db, $this->tname);
|
||||
$mapper->load();
|
||||
$num = $this->current_engine == 'sqlite' ? '123456789.012345' : '123456789012.345678';
|
||||
$mapper->column_6 = $num;
|
||||
$mapper->save();
|
||||
$mapper->reset();
|
||||
$result = $mapper->findone(array('column_7 = ?', 'hello world'))->cast();
|
||||
$this->test->expect(
|
||||
$result['column_6'] == $num,
|
||||
$this->getTestDesc('testing max decimal precision: '.$result['column_6'])
|
||||
);
|
||||
unset($mapper);
|
||||
|
||||
// default value text, not nullable
|
||||
$table->addColumn('text_default_not_null')
|
||||
->type($schema::DT_VARCHAR128)
|
||||
->nullable(false)->defaults('foo bar');
|
||||
->type($schema::DT_VARCHAR128)
|
||||
->nullable(false)->defaults('foo bar');
|
||||
$table->build();
|
||||
$r1 = $table->getCols(true);
|
||||
$this->test->expect(
|
||||
@@ -160,7 +199,7 @@ class Schema extends Controller
|
||||
|
||||
// default value numeric, not nullable
|
||||
$table->addColumn('int_default_not_null')
|
||||
->type($schema::DT_INT4)->nullable(false)->defaults(123);
|
||||
->type($schema::DT_INT4)->nullable(false)->defaults(123);
|
||||
$table->build();
|
||||
$r1 = $table->getCols(true);
|
||||
$this->test->expect(
|
||||
@@ -187,8 +226,8 @@ class Schema extends Controller
|
||||
|
||||
// default value text, nullable
|
||||
$table->addColumn('text_default_nullable')
|
||||
->type($schema::DT_VARCHAR128)
|
||||
->defaults('foo bar');
|
||||
->type($schema::DT_VARCHAR128)
|
||||
->defaults('foo bar');
|
||||
$table->build();
|
||||
$r1 = $table->getCols(true);
|
||||
$this->test->expect(
|
||||
@@ -253,9 +292,9 @@ class Schema extends Controller
|
||||
|
||||
// current timestamp
|
||||
$table->addColumn('stamp')
|
||||
->type($schema::DT_TIMESTAMP)
|
||||
->nullable(false)
|
||||
->defaults($schema::DF_CURRENT_TIMESTAMP);
|
||||
->type($schema::DT_TIMESTAMP)
|
||||
->nullable(false)
|
||||
->defaults($schema::DF_CURRENT_TIMESTAMP);
|
||||
$table->build();
|
||||
$r1 = $table->getCols(true);
|
||||
$this->test->expect(
|
||||
@@ -321,7 +360,7 @@ class Schema extends Controller
|
||||
$table->renameColumn('title123', 'text_default_not_null');
|
||||
$table->build();
|
||||
unset($result,$mapper);
|
||||
|
||||
|
||||
// remove column
|
||||
$table->dropColumn('column_1');
|
||||
$table->build();
|
||||
@@ -380,7 +419,7 @@ class Schema extends Controller
|
||||
// adding composite primary keys
|
||||
$table = $schema->createTable($this->tname);
|
||||
$table->addColumn('version')->type($schema::DT_INT4)
|
||||
->defaults(1)->nullable(false);
|
||||
->defaults(1)->nullable(false);
|
||||
$table->primary(array('id', 'version'));
|
||||
$table = $table->build();
|
||||
$r1 = $table->getCols(true);
|
||||
@@ -399,7 +438,7 @@ class Schema extends Controller
|
||||
$table->addColumn('title')->type($schema::DT_VARCHAR256);
|
||||
$table->addColumn('title2')->type($schema::DT_TEXT);
|
||||
$table->addColumn('title_notnull')
|
||||
->type($schema::DT_VARCHAR128)->nullable(false)->defaults("foo");
|
||||
->type($schema::DT_VARCHAR128)->nullable(false)->defaults("foo");
|
||||
$table->build();
|
||||
$r1 = $table->getCols(true);
|
||||
$this->test->expect(
|
||||
@@ -461,6 +500,18 @@ class Schema extends Controller
|
||||
$this->getTestDesc('adding items with composite primary-keys')
|
||||
);
|
||||
|
||||
$mapper = new \DB\SQL\Mapper($db, $this->tname);
|
||||
$mapper->load();
|
||||
$rec_count_cur = $mapper->loaded();
|
||||
$schema->truncateTable($this->tname);
|
||||
$mapper->reset();
|
||||
$mapper->load();
|
||||
$rec_count_new = $mapper->loaded();
|
||||
$this->test->expect(
|
||||
$rec_count_cur==3 && $rec_count_new == 0,
|
||||
$this->getTestDesc('truncate table')
|
||||
);
|
||||
|
||||
$schema->dropTable($this->tname);
|
||||
|
||||
// indexes
|
||||
@@ -520,11 +571,54 @@ class Schema extends Controller
|
||||
$table->updateColumn('bar',$schema::DT_TEXT);
|
||||
$table->build();
|
||||
$r1 = $table->getCols(true);
|
||||
$text = preg_match('/sybase|dblib|odbc|sqlsrv/',$this->current_engine)
|
||||
? 'nvarchar' : 'text';
|
||||
$this->test->expect(
|
||||
array_key_exists('bar', $r1) && $r1['bar']['type'] == 'text',
|
||||
array_key_exists('bar', $r1) && $r1['bar']['type'] == $text,
|
||||
$this->getTestDesc('update column')
|
||||
);
|
||||
|
||||
// update column
|
||||
$cols = $table->getCols(true);
|
||||
$bar = $cols['bar'];
|
||||
$col = new \DB\SQL\Column('bar',$table);
|
||||
$col->copyfrom($bar);
|
||||
$col->type_varchar(60);
|
||||
$col->defaults('great');
|
||||
$table->updateColumn('bar',$col);
|
||||
$table->build();
|
||||
$r1 = $table->getCols(true);
|
||||
$this->test->expect(
|
||||
array_key_exists('bar', $r1)
|
||||
&& $r1['bar']['default'] == 'great',
|
||||
$this->getTestDesc('update column and default')
|
||||
);
|
||||
|
||||
// update column default only
|
||||
$cols = $table->getCols(true);
|
||||
$bar = $cols['bar'];
|
||||
$col = new \DB\SQL\Column('bar',$table);
|
||||
$col->copyfrom($bar);
|
||||
$col->passThrough();
|
||||
$col->defaults('');
|
||||
$table->updateColumn('bar',$col);
|
||||
$table->build();
|
||||
$r1 = $table->getCols(true);
|
||||
$this->test->expect(
|
||||
array_key_exists('bar', $r1) && $r1['bar']['default'] == '',
|
||||
$this->getTestDesc('update default value')
|
||||
);
|
||||
|
||||
$col->nullable(false);
|
||||
$table->updateColumn('bar',$col);
|
||||
$table->build();
|
||||
$r1 = $table->getCols(true);
|
||||
$this->test->expect(
|
||||
array_key_exists('bar', $r1) && $r1['bar']['nullable'] == false,
|
||||
$this->getTestDesc('update nullable flag')
|
||||
);
|
||||
|
||||
|
||||
// create table with text not nullable column
|
||||
$table2 = $schema->createTable($this->tname.'_notnulltext');
|
||||
$table2->addColumn('desc')->type($schema::DT_TEXT)->nullable(false);
|
||||
@@ -538,7 +632,37 @@ class Schema extends Controller
|
||||
);
|
||||
$table2->drop();
|
||||
|
||||
|
||||
// boolean fields are actually bit/tinyint
|
||||
$schema->dropTable($this->tname.'_notnullbool');
|
||||
$table2 = $schema->createTable($this->tname.'_notnullbool');
|
||||
$table2->addColumn('active')->type($schema::DT_BOOL)->nullable(false);
|
||||
$table2 = $table2->build();
|
||||
$r1 = $schema->getTables();
|
||||
$r2 = $table2->getCols(true);
|
||||
$this->test->expect(
|
||||
in_array($this->tname.'_notnullbool', $r1) && array_key_exists('active', $r2)
|
||||
&& $r2['active']['nullable']==false,
|
||||
$this->getTestDesc('create new table with not nullable boolean column')
|
||||
);
|
||||
|
||||
$table2->addColumn('active2')->type($schema::DT_BOOL)->nullable(false)->defaults(0);
|
||||
$table2->addColumn('active3')->type($schema::DT_BOOL)->nullable(false)->defaults(1);
|
||||
$table2->build();
|
||||
$r1 = $schema->getTables();
|
||||
$r2 = $table2->getCols(true);
|
||||
$this->test->expect(
|
||||
in_array($this->tname.'_notnullbool', $r1)
|
||||
&& array_key_exists('active2', $r2) && $r2['active2']['nullable']==false &&
|
||||
((int)$r2['active2']['default']==0||$r2['active2']['default']=='false')
|
||||
&& array_key_exists('active3', $r2) && $r2['active3']['nullable']==false &&
|
||||
((int)$r2['active3']['default']==1||$r2['active3']['default']=='true'),
|
||||
$this->getTestDesc('add not nullable boolean columns with default to existing table')
|
||||
);
|
||||
|
||||
|
||||
$table2->drop();
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -21,6 +21,13 @@ TZ = UTC
|
||||
CACHE = folder=tmp/cache/
|
||||
;CACHE = redis=localhost:6379
|
||||
|
||||
; Cache backend used by Session handler.
|
||||
; default
|
||||
; -If CACHE is enabled (see above), the same location is used for Session data (e.g. fileCache, RedisDB)
|
||||
; mysql
|
||||
; - Session data get stored in your 'PathfinderDB' table 'sessions' (faster)
|
||||
SESSION_CACHE = mysql
|
||||
|
||||
; Callback functions ==============================================================================
|
||||
ONERROR = Controller\Controller->showError
|
||||
UNLOAD = Controller\Controller->unload
|
||||
|
||||
@@ -14,13 +14,13 @@ BASE =
|
||||
URL = {{@SCHEME}}://local.pathfinder
|
||||
; level of debug/error stack trace
|
||||
DEBUG = 3
|
||||
; main db
|
||||
DB_DNS = mysql:host=localhost;port=3306;dbname=
|
||||
DB_NAME = pathfinder
|
||||
DB_USER = root
|
||||
DB_PASS =
|
||||
; Pathfinder database
|
||||
DB_PF_DNS = mysql:host=localhost;port=3306;dbname=
|
||||
DB_PF_NAME = pathfinder
|
||||
DB_PF_USER = root
|
||||
DB_PF_PASS =
|
||||
|
||||
; EVE-Online CCP Database export
|
||||
; EVE-Online CCP database export
|
||||
DB_CCP_DNS = mysql:host=localhost;port=3306;dbname=
|
||||
DB_CCP_NAME = eve_citadel_min
|
||||
DB_CCP_USER = root
|
||||
@@ -60,11 +60,11 @@ BASE =
|
||||
URL = {{@SCHEME}}://www.pathfinder-w.space
|
||||
; level of debug/error stack trace
|
||||
DEBUG = 0
|
||||
; main db
|
||||
DB_DNS = mysql:host=localhost;port=3306;dbname=
|
||||
DB_NAME =
|
||||
DB_USER =
|
||||
DB_PASS =
|
||||
; Pathfinder database
|
||||
DB_PF_DNS = mysql:host=localhost;port=3306;dbname=
|
||||
DB_PF_NAME =
|
||||
DB_PF_USER =
|
||||
DB_PF_PASS =
|
||||
|
||||
; EVE-Online CCP Database export
|
||||
DB_CCP_DNS = mysql:host=localhost;port=3306;dbname=
|
||||
|
||||
@@ -18,8 +18,8 @@
|
||||
* https://github.com/ikkez/F3-Sugar/
|
||||
*
|
||||
* @package DB
|
||||
* @version 1.5.0-dev
|
||||
* @date 27.02.2017
|
||||
* @version 1.5.0
|
||||
* @date 30.06.2017
|
||||
* @since 24.04.2012
|
||||
*/
|
||||
|
||||
@@ -312,6 +312,7 @@ class Cortex extends Cursor {
|
||||
static public function setup($db=null, $table=null, $fields=null) {
|
||||
/** @var Cortex $self */
|
||||
$self = get_called_class();
|
||||
$self::$schema_cache=[];
|
||||
if (is_null($db) || is_null($table) || is_null($fields))
|
||||
$df = $self::resolveConfiguration();
|
||||
if (!is_object($db=(is_string($db=($db?:$df['db']))?\Base::instance()->get($db):$db)))
|
||||
@@ -836,9 +837,14 @@ class Cortex extends Cursor {
|
||||
array_unshift($filter,$crit);
|
||||
}
|
||||
}
|
||||
if ($options) {
|
||||
$options = $this->queryParser->prepareOptions($options,$this->dbsType);
|
||||
if ($count)
|
||||
unset($options['order']);
|
||||
}
|
||||
return ($count)
|
||||
? $this->mapper->count($filter,$options,$ttl)
|
||||
: $this->mapper->find($filter,$this->queryParser->prepareOptions($options,$this->dbsType),$ttl);
|
||||
: $this->mapper->find($filter,$options,$ttl);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1250,12 +1256,12 @@ class Cortex extends Cursor {
|
||||
$mmTable = $this->mmTable($relConf,$key);
|
||||
$filter = array($mmTable.'.'.$relConf['relField']
|
||||
.' = '.$this->table.'.'.$this->primary);
|
||||
$from=$mmTable;
|
||||
$from = $this->db->quotekey($mmTable);
|
||||
if (array_key_exists($key, $this->relFilter) &&
|
||||
!empty($this->relFilter[$key][0])) {
|
||||
$options=array();
|
||||
$from = $mmTable.' '.$this->_sql_left_join($key,$mmTable,
|
||||
$relConf['relPK'],$relConf['relTable']);
|
||||
$from = $this->db->quotekey($mmTable).' '.
|
||||
$this->_sql_left_join($key,$mmTable,$relConf['relPK'],$relConf['relTable']);
|
||||
$relFilter = $this->relFilter[$key];
|
||||
$this->_sql_mergeRelCondition($relFilter,$relConf['relTable'],
|
||||
$filter,$options);
|
||||
@@ -1266,8 +1272,8 @@ class Cortex extends Cursor {
|
||||
if (count($filter)>0)
|
||||
$this->preBinds=array_merge($this->preBinds,$filter);
|
||||
$this->mapper->set($alias,
|
||||
'(select count('.$this->db->quotekey($mmTable.'.'.$relConf['relField']).') from '.
|
||||
$this->db->quotekey($from).' where '.$crit.
|
||||
'(select count('.$this->db->quotekey($mmTable.'.'.$relConf['relField']).')'.
|
||||
' from '.$from.' where '.$crit.
|
||||
' group by '.$this->db->quotekey($mmTable.'.'.$relConf['relField']).')');
|
||||
if ($this->whitelist && !in_array($alias,$this->whitelist))
|
||||
$this->whitelist[] = $alias;
|
||||
@@ -2444,6 +2450,8 @@ class CortexQueryParser extends \Prefab {
|
||||
$child = array();
|
||||
for ($i = 0, $max = count($parts); $i < $max; $i++) {
|
||||
$part = $parts[$i];
|
||||
if (is_string($part))
|
||||
$part = trim($part);
|
||||
if ($part == '(') {
|
||||
// add sub-bracket to parse array
|
||||
if ($b_offset > 0)
|
||||
@@ -2460,14 +2468,15 @@ class CortexQueryParser extends \Prefab {
|
||||
else
|
||||
// add sub-bracket to parse array
|
||||
$child[] = $part;
|
||||
} // add to parse array
|
||||
elseif ($b_offset > 0)
|
||||
$child[] = $part;
|
||||
// condition type
|
||||
elseif (!is_array($part)) {
|
||||
if (strtoupper(trim($part)) == 'AND')
|
||||
}
|
||||
elseif ($b_offset > 0) {
|
||||
// add to parse array
|
||||
$child[]=$part;
|
||||
// condition type
|
||||
} elseif (!is_array($part)) {
|
||||
if (strtoupper($part) == 'AND')
|
||||
$add = true;
|
||||
elseif (strtoupper(trim($part)) == 'OR')
|
||||
elseif (strtoupper($part) == 'OR')
|
||||
$or = true;
|
||||
} else // skip
|
||||
$ncond[] = $part;
|
||||
@@ -2589,7 +2598,7 @@ 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;
|
||||
|
||||
@@ -32,77 +32,77 @@ class Schema extends DB_Utils {
|
||||
public
|
||||
$dataTypes = array(
|
||||
'BOOLEAN' => array('mysql' => 'tinyint(1)',
|
||||
'sqlite2?|pgsql' => 'BOOLEAN',
|
||||
'mssql|sybase|dblib|odbc|sqlsrv' => 'bit',
|
||||
'ibm' => 'numeric(1,0)',
|
||||
'sqlite2?|pgsql' => 'BOOLEAN',
|
||||
'mssql|sybase|dblib|odbc|sqlsrv' => 'bit',
|
||||
'ibm' => 'numeric(1,0)',
|
||||
),
|
||||
'INT1' => array('mysql' => 'tinyint(4)',
|
||||
'sqlite2?' => 'integer(4)',
|
||||
'mssql|sybase|dblib|odbc|sqlsrv' => 'tinyint',
|
||||
'pgsql|ibm' => 'smallint',
|
||||
'sqlite2?' => 'integer(4)',
|
||||
'mssql|sybase|dblib|odbc|sqlsrv' => 'tinyint',
|
||||
'pgsql|ibm' => 'smallint',
|
||||
),
|
||||
'INT2' => array('mysql' => 'smallint(6)',
|
||||
'sqlite2?' => 'integer(6)',
|
||||
'pgsql|ibm|mssql|sybase|dblib|odbc|sqlsrv' => 'smallint',
|
||||
'sqlite2?' => 'integer(6)',
|
||||
'pgsql|ibm|mssql|sybase|dblib|odbc|sqlsrv' => 'smallint',
|
||||
),
|
||||
'INT4' => array('sqlite2?' => 'integer(11)',
|
||||
'pgsql|imb' => 'integer',
|
||||
'mysql' => 'int(11)',
|
||||
'mssql|dblib|sybase|odbc|sqlsrv' => 'int',
|
||||
'pgsql|imb' => 'integer',
|
||||
'mysql' => 'int(11)',
|
||||
'mssql|dblib|sybase|odbc|sqlsrv' => 'int',
|
||||
),
|
||||
'INT8' => array('sqlite2?' => 'integer(20)',
|
||||
'pgsql|mssql|sybase|dblib|odbc|sqlsrv|imb' => 'bigint',
|
||||
'mysql' => 'bigint(20)',
|
||||
'pgsql|mssql|sybase|dblib|odbc|sqlsrv|imb' => 'bigint',
|
||||
'mysql' => 'bigint(20)',
|
||||
),
|
||||
'FLOAT' => array('mysql|sqlite2?' => 'FLOAT',
|
||||
'pgsql' => 'double precision',
|
||||
'mssql|sybase|dblib|odbc|sqlsrv' => 'float',
|
||||
'imb' => 'decfloat'
|
||||
'pgsql' => 'double precision',
|
||||
'mssql|sybase|dblib|odbc|sqlsrv' => 'float',
|
||||
'imb' => 'decfloat'
|
||||
),
|
||||
'DOUBLE' => array('mysql|ibm' => 'decimal(18,6)',
|
||||
'sqlite2?' => 'decimal(15,6)', // max 15-digit on sqlite
|
||||
'pgsql' => 'numeric(18,6)',
|
||||
'mssql|dblib|sybase|odbc|sqlsrv' => 'decimal(18,6)',
|
||||
'sqlite2?' => 'decimal(15,6)', // max 15-digit on sqlite
|
||||
'pgsql' => 'numeric(18,6)',
|
||||
'mssql|dblib|sybase|odbc|sqlsrv' => 'decimal(18,6)',
|
||||
),
|
||||
'VARCHAR128' => array('mysql|sqlite2?|ibm|mssql|sybase|dblib|odbc|sqlsrv' => 'varchar(128)',
|
||||
'pgsql' => 'character varying(128)',
|
||||
'pgsql' => 'character varying(128)',
|
||||
),
|
||||
'VARCHAR256' => array('mysql|sqlite2?|ibm|mssql|sybase|dblib|odbc|sqlsrv' => 'varchar(255)',
|
||||
'pgsql' => 'character varying(255)',
|
||||
'pgsql' => 'character varying(255)',
|
||||
),
|
||||
'VARCHAR512' => array('mysql|sqlite2?|ibm|mssql|sybase|dblib|odbc|sqlsrv' => 'varchar(512)',
|
||||
'pgsql' => 'character varying(512)',
|
||||
'pgsql' => 'character varying(512)',
|
||||
),
|
||||
'TEXT' => array('mysql|sqlite2?|pgsql|mssql' => 'text',
|
||||
'sybase|dblib|odbc|sqlsrv' => 'nvarchar(max)',
|
||||
'ibm' => 'BLOB SUB_TYPE TEXT',
|
||||
'sybase|dblib|odbc|sqlsrv' => 'nvarchar(max)',
|
||||
'ibm' => 'BLOB SUB_TYPE TEXT',
|
||||
),
|
||||
'LONGTEXT' => array('mysql' => 'LONGTEXT',
|
||||
'sqlite2?|pgsql|mssql' => 'text',
|
||||
'sybase|dblib|odbc|sqlsrv' => 'nvarchar(max)',
|
||||
'ibm' => 'CLOB(2000000000)',
|
||||
'sqlite2?|pgsql|mssql' => 'text',
|
||||
'sybase|dblib|odbc|sqlsrv' => 'nvarchar(max)',
|
||||
'ibm' => 'CLOB(2000000000)',
|
||||
),
|
||||
'DATE' => array('mysql|sqlite2?|pgsql|mssql|sybase|dblib|odbc|sqlsrv|ibm' => 'date',
|
||||
),
|
||||
'DATETIME' => array('pgsql' => 'timestamp without time zone',
|
||||
'mysql|sqlite2?|mssql|sybase|dblib|odbc|sqlsrv' => 'datetime',
|
||||
'ibm' => 'timestamp',
|
||||
'mysql|sqlite2?|mssql|sybase|dblib|odbc|sqlsrv' => 'datetime',
|
||||
'ibm' => 'timestamp',
|
||||
),
|
||||
'TIMESTAMP' => array('mysql|ibm' => 'timestamp',
|
||||
'pgsql|odbc' => 'timestamp without time zone',
|
||||
'sqlite2?|mssql|sybase|dblib|sqlsrv'=>'DATETIME',
|
||||
'pgsql|odbc' => 'timestamp without time zone',
|
||||
'sqlite2?|mssql|sybase|dblib|sqlsrv'=>'DATETIME',
|
||||
),
|
||||
'BLOB' => array('mysql|odbc|sqlite2?|ibm' => 'blob',
|
||||
'pgsql' => 'bytea',
|
||||
'mssql|sybase|dblib' => 'image',
|
||||
'sqlsrv' => 'varbinary(max)',
|
||||
'pgsql' => 'bytea',
|
||||
'mssql|sybase|dblib' => 'image',
|
||||
'sqlsrv' => 'varbinary(max)',
|
||||
),
|
||||
),
|
||||
$defaultTypes = array(
|
||||
'CUR_STAMP' => array('mysql' => 'CURRENT_TIMESTAMP',
|
||||
'mssql|sybase|dblib|odbc|sqlsrv' => 'getdate()',
|
||||
'pgsql' => 'LOCALTIMESTAMP(0)',
|
||||
'sqlite2?' => "(datetime('now','localtime'))",
|
||||
'mssql|sybase|dblib|odbc|sqlsrv' => 'getdate()',
|
||||
'pgsql' => 'LOCALTIMESTAMP(0)',
|
||||
'sqlite2?' => "(datetime('now','localtime'))",
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
@@ -17,30 +17,30 @@ class AccessController extends Controller {
|
||||
/**
|
||||
* event handler
|
||||
* @param \Base $f3
|
||||
* @param array $params
|
||||
* @param $params
|
||||
* @return bool
|
||||
*/
|
||||
function beforeroute(\Base $f3, $params) {
|
||||
parent::beforeroute($f3, $params);
|
||||
function beforeroute(\Base $f3, $params): bool {
|
||||
if($return = parent::beforeroute($f3, $params)){
|
||||
// Any route/endpoint of a child class of this one,
|
||||
// requires a valid logged in user!
|
||||
if( !$this->isLoggedIn($f3) ){
|
||||
// no character found or login timer expired
|
||||
$this->logoutCharacter();
|
||||
|
||||
// Any route/endpoint of a child class of this one,
|
||||
// requires a valid logged in user!
|
||||
$loginCheck = $this->isLoggedIn($f3);
|
||||
|
||||
if( !$loginCheck ){
|
||||
// no user found or login timer expired
|
||||
$this->logout($f3);
|
||||
|
||||
if( $f3->get('AJAX') ){
|
||||
// unauthorized request
|
||||
$f3->status(403);
|
||||
}else{
|
||||
// redirect to landing page
|
||||
$f3->reroute(['login']);
|
||||
if($f3->get('AJAX')){
|
||||
// unauthorized request
|
||||
$f3->status(403);
|
||||
}else{
|
||||
// redirect to landing page
|
||||
$f3->reroute(['login']);
|
||||
}
|
||||
// skip route handler and afterroute()
|
||||
$return = false;
|
||||
}
|
||||
|
||||
// die() triggers unload() function
|
||||
die();
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -48,7 +48,7 @@ class AccessController extends Controller {
|
||||
* @param \Base $f3
|
||||
* @return bool
|
||||
*/
|
||||
protected function isLoggedIn(\Base $f3){
|
||||
protected function isLoggedIn(\Base $f3): bool {
|
||||
$loginCheck = false;
|
||||
if( $character = $this->getCharacter() ){
|
||||
if($this->checkLogTimer($f3, $character)){
|
||||
@@ -84,7 +84,7 @@ class AccessController extends Controller {
|
||||
$minutes += $timeDiff->h * 60;
|
||||
$minutes += $timeDiff->i;
|
||||
|
||||
if($minutes <= $f3->get('PATHFINDER.TIMER.LOGGED')){
|
||||
if($minutes <= Config::getPathfinderData('timer.logged')){
|
||||
$loginCheck = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,9 +32,11 @@ class Admin extends Controller{
|
||||
* event handler for all "views"
|
||||
* some global template variables are set in here
|
||||
* @param \Base $f3
|
||||
* @param $params
|
||||
* @return bool
|
||||
*/
|
||||
function beforeroute(\Base $f3, $params) {
|
||||
parent::beforeroute($f3, $params);
|
||||
function beforeroute(\Base $f3, $params): bool {
|
||||
$return = parent::beforeroute($f3, $params);
|
||||
|
||||
$f3->set('tplPage', 'login');
|
||||
|
||||
@@ -54,6 +56,8 @@ class Admin extends Controller{
|
||||
|
||||
// body element class
|
||||
$f3->set('tplBodyClass', 'pf-landing');
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -291,7 +295,9 @@ class Admin extends Controller{
|
||||
}
|
||||
|
||||
foreach($corporations as $corporation){
|
||||
$data->corpMembers[$corporation->name] = $corporation->getCharacters();
|
||||
if($characters = $corporation->getCharacters()){
|
||||
$data->corpMembers[$corporation->name] = $corporation->getCharacters();
|
||||
}
|
||||
}
|
||||
|
||||
// sort corporation from current user first
|
||||
|
||||
@@ -12,16 +12,6 @@ use Model;
|
||||
|
||||
class Access extends Controller\AccessController {
|
||||
|
||||
/**
|
||||
* event handler
|
||||
* @param \Base $f3
|
||||
* @param array $params
|
||||
*/
|
||||
function beforeroute(\Base $f3, $params) {
|
||||
// set header for all routes
|
||||
header('Content-type: application/json');
|
||||
parent::beforeroute($f3, $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* search character/corporation or alliance by name
|
||||
|
||||
@@ -12,15 +12,6 @@ use Model;
|
||||
|
||||
class Connection extends Controller\AccessController {
|
||||
|
||||
/**
|
||||
* @param \Base $f3
|
||||
* @param array $params
|
||||
*/
|
||||
function beforeroute(\Base $f3, $params) {
|
||||
// set header for all routes
|
||||
header('Content-type: application/json');
|
||||
parent::beforeroute($f3, $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* save a new connection or updates an existing (drag/drop) between two systems
|
||||
@@ -29,7 +20,10 @@ class Connection extends Controller\AccessController {
|
||||
*/
|
||||
public function save(\Base $f3){
|
||||
$postData = (array)$f3->get('POST');
|
||||
$newConnectionData = [];
|
||||
|
||||
$return = (object) [];
|
||||
$return->error = [];
|
||||
$return->connectionData = (object) [];
|
||||
|
||||
if(
|
||||
isset($postData['connectionData']) &&
|
||||
@@ -75,27 +69,23 @@ class Connection extends Controller\AccessController {
|
||||
$connectionData['scope'] = 'wh';
|
||||
$connectionData['type'] = ['wh_fresh'];
|
||||
}
|
||||
|
||||
$connectionData['mapId'] = $map;
|
||||
|
||||
// "updated" should not be set by client e.g. after manual drag&drop
|
||||
unset($connectionData['updated']);
|
||||
|
||||
$connection->setData($connectionData);
|
||||
|
||||
if( $connection->isValid() ){
|
||||
$connection->save();
|
||||
|
||||
$newConnectionData = $connection->getData();
|
||||
if($connection->save($activeCharacter)){
|
||||
$return->connectionData = $connection->getData();
|
||||
|
||||
// broadcast map changes
|
||||
$this->broadcastMapData($connection->mapId);
|
||||
}else{
|
||||
$return->error = $connection->getErrors();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
echo json_encode($newConnectionData);
|
||||
echo json_encode($return);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
*/
|
||||
|
||||
namespace Controller\Api;
|
||||
use Model;
|
||||
use lib\Config;
|
||||
use Controller;
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ class GitHub extends Controller\Controller {
|
||||
$releaseCount = 4;
|
||||
|
||||
if( !$f3->exists($cacheKey) ){
|
||||
$apiPath = $this->getF3()->get('PATHFINDER.API.GIT_HUB') . '/repos/exodus4d/pathfinder/releases';
|
||||
$apiPath = Config::getPathfinderData('api.git_hub') . '/repos/exodus4d/pathfinder/releases';
|
||||
|
||||
// build request URL
|
||||
$options = $this->getRequestOptions();
|
||||
|
||||
@@ -8,9 +8,11 @@
|
||||
|
||||
namespace Controller\Api;
|
||||
use Controller;
|
||||
use data\file\FileHandler;
|
||||
use lib\Config;
|
||||
use lib\Socket;
|
||||
use Model;
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* Map controller
|
||||
@@ -23,24 +25,15 @@ class Map extends Controller\AccessController {
|
||||
const CACHE_KEY_INIT = 'CACHED_INIT';
|
||||
const CACHE_KEY_MAP_DATA = 'CACHED.MAP_DATA.%s';
|
||||
const CACHE_KEY_USER_DATA = 'CACHED.USER_DATA.%s_%s';
|
||||
const CACHE_KEY_HISTORY = 'CACHED_MAP_HISTORY_%s';
|
||||
|
||||
/**
|
||||
* event handler
|
||||
* @param \Base $f3
|
||||
* @param array $params
|
||||
*/
|
||||
function beforeroute(\Base $f3, $params) {
|
||||
// set header for all routes
|
||||
header('Content-type: application/json');
|
||||
parent::beforeroute($f3, $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* get map data cache key
|
||||
* @param Model\CharacterModel $character
|
||||
* @return string
|
||||
*/
|
||||
protected function getMapDataCacheKey(Model\CharacterModel $character){
|
||||
protected function getMapDataCacheKey(Model\CharacterModel $character): string {
|
||||
return sprintf(self::CACHE_KEY_MAP_DATA, 'CHAR_' . $character->_id);
|
||||
}
|
||||
|
||||
@@ -62,10 +55,19 @@ class Map extends Controller\AccessController {
|
||||
* @param int $systemId
|
||||
* @return string
|
||||
*/
|
||||
protected function getUserDataCacheKey($mapId, $systemId = 0){
|
||||
protected function getUserDataCacheKey($mapId, $systemId = 0): string {
|
||||
return sprintf(self::CACHE_KEY_USER_DATA, 'MAP_' . $mapId, 'SYS_' . $systemId);
|
||||
}
|
||||
|
||||
/**
|
||||
* get log history data cache key
|
||||
* @param int $mapId
|
||||
* @return string
|
||||
*/
|
||||
protected function getHistoryDataCacheKey(int $mapId): string {
|
||||
return sprintf(self::CACHE_KEY_HISTORY, 'MAP_' . $mapId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all required static config data for program initialization
|
||||
* @param \Base $f3
|
||||
@@ -79,10 +81,10 @@ class Map extends Controller\AccessController {
|
||||
$return = (object) [];
|
||||
$return->error = [];
|
||||
|
||||
// static program data ----------------------------------------------------------------------------------------
|
||||
$return->timer = $f3->get('PATHFINDER.TIMER');
|
||||
// static program data ------------------------------------------------------------------------------------
|
||||
$return->timer = Config::getPathfinderData('timer');
|
||||
|
||||
// get all available map types --------------------------------------------------------------------------------
|
||||
// get all available map types ----------------------------------------------------------------------------
|
||||
$mapType = Model\BasicModel::getNew('MapTypeModel');
|
||||
$rows = $mapType->find('active = 1', null, $expireTimeSQL);
|
||||
|
||||
@@ -103,7 +105,7 @@ class Map extends Controller\AccessController {
|
||||
}
|
||||
$return->mapTypes = $mapTypeData;
|
||||
|
||||
// get all available map scopes -------------------------------------------------------------------------------
|
||||
// get all available map scopes ---------------------------------------------------------------------------
|
||||
$mapScope = Model\BasicModel::getNew('MapScopeModel');
|
||||
$rows = $mapScope->find('active = 1', null, $expireTimeSQL);
|
||||
$mapScopeData = [];
|
||||
@@ -116,7 +118,7 @@ class Map extends Controller\AccessController {
|
||||
}
|
||||
$return->mapScopes = $mapScopeData;
|
||||
|
||||
// get all available system status ----------------------------------------------------------------------------
|
||||
// get all available system status ------------------------------------------------------------------------
|
||||
$systemStatus = Model\BasicModel::getNew('SystemStatusModel');
|
||||
$rows = $systemStatus->find('active = 1', null, $expireTimeSQL);
|
||||
$systemScopeData = [];
|
||||
@@ -130,7 +132,7 @@ class Map extends Controller\AccessController {
|
||||
}
|
||||
$return->systemStatus = $systemScopeData;
|
||||
|
||||
// get all available system types -----------------------------------------------------------------------------
|
||||
// get all available system types -------------------------------------------------------------------------
|
||||
$systemType = Model\BasicModel::getNew('SystemTypeModel');
|
||||
$rows = $systemType->find('active = 1', null, $expireTimeSQL);
|
||||
$systemTypeData = [];
|
||||
@@ -143,7 +145,7 @@ class Map extends Controller\AccessController {
|
||||
}
|
||||
$return->systemType = $systemTypeData;
|
||||
|
||||
// get available connection scopes ----------------------------------------------------------------------------
|
||||
// get available connection scopes ------------------------------------------------------------------------
|
||||
$connectionScope = Model\BasicModel::getNew('ConnectionScopeModel');
|
||||
$rows = $connectionScope->find('active = 1', null, $expireTimeSQL);
|
||||
$connectionScopeData = [];
|
||||
@@ -157,7 +159,7 @@ class Map extends Controller\AccessController {
|
||||
}
|
||||
$return->connectionScopes = $connectionScopeData;
|
||||
|
||||
// get available character status -----------------------------------------------------------------------------
|
||||
// get available character status -------------------------------------------------------------------------
|
||||
$characterStatus = Model\BasicModel::getNew('CharacterStatusModel');
|
||||
$rows = $characterStatus->find('active = 1', null, $expireTimeSQL);
|
||||
$characterStatusData = [];
|
||||
@@ -171,21 +173,27 @@ class Map extends Controller\AccessController {
|
||||
}
|
||||
$return->characterStatus = $characterStatusData;
|
||||
|
||||
// route search config ----------------------------------------------------------------------------------------
|
||||
// route search config ------------------------------------------------------------------------------------
|
||||
$return->routeSearch = [
|
||||
'defaultCount' => $this->getF3()->get('PATHFINDER.ROUTE.SEARCH_DEFAULT_COUNT'),
|
||||
'maxDefaultCount' => $this->getF3()->get('PATHFINDER.ROUTE.MAX_Default_COUNT'),
|
||||
'limit' => $this->getF3()->get('PATHFINDER.ROUTE.LIMIT'),
|
||||
'defaultCount' => Config::getPathfinderData('route.search_default_count'),
|
||||
'maxDefaultCount' => Config::getPathfinderData('route.max_default_count'),
|
||||
'limit' => Config::getPathfinderData('route.limit')
|
||||
];
|
||||
|
||||
// get program routes -----------------------------------------------------------------------------------------
|
||||
// get program routes -------------------------------------------------------------------------------------
|
||||
$return->routes = [
|
||||
'ssoLogin' => $this->getF3()->alias( 'sso', ['action' => 'requestAuthorization'] )
|
||||
];
|
||||
|
||||
// get notification status ------------------------------------------------------------------------------------
|
||||
$return->notificationStatus = [
|
||||
'rallySet' => (bool)Config::getNotificationMail('RALLY_SET')
|
||||
// get third party APIs -----------------------------------------------------------------------------------
|
||||
$return->url = [
|
||||
'ccpImageServer' => Config::getPathfinderData('api.ccp_image_server'),
|
||||
'zKillboard' => Config::getPathfinderData('api.z_killboard')
|
||||
];
|
||||
|
||||
// Slack integration status -------------------------------------------------------------------------------
|
||||
$return->slack = [
|
||||
'status' => (bool)Config::getPathfinderData('slack.status')
|
||||
];
|
||||
|
||||
$f3->set(self::CACHE_KEY_INIT, $return, $expireTimeCache );
|
||||
@@ -195,7 +203,7 @@ class Map extends Controller\AccessController {
|
||||
|
||||
// program mode (e.g. "maintenance") --------------------------------------------------------------------------
|
||||
$return->programMode = [
|
||||
'maintenance' => $this->getF3()->get('PATHFINDER.LOGIN.MODE_MAINTENANCE')
|
||||
'maintenance' => Config::getPathfinderData('login.mode_maintenance')
|
||||
];
|
||||
|
||||
// get SSO error messages that should be shown immediately ----------------------------------------------------
|
||||
@@ -263,17 +271,12 @@ class Map extends Controller\AccessController {
|
||||
isset($mapData['data']['systems']) &&
|
||||
isset($mapData['data']['connections'])
|
||||
){
|
||||
if(isset($mapData['config']['id'])){
|
||||
unset($mapData['config']['id']);
|
||||
}
|
||||
|
||||
|
||||
$systemCount = count($mapData['data']['systems']);
|
||||
if( $systemCount <= $defaultConfig['max_systems']){
|
||||
|
||||
$map->setData($mapData['config']);
|
||||
$map->typeId = (int)$importData['typeId'];
|
||||
$map->save();
|
||||
$map->save($activeCharacter);
|
||||
|
||||
// new system IDs will be generated
|
||||
// therefore we need to temp store a mapping between IDs
|
||||
@@ -282,13 +285,10 @@ class Map extends Controller\AccessController {
|
||||
foreach($mapData['data']['systems'] as $systemData){
|
||||
if(isset($systemData['id'])){
|
||||
$oldId = (int)$systemData['id'];
|
||||
unset($systemData['id']);
|
||||
|
||||
$system->setData($systemData);
|
||||
$system->mapId = $map;
|
||||
$system->createdCharacterId = $activeCharacter;
|
||||
$system->updatedCharacterId = $activeCharacter;
|
||||
$system->save();
|
||||
$system->save($activeCharacter);
|
||||
|
||||
$tempSystemIdMapping[$oldId] = $system->id;
|
||||
$system->reset();
|
||||
@@ -301,15 +301,11 @@ class Map extends Controller\AccessController {
|
||||
isset( $tempSystemIdMapping[$connectionData['source']] ) &&
|
||||
isset( $tempSystemIdMapping[$connectionData['target']] )
|
||||
){
|
||||
if(isset($connectionData['id'])){
|
||||
unset($connectionData['id']);
|
||||
}
|
||||
|
||||
$connection->setData($connectionData);
|
||||
$connection->mapId = $map;
|
||||
$connection->source = $tempSystemIdMapping[$connectionData['source']];
|
||||
$connection->target = $tempSystemIdMapping[$connectionData['target']];
|
||||
$connection->save();
|
||||
$connection->save($activeCharacter);
|
||||
|
||||
$connection->reset();
|
||||
}
|
||||
@@ -391,150 +387,161 @@ class Map extends Controller\AccessController {
|
||||
$map->dry() ||
|
||||
$map->hasAccess($activeCharacter)
|
||||
){
|
||||
// new map
|
||||
$map->setData($formData);
|
||||
$map = $map->save();
|
||||
try{
|
||||
// new map
|
||||
$map->setData($formData);
|
||||
$map = $map->save($activeCharacter);
|
||||
|
||||
// save global map access. Depends on map "type"
|
||||
if($map->isPrivate()){
|
||||
$mapDefaultConf = Config::getMapsDefaultConfig();
|
||||
|
||||
// share map between characters -> set access
|
||||
if(isset($formData['mapCharacters'])){
|
||||
// remove character corporation (re-add later)
|
||||
$accessCharacters = array_diff($formData['mapCharacters'], [$activeCharacter->_id]);
|
||||
// save global map access. Depends on map "type"
|
||||
if($map->isPrivate()){
|
||||
|
||||
// avoid abuse -> respect share limits
|
||||
$maxShared = max($f3->get('PATHFINDER.MAP.PRIVATE.MAX_SHARED') - 1, 0);
|
||||
$accessCharacters = array_slice($accessCharacters, 0, $maxShared);
|
||||
|
||||
// clear map access. In case something has removed from access list
|
||||
$map->clearAccess();
|
||||
|
||||
if($accessCharacters){
|
||||
/**
|
||||
* @var $tempCharacter Model\CharacterModel
|
||||
*/
|
||||
$tempCharacter = Model\BasicModel::getNew('CharacterModel');
|
||||
|
||||
foreach($accessCharacters as $characterId){
|
||||
$tempCharacter->getById( (int)$characterId );
|
||||
|
||||
if(
|
||||
!$tempCharacter->dry() &&
|
||||
$tempCharacter->shared == 1 // check if map shared is enabled
|
||||
){
|
||||
$map->setAccess($tempCharacter);
|
||||
}
|
||||
|
||||
$tempCharacter->reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// the current character itself should always have access
|
||||
// just in case he removed himself :)
|
||||
$map->setAccess($activeCharacter);
|
||||
}elseif($map->isCorporation()){
|
||||
$corporation = $activeCharacter->getCorporation();
|
||||
|
||||
if($corporation){
|
||||
// the current user has to have a corporation when
|
||||
// working on corporation maps!
|
||||
|
||||
// share map between corporations -> set access
|
||||
if(isset($formData['mapCorporations'])){
|
||||
// share map between characters -> set access
|
||||
if(isset($formData['mapCharacters'])){
|
||||
// remove character corporation (re-add later)
|
||||
$accessCorporations = array_diff($formData['mapCorporations'], [$corporation->_id]);
|
||||
$accessCharacters = array_diff($formData['mapCharacters'], [$activeCharacter->_id]);
|
||||
|
||||
// avoid abuse -> respect share limits
|
||||
$maxShared = max($f3->get('PATHFINDER.MAP.CORPORATION.MAX_SHARED') - 1, 0);
|
||||
$accessCorporations = array_slice($accessCorporations, 0, $maxShared);
|
||||
$maxShared = max($mapDefaultConf['private']['max_shared'] - 1, 0);
|
||||
$accessCharacters = array_slice($accessCharacters, 0, $maxShared);
|
||||
|
||||
// clear map access. In case something has removed from access list
|
||||
$map->clearAccess();
|
||||
|
||||
if($accessCorporations){
|
||||
if($accessCharacters){
|
||||
/**
|
||||
* @var $tempCorporation Model\CorporationModel
|
||||
* @var $tempCharacter Model\CharacterModel
|
||||
*/
|
||||
$tempCorporation = Model\BasicModel::getNew('CorporationModel');
|
||||
$tempCharacter = Model\BasicModel::getNew('CharacterModel');
|
||||
|
||||
foreach($accessCorporations as $corporationId){
|
||||
$tempCorporation->getById( (int)$corporationId );
|
||||
foreach($accessCharacters as $characterId){
|
||||
$tempCharacter->getById( (int)$characterId );
|
||||
|
||||
if(
|
||||
!$tempCorporation->dry() &&
|
||||
$tempCorporation->shared == 1 // check if map shared is enabled
|
||||
!$tempCharacter->dry() &&
|
||||
$tempCharacter->shared == 1 // check if map shared is enabled
|
||||
){
|
||||
$map->setAccess($tempCorporation);
|
||||
$map->setAccess($tempCharacter);
|
||||
}
|
||||
|
||||
$tempCorporation->reset();
|
||||
$tempCharacter->reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// the corporation of the current user should always have access
|
||||
$map->setAccess($corporation);
|
||||
}
|
||||
}elseif($map->isAlliance()){
|
||||
$alliance = $activeCharacter->getAlliance();
|
||||
// the current character itself should always have access
|
||||
// just in case he removed himself :)
|
||||
$map->setAccess($activeCharacter);
|
||||
}elseif($map->isCorporation()){
|
||||
$corporation = $activeCharacter->getCorporation();
|
||||
|
||||
if($alliance){
|
||||
// the current user has to have a alliance when
|
||||
// working on alliance maps!
|
||||
if($corporation){
|
||||
// the current user has to have a corporation when
|
||||
// working on corporation maps!
|
||||
|
||||
// share map between alliances -> set access
|
||||
if(isset($formData['mapAlliances'])){
|
||||
// remove character alliance (re-add later)
|
||||
$accessAlliances = array_diff($formData['mapAlliances'], [$alliance->_id]);
|
||||
// share map between corporations -> set access
|
||||
if(isset($formData['mapCorporations'])){
|
||||
// remove character corporation (re-add later)
|
||||
$accessCorporations = array_diff($formData['mapCorporations'], [$corporation->_id]);
|
||||
|
||||
// avoid abuse -> respect share limits
|
||||
$maxShared = max($f3->get('PATHFINDER.MAP.ALLIANCE.MAX_SHARED') - 1, 0);
|
||||
$accessAlliances = array_slice($accessAlliances, 0, $maxShared);
|
||||
// avoid abuse -> respect share limits
|
||||
$maxShared = max($mapDefaultConf['corporation']['max_shared'] - 1, 0);
|
||||
$accessCorporations = array_slice($accessCorporations, 0, $maxShared);
|
||||
|
||||
// clear map access. In case something has removed from access list
|
||||
$map->clearAccess();
|
||||
// clear map access. In case something has removed from access list
|
||||
$map->clearAccess();
|
||||
|
||||
if($accessAlliances){
|
||||
/**
|
||||
* @var $tempAlliance Model\AllianceModel
|
||||
*/
|
||||
$tempAlliance = Model\BasicModel::getNew('AllianceModel');
|
||||
if($accessCorporations){
|
||||
/**
|
||||
* @var $tempCorporation Model\CorporationModel
|
||||
*/
|
||||
$tempCorporation = Model\BasicModel::getNew('CorporationModel');
|
||||
|
||||
foreach($accessAlliances as $allianceId){
|
||||
$tempAlliance->getById( (int)$allianceId );
|
||||
foreach($accessCorporations as $corporationId){
|
||||
$tempCorporation->getById( (int)$corporationId );
|
||||
|
||||
if(
|
||||
!$tempAlliance->dry() &&
|
||||
$tempAlliance->shared == 1 // check if map shared is enabled
|
||||
){
|
||||
$map->setAccess($tempAlliance);
|
||||
if(
|
||||
!$tempCorporation->dry() &&
|
||||
$tempCorporation->shared == 1 // check if map shared is enabled
|
||||
){
|
||||
$map->setAccess($tempCorporation);
|
||||
}
|
||||
|
||||
$tempCorporation->reset();
|
||||
}
|
||||
|
||||
$tempAlliance->reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// the alliance of the current user should always have access
|
||||
$map->setAccess($alliance);
|
||||
// the corporation of the current user should always have access
|
||||
$map->setAccess($corporation);
|
||||
}
|
||||
}elseif($map->isAlliance()){
|
||||
$alliance = $activeCharacter->getAlliance();
|
||||
|
||||
if($alliance){
|
||||
// the current user has to have a alliance when
|
||||
// working on alliance maps!
|
||||
|
||||
// share map between alliances -> set access
|
||||
if(isset($formData['mapAlliances'])){
|
||||
// remove character alliance (re-add later)
|
||||
$accessAlliances = array_diff($formData['mapAlliances'], [$alliance->_id]);
|
||||
|
||||
// avoid abuse -> respect share limits
|
||||
$maxShared = max($mapDefaultConf['alliance']['max_shared'] - 1, 0);
|
||||
$accessAlliances = array_slice($accessAlliances, 0, $maxShared);
|
||||
|
||||
// clear map access. In case something has removed from access list
|
||||
$map->clearAccess();
|
||||
|
||||
if($accessAlliances){
|
||||
/**
|
||||
* @var $tempAlliance Model\AllianceModel
|
||||
*/
|
||||
$tempAlliance = Model\BasicModel::getNew('AllianceModel');
|
||||
|
||||
foreach($accessAlliances as $allianceId){
|
||||
$tempAlliance->getById( (int)$allianceId );
|
||||
|
||||
if(
|
||||
!$tempAlliance->dry() &&
|
||||
$tempAlliance->shared == 1 // check if map shared is enabled
|
||||
){
|
||||
$map->setAccess($tempAlliance);
|
||||
}
|
||||
|
||||
$tempAlliance->reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// the alliance of the current user should always have access
|
||||
$map->setAccess($alliance);
|
||||
}
|
||||
}
|
||||
// reload the same map model (refresh)
|
||||
// this makes sure all data is up2date
|
||||
$map->getById( $map->_id, 0 );
|
||||
|
||||
|
||||
$charactersData = $map->getCharactersData();
|
||||
$characterIds = array_map(function ($data){
|
||||
return $data->id;
|
||||
}, $charactersData);
|
||||
|
||||
// broadcast map Access -> and send map Data
|
||||
$this->broadcastMapAccess($map, $characterIds);
|
||||
|
||||
$return->mapData = $map->getData();
|
||||
}catch(Exception\ValidationException $e){
|
||||
$validationError = (object) [];
|
||||
$validationError->type = 'error';
|
||||
$validationError->field = $e->getField();
|
||||
$validationError->message = $e->getMessage();
|
||||
$return->error[] = $validationError;
|
||||
}
|
||||
// reload the same map model (refresh)
|
||||
// this makes sure all data is up2date
|
||||
$map->getById( $map->_id, 0 );
|
||||
|
||||
|
||||
$charactersData = $map->getCharactersData();
|
||||
$characterIds = array_map(function ($data){
|
||||
return $data->id;
|
||||
}, $charactersData);
|
||||
|
||||
// broadcast map Access -> and send map Data
|
||||
$this->broadcastMapAccess($map, $characterIds);
|
||||
|
||||
$return->mapData = $map->getData();
|
||||
}else{
|
||||
// map access denied
|
||||
$captchaError = (object) [];
|
||||
@@ -559,18 +566,30 @@ class Map extends Controller\AccessController {
|
||||
*/
|
||||
public function delete(\Base $f3){
|
||||
$mapData = (array)$f3->get('POST.mapData');
|
||||
$activeCharacter = $this->getCharacter();
|
||||
$mapId = (int)$mapData['id'];
|
||||
$return = (object) [];
|
||||
$return->deletedMapIds = [];
|
||||
|
||||
/**
|
||||
* @var $map Model\MapModel
|
||||
*/
|
||||
$map = Model\BasicModel::getNew('MapModel');
|
||||
$map->getById($mapData['id']);
|
||||
$map->delete( $activeCharacter, function($mapId){
|
||||
$this->broadcastMapDeleted($mapId);
|
||||
});
|
||||
if($mapId){
|
||||
$activeCharacter = $this->getCharacter();
|
||||
|
||||
echo json_encode([]);
|
||||
/**
|
||||
* @var $map Model\MapModel
|
||||
*/
|
||||
$map = Model\BasicModel::getNew('MapModel');
|
||||
$map->getById($mapId);
|
||||
|
||||
if($map->hasAccess($activeCharacter)){
|
||||
$map->setActive(false);
|
||||
$map->save($activeCharacter);
|
||||
$return->deletedMapIds[] = $mapId;
|
||||
|
||||
// broadcast map delete
|
||||
$this->broadcastMapDeleted($mapId);
|
||||
}
|
||||
}
|
||||
|
||||
echo json_encode($return);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -654,7 +673,7 @@ class Map extends Controller\AccessController {
|
||||
$return = (object) [];
|
||||
$return->error = [];
|
||||
|
||||
// get current map data ===============================================================================
|
||||
// get current map data ===================================================================================
|
||||
$maps = $activeCharacter->getMaps();
|
||||
|
||||
// loop all submitted map data that should be saved
|
||||
@@ -680,13 +699,13 @@ class Map extends Controller\AccessController {
|
||||
count($connections) > 0
|
||||
){
|
||||
|
||||
// map changes expected =======================================================================
|
||||
// map changes expected ===========================================================================
|
||||
|
||||
// loop current user maps and check for changes
|
||||
foreach($maps as $map){
|
||||
$mapChanged = false;
|
||||
|
||||
// update system data ---------------------------------------------------------------------
|
||||
// update system data -------------------------------------------------------------------------
|
||||
foreach($systems as $i => $systemData){
|
||||
|
||||
// check if current system belongs to the current map
|
||||
@@ -703,25 +722,25 @@ class Map extends Controller\AccessController {
|
||||
// system belongs to the current map
|
||||
if(is_object($filteredMap->systems)){
|
||||
// update
|
||||
unset($systemData['updated']);
|
||||
|
||||
/**
|
||||
* @var $system Model\SystemModel
|
||||
*/
|
||||
$system = $filteredMap->systems->current();
|
||||
$system->setData($systemData);
|
||||
$system->updatedCharacterId = $activeCharacter;
|
||||
$system->save();
|
||||
|
||||
$mapChanged = true;
|
||||
|
||||
// a system belongs to ONE map -> speed up for multiple maps
|
||||
unset($systemData[$i]);
|
||||
if($system->save($activeCharacter)){
|
||||
$mapChanged = true;
|
||||
// one system belongs to ONE map -> speed up for multiple maps
|
||||
unset($systemData[$i]);
|
||||
}else{
|
||||
$return->error = array_merge($return->error, $system->getErrors());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// update connection data -----------------------------------------------------------------
|
||||
// update connection data ---------------------------------------------------------------------
|
||||
foreach($connections as $i => $connectionData){
|
||||
|
||||
// check if the current connection belongs to the current map
|
||||
@@ -738,19 +757,20 @@ class Map extends Controller\AccessController {
|
||||
// connection belongs to the current map
|
||||
if(is_object($filteredMap->connections)){
|
||||
// update
|
||||
unset($connectionData['updated']);
|
||||
|
||||
/**
|
||||
* @var $connection Model\ConnectionModel
|
||||
*/
|
||||
$connection = $filteredMap->connections->current();
|
||||
$connection->setData($connectionData);
|
||||
$connection->save();
|
||||
|
||||
$mapChanged = true;
|
||||
|
||||
// a connection belongs to ONE map -> speed up for multiple maps
|
||||
unset($connectionData[$i]);
|
||||
if($connection->save($activeCharacter)){
|
||||
$mapChanged = true;
|
||||
// one connection belongs to ONE map -> speed up for multiple maps
|
||||
unset($connectionData[$i]);
|
||||
}else{
|
||||
$return->error = array_merge($return->error, $connection->getErrors());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -767,7 +787,7 @@ class Map extends Controller\AccessController {
|
||||
|
||||
// cache time(s) per user should be equal or less than this function is called
|
||||
// prevent request flooding
|
||||
$responseTTL = (int)$f3->get('PATHFINDER.TIMER.UPDATE_SERVER_MAP.DELAY') / 1000;
|
||||
$responseTTL = (int)Config::getPathfinderData('timer.update_server_map.delay') / 1000;
|
||||
|
||||
$f3->set($cacheKey, $return, $responseTTL);
|
||||
}
|
||||
@@ -852,7 +872,7 @@ class Map extends Controller\AccessController {
|
||||
|
||||
// cache time (seconds) should be equal or less than request trigger time
|
||||
// prevent request flooding
|
||||
$responseTTL = (int)$f3->get('PATHFINDER.TIMER.UPDATE_SERVER_USER_DATA.DELAY') / 1000;
|
||||
$responseTTL = (int)Config::getPathfinderData('timer.update_server_user_data.delay') / 1000;
|
||||
|
||||
// cache response
|
||||
$f3->set($cacheKey, $return, $responseTTL);
|
||||
@@ -1003,13 +1023,13 @@ class Map extends Controller\AccessController {
|
||||
break;
|
||||
}
|
||||
|
||||
// save source system -------------------------------------------------------------------------------------
|
||||
// save source system ---------------------------------------------------------------------------------
|
||||
if(
|
||||
$addSourceSystem &&
|
||||
$sourceSystem &&
|
||||
!$sourceExists
|
||||
){
|
||||
$sourceSystem = $map->saveSystem($sourceSystem, $systemPosX, $systemPosY, $character);
|
||||
$sourceSystem = $map->saveSystem($sourceSystem, $character, $systemPosX, $systemPosY);
|
||||
// get updated maps object
|
||||
if($sourceSystem){
|
||||
$map = $sourceSystem->mapId;
|
||||
@@ -1021,13 +1041,13 @@ class Map extends Controller\AccessController {
|
||||
}
|
||||
}
|
||||
|
||||
// save target system -------------------------------------------------------------------------------------
|
||||
// save target system ---------------------------------------------------------------------------------
|
||||
if(
|
||||
$addTargetSystem &&
|
||||
$targetSystem &&
|
||||
!$targetExists
|
||||
){
|
||||
$targetSystem = $map->saveSystem($targetSystem, $systemPosX, $systemPosY, $character);
|
||||
$targetSystem = $map->saveSystem($targetSystem, $character, $systemPosX, $systemPosY);
|
||||
// get updated maps object
|
||||
if($targetSystem){
|
||||
$map = $targetSystem->mapId;
|
||||
@@ -1036,7 +1056,7 @@ class Map extends Controller\AccessController {
|
||||
}
|
||||
}
|
||||
|
||||
// save connection ----------------------------------------------------------------------------------------
|
||||
// save connection ------------------------------------------------------------------------------------
|
||||
if(
|
||||
$addConnection &&
|
||||
$sourceExists &&
|
||||
@@ -1046,7 +1066,7 @@ class Map extends Controller\AccessController {
|
||||
!$map->searchConnection( $sourceSystem, $targetSystem )
|
||||
){
|
||||
$connection = $map->getNewConnection($sourceSystem, $targetSystem);
|
||||
$connection = $map->saveConnection($connection);
|
||||
$connection = $map->saveConnection($connection, $character);
|
||||
// get updated maps object
|
||||
if($connection){
|
||||
$map = $connection->mapId;
|
||||
@@ -1097,6 +1117,53 @@ class Map extends Controller\AccessController {
|
||||
echo json_encode($connectionData);
|
||||
}
|
||||
|
||||
/**
|
||||
* get map log data
|
||||
* @param \Base $f3
|
||||
*/
|
||||
public function getLogData(\Base $f3){
|
||||
$postData = (array)$f3->get('POST');
|
||||
$return = (object) [];
|
||||
$return->data = [];
|
||||
|
||||
// validate query parameters
|
||||
$return->query = [
|
||||
'mapId' => (int) $postData['mapId'],
|
||||
'offset' => FileHandler::validateOffset( (int)$postData['offset'] ),
|
||||
'limit' => FileHandler::validateLimit( (int)$postData['limit'] )
|
||||
];
|
||||
|
||||
if($mapId = (int)$postData['mapId']){
|
||||
$activeCharacter = $this->getCharacter();
|
||||
|
||||
/**
|
||||
* @var Model\MapModel $map
|
||||
*/
|
||||
$map = Model\BasicModel::getNew('MapModel');
|
||||
$map->getById($mapId);
|
||||
|
||||
if($map->hasAccess($activeCharacter)){
|
||||
$cacheKey = $this->getHistoryDataCacheKey($mapId);
|
||||
if($return->query['offset'] === 0){
|
||||
// check cache
|
||||
$return->data = $f3->get($cacheKey);
|
||||
}
|
||||
|
||||
if(empty($return->data)){
|
||||
$return->data = $map->getLogData($return->query['offset'], $return->query['limit']);
|
||||
if(
|
||||
$return->query['offset'] === 0 &&
|
||||
!empty($return->data))
|
||||
{
|
||||
$f3->set($cacheKey, $return->data, (int)Config::getPathfinderData('history.cache'));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
echo json_encode($return);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
namespace Controller\Api;
|
||||
use Controller;
|
||||
use lib\Config;
|
||||
use Model;
|
||||
|
||||
|
||||
@@ -530,7 +531,7 @@ class Route extends Controller\AccessController {
|
||||
$map = Model\BasicModel::getNew('MapModel');
|
||||
|
||||
// limit max search routes to max limit
|
||||
array_splice($routesData, $f3->get('PATHFINDER.ROUTE.LIMIT'));
|
||||
array_splice($routesData, Config::getPathfinderData('route.limit'));
|
||||
|
||||
foreach($routesData as $key => $routeData){
|
||||
// mapIds are optional. If mapIds is empty or not set
|
||||
@@ -609,7 +610,7 @@ class Route extends Controller\AccessController {
|
||||
$returnRoutData = $cachedData;
|
||||
}else{
|
||||
// max search depth for search
|
||||
$searchDepth = $f3->get('PATHFINDER.ROUTE.SEARCH_DEPTH');
|
||||
$searchDepth = Config::getPathfinderData('route.search_depth');
|
||||
|
||||
// set jump data for following route search
|
||||
// --> don´t filter some systems (e.g. systemFrom, systemTo) even if they are are WH,LS,0.0
|
||||
|
||||
@@ -13,16 +13,6 @@ use Model;
|
||||
|
||||
class Signature extends Controller\AccessController {
|
||||
|
||||
/**
|
||||
* event handler
|
||||
* @param \Base $f3
|
||||
* @param array $params
|
||||
*/
|
||||
function beforeroute(\Base $f3, $params) {
|
||||
// set header for all routes
|
||||
header('Content-type: application/json');
|
||||
parent::beforeroute($f3, $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* get signature data for systems
|
||||
@@ -120,8 +110,6 @@ class Signature extends Controller\AccessController {
|
||||
if($signature->dry()){
|
||||
// new signature
|
||||
$signature->systemId = $system;
|
||||
$signature->updatedCharacterId = $activeCharacter;
|
||||
$signature->createdCharacterId = $activeCharacter;
|
||||
$signature->setData($data);
|
||||
}else{
|
||||
// update signature
|
||||
@@ -181,13 +169,11 @@ class Signature extends Controller\AccessController {
|
||||
}
|
||||
|
||||
if( $signature->hasChanged($newData) ){
|
||||
// Character should only be changed if something else has changed
|
||||
$signature->updatedCharacterId = $activeCharacter;
|
||||
$signature->setData($newData);
|
||||
}
|
||||
}
|
||||
|
||||
$signature->save();
|
||||
$signature->save($activeCharacter);
|
||||
$updatedSignatureIds[] = $signature->id;
|
||||
|
||||
// get a fresh signature object with the new data. This is a bad work around!
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
namespace controller\api;
|
||||
|
||||
use Controller;
|
||||
use lib\Config;
|
||||
use Model\CharacterModel;
|
||||
|
||||
class Statistic extends Controller\AccessController {
|
||||
@@ -131,19 +132,19 @@ class Statistic extends Controller\AccessController {
|
||||
$objectId = 0;
|
||||
|
||||
// add map-"typeId" (private/corp/ally) condition -------------------------------------------------------------
|
||||
// check if "ACTIVITY_LOGGING" is active for a given "typeId"
|
||||
// check if "LOG_ACTIVITY_ENABLED" is active for a given "typeId"
|
||||
$sqlMapType = "";
|
||||
|
||||
switch($typeId){
|
||||
case 2:
|
||||
if( $this->getF3()->get('PATHFINDER.MAP.PRIVATE.ACTIVITY_LOGGING') ){
|
||||
if( Config::getMapsDefaultConfig('private')['log_activity_enabled'] ){
|
||||
$sqlMapType .= " AND `character`.`id` = :objectId ";
|
||||
$objectId = $character->_id;
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
if(
|
||||
$this->getF3()->get('PATHFINDER.MAP.CORPORATION.ACTIVITY_LOGGING') &&
|
||||
Config::getMapsDefaultConfig('corporation')['log_activity_enabled'] &&
|
||||
$character->hasCorporation()
|
||||
){
|
||||
$sqlMapType .= " AND `character`.`corporationId` = :objectId ";
|
||||
@@ -152,7 +153,7 @@ class Statistic extends Controller\AccessController {
|
||||
break;
|
||||
case 4:
|
||||
if(
|
||||
$this->getF3()->get('PATHFINDER.MAP.ALLIANCE.ACTIVITY_LOGGING') &&
|
||||
Config::getMapsDefaultConfig('alliance')['log_activity_enabled'] &&
|
||||
$character->hasAlliance()
|
||||
){
|
||||
$sqlMapType .= " AND `character`.`allianceId` = :objectId ";
|
||||
@@ -181,6 +182,9 @@ class Statistic extends Controller\AccessController {
|
||||
`log`.`characterId`,
|
||||
`character`.`name`,
|
||||
`character`.`lastLogin`,
|
||||
SUM(`log`.`mapCreate`) `mapCreate`,
|
||||
SUM(`log`.`mapUpdate`) `mapUpdate`,
|
||||
SUM(`log`.`mapDelete`) `mapDelete`,
|
||||
SUM(`log`.`systemCreate`) `systemCreate`,
|
||||
SUM(`log`.`systemUpdate`) `systemUpdate`,
|
||||
SUM(`log`.`systemDelete`) `systemDelete`,
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
|
||||
namespace Controller\Api;
|
||||
use Controller;
|
||||
use Controller\Ccp\Sso;
|
||||
use Data\Mapper as Mapper;
|
||||
use lib\Config;
|
||||
use Model;
|
||||
|
||||
class System extends Controller\AccessController {
|
||||
@@ -65,16 +65,6 @@ class System extends Controller\AccessController {
|
||||
|
||||
private $limitQuery = "";
|
||||
|
||||
/**
|
||||
* @param \Base $f3
|
||||
* @param array $params
|
||||
*/
|
||||
function beforeroute(\Base $f3, $params) {
|
||||
parent::beforeroute($f3, $params);
|
||||
|
||||
// set header for all routes
|
||||
header('Content-type: application/json');
|
||||
}
|
||||
|
||||
/**
|
||||
* build query
|
||||
@@ -187,9 +177,12 @@ class System extends Controller\AccessController {
|
||||
* @param \Base $f3
|
||||
*/
|
||||
public function save(\Base $f3){
|
||||
$newSystemData = [];
|
||||
$postData = (array)$f3->get('POST');
|
||||
|
||||
$return = (object) [];
|
||||
$return->error = [];
|
||||
$return->systemData = (object) [];
|
||||
|
||||
if(
|
||||
isset($postData['systemData']) &&
|
||||
isset($postData['mapData'])
|
||||
@@ -229,23 +222,21 @@ class System extends Controller\AccessController {
|
||||
*/
|
||||
$map = Model\BasicModel::getNew('MapModel');
|
||||
$map->getById($mapData['id']);
|
||||
if(
|
||||
!$map->dry() &&
|
||||
$map->hasAccess($activeCharacter)
|
||||
){
|
||||
if( $map->hasAccess($activeCharacter) ){
|
||||
// make sure system is not already on map
|
||||
// --> (e.g. multiple simultaneously save() calls for the same system)
|
||||
$systemModel = $map->getSystemByCCPId($systemData['systemId']);
|
||||
if( is_null($systemModel) ){
|
||||
// system not found on map -> get static system data (CCP DB)
|
||||
$systemModel = $map->getNewSystem($systemData['systemId']);
|
||||
$systemModel->createdCharacterId = $activeCharacter;
|
||||
$systemModel->statusId = isset($systemData['statusId']) ? $systemData['statusId'] : 1;
|
||||
$defaultStatusId = 1;
|
||||
}else{
|
||||
// system already exists (e.g. was inactive)
|
||||
$systemModel->statusId = isset($systemData['statusId']) ? $systemData['statusId'] : $systemModel->statusId;
|
||||
$defaultStatusId = $systemModel->statusId;
|
||||
}
|
||||
|
||||
$systemModel->statusId = isset($systemData['statusId']) ? $systemData['statusId'] : $defaultStatusId;
|
||||
|
||||
// map is not changeable for a system! (security)
|
||||
$systemData['mapId'] = $map;
|
||||
}
|
||||
@@ -255,28 +246,32 @@ class System extends Controller\AccessController {
|
||||
// "statusId" was set above
|
||||
unset($systemData['statusId']);
|
||||
unset($systemData['mapId']);
|
||||
unset($systemData['createdCharacterId']);
|
||||
unset($systemData['updatedCharacterId']);
|
||||
|
||||
// set/update system
|
||||
$systemModel->setData($systemData);
|
||||
// activate system (e.g. was inactive))
|
||||
$systemModel->setActive(true);
|
||||
$systemModel->updatedCharacterId = $activeCharacter;
|
||||
$systemModel->save();
|
||||
|
||||
// get data from "fresh" model (e.g. some relational data has changed: "statusId")
|
||||
$newSystemModel = Model\BasicModel::getNew('SystemModel');
|
||||
$newSystemModel->getById( $systemModel->id, 0);
|
||||
$newSystemModel->clearCacheData();
|
||||
$newSystemData = $newSystemModel->getData();
|
||||
|
||||
// broadcast map changes
|
||||
$this->broadcastMapData($newSystemModel->mapId);
|
||||
if($systemModel->save($activeCharacter)){
|
||||
// get data from "fresh" model (e.g. some relational data has changed: "statusId")
|
||||
/**
|
||||
* @var $newSystemModel Model\SystemModel
|
||||
*/
|
||||
$newSystemModel = Model\BasicModel::getNew('SystemModel');
|
||||
$newSystemModel->getById( $systemModel->id, 0);
|
||||
$newSystemModel->clearCacheData();
|
||||
$return->systemData = $newSystemModel->getData();
|
||||
|
||||
// broadcast map changes
|
||||
$this->broadcastMapData($newSystemModel->mapId);
|
||||
}else{
|
||||
$return->error = $systemModel->getErrors();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
echo json_encode($newSystemData);
|
||||
echo json_encode($return);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -358,7 +353,7 @@ class System extends Controller\AccessController {
|
||||
$return->systemData[] = $systemModel->getData();
|
||||
}
|
||||
|
||||
$f3->set($cacheKey, $return->systemData, $f3->get('PATHFINDER.CACHE.CONSTELLATION_SYSTEMS') );
|
||||
$f3->set($cacheKey, $return->systemData, Config::getPathfinderData('cache.constellation_systems'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -407,6 +402,37 @@ class System extends Controller\AccessController {
|
||||
echo json_encode($return);
|
||||
}
|
||||
|
||||
/**
|
||||
* send Rally Point poke
|
||||
* @param \Base $f3
|
||||
*/
|
||||
public function pokeRally(\Base $f3){
|
||||
$rallyData = (array)$f3->get('POST');
|
||||
$systemId = (int)$rallyData['systemId'];
|
||||
$return = (object) [];
|
||||
|
||||
if($systemId){
|
||||
$activeCharacter = $this->getCharacter();
|
||||
|
||||
/**
|
||||
* @var Model\SystemModel $system
|
||||
*/
|
||||
$system = Model\BasicModel::getNew('SystemModel');
|
||||
$system->getById($systemId);
|
||||
|
||||
if($system->hasAccess($activeCharacter)){
|
||||
$rallyData['pokeDesktop'] = $rallyData['pokeDesktop'] === '1';
|
||||
$rallyData['pokeMail'] = $rallyData['pokeMail'] === '1';
|
||||
$rallyData['pokeSlack'] = $rallyData['pokeSlack'] === '1';
|
||||
$rallyData['message'] = trim($rallyData['message']);
|
||||
|
||||
$system->sendRallyPoke($rallyData, $activeCharacter);
|
||||
}
|
||||
}
|
||||
|
||||
echo json_encode($return);
|
||||
}
|
||||
|
||||
/**
|
||||
* delete systems and all its connections from map
|
||||
* -> set "active" flag
|
||||
@@ -428,7 +454,7 @@ class System extends Controller\AccessController {
|
||||
$map = Model\BasicModel::getNew('MapModel');
|
||||
$map->getById($mapId);
|
||||
|
||||
if( $map->hasAccess($activeCharacter) ){
|
||||
if($map->hasAccess($activeCharacter)){
|
||||
foreach($systemIds as $systemId){
|
||||
if( $system = $map->getSystemById($systemId) ){
|
||||
// check whether system should be deleted OR set "inactive"
|
||||
@@ -437,7 +463,7 @@ class System extends Controller\AccessController {
|
||||
}else{
|
||||
// keep data -> set "inactive"
|
||||
$system->setActive(false);
|
||||
$system->save();
|
||||
$system->save($activeCharacter);
|
||||
}
|
||||
|
||||
$system->reset();
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
|
||||
namespace Controller\Api;
|
||||
use Controller;
|
||||
use controller\MailController;
|
||||
use Model;
|
||||
use Exception;
|
||||
|
||||
@@ -26,8 +25,8 @@ class User extends Controller\Controller{
|
||||
// character specific session keys
|
||||
const SESSION_KEY_CHARACTERS = 'SESSION.CHARACTERS';
|
||||
|
||||
// temp login character ID (during HTTP redirects on login)
|
||||
const SESSION_KEY_TEMP_CHARACTER_ID = 'SESSION.TEMP_CHARACTER_ID';
|
||||
// temp login character data (during HTTP redirects on login)
|
||||
const SESSION_KEY_TEMP_CHARACTER_DATA = 'SESSION.TEMP_CHARACTER_DATA';
|
||||
|
||||
// log text
|
||||
const LOG_LOGGED_IN = 'userId: [%10s], userName: [%30s], charId: [%20s], charName: %s';
|
||||
@@ -43,9 +42,10 @@ class User extends Controller\Controller{
|
||||
/**
|
||||
* login a valid character
|
||||
* @param Model\CharacterModel $characterModel
|
||||
* @param string $browserTabId
|
||||
* @return bool
|
||||
*/
|
||||
protected function loginByCharacter(Model\CharacterModel &$characterModel){
|
||||
protected function loginByCharacter(Model\CharacterModel &$characterModel, string $browserTabId){
|
||||
$login = false;
|
||||
|
||||
if($user = $characterModel->getUser()){
|
||||
@@ -68,7 +68,7 @@ class User extends Controller\Controller{
|
||||
){
|
||||
// user has changed OR new user ---------------------------------------------------
|
||||
//-> set user/character data to session
|
||||
$this->f3->set(self::SESSION_KEY_USER, [
|
||||
$this->getF3()->set(self::SESSION_KEY_USER, [
|
||||
'ID' => $user->_id,
|
||||
'NAME' => $user->name
|
||||
]);
|
||||
@@ -77,7 +77,7 @@ class User extends Controller\Controller{
|
||||
$sessionCharacters = $characterModel::mergeSessionCharacterData($sessionCharacters);
|
||||
}
|
||||
|
||||
$this->f3->set(self::SESSION_KEY_CHARACTERS, $sessionCharacters);
|
||||
$this->getF3()->set(self::SESSION_KEY_CHARACTERS, $sessionCharacters);
|
||||
|
||||
// save user login information --------------------------------------------------------
|
||||
$characterModel->roleId = $characterModel->requestRoleId();
|
||||
@@ -94,6 +94,10 @@ class User extends Controller\Controller{
|
||||
)
|
||||
);
|
||||
|
||||
// set temp character data ------------------------------------------------------------
|
||||
// -> pass character data over for next http request (reroute())
|
||||
$this->setTempCharacterData($characterModel->_id, $browserTabId);
|
||||
|
||||
$login = true;
|
||||
}
|
||||
|
||||
@@ -118,6 +122,14 @@ class User extends Controller\Controller{
|
||||
if( !empty($characters = $this->getCookieCharacters(array_slice($cookieData, 0, 1, true), false)) ){
|
||||
// character is valid and allowed to login
|
||||
$return->character = reset($characters)->getData();
|
||||
// get Session status for character
|
||||
if($activeCharacter = $this->getCharacter()){
|
||||
if($activeUser = $activeCharacter->getUser()){
|
||||
if($sessionCharacterData = $activeUser->findSessionCharacterData($return->character->id)){
|
||||
$return->character->hasActiveSession = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}else{
|
||||
$characterError = (object) [];
|
||||
$characterError->type = 'warning';
|
||||
@@ -179,9 +191,7 @@ class User extends Controller\Controller{
|
||||
*/
|
||||
public function deleteLog(\Base $f3){
|
||||
if($activeCharacter = $this->getCharacter()){
|
||||
if($characterLog = $activeCharacter->getLog()){
|
||||
$characterLog->erase();
|
||||
}
|
||||
$activeCharacter->logout(false, true, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -190,8 +200,7 @@ class User extends Controller\Controller{
|
||||
* @param \Base $f3
|
||||
*/
|
||||
public function logout(\Base $f3){
|
||||
$this->deleteLog($f3);
|
||||
parent::logout($f3);
|
||||
$this->logoutCharacter(false, true, true, true);
|
||||
|
||||
$return = (object) [];
|
||||
$return->reroute = rtrim(self::getEnvironmentData('URL'), '/') . $f3->alias('login');
|
||||
@@ -371,23 +380,15 @@ class User extends Controller\Controller{
|
||||
$user = $activeCharacter->getUser();
|
||||
|
||||
if($user){
|
||||
// try to send delete account mail
|
||||
$msg = 'Hello ' . $user->name . ',<br><br>';
|
||||
$msg .= 'your account data has been successfully deleted.';
|
||||
|
||||
$mailController = new MailController();
|
||||
$mailController->sendDeleteAccount($user->email, $msg);
|
||||
|
||||
// save log
|
||||
self::getLogger('DELETE_ACCOUNT')->write(
|
||||
sprintf(self::LOG_DELETE_ACCOUNT, $user->id, $user->name)
|
||||
);
|
||||
|
||||
// remove user
|
||||
$this->logoutCharacter(true, true, true, true);
|
||||
$user->erase();
|
||||
|
||||
$this->logout($f3);
|
||||
die();
|
||||
$return->reroute = rtrim(self::getEnvironmentData('URL'), '/') . $f3->alias('login');
|
||||
}
|
||||
}else{
|
||||
// captcha not valid -> return error
|
||||
|
||||
@@ -13,6 +13,33 @@ use lib\Config;
|
||||
|
||||
class AppController extends Controller {
|
||||
|
||||
public function beforeroute(\Base $f3, $params) : bool{
|
||||
// page title
|
||||
$f3->set('tplPageTitle', Config::getPathfinderData('name'));
|
||||
|
||||
// main page content
|
||||
$f3->set('tplPageContent', Config::getPathfinderData('view.login'));
|
||||
|
||||
// body element class
|
||||
$f3->set('tplBodyClass', 'pf-landing');
|
||||
|
||||
// JS main file
|
||||
$f3->set('tplJsView', 'login');
|
||||
|
||||
if($return = parent::beforeroute($f3, $params)){
|
||||
// href for SSO Auth
|
||||
$f3->set('tplAuthType', $f3->alias( 'sso', ['action' => 'requestAuthorization'] ));
|
||||
|
||||
// characters from cookies
|
||||
$f3->set('cookieCharacters', $this->getCookieByName(self::COOKIE_PREFIX_CHARACTER, true));
|
||||
$f3->set('getCharacterGrid', function($characters){
|
||||
return ( ((12 / count($characters)) <= 3) ? 3 : (12 / count($characters)) );
|
||||
});
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* event handler after routing
|
||||
* @param \Base $f3
|
||||
@@ -31,26 +58,7 @@ class AppController extends Controller {
|
||||
* @param \Base $f3
|
||||
*/
|
||||
public function init(\Base $f3) {
|
||||
// page title
|
||||
$f3->set('tplPageTitle', Config::getPathfinderData('name'));
|
||||
|
||||
// main page content
|
||||
$f3->set('tplPageContent', Config::getPathfinderData('view.login'));
|
||||
|
||||
// body element class
|
||||
$f3->set('tplBodyClass', 'pf-landing');
|
||||
|
||||
// JS main file
|
||||
$f3->set('tplJsView', 'login');
|
||||
|
||||
// href for SSO Auth
|
||||
$f3->set('tplAuthType', $f3->alias( 'sso', ['action' => 'requestAuthorization'] ));
|
||||
|
||||
// characters from cookies
|
||||
$f3->set('cookieCharacters', $this->getCookieByName(self::COOKIE_PREFIX_CHARACTER, true));
|
||||
$f3->set('getCharacterGrid', function($characters){
|
||||
return ( ((12 / count($characters)) <= 3) ? 3 : (12 / count($characters)) );
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -34,6 +34,7 @@ class Sso extends Api\User{
|
||||
const SESSION_KEY_SSO_ERROR = 'SESSION.SSO.ERROR';
|
||||
const SESSION_KEY_SSO_STATE = 'SESSION.SSO.STATE';
|
||||
const SESSION_KEY_SSO_FROM = 'SESSION.SSO.FROM';
|
||||
const SESSION_KEY_SSO_TAB_ID = 'SESSION.SSO.TABID';
|
||||
|
||||
// error messages
|
||||
const ERROR_CCP_SSO_URL = 'Invalid "ENVIRONMENT.[ENVIRONMENT].CCP_SSO_URL" url. %s';
|
||||
@@ -53,6 +54,8 @@ class Sso extends Api\User{
|
||||
* @param \Base $f3
|
||||
*/
|
||||
public function requestAdminAuthorization($f3){
|
||||
// store browser tabId to be "targeted" after login
|
||||
$f3->set(self::SESSION_KEY_SSO_TAB_ID, '');
|
||||
$f3->set(self::SESSION_KEY_SSO_FROM, 'admin');
|
||||
|
||||
$scopes = self::getScopesByAuthType('admin');
|
||||
@@ -66,6 +69,10 @@ class Sso extends Api\User{
|
||||
*/
|
||||
public function requestAuthorization($f3){
|
||||
$params = $f3->get('GET');
|
||||
$browserTabId = trim((string)$params['tabId']);
|
||||
|
||||
// store browser tabId to be "targeted" after login
|
||||
$f3->set(self::SESSION_KEY_SSO_TAB_ID, $browserTabId);
|
||||
|
||||
if(
|
||||
isset($params['characterId']) &&
|
||||
@@ -73,7 +80,7 @@ class Sso extends Api\User{
|
||||
){
|
||||
// authentication restricted to a characterId -----------------------------------------------
|
||||
// restrict login to this characterId e.g. for character switch on map page
|
||||
$characterId = (int)trim($params['characterId']);
|
||||
$characterId = (int)trim((string)$params['characterId']);
|
||||
|
||||
/**
|
||||
* @var Model\CharacterModel $character
|
||||
@@ -101,15 +108,12 @@ class Sso extends Api\User{
|
||||
$character->hasUserCharacter() &&
|
||||
($character->isAuthorized() === 'OK')
|
||||
){
|
||||
$loginCheck = $this->loginByCharacter($character);
|
||||
$loginCheck = $this->loginByCharacter($character, $browserTabId);
|
||||
|
||||
if($loginCheck){
|
||||
// set "login" cookie
|
||||
$this->setLoginCookie($character);
|
||||
|
||||
// -> pass current character data to target page
|
||||
$f3->set(Api\User::SESSION_KEY_TEMP_CHARACTER_ID, $character->_id);
|
||||
|
||||
// route to "map"
|
||||
$f3->reroute(['map']);
|
||||
}
|
||||
@@ -174,6 +178,8 @@ class Sso extends Api\User{
|
||||
$rootAlias = $f3->get(self::SESSION_KEY_SSO_FROM);
|
||||
}
|
||||
|
||||
$browserTabId = (string)$f3->get(self::SESSION_KEY_SSO_TAB_ID) ;
|
||||
|
||||
if($f3->exists(self::SESSION_KEY_SSO_STATE)){
|
||||
// check response and validate 'state'
|
||||
if(
|
||||
@@ -186,6 +192,7 @@ class Sso extends Api\User{
|
||||
// clear 'state' for new next login request
|
||||
$f3->clear(self::SESSION_KEY_SSO_STATE);
|
||||
$f3->clear(self::SESSION_KEY_SSO_FROM);
|
||||
$f3->clear(self::SESSION_KEY_SSO_TAB_ID);
|
||||
|
||||
$accessData = $this->getSsoAccessData($getParams['code']);
|
||||
|
||||
@@ -252,14 +259,14 @@ class Sso extends Api\User{
|
||||
$characterModel = $userCharactersModel->getCharacter();
|
||||
|
||||
// login by character
|
||||
$loginCheck = $this->loginByCharacter($characterModel);
|
||||
$loginCheck = $this->loginByCharacter($characterModel, $browserTabId);
|
||||
|
||||
if($loginCheck){
|
||||
// set "login" cookie
|
||||
$this->setLoginCookie($characterModel);
|
||||
|
||||
// -> pass current character data to target page
|
||||
$f3->set(Api\User::SESSION_KEY_TEMP_CHARACTER_ID, $characterModel->_id);
|
||||
$f3->set(Api\User::SESSION_KEY_TEMP_CHARACTER_DATA, $characterModel->_id);
|
||||
|
||||
// route to "map"
|
||||
if($rootAlias == 'admin'){
|
||||
@@ -302,6 +309,7 @@ class Sso extends Api\User{
|
||||
public function login(\Base $f3){
|
||||
$data = (array)$f3->get('GET');
|
||||
$cookieName = empty($data['cookie']) ? '' : $data['cookie'];
|
||||
$browserTabId = empty($data['tabId']) ? '' : $data['tabId'];
|
||||
$character = null;
|
||||
|
||||
if( !empty($cookieName) ){
|
||||
@@ -316,12 +324,8 @@ class Sso extends Api\User{
|
||||
|
||||
if( is_object($character)){
|
||||
// login by character
|
||||
$loginCheck = $this->loginByCharacter($character);
|
||||
$loginCheck = $this->loginByCharacter($character, $browserTabId);
|
||||
if($loginCheck){
|
||||
// set character id
|
||||
// -> pass current character data to target page
|
||||
$f3->set(Api\User::SESSION_KEY_TEMP_CHARACTER_ID, $character->_id);
|
||||
|
||||
// route to "map"
|
||||
$f3->reroute(['map']);
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
namespace Controller;
|
||||
use Controller\Api as Api;
|
||||
use lib\Config;
|
||||
use Lib\Monolog;
|
||||
use lib\Socket;
|
||||
use Lib\Util;
|
||||
use Model;
|
||||
@@ -21,8 +22,8 @@ class Controller {
|
||||
const COOKIE_PREFIX_CHARACTER = 'char';
|
||||
|
||||
// log text
|
||||
const LOG_UNAUTHORIZED = 'User-Agent: [%s]';
|
||||
const ERROR_SESSION_SUSPECT = 'Suspect id: [%45s], ip: [%45s], new ip: [%45s], User-Agent: [%s]';
|
||||
const ERROR_SESSION_SUSPECT = 'id: [%45s], ip: [%45s], User-Agent: [%s]';
|
||||
const ERROR_TEMP_CHARACTER_ID = 'Invalid temp characterId: %s';
|
||||
|
||||
/**
|
||||
* @var \Base
|
||||
@@ -48,46 +49,38 @@ class Controller {
|
||||
return $this->template;
|
||||
}
|
||||
|
||||
/**
|
||||
* set $f3 base object
|
||||
* @param \Base $f3
|
||||
*/
|
||||
protected function setF3(\Base $f3){
|
||||
$this->f3 = $f3;
|
||||
}
|
||||
|
||||
/**
|
||||
* get $f3 base object
|
||||
* @return \Base
|
||||
*/
|
||||
protected function getF3(){
|
||||
if( !($this->f3 instanceof \Base) ){
|
||||
$this->setF3( \Base::instance() );
|
||||
}
|
||||
return $this->f3;
|
||||
return \Base::instance();
|
||||
}
|
||||
|
||||
/**
|
||||
* event handler for all "views"
|
||||
* some global template variables are set in here
|
||||
* @param \Base $f3
|
||||
* @param array $params
|
||||
* @param $params
|
||||
* @return bool
|
||||
*/
|
||||
function beforeroute(\Base $f3, $params) {
|
||||
$this->setF3($f3);
|
||||
|
||||
function beforeroute(\Base $f3, $params): bool {
|
||||
// initiate DB connection
|
||||
DB\Database::instance('PF');
|
||||
DB\Database::instance()->getDB('PF');
|
||||
|
||||
// init user session
|
||||
$this->initSession();
|
||||
$this->initSession($f3);
|
||||
|
||||
if( !$f3->get('AJAX') ){
|
||||
if($f3->get('AJAX')){
|
||||
header('Content-type: application/json');
|
||||
}else{
|
||||
// js path (build/minified or raw uncompressed files)
|
||||
$f3->set('tplPathJs', 'public/js/' . Config::getPathfinderData('version') );
|
||||
|
||||
$this->setTemplate( Config::getPathfinderData('view.index') );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -96,9 +89,6 @@ class Controller {
|
||||
* @param \Base $f3
|
||||
*/
|
||||
public function afterroute(\Base $f3){
|
||||
// store all user activities that are buffered for logging in this request
|
||||
self::storeActivities();
|
||||
|
||||
if($this->getTemplate()){
|
||||
// Ajax calls don´t need a page render..
|
||||
// this happens on client side
|
||||
@@ -118,32 +108,35 @@ class Controller {
|
||||
/**
|
||||
* init new Session handler
|
||||
*/
|
||||
protected function initSession(){
|
||||
protected function initSession(\Base $f3){
|
||||
$sessionCacheKey = $f3->get('SESSION_CACHE');
|
||||
$session = null;
|
||||
|
||||
// init DB based Session (not file based)
|
||||
if( $this->getDB('PF') instanceof DB\SQL){
|
||||
// init session with custom "onsuspect()" handler
|
||||
new DB\SQL\Session($this->getDB('PF'), 'sessions', true, function($session, $sid){
|
||||
$f3 = $this->getF3();
|
||||
if( ($ip = $session->ip() )!= $f3->get('IP') ){
|
||||
// IP address changed -> not critical
|
||||
self::getLogger('SESSION_SUSPECT')->write( sprintf(
|
||||
self::ERROR_SESSION_SUSPECT,
|
||||
$sid,
|
||||
$session->ip(),
|
||||
$f3->get('IP'),
|
||||
$f3->get('AGENT')
|
||||
));
|
||||
// no more error handling here
|
||||
return true;
|
||||
}elseif($session->agent() != $f3->get('AGENT') ){
|
||||
// The default behaviour destroys the suspicious session.
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* callback() for suspect sessions
|
||||
* @param $session
|
||||
* @param $sid
|
||||
* @return bool
|
||||
*/
|
||||
$onSuspect = function($session, $sid){
|
||||
self::getLogger('SESSION_SUSPECT')->write( sprintf(
|
||||
self::ERROR_SESSION_SUSPECT,
|
||||
$sid,
|
||||
$session->ip(),
|
||||
$session->agent()
|
||||
));
|
||||
// .. continue with default onSuspect() handler
|
||||
// -> destroy session
|
||||
return false;
|
||||
};
|
||||
|
||||
return true;
|
||||
});
|
||||
if(
|
||||
$sessionCacheKey === 'mysql' &&
|
||||
$this->getDB('PF') instanceof DB\SQL
|
||||
){
|
||||
$session = new DB\SQL\Session($this->getDB('PF'), 'sessions', true, $onSuspect);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -190,12 +183,11 @@ class Controller {
|
||||
* @param Model\CharacterModel $character
|
||||
*/
|
||||
protected function setLoginCookie(Model\CharacterModel $character){
|
||||
|
||||
if( $this->getCookieState() ){
|
||||
$expireSeconds = (int) $this->getF3()->get('PATHFINDER.LOGIN.COOKIE_EXPIRE');
|
||||
$expireSeconds = (int)Config::getPathfinderData('login.cookie_expire');
|
||||
$expireSeconds *= 24 * 60 * 60;
|
||||
|
||||
$timezone = new \DateTimeZone( $this->getF3()->get('TZ') );
|
||||
$timezone = $this->getF3()->get('getTimeZone')();
|
||||
$expireTime = new \DateTime('now', $timezone);
|
||||
|
||||
// add cookie expire time
|
||||
@@ -258,7 +250,7 @@ class Controller {
|
||||
*/
|
||||
$characterAuth = Model\BasicModel::getNew('CharacterAuthenticationModel');
|
||||
|
||||
$timezone = new \DateTimeZone( $this->getF3()->get('TZ') );
|
||||
$timezone = $this->getF3()->get('getTimeZone')();
|
||||
$currentTime = new \DateTime('now', $timezone);
|
||||
|
||||
foreach($cookieData as $name => $value){
|
||||
@@ -268,7 +260,7 @@ class Controller {
|
||||
$data = explode(':', $value);
|
||||
if(count($data) === 2){
|
||||
// cookie data is well formatted
|
||||
$characterAuth->getByForeignKey('selector', $data[0], ['limit' => 1], 0);
|
||||
$characterAuth->getByForeignKey('selector', $data[0], ['limit' => 1]);
|
||||
|
||||
// validate "scope hash"
|
||||
// -> either "normal" scopes OR "admin" scopes
|
||||
@@ -354,28 +346,37 @@ class Controller {
|
||||
$data = [];
|
||||
|
||||
if($user = $this->getUser()){
|
||||
$requestedCharacterId = 0;
|
||||
$header = self::getRequestHeaders();
|
||||
$requestedCharacterId = (int)$header['Pf-Character'];
|
||||
$browserTabId = (string)$header['Pf-Tab-Id'];
|
||||
$tempCharacterData = (array)$this->getF3()->get(Api\User::SESSION_KEY_TEMP_CHARACTER_DATA);
|
||||
|
||||
// get all characterData from currently active characters
|
||||
if($this->getF3()->get('AJAX')){
|
||||
// Ajax request -> get characterId from Header (if already available!)
|
||||
$header = $this->getRequestHeaders();
|
||||
$requestedCharacterId = (int)$header['Pf-Character'];
|
||||
|
||||
// _blank browser tab don´t have a $browserTabId jet..
|
||||
// first Ajax call from that new tab with empty $requestedCharacterId -> bind to that new tab
|
||||
if(
|
||||
!empty($browserTabId) &&
|
||||
$requestedCharacterId <= 0 &&
|
||||
(int)$tempCharacterData['ID'] > 0 &&
|
||||
empty($tempCharacterData['TAB_ID'])
|
||||
){
|
||||
$tempCharacterData['TAB_ID'] = $browserTabId;
|
||||
// update tempCharacterData (SESSION)
|
||||
$this->setTempCharacterData($tempCharacterData['ID'], $tempCharacterData['TAB_ID']);
|
||||
}
|
||||
|
||||
if(
|
||||
$requestedCharacterId > 0 &&
|
||||
(int)$this->getF3()->get(Api\User::SESSION_KEY_TEMP_CHARACTER_ID) === $requestedCharacterId
|
||||
!empty($browserTabId) &&
|
||||
!empty($tempCharacterData['TAB_ID']) &&
|
||||
(int)$tempCharacterData['ID'] > 0 &&
|
||||
$browserTabId === $tempCharacterData['TAB_ID']
|
||||
){
|
||||
// requested characterId is "now" available on the client (Javascript)
|
||||
// -> clear temp characterId for next character login/switch
|
||||
$this->getF3()->clear(Api\User::SESSION_KEY_TEMP_CHARACTER_ID);
|
||||
$requestedCharacterId = (int)$tempCharacterData['ID'];
|
||||
}
|
||||
}
|
||||
|
||||
if($requestedCharacterId <= 0){
|
||||
// Ajax BUT characterID not yet set as HTTP header
|
||||
// OR non Ajax -> get characterId from temp session (e.g. from HTTP redirect)
|
||||
$requestedCharacterId = (int)$this->getF3()->get(Api\User::SESSION_KEY_TEMP_CHARACTER_ID);
|
||||
}elseif((int)$tempCharacterData['ID'] > 0){
|
||||
$requestedCharacterId = (int)$tempCharacterData['ID'];
|
||||
}
|
||||
|
||||
$data = $user->getSessionCharacterData($requestedCharacterId);
|
||||
@@ -420,21 +421,18 @@ class Controller {
|
||||
public function getUser($ttl = 0){
|
||||
$user = null;
|
||||
|
||||
if( $this->getF3()->exists(Api\User::SESSION_KEY_USER_ID) ){
|
||||
$userId = (int)$this->getF3()->get(Api\User::SESSION_KEY_USER_ID);
|
||||
if($userId){
|
||||
/**
|
||||
* @var $userModel Model\UserModel
|
||||
*/
|
||||
$userModel = Model\BasicModel::getNew('UserModel');
|
||||
$userModel->getById($userId, $ttl);
|
||||
if($this->getF3()->exists(Api\User::SESSION_KEY_USER_ID, $userId)){
|
||||
/**
|
||||
* @var $userModel Model\UserModel
|
||||
*/
|
||||
$userModel = Model\BasicModel::getNew('UserModel');
|
||||
$userModel->getById($userId, $ttl);
|
||||
|
||||
if(
|
||||
!$userModel->dry() &&
|
||||
$userModel->hasUserCharacters()
|
||||
){
|
||||
$user = &$userModel;
|
||||
}
|
||||
if(
|
||||
!$userModel->dry() &&
|
||||
$userModel->hasUserCharacters()
|
||||
){
|
||||
$user = &$userModel;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -442,26 +440,57 @@ class Controller {
|
||||
}
|
||||
|
||||
/**
|
||||
* log out current character
|
||||
* @param \Base $f3
|
||||
* set temp login character data (required during HTTP redirects on login)
|
||||
* @param int $characterId
|
||||
* @param string $browserTabId
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function logout(\Base $f3){
|
||||
$params = (array)$f3->get('POST');
|
||||
protected function setTempCharacterData(int $characterId, string $browserTabId){
|
||||
if($characterId > 0){
|
||||
$tempCharacterData = [
|
||||
'ID' => $characterId,
|
||||
'TAB_ID' => trim($browserTabId)
|
||||
];
|
||||
$this->getF3()->set(Api\User::SESSION_KEY_TEMP_CHARACTER_DATA, $tempCharacterData);
|
||||
}else{
|
||||
throw new \Exception( sprintf(self::ERROR_TEMP_CHARACTER_ID, $characterId) );
|
||||
}
|
||||
}
|
||||
|
||||
if( $activeCharacter = $this->getCharacter() ){
|
||||
/**
|
||||
* log out current character or all active characters (multiple browser tabs)
|
||||
* @param bool $all
|
||||
* @param bool $deleteSession
|
||||
* @param bool $deleteLog
|
||||
* @param bool $deleteCookie
|
||||
*/
|
||||
protected function logoutCharacter(bool $all = false, bool $deleteSession = true, bool $deleteLog = true, bool $deleteCookie = false){
|
||||
$sessionCharacterData = (array)$this->getF3()->get(Api\User::SESSION_KEY_CHARACTERS);
|
||||
|
||||
if($params['clearCookies'] === '1'){
|
||||
// delete server side cookie validation data
|
||||
// for the active character
|
||||
$activeCharacter->logout();
|
||||
if($sessionCharacterData){
|
||||
$activeCharacterId = ($activeCharacter = $this->getCharacter()) ? $activeCharacter->_id : 0;
|
||||
/**
|
||||
* @var Model\CharacterModel $character
|
||||
*/
|
||||
$character = Model\BasicModel::getNew('CharacterModel');
|
||||
$characterIds = [];
|
||||
foreach($sessionCharacterData as $characterData){
|
||||
if($characterData['ID'] === $activeCharacterId){
|
||||
$characterIds[] = $activeCharacter->_id;
|
||||
$activeCharacter->logout($deleteSession, $deleteLog, $deleteCookie);
|
||||
}elseif($all){
|
||||
$character->getById($characterData['ID']);
|
||||
$characterIds[] = $character->_id;
|
||||
$character->logout($deleteSession, $deleteLog, $deleteCookie);
|
||||
}
|
||||
$character->reset();
|
||||
}
|
||||
|
||||
// broadcast logout information to webSocket server
|
||||
(new Socket( Config::getSocketUri() ))->sendData('characterLogout', $activeCharacter->_id);
|
||||
if($characterIds){
|
||||
// broadcast logout information to webSocket server
|
||||
(new Socket( Config::getSocketUri() ))->sendData('characterLogout', $characterIds);
|
||||
}
|
||||
}
|
||||
|
||||
// destroy session login data -------------------------------
|
||||
$f3->clear('SESSION');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -482,7 +511,7 @@ class Controller {
|
||||
|
||||
if( !empty($response) ){
|
||||
// calculate time diff since last server restart
|
||||
$timezone = new \DateTimeZone( $f3->get('TZ') );
|
||||
$timezone = $f3->get('getTimeZone')();
|
||||
$dateNow = new \DateTime('now', $timezone);
|
||||
$dateServerStart = new \DateTime($response['startTime']);
|
||||
$interval = $dateNow->diff($dateServerStart);
|
||||
@@ -504,14 +533,24 @@ class Controller {
|
||||
}
|
||||
|
||||
/**
|
||||
* get error object is a user is not found/logged of
|
||||
* @param int $code
|
||||
* @param string $message
|
||||
* @param string $status
|
||||
* @param null $trace
|
||||
* @return \stdClass
|
||||
*/
|
||||
protected function getLogoutError(){
|
||||
$userError = (object) [];
|
||||
$userError->type = 'error';
|
||||
$userError->message = 'User not found';
|
||||
return $userError;
|
||||
protected function getErrorObject(int $code, string $message = '', string $status = '', $trace = null): \stdClass{
|
||||
$object = (object) [];
|
||||
$object->type = 'error';
|
||||
$object->code = $code;
|
||||
$object->status = empty($status) ? @constant('Base::HTTP_' . $code) : $status;
|
||||
if(!empty($message)){
|
||||
$object->message = $message;
|
||||
}
|
||||
if(!empty($trace)){
|
||||
$object->trace = $trace;
|
||||
}
|
||||
return $object;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -558,59 +597,53 @@ class Controller {
|
||||
* -> on AJAX request -> return JSON with error information
|
||||
* -> on HTTP request -> render error page
|
||||
* @param \Base $f3
|
||||
* @return bool
|
||||
*/
|
||||
public function showError(\Base $f3){
|
||||
// set HTTP status
|
||||
$errorCode = $f3->get('ERROR.code');
|
||||
if(!empty($errorCode)){
|
||||
$f3->status($errorCode);
|
||||
}
|
||||
if(!headers_sent()){
|
||||
// collect error info -------------------------------------------------------------------------------------
|
||||
$error = $this->getErrorObject(
|
||||
$f3->get('ERROR.code'),
|
||||
$f3->get('ERROR.status'),
|
||||
$f3->get('ERROR.text'),
|
||||
$f3->get('DEBUG') === 3 ? $f3->get('ERROR.trace') : null
|
||||
);
|
||||
|
||||
// collect error info ---------------------------------------
|
||||
$return = (object) [];
|
||||
$error = (object) [];
|
||||
$error->type = 'error';
|
||||
$error->code = $errorCode;
|
||||
$error->status = $f3->get('ERROR.status');
|
||||
$error->message = $f3->get('ERROR.text');
|
||||
// check if error is a PDO Exception ----------------------------------------------------------------------
|
||||
if(strpos(strtolower( $f3->get('ERROR.text') ), 'duplicate') !== false){
|
||||
preg_match_all('/\'([^\']+)\'/', $f3->get('ERROR.text'), $matches, PREG_SET_ORDER);
|
||||
|
||||
// append stack trace for greater debug level
|
||||
if( $f3->get('DEBUG') === 3){
|
||||
$error->trace = $f3->get('ERROR.trace');
|
||||
}
|
||||
|
||||
// check if error is a PDO Exception
|
||||
if(strpos(strtolower( $f3->get('ERROR.text') ), 'duplicate') !== false){
|
||||
preg_match_all('/\'([^\']+)\'/', $f3->get('ERROR.text'), $matches, PREG_SET_ORDER);
|
||||
|
||||
if(count($matches) === 2){
|
||||
$error->field = $matches[1][1];
|
||||
$error->message = 'Value "' . $matches[0][1] . '" already exists';
|
||||
}
|
||||
}
|
||||
$return->error[] = $error;
|
||||
|
||||
// return error information ---------------------------------
|
||||
if($f3->get('AJAX')){
|
||||
header('Content-type: application/json');
|
||||
echo json_encode($return);
|
||||
die();
|
||||
}else{
|
||||
$f3->set('tplPageTitle', 'ERROR - ' . $error->code . ' | Pathfinder');
|
||||
// set error data for template rendering
|
||||
$error->redirectUrl = $this->getRouteUrl();
|
||||
$f3->set('errorData', $error);
|
||||
|
||||
if( preg_match('/^4[0-9]{2}$/', $error->code) ){
|
||||
// 4xx error -> render error page
|
||||
$f3->set('tplPageContent', Config::getPathfinderData('STATUS.4XX') );
|
||||
}elseif( preg_match('/^5[0-9]{2}$/', $error->code) ){
|
||||
$f3->set('tplPageContent', Config::getPathfinderData('STATUS.5XX'));
|
||||
if(count($matches) === 2){
|
||||
$error->field = $matches[1][1];
|
||||
$error->message = 'Value "' . $matches[0][1] . '" already exists';
|
||||
}
|
||||
}
|
||||
|
||||
echo \Template::instance()->render( Config::getPathfinderData('view.index') );
|
||||
die();
|
||||
// set response status ------------------------------------------------------------------------------------
|
||||
if(!empty($error->code)){
|
||||
$f3->status($error->code);
|
||||
}
|
||||
|
||||
if($f3->get('AJAX')){
|
||||
$return = (object) [];
|
||||
$return->error[] = $error;
|
||||
echo json_encode($return);
|
||||
}else{
|
||||
$f3->set('tplPageTitle', 'ERROR - ' . $error->code);
|
||||
// set error data for template rendering
|
||||
$error->redirectUrl = $this->getRouteUrl();
|
||||
$f3->set('errorData', $error);
|
||||
|
||||
if( preg_match('/^4[0-9]{2}$/', $error->code) ){
|
||||
// 4xx error -> render error page
|
||||
$f3->set('tplPageContent', Config::getPathfinderData('STATUS.4XX') );
|
||||
}elseif( preg_match('/^5[0-9]{2}$/', $error->code) ){
|
||||
$f3->set('tplPageContent', Config::getPathfinderData('STATUS.5XX'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -624,44 +657,36 @@ class Controller {
|
||||
// track some 4xx Client side errors
|
||||
// 5xx errors are handled in "ONERROR" callback
|
||||
$status = http_response_code();
|
||||
$halt = false;
|
||||
if(!headers_sent() && $status >= 300){
|
||||
if($f3->get('AJAX')){
|
||||
$params = (array)$f3->get('POST');
|
||||
$return = (object) [];
|
||||
if((bool)$params['reroute']){
|
||||
$return->reroute = rtrim(self::getEnvironmentData('URL'), '/') . $f3->alias('login');
|
||||
}else{
|
||||
// no reroute -> errors can be shown
|
||||
$return->error[] = $this->getErrorObject($status, Config::getMessageFromHTTPStatus($status));
|
||||
}
|
||||
|
||||
switch( $status ){
|
||||
case 403: // Unauthorized
|
||||
self::getLogger('UNAUTHORIZED')->write(sprintf(
|
||||
self::LOG_UNAUTHORIZED,
|
||||
$f3->get('AGENT')
|
||||
));
|
||||
$halt = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// Ajax
|
||||
if(
|
||||
$halt &&
|
||||
$f3->get('AJAX')
|
||||
){
|
||||
$params = (array)$f3->get('POST');
|
||||
$response = (object) [];
|
||||
$response->type = 'error';
|
||||
$response->code = $status;
|
||||
$response->message = 'Access denied: User not found';
|
||||
|
||||
$return = (object) [];
|
||||
if( (bool)$params['reroute']){
|
||||
$return->reroute = rtrim(self::getEnvironmentData('URL'), '/') . $f3->alias('login');
|
||||
}else{
|
||||
// no reroute -> errors can be shown
|
||||
$return->error[] = $response;
|
||||
echo json_encode($return);
|
||||
}
|
||||
|
||||
echo json_encode($return);
|
||||
die();
|
||||
}
|
||||
|
||||
// store all user activities that are buffered for logging in this request
|
||||
// this should work even on non HTTP200 responses
|
||||
$this->logActivities();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* store activity log data to DB
|
||||
*/
|
||||
protected function logActivities(){
|
||||
LogController::instance()->logActivities();
|
||||
Monolog::instance()->log();
|
||||
}
|
||||
|
||||
/**
|
||||
* get controller by class name
|
||||
* -> controller class is searched within all controller directories
|
||||
@@ -793,7 +818,7 @@ class Controller {
|
||||
* @return int
|
||||
*/
|
||||
static function getRegistrationStatus(){
|
||||
return (int)\Base::instance()->get('PATHFINDER.REGISTRATION.STATUS');
|
||||
return (int)Config::getPathfinderData('registration.status');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -806,13 +831,6 @@ class Controller {
|
||||
return LogController::getLogger($type);
|
||||
}
|
||||
|
||||
/**
|
||||
* store activity log data to DB
|
||||
*/
|
||||
static function storeActivities(){
|
||||
LogController::instance()->storeActivities();
|
||||
}
|
||||
|
||||
/**
|
||||
* removes illegal characters from a Hive-key that are not allowed
|
||||
* @param $key
|
||||
@@ -842,20 +860,4 @@ class Controller {
|
||||
(new Socket( Config::getSocketUri(), $ttl ))->sendData('healthCheck', $load);
|
||||
}
|
||||
|
||||
/**
|
||||
* get required MySQL variable value
|
||||
* @param $key
|
||||
* @return string|null
|
||||
*/
|
||||
static function getRequiredMySqlVariables($key){
|
||||
$f3 = \Base::instance();
|
||||
$requiredMySqlVarKey = 'REQUIREMENTS[MYSQL][VARS][' . $key . ']';
|
||||
$data = null;
|
||||
|
||||
if( $f3->exists($requiredMySqlVarKey) ){
|
||||
$data = $f3->get($requiredMySqlVarKey);
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -8,42 +8,73 @@
|
||||
|
||||
namespace controller;
|
||||
use DB;
|
||||
use lib\Config;
|
||||
use Lib\Logging\MapLog;
|
||||
use Model\ActivityLogModel;
|
||||
use Model\BasicModel;
|
||||
|
||||
class LogController extends \Prefab {
|
||||
|
||||
const CACHE_KEY_ACTIVITY_COLUMNS = 'CACHED_ACTIVITY_COLUMNS';
|
||||
const CACHE_TTL_ACTIVITY_COLUMNS = 300;
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
protected $activityLogColumns = [];
|
||||
|
||||
/**
|
||||
* buffered activity log data for this singleton LogController() class
|
||||
* -> this buffered data can be stored somewhere (e.g. DB) before HTTP response
|
||||
* -> should be cleared afterwards!
|
||||
* @var array
|
||||
*/
|
||||
protected $activityLogBuffer = [];
|
||||
protected $activityLogBuffer = [];
|
||||
|
||||
/**
|
||||
* reserve a "new" character activity for logging
|
||||
* @param $characterId
|
||||
* @param $mapId
|
||||
* @param $action
|
||||
* get columns from ActivityLogModel that can be uses as counter
|
||||
* @return array
|
||||
*/
|
||||
public function bufferActivity($characterId, $mapId, $action){
|
||||
$characterId = (int)$characterId;
|
||||
$mapId = (int)$mapId;
|
||||
protected function getActivityLogColumns(): array{
|
||||
if(empty($this->activityLogColumns)){
|
||||
$f3 = \Base::instance();
|
||||
if(!$f3->exists(self::CACHE_KEY_ACTIVITY_COLUMNS, $this->activityLogColumns)){
|
||||
/**
|
||||
* @var $activityLogModel ActivityLogModel
|
||||
*/
|
||||
$activityLogModel = BasicModel::getNew('ActivityLogModel');
|
||||
$this->activityLogColumns = $activityLogModel->getCountableColumnNames();
|
||||
$f3->set(self::CACHE_KEY_ACTIVITY_COLUMNS, self::CACHE_TTL_ACTIVITY_COLUMNS);
|
||||
}
|
||||
}
|
||||
|
||||
if(
|
||||
$characterId > 0 &&
|
||||
$mapId > 0
|
||||
){
|
||||
$key = $this->getBufferedActivityKey($characterId, $mapId);
|
||||
return $this->activityLogColumns;
|
||||
}
|
||||
|
||||
if( is_null($key) ){
|
||||
$activity = [
|
||||
'characterId' => $characterId,
|
||||
'mapId' => $mapId,
|
||||
$action => 1
|
||||
];
|
||||
$this->activityLogBuffer[] = $activity;
|
||||
}else{
|
||||
$this->activityLogBuffer[$key][$action]++;
|
||||
/**
|
||||
* buffered activity log data for this singleton LogController() class
|
||||
* -> this buffered data can be stored somewhere (e.g. DB) before HTTP response
|
||||
* -> should be cleared afterwards!
|
||||
* @param MapLog $log
|
||||
*/
|
||||
public function push(MapLog $log){
|
||||
$action = $log->getAction();
|
||||
|
||||
// check $action to be valid (table column exists)
|
||||
if($action && in_array($action, $this->getActivityLogColumns())){
|
||||
if($mapId = $log->getChannelId()){
|
||||
$logData = $log->getData();
|
||||
if($characterId = (int)$logData['character']['id']){
|
||||
if($index = $this->getBufferedActivityIndex($characterId, $mapId)){
|
||||
$this->activityLogBuffer[$index][$action]++;
|
||||
}else{
|
||||
$this->activityLogBuffer[] = [
|
||||
'characterId' => $characterId,
|
||||
'mapId' => $mapId,
|
||||
$action => 1
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -51,7 +82,7 @@ class LogController extends \Prefab {
|
||||
/**
|
||||
* store all buffered activity log data to DB
|
||||
*/
|
||||
public function storeActivities(){
|
||||
public function logActivities(){
|
||||
if( !empty($this->activityLogBuffer) ){
|
||||
$db = DB\Database::instance()->getDB('PF');
|
||||
|
||||
@@ -104,24 +135,20 @@ class LogController extends \Prefab {
|
||||
}
|
||||
|
||||
/**
|
||||
* get array key from "buffered activity log" array
|
||||
* get array key/index from "buffered activity log" array
|
||||
* @param int $characterId
|
||||
* @param int $mapId
|
||||
* @return int|null
|
||||
* @return int
|
||||
*/
|
||||
private function getBufferedActivityKey($characterId, $mapId){
|
||||
$activityKey = null;
|
||||
|
||||
if(
|
||||
$characterId > 0 &&
|
||||
$mapId > 0
|
||||
){
|
||||
private function getBufferedActivityIndex(int $characterId, int $mapId): int {
|
||||
$activityKey = 0;
|
||||
if($characterId > 0 && $mapId > 0 ){
|
||||
foreach($this->activityLogBuffer as $key => $activityData){
|
||||
if(
|
||||
$activityData['characterId'] === $characterId &&
|
||||
$activityData['mapId'] === $mapId
|
||||
){
|
||||
$activityKey = $key;
|
||||
$activityKey = (int)$key;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -136,8 +163,7 @@ class LogController extends \Prefab {
|
||||
* @return \Log|null
|
||||
*/
|
||||
public static function getLogger($type){
|
||||
$f3 = \Base::instance();
|
||||
$logFiles = $f3->get('PATHFINDER.LOGFILES');
|
||||
$logFiles = Config::getPathfinderData('logfiles');
|
||||
|
||||
$logFileName = empty($logFiles[$type]) ? 'error' : $logFiles[$type];
|
||||
$logFile = $logFileName . '.log';
|
||||
|
||||
@@ -1,80 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: Exodus
|
||||
* Date: 30.08.2015
|
||||
* Time: 14:48
|
||||
*/
|
||||
|
||||
namespace controller;
|
||||
|
||||
class MailController extends \SMTP{
|
||||
|
||||
|
||||
public function __construct(){
|
||||
|
||||
$host = Controller::getEnvironmentData('SMTP_HOST');
|
||||
$port = Controller::getEnvironmentData('SMTP_PORT');
|
||||
$scheme = Controller::getEnvironmentData('SMTP_SCHEME');
|
||||
$user = Controller::getEnvironmentData('SMTP_USER');
|
||||
$pw = Controller::getEnvironmentData('SMTP_PASS');
|
||||
|
||||
parent::__construct($host,$port,$scheme,$user,$pw);
|
||||
|
||||
// error handling
|
||||
$this->set('Errors-to', '<' . Controller::getEnvironmentData('SMTP_ERROR') . '>');
|
||||
$this->set('MIME-Version', '1.0');
|
||||
$this->set('Content-Type', 'text/html; charset=ISO-8859-1');
|
||||
}
|
||||
|
||||
/**
|
||||
* send mail to removed user account
|
||||
* @param $to
|
||||
* @param $msg
|
||||
* @return bool
|
||||
*/
|
||||
public function sendDeleteAccount($to, $msg){
|
||||
$status = false;
|
||||
|
||||
if( !empty($to)){
|
||||
$this->set('To', '<' . $to . '>');
|
||||
$this->set('From', '"Pathfinder" <' . Controller::getEnvironmentData('SMTP_FROM') . '>');
|
||||
$this->set('Subject', 'Account deleted');
|
||||
$status = $this->send($msg);
|
||||
}
|
||||
|
||||
return $status;
|
||||
}
|
||||
|
||||
/**
|
||||
* send notification mail for new rally point systems
|
||||
* @param $to
|
||||
* @param $msg
|
||||
* @return bool
|
||||
*/
|
||||
public function sendRallyPoint($to, $msg){
|
||||
$status = false;
|
||||
|
||||
if( !empty($to)){
|
||||
$this->set('To', '<' . $to . '>');
|
||||
$this->set('From', '"Pathfinder" <' . Controller::getEnvironmentData('SMTP_FROM') . '>');
|
||||
$this->set('Subject', 'PATHFINDER - New rally point');
|
||||
$status = $this->send($msg);
|
||||
}
|
||||
|
||||
return $status;
|
||||
}
|
||||
|
||||
public function send($message, $log = true, $mock = false){
|
||||
$status = false;
|
||||
|
||||
if(
|
||||
!empty($this->host) &&
|
||||
!empty($this->port)
|
||||
){
|
||||
$status = parent::send($message, $log, $mock);
|
||||
}
|
||||
|
||||
return $status;
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,7 @@ use DB;
|
||||
use DB\SQL;
|
||||
use DB\SQL\MySQL as MySQL;
|
||||
use lib\Config;
|
||||
use Lib\Util;
|
||||
use Model;
|
||||
|
||||
class Setup extends Controller {
|
||||
@@ -26,10 +27,10 @@ class Setup extends Controller {
|
||||
'BASE',
|
||||
'URL',
|
||||
'DEBUG',
|
||||
'DB_DNS',
|
||||
'DB_NAME',
|
||||
'DB_USER',
|
||||
'DB_PASS',
|
||||
'DB_PF_DNS',
|
||||
'DB_PF_NAME',
|
||||
'DB_PF_USER',
|
||||
'DB_PF_PASS',
|
||||
'DB_CCP_DNS',
|
||||
'DB_CCP_NAME',
|
||||
'DB_CCP_USER',
|
||||
@@ -95,15 +96,15 @@ class Setup extends Controller {
|
||||
],
|
||||
'tables' => []
|
||||
],
|
||||
/* WIP ...
|
||||
'UNIVERSE' => [
|
||||
'info' => [],
|
||||
'models' => [
|
||||
'Model\Universe\RegionModel',
|
||||
'Model\Universe\ConstellationModel'
|
||||
'Model\Universe\TypeModel',
|
||||
//'Model\Universe\RegionModel',
|
||||
//'Model\Universe\ConstellationModel'
|
||||
],
|
||||
'tables' => []
|
||||
], */
|
||||
],
|
||||
'CCP' => [
|
||||
'info' => [],
|
||||
'models' => [],
|
||||
@@ -120,19 +121,28 @@ class Setup extends Controller {
|
||||
]
|
||||
];
|
||||
|
||||
/**
|
||||
* @var DB\Database
|
||||
*/
|
||||
protected $dbLib = null;
|
||||
|
||||
/**
|
||||
* database error
|
||||
* @var bool
|
||||
*/
|
||||
protected $databaseCheck = true;
|
||||
protected $databaseHasError = false;
|
||||
|
||||
/**
|
||||
* event handler for all "views"
|
||||
* some global template variables are set in here
|
||||
* @param \Base $f3
|
||||
* @param array $params
|
||||
* @return bool
|
||||
*/
|
||||
function beforeroute(\Base $f3, $params) {
|
||||
function beforeroute(\Base $f3, $params): bool {
|
||||
// init dbLib class. Manages all DB connections
|
||||
$this->dbLib = DB\Database::instance();
|
||||
|
||||
// page title
|
||||
$f3->set('tplPageTitle', 'Setup | ' . Config::getPathfinderData('name'));
|
||||
|
||||
@@ -144,8 +154,13 @@ class Setup extends Controller {
|
||||
|
||||
// js path (build/minified or raw uncompressed files)
|
||||
$f3->set('tplPathJs', 'public/js/' . Config::getPathfinderData('version') );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Base $f3
|
||||
*/
|
||||
public function afterroute(\Base $f3) {
|
||||
// js view (file)
|
||||
$f3->set('tplJsView', 'setup');
|
||||
@@ -159,6 +174,16 @@ class Setup extends Controller {
|
||||
return $cacheType;
|
||||
});
|
||||
|
||||
// simple counter (called within template)
|
||||
$counter = 0;
|
||||
$f3->set('tplCounter', function(string $action = 'add') use (&$counter){
|
||||
switch($action){
|
||||
case 'add': $counter++; break;
|
||||
case 'get': return $counter; break;
|
||||
case 'reset': $counter = 0; break;
|
||||
}
|
||||
});
|
||||
|
||||
// render view
|
||||
echo \Template::instance()->render( Config::getPathfinderData('view.index') );
|
||||
}
|
||||
@@ -175,26 +200,31 @@ class Setup extends Controller {
|
||||
// enables automatic column fix
|
||||
$fixColumns = false;
|
||||
|
||||
// bootstrap database from model class definition
|
||||
if( !empty($params['db']) ){
|
||||
$this->bootstrapDB($params['db']);
|
||||
|
||||
// reload page
|
||||
// -> remove GET param
|
||||
$f3->reroute('@setup');
|
||||
return;
|
||||
}elseif( !empty($params['fixCols']) ){
|
||||
$fixColumns = true;
|
||||
}elseif( !empty($params['buildIndex']) ){
|
||||
$this->setupSystemJumpTable();
|
||||
}elseif( !empty($params['importTable']) ){
|
||||
$this->importTable($params['importTable']);
|
||||
}elseif( !empty($params['exportTable']) ){
|
||||
$this->exportTable($params['exportTable']);
|
||||
}elseif( !empty($params['clearCache']) ){
|
||||
$this->clearCache($f3);
|
||||
}elseif( !empty($params['invalidateCookies']) ){
|
||||
$this->invalidateCookies($f3);
|
||||
switch($params['action']){
|
||||
case 'createDB':
|
||||
$this->createDB($params['db']);
|
||||
break;
|
||||
case 'bootstrapDB':
|
||||
$this->bootstrapDB($params['db']);
|
||||
break;
|
||||
case 'fixCols':
|
||||
$fixColumns = true;
|
||||
break;
|
||||
case 'buildIndex':
|
||||
$this->setupSystemJumpTable();
|
||||
break;
|
||||
case 'importTable':
|
||||
$this->importTable($params['model']);
|
||||
break;
|
||||
case 'exportTable':
|
||||
$this->exportTable($params['model']);
|
||||
break;
|
||||
case 'clearCache':
|
||||
$this->clearCache($f3);
|
||||
break;
|
||||
case 'invalidateCookies':
|
||||
$this->invalidateCookies($f3);
|
||||
break;
|
||||
}
|
||||
|
||||
// set template data ----------------------------------------------------------------
|
||||
@@ -207,6 +237,12 @@ class Setup extends Controller {
|
||||
// set requirement check information
|
||||
$f3->set('checkRequirements', $this->checkRequirements($f3));
|
||||
|
||||
// set php config check information
|
||||
$f3->set('checkPHPConfig', $this->checkPHPConfig($f3));
|
||||
|
||||
// set map default config
|
||||
$f3->set('mapsDefaultConfig', $this->getMapsDefaultConfig($f3));
|
||||
|
||||
// set database connection information
|
||||
$f3->set('checkDatabase', $this->checkDatabase($f3, $fixColumns));
|
||||
|
||||
@@ -320,9 +356,9 @@ class Setup extends Controller {
|
||||
protected function getEnvironmentInformation(\Base $f3){
|
||||
$environmentData = [];
|
||||
// exclude some sensitive data (e.g. database, passwords)
|
||||
$excludeVars = ['DB_DNS', 'DB_NAME', 'DB_USER',
|
||||
'DB_PASS', 'DB_CCP_DNS', 'DB_CCP_NAME',
|
||||
'DB_CCP_USER', 'DB_CCP_PASS'
|
||||
$excludeVars = [
|
||||
'DB_PF_DNS', 'DB_PF_NAME', 'DB_PF_USER', 'DB_PF_PASS',
|
||||
'DB_CCP_DNS', 'DB_CCP_NAME', 'DB_CCP_USER', 'DB_CCP_PASS'
|
||||
];
|
||||
|
||||
// obscure some values
|
||||
@@ -338,10 +374,7 @@ class Setup extends Controller {
|
||||
$check = false;
|
||||
$value = '[missing]';
|
||||
}elseif( in_array($var, $obscureVars)){
|
||||
$length = strlen($value);
|
||||
$hideChars = ($length < 10) ? $length : 10;
|
||||
$value = substr_replace($value, str_repeat('.', 3), -$hideChars);
|
||||
$value .= ' [' . $length . ']';
|
||||
$value = Util::obscureString($value);
|
||||
}
|
||||
|
||||
$environmentData[$var] = [
|
||||
@@ -444,6 +477,9 @@ class Setup extends Controller {
|
||||
'version' => (PHP_INT_SIZE * 8) . '-bit',
|
||||
'check' => $f3->get('REQUIREMENTS.PHP.PHP_INT_SIZE') == PHP_INT_SIZE
|
||||
],
|
||||
[
|
||||
'label' => 'PHP extensions'
|
||||
],
|
||||
'pcre' => [
|
||||
'label' => 'PCRE',
|
||||
'required' => $f3->get('REQUIREMENTS.PHP.PCRE_VERSION'),
|
||||
@@ -486,20 +522,6 @@ class Setup extends Controller {
|
||||
'version' => (extension_loaded('curl') && function_exists('curl_version')) ? 'installed' : 'missing',
|
||||
'check' => (extension_loaded('curl') && function_exists('curl_version'))
|
||||
],
|
||||
'maxInputVars' => [
|
||||
'label' => 'max_input_vars',
|
||||
'required' => $f3->get('REQUIREMENTS.PHP.MAX_INPUT_VARS'),
|
||||
'version' => ini_get('max_input_vars'),
|
||||
'check' => ini_get('max_input_vars') >= $f3->get('REQUIREMENTS.PHP.MAX_INPUT_VARS'),
|
||||
'tooltip' => 'PHP default = 1000. Increase it in order to import larger maps.'
|
||||
],
|
||||
'maxExecutionTime' => [
|
||||
'label' => 'max_execution_time',
|
||||
'required' => $f3->get('REQUIREMENTS.PHP.MAX_EXECUTION_TIME'),
|
||||
'version' => ini_get('max_execution_time'),
|
||||
'check' => ini_get('max_execution_time') >= $f3->get('REQUIREMENTS.PHP.MAX_EXECUTION_TIME'),
|
||||
'tooltip' => 'PHP default = 30. Max execution time for PHP scripts.'
|
||||
],
|
||||
[
|
||||
'label' => 'Redis Server [optional]'
|
||||
],
|
||||
@@ -594,6 +616,125 @@ class Setup extends Controller {
|
||||
return $checkRequirements;
|
||||
}
|
||||
|
||||
/**
|
||||
* check PHP config (php.ini)
|
||||
* @param \Base $f3
|
||||
* @return array
|
||||
*/
|
||||
protected function checkPHPConfig(\Base $f3): array {
|
||||
$phpConfig = [
|
||||
'maxInputVars' => [
|
||||
'label' => 'max_input_vars',
|
||||
'required' => $f3->get('REQUIREMENTS.PHP.MAX_INPUT_VARS'),
|
||||
'version' => ini_get('max_input_vars'),
|
||||
'check' => ini_get('max_input_vars') >= $f3->get('REQUIREMENTS.PHP.MAX_INPUT_VARS'),
|
||||
'tooltip' => 'PHP default = 1000. Increase it in order to import larger maps.'
|
||||
],
|
||||
'maxExecutionTime' => [
|
||||
'label' => 'max_execution_time',
|
||||
'required' => $f3->get('REQUIREMENTS.PHP.MAX_EXECUTION_TIME'),
|
||||
'version' => ini_get('max_execution_time'),
|
||||
'check' => ini_get('max_execution_time') >= $f3->get('REQUIREMENTS.PHP.MAX_EXECUTION_TIME'),
|
||||
'tooltip' => 'PHP default = 30. Max execution time for PHP scripts.'
|
||||
],
|
||||
'htmlErrors' => [
|
||||
'label' => 'html_errors',
|
||||
'required' => $f3->get('REQUIREMENTS.PHP.HTML_ERRORS'),
|
||||
'version' => (int)ini_get('html_errors'),
|
||||
'check' => (bool)ini_get('html_errors') == (bool)$f3->get('REQUIREMENTS.PHP.HTML_ERRORS'),
|
||||
'tooltip' => 'Formatted HTML StackTrace on error.'
|
||||
],
|
||||
[
|
||||
'label' => 'Session'
|
||||
],
|
||||
'sessionSaveHandler' => [
|
||||
'label' => 'save_handler',
|
||||
'version' => ini_get('session.save_handler'),
|
||||
'check' => true,
|
||||
'tooltip' => 'PHP Session save handler (Redis is preferred).'
|
||||
],
|
||||
'sessionSavePath' => [
|
||||
'label' => 'session.save_path',
|
||||
'version' => ini_get('session.save_path'),
|
||||
'check' => true,
|
||||
'tooltip' => 'PHP Session save path (Redis is preferred).'
|
||||
],
|
||||
'sessionName' => [
|
||||
'label' => 'session.name',
|
||||
'version' => ini_get('session.name'),
|
||||
'check' => true,
|
||||
'tooltip' => 'PHP Session name.'
|
||||
]
|
||||
];
|
||||
|
||||
return $phpConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* get default map config
|
||||
* @param \Base $f3
|
||||
* @return array
|
||||
*/
|
||||
protected function getMapsDefaultConfig(\Base $f3): array {
|
||||
$matrix = \Matrix::instance();
|
||||
$mapsDefaultConfig = (array)Config::getMapsDefaultConfig();
|
||||
$matrix->transpose($mapsDefaultConfig);
|
||||
|
||||
$mapConfig = ['mapTypes' => array_keys(reset($mapsDefaultConfig))];
|
||||
|
||||
foreach($mapsDefaultConfig as $option => $defaultConfig){
|
||||
$tooltip = '';
|
||||
switch($option){
|
||||
case 'lifetime':
|
||||
$label = 'Map lifetime (days)';
|
||||
$tooltip = 'Unchanged/inactive maps get auto deleted afterwards (cronjob).';
|
||||
break;
|
||||
case 'max_count':
|
||||
$label = 'Max. maps count/user';
|
||||
break;
|
||||
case 'max_shared':
|
||||
$label = 'Map share limit/map';
|
||||
$tooltip = 'E.g. A Corp map can be shared with X other corps.';
|
||||
break;
|
||||
case 'max_systems':
|
||||
$label = 'Max. systems count/map';
|
||||
break;
|
||||
case 'log_activity_enabled':
|
||||
$label = '<i class="fa fa-fw fa-bar-chart"></i> Activity statistics';
|
||||
$tooltip = 'If "enabled", map admins can enable user statistics for a map.';
|
||||
break;
|
||||
case 'log_history_enabled':
|
||||
$label = '<i class="fa fa-fw fa-file-text"></i> History log files';
|
||||
$tooltip = 'If "enabled", map admins can pipe map logs to file. (one file per map)';
|
||||
break;
|
||||
case 'send_history_slack_enabled':
|
||||
$label = '<i class="fa fa-fw fa-slack"></i> History log Slack';
|
||||
$tooltip = 'If "enabled", map admins can set a Slack channel were map logs get piped to.';
|
||||
break;
|
||||
case 'send_rally_slack_enabled':
|
||||
$label = '<i class="fa fa-fw fa-slack"></i> Rally point poke Slack';
|
||||
$tooltip = 'If "enabled", map admins can set a Slack channel for rally point pokes.';
|
||||
break;
|
||||
case 'send_rally_mail_enabled':
|
||||
$label = '<i class="fa fa-fw fa-envelope"></i> Rally point poke Email';
|
||||
$tooltip = 'If "enabled", rally point pokes can be send by Email (SMTP config + recipient address required).';
|
||||
break;
|
||||
default:
|
||||
$label = 'unknown';
|
||||
}
|
||||
|
||||
$mapsDefaultConfig[$option] = [
|
||||
'label' => $label,
|
||||
'tooltip' => $tooltip,
|
||||
'data' => $defaultConfig
|
||||
];
|
||||
}
|
||||
|
||||
$mapConfig['mapConfig'] = $mapsDefaultConfig;
|
||||
|
||||
return $mapConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* get database connection information
|
||||
* @param \Base $f3
|
||||
@@ -611,7 +752,9 @@ class Setup extends Controller {
|
||||
$dbConnected = false;
|
||||
// DB type (e.g. MySql,..)
|
||||
$dbDriver = 'unknown';
|
||||
// enable database ::setup() function in UI
|
||||
// enable database ::create() function on UI
|
||||
$dbCreate = false;
|
||||
// enable database ::setup() function on UI
|
||||
$dbSetupEnable = false;
|
||||
// check of everything is OK (connection, tables, columns, indexes,..)
|
||||
$dbStatusCheckCount = 0;
|
||||
@@ -622,7 +765,9 @@ class Setup extends Controller {
|
||||
// get DB config
|
||||
$dbConfigValues = Config::getDatabaseConfig($dbKey);
|
||||
// check DB for valid connection
|
||||
$db = DB\Database::instance()->getDB($dbKey);
|
||||
$db = $this->dbLib->getDB($dbKey);
|
||||
// collection for errors
|
||||
$dbErrors = [];
|
||||
|
||||
// check config that does NOT require a valid DB connection
|
||||
switch($dbKey){
|
||||
@@ -631,8 +776,9 @@ class Setup extends Controller {
|
||||
case 'CCP': $dbLabel = 'EVE-Online [SDE]'; break;
|
||||
}
|
||||
|
||||
$dbName = $dbConfigValues['NAME'];
|
||||
$dbUser = $dbConfigValues['USER'];
|
||||
$dbName = $dbConfigValues['NAME'];
|
||||
$dbUser = $dbConfigValues['USER'];
|
||||
$dbAlias = $dbConfigValues['ALIAS'];
|
||||
|
||||
if($db){
|
||||
switch($dbKey){
|
||||
@@ -858,27 +1004,47 @@ class Setup extends Controller {
|
||||
}else{
|
||||
// DB connection failed
|
||||
$dbStatusCheckCount++;
|
||||
|
||||
foreach($this->dbLib->getErrors($dbAlias, 10) as $dbException){
|
||||
$dbErrors[] = $dbException->getMessage();
|
||||
}
|
||||
|
||||
// try to connect without! DB (-> offer option to create them)
|
||||
// do not log errors (silent)
|
||||
$this->dbLib->setSilent(true);
|
||||
$dbServer = $this->dbLib->connectToServer($dbAlias);
|
||||
$this->dbLib->setSilent(false);
|
||||
if(!is_null($dbServer)){
|
||||
// connection succeeded
|
||||
$dbCreate = true;
|
||||
$dbDriver = $dbServer->driver();
|
||||
}
|
||||
}
|
||||
|
||||
if($dbStatusCheckCount !== 0){
|
||||
$this->databaseCheck = false;
|
||||
$this->databaseHasError = true;
|
||||
}
|
||||
|
||||
// sort tables for better readability
|
||||
ksort($requiredTables);
|
||||
|
||||
$this->databases[$dbKey]['info'] = [
|
||||
'db' => $db,
|
||||
'label' => $dbLabel,
|
||||
'driver' => $dbDriver,
|
||||
'name' => $dbName,
|
||||
'user' => $dbUser,
|
||||
'dbConfig' => $dbConfig,
|
||||
'setupEnable' => $dbSetupEnable,
|
||||
'connected' => $dbConnected,
|
||||
'statusCheckCount' => $dbStatusCheckCount,
|
||||
'columnQueries' => $dbColumnQueries,
|
||||
'tableData' => $requiredTables
|
||||
// 'db' => $db,
|
||||
'label' => $dbLabel,
|
||||
'host' => Config::getDatabaseDNSValue((string)$dbConfigValues['DNS'], 'host'),
|
||||
'port' => Config::getDatabaseDNSValue((string)$dbConfigValues['DNS'], 'port'),
|
||||
'driver' => $dbDriver,
|
||||
'name' => $dbName,
|
||||
'user' => $dbUser,
|
||||
'pass' => Util::obscureString((string)$dbConfigValues['PASS'], 8),
|
||||
'dbConfig' => $dbConfig,
|
||||
'dbCreate' => $dbCreate,
|
||||
'setupEnable' => $dbSetupEnable,
|
||||
'connected' => $dbConnected,
|
||||
'statusCheckCount' => $dbStatusCheckCount,
|
||||
'columnQueries' => $dbColumnQueries,
|
||||
'tableData' => $requiredTables,
|
||||
'errors' => $dbErrors
|
||||
];
|
||||
}
|
||||
|
||||
@@ -941,24 +1107,46 @@ class Setup extends Controller {
|
||||
return $dbConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* try to create a fresh database
|
||||
* @param string $dbKey
|
||||
*/
|
||||
protected function createDB(string $dbKey){
|
||||
// check for valid key
|
||||
if(!empty($this->databases[$dbKey])){
|
||||
// disable logging (we expect the DB connect to fail -> no db created)
|
||||
$this->dbLib->setSilent(true);
|
||||
// try to connect
|
||||
$db = $this->dbLib->getDB($dbKey);
|
||||
// enable logging
|
||||
$this->dbLib->setSilent(false, true);
|
||||
if(is_null($db)){
|
||||
// try create new db
|
||||
$db = $this->dbLib->createDB($dbKey);
|
||||
if(is_null($db)){
|
||||
foreach($this->dbLib->getErrors($dbKey, 5) as $error){
|
||||
// ... no further error handling here -> check log files
|
||||
//$error->getMessage()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* init the complete database
|
||||
* - create tables
|
||||
* - create indexes
|
||||
* - set default static values
|
||||
* @param $dbKey
|
||||
* @param string $dbKey
|
||||
* @return array
|
||||
*/
|
||||
protected function bootstrapDB($dbKey){
|
||||
$db = DB\Database::instance()->getDB($dbKey);
|
||||
|
||||
protected function bootstrapDB(string $dbKey){
|
||||
$db = $this->dbLib->getDB($dbKey);
|
||||
$checkTables = [];
|
||||
if($db){
|
||||
// set/change default "character set" and "collation"
|
||||
$db->exec('ALTER DATABASE ' . $db->quotekey($db->name())
|
||||
. ' CHARACTER SET ' . self::getRequiredMySqlVariables('CHARACTER_SET_DATABASE')
|
||||
. ' COLLATE ' . self::getRequiredMySqlVariables('COLLATION_DATABASE')
|
||||
);
|
||||
// set some default config for this database
|
||||
DB\Database::prepareDatabase($db);
|
||||
|
||||
// setup tables
|
||||
foreach($this->databases[$dbKey]['models'] as $modelClass){
|
||||
@@ -976,10 +1164,10 @@ class Setup extends Controller {
|
||||
// $ttl for health check
|
||||
$ttl = 600;
|
||||
|
||||
$heachCheckToken = microtime(true);
|
||||
$healthCheckToken = microtime(true);
|
||||
|
||||
// ping TCP Socket with checkToken
|
||||
self::checkTcpSocket($ttl, $heachCheckToken);
|
||||
self::checkTcpSocket($ttl, $healthCheckToken);
|
||||
|
||||
$socketInformation = [
|
||||
'tcpSocket' => [
|
||||
@@ -1004,7 +1192,7 @@ class Setup extends Controller {
|
||||
'check' => !empty( $ttl )
|
||||
]
|
||||
],
|
||||
'token' => $heachCheckToken
|
||||
'token' => $healthCheckToken
|
||||
],
|
||||
'webSocket' => [
|
||||
'label' => 'WebSocket (clients) [HTTP]',
|
||||
@@ -1026,78 +1214,77 @@ class Setup extends Controller {
|
||||
* @return array
|
||||
*/
|
||||
protected function getIndexData(){
|
||||
|
||||
// active DB and tables are required for obtain index data
|
||||
if( $this->databaseCheck ){
|
||||
if(!$this->databaseHasError){
|
||||
$indexInfo = [
|
||||
'SystemNeighbourModel' => [
|
||||
'action' => [
|
||||
'task' => [
|
||||
[
|
||||
'task' => 'buildIndex',
|
||||
'action' => 'buildIndex',
|
||||
'label' => 'build',
|
||||
'icon' => 'fa-refresh',
|
||||
'btn' => 'btn-primary'
|
||||
]
|
||||
],
|
||||
'table' => Model\BasicModel::getNew('SystemNeighbourModel')->getTable(),
|
||||
'count' => DB\Database::instance()->getRowCount( Model\BasicModel::getNew('SystemNeighbourModel')->getTable() )
|
||||
'count' => $this->dbLib->getRowCount( Model\BasicModel::getNew('SystemNeighbourModel')->getTable() )
|
||||
],
|
||||
'WormholeModel' => [
|
||||
'action' => [
|
||||
'task' => [
|
||||
[
|
||||
'task' => 'exportTable',
|
||||
'action' => 'exportTable',
|
||||
'label' => 'export',
|
||||
'icon' => 'fa-download',
|
||||
'btn' => 'btn-default'
|
||||
],[
|
||||
'task' => 'importTable',
|
||||
'action' => 'importTable',
|
||||
'label' => 'import',
|
||||
'icon' => 'fa-upload',
|
||||
'btn' => 'btn-primary'
|
||||
]
|
||||
],
|
||||
'table' => Model\BasicModel::getNew('WormholeModel')->getTable(),
|
||||
'count' => DB\Database::instance()->getRowCount( Model\BasicModel::getNew('WormholeModel')->getTable() )
|
||||
'count' => $this->dbLib->getRowCount( Model\BasicModel::getNew('WormholeModel')->getTable() )
|
||||
],
|
||||
'SystemWormholeModel' => [
|
||||
'action' => [
|
||||
'task' => [
|
||||
[
|
||||
'task' => 'exportTable',
|
||||
'action' => 'exportTable',
|
||||
'label' => 'export',
|
||||
'icon' => 'fa-download',
|
||||
'btn' => 'btn-default'
|
||||
],[
|
||||
'task' => 'importTable',
|
||||
'action' => 'importTable',
|
||||
'label' => 'import',
|
||||
'icon' => 'fa-upload',
|
||||
'btn' => 'btn-primary'
|
||||
]
|
||||
],
|
||||
'table' => Model\BasicModel::getNew('SystemWormholeModel')->getTable(),
|
||||
'count' => DB\Database::instance()->getRowCount( Model\BasicModel::getNew('SystemWormholeModel')->getTable() )
|
||||
'count' => $this->dbLib->getRowCount( Model\BasicModel::getNew('SystemWormholeModel')->getTable() )
|
||||
],
|
||||
'ConstellationWormholeModel' => [
|
||||
'action' => [
|
||||
'task' => [
|
||||
[
|
||||
'task' => 'exportTable',
|
||||
'action' => 'exportTable',
|
||||
'label' => 'export',
|
||||
'icon' => 'fa-download',
|
||||
'btn' => 'btn-default'
|
||||
],[
|
||||
'task' => 'importTable',
|
||||
'action' => 'importTable',
|
||||
'label' => 'import',
|
||||
'icon' => 'fa-upload',
|
||||
'btn' => 'btn-primary'
|
||||
]
|
||||
],
|
||||
'table' => Model\BasicModel::getNew('ConstellationWormholeModel')->getTable(),
|
||||
'count' => DB\Database::instance()->getRowCount( Model\BasicModel::getNew('ConstellationWormholeModel')->getTable() )
|
||||
'count' => $this->dbLib->getRowCount( Model\BasicModel::getNew('ConstellationWormholeModel')->getTable() )
|
||||
]
|
||||
];
|
||||
}else{
|
||||
$indexInfo = [
|
||||
'SystemNeighbourModel' => [
|
||||
'action' => [],
|
||||
'task' => [],
|
||||
'table' => 'Fix database errors first!'
|
||||
]
|
||||
];
|
||||
@@ -1178,7 +1365,7 @@ class Setup extends Controller {
|
||||
|
||||
/**
|
||||
* import table data from existing dump file (e.g *.csv)
|
||||
* @param $modelClass
|
||||
* @param string $modelClass
|
||||
* @return bool
|
||||
* @throws \Exception
|
||||
*/
|
||||
@@ -1189,7 +1376,7 @@ class Setup extends Controller {
|
||||
|
||||
/**
|
||||
* export table data
|
||||
* @param $modelClass
|
||||
* @param string $modelClass
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function exportTable($modelClass){
|
||||
|
||||
@@ -12,11 +12,25 @@ use data\filesystem\Search;
|
||||
|
||||
class Cache {
|
||||
|
||||
const LOG_TEXT = '%s [%\'_10s] files, size [%\'_10s] byte, not writable [%\'_10s] files, errors [%\'_10s], exec (%.3Fs)';
|
||||
const LOG_TEXT = '%s [%\'_10s] files, size [%\'_10s] byte, not writable [%\'_10s] files, errors [%\'_10s], exec (%.3Fs)';
|
||||
|
||||
/**
|
||||
* default max expire for files (seconds)
|
||||
*/
|
||||
const CACHE_EXPIRE_MAX = 864000;
|
||||
|
||||
/**
|
||||
* @param \Base $f3
|
||||
* @return int
|
||||
*/
|
||||
protected function getExpireMaxTime(\Base $f3): int {
|
||||
$expireTime = (int)$f3->get('PATHFINDER.CACHE.EXPIRE_MAX');
|
||||
return ($expireTime >= 0) ? $expireTime : self::CACHE_EXPIRE_MAX;
|
||||
}
|
||||
|
||||
/**
|
||||
* clear expired cached files
|
||||
* >> >php index.php "/cron/deleteExpiredCacheData"
|
||||
* >> php index.php "/cron/deleteExpiredCacheData"
|
||||
* @param \Base $f3
|
||||
*/
|
||||
function deleteExpiredData(\Base $f3){
|
||||
@@ -25,8 +39,8 @@ class Cache {
|
||||
// cache dir (dir is recursively searched...)
|
||||
$cacheDir = $f3->get('TEMP');
|
||||
|
||||
$filterTime = (int)strtotime('-' . $f3->get('PATHFINDER.CACHE.EXPIRE_MAX') . ' seconds');
|
||||
$expiredFiles = Search::getFilesByMTime($cacheDir, $filterTime);
|
||||
$filterTime = (int)strtotime('-' . $this->getExpireMaxTime($f3) . ' seconds');
|
||||
$expiredFiles = Search::getFilesByMTime($cacheDir, $filterTime, Search::DEFAULT_FILE_LIMIT);
|
||||
|
||||
$deletedFiles = 0;
|
||||
$deletedSize = 0;
|
||||
@@ -36,16 +50,18 @@ class Cache {
|
||||
/**
|
||||
* @var $file \SplFileInfo
|
||||
*/
|
||||
if( $file->isWritable() ){
|
||||
$tmpSize = $file->getSize();
|
||||
if( unlink($file->getRealPath()) ){
|
||||
$deletedSize += $tmpSize;
|
||||
$deletedFiles++;
|
||||
if($file->isFile()){
|
||||
if( $file->isWritable() ){
|
||||
$tmpSize = $file->getSize();
|
||||
if( unlink($file->getRealPath()) ){
|
||||
$deletedSize += $tmpSize;
|
||||
$deletedFiles++;
|
||||
}else{
|
||||
$deleteErrors++;
|
||||
}
|
||||
}else{
|
||||
$deleteErrors++;
|
||||
$notWritableFiles++;
|
||||
}
|
||||
}else{
|
||||
$notWritableFiles++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
namespace cron;
|
||||
use DB;
|
||||
use lib\Config;
|
||||
use Model;
|
||||
|
||||
class MapUpdate {
|
||||
@@ -23,19 +24,20 @@ class MapUpdate {
|
||||
* @param \Base $f3
|
||||
*/
|
||||
function deactivateMapData(\Base $f3){
|
||||
$privateMapLifetime = (int)$f3->get('PATHFINDER.MAP.PRIVATE.LIFETIME');
|
||||
$privateMapLifetime = (int)Config::getMapsDefaultConfig('private.lifetime');
|
||||
|
||||
if($privateMapLifetime > 0){
|
||||
$pfDB = DB\Database::instance()->getDB('PF');
|
||||
|
||||
$sqlDeactivateExpiredMaps = "UPDATE map SET
|
||||
if($pfDB){
|
||||
$sqlDeactivateExpiredMaps = "UPDATE map SET
|
||||
active = 0
|
||||
WHERE
|
||||
map.active = 1 AND
|
||||
map.typeId = 2 AND
|
||||
TIMESTAMPDIFF(DAY, map.updated, NOW() ) > :lifetime";
|
||||
|
||||
$pfDB->exec($sqlDeactivateExpiredMaps, ['lifetime' => $privateMapLifetime]);
|
||||
$pfDB->exec($sqlDeactivateExpiredMaps, ['lifetime' => $privateMapLifetime]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,18 +47,31 @@ class MapUpdate {
|
||||
* @param \Base $f3
|
||||
*/
|
||||
function deleteMapData(\Base $f3){
|
||||
|
||||
$pfDB = DB\Database::instance()->getDB('PF');
|
||||
$deletedMapsCount = 0;
|
||||
|
||||
$sqlDeleteDisabledMaps = "DELETE FROM
|
||||
if($pfDB){
|
||||
$sqlDeleteDisabledMaps = "SELECT
|
||||
id
|
||||
FROM
|
||||
map
|
||||
WHERE
|
||||
map.active = 0 AND
|
||||
TIMESTAMPDIFF(DAY, map.updated, NOW() ) > :deletion_time";
|
||||
|
||||
$pfDB->exec($sqlDeleteDisabledMaps, ['deletion_time' => self::DAYS_UNTIL_MAP_DELETION]);
|
||||
$disabledMaps = $pfDB->exec($sqlDeleteDisabledMaps, ['deletion_time' => self::DAYS_UNTIL_MAP_DELETION]);
|
||||
|
||||
$deletedMapsCount = $pfDB->count();
|
||||
if($deletedMapsCount = $pfDB->count()){
|
||||
$mapModel = Model\BasicModel::getNew('MapModel');
|
||||
foreach($disabledMaps as $data){
|
||||
$mapModel->getById( (int)$data['id'], 3, false );
|
||||
if( !$mapModel->dry() ){
|
||||
$mapModel->erase();
|
||||
}
|
||||
$mapModel->reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Log ------------------------
|
||||
$log = new \Log('cron_' . __FUNCTION__ . '.log');
|
||||
@@ -73,8 +88,8 @@ class MapUpdate {
|
||||
|
||||
if($eolExpire > 0){
|
||||
$pfDB = DB\Database::instance()->getDB('PF');
|
||||
|
||||
$sql = "SELECT
|
||||
if($pfDB){
|
||||
$sql = "SELECT
|
||||
`con`.`id`
|
||||
FROM
|
||||
`connection` `con` INNER JOIN
|
||||
@@ -85,20 +100,21 @@ class MapUpdate {
|
||||
TIMESTAMPDIFF(SECOND, `con`.`eolUpdated`, NOW() ) > :expire_time
|
||||
";
|
||||
|
||||
$connectionsData = $pfDB->exec($sql, [
|
||||
'deleteEolConnections' => 1,
|
||||
'expire_time' => $eolExpire
|
||||
]);
|
||||
$connectionsData = $pfDB->exec($sql, [
|
||||
'deleteEolConnections' => 1,
|
||||
'expire_time' => $eolExpire
|
||||
]);
|
||||
|
||||
if($connectionsData){
|
||||
/**
|
||||
* @var $connection Model\ConnectionModel
|
||||
*/
|
||||
$connection = Model\BasicModel::getNew('ConnectionModel');
|
||||
foreach($connectionsData as $data){
|
||||
$connection->getById( (int)$data['id'] );
|
||||
if( !$connection->dry() ){
|
||||
$connection->erase();
|
||||
if($connectionsData){
|
||||
/**
|
||||
* @var $connection Model\ConnectionModel
|
||||
*/
|
||||
$connection = Model\BasicModel::getNew('ConnectionModel');
|
||||
foreach($connectionsData as $data){
|
||||
$connection->getById( (int)$data['id'] );
|
||||
if( !$connection->dry() ){
|
||||
$connection->erase();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -115,8 +131,8 @@ class MapUpdate {
|
||||
|
||||
if($whExpire > 0){
|
||||
$pfDB = DB\Database::instance()->getDB('PF');
|
||||
|
||||
$sql = "SELECT
|
||||
if($pfDB){
|
||||
$sql = "SELECT
|
||||
`con`.`id`
|
||||
FROM
|
||||
`connection` `con` INNER JOIN
|
||||
@@ -128,21 +144,22 @@ class MapUpdate {
|
||||
TIMESTAMPDIFF(SECOND, `con`.`created`, NOW() ) > :expire_time
|
||||
";
|
||||
|
||||
$connectionsData = $pfDB->exec($sql, [
|
||||
'deleteExpiredConnections' => 1,
|
||||
'scope' => 'wh',
|
||||
'expire_time' => $whExpire
|
||||
]);
|
||||
$connectionsData = $pfDB->exec($sql, [
|
||||
'deleteExpiredConnections' => 1,
|
||||
'scope' => 'wh',
|
||||
'expire_time' => $whExpire
|
||||
]);
|
||||
|
||||
if($connectionsData){
|
||||
/**
|
||||
* @var $connection Model\ConnectionModel
|
||||
*/
|
||||
$connection = Model\BasicModel::getNew('ConnectionModel');
|
||||
foreach($connectionsData as $data){
|
||||
$connection->getById( (int)$data['id'] );
|
||||
if( !$connection->dry() ){
|
||||
$connection->erase();
|
||||
if($connectionsData){
|
||||
/**
|
||||
* @var $connection Model\ConnectionModel
|
||||
*/
|
||||
$connection = Model\BasicModel::getNew('ConnectionModel');
|
||||
foreach($connectionsData as $data){
|
||||
$connection->getById( (int)$data['id'] );
|
||||
if( !$connection->dry() ){
|
||||
$connection->erase();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -159,8 +176,8 @@ class MapUpdate {
|
||||
|
||||
if($signatureExpire > 0){
|
||||
$pfDB = DB\Database::instance()->getDB('PF');
|
||||
|
||||
$sqlDeleteExpiredSignatures = "DELETE `sigs` FROM
|
||||
if($pfDB){
|
||||
$sqlDeleteExpiredSignatures = "DELETE `sigs` FROM
|
||||
`system_signature` `sigs` INNER JOIN
|
||||
`system` ON
|
||||
`system`.`id` = `sigs`.`systemId`
|
||||
@@ -169,7 +186,8 @@ class MapUpdate {
|
||||
TIMESTAMPDIFF(SECOND, `sigs`.`updated`, NOW() ) > :lifetime
|
||||
";
|
||||
|
||||
$pfDB->exec($sqlDeleteExpiredSignatures, ['lifetime' => $signatureExpire]);
|
||||
$pfDB->exec($sqlDeleteExpiredSignatures, ['lifetime' => $signatureExpire]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
90
app/main/data/file/filehandler.php
Normal file
90
app/main/data/file/filehandler.php
Normal file
@@ -0,0 +1,90 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: exodu
|
||||
* Date: 06.08.2017
|
||||
* Time: 18:47
|
||||
*/
|
||||
|
||||
namespace data\file;
|
||||
|
||||
|
||||
class FileHandler extends \Prefab {
|
||||
|
||||
const ERROR_STREAM_READABLE = 'Stream is not readable "%s"';
|
||||
|
||||
const LOG_FILE_OFFSET_MIN = 0;
|
||||
const LOG_FILE_OFFSET = 0;
|
||||
const LOG_FILE_OFFSET_MAX = 10000;
|
||||
const LOG_FILE_LIMIT_MIN = 1;
|
||||
const LOG_FILE_LIMIT = 100;
|
||||
const Log_File_LIMIT_MAX = 100;
|
||||
|
||||
/**
|
||||
* parse local log file from end to first line
|
||||
* -> Each row is a JSON object
|
||||
* @param string $sourceFile
|
||||
* @param int $offset
|
||||
* @param int $limit
|
||||
* @param null|callable $formatter
|
||||
* @return array
|
||||
*/
|
||||
public static function readLogFile(
|
||||
string $sourceFile,
|
||||
int $offset = self::LOG_FILE_OFFSET,
|
||||
int $limit = self::LOG_FILE_LIMIT,
|
||||
$formatter = null
|
||||
): array {
|
||||
$data = [];
|
||||
|
||||
if(is_file($sourceFile)){
|
||||
if(is_readable($sourceFile)){
|
||||
$file = new ReverseSplFileObject($sourceFile, $offset);
|
||||
$file->setFlags(\SplFileObject::DROP_NEW_LINE | \SplFileObject::READ_AHEAD | \SplFileObject::SKIP_EMPTY);
|
||||
|
||||
foreach( new \LimitIterator($file, 0, $limit) as $i => $rowData){
|
||||
if( !empty($rowDataObj = (array)json_decode($rowData, true)) ){
|
||||
if(is_callable($formatter)){
|
||||
$formatter($rowDataObj);
|
||||
}
|
||||
$data[] = $rowDataObj;
|
||||
}
|
||||
}
|
||||
}else{
|
||||
\Base::instance()->error(500, sprintf(self::ERROR_STREAM_READABLE, $sourceFile));
|
||||
}
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* validate offset
|
||||
* @param int $offset
|
||||
* @return int
|
||||
*/
|
||||
public static function validateOffset(int $offset): int{
|
||||
if(
|
||||
$offset < self::LOG_FILE_OFFSET_MIN ||
|
||||
$offset > self::LOG_FILE_OFFSET_MAX
|
||||
){
|
||||
$offset = self::LOG_FILE_OFFSET;
|
||||
}
|
||||
return $offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* validate limit
|
||||
* @param int $limit
|
||||
* @return int
|
||||
*/
|
||||
public static function validateLimit(int $limit): int{
|
||||
if(
|
||||
$limit < self::LOG_FILE_LIMIT_MIN ||
|
||||
$limit > self::Log_File_LIMIT_MAX
|
||||
){
|
||||
$limit = self::LOG_FILE_LIMIT;
|
||||
}
|
||||
return $limit;
|
||||
}
|
||||
}
|
||||
219
app/main/data/file/reversesplfileobject.php
Normal file
219
app/main/data/file/reversesplfileobject.php
Normal file
@@ -0,0 +1,219 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: exodu
|
||||
* Date: 06.08.2017
|
||||
* Time: 18:42
|
||||
*/
|
||||
|
||||
namespace data\file;
|
||||
|
||||
|
||||
class ReverseSplFileObject extends \SplFileObject{
|
||||
|
||||
/**
|
||||
* pointer position indicates file start
|
||||
* @var int
|
||||
*/
|
||||
protected $begin = 0;
|
||||
|
||||
/**
|
||||
* line offset from file end (1 => 'start with 2nd last line')
|
||||
* @var int
|
||||
*/
|
||||
protected $offset = 0;
|
||||
|
||||
/**
|
||||
* total lines found in file
|
||||
* @var int
|
||||
*/
|
||||
protected $lineCount = 0;
|
||||
|
||||
/**
|
||||
* empty lines found in file
|
||||
* @var int
|
||||
*/
|
||||
protected $lineCountEmpty = 0;
|
||||
|
||||
/**
|
||||
* current pointer position
|
||||
* @var int
|
||||
*/
|
||||
protected $pointer = 0;
|
||||
|
||||
/**
|
||||
* position increments when valid row data found
|
||||
* @var
|
||||
*/
|
||||
protected $position;
|
||||
|
||||
/**
|
||||
* control characters
|
||||
* @var array
|
||||
*/
|
||||
protected $eol = ["\r", "\n"];
|
||||
|
||||
public function __construct($sourceFile, $offset = 0){
|
||||
parent::__construct($sourceFile);
|
||||
|
||||
// set total line count of the file
|
||||
$this->setLineCount();
|
||||
|
||||
//Seek to the first position of the file and record its position
|
||||
//Should be 0
|
||||
$this->fseek(0);
|
||||
$this->begin = $this->ftell();
|
||||
$this->offset = $offset;
|
||||
|
||||
//Seek to the last position from the end of the file
|
||||
//This varies depending on the file
|
||||
$this->fseek($this->pointer, SEEK_END);
|
||||
}
|
||||
|
||||
/**
|
||||
* reverse rewind file.
|
||||
*/
|
||||
public function rewind(){
|
||||
//Set the line position to 0 - First Line
|
||||
$this->position = 0;
|
||||
|
||||
//Reset the file pointer to the end of the file minus 1 character. "0" == false
|
||||
$this->fseek(-1, SEEK_END);
|
||||
|
||||
$this->findLineBegin();
|
||||
//... File pointer is now at the beginning of the last line that contains data
|
||||
|
||||
// add custom line offset
|
||||
if($this->offset){
|
||||
// calculate offset start line
|
||||
$offsetLine = $this->lineCount - $this->lineCountEmpty - $this->offset;
|
||||
|
||||
if($offsetLine > 0){
|
||||
// row is zero based
|
||||
$offsetIndex = $offsetLine - 1;
|
||||
|
||||
parent::seek($offsetIndex);
|
||||
// seek() sets pointer to next line... set it back to previous
|
||||
$this->fseek(-2, SEEK_CUR);
|
||||
|
||||
$this->findLineBegin();
|
||||
//... File pointer is now at the beginning of the last line that contains data from $offset
|
||||
}else{
|
||||
// negative offsetLine -> invalid!
|
||||
$this->pointer = $this->begin -1;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current line after the file pointer
|
||||
* @return string
|
||||
*/
|
||||
public function current(){
|
||||
return trim($this->fgets());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current key of the line we're on
|
||||
* These go in reverse order
|
||||
* @return mixed
|
||||
*/
|
||||
public function key(){
|
||||
return $this->position;
|
||||
}
|
||||
|
||||
/**
|
||||
* move one line up
|
||||
*/
|
||||
public function next(){
|
||||
//Step the file pointer back one step to the last letter of the previous line
|
||||
--$this->pointer;
|
||||
if($this->pointer < $this->begin){
|
||||
return;
|
||||
}
|
||||
|
||||
$this->fseek($this->pointer);
|
||||
|
||||
$this->findLineBegin();
|
||||
|
||||
//File pointer is now on the next previous line
|
||||
//Increment the line position
|
||||
++$this->position;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the current file pointer to make sure we are not at the beginning of the file
|
||||
* @return bool
|
||||
*/
|
||||
public function valid(){
|
||||
return ($this->pointer >= $this->begin);
|
||||
}
|
||||
|
||||
/**
|
||||
* seek to previous lines
|
||||
* @param int $lineCount
|
||||
*/
|
||||
public function seek($lineCount){
|
||||
for($i = 0; $i < $lineCount; $i++){
|
||||
$this->next();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* move pointer to line begin
|
||||
* -> skip line breaks
|
||||
*/
|
||||
private function findLineBegin(){
|
||||
//Check the character over and over till we hit another new line
|
||||
$c = $this->fgetc();
|
||||
|
||||
// skip empty lines
|
||||
while(in_array($c, $this->eol)){
|
||||
$this->fseek(-2, SEEK_CUR);
|
||||
if(!$this->pointer = $this->ftell()){
|
||||
break;
|
||||
}
|
||||
$c = $this->fgetc();
|
||||
|
||||
$this->lineCountEmpty++;
|
||||
}
|
||||
|
||||
//Check the last character to make sure it is not a new line
|
||||
while(!in_array($c, $this->eol)){
|
||||
$this->fseek(-2, SEEK_CUR);
|
||||
if(!$this->pointer = $this->ftell()){
|
||||
break;
|
||||
}
|
||||
$c = $this->fgetc();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* set total line count. No matter if there are empty lines in between
|
||||
*/
|
||||
private function setLineCount(){
|
||||
// Store flags and position
|
||||
$flags = $this->getFlags();
|
||||
$currentPointer = $this->ftell();
|
||||
|
||||
// Prepare count by resetting flags as READ_CSV for example make the tricks very slow
|
||||
$this->setFlags(null);
|
||||
|
||||
// Go to the larger INT we can as seek will not throw exception, errors, notice if we go beyond the bottom line
|
||||
//$this->seek(PHP_INT_MAX);
|
||||
parent::seek(PHP_INT_MAX);
|
||||
|
||||
// We store the key position
|
||||
// As key starts at 0, we add 1
|
||||
$this->lineCount = parent::key() + 1;
|
||||
|
||||
// We move to old position
|
||||
// As seek method is longer with line number < to the max line number, it is better to count at the beginning of iteration
|
||||
//parent::seek($currentPointer);
|
||||
$this->fseek($currentPointer);
|
||||
|
||||
// Re set flags
|
||||
$this->setFlags($flags);
|
||||
}
|
||||
}
|
||||
@@ -11,20 +11,26 @@ namespace data\filesystem;
|
||||
|
||||
class Search {
|
||||
|
||||
/**
|
||||
* max file count that should be deleted in this session
|
||||
*/
|
||||
const DEFAULT_FILE_LIMIT = 1000;
|
||||
|
||||
/**
|
||||
* timestamp (seconds) filter files by mTime()
|
||||
* -> default = "no filter"
|
||||
* @var int
|
||||
*/
|
||||
static $filterTime = 0;
|
||||
static $filterTime = 0;
|
||||
|
||||
/**
|
||||
* recursive file filter by mTime
|
||||
* @param string $dir
|
||||
* @param int $mTime
|
||||
* @return array|\RecursiveCallbackFilterIterator
|
||||
* @param int $limit
|
||||
* @return array|\LimitIterator
|
||||
*/
|
||||
static function getFilesByMTime($dir, $mTime = null){
|
||||
static function getFilesByMTime(string $dir, $mTime = null, $limit = self::DEFAULT_FILE_LIMIT){
|
||||
$files = [];
|
||||
|
||||
if(is_dir($dir)){
|
||||
@@ -53,7 +59,8 @@ class Search {
|
||||
return false;
|
||||
});
|
||||
|
||||
$files = new \RecursiveIteratorIterator($files);
|
||||
// limit max files
|
||||
$files = new \LimitIterator($files, 0, $limit);
|
||||
}
|
||||
|
||||
return $files;
|
||||
|
||||
@@ -7,45 +7,71 @@
|
||||
*/
|
||||
|
||||
namespace DB;
|
||||
use Controller;
|
||||
use controller\LogController;
|
||||
use lib\Config;
|
||||
|
||||
class Database extends \Prefab {
|
||||
|
||||
/**
|
||||
* if true, errors will not get logged
|
||||
* @var bool
|
||||
*/
|
||||
private $silent = false;
|
||||
|
||||
function __construct($database = 'PF'){
|
||||
// set database
|
||||
$this->setDB($database);
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $errors = [];
|
||||
|
||||
/**
|
||||
* connect to the DB server itself -> NO database is used
|
||||
* -> can be used to check if a certain DB exists without connecting to it directly
|
||||
* @param string $dbKey
|
||||
* @return SQL|null
|
||||
*/
|
||||
public function connectToServer(string $dbKey = 'PF'){
|
||||
$dbConfig = Config::getDatabaseConfig($dbKey);
|
||||
$dbConfig['DNS'] = str_replace(';dbname=', '', $dbConfig['DNS'] );
|
||||
$dbConfig['NAME'] = '';
|
||||
return call_user_func_array([$this, 'connect'], $dbConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* set database
|
||||
* @param string $database
|
||||
* @return SQL
|
||||
* tries to create a database if not exists
|
||||
* -> DB user needs rights to create a DB
|
||||
* @param string $dbKey
|
||||
* @return SQL|null
|
||||
*/
|
||||
public function setDB($database = 'PF'){
|
||||
$f3 = \Base::instance();
|
||||
public function createDB(string $dbKey = 'PF'){
|
||||
$db = null;
|
||||
$dbConfig = Config::getDatabaseConfig($dbKey);
|
||||
// remove database from $dsn (we want to crate it)
|
||||
$newDbName = $dbConfig['NAME'];
|
||||
if(!empty($newDbName)){
|
||||
$dbConfig['NAME'] = '';
|
||||
$dbConfig['DNS'] = str_replace(';dbname=', '', $dbConfig['DNS'] );
|
||||
|
||||
// "Hive" Key for DB storage
|
||||
$dbHiveKey = $this->getDbHiveKey($database);
|
||||
$db = call_user_func_array([$this, 'connect'], $dbConfig);
|
||||
|
||||
// check if DB connection already exists
|
||||
if( !$f3->exists($dbHiveKey, $db) ){
|
||||
$dbConfig = Config::getDatabaseConfig($database);
|
||||
if(!is_null($db)){
|
||||
$schema = new SQL\Schema($db);
|
||||
if(!in_array($newDbName, $schema->getDatabases())){
|
||||
$db->exec("CREATE DATABASE IF NOT EXISTS
|
||||
`" . $newDbName . "` DEFAULT CHARACTER SET utf8
|
||||
COLLATE utf8_general_ci;");
|
||||
$db->exec("USE `" . $newDbName . "`");
|
||||
|
||||
$db = call_user_func_array([$this, 'connect'], $dbConfig);
|
||||
|
||||
if( !is_null($db) ){
|
||||
// set DB timezone to UTC +00:00 (eve server time)
|
||||
$db->exec('SET @@session.time_zone = "+00:00";');
|
||||
|
||||
// set default storage engine
|
||||
$db->exec('SET @@session.default_storage_engine = "' .
|
||||
Controller\Controller::getRequiredMySqlVariables('DEFAULT_STORAGE_ENGINE') . '"');
|
||||
|
||||
// store DB object
|
||||
$f3->set($dbHiveKey, $db);
|
||||
// check if DB create was successful
|
||||
$dbCheck = $db->exec("SELECT DATABASE()");
|
||||
if(
|
||||
!empty($dbCheck[0]) &&
|
||||
!empty($checkDbName = reset($dbCheck[0])) &&
|
||||
$checkDbName == $newDbName
|
||||
){
|
||||
self::prepareDBConnection($db);
|
||||
self::prepareDatabase($db);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,14 +80,20 @@ class Database extends \Prefab {
|
||||
|
||||
/**
|
||||
* get database
|
||||
* @param string $database
|
||||
* @return SQL
|
||||
* @param string $dbKey
|
||||
* @return SQL|null
|
||||
*/
|
||||
public function getDB($database = 'PF'){
|
||||
public function getDB(string $dbKey = 'PF'){
|
||||
$f3 = \Base::instance();
|
||||
$dbHiveKey = $this->getDbHiveKey($database);
|
||||
// "Hive" Key for DB object cache
|
||||
$dbHiveKey = $this->getDbHiveKey($dbKey);
|
||||
if( !$f3->exists($dbHiveKey, $db) ){
|
||||
$db = $this->setDB($database);
|
||||
$dbConfig = Config::getDatabaseConfig($dbKey);
|
||||
$db = call_user_func_array([$this, 'connect'], $dbConfig);
|
||||
if(!is_null($db)){
|
||||
self::prepareDBConnection($db);
|
||||
$f3->set($dbHiveKey, $db);
|
||||
}
|
||||
}
|
||||
|
||||
return $db;
|
||||
@@ -69,23 +101,23 @@ class Database extends \Prefab {
|
||||
|
||||
/**
|
||||
* get a unique hive key for each DB connection
|
||||
* @param $database
|
||||
* @param $dbKey
|
||||
* @return string
|
||||
*/
|
||||
protected function getDbHiveKey($database){
|
||||
return 'DB_' . $database;
|
||||
protected function getDbHiveKey($dbKey){
|
||||
return 'DB_' . $dbKey;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* connect to a database
|
||||
* @param $dns
|
||||
* @param $name
|
||||
* @param $user
|
||||
* @param $password
|
||||
* @return SQL
|
||||
* @param string $dns
|
||||
* @param string $name
|
||||
* @param string $user
|
||||
* @param string $password
|
||||
* @param string $alias
|
||||
* @return SQL|null
|
||||
*/
|
||||
protected function connect($dns, $name, $user, $password){
|
||||
protected function connect($dns, $name, $user, $password, $alias){
|
||||
$db = null;
|
||||
$f3 = \Base::instance();
|
||||
|
||||
@@ -109,9 +141,10 @@ class Database extends \Prefab {
|
||||
$options
|
||||
);
|
||||
}catch(\PDOException $e){
|
||||
// DB connection error
|
||||
// -> log it
|
||||
self::getLogger()->write($e->getMessage());
|
||||
$this->pushError($alias, $e);
|
||||
if(!$this->isSilent()){
|
||||
self::getLogger()->write($e);
|
||||
}
|
||||
}
|
||||
|
||||
return $db;
|
||||
@@ -119,22 +152,22 @@ class Database extends \Prefab {
|
||||
|
||||
/**
|
||||
* get all table names from a DB
|
||||
* @param string $database
|
||||
* @param string $dbKey
|
||||
* @return array|bool
|
||||
*/
|
||||
public function getTables($database = 'PF'){
|
||||
$schema = new SQL\Schema( $this->getDB($database) );
|
||||
public function getTables($dbKey = 'PF'){
|
||||
$schema = new SQL\Schema( $this->getDB($dbKey) );
|
||||
return $schema->getTables();
|
||||
}
|
||||
|
||||
/**
|
||||
* checks whether a table exists on a DB or not
|
||||
* @param $table
|
||||
* @param string $database
|
||||
* @param string $dbKey
|
||||
* @return bool
|
||||
*/
|
||||
public function tableExists($table, $database = 'PF'){
|
||||
$tableNames = $this->getTables($database);
|
||||
public function tableExists($table, $dbKey = 'PF'){
|
||||
$tableNames = $this->getTables($dbKey);
|
||||
return in_array($table, $tableNames);
|
||||
}
|
||||
|
||||
@@ -142,13 +175,13 @@ class Database extends \Prefab {
|
||||
* get current row (data) count for an existing table
|
||||
* -> returns 0 if table not exists or empty
|
||||
* @param $table
|
||||
* @param string $database
|
||||
* @param string $dbKey
|
||||
* @return int
|
||||
*/
|
||||
public function getRowCount($table, $database = 'PF') {
|
||||
public function getRowCount($table, $dbKey = 'PF') {
|
||||
$count = 0;
|
||||
if( $this->tableExists($table, $database) ){
|
||||
$db = $this->getDB($database);
|
||||
if( $this->tableExists($table, $dbKey) ){
|
||||
$db = $this->getDB($dbKey);
|
||||
$countRes = $db->exec("SELECT COUNT(*) `num` FROM " . $db->quotekey($table));
|
||||
if(isset($countRes[0]['num'])){
|
||||
$count = (int)$countRes[0]['num'];
|
||||
@@ -157,6 +190,98 @@ class Database extends \Prefab {
|
||||
return $count;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isSilent() : bool{
|
||||
return $this->silent;
|
||||
}
|
||||
|
||||
/**
|
||||
* set "silent" mode (no error logging)
|
||||
* -> optional clear $this->errors
|
||||
* @param bool $silent
|
||||
* @param bool $clearErrors
|
||||
*/
|
||||
public function setSilent(bool $silent, bool $clearErrors = false){
|
||||
$this->silent = $silent;
|
||||
if($clearErrors){
|
||||
$this->errors = [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* push new Exception into static error history
|
||||
* @param string $alias
|
||||
* @param \PDOException $e
|
||||
*/
|
||||
protected function pushError(string $alias, \PDOException $e){
|
||||
if(!is_array($this->errors[$alias])){
|
||||
$this->errors[$alias] = [];
|
||||
}
|
||||
|
||||
// prevent adding same errors twice
|
||||
if(!empty($this->errors[$alias])){
|
||||
$lastError = array_values($this->errors[$alias])[0];
|
||||
if($lastError->getMessage() === $e->getMessage()){
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
array_unshift($this->errors[$alias], $e);
|
||||
if(count($this->errors[$alias]) > 5){
|
||||
$this->errors[$alias] = array_pop($this->errors[$alias]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* get last recent Exceptions from error history
|
||||
* @param string $alias
|
||||
* @param int $limit
|
||||
* @return \PDOException[]
|
||||
*/
|
||||
public function getErrors(string $alias, int $limit = 1){
|
||||
return array_slice((array)$this->errors[$alias] , 0, $limit);
|
||||
}
|
||||
|
||||
/**
|
||||
* prepare current DB
|
||||
* -> set session connection variables
|
||||
* @param SQL $db
|
||||
*/
|
||||
public static function prepareDBConnection(SQL &$db){
|
||||
// set DB timezone to UTC +00:00 (eve server time)
|
||||
$db->exec('SET @@session.time_zone = "+00:00";');
|
||||
|
||||
// set default storage engine
|
||||
$db->exec('SET @@session.default_storage_engine = "' .
|
||||
self::getRequiredMySqlVariables('DEFAULT_STORAGE_ENGINE') . '"');
|
||||
}
|
||||
|
||||
/**
|
||||
* set some default config for current DB
|
||||
* @param SQL $db
|
||||
*/
|
||||
public static function prepareDatabase(SQL &$db){
|
||||
if($db->name()){
|
||||
// set/change default "character set" and "collation"
|
||||
$db->exec('ALTER DATABASE ' . $db->quotekey($db->name())
|
||||
. ' CHARACTER SET ' . self::getRequiredMySqlVariables('CHARACTER_SET_DATABASE')
|
||||
. ' COLLATE ' . self::getRequiredMySqlVariables('COLLATION_DATABASE')
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* get required MySQL variable value
|
||||
* @param string $key
|
||||
* @return string|null
|
||||
*/
|
||||
public static function getRequiredMySqlVariables(string $key){
|
||||
\Base::instance()->exists('REQUIREMENTS[MYSQL][VARS][' . $key . ']', $data);
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* get logger for DB logging
|
||||
* @return \Log
|
||||
|
||||
@@ -11,11 +11,12 @@ namespace Exception;
|
||||
|
||||
class BaseException extends \Exception {
|
||||
|
||||
const VALIDATION_FAILED = 403;
|
||||
const REGISTRATION_FAILED = 403;
|
||||
const CONFIGURATION_FAILED = 500;
|
||||
const VALIDATION_EXCEPTION = 403;
|
||||
const REGISTRATION_EXCEPTION = 403;
|
||||
const CONFIG_VALUE_EXCEPTION = 500;
|
||||
const DB_EXCEPTION = 500;
|
||||
|
||||
public function __construct($message, $code = 0){
|
||||
public function __construct(string $message, int $code = 0){
|
||||
parent::__construct($message, $code);
|
||||
}
|
||||
|
||||
|
||||
16
app/main/exception/databaseexception.php
Normal file
16
app/main/exception/databaseexception.php
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: exodu
|
||||
* Date: 25.08.2017
|
||||
* Time: 20:31
|
||||
*/
|
||||
|
||||
namespace Exception;
|
||||
|
||||
class DatabaseException extends BaseException {
|
||||
|
||||
public function __construct(string $message){
|
||||
parent::__construct($message, self::DB_EXCEPTION);
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,6 @@ namespace Exception;
|
||||
class PathfinderException extends BaseException{
|
||||
|
||||
public function __construct($message){
|
||||
parent::__construct($message, self::CONFIGURATION_FAILED);
|
||||
parent::__construct($message, self::CONFIG_VALUE_EXCEPTION);
|
||||
}
|
||||
}
|
||||
@@ -32,9 +32,7 @@ class RegistrationException extends BaseException{
|
||||
}
|
||||
|
||||
public function __construct($message, $field = ''){
|
||||
|
||||
parent::__construct($message, self::REGISTRATION_FAILED);
|
||||
|
||||
parent::__construct($message, self::REGISTRATION_EXCEPTION);
|
||||
$this->setField($field);
|
||||
}
|
||||
}
|
||||
@@ -11,27 +11,41 @@ namespace Exception;
|
||||
|
||||
class ValidationException extends BaseException {
|
||||
|
||||
/**
|
||||
* table column that triggers the exception
|
||||
* @var string
|
||||
*/
|
||||
private $field;
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
* @return string
|
||||
*/
|
||||
public function getField(){
|
||||
public function getField(): string {
|
||||
return $this->field;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $field
|
||||
* @param string $field
|
||||
*/
|
||||
public function setField($field){
|
||||
public function setField(string $field){
|
||||
$this->field = $field;
|
||||
}
|
||||
|
||||
|
||||
public function __construct($message, $field = 0){
|
||||
|
||||
parent::__construct($message, self::VALIDATION_FAILED);
|
||||
|
||||
public function __construct(string $message, string $field = ''){
|
||||
parent::__construct($message, self::VALIDATION_EXCEPTION);
|
||||
$this->setField($field);
|
||||
}
|
||||
|
||||
/**
|
||||
* get error object
|
||||
* @return \stdClass
|
||||
*/
|
||||
public function getError(){
|
||||
$error = (object) [];
|
||||
$error->type = 'error';
|
||||
$error->field = $this->getField();
|
||||
$error->message = $this->getMessage();
|
||||
return $error;
|
||||
}
|
||||
}
|
||||
216
app/main/lib/Monolog.php
Normal file
216
app/main/lib/Monolog.php
Normal file
@@ -0,0 +1,216 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: exodu
|
||||
* Date: 04.08.2017
|
||||
* Time: 20:17
|
||||
*/
|
||||
|
||||
namespace Lib;
|
||||
|
||||
|
||||
use controller\LogController;
|
||||
use Lib\Logging;
|
||||
use Monolog\Registry;
|
||||
use Monolog\Formatter\FormatterInterface;
|
||||
use Monolog\Handler\HandlerInterface;
|
||||
use Monolog\Handler\BufferHandler;
|
||||
use Monolog\Logger;
|
||||
|
||||
|
||||
class Monolog extends \Prefab {
|
||||
|
||||
const ERROR_FORMATTER = 'Unknown log formatter for key "%s"';
|
||||
const ERROR_HANDLER = 'Unknown log handler for key "%s"';
|
||||
const ERROR_PROCESSOR = 'Unknown log processor for key "%s"';
|
||||
|
||||
const FORMATTER = [
|
||||
'line' => 'Monolog\Formatter\LineFormatter',
|
||||
'json' => 'Monolog\Formatter\JsonFormatter',
|
||||
'html' => 'Monolog\Formatter\HtmlFormatter',
|
||||
'mail' => 'Lib\Logging\Formatter\MailFormatter'
|
||||
];
|
||||
|
||||
const HANDLER = [
|
||||
'stream' => 'Monolog\Handler\StreamHandler',
|
||||
'mail' => 'Monolog\Handler\SwiftMailerHandler',
|
||||
'slackMap' => 'Lib\Logging\Handler\SlackMapWebhookHandler',
|
||||
'slackRally' => 'Lib\Logging\Handler\SlackRallyWebhookHandler',
|
||||
'zmq' => 'Lib\Logging\Handler\ZMQHandler'
|
||||
];
|
||||
|
||||
const PROCESSOR = [
|
||||
'psr' => 'Monolog\Processor\PsrLogMessageProcessor'
|
||||
];
|
||||
|
||||
/**
|
||||
* @var Logging\LogCollection[][]|Logging\MapLog[][]
|
||||
*/
|
||||
private $logs = [
|
||||
'solo' => [],
|
||||
'groups' => []
|
||||
];
|
||||
|
||||
public function __construct(){
|
||||
// set timezone for all Logger instances
|
||||
if(class_exists(Logger::class)){
|
||||
if( is_callable($getTimezone = \Base::instance()->get('getTimeZone')) ){
|
||||
Logger::setTimezone($getTimezone());
|
||||
};
|
||||
}else{
|
||||
LogController::getLogger('ERROR')->write(sprintf(Config::ERROR_CLASS_NOT_EXISTS_COMPOSER, Logger::class));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* buffer log object, add to objectStorage collection
|
||||
* -> this buffered data can be stored/logged somewhere (e.g. DB/file) at any time
|
||||
* -> should be cleared afterwards!
|
||||
* @param Logging\AbstractLog $log
|
||||
*/
|
||||
public function push(Logging\AbstractLog $log){
|
||||
// check whether $log should be "grouped" by common handlers
|
||||
if($log->isGrouped()){
|
||||
$groupHash = $log->getGroupHash();
|
||||
|
||||
if(!isset($this->logs['groups'][$groupHash])){
|
||||
// create new log collection
|
||||
// $this->logs['groups'][$groupHash] = new Logging\LogCollection($log->getChannelName());
|
||||
$this->logs['groups'][$groupHash] = new Logging\LogCollection('mapDelete');
|
||||
}
|
||||
$this->logs['groups'][$groupHash]->addLog($log);
|
||||
|
||||
// remove "group" handler from $log
|
||||
// each log should only be logged once per handler!
|
||||
$log->removeHandlerGroups();
|
||||
}
|
||||
|
||||
$this->logs['solo'][] = $log;
|
||||
}
|
||||
|
||||
/**
|
||||
* bulk process all stored logs -> send to Monolog lib
|
||||
*/
|
||||
public function log(){
|
||||
|
||||
foreach($this->logs as $logType => $logs){
|
||||
foreach($logs as $logKey => $log){
|
||||
$groupHash = $log->getGroupHash();
|
||||
$level = Logger::toMonologLevel($log->getLevel());
|
||||
|
||||
// add new logger to Registry if not already exists
|
||||
if(Registry::hasLogger($groupHash)){
|
||||
$logger = Registry::getInstance($groupHash);
|
||||
}else{
|
||||
$logger = new Logger($log->getChannelName());
|
||||
|
||||
// disable microsecond timestamps (seconds should be fine)
|
||||
$logger->useMicrosecondTimestamps(true);
|
||||
|
||||
// configure new $logger --------------------------------------------------------------------------
|
||||
// get Monolog Handler with Formatter config
|
||||
// -> $log could have multiple handler with different Formatters
|
||||
$handlerConf = $log->getHandlerConfig();
|
||||
foreach($handlerConf as $handlerKey => $formatterKey){
|
||||
// get Monolog Handler class
|
||||
$handlerParams = $log->getHandlerParams($handlerKey);
|
||||
$handler = $this->getHandler($handlerKey, $handlerParams);
|
||||
|
||||
// get Monolog Formatter
|
||||
$formatter = $this->getFormatter((string)$formatterKey);
|
||||
if( $formatter instanceof FormatterInterface){
|
||||
$handler->setFormatter($formatter);
|
||||
}
|
||||
|
||||
if($log->hasBuffer()){
|
||||
// wrap Handler into bufferHandler
|
||||
// -> bulk save all logs for this $logger
|
||||
$bufferHandler = new BufferHandler($handler);
|
||||
$logger->pushHandler($bufferHandler);
|
||||
}else{
|
||||
$logger->pushHandler($handler);
|
||||
}
|
||||
}
|
||||
|
||||
// get Monolog Processor config
|
||||
$processorConf = $log->getProcessorConfig();
|
||||
foreach($processorConf as $processorKey => $processorCallback){
|
||||
if(is_callable($processorCallback)){
|
||||
// custom Processor callback function
|
||||
$logger->pushProcessor($processorCallback);
|
||||
}else{
|
||||
// get Monolog Processor class
|
||||
$processor = $this->getProcessor($processorKey);
|
||||
$logger->pushProcessor($processor);
|
||||
}
|
||||
}
|
||||
|
||||
Registry::addLogger($logger, $groupHash);
|
||||
}
|
||||
|
||||
$logger->addRecord($level, $log->getMessage(), $log->getContext());
|
||||
}
|
||||
}
|
||||
|
||||
// clear log object storage
|
||||
$this->logs['groups'] = [];
|
||||
$this->logs['solo'] = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* get Monolog Formatter instance by key
|
||||
* @param string $formatKey
|
||||
* @return FormatterInterface|null
|
||||
* @throws \Exception
|
||||
*/
|
||||
private function getFormatter(string $formatKey){
|
||||
$formatter = null;
|
||||
if(!empty($formatKey)){
|
||||
if(array_key_exists($formatKey, self::FORMATTER)){
|
||||
$formatClass = self::FORMATTER[$formatKey];
|
||||
$formatter = new $formatClass();
|
||||
}else{
|
||||
throw new \Exception(sprintf(self::ERROR_FORMATTER, $formatKey));
|
||||
}
|
||||
}
|
||||
|
||||
return $formatter;
|
||||
}
|
||||
|
||||
/**
|
||||
* get Monolog Handler instance by key
|
||||
* @param string $handlerKey
|
||||
* @param array $handlerParams
|
||||
* @return HandlerInterface
|
||||
* @throws \Exception
|
||||
*/
|
||||
private function getHandler(string $handlerKey, array $handlerParams = []): HandlerInterface{
|
||||
if(array_key_exists($handlerKey, self::HANDLER)){
|
||||
$handlerClass = self::HANDLER[$handlerKey];
|
||||
$handler = new $handlerClass(...$handlerParams);
|
||||
}else{
|
||||
throw new \Exception(sprintf(self::ERROR_HANDLER, $handlerKey));
|
||||
}
|
||||
|
||||
return $handler;
|
||||
}
|
||||
|
||||
/**
|
||||
* get Monolog Processor instance by key
|
||||
* @param string $processorKey
|
||||
* @return callable
|
||||
* @throws \Exception
|
||||
*/
|
||||
private function getProcessor(string $processorKey): callable {
|
||||
if(array_key_exists($processorKey, self::PROCESSOR)){
|
||||
$ProcessorClass = self::PROCESSOR[$processorKey];
|
||||
$processor = new $ProcessorClass();
|
||||
}else{
|
||||
throw new \Exception(sprintf(self::ERROR_PROCESSOR, $processorKey));
|
||||
}
|
||||
|
||||
return $processor;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -16,24 +16,26 @@ class CcpClient extends \Prefab {
|
||||
private $apiClient;
|
||||
|
||||
public function __construct(\Base $f3){
|
||||
$this->apiClient = $this->getClient();
|
||||
$this->apiClient = $this->getClient($f3);
|
||||
$f3->set('ccpClient', $this);
|
||||
}
|
||||
|
||||
/**
|
||||
* get ApiClient instance
|
||||
* @param \Base $f3
|
||||
* @return ApiClient|null
|
||||
*/
|
||||
protected function getClient(){
|
||||
protected function getClient(\Base $f3){
|
||||
$client = null;
|
||||
|
||||
if( !class_exists(ApiClient::class) ){
|
||||
LogController::getLogger('ERROR')->write($this->getMissingClientError());
|
||||
}else{
|
||||
if(class_exists(ApiClient::class)){
|
||||
$client = new ApiClient();
|
||||
$client->setUrl( Config::getEnvironmentData('CCP_ESI_URL') );
|
||||
$client->setDatasource( Config::getEnvironmentData('CCP_ESI_DATASOURCE') );
|
||||
$client->setUserAgent($this->getUserAgent());
|
||||
$client->setDebugLevel($f3->get('DEBUG'));
|
||||
}else{
|
||||
LogController::getLogger('ERROR')->write(sprintf(Config::ERROR_CLASS_NOT_EXISTS_COMPOSER, ApiClient::class));
|
||||
}
|
||||
|
||||
return $client;
|
||||
@@ -52,14 +54,6 @@ class CcpClient extends \Prefab {
|
||||
return $userAgent;
|
||||
}
|
||||
|
||||
/**
|
||||
* get error msg for failed ApiClient() class -> Composer package not found
|
||||
* @return string
|
||||
*/
|
||||
protected function getMissingClientError(){
|
||||
return "Class '" . ApiClient::class . "' not found. -> Check installed Composer packages.'";
|
||||
}
|
||||
|
||||
/**
|
||||
* get error msg for undefined method in ApiClient() class
|
||||
* @param $method
|
||||
@@ -86,8 +80,8 @@ class CcpClient extends \Prefab {
|
||||
\Base::instance()->error(501, $this->getMissingMethodError($name));
|
||||
}
|
||||
}else{
|
||||
LogController::getLogger('ERROR')->write($this->getMissingClientError());
|
||||
\Base::instance()->error(501, $this->getMissingClientError());
|
||||
LogController::getLogger('ERROR')->write(sprintf(Config::ERROR_CLASS_NOT_EXISTS_COMPOSER, ApiClient::class));
|
||||
\Base::instance()->error(501, sprintf(Config::ERROR_CLASS_NOT_EXISTS_COMPOSER, ApiClient::class));
|
||||
}
|
||||
|
||||
return $return;
|
||||
|
||||
@@ -17,8 +17,11 @@ class Config extends \Prefab {
|
||||
const ARRAY_DELIMITER = '-';
|
||||
const HIVE_KEY_PATHFINDER = 'PATHFINDER';
|
||||
const HIVE_KEY_ENVIRONMENT = 'ENVIRONMENT';
|
||||
const CACHE_KEY_SOCKET_VALID = 'CACHED_SOCKET_VALID';
|
||||
const CACHE_TTL_SOCKET_VALID = 60;
|
||||
|
||||
const ERROR_CONF_PATHFINDER = 'Config value missing in pathfinder.ini file [%s]';
|
||||
const ERROR_CLASS_NOT_EXISTS_COMPOSER = 'Class "%s" not found. -> Check installed Composer packages';
|
||||
|
||||
|
||||
/**
|
||||
@@ -43,6 +46,11 @@ class Config extends \Prefab {
|
||||
// set hive configuration variables
|
||||
// -> overwrites default configuration
|
||||
$this->setHiveVariables($f3);
|
||||
|
||||
// set global function for current DateTimeZone()
|
||||
$f3->set('getTimeZone', function() use ($f3){
|
||||
return new \DateTimeZone( $f3->get('TZ') );
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -164,7 +172,6 @@ class Config extends \Prefab {
|
||||
static function getEnvironmentData($key){
|
||||
$hiveKey = self::HIVE_KEY_ENVIRONMENT . '.' . $key;
|
||||
\Base::instance()->exists($hiveKey, $data);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
@@ -173,16 +180,74 @@ class Config extends \Prefab {
|
||||
* @param string $dbKey
|
||||
* @return array
|
||||
*/
|
||||
static function getDatabaseConfig($dbKey = 'PF'){
|
||||
$dbConfKey = ($dbKey === 'PF') ? '' : $dbKey . '_';
|
||||
static function getDatabaseConfig(string $dbKey = 'PF'){
|
||||
$dbKey = strtoupper($dbKey);
|
||||
return [
|
||||
'DNS' => self::getEnvironmentData('DB_' . $dbConfKey . 'DNS'),
|
||||
'NAME' => self::getEnvironmentData('DB_' . $dbConfKey . 'NAME'),
|
||||
'USER' => self::getEnvironmentData('DB_' . $dbConfKey . 'USER'),
|
||||
'PASS' => self::getEnvironmentData('DB_' . $dbConfKey . 'PASS')
|
||||
'DNS' => self::getEnvironmentData('DB_' . $dbKey . '_DNS'),
|
||||
'NAME' => self::getEnvironmentData('DB_' . $dbKey . '_NAME'),
|
||||
'USER' => self::getEnvironmentData('DB_' . $dbKey . '_USER'),
|
||||
'PASS' => self::getEnvironmentData('DB_' . $dbKey . '_PASS'),
|
||||
'ALIAS' => $dbKey
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* get DB config value from PDO connect $dns string
|
||||
* @param string $dns
|
||||
* @param string $key
|
||||
* @return bool
|
||||
*/
|
||||
static function getDatabaseDNSValue(string $dns, string $key = 'dbname'){
|
||||
$value = false;
|
||||
if(preg_match('/' . preg_quote($key, '/') . '=([[:alnum:]]+)/is', $dns, $parts)){
|
||||
$value = $parts[1];
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* get SMTP config values
|
||||
* @return \stdClass
|
||||
*/
|
||||
static function getSMTPConfig(): \stdClass{
|
||||
$config = new \stdClass();
|
||||
$config->host = self::getEnvironmentData('SMTP_HOST');
|
||||
$config->port = self::getEnvironmentData('SMTP_PORT');
|
||||
$config->scheme = self::getEnvironmentData('SMTP_SCHEME');
|
||||
$config->username = self::getEnvironmentData('SMTP_USER');
|
||||
$config->password = self::getEnvironmentData('SMTP_PASS');
|
||||
$config->from = [
|
||||
self::getEnvironmentData('SMTP_FROM') => self::getPathfinderData('name')
|
||||
];
|
||||
return $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* validates an SMTP config
|
||||
* @param \stdClass $config
|
||||
* @return bool
|
||||
*/
|
||||
static function isValidSMTPConfig(\stdClass $config): bool {
|
||||
// validate email from either an configured array or plain string
|
||||
$validateMailConfig = function($mailConf = null): bool {
|
||||
$email = null;
|
||||
if(is_array($mailConf)){
|
||||
reset($mailConf);
|
||||
$email = key($mailConf);
|
||||
}elseif(is_string($mailConf)){
|
||||
$email = $mailConf;
|
||||
}
|
||||
return \Audit::instance()->email($email);
|
||||
};
|
||||
|
||||
return (
|
||||
!empty($config->host) &&
|
||||
!empty($config->username) &&
|
||||
$validateMailConfig($config->from) &&
|
||||
$validateMailConfig($config->to)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* get email for notifications by hive key
|
||||
* @param $key
|
||||
@@ -196,7 +261,7 @@ class Config extends \Prefab {
|
||||
* get map default config values for map types (private/corp/ally)
|
||||
* -> read from pathfinder.ini
|
||||
* @param string $mapType
|
||||
* @return array
|
||||
* @return mixed
|
||||
*/
|
||||
static function getMapsDefaultConfig($mapType = ''){
|
||||
if( $mapConfig = self::getPathfinderData('map' . ($mapType ? '.' . $mapType : '')) ){
|
||||
@@ -206,6 +271,92 @@ class Config extends \Prefab {
|
||||
return $mapConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* get custom $message for a a HTTP $status
|
||||
* -> use this in addition to the very general Base::HTTP_XXX labels
|
||||
* @param int $status
|
||||
* @return string
|
||||
*/
|
||||
static function getMessageFromHTTPStatus(int $status): string {
|
||||
switch($status){
|
||||
case 403:
|
||||
$message = 'Access denied: User not found'; break;
|
||||
default:
|
||||
$message = '';
|
||||
}
|
||||
return $message;
|
||||
}
|
||||
|
||||
/**
|
||||
* check whether this installation fulfills all requirements
|
||||
* -> check for ZMQ PHP extension and installed ZQM version
|
||||
* -> this does NOT check versions! -> those can be verified on /setup page
|
||||
* @return bool
|
||||
*/
|
||||
static function checkSocketRequirements(): bool {
|
||||
return extension_loaded('zmq') && class_exists('ZMQ');
|
||||
}
|
||||
|
||||
/**
|
||||
* use this function to "validate" the socket connection.
|
||||
* The result will be CACHED for a few seconds!
|
||||
* This function is intended to pre-check a Socket connection if it MIGHT exists.
|
||||
* No data will be send to the Socket, this function just validates if a socket is available
|
||||
* -> see pingDomain()
|
||||
* @return bool
|
||||
*/
|
||||
static function validSocketConnect(): bool{
|
||||
$valid = false;
|
||||
$f3 = \Base::instance();
|
||||
|
||||
if( !$f3->exists(self::CACHE_KEY_SOCKET_VALID, $valid) ){
|
||||
if(self::checkSocketRequirements() && ($socketUrl = self::getSocketUri()) ){
|
||||
// get socket URI parts -> not elegant...
|
||||
$domain = parse_url( $socketUrl, PHP_URL_SCHEME) . '://' . parse_url( $socketUrl, PHP_URL_HOST);
|
||||
$port = parse_url( $socketUrl, PHP_URL_PORT);
|
||||
// check connection -> get ms
|
||||
$status = self::pingDomain($domain, $port);
|
||||
if($status >= 0){
|
||||
// connection OK
|
||||
$valid = true;
|
||||
}else{
|
||||
// connection error/timeout
|
||||
$valid = false;
|
||||
}
|
||||
}else{
|
||||
// requirements check failed or URL not valid
|
||||
$valid = false;
|
||||
}
|
||||
|
||||
$f3->set(self::CACHE_KEY_SOCKET_VALID, $valid, self::CACHE_TTL_SOCKET_VALID);
|
||||
}
|
||||
|
||||
return $valid;
|
||||
}
|
||||
|
||||
/**
|
||||
* get response time for a host in ms or -1 on error/timeout
|
||||
* @param string $domain
|
||||
* @param int $port
|
||||
* @param int $timeout
|
||||
* @return int
|
||||
*/
|
||||
static function pingDomain(string $domain, int $port, $timeout = 1): int {
|
||||
$starttime = microtime(true);
|
||||
$file = @fsockopen ($domain, $port, $errno, $errstr, $timeout);
|
||||
$stoptime = microtime(true);
|
||||
|
||||
if (!$file){
|
||||
// Site is down
|
||||
$status = -1;
|
||||
}else {
|
||||
fclose($file);
|
||||
$status = ($stoptime - $starttime) * 1000;
|
||||
$status = floor($status);
|
||||
}
|
||||
return $status;
|
||||
}
|
||||
|
||||
/**
|
||||
* get URI for TCP socket
|
||||
* @return bool|string
|
||||
|
||||
87
app/main/lib/logging/AbstractChannelLog.php
Normal file
87
app/main/lib/logging/AbstractChannelLog.php
Normal file
@@ -0,0 +1,87 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: exodu
|
||||
* Date: 04.08.2017
|
||||
* Time: 22:13
|
||||
*/
|
||||
|
||||
namespace Lib\Logging;
|
||||
|
||||
|
||||
abstract class AbstractChannelLog extends AbstractLog {
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $channelData = [];
|
||||
|
||||
|
||||
public function __construct(string $action, array $channelData){
|
||||
parent::__construct($action);
|
||||
$this->setChannelData($channelData);
|
||||
|
||||
// add log processor -> remove $channelData from log
|
||||
$processorClearChannelData = function($record){
|
||||
$record['context'] = array_diff_key($record['context'], $this->getChannelData());
|
||||
return $record;
|
||||
};
|
||||
|
||||
// init processorConfig. IMPORTANT: first processor gets executed at the end!
|
||||
$this->processorConfig = ['clearChannelData' => $processorClearChannelData] + $this->processorConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $channelData
|
||||
*/
|
||||
protected function setChannelData(array $channelData){
|
||||
$this->channelData = $channelData;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getChannelData() : array{
|
||||
return $this->channelData;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getChannelId() : int{
|
||||
return (int)$this->getChannelData()['channelId'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getChannelName() : string{
|
||||
return (string)$this->getChannelData()['channelName'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getData() : array{
|
||||
$data['main'] = parent::getData();
|
||||
|
||||
if(!empty($channelLogData = $this->getChannelData())){
|
||||
$channelData['channel'] = $channelLogData;
|
||||
$data = $channelData + $data;
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getContext(): array{
|
||||
$context = parent::getContext();
|
||||
|
||||
// add temp data (e.g. used for $message placeholder replacement
|
||||
$context += $this->getChannelData();
|
||||
|
||||
return $context;
|
||||
}
|
||||
}
|
||||
81
app/main/lib/logging/AbstractCharacterLog.php
Normal file
81
app/main/lib/logging/AbstractCharacterLog.php
Normal file
@@ -0,0 +1,81 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: exodu
|
||||
* Date: 22.09.2017
|
||||
* Time: 19:05
|
||||
*/
|
||||
|
||||
namespace Lib\Logging;
|
||||
|
||||
use lib\Config;
|
||||
use Model\CharacterModel;
|
||||
|
||||
abstract class AbstractCharacterLog extends AbstractChannelLog{
|
||||
|
||||
/**
|
||||
* @var CharacterModel
|
||||
*/
|
||||
private $character = null;
|
||||
|
||||
public function __construct(string $action, array $objectData){
|
||||
parent::__construct($action, $objectData);
|
||||
|
||||
// add log processor -> remove $channelData from log
|
||||
$processorAddThumbData = function($record){
|
||||
$record['extra']['thumb']['url'] = $this->getThumbUrl();
|
||||
return $record;
|
||||
};
|
||||
|
||||
// init processorConfig. IMPORTANT: first processor gets executed at the end!
|
||||
$this->processorConfig = ['addThumbData' => $processorAddThumbData] + $this->processorConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* CharacterModel $character
|
||||
* @param CharacterModel $character
|
||||
* @return LogInterface
|
||||
*/
|
||||
public function setCharacter(CharacterModel $character): LogInterface{
|
||||
$this->character = $character;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return CharacterModel
|
||||
*/
|
||||
public function getCharacter(): CharacterModel{
|
||||
return $this->character;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getData() : array{
|
||||
$data = parent::getData();
|
||||
|
||||
if(is_object($character = $this->getCharacter())){
|
||||
$characterData['character'] = [
|
||||
'id' => $character->_id,
|
||||
'name' => $character->name
|
||||
];
|
||||
$data = $characterData + $data;
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* get character thumbnailUrl
|
||||
* @return string
|
||||
*/
|
||||
protected function getThumbUrl(): string {
|
||||
$url = '';
|
||||
if(is_object($character = $this->getCharacter())){
|
||||
$url = Config::getPathfinderData('api.ccp_image_server') . '/Character/' . $character->_id . '_128.jpg';
|
||||
}
|
||||
|
||||
return $url;
|
||||
}
|
||||
|
||||
}
|
||||
549
app/main/lib/logging/AbstractLog.php
Normal file
549
app/main/lib/logging/AbstractLog.php
Normal file
@@ -0,0 +1,549 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: exodu
|
||||
* Date: 04.08.2017
|
||||
* Time: 22:13
|
||||
*/
|
||||
|
||||
namespace Lib\Logging;
|
||||
|
||||
|
||||
use Lib\Monolog;
|
||||
use Monolog\Logger;
|
||||
|
||||
abstract class AbstractLog implements LogInterface {
|
||||
|
||||
const ERROR_LEVEL = 'Invalid log level "%s"';
|
||||
const ERROR_TAG = 'Invalid log tag "%s"';
|
||||
const ERROR_HANDLER_KEY = 'Handler key "%s" not found in handlerConfig (%s)';
|
||||
const ERROR_HANDLER_PARAMS = 'No handler parameters found for handler key "%s"';
|
||||
|
||||
/**
|
||||
* PSR-3 log levels
|
||||
*/
|
||||
const LEVEL = ['emergency', 'alert', 'critical', 'error', 'warning', 'notice', 'info', 'debug'];
|
||||
|
||||
/**
|
||||
* log tags
|
||||
*/
|
||||
const TAG = ['danger', 'warning', 'information', 'success', 'primary', 'default'];
|
||||
|
||||
/**
|
||||
* @var null|\Base
|
||||
*/
|
||||
protected $f3 = null;
|
||||
|
||||
/**
|
||||
* log Handler type with Formatter type
|
||||
* -> check Monolog::HANDLER and Monolog::FORMATTER
|
||||
* @var array
|
||||
*/
|
||||
protected $handlerConfig = ['stream' => 'line'];
|
||||
|
||||
/**
|
||||
* log Processors, array with either callable functions or Processor class with __invoce() method
|
||||
* -> functions used to add "extra" data to a log
|
||||
* @var array
|
||||
*/
|
||||
protected $processorConfig = ['psr' => null];
|
||||
|
||||
/**
|
||||
* some handler need individual configuration parameters
|
||||
* -> see $handlerConfig end getHandlerParams()
|
||||
* @var array
|
||||
*/
|
||||
protected $handlerParamsConfig = [];
|
||||
|
||||
/**
|
||||
* multiple Log() objects can be marked as "grouped"
|
||||
* -> Logs with Slack Handler should be grouped by map (send multiple log data in once
|
||||
* @var array
|
||||
*/
|
||||
protected $handlerGroups = [];
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $message = '';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $action = '';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $channelType = '';
|
||||
|
||||
/**
|
||||
* log level from self::LEVEL
|
||||
* -> private - use setLevel() to set
|
||||
* @var string
|
||||
*/
|
||||
private $level = 'debug';
|
||||
|
||||
/**
|
||||
* log tag from self::TAG
|
||||
* -> private - use setTag() to set
|
||||
* @var string
|
||||
*/
|
||||
private $tag = 'default';
|
||||
|
||||
/**
|
||||
* log data (main log data)
|
||||
* @var array
|
||||
*/
|
||||
private $data = [];
|
||||
|
||||
/**
|
||||
* (optional) temp data for logger (will not be stored with the log entry)
|
||||
* @var array
|
||||
*/
|
||||
private $tmpData = [];
|
||||
|
||||
/**
|
||||
* buffer multiple logs with the same chanelType and store all at once
|
||||
* @var bool
|
||||
*/
|
||||
private $buffer = true;
|
||||
|
||||
|
||||
public function __construct(string $action){
|
||||
$this->setF3();
|
||||
$this->action = $action;
|
||||
|
||||
// add custom log processor callback -> add "extra" (meta) data
|
||||
$f3 = $this->f3;
|
||||
$processorExtraData = function($record) use(&$f3){
|
||||
$record['extra'] = [
|
||||
'path' => $f3->get('PATH'),
|
||||
'ip' => $f3->get('IP')
|
||||
];
|
||||
return $record;
|
||||
};
|
||||
|
||||
// add log processor -> remove §tempData from log
|
||||
$processorClearTempData = function($record){
|
||||
$record['context'] = array_diff_key($record['context'], $this->getTempData());
|
||||
return $record;
|
||||
};
|
||||
|
||||
// init processorConfig. IMPORTANT: first processor gets executed at the end!
|
||||
$this->processorConfig = ['cleaTempData' => $processorClearTempData] + [ 'addExtra' => $processorExtraData] + $this->processorConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* set $f3 base object
|
||||
*/
|
||||
public function setF3(){
|
||||
$this->f3 = \Base::instance();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $message
|
||||
*/
|
||||
public function setMessage(string $message){
|
||||
$this->message = $message;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $level
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function setLevel(string $level){
|
||||
if( in_array($level, self::LEVEL)){
|
||||
$this->level = $level;
|
||||
}else{
|
||||
throw new \Exception( sprintf(self::ERROR_LEVEL, $level));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $tag
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function setTag(string $tag){
|
||||
if( in_array($tag, self::TAG)){
|
||||
$this->tag = $tag;
|
||||
}else{
|
||||
throw new \Exception( sprintf(self::ERROR_TAG, $tag));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
* @return LogInterface
|
||||
*/
|
||||
public function setData(array $data): LogInterface{
|
||||
$this->data = $data;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
* @return LogInterface
|
||||
*/
|
||||
public function setTempData(array $data): LogInterface{
|
||||
$this->tmpData = $data;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* add new Handler by $handlerKey
|
||||
* set its default Formatter by $formatterKey
|
||||
* @param string $handlerKey
|
||||
* @param string|null $formatterKey
|
||||
* @param \stdClass|null $handlerParams
|
||||
* @return LogInterface
|
||||
*/
|
||||
public function addHandler(string $handlerKey, string $formatterKey = null, \stdClass $handlerParams = null): LogInterface{
|
||||
if(!$this->hasHandlerKey($handlerKey)){
|
||||
$this->handlerConfig[$handlerKey] = $formatterKey;
|
||||
// add more configuration params for the new handler
|
||||
if(!is_null($handlerParams)){
|
||||
$this->handlerParamsConfig[$handlerKey] = $handlerParams;
|
||||
}
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* add new handler for Log() grouping
|
||||
* @param string $handlerKey
|
||||
* @return LogInterface
|
||||
*/
|
||||
public function addHandlerGroup(string $handlerKey): LogInterface{
|
||||
if(
|
||||
$this->hasHandlerKey($handlerKey) &&
|
||||
!$this->hasHandlerGroupKey($handlerKey)
|
||||
){
|
||||
$this->handlerGroups[] = $handlerKey;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getHandlerConfig(): array{
|
||||
return $this->handlerConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* get __construct() parameters for a given $handlerKey
|
||||
* @param string $handlerKey
|
||||
* @return array
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function getHandlerParams(string $handlerKey): array{
|
||||
$params = [];
|
||||
|
||||
if($this->hasHandlerKey($handlerKey)){
|
||||
switch($handlerKey){
|
||||
case 'stream': $params = $this->getHandlerParamsStream();
|
||||
break;
|
||||
case 'zmq': $params = $this->getHandlerParamsZMQ();
|
||||
break;
|
||||
case 'mail': $params = $this->getHandlerParamsMail();
|
||||
break;
|
||||
case 'slackMap':
|
||||
case 'slackRally':
|
||||
$params = $this->getHandlerParamsSlack($handlerKey);
|
||||
break;
|
||||
default:
|
||||
throw new \Exception( sprintf(self::ERROR_HANDLER_PARAMS, $handlerKey));
|
||||
}
|
||||
}else{
|
||||
throw new \Exception( sprintf(self::ERROR_HANDLER_KEY, $handlerKey, implode(', ', array_flip($this->handlerConfig))));
|
||||
}
|
||||
|
||||
return $params;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getHandlerParamsConfig(): array{
|
||||
return $this->handlerParamsConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getProcessorConfig(): array{
|
||||
return $this->processorConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getMessage(): string{
|
||||
return $this->message;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getAction(): string{
|
||||
return $this->action;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getChannelType(): string{
|
||||
return $this->channelType;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getChannelName(): string{
|
||||
return $this->getChannelType();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getLevel(): string{
|
||||
return $this->level;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getTag(): string{
|
||||
return $this->tag;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getData(): array{
|
||||
return $this->data;
|
||||
}
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getContext(): array{
|
||||
$context = [
|
||||
'data' => $this->getData(),
|
||||
'tag' => $this->getTag()
|
||||
];
|
||||
|
||||
// add temp data (e.g. used for $message placeholder replacement
|
||||
$context += $this->getTempData();
|
||||
|
||||
return $context;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
protected function getTempData(): array {
|
||||
return $this->tmpData;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getHandlerGroups(): array{
|
||||
return $this->handlerGroups;
|
||||
}
|
||||
|
||||
/**
|
||||
* get unique hash for this kind of logs (channel) and same $handlerGroups
|
||||
* @return string
|
||||
*/
|
||||
public function getGroupHash(): string {
|
||||
$groupName = $this->getChannelName();
|
||||
if($this->isGrouped()){
|
||||
$groupName .= '_' . implode('_', $this->getHandlerGroups());
|
||||
}
|
||||
|
||||
return $this->f3->hash($groupName);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $handlerKey
|
||||
* @return bool
|
||||
*/
|
||||
public function hasHandlerKey(string $handlerKey): bool{
|
||||
return array_key_exists($handlerKey, $this->handlerConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $handlerKey
|
||||
* @return bool
|
||||
*/
|
||||
public function hasHandlerGroupKey(string $handlerKey): bool{
|
||||
return in_array($handlerKey, $this->getHandlerGroups());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function hasBuffer(): bool{
|
||||
return $this->buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isGrouped(): bool{
|
||||
return !empty($this->getHandlerGroups());
|
||||
}
|
||||
|
||||
/**
|
||||
* remove all group handlers and their config params
|
||||
*/
|
||||
public function removeHandlerGroups(){
|
||||
foreach($this->getHandlerGroups() as $handlerKey){
|
||||
$this->removeHandlerGroup($handlerKey);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $handlerKey
|
||||
*/
|
||||
public function removeHandlerGroup(string $handlerKey){
|
||||
unset($this->handlerConfig[$handlerKey]);
|
||||
unset($this->handlerParamsConfig[$handlerKey]);
|
||||
}
|
||||
|
||||
// Handler parameters for Monolog\Handler\AbstractHandler ---------------------------------------------------------
|
||||
protected function getHandlerParamsStream(): array{
|
||||
$params = [];
|
||||
if( !empty($conf = $this->handlerParamsConfig['stream']) ){
|
||||
$params[] = $conf->stream;
|
||||
}
|
||||
|
||||
return $params;
|
||||
}
|
||||
|
||||
/**
|
||||
* get __construct() parameters for ZMQHandler() call
|
||||
* @return array
|
||||
*/
|
||||
protected function getHandlerParamsZMQ(): array {
|
||||
$params = [];
|
||||
if( !empty($conf = $this->handlerParamsConfig['zmq']) ){
|
||||
// meta data (required by receiver socket)
|
||||
$meta = [
|
||||
'logType' => 'mapLog',
|
||||
'stream'=> $conf->streamConf->stream
|
||||
];
|
||||
|
||||
$context = new \ZMQContext();
|
||||
$pusher = $context->getSocket(\ZMQ::SOCKET_PUSH);
|
||||
$pusher->connect($conf->uri);
|
||||
|
||||
$params[] = $pusher;
|
||||
$params[] = \ZMQ::MODE_DONTWAIT;
|
||||
$params[] = false; // multipart
|
||||
$params[] = Logger::toMonologLevel($this->getLevel()); // min level that is handled
|
||||
$params[] = true; // bubble
|
||||
$params[] = $meta;
|
||||
}
|
||||
|
||||
return $params;
|
||||
}
|
||||
|
||||
/**
|
||||
* get __construct() parameters for SwiftMailerHandler() call
|
||||
* @return array
|
||||
*/
|
||||
protected function getHandlerParamsMail(): array{
|
||||
$params = [];
|
||||
if( !empty($conf = $this->handlerParamsConfig['mail']) ){
|
||||
$transport = (new \Swift_SmtpTransport())
|
||||
->setHost($conf->host)
|
||||
->setPort($conf->port)
|
||||
->setEncryption($conf->scheme)
|
||||
->setUsername($conf->username)
|
||||
->setPassword($conf->password)
|
||||
->setStreamOptions([
|
||||
'ssl' => [
|
||||
'allow_self_signed' => true,
|
||||
'verify_peer' => false
|
||||
]
|
||||
]);
|
||||
|
||||
$mailer = new \Swift_Mailer($transport);
|
||||
|
||||
// callback function used instead of Swift_Message() object
|
||||
// -> we want the formatted/replaced message as subject
|
||||
$messageCallback = function($content, $records) use ($conf){
|
||||
$subject = 'No Subject';
|
||||
if(!empty($records)){
|
||||
// build subject from first record -> remove "markdown"
|
||||
$subject = str_replace(['*', '_'], '', $records[0]['message']);
|
||||
}
|
||||
|
||||
$jsonData = @json_encode($records, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
|
||||
|
||||
|
||||
$message = (new \Swift_Message())
|
||||
->setSubject($subject)
|
||||
->addPart($jsonData)
|
||||
->setFrom($conf->from)
|
||||
->setTo($conf->to)
|
||||
->setContentType('text/html')
|
||||
->setCharset('utf-8')
|
||||
->setMaxLineLength(1000);
|
||||
|
||||
if($conf->addJson){
|
||||
$jsonAttachment = (new \Swift_Attachment())
|
||||
->setFilename('data.json')
|
||||
->setContentType('application/json')
|
||||
->setBody($jsonData);
|
||||
$message->attach($jsonAttachment);
|
||||
}
|
||||
|
||||
return $message;
|
||||
};
|
||||
|
||||
$params[] = $mailer;
|
||||
$params[] = $messageCallback;
|
||||
$params[] = Logger::toMonologLevel($this->getLevel()); // min level that is handled
|
||||
$params[] = true; // bubble
|
||||
}
|
||||
|
||||
return $params;
|
||||
}
|
||||
|
||||
/**
|
||||
* get __construct() params for SlackWebhookHandler() call
|
||||
* @param string $handlerKey
|
||||
* @return array
|
||||
*/
|
||||
protected function getHandlerParamsSlack(string $handlerKey): array {
|
||||
$params = [];
|
||||
if( !empty($conf = $this->handlerParamsConfig[$handlerKey]) ){
|
||||
$params[] = $conf->slackWebHookURL;
|
||||
$params[] = $conf->slackChannel;
|
||||
$params[] = $conf->slackUsername;
|
||||
$params[] = true; // $useAttachment
|
||||
$params[] = $conf->slackIcon;
|
||||
$params[] = true; // $includeContext
|
||||
$params[] = false; // $includeExtra
|
||||
$params[] = Logger::toMonologLevel($this->getLevel()); // min level that is handled
|
||||
$params[] = true; // $bubble
|
||||
//$params[] = ['extra', 'context.tag']; // $excludeFields
|
||||
$params[] = []; // $excludeFields
|
||||
}
|
||||
|
||||
return $params;
|
||||
}
|
||||
|
||||
/**
|
||||
* send this Log to global log buffer storage
|
||||
*/
|
||||
public function buffer(){
|
||||
Monolog::instance()->push($this);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
19
app/main/lib/logging/DefaultLog.php
Normal file
19
app/main/lib/logging/DefaultLog.php
Normal file
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: exodu
|
||||
* Date: 04.08.2017
|
||||
* Time: 22:08
|
||||
*/
|
||||
|
||||
namespace Lib\Logging;
|
||||
|
||||
|
||||
class DefaultLog extends AbstractLog {
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $channelType = 'default';
|
||||
|
||||
}
|
||||
178
app/main/lib/logging/LogCollection.php
Normal file
178
app/main/lib/logging/LogCollection.php
Normal file
@@ -0,0 +1,178 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: exodu
|
||||
* Date: 16.09.2017
|
||||
* Time: 11:23
|
||||
*/
|
||||
|
||||
namespace Lib\Logging;
|
||||
|
||||
|
||||
class LogCollection extends AbstractLog {
|
||||
|
||||
const ERROR_EMPTY = __CLASS__ . ' is empty';
|
||||
|
||||
/**
|
||||
* handlers for this collection
|
||||
* -> no default is set
|
||||
* @var array
|
||||
*/
|
||||
protected $handlerConfig = [];
|
||||
|
||||
/**
|
||||
* processors for this collection
|
||||
* -> no default is set
|
||||
* @var array
|
||||
*/
|
||||
protected $processorConfig = [];
|
||||
|
||||
/**
|
||||
* @var null|\SplObjectStorage
|
||||
*/
|
||||
private $collection = null;
|
||||
|
||||
public function __construct(string $action){
|
||||
parent::__construct($action);
|
||||
|
||||
$this->collection = new \SplObjectStorage();
|
||||
}
|
||||
|
||||
/**
|
||||
* get first Log from Collection
|
||||
* @return AbstractLog
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function getPrimaryLog(): AbstractLog{
|
||||
$this->collection->rewind();
|
||||
if($this->collection->valid()){
|
||||
/**
|
||||
* @var $log AbstractLog
|
||||
*/
|
||||
$log = $this->collection->current();
|
||||
}else{
|
||||
throw new \Exception( self::ERROR_EMPTY);
|
||||
}
|
||||
|
||||
return $log;
|
||||
}
|
||||
|
||||
/**
|
||||
* add a new log object to this collection
|
||||
* @param AbstractLog $log
|
||||
*/
|
||||
public function addLog(AbstractLog $log){
|
||||
if(!$this->collection->contains($log)){
|
||||
if(!$this->collection->count()){
|
||||
// first log sets the default for this collection
|
||||
$this->channelType = $log->getChannelType();
|
||||
|
||||
// get relevant handlerKeys for this collection
|
||||
$handlerGroups = array_flip($log->getHandlerGroups());
|
||||
|
||||
// remove handlers that are not relevant for this collection
|
||||
$handlerConfig = $log->getHandlerConfig();
|
||||
$handlerConfigGroup = array_intersect_key($handlerConfig, $handlerGroups);
|
||||
|
||||
// remove handlersParams that are not relevant for this collection
|
||||
$handlerParamsConfig = $log->getHandlerParamsConfig();
|
||||
$handlerParamsConfigGroup = array_intersect_key($handlerParamsConfig, $handlerGroups);
|
||||
|
||||
// add all handlers that are relevant for this collection
|
||||
foreach($handlerConfigGroup as $handlerKey => $formatterKey){
|
||||
$handlerParams = array_key_exists($handlerKey, $handlerParamsConfigGroup) ? $handlerParamsConfigGroup[$handlerKey] : null;
|
||||
$this->addHandler($handlerKey, $formatterKey, $handlerParams);
|
||||
}
|
||||
|
||||
// add processors for this collection
|
||||
$this->processorConfig = $log->getProcessorConfig();
|
||||
}
|
||||
|
||||
$this->setMessage($log->getMessage());
|
||||
$this->setTag($log->getTag());
|
||||
|
||||
$this->collection->attach($log);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $message
|
||||
*/
|
||||
public function setMessage(string $message){
|
||||
$currentMessage = parent::getMessage();
|
||||
if(empty($currentMessage)){
|
||||
$newMessage = $message;
|
||||
}elseif($message !== $currentMessage){
|
||||
$newMessage = 'multi changes';
|
||||
}else{
|
||||
$newMessage = $currentMessage ;
|
||||
}
|
||||
|
||||
parent::setMessage($newMessage);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $tag
|
||||
*/
|
||||
public function setTag(string $tag){
|
||||
$currentTag = parent::getTag();
|
||||
switch($currentTag){
|
||||
case 'default':
|
||||
// no specific tag set so far... set new
|
||||
$newTag = $tag; break;
|
||||
case 'information':
|
||||
// do not change "information" tag (mixed tag logs in this collection)
|
||||
$newTag = $currentTag; break;
|
||||
default:
|
||||
// set mixed tag -> "information"
|
||||
$newTag = ($tag !== $currentTag) ? 'information': $tag;
|
||||
}
|
||||
|
||||
parent::setTag($newTag);
|
||||
}
|
||||
|
||||
/**
|
||||
* get log data for all logs in this collection
|
||||
* @return array
|
||||
*/
|
||||
public function getData() : array{
|
||||
$this->collection->rewind();
|
||||
$data = [];
|
||||
while($this->collection->valid()){
|
||||
$data[] = $this->collection->current()->getData();
|
||||
$this->collection->next();
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getChannelName() : string{
|
||||
return $this->getPrimaryLog()->getChannelName();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getLevel() : string{
|
||||
return $this->getPrimaryLog()->getLevel();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function hasBuffer() : bool{
|
||||
return $this->getPrimaryLog()->hasBuffer();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getTempData() : array{
|
||||
return $this->getPrimaryLog()->getTempData();
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
64
app/main/lib/logging/LogInterface.php
Normal file
64
app/main/lib/logging/LogInterface.php
Normal file
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: exodu
|
||||
* Date: 05.08.2017
|
||||
* Time: 14:10
|
||||
*/
|
||||
|
||||
namespace Lib\Logging;
|
||||
|
||||
|
||||
interface LogInterface {
|
||||
|
||||
public function setMessage(string $message);
|
||||
|
||||
public function setLevel(string $level);
|
||||
|
||||
public function setData(array $data): LogInterface;
|
||||
|
||||
public function setTempData(array $data): LogInterface;
|
||||
|
||||
public function addHandler(string $handlerKey, string $formatterKey = null, \stdClass $handlerParams = null): LogInterface;
|
||||
|
||||
public function addHandlerGroup(string $handlerKey): LogInterface;
|
||||
|
||||
public function getHandlerConfig(): array;
|
||||
|
||||
public function getHandlerParamsConfig(): array;
|
||||
|
||||
public function getProcessorConfig(): array;
|
||||
|
||||
public function getHandlerParams(string $handlerKey): array;
|
||||
|
||||
public function getMessage(): string;
|
||||
|
||||
public function getAction(): string;
|
||||
|
||||
public function getChannelType(): string;
|
||||
|
||||
public function getChannelName(): string;
|
||||
|
||||
public function getLevel(): string;
|
||||
|
||||
public function getData(): array;
|
||||
|
||||
public function getContext(): array;
|
||||
|
||||
public function getHandlerGroups(): array;
|
||||
|
||||
public function getGroupHash(): string;
|
||||
|
||||
public function hasHandlerKey(string $handlerKey): bool;
|
||||
|
||||
public function hasHandlerGroupKey(string $handlerKey): bool;
|
||||
|
||||
public function hasBuffer(): bool;
|
||||
|
||||
public function isGrouped(): bool;
|
||||
|
||||
public function removeHandlerGroups();
|
||||
|
||||
public function removeHandlerGroup(string $handlerKey);
|
||||
|
||||
}
|
||||
166
app/main/lib/logging/MapLog.php
Normal file
166
app/main/lib/logging/MapLog.php
Normal file
@@ -0,0 +1,166 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: exodu
|
||||
* Date: 04.08.2017
|
||||
* Time: 22:08
|
||||
*/
|
||||
|
||||
namespace Lib\Logging;
|
||||
|
||||
|
||||
use controller\LogController;
|
||||
|
||||
class MapLog extends AbstractCharacterLog{
|
||||
|
||||
/**
|
||||
* List of possible handlers (tested)
|
||||
* -> final handler will be set dynamic for per instance
|
||||
* @var array
|
||||
*/
|
||||
protected $handlerConfig = [
|
||||
//'stream' => 'json',
|
||||
//'zmq' => 'json',
|
||||
//'slackMap' => 'json'
|
||||
];
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $channelType = 'map';
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $logActivity = false;
|
||||
|
||||
|
||||
public function __construct(string $action, array $objectData){
|
||||
parent::__construct($action, $objectData);
|
||||
|
||||
$this->setLevel('info');
|
||||
$this->setTag($this->getTagFromAction());
|
||||
}
|
||||
|
||||
/**
|
||||
* get log tag depending on log action
|
||||
* @return string
|
||||
*/
|
||||
public function getTagFromAction(){
|
||||
$tag = parent::getTag();
|
||||
$actionParts = $this->getActionParts();
|
||||
switch($actionParts[1]){
|
||||
case 'create': $tag = 'success'; break;
|
||||
case 'update': $tag = 'warning'; break;
|
||||
case 'delete': $tag = 'danger'; break;
|
||||
}
|
||||
|
||||
return $tag;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getChannelName(): string{
|
||||
return $this->getChannelType() . '_' . $this->getChannelId();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getMessage() : string{
|
||||
return $this->getActionParts()[0] . " '{objName}'";
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getData() : array{
|
||||
$data = parent::getData();
|
||||
|
||||
// add system, connection, signature data -------------------------------------------------
|
||||
if(!empty($tempLogData = $this->getTempData())){
|
||||
$objectData['object'] = $tempLogData;
|
||||
$data = $objectData + $data;
|
||||
}
|
||||
|
||||
// add human readable changes to string ---------------------------------------------------
|
||||
$data['formatted'] = $this->formatData($data);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
* @return string
|
||||
*/
|
||||
protected function formatData(array $data): string{
|
||||
$actionParts = $this->getActionParts();
|
||||
$objectString = !empty($data['object']) ? "'" . $data['object']['objName'] . "'" . ' #' . $data['object']['objId'] : '';
|
||||
$string = ucfirst($actionParts[1]) . 'd ' . $actionParts[0] . " " . $objectString;
|
||||
|
||||
// format changed columns (recursive) ---------------------------------------------
|
||||
switch($actionParts[1]){
|
||||
case 'create':
|
||||
case 'update':
|
||||
$formatChanges = function(array $changes) use ( &$formatChanges ): string{
|
||||
$string = '';
|
||||
foreach($changes as $field => $value){
|
||||
if(is_array($value)){
|
||||
$string .= $field . ": ";
|
||||
$string .= $formatChanges($value);
|
||||
$string .= next( $changes ) ? " , " : '';
|
||||
}else{
|
||||
if(is_numeric($value)){
|
||||
$formattedValue = $value;
|
||||
}elseif(is_null($value)){
|
||||
$formattedValue = "NULL";
|
||||
}elseif(empty($value)){
|
||||
$formattedValue = "' '";
|
||||
}elseif(is_string($value)){
|
||||
$formattedValue = "'" . $value . "'";
|
||||
}else{
|
||||
$formattedValue = (string)$value;
|
||||
}
|
||||
|
||||
$string .= $formattedValue;
|
||||
if($field == 'old'){
|
||||
$string .= " ➜ ";
|
||||
}
|
||||
}
|
||||
}
|
||||
return $string;
|
||||
};
|
||||
|
||||
$string .= ' | ' . $formatChanges($data['main']);
|
||||
break;
|
||||
}
|
||||
|
||||
return $string;
|
||||
}
|
||||
|
||||
/**
|
||||
* split $action "CamelCase" wise
|
||||
* @return array
|
||||
*/
|
||||
protected function getActionParts(): array{
|
||||
return array_map('strtolower', preg_split('/(?=[A-Z])/', $this->getAction()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $logActivity
|
||||
*/
|
||||
public function logActivity(bool $logActivity){
|
||||
$this->logActivity = $logActivity;
|
||||
}
|
||||
|
||||
public function buffer(){
|
||||
parent::buffer();
|
||||
|
||||
if($this->logActivity){
|
||||
// map logs should also used for "activity" logging
|
||||
LogController::instance()->push($this);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
105
app/main/lib/logging/RallyLog.php
Normal file
105
app/main/lib/logging/RallyLog.php
Normal file
@@ -0,0 +1,105 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: exodu
|
||||
* Date: 22.09.2017
|
||||
* Time: 16:50
|
||||
*/
|
||||
|
||||
namespace Lib\Logging;
|
||||
|
||||
use lib\Config;
|
||||
|
||||
class RallyLog extends AbstractCharacterLog{
|
||||
|
||||
/**
|
||||
* List of possible handlers (tested)
|
||||
* -> final handler will be set dynamic for per instance
|
||||
* @var array
|
||||
*/
|
||||
protected $handlerConfig = [
|
||||
// 'slackRally' => 'json',
|
||||
// 'mail' => 'html'
|
||||
];
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $channelType = 'rally';
|
||||
|
||||
|
||||
public function __construct(string $action, array $objectData){
|
||||
parent::__construct($action, $objectData);
|
||||
|
||||
$this->setLevel('notice');
|
||||
$this->setTag('information');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
protected function getThumbUrl() : string{
|
||||
$url = '';
|
||||
if(is_object($character = $this->getCharacter())){
|
||||
$characterLog = $character->getLog();
|
||||
if($characterLog && !empty($characterLog->shipTypeId)){
|
||||
$url = Config::getPathfinderData('api.ccp_image_server') . '/Render/' . $characterLog->shipTypeId . '_64.png';
|
||||
}else{
|
||||
$url = parent::getThumbUrl();
|
||||
}
|
||||
}
|
||||
|
||||
return $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getMessage() : string{
|
||||
return "*New RallyPoint system '{objName}'* _#{objId}_ *map '{channelName}'* _#{channelId}_ ";
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getData() : array{
|
||||
$data = parent::getData();
|
||||
|
||||
// add system -----------------------------------------------------------------------------
|
||||
if(!empty($tempLogData = $this->getTempData())){
|
||||
$objectData['object'] = $tempLogData;
|
||||
$data = $objectData + $data;
|
||||
}
|
||||
|
||||
// add human readable changes to string ---------------------------------------------------
|
||||
$data['formatted'] =$this->formatData($data);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
* @return string
|
||||
*/
|
||||
protected function formatData(array $data): string{
|
||||
$string = '';
|
||||
|
||||
if(
|
||||
!empty($data['object']) &&
|
||||
!empty($data['channel'])
|
||||
){
|
||||
$replace = [
|
||||
'{objName}' => $data['object']['objName'],
|
||||
'{objId}' => $data['object']['objId'],
|
||||
'{channelName}' => $data['channel']['channelName'],
|
||||
'{channelId}' => $data['channel']['channelId']
|
||||
];
|
||||
$string = str_replace(array_keys($replace), array_values($replace), $this->getMessage());
|
||||
}
|
||||
|
||||
return $string;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
36
app/main/lib/logging/UserLog.php
Normal file
36
app/main/lib/logging/UserLog.php
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: exodu
|
||||
* Date: 07.10.2017
|
||||
* Time: 14:15
|
||||
*/
|
||||
|
||||
namespace Lib\Logging;
|
||||
|
||||
|
||||
class UserLog extends AbstractChannelLog {
|
||||
|
||||
/**
|
||||
* List of possible handlers (tested)
|
||||
* -> final handler will be set dynamic for per instance
|
||||
* @var array
|
||||
*/
|
||||
protected $handlerConfig = [
|
||||
// 'mail' => 'html'
|
||||
];
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $channelType = 'user';
|
||||
|
||||
public function __construct(string $action, array $objectData){
|
||||
parent::__construct($action, $objectData);
|
||||
|
||||
$this->setLevel('notice');
|
||||
$this->setTag('information');
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
46
app/main/lib/logging/formatter/MailFormatter.php
Normal file
46
app/main/lib/logging/formatter/MailFormatter.php
Normal file
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: exodu
|
||||
* Date: 07.10.2017
|
||||
* Time: 14:49
|
||||
*/
|
||||
|
||||
namespace Lib\Logging\Formatter;
|
||||
|
||||
use lib\Config;
|
||||
use Monolog\Formatter;
|
||||
|
||||
class MailFormatter implements Formatter\FormatterInterface {
|
||||
|
||||
public function format(array $record){
|
||||
|
||||
$tplDefaultData = [
|
||||
'tplPretext' => $record['message'],
|
||||
'tplGreeting' => \Markdown::instance()->convert(str_replace('*', '', $record['message'])),
|
||||
'message' => false,
|
||||
'tplText2' => false,
|
||||
'tplClosing' => 'Fly save!',
|
||||
'actionPrimary' => false,
|
||||
'appName' => Config::getPathfinderData('name'),
|
||||
'appUrl' => Config::getEnvironmentData('URL'),
|
||||
'appHost' => $_SERVER['HTTP_HOST'],
|
||||
'appContact' => Config::getPathfinderData('contact'),
|
||||
'appMail' => Config::getPathfinderData('email'),
|
||||
];
|
||||
|
||||
$tplData = array_replace_recursive($tplDefaultData, (array)$record['context']['data']['main']);
|
||||
|
||||
return \Template::instance()->render('templates/mail/basic_inline.html', 'text/html', $tplData);
|
||||
}
|
||||
|
||||
public function formatBatch(array $records){
|
||||
$message = '';
|
||||
foreach ($records as $key => $record) {
|
||||
$message .= $this->format($record);
|
||||
}
|
||||
|
||||
return $message;
|
||||
}
|
||||
|
||||
}
|
||||
260
app/main/lib/logging/handler/AbstractSlackWebhookHandler.php
Normal file
260
app/main/lib/logging/handler/AbstractSlackWebhookHandler.php
Normal file
@@ -0,0 +1,260 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: exodu
|
||||
* Date: 22.09.2017
|
||||
* Time: 20:08
|
||||
*/
|
||||
|
||||
namespace Lib\Logging\Handler;
|
||||
|
||||
use lib\Config;
|
||||
use Monolog\Handler;
|
||||
use Monolog\Logger;
|
||||
|
||||
abstract class AbstractSlackWebhookHandler extends Handler\AbstractProcessingHandler {
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $webhookUrl;
|
||||
|
||||
/**
|
||||
* Slack channel (encoded ID or name)
|
||||
* @var string|null
|
||||
*/
|
||||
private $channel;
|
||||
|
||||
/**
|
||||
* Name of a bot
|
||||
* @var string|null
|
||||
*/
|
||||
private $username;
|
||||
|
||||
/**
|
||||
* User icon e.g. 'ghost', 'http://example.com/user.png'
|
||||
* @var string
|
||||
*/
|
||||
private $userIcon;
|
||||
|
||||
/**
|
||||
* Whether the message should be added to Slack as attachment (plain text otherwise)
|
||||
* @var bool
|
||||
*/
|
||||
protected $useAttachment;
|
||||
|
||||
/**
|
||||
* Whether the attachment should include context
|
||||
* @var bool
|
||||
*/
|
||||
protected $includeContext;
|
||||
|
||||
/**
|
||||
* Whether the attachment should include extra
|
||||
* @var bool
|
||||
*/
|
||||
protected $includeExtra;
|
||||
|
||||
/**
|
||||
* Dot separated list of fields to exclude from slack message. E.g. ['context.field1', 'extra.field2']
|
||||
* @var array
|
||||
*/
|
||||
private $excludeFields;
|
||||
|
||||
/**
|
||||
* Max attachment count per message (20 is max)
|
||||
* @var int
|
||||
*/
|
||||
private $maxAttachments = 15;
|
||||
|
||||
/**
|
||||
* @param string $webhookUrl Slack Webhook URL
|
||||
* @param string|null $channel Slack channel (encoded ID or name)
|
||||
* @param string|null $username Name of a bot
|
||||
* @param bool $useAttachment Whether the message should be added to Slack as attachment (plain text otherwise)
|
||||
* @param string|null $iconEmoji The emoji name to use (or null)
|
||||
* @param bool $includeContext Whether the context data added to Slack as attachments are in a short style
|
||||
* @param bool $includeExtra Whether the extra data added to Slack as attachments are in a short style
|
||||
* @param int $level The minimum logging level at which this handler will be triggered
|
||||
* @param bool $bubble Whether the messages that are handled can bubble up the stack or not
|
||||
* @param array $excludeFields Dot separated list of fields to exclude from slack message. E.g. ['context.field1', 'extra.field2']
|
||||
*/
|
||||
public function __construct($webhookUrl, $channel = null, $username = null, $useAttachment = true, $iconEmoji = null, $includeContext = true, $includeExtra = false, $level = Logger::CRITICAL, $bubble = true, array $excludeFields = []){
|
||||
$this->webhookUrl = $webhookUrl;
|
||||
$this->channel = $channel;
|
||||
$this->username = $username;
|
||||
$this->userIcon = trim($iconEmoji, ':');
|
||||
$this->useAttachment = $useAttachment;
|
||||
$this->includeContext = $includeContext;
|
||||
$this->includeExtra = $includeExtra;
|
||||
$this->excludeFields = $excludeFields;
|
||||
|
||||
parent::__construct($level, $bubble);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* format
|
||||
* @param array $record
|
||||
* @return array
|
||||
*/
|
||||
protected function getSlackData(array $record): array {
|
||||
$postData = [];
|
||||
|
||||
if ($this->username) {
|
||||
$postData['username'] = $this->username;
|
||||
}
|
||||
|
||||
if ($this->channel) {
|
||||
$postData['channel'] = $this->channel;
|
||||
}
|
||||
|
||||
$postData['text'] = (string)$record['message'];
|
||||
|
||||
if ($this->userIcon) {
|
||||
if (filter_var($this->userIcon, FILTER_VALIDATE_URL)) {
|
||||
$postData['icon_url'] = $this->userIcon;
|
||||
} else {
|
||||
$postData['icon_emoji'] = ":{$this->userIcon}:";
|
||||
}
|
||||
}
|
||||
|
||||
return $postData;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @param array $record
|
||||
*/
|
||||
protected function write(array $record){
|
||||
$record = $this->excludeFields($record);
|
||||
|
||||
$postData = $this->getSlackData($record);
|
||||
|
||||
$postData = $this->cleanAttachments($postData);
|
||||
|
||||
$postString = json_encode($postData);
|
||||
|
||||
$ch = curl_init();
|
||||
$options = [
|
||||
CURLOPT_URL => $this->webhookUrl,
|
||||
CURLOPT_CUSTOMREQUEST => 'POST',
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_HTTPHEADER => ['Content-type: application/json'],
|
||||
CURLOPT_POSTFIELDS => $postString
|
||||
];
|
||||
if (defined('CURLOPT_SAFE_UPLOAD')) {
|
||||
$options[CURLOPT_SAFE_UPLOAD] = true;
|
||||
}
|
||||
|
||||
curl_setopt_array($ch, $options);
|
||||
|
||||
Handler\Curl\Util::execute($ch);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $postData
|
||||
* @return array
|
||||
*/
|
||||
protected function cleanAttachments(array $postData): array{
|
||||
$attachmentCount = count($postData['attachments']);
|
||||
if( $attachmentCount > $this->maxAttachments){
|
||||
$text = 'To many attachments! ' . ($attachmentCount - $this->maxAttachments) . ' of ' . $attachmentCount . ' attachments not visible';
|
||||
$postData['attachments'] = array_slice($postData['attachments'], 0, $this->maxAttachments);
|
||||
|
||||
$attachment = [
|
||||
'title' => $text,
|
||||
'fallback' => $text,
|
||||
'color' => $this->getAttachmentColor('information')
|
||||
];
|
||||
|
||||
$postData['attachments'][] = $attachment;
|
||||
}
|
||||
|
||||
return $postData;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $attachment
|
||||
* @param array $characterData
|
||||
* @return array
|
||||
*/
|
||||
protected function setAuthor(array $attachment, array $characterData): array {
|
||||
if( !empty($characterData['id']) && !empty($characterData['name'])){
|
||||
$attachment['author_name'] = $characterData['name'] . ' #' . $characterData['id'];
|
||||
$attachment['author_link'] = Config::getPathfinderData('api.z_killboard') . '/character/' . $characterData['id'] . '/';
|
||||
$attachment['author_icon'] = Config::getPathfinderData('api.ccp_image_server') . '/Character/' . $characterData['id'] . '_32.jpg';
|
||||
}
|
||||
|
||||
return $attachment;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $attachment
|
||||
* @param array $thumbData
|
||||
* @return array
|
||||
*/
|
||||
protected function setThumb(array $attachment, array $thumbData): array {
|
||||
if( !empty($thumbData['url'])) {
|
||||
$attachment['thumb_url'] = $thumbData['url'];
|
||||
}
|
||||
|
||||
return $attachment;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $title
|
||||
* @param $value
|
||||
* @param bool $format
|
||||
* @param bool $short
|
||||
* @return array
|
||||
*/
|
||||
protected function generateAttachmentField($title, $value, $format = false, $short = true){
|
||||
return [
|
||||
'title' => $title,
|
||||
'value' => !empty($value) ? ( $format ? sprintf('`%s`', $value) : $value ) : '',
|
||||
'short' => $short
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $tag
|
||||
* @return string
|
||||
*/
|
||||
protected function getAttachmentColor(string $tag): string {
|
||||
switch($tag){
|
||||
case 'information': $color = '#428bca'; break;
|
||||
case 'success': $color = '#4f9e4f'; break;
|
||||
case 'warning': $color = '#e28a0d'; break;
|
||||
case 'danger': $color = '#a52521'; break;
|
||||
default: $color = '#313335'; break;
|
||||
}
|
||||
return $color;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a copy of record with fields excluded according to $this->excludeFields
|
||||
* @param array $record
|
||||
* @return array
|
||||
*/
|
||||
private function excludeFields(array $record){
|
||||
foreach($this->excludeFields as $field){
|
||||
$keys = explode('.', $field);
|
||||
$node = &$record;
|
||||
$lastKey = end($keys);
|
||||
foreach($keys as $key){
|
||||
if(!isset($node[$key])){
|
||||
break;
|
||||
}
|
||||
if($lastKey === $key){
|
||||
unset($node[$key]);
|
||||
break;
|
||||
}
|
||||
$node = &$node[$key];
|
||||
}
|
||||
}
|
||||
|
||||
return $record;
|
||||
}
|
||||
}
|
||||
101
app/main/lib/logging/handler/SlackMapWebhookHandler.php
Normal file
101
app/main/lib/logging/handler/SlackMapWebhookHandler.php
Normal file
@@ -0,0 +1,101 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: exodu
|
||||
* Date: 10.09.2017
|
||||
* Time: 20:52
|
||||
*/
|
||||
|
||||
namespace Lib\Logging\Handler;
|
||||
|
||||
use Lib\Util;
|
||||
|
||||
class SlackMapWebhookHandler extends AbstractSlackWebhookHandler {
|
||||
|
||||
/**
|
||||
* @param array $record
|
||||
* @return array
|
||||
*/
|
||||
protected function getSlackData(array $record) : array{
|
||||
$postData = parent::getSlackData($record);
|
||||
|
||||
$tag = (string)$record['context']['tag'];
|
||||
$timestamp = (int)$record['datetime']->getTimestamp();
|
||||
$text = '';
|
||||
|
||||
if (
|
||||
$this->useAttachment &&
|
||||
!empty( $attachmentsData = $record['context']['data'])
|
||||
) {
|
||||
|
||||
// convert non grouped data (associative array) to multi dimensional (sequential) array
|
||||
// -> see "group" records
|
||||
$attachmentsData = Util::is_assoc($attachmentsData) ? [$attachmentsData] : $attachmentsData;
|
||||
|
||||
$thumbData = (array)$record['extra']['thumb'];
|
||||
|
||||
$postData['attachments'] = [];
|
||||
|
||||
foreach($attachmentsData as $attachmentData){
|
||||
$channelData = (array)$attachmentData['channel'];
|
||||
$characterData = (array)$attachmentData['character'];
|
||||
$formatted = (string)$attachmentData['formatted'];
|
||||
|
||||
// get "message" from $formatted
|
||||
$msgParts = explode('|', $formatted, 2);
|
||||
|
||||
// build main text from first Attachment (they belong to same channel)
|
||||
if(!empty($channelData)){
|
||||
$text = "*Map '" . $channelData['channelName'] . "'* _#" . $channelData['channelId'] . "_ *changed*";
|
||||
}
|
||||
|
||||
$attachment = [
|
||||
'title' => !empty($msgParts[0]) ? $msgParts[0] : 'No Title',
|
||||
//'pretext' => '',
|
||||
'text' => !empty($msgParts[1]) ? sprintf('```%s```', $msgParts[1]) : '',
|
||||
'fallback' => !empty($msgParts[1]) ? $msgParts[1] : 'No Fallback',
|
||||
'color' => $this->getAttachmentColor($tag),
|
||||
'fields' => [],
|
||||
'mrkdwn_in' => ['fields', 'text'],
|
||||
'footer' => 'Pathfinder API',
|
||||
//'footer_icon'=> '',
|
||||
'ts' => $timestamp
|
||||
];
|
||||
|
||||
$attachment = $this->setAuthor($attachment, $characterData);
|
||||
$attachment = $this->setThumb($attachment, $thumbData);
|
||||
|
||||
|
||||
// set 'field' array ----------------------------------------------------------------------------------
|
||||
if ($this->includeExtra) {
|
||||
$attachment['fields'][] = $this->generateAttachmentField('', 'Meta data:', false, false);
|
||||
|
||||
if(!empty($record['extra']['path'])){
|
||||
$attachment['fields'][] = $this->generateAttachmentField('Path', $record['extra']['path'], true);
|
||||
}
|
||||
|
||||
if(!empty($tag)){
|
||||
$attachment['fields'][] = $this->generateAttachmentField('Tag', $tag, true);
|
||||
}
|
||||
|
||||
if(!empty($record['level_name'])){
|
||||
$attachment['fields'][] = $this->generateAttachmentField('Level', $record['level_name'], true);
|
||||
}
|
||||
|
||||
if(!empty($record['extra']['ip'])){
|
||||
$attachment['fields'][] = $this->generateAttachmentField('IP', $record['extra']['ip'], true);
|
||||
}
|
||||
}
|
||||
|
||||
$postData['attachments'][] = $attachment;
|
||||
}
|
||||
}
|
||||
|
||||
$postData['text'] = empty($text) ? $postData['text'] : $text;
|
||||
|
||||
|
||||
return $postData;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
135
app/main/lib/logging/handler/SlackRallyWebhookHandler.php
Normal file
135
app/main/lib/logging/handler/SlackRallyWebhookHandler.php
Normal file
@@ -0,0 +1,135 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: exodu
|
||||
* Date: 22.09.2017
|
||||
* Time: 17:32
|
||||
*/
|
||||
|
||||
namespace Lib\Logging\Handler;
|
||||
|
||||
use Lib\Util;
|
||||
|
||||
class SlackRallyWebhookHandler extends AbstractSlackWebhookHandler {
|
||||
|
||||
/**
|
||||
* @param array $record
|
||||
* @return array
|
||||
*/
|
||||
protected function getSlackData(array $record) : array{
|
||||
$postData = parent::getSlackData($record);
|
||||
|
||||
$tag = (string)$record['context']['tag'];
|
||||
$timestamp = (int)$record['datetime']->getTimestamp();
|
||||
$text = '';
|
||||
|
||||
if (
|
||||
$this->useAttachment &&
|
||||
!empty( $attachmentsData = $record['context']['data'])
|
||||
){
|
||||
// convert non grouped data (associative array) to multi dimensional (sequential) array
|
||||
// -> see "group" records
|
||||
$attachmentsData = Util::is_assoc($attachmentsData) ? [$attachmentsData] : $attachmentsData;
|
||||
|
||||
$thumbData = (array)$record['extra']['thumb'];
|
||||
|
||||
$postData['attachments'] = [];
|
||||
|
||||
foreach($attachmentsData as $attachmentData){
|
||||
$characterData = (array)$attachmentData['character'];
|
||||
|
||||
$text = 'No Title';
|
||||
if( !empty($attachmentData['formatted']) ){
|
||||
$text = $attachmentData['formatted'];
|
||||
}
|
||||
|
||||
$attachment = [
|
||||
'title' => !empty($attachmentData['main']['message']) ? 'Message' : '',
|
||||
//'pretext' => '',
|
||||
'text' => !empty($attachmentData['main']['message']) ? sprintf('```%s```', $attachmentData['main']['message']) : '',
|
||||
'fallback' => !empty($attachmentData['main']['message']) ? $attachmentData['main']['message'] : 'No Fallback',
|
||||
'color' => $this->getAttachmentColor($tag),
|
||||
'fields' => [],
|
||||
'mrkdwn_in' => ['fields', 'text'],
|
||||
'footer' => 'Pathfinder API',
|
||||
//'footer_icon'=> '',
|
||||
'ts' => $timestamp
|
||||
];
|
||||
|
||||
$attachment = $this->setAuthor($attachment, $characterData);
|
||||
$attachment = $this->setThumb($attachment, $thumbData);
|
||||
|
||||
// set 'field' array ----------------------------------------------------------------------------------
|
||||
if ($this->includeContext) {
|
||||
if(!empty($objectData = $attachmentData['object'])){
|
||||
if(!empty($objectData['objAlias'])){
|
||||
// System alias
|
||||
$attachment['fields'][] = $this->generateAttachmentField('Alias', $objectData['objAlias']);
|
||||
}
|
||||
|
||||
if(!empty($objectData['objName'])){
|
||||
// System name
|
||||
$attachment['fields'][] = $this->generateAttachmentField('System', $objectData['objName']);
|
||||
}
|
||||
|
||||
if(!empty($objectData['objRegion'])){
|
||||
// System region
|
||||
$attachment['fields'][] = $this->generateAttachmentField('Region', $objectData['objRegion']);
|
||||
}
|
||||
|
||||
if(isset($objectData['objIsWormhole'])){
|
||||
// Is wormhole
|
||||
$attachment['fields'][] = $this->generateAttachmentField('Wormhole', $objectData['objIsWormhole'] ? 'Yes' : 'No');
|
||||
}
|
||||
|
||||
if(!empty($objectData['objSecurity'])){
|
||||
// System security
|
||||
$attachment['fields'][] = $this->generateAttachmentField('Security', $objectData['objSecurity']);
|
||||
}
|
||||
|
||||
if(!empty($objectData['objEffect'])){
|
||||
// System effect
|
||||
$attachment['fields'][] = $this->generateAttachmentField('Effect', $objectData['objEffect']);
|
||||
}
|
||||
|
||||
if(!empty($objectData['objTrueSec'])){
|
||||
// System trueSec
|
||||
$attachment['fields'][] = $this->generateAttachmentField('TrueSec', $objectData['objTrueSec']);
|
||||
}
|
||||
|
||||
if(!empty($objectData['objDescription'])){
|
||||
// System trueSec
|
||||
$attachment['fields'][] = $this->generateAttachmentField('System description', '```' . $objectData['objDescription'] . '```', false, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if($this->includeExtra){
|
||||
if(!empty($record['extra']['path'])){
|
||||
$attachment['fields'][] = $this->generateAttachmentField('Path', $record['extra']['path'], true);
|
||||
}
|
||||
|
||||
if(!empty($tag)){
|
||||
$attachment['fields'][] = $this->generateAttachmentField('Tag', $tag, true);
|
||||
}
|
||||
|
||||
if(!empty($record['level_name'])){
|
||||
$attachment['fields'][] = $this->generateAttachmentField('Level', $record['level_name'], true);
|
||||
}
|
||||
|
||||
if(!empty($record['extra']['ip'])){
|
||||
$attachment['fields'][] = $this->generateAttachmentField('IP', $record['extra']['ip'], true);
|
||||
}
|
||||
}
|
||||
|
||||
$postData['attachments'][] = $attachment;
|
||||
}
|
||||
}
|
||||
|
||||
$postData['text'] = empty($text) ? $postData['text'] : $text;
|
||||
|
||||
return $postData;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
65
app/main/lib/logging/handler/ZMQHandler.php
Normal file
65
app/main/lib/logging/handler/ZMQHandler.php
Normal file
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: exodu
|
||||
* Date: 03.09.2017
|
||||
* Time: 17:39
|
||||
*/
|
||||
|
||||
namespace Lib\Logging\Handler;
|
||||
|
||||
|
||||
use Monolog\Logger;
|
||||
|
||||
class ZMQHandler extends \Websoftwares\Monolog\Handler\ZMQHandler {
|
||||
|
||||
|
||||
/**
|
||||
* some meta data (additional processing information)
|
||||
* @var array|string
|
||||
*/
|
||||
protected $metaData = [];
|
||||
|
||||
public function __construct(
|
||||
\zmqSocket $zmqSocket,
|
||||
$zmqMode = \ZMQ::MODE_DONTWAIT,
|
||||
$multipart = false,
|
||||
$level = Logger::DEBUG,
|
||||
$bubble = true,
|
||||
$metaData = []
|
||||
){
|
||||
$this->metaData = $metaData;
|
||||
|
||||
parent::__construct($zmqSocket, $zmqMode, $multipart, $level, $bubble);
|
||||
}
|
||||
|
||||
/**
|
||||
* overwrite default handle()
|
||||
* -> change data structure after processor() calls and before formatter() calls
|
||||
* @param array $record
|
||||
* @return bool
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function handle(array $record){
|
||||
if (!$this->isHandling($record)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$record = $this->processRecord($record);
|
||||
|
||||
$record = [
|
||||
'task' => 'logData',
|
||||
'load' => [
|
||||
'meta' => $this->metaData,
|
||||
'log' => $record
|
||||
]
|
||||
];
|
||||
|
||||
$record['formatted'] = $this->getFormatter()->format($record);
|
||||
|
||||
$this->write($record);
|
||||
|
||||
return false === $this->bubble;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -79,27 +79,19 @@ class Socket {
|
||||
/**
|
||||
* init new socket
|
||||
*/
|
||||
/*
|
||||
public function initSocket(){
|
||||
if(self::checkRequirements()){
|
||||
$context = new \ZMQContext();
|
||||
$this->socket = $context->getSocket(\ZMQ::SOCKET_REQ);
|
||||
// The linger value of the socket. Specifies how long the socket blocks trying flush messages after it has been closed
|
||||
$this->socket->setSockOpt(\ZMQ::SOCKOPT_LINGER, 0);
|
||||
}
|
||||
} */
|
||||
|
||||
/**
|
||||
* init new socket
|
||||
*/
|
||||
public function initSocket(){
|
||||
if(self::checkRequirements()){
|
||||
if(Config::checkSocketRequirements()){
|
||||
$context = new \ZMQContext();
|
||||
$this->socket = $context->getSocket(\ZMQ::SOCKET_PUSH);
|
||||
}
|
||||
}
|
||||
|
||||
public function sendData($task, $load = ''){
|
||||
/**
|
||||
* @param $task
|
||||
* @param string $load
|
||||
* @return bool|string
|
||||
*/
|
||||
public function sendData(string $task, $load = ''){
|
||||
$response = false;
|
||||
|
||||
$this->initSocket();
|
||||
@@ -122,7 +114,7 @@ class Socket {
|
||||
//$this->socket->send(json_encode($send), \ZMQ::MODE_DONTWAIT);
|
||||
|
||||
$this->socket->send(json_encode($send));
|
||||
$this->socket->disconnect($this->socketUri);
|
||||
// $this->socket->disconnect($this->socketUri);
|
||||
|
||||
$response = 'OK';
|
||||
|
||||
@@ -230,24 +222,5 @@ class Socket {
|
||||
return $response;
|
||||
}*/
|
||||
|
||||
/**
|
||||
* check whether this installation fulfills all requirements
|
||||
* -> check for ZMQ PHP extension and installed ZQM version
|
||||
* -> this does NOT check versions! -> those can be verified on /setup page
|
||||
* @return bool
|
||||
*/
|
||||
static function checkRequirements(){
|
||||
$check = false;
|
||||
|
||||
if(
|
||||
extension_loaded('zmq') &&
|
||||
class_exists('ZMQ')
|
||||
){
|
||||
$check = true;
|
||||
}
|
||||
|
||||
return $check;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -17,11 +17,32 @@ class Util {
|
||||
* @return array
|
||||
*/
|
||||
static function arrayChangeKeyCaseRecursive($arr, $case = CASE_LOWER){
|
||||
return array_map( function($item){
|
||||
if( is_array($item) )
|
||||
$item = self::arrayChangeKeyCaseRecursive($item);
|
||||
return $item;
|
||||
}, array_change_key_case($arr, $case));
|
||||
if(is_array($arr)){
|
||||
$arr = array_map( function($item){
|
||||
if( is_array($item) )
|
||||
$item = self::arrayChangeKeyCaseRecursive($item);
|
||||
return $item;
|
||||
}, array_change_key_case((array)$arr, $case));
|
||||
}
|
||||
|
||||
return $arr;
|
||||
}
|
||||
|
||||
/**
|
||||
* checks whether an array is associative or not (sequential)
|
||||
* @param mixed $array
|
||||
* @return bool
|
||||
*/
|
||||
static function is_assoc($array): bool {
|
||||
$isAssoc = false;
|
||||
if(
|
||||
is_array($array) &&
|
||||
array_keys($array) !== range(0, count($array) - 1)
|
||||
){
|
||||
$isAssoc = true;
|
||||
}
|
||||
|
||||
return $isAssoc;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -59,6 +80,23 @@ class Util {
|
||||
return $scopes;
|
||||
}
|
||||
|
||||
/**
|
||||
* obsucre string e.g. password (hide last characters)
|
||||
* @param string $string
|
||||
* @param int $maxHideChars
|
||||
* @return string
|
||||
*/
|
||||
static function obscureString(string $string, int $maxHideChars = 10): string {
|
||||
$formatted = '';
|
||||
$length = mb_strlen((string)$string);
|
||||
if($length > 0){
|
||||
$hideChars = ($length < $maxHideChars) ? $length : $maxHideChars;
|
||||
$formatted = substr_replace($string, str_repeat('_', min(3, $length)), -$hideChars) .
|
||||
' [' . $length . ']';
|
||||
}
|
||||
return $formatted;
|
||||
}
|
||||
|
||||
/**
|
||||
* get hash from an array of ESI scopes
|
||||
* @param array $scopes
|
||||
|
||||
139
app/main/model/abstractmaptrackingmodel.php
Normal file
139
app/main/model/abstractmaptrackingmodel.php
Normal file
@@ -0,0 +1,139 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: exodu
|
||||
* Date: 25.08.2017
|
||||
* Time: 18:45
|
||||
*/
|
||||
|
||||
namespace Model;
|
||||
|
||||
use DB\SQL\Schema;
|
||||
|
||||
abstract class AbstractMapTrackingModel extends BasicModel implements LogModelInterface {
|
||||
|
||||
private $trackingFieldConf = [
|
||||
'createdCharacterId' => [
|
||||
'type' => Schema::DT_INT,
|
||||
'index' => true,
|
||||
'belongs-to-one' => 'Model\CharacterModel',
|
||||
'constraint' => [
|
||||
[
|
||||
'table' => 'character',
|
||||
'on-delete' => 'CASCADE'
|
||||
]
|
||||
],
|
||||
'validate' => 'validate_notDry'
|
||||
],
|
||||
'updatedCharacterId' => [
|
||||
'type' => Schema::DT_INT,
|
||||
'index' => true,
|
||||
'belongs-to-one' => 'Model\CharacterModel',
|
||||
'constraint' => [
|
||||
[
|
||||
'table' => 'character',
|
||||
'on-delete' => 'CASCADE'
|
||||
]
|
||||
],
|
||||
'validate' => 'validate_notDry'
|
||||
]
|
||||
];
|
||||
|
||||
/**
|
||||
* get static character fields for this model instance
|
||||
* @return array
|
||||
*/
|
||||
protected function getStaticFieldConf(): array{
|
||||
return array_merge(parent::getStaticFieldConf(), $this->trackingFieldConf);
|
||||
}
|
||||
|
||||
/**
|
||||
* validates a model field to be a valid relational model
|
||||
* @param $key
|
||||
* @param $val
|
||||
* @return bool
|
||||
*/
|
||||
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
|
||||
*/
|
||||
protected function logActivity($action){
|
||||
// check if activity logging is enabled for this object
|
||||
if($this->enableActivityLogging){
|
||||
// check for field changes
|
||||
if(
|
||||
mb_stripos(mb_strtolower($action), 'delete') !== false ||
|
||||
!empty($this->fieldChanges)
|
||||
){
|
||||
$this->newLog($action)->setCharacter($this->updatedCharacterId)->setData($this->fieldChanges)->buffer();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* validates all required columns of this class
|
||||
* @return bool
|
||||
* @throws \Exception\ValidationException
|
||||
*/
|
||||
public function isValid(): bool {
|
||||
if($valid = parent::isValid()){
|
||||
foreach($this->trackingFieldConf as $key => $colConf){
|
||||
if($this->exists($key)){
|
||||
$valid = $this->validateField($key, $this->$key);
|
||||
if(!$valid){
|
||||
break;
|
||||
}
|
||||
}else{
|
||||
$valid = false;
|
||||
$this->throwDbException('Missing table column "' . $this->getTable(). '.' . $key . '"');
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $valid;
|
||||
}
|
||||
|
||||
/**
|
||||
* get log file data
|
||||
* @return array
|
||||
*/
|
||||
public function getLogData(): array {
|
||||
return [];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* save connection
|
||||
* @param CharacterModel $characterModel
|
||||
* @return ConnectionModel|false
|
||||
*/
|
||||
public function save(CharacterModel $characterModel = null){
|
||||
if($this->dry()){
|
||||
$this->createdCharacterId = $characterModel;
|
||||
}
|
||||
$this->updatedCharacterId = $characterModel;
|
||||
|
||||
return parent::save();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -44,22 +44,46 @@ class ActivityLogModel extends BasicModel {
|
||||
]
|
||||
],
|
||||
|
||||
// map actions -----------------------------------------------------
|
||||
|
||||
'mapCreate' => [
|
||||
'type' => Schema::DT_SMALLINT,
|
||||
'nullable' => false,
|
||||
'default' => 0,
|
||||
'counter' => true
|
||||
],
|
||||
'mapUpdate' => [
|
||||
'type' => Schema::DT_SMALLINT,
|
||||
'nullable' => false,
|
||||
'default' => 0,
|
||||
'counter' => true
|
||||
],
|
||||
'mapDelete' => [
|
||||
'type' => Schema::DT_SMALLINT,
|
||||
'nullable' => false,
|
||||
'default' => 0,
|
||||
'counter' => true
|
||||
],
|
||||
|
||||
// system actions -----------------------------------------------------
|
||||
|
||||
'systemCreate' => [
|
||||
'type' => Schema::DT_SMALLINT,
|
||||
'nullable' => false,
|
||||
'default' => 0,
|
||||
'counter' => true
|
||||
],
|
||||
'systemUpdate' => [
|
||||
'type' => Schema::DT_SMALLINT,
|
||||
'nullable' => false,
|
||||
'default' => 0,
|
||||
'counter' => true
|
||||
],
|
||||
'systemDelete' => [
|
||||
'type' => Schema::DT_SMALLINT,
|
||||
'nullable' => false,
|
||||
'default' => 0,
|
||||
'counter' => true
|
||||
],
|
||||
|
||||
// connection actions -------------------------------------------------
|
||||
@@ -68,16 +92,19 @@ class ActivityLogModel extends BasicModel {
|
||||
'type' => Schema::DT_SMALLINT,
|
||||
'nullable' => false,
|
||||
'default' => 0,
|
||||
'counter' => true
|
||||
],
|
||||
'connectionUpdate' => [
|
||||
'type' => Schema::DT_SMALLINT,
|
||||
'nullable' => false,
|
||||
'default' => 0,
|
||||
'counter' => true
|
||||
],
|
||||
'connectionDelete' => [
|
||||
'type' => Schema::DT_SMALLINT,
|
||||
'nullable' => false,
|
||||
'default' => 0,
|
||||
'counter' => true
|
||||
],
|
||||
|
||||
// signature actions -------------------------------------------------
|
||||
@@ -86,16 +113,19 @@ class ActivityLogModel extends BasicModel {
|
||||
'type' => Schema::DT_SMALLINT,
|
||||
'nullable' => false,
|
||||
'default' => 0,
|
||||
'counter' => true
|
||||
],
|
||||
'signatureUpdate' => [
|
||||
'type' => Schema::DT_SMALLINT,
|
||||
'nullable' => false,
|
||||
'default' => 0,
|
||||
'counter' => true
|
||||
],
|
||||
'signatureDelete' => [
|
||||
'type' => Schema::DT_SMALLINT,
|
||||
'nullable' => false,
|
||||
'default' => 0,
|
||||
'counter' => true
|
||||
],
|
||||
];
|
||||
|
||||
@@ -128,6 +158,20 @@ class ActivityLogModel extends BasicModel {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* get all table columns that are used as "counter" columns
|
||||
* @return array
|
||||
*/
|
||||
public function getCountableColumnNames(): array {
|
||||
$fieldConf = $this->getFieldConfiguration();
|
||||
|
||||
$filterCounterColumns = function($key, $value){
|
||||
return isset($value['counter']) ? $key : false;
|
||||
};
|
||||
|
||||
return array_values(array_filter(array_map($filterCounterColumns, array_keys($fieldConf), $fieldConf)));
|
||||
}
|
||||
|
||||
/**
|
||||
* overwrites parent
|
||||
* @param null $db
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
namespace Model;
|
||||
|
||||
use DB\SQL\Schema;
|
||||
use lib\Config;
|
||||
|
||||
class AllianceModel extends BasicModel {
|
||||
|
||||
@@ -60,8 +61,6 @@ class AllianceModel extends BasicModel {
|
||||
public function getMaps(){
|
||||
$maps = [];
|
||||
|
||||
$f3 = self::getF3();
|
||||
|
||||
$this->filter('mapAlliances',
|
||||
['active = ?', 1],
|
||||
['order' => 'created']
|
||||
@@ -72,7 +71,7 @@ class AllianceModel extends BasicModel {
|
||||
foreach($this->mapAlliances as $mapAlliance){
|
||||
if(
|
||||
$mapAlliance->mapId->isActive() &&
|
||||
$mapCount < $f3->get('PATHFINDER.MAP.ALLIANCE.MAX_COUNT')
|
||||
$mapCount < Config::getMapsDefaultConfig('alliance')['max_count']
|
||||
){
|
||||
$maps[] = $mapAlliance->mapId;
|
||||
$mapCount++;
|
||||
|
||||
@@ -9,9 +9,11 @@
|
||||
namespace Model;
|
||||
|
||||
use DB\SQL\Schema;
|
||||
use Exception;
|
||||
use Controller;
|
||||
use DB;
|
||||
use Lib\Logging;
|
||||
use Exception\ValidationException;
|
||||
use Exception\DatabaseException;
|
||||
|
||||
abstract class BasicModel extends \DB\Cortex {
|
||||
|
||||
@@ -19,7 +21,7 @@ abstract class BasicModel extends \DB\Cortex {
|
||||
* Hive key with DB object
|
||||
* @var string
|
||||
*/
|
||||
protected $db = 'DB_PF';
|
||||
protected $db = 'DB_PF';
|
||||
|
||||
/**
|
||||
* caching time of field schema - seconds
|
||||
@@ -27,26 +29,20 @@ abstract class BasicModel extends \DB\Cortex {
|
||||
* -> leave this at a higher value
|
||||
* @var int
|
||||
*/
|
||||
protected $ttl = 120;
|
||||
protected $ttl = 60;
|
||||
|
||||
/**
|
||||
* caching for relational data
|
||||
* @var int
|
||||
*/
|
||||
protected $rel_ttl = 0;
|
||||
protected $rel_ttl = 0;
|
||||
|
||||
/**
|
||||
* ass static columns for this table
|
||||
* -> can be overwritten in child models
|
||||
* @var bool
|
||||
*/
|
||||
protected $addStaticFields = true;
|
||||
|
||||
/**
|
||||
* field validation array
|
||||
* @var array
|
||||
*/
|
||||
protected $validate = [];
|
||||
protected $addStaticFields = true;
|
||||
|
||||
/**
|
||||
* enables check for $fieldChanges on update/insert
|
||||
@@ -54,7 +50,7 @@ abstract class BasicModel extends \DB\Cortex {
|
||||
* in $fieldConf config
|
||||
* @var bool
|
||||
*/
|
||||
protected $enableActivityLogging = true;
|
||||
protected $enableActivityLogging = true;
|
||||
|
||||
/**
|
||||
* enables change for "active" column
|
||||
@@ -62,40 +58,53 @@ abstract class BasicModel extends \DB\Cortex {
|
||||
* -> $this->active = false; will NOT work (prevent abuse)!
|
||||
* @var bool
|
||||
*/
|
||||
private $allowActiveChange = false;
|
||||
private $allowActiveChange = false;
|
||||
|
||||
/**
|
||||
* getData() cache key prefix
|
||||
* -> do not change, otherwise cached data is lost
|
||||
* @var string
|
||||
*/
|
||||
private $dataCacheKeyPrefix = 'DATACACHE';
|
||||
private $dataCacheKeyPrefix = 'DATACACHE';
|
||||
|
||||
/**
|
||||
* enables data export for this table
|
||||
* -> can be overwritten in child models
|
||||
* @var bool
|
||||
*/
|
||||
public static $enableDataExport = false;
|
||||
public static $enableDataExport = false;
|
||||
|
||||
/**
|
||||
* enables data import for this table
|
||||
* -> can be overwritten in child models
|
||||
* @var bool
|
||||
*/
|
||||
public static $enableDataImport = false;
|
||||
public static $enableDataImport = false;
|
||||
|
||||
/**
|
||||
* changed fields (columns) on update/insert
|
||||
* -> e.g. for character "activity logging"
|
||||
* @var array
|
||||
*/
|
||||
protected $fieldChanges = [];
|
||||
protected $fieldChanges = [];
|
||||
|
||||
/**
|
||||
* default TTL for getData(); cache
|
||||
* collection for validation errors
|
||||
* @var array
|
||||
*/
|
||||
const DEFAULT_CACHE_TTL = 120;
|
||||
protected $validationError = [];
|
||||
|
||||
/**
|
||||
* default caching time of field schema - seconds
|
||||
*/
|
||||
const DEFAULT_TTL = 86400;
|
||||
|
||||
/**
|
||||
* default TTL for getData(); cache - seconds
|
||||
*/
|
||||
const DEFAULT_CACHE_TTL = 120;
|
||||
|
||||
const ERROR_INVALID_MODEL_CLASS = 'Model class (%s) not found';
|
||||
|
||||
public function __construct($db = NULL, $table = NULL, $fluid = NULL, $ttl = 0){
|
||||
|
||||
@@ -135,8 +144,8 @@ abstract class BasicModel extends \DB\Cortex {
|
||||
/**
|
||||
* @param string $key
|
||||
* @param mixed $val
|
||||
* @return mixed|void
|
||||
* @throws Exception\ValidationException
|
||||
* @return mixed
|
||||
* @throws ValidationException
|
||||
*/
|
||||
public function set($key, $val){
|
||||
if(
|
||||
@@ -167,15 +176,13 @@ abstract class BasicModel extends \DB\Cortex {
|
||||
$val = trim($val);
|
||||
}
|
||||
|
||||
$valid = $this->validateField($key, $val);
|
||||
|
||||
if(!$valid){
|
||||
$this->throwValidationError($key);
|
||||
if( !$this->validateField($key, $val) ){
|
||||
$this->throwValidationException($key);
|
||||
}else{
|
||||
$this->checkFieldForActivityLogging($key, $val);
|
||||
|
||||
return parent::set($key, $val);
|
||||
}
|
||||
|
||||
return parent::set($key, $val);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -206,15 +213,25 @@ abstract class BasicModel extends \DB\Cortex {
|
||||
$val = (int)$val;
|
||||
}
|
||||
|
||||
if(is_object($val)){
|
||||
$val = $val->_id;
|
||||
}
|
||||
|
||||
if( $fieldConf['type'] === self::DT_JSON){
|
||||
$currentValue = $this->get($key);
|
||||
}else{
|
||||
$currentValue = $this->get($key, true);
|
||||
}
|
||||
|
||||
|
||||
if($currentValue !== $val){
|
||||
// field has changed
|
||||
in_array($key, $this->fieldChanges) ?: $this->fieldChanges[] = $key;
|
||||
if( !array_key_exists($key, $this->fieldChanges) ){
|
||||
$this->fieldChanges[$key] = [
|
||||
'old' => $currentValue,
|
||||
'new' => $val
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -239,11 +256,12 @@ abstract class BasicModel extends \DB\Cortex {
|
||||
}
|
||||
|
||||
/**
|
||||
* extent the fieldConf Array with static fields for each table
|
||||
* get static fields for this model instance
|
||||
* @return array
|
||||
*/
|
||||
private function addStaticFieldConfig(){
|
||||
protected function getStaticFieldConf(): array {
|
||||
$staticFieldConfig = [];
|
||||
|
||||
// add static fields to this mapper
|
||||
// static tables (fixed data) do not require them...
|
||||
if($this->addStaticFields){
|
||||
$staticFieldConfig = [
|
||||
@@ -258,61 +276,37 @@ abstract class BasicModel extends \DB\Cortex {
|
||||
'index' => true
|
||||
]
|
||||
];
|
||||
|
||||
|
||||
$this->fieldConf = array_merge($staticFieldConfig, $this->fieldConf);
|
||||
}
|
||||
|
||||
return $staticFieldConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* extent the fieldConf Array with static fields for each table
|
||||
*/
|
||||
private function addStaticFieldConfig(){
|
||||
$this->fieldConf = array_merge($this->getStaticFieldConf(), $this->fieldConf);
|
||||
}
|
||||
|
||||
/**
|
||||
* validates a table column based on validation settings
|
||||
* @param $col
|
||||
* @param string $key
|
||||
* @param $val
|
||||
* @return bool
|
||||
*/
|
||||
private function validateField($col, $val){
|
||||
protected function validateField(string $key, $val): bool {
|
||||
$valid = true;
|
||||
|
||||
if(array_key_exists($col, $this->validate)){
|
||||
|
||||
$fieldValidationOptions = $this->validate[$col];
|
||||
|
||||
foreach($fieldValidationOptions as $validateKey => $validateOption ){
|
||||
if(is_array($fieldValidationOptions[$validateKey])){
|
||||
$fieldSubValidationOptions = $fieldValidationOptions[$validateKey];
|
||||
|
||||
foreach($fieldSubValidationOptions as $validateSubKey => $validateSubOption ){
|
||||
switch($validateKey){
|
||||
case 'length':
|
||||
switch($validateSubKey){
|
||||
case 'min';
|
||||
if(strlen($val) < $validateSubOption){
|
||||
$valid = false;
|
||||
}
|
||||
break;
|
||||
case 'max';
|
||||
|
||||
if(strlen($val) > $validateSubOption){
|
||||
$valid = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if($fieldConf = $this->fieldConf[$key]){
|
||||
if($method = $this->fieldConf[$key]['validate']){
|
||||
if( !is_string($method)){
|
||||
$method = 'validate_' . $key;
|
||||
}
|
||||
if(method_exists($this, $method)){
|
||||
// validate $key (column) with this method...
|
||||
$valid = $this->$method($key, $val);
|
||||
}else{
|
||||
switch($validateKey){
|
||||
case 'regex':
|
||||
$valid = (bool)preg_match($fieldValidationOptions[$validateKey], $val);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// a validation rule failed
|
||||
if(!$valid){
|
||||
break;
|
||||
}
|
||||
self::getF3()->error(501, 'Method ' . get_class($this) . '->' . $method . '() is not implemented');
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -428,13 +422,22 @@ abstract class BasicModel extends \DB\Cortex {
|
||||
}
|
||||
|
||||
/**
|
||||
* Throws a validation error for a giben column
|
||||
* @param $col
|
||||
* @throws \Exception\ValidationException
|
||||
* throw validation exception for a model property
|
||||
* @param string $col
|
||||
* @param string $msg
|
||||
* @throws ValidationException
|
||||
*/
|
||||
protected function throwValidationError($col){
|
||||
throw new Exception\ValidationException('Validation failed: "' . $col . '".', $col);
|
||||
protected function throwValidationException(string $col, string $msg = ''){
|
||||
$msg = empty($msg) ? 'Validation failed: "' . $col . '".' : $msg;
|
||||
throw new ValidationException($msg, $col);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $msg
|
||||
* @throws DatabaseException
|
||||
*/
|
||||
protected function throwDbException(string $msg){
|
||||
throw new DatabaseException($msg);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -458,11 +461,11 @@ abstract class BasicModel extends \DB\Cortex {
|
||||
* get single dataSet by id
|
||||
* @param $id
|
||||
* @param int $ttl
|
||||
* @param bool $isActive
|
||||
* @return \DB\Cortex
|
||||
*/
|
||||
public function getById($id, $ttl = 3) {
|
||||
|
||||
return $this->getByForeignKey('id', (int)$id, ['limit' => 1], $ttl);
|
||||
public function getById(int $id, int $ttl = 3, bool $isActive = true){
|
||||
return $this->getByForeignKey('id', (int)$id, ['limit' => 1], $ttl, $isActive);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -492,10 +495,10 @@ abstract class BasicModel extends \DB\Cortex {
|
||||
* @param $value
|
||||
* @param array $options
|
||||
* @param int $ttl
|
||||
* @param bool $isActive
|
||||
* @return \DB\Cortex
|
||||
*/
|
||||
public function getByForeignKey($key, $value, $options = [], $ttl = 60){
|
||||
|
||||
public function getByForeignKey($key, $value, $options = [], $ttl = 0, $isActive = true){
|
||||
$querySet = [];
|
||||
$query = [];
|
||||
if($this->exists($key)){
|
||||
@@ -504,7 +507,7 @@ abstract class BasicModel extends \DB\Cortex {
|
||||
}
|
||||
|
||||
// check active column
|
||||
if($this->exists('active')){
|
||||
if($isActive && $this->exists('active')){
|
||||
$query[] = "active = :active";
|
||||
$querySet[':active'] = 1;
|
||||
}
|
||||
@@ -594,7 +597,7 @@ abstract class BasicModel extends \DB\Cortex {
|
||||
* function should be overwritten in parent classes
|
||||
* @return bool
|
||||
*/
|
||||
public function isValid(){
|
||||
public function isValid(): bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -678,7 +681,7 @@ abstract class BasicModel extends \DB\Cortex {
|
||||
$status = $this->importStaticData($tableData);
|
||||
$this->getF3()->status(202);
|
||||
}else{
|
||||
$this->getF3()->error(502, 'File could not be read');
|
||||
$this->getF3()->error(500, 'File could not be read');
|
||||
}
|
||||
}else{
|
||||
$this->getF3()->error(404, 'File not found: ' . $filePath);
|
||||
@@ -727,15 +730,54 @@ abstract class BasicModel extends \DB\Cortex {
|
||||
}
|
||||
|
||||
/**
|
||||
* buffer a new activity (action) logging
|
||||
* -> increment buffered counter
|
||||
* -> log character activity create/update/delete events
|
||||
* @param int $characterId
|
||||
* @param int $mapId
|
||||
* get "default" logging object for this kind of model
|
||||
* -> can be overwritten
|
||||
* @param string $action
|
||||
* @return Logging\LogInterface
|
||||
*/
|
||||
protected function bufferActivity($characterId, $mapId, $action){
|
||||
Controller\LogController::instance()->bufferActivity($characterId, $mapId, $action);
|
||||
protected function newLog($action = ''): Logging\LogInterface{
|
||||
return new Logging\DefaultLog($action);
|
||||
}
|
||||
|
||||
/**
|
||||
* get formatter callback function for parsed logs
|
||||
* @return null
|
||||
*/
|
||||
protected function getLogFormatter(){
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* add new validation error
|
||||
* @param ValidationException $e
|
||||
*/
|
||||
protected function setValidationError(ValidationException $e){
|
||||
$this->validationError[] = $e->getError();
|
||||
}
|
||||
|
||||
/**
|
||||
* get all validation errors
|
||||
* @return array
|
||||
*/
|
||||
public function getErrors(): array {
|
||||
return $this->validationError;
|
||||
}
|
||||
|
||||
public function save(){
|
||||
try{
|
||||
return parent::save();
|
||||
}catch(ValidationException $e){
|
||||
$this->setValidationError($e);
|
||||
}catch(DatabaseException $e){
|
||||
self::getF3()->error($e->getCode(), $e->getMessage(), $e->getTrace());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function __toString(){
|
||||
return $this->getTable();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -755,14 +797,14 @@ abstract class BasicModel extends \DB\Cortex {
|
||||
* @return BasicModel
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function getNew($model, $ttl = 86400){
|
||||
public static function getNew($model, $ttl = self::DEFAULT_TTL){
|
||||
$class = null;
|
||||
|
||||
$model = '\\' . __NAMESPACE__ . '\\' . $model;
|
||||
if(class_exists($model)){
|
||||
$class = new $model( null, null, null, $ttl );
|
||||
}else{
|
||||
throw new \Exception('No model class found');
|
||||
throw new \Exception(sprintf(self::ERROR_INVALID_MODEL_CLASS, $model));
|
||||
}
|
||||
|
||||
return $class;
|
||||
|
||||
@@ -57,6 +57,11 @@ class CharacterLogModel extends BasicModel {
|
||||
'type' => Schema::DT_BIGINT,
|
||||
'index' => true
|
||||
],
|
||||
'shipMass' => [
|
||||
'type' => Schema::DT_FLOAT,
|
||||
'nullable' => false,
|
||||
'default' => 0
|
||||
],
|
||||
'shipName' => [
|
||||
'type' => Schema::DT_VARCHAR128,
|
||||
'nullable' => false,
|
||||
@@ -92,11 +97,13 @@ class CharacterLogModel extends BasicModel {
|
||||
$this->shipTypeName = $logData['ship']['typeName'];
|
||||
$this->shipId = (int)$logData['ship']['id'];
|
||||
$this->shipName = $logData['ship']['name'];
|
||||
$this->shipMass = (float)$logData['ship']['mass'];
|
||||
}else{
|
||||
$this->shipTypeId = null;
|
||||
$this->shipTypeName = '';
|
||||
$this->shipId = null;
|
||||
$this->shipName = '';
|
||||
$this->shipMass = 0;
|
||||
}
|
||||
|
||||
if( isset($logData['station']) ){
|
||||
@@ -125,6 +132,7 @@ class CharacterLogModel extends BasicModel {
|
||||
$logData->ship->typeName = $this->shipTypeName;
|
||||
$logData->ship->id = $this->shipId;
|
||||
$logData->ship->name = $this->shipName;
|
||||
$logData->ship->mass = $this->shipMass;
|
||||
|
||||
$logData->station = (object) [];
|
||||
$logData->station->id = (int)$this->stationId;
|
||||
@@ -191,23 +199,26 @@ class CharacterLogModel extends BasicModel {
|
||||
* update session data for active character
|
||||
* @param int $systemId
|
||||
*/
|
||||
protected function updateCharacterSessionLocation($systemId){
|
||||
protected function updateCharacterSessionLocation(int $systemId){
|
||||
$controller = new Controller();
|
||||
|
||||
if(
|
||||
!empty($sessionCharacter = $controller->getSessionCharacterData()) &&
|
||||
$sessionCharacter['ID'] === $this->get('characterId', true)
|
||||
){
|
||||
$prevSystemId = (int)$sessionCharacter['PREV_SYSTEM_ID'];
|
||||
|
||||
if($prevSystemId === 0){
|
||||
$systemChanged = false;
|
||||
if((int)$sessionCharacter['PREV_SYSTEM_ID'] === 0){
|
||||
$sessionCharacter['PREV_SYSTEM_ID'] = (int)$systemId;
|
||||
}else{
|
||||
$systemChanged = true;
|
||||
}elseif((int)$sessionCharacter['PREV_SYSTEM_ID'] !== $this->systemId){
|
||||
$sessionCharacter['PREV_SYSTEM_ID'] = $this->systemId;
|
||||
$systemChanged = true;
|
||||
}
|
||||
|
||||
$sessionCharacters = CharacterModel::mergeSessionCharacterData([$sessionCharacter]);
|
||||
$this->getF3()->set(User::SESSION_KEY_CHARACTERS, $sessionCharacters);
|
||||
if($systemChanged){
|
||||
$sessionCharacters = CharacterModel::mergeSessionCharacterData([$sessionCharacter]);
|
||||
$this->getF3()->set(User::SESSION_KEY_CHARACTERS, $sessionCharacters);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,8 @@ use Controller\Ccp\Sso as Sso;
|
||||
use Controller\Api\User as User;
|
||||
use DB\SQL\Schema;
|
||||
use Lib\Util;
|
||||
use lib\Config;
|
||||
use Model\Universe;
|
||||
|
||||
class CharacterModel extends BasicModel {
|
||||
|
||||
@@ -279,7 +281,7 @@ class CharacterModel extends BasicModel {
|
||||
|
||||
if($minutes){
|
||||
$seconds = $minutes * 60;
|
||||
$timezone = new \DateTimeZone( self::getF3()->get('TZ') );
|
||||
$timezone = self::getF3()->get('getTimeZone')();
|
||||
$kickedUntil = new \DateTime('now', $timezone);
|
||||
|
||||
// add cookie expire time
|
||||
@@ -306,7 +308,7 @@ class CharacterModel extends BasicModel {
|
||||
$banned = null;
|
||||
|
||||
if($status){
|
||||
$timezone = new \DateTimeZone( self::getF3()->get('TZ') );
|
||||
$timezone = self::getF3()->get('getTimeZone')();
|
||||
$bannedSince = new \DateTime('now', $timezone);
|
||||
$banned = $bannedSince->format('Y-m-d H:i:s');
|
||||
}
|
||||
@@ -479,7 +481,7 @@ class CharacterModel extends BasicModel {
|
||||
!empty($this->crestAccessToken) &&
|
||||
!empty($this->crestAccessTokenUpdated)
|
||||
){
|
||||
$timezone = new \DateTimeZone( self::getF3()->get('TZ') );
|
||||
$timezone = self::getF3()->get('getTimeZone')();
|
||||
$tokenTime = \DateTime::createFromFormat(
|
||||
'Y-m-d H:i:s',
|
||||
$this->crestAccessTokenUpdated,
|
||||
@@ -547,8 +549,8 @@ class CharacterModel extends BasicModel {
|
||||
if(is_null($this->banned)){
|
||||
if( !$this->isKicked() ){
|
||||
$f3 = self::getF3();
|
||||
$whitelistCorporations = array_filter( array_map('trim', (array)$f3->get('PATHFINDER.LOGIN.CORPORATION') ) );
|
||||
$whitelistAlliance = array_filter( array_map('trim', (array)$f3->get('PATHFINDER.LOGIN.ALLIANCE') ) );
|
||||
$whitelistCorporations = array_filter( array_map('trim', (array)Config::getPathfinderData('login.corporation') ) );
|
||||
$whitelistAlliance = array_filter( array_map('trim', (array)Config::getPathfinderData('login.alliance') ) );
|
||||
|
||||
if(
|
||||
empty($whitelistCorporations) &&
|
||||
@@ -676,9 +678,7 @@ class CharacterModel extends BasicModel {
|
||||
if( !empty($locationData['system']['id']) ){
|
||||
// character is currently in-game
|
||||
|
||||
// IDs for "systemId", "stationId and "shipTypeId" that require more data
|
||||
$lookupIds = [];
|
||||
|
||||
// get current $characterLog or get new ---------------------------------------------------
|
||||
if( !($characterLog = $this->getLog()) ){
|
||||
// create new log
|
||||
$characterLog = $this->rel('characterLog');
|
||||
@@ -687,12 +687,17 @@ class CharacterModel extends BasicModel {
|
||||
// get current log data and modify on change
|
||||
$logData = json_decode(json_encode( $characterLog->getData()), true);
|
||||
|
||||
// check system and station data for changes ----------------------------------------------
|
||||
|
||||
// IDs for "systemId", "stationId" that require more data
|
||||
$lookupUniverseIds = [];
|
||||
|
||||
if(
|
||||
empty($logData['system']['name']) ||
|
||||
$logData['system']['id'] !== $locationData['system']['id']
|
||||
){
|
||||
// system changed -> request "system name" for current system
|
||||
$lookupIds[] = $locationData['system']['id'];
|
||||
$lookupUniverseIds[] = $locationData['system']['id'];
|
||||
}
|
||||
|
||||
if( !empty($locationData['station']['id']) ){
|
||||
@@ -701,7 +706,7 @@ class CharacterModel extends BasicModel {
|
||||
$logData['station']['id'] !== $locationData['station']['id']
|
||||
){
|
||||
// station changed -> request "station name" for current station
|
||||
$lookupIds[] = $locationData['station']['id'];
|
||||
$lookupUniverseIds[] = $locationData['station']['id'];
|
||||
}
|
||||
}else{
|
||||
unset($logData['station']);
|
||||
@@ -709,30 +714,10 @@ class CharacterModel extends BasicModel {
|
||||
|
||||
$logData = array_replace_recursive($logData, $locationData);
|
||||
|
||||
// get current ship data
|
||||
$shipData = self::getF3()->ccpClient->getCharacterShipData($this->_id, $accessToken, $additionalOptions);
|
||||
|
||||
if( !empty($shipData['ship']['typeId']) ){
|
||||
if(
|
||||
empty($logData['ship']['typeName']) ||
|
||||
$logData['ship']['typeId'] !== $shipData['ship']['typeId']
|
||||
){
|
||||
// ship changed -> request "station name" for current station
|
||||
$lookupIds[] = $shipData['ship']['typeId'];
|
||||
}
|
||||
|
||||
// "shipName"/"shipId" could have changed...
|
||||
$logData = array_replace_recursive($logData, $shipData);
|
||||
}else{
|
||||
// ship data should never be empty -> keep current one
|
||||
//unset($logData['ship']);
|
||||
$invalidResponse = true;
|
||||
}
|
||||
|
||||
if( !empty($lookupIds) ){
|
||||
// get "more" data for systemId and/or stationId -----------------------------------------
|
||||
if( !empty($lookupUniverseIds) ){
|
||||
// get "more" information for some Ids (e.g. name)
|
||||
$universeData = self::getF3()->ccpClient->getUniverseNamesData($lookupIds, $additionalOptions);
|
||||
|
||||
$universeData = self::getF3()->ccpClient->getUniverseNamesData($lookupUniverseIds, $additionalOptions);
|
||||
if( !empty($universeData) ){
|
||||
$logData = array_replace_recursive($logData, $universeData);
|
||||
}else{
|
||||
@@ -741,6 +726,48 @@ class CharacterModel extends BasicModel {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// check ship data for changes ------------------------------------------------------------
|
||||
if( !$deleteLog ){
|
||||
$shipData = self::getF3()->ccpClient->getCharacterShipData($this->_id, $accessToken, $additionalOptions);
|
||||
|
||||
// IDs for "systemId", "stationId" that require more data
|
||||
$lookupShipTypeId = 0;
|
||||
|
||||
if( !empty($shipData['ship']['typeId']) ){
|
||||
if(
|
||||
empty($logData['ship']['typeName']) ||
|
||||
$logData['ship']['typeId'] !== $shipData['ship']['typeId']
|
||||
){
|
||||
// ship changed -> request "station name" for current station
|
||||
$lookupShipTypeId = $shipData['ship']['typeId'];
|
||||
}
|
||||
|
||||
// "shipName"/"shipId" could have changed...
|
||||
$logData = array_replace_recursive($logData, $shipData);
|
||||
}else{
|
||||
// ship data should never be empty -> keep current one
|
||||
//unset($logData['ship']);
|
||||
$invalidResponse = true;
|
||||
}
|
||||
|
||||
// get "more" data for shipTypeId ----------------------------------------------------
|
||||
if($lookupShipTypeId > 0){
|
||||
/**
|
||||
* @var $typeModel Universe\TypeModel
|
||||
*/
|
||||
$typeModel = Universe\BasicUniverseModel::getNew('TypeModel');
|
||||
$typeModel->loadById($lookupShipTypeId, $additionalOptions);
|
||||
if(!$typeModel->dry()){
|
||||
$shipData['ship'] = (array)$typeModel->getShipData();
|
||||
$logData = array_replace_recursive($logData, $shipData);
|
||||
}else{
|
||||
// this is important! ship data is a MUST HAVE!
|
||||
$deleteLog = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( !$deleteLog ){
|
||||
// mark log as "updated" even if no changes were made
|
||||
if($additionalOptions['markUpdated'] === true){
|
||||
@@ -921,7 +948,7 @@ class CharacterModel extends BasicModel {
|
||||
$mapCountPrivate = 0;
|
||||
foreach($this->characterMaps as $characterMap){
|
||||
if(
|
||||
$mapCountPrivate < self::getF3()->get('PATHFINDER.MAP.PRIVATE.MAX_COUNT') &&
|
||||
$mapCountPrivate < Config::getMapsDefaultConfig('private')['max_count'] &&
|
||||
$characterMap->mapId->isActive()
|
||||
){
|
||||
$maps[] = $characterMap->mapId;
|
||||
@@ -934,11 +961,19 @@ class CharacterModel extends BasicModel {
|
||||
}
|
||||
|
||||
/**
|
||||
* character logout
|
||||
* -> clear authentication data
|
||||
* delete current location
|
||||
*/
|
||||
public function logout(){
|
||||
if( is_object($this->characterAuthentications) ){
|
||||
protected function deleteLog(){
|
||||
if($characterLog = $this->getLog()){
|
||||
$characterLog->erase();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* delete authentications data
|
||||
*/
|
||||
protected function deleteAuthentications(){
|
||||
if(is_object($this->characterAuthentications)){
|
||||
foreach($this->characterAuthentications as $characterAuthentication){
|
||||
/**
|
||||
* @var $characterAuthentication CharacterAuthenticationModel
|
||||
@@ -947,6 +982,40 @@ class CharacterModel extends BasicModel {
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* character logout
|
||||
* @param bool $deleteLog
|
||||
* @param bool $deleteSession
|
||||
* @param bool $deleteCookie
|
||||
*/
|
||||
public function logout(bool $deleteSession = true, bool $deleteLog = true, bool $deleteCookie = false){
|
||||
// delete current session data --------------------------------------------------------------------------------
|
||||
if($deleteSession){
|
||||
$sessionCharacterData = (array)$this->getF3()->get(User::SESSION_KEY_CHARACTERS);
|
||||
$sessionCharacterData = array_filter($sessionCharacterData, function($data){
|
||||
return ($data['ID'] != $this->_id);
|
||||
});
|
||||
|
||||
if(empty($sessionCharacterData)){
|
||||
// no active characters logged in -> log user out
|
||||
$this->getF3()->clear(User::SESSION_KEY_USER);
|
||||
$this->getF3()->clear(User::SESSION_KEY_CHARACTERS);
|
||||
}else{
|
||||
// update remaining active characters
|
||||
$this->getF3()->set(User::SESSION_KEY_CHARACTERS, $sessionCharacterData);
|
||||
}
|
||||
}
|
||||
|
||||
// delete current location data -------------------------------------------------------------------------------
|
||||
if($deleteLog){
|
||||
$this->deleteLog();
|
||||
}
|
||||
|
||||
// delete auth cookie data ------------------------------------------------------------------------------------
|
||||
if($deleteCookie ){
|
||||
$this->deleteAuthentications();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* merges two multidimensional characterSession arrays by checking characterID
|
||||
|
||||
@@ -9,10 +9,10 @@
|
||||
namespace Model;
|
||||
|
||||
use DB\SQL\Schema;
|
||||
use Controller;
|
||||
use Controller\Api\Route;
|
||||
use Lib\Logging;
|
||||
|
||||
class ConnectionModel extends BasicModel{
|
||||
class ConnectionModel extends AbstractMapTrackingModel {
|
||||
|
||||
protected $table = 'connection';
|
||||
|
||||
@@ -79,10 +79,16 @@ class ConnectionModel extends BasicModel{
|
||||
|
||||
/**
|
||||
* set an array with all data for a system
|
||||
* @param $systemData
|
||||
* @param array $data
|
||||
*/
|
||||
public function setData($systemData){
|
||||
foreach((array)$systemData as $key => $value){
|
||||
public function setData($data){
|
||||
unset($data['id']);
|
||||
unset($data['created']);
|
||||
unset($data['updated']);
|
||||
unset($data['createdCharacterId']);
|
||||
unset($data['updatedCharacterId']);
|
||||
|
||||
foreach((array)$data as $key => $value){
|
||||
if( !is_array($value) ){
|
||||
if( $this->exists($key) ){
|
||||
$this->$key = $value;
|
||||
@@ -100,7 +106,6 @@ class ConnectionModel extends BasicModel{
|
||||
* @return \stdClass
|
||||
*/
|
||||
public function getData($addSignatureData = false){
|
||||
|
||||
$connectionData = (object) [];
|
||||
$connectionData->id = $this->id;
|
||||
$connectionData->source = $this->source->id;
|
||||
@@ -189,21 +194,21 @@ class ConnectionModel extends BasicModel{
|
||||
* check whether this model is valid or not
|
||||
* @return bool
|
||||
*/
|
||||
public function isValid(){
|
||||
$isValid = true;
|
||||
|
||||
// check if source/target system are not equal
|
||||
// check if source/target belong to same map
|
||||
if(
|
||||
is_object($this->source) &&
|
||||
is_object($this->target) &&
|
||||
$this->get('source', true) === $this->get('target', true) ||
|
||||
$this->source->get('mapId', true) !== $this->target->get('mapId', true)
|
||||
){
|
||||
$isValid = false;
|
||||
public function isValid(): bool {
|
||||
if($valid = parent::isValid()){
|
||||
// check if source/target system are not equal
|
||||
// check if source/target belong to same map
|
||||
if(
|
||||
is_object($this->source) &&
|
||||
is_object($this->target) &&
|
||||
$this->get('source', true) === $this->get('target', true) ||
|
||||
$this->source->get('mapId', true) !== $this->target->get('mapId', true)
|
||||
){
|
||||
$valid = false;
|
||||
}
|
||||
}
|
||||
|
||||
return $isValid;
|
||||
return $valid;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -218,7 +223,6 @@ class ConnectionModel extends BasicModel{
|
||||
// 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) );
|
||||
|
||||
if(
|
||||
!$this->scope ||
|
||||
empty($types)
|
||||
@@ -226,7 +230,7 @@ class ConnectionModel extends BasicModel{
|
||||
$this->setDefaultTypeData();
|
||||
}
|
||||
|
||||
return parent::beforeInsertEvent($self, $pkeys);
|
||||
return $this->isValid() ? parent::beforeInsertEvent($self, $pkeys) : false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -263,35 +267,18 @@ class ConnectionModel extends BasicModel{
|
||||
}
|
||||
|
||||
/**
|
||||
* log character activity create/update/delete events
|
||||
* @param string $action
|
||||
* @return Logging\LogInterface
|
||||
*/
|
||||
protected function logActivity($action){
|
||||
|
||||
if(
|
||||
$this->enableActivityLogging &&
|
||||
(
|
||||
$action === 'connectionDelete' ||
|
||||
!empty($this->fieldChanges)
|
||||
) &&
|
||||
$this->get('mapId')->isActivityLogEnabled()
|
||||
){
|
||||
// TODO implement "dependency injection" for active character object...
|
||||
$controller = new Controller\Controller();
|
||||
$currentActiveCharacter = $controller->getCharacter();
|
||||
$characterId = is_null($currentActiveCharacter) ? 0 : $currentActiveCharacter->_id;
|
||||
$mapId = $this->get('mapId', true);
|
||||
|
||||
parent::bufferActivity($characterId, $mapId, $action);
|
||||
}
|
||||
public function newLog($action = ''): Logging\LogInterface{
|
||||
return $this->getMap()->newLog($action)->setTempData($this->getLogObjectData());
|
||||
}
|
||||
|
||||
/**
|
||||
* save connection and check if obj is valid
|
||||
* @return ConnectionModel|false
|
||||
* @return MapModel
|
||||
*/
|
||||
public function save(){
|
||||
return ( $this->isValid() ) ? parent::save() : false;
|
||||
public function getMap(): MapModel{
|
||||
return $this->get('mapId');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -307,6 +294,17 @@ class ConnectionModel extends BasicModel{
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* get object relevant data for model log
|
||||
* @return array
|
||||
*/
|
||||
public function getLogObjectData() : array{
|
||||
return [
|
||||
'objId' => $this->_id,
|
||||
'objName' => $this->scope
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* see parent
|
||||
*/
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
namespace Model;
|
||||
|
||||
use DB\SQL\Schema;
|
||||
use lib\Config;
|
||||
|
||||
class CorporationModel extends BasicModel {
|
||||
|
||||
@@ -131,8 +132,6 @@ class CorporationModel extends BasicModel {
|
||||
public function getMaps(){
|
||||
$maps = [];
|
||||
|
||||
$f3 = self::getF3();
|
||||
|
||||
$this->filter('mapCorporations',
|
||||
['active = ?', 1],
|
||||
['order' => 'created']
|
||||
@@ -143,7 +142,7 @@ class CorporationModel extends BasicModel {
|
||||
foreach($this->mapCorporations as $mapCorporation){
|
||||
if(
|
||||
$mapCorporation->mapId->isActive() &&
|
||||
$mapCount < $f3->get('PATHFINDER.MAP.CORPORATION.MAX_COUNT')
|
||||
$mapCount < Config::getMapsDefaultConfig('corporation')['max_count']
|
||||
){
|
||||
$maps[] = $mapCorporation->mapId;
|
||||
$mapCount++;
|
||||
|
||||
19
app/main/model/logmodelinterface.php
Normal file
19
app/main/model/logmodelinterface.php
Normal file
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: exodu
|
||||
* Date: 19.08.2017
|
||||
* Time: 04:10
|
||||
*/
|
||||
|
||||
namespace Model;
|
||||
|
||||
|
||||
interface LogModelInterface {
|
||||
|
||||
public function getLogObjectData(): array;
|
||||
|
||||
public function getMap(): MapModel;
|
||||
|
||||
public function getLogData(): array;
|
||||
}
|
||||
@@ -10,20 +10,21 @@ namespace Model;
|
||||
|
||||
use Controller\Api\System;
|
||||
use DB\SQL\Schema;
|
||||
use data\file\FileHandler;
|
||||
use lib\Config;
|
||||
use Lib\Logging;
|
||||
use Exception\PathfinderException;
|
||||
|
||||
class MapModel extends BasicModel {
|
||||
class MapModel extends AbstractMapTrackingModel {
|
||||
|
||||
protected $table = 'map';
|
||||
|
||||
/**
|
||||
* cache key prefix for getCharactersData();
|
||||
*/
|
||||
const DATA_CACHE_KEY_CHARACTER = 'CHARACTERS';
|
||||
const DATA_CACHE_KEY_CHARACTER = 'CHARACTERS';
|
||||
|
||||
/**
|
||||
* default TTL for getData(); cache
|
||||
*/
|
||||
const DEFAULT_CACHE_TTL = 60;
|
||||
const ERROR_SLACK_CHANNEL = 'Invalid #Slack channel column [%s]';
|
||||
|
||||
protected $fieldConf = [
|
||||
'active' => [
|
||||
@@ -31,7 +32,7 @@ class MapModel extends BasicModel {
|
||||
'nullable' => false,
|
||||
'default' => 1,
|
||||
'index' => true,
|
||||
'after' => 'updated'
|
||||
'activity-log' => true
|
||||
],
|
||||
'scopeId' => [
|
||||
'type' => Schema::DT_INT,
|
||||
@@ -42,7 +43,9 @@ class MapModel extends BasicModel {
|
||||
'table' => 'map_scope',
|
||||
'on-delete' => 'CASCADE'
|
||||
]
|
||||
]
|
||||
],
|
||||
'validate' => 'validate_notDry',
|
||||
'activity-log' => true
|
||||
],
|
||||
'typeId' => [
|
||||
'type' => Schema::DT_INT,
|
||||
@@ -53,32 +56,82 @@ class MapModel extends BasicModel {
|
||||
'table' => 'map_type',
|
||||
'on-delete' => 'CASCADE'
|
||||
]
|
||||
]
|
||||
],
|
||||
'validate' => 'validate_notDry',
|
||||
'activity-log' => true
|
||||
],
|
||||
'name' => [
|
||||
'type' => Schema::DT_VARCHAR128,
|
||||
'nullable' => false,
|
||||
'default' => ''
|
||||
'default' => '',
|
||||
'activity-log' => true,
|
||||
'validate' => true
|
||||
],
|
||||
'icon' => [
|
||||
'type' => Schema::DT_VARCHAR128,
|
||||
'nullable' => false,
|
||||
'default' => ''
|
||||
'default' => '',
|
||||
'activity-log' => true
|
||||
],
|
||||
'deleteExpiredConnections' => [
|
||||
'type' => Schema::DT_BOOL,
|
||||
'nullable' => false,
|
||||
'default' => 1
|
||||
'default' => 1,
|
||||
'activity-log' => true
|
||||
],
|
||||
'deleteEolConnections' => [
|
||||
'type' => Schema::DT_BOOL,
|
||||
'nullable' => false,
|
||||
'default' => 1
|
||||
'default' => 1,
|
||||
'activity-log' => true
|
||||
],
|
||||
'persistentAliases' => [
|
||||
'type' => Schema::DT_BOOL,
|
||||
'nullable' => false,
|
||||
'default' => 1
|
||||
'default' => 1,
|
||||
'activity-log' => true
|
||||
],
|
||||
'logActivity' => [
|
||||
'type' => Schema::DT_BOOL,
|
||||
'nullable' => false,
|
||||
'default' => 1,
|
||||
'activity-log' => true
|
||||
],
|
||||
'logHistory' => [
|
||||
'type' => Schema::DT_BOOL,
|
||||
'nullable' => false,
|
||||
'default' => 0,
|
||||
'activity-log' => true
|
||||
],
|
||||
'slackWebHookURL' => [
|
||||
'type' => Schema::DT_VARCHAR128,
|
||||
'nullable' => false,
|
||||
'default' => '',
|
||||
'validate' => true
|
||||
],
|
||||
'slackUsername' => [
|
||||
'type' => Schema::DT_VARCHAR128,
|
||||
'nullable' => false,
|
||||
'default' => '',
|
||||
'activity-log' => true
|
||||
],
|
||||
'slackIcon' => [
|
||||
'type' => Schema::DT_VARCHAR128,
|
||||
'nullable' => false,
|
||||
'default' => '',
|
||||
'activity-log' => true
|
||||
],
|
||||
'slackChannelHistory' => [
|
||||
'type' => Schema::DT_VARCHAR128,
|
||||
'nullable' => false,
|
||||
'default' => '',
|
||||
'activity-log' => true
|
||||
],
|
||||
'slackChannelRally' => [
|
||||
'type' => Schema::DT_VARCHAR128,
|
||||
'nullable' => false,
|
||||
'default' => '',
|
||||
'activity-log' => true
|
||||
],
|
||||
'systems' => [
|
||||
'has-many' => ['Model\SystemModel', 'mapId']
|
||||
@@ -97,37 +150,18 @@ class MapModel extends BasicModel {
|
||||
]
|
||||
];
|
||||
|
||||
protected $validate = [
|
||||
'name' => [
|
||||
'length' => [
|
||||
'min' => 3
|
||||
]
|
||||
],
|
||||
'icon' => [
|
||||
'length' => [
|
||||
'min' => 3
|
||||
]
|
||||
],
|
||||
'scopeId' => [
|
||||
'regex' => '/^[1-9]+$/'
|
||||
],
|
||||
'typeId' => [
|
||||
'regex' => '/^[1-9]+$/'
|
||||
]
|
||||
];
|
||||
|
||||
/**
|
||||
* set map data by an associative array
|
||||
* @param $data
|
||||
* @param array $data
|
||||
*/
|
||||
public function setData($data){
|
||||
unset($data['id']);
|
||||
unset($data['created']);
|
||||
unset($data['updated']);
|
||||
unset($data['createdCharacterId']);
|
||||
unset($data['updatedCharacterId']);
|
||||
|
||||
foreach((array)$data as $key => $value){
|
||||
|
||||
if($key == 'created'){
|
||||
continue;
|
||||
}
|
||||
|
||||
if(!is_array($value)){
|
||||
if($this->exists($key)){
|
||||
$this->$key = $value;
|
||||
@@ -155,35 +189,62 @@ class MapModel extends BasicModel {
|
||||
if(is_null($mapDataAll)){
|
||||
// no cached map data found
|
||||
|
||||
$mapData = (object) [];
|
||||
$mapData->id = $this->id;
|
||||
$mapData->name = $this->name;
|
||||
$mapData->icon = $this->icon;
|
||||
$mapData->deleteExpiredConnections = $this->deleteExpiredConnections;
|
||||
$mapData->deleteEolConnections = $this->deleteEolConnections;
|
||||
$mapData->persistentAliases = $this->persistentAliases;
|
||||
$mapData->created = strtotime($this->created);
|
||||
$mapData->updated = strtotime($this->updated);
|
||||
$mapData = (object) [];
|
||||
$mapData->id = $this->id;
|
||||
$mapData->name = $this->name;
|
||||
$mapData->icon = $this->icon;
|
||||
$mapData->deleteExpiredConnections = $this->deleteExpiredConnections;
|
||||
$mapData->deleteEolConnections = $this->deleteEolConnections;
|
||||
$mapData->persistentAliases = $this->persistentAliases;
|
||||
|
||||
// map scope
|
||||
$mapData->scope = (object) [];
|
||||
$mapData->scope->id = $this->scopeId->id;
|
||||
$mapData->scope->name = $this->scopeId->name;
|
||||
$mapData->scope->label = $this->scopeId->label;
|
||||
$mapData->scope = (object) [];
|
||||
$mapData->scope->id = $this->scopeId->id;
|
||||
$mapData->scope->name = $this->scopeId->name;
|
||||
$mapData->scope->label = $this->scopeId->label;
|
||||
|
||||
// map type
|
||||
$mapData->type = (object) [];
|
||||
$mapData->type->id = $this->typeId->id;
|
||||
$mapData->type->name = $this->typeId->name;
|
||||
$mapData->type->classTab = $this->typeId->classTab;
|
||||
$mapData->type = (object) [];
|
||||
$mapData->type->id = $this->typeId->id;
|
||||
$mapData->type->name = $this->typeId->name;
|
||||
$mapData->type->classTab = $this->typeId->classTab;
|
||||
|
||||
// map logging
|
||||
$mapData->logging = (object) [];
|
||||
$mapData->logging->activity = $this->isActivityLogEnabled();
|
||||
$mapData->logging->history = $this->isHistoryLogEnabled();
|
||||
|
||||
// map Slack logging
|
||||
$mapData->logging->slackHistory = $this->isSlackChannelEnabled('slackChannelHistory');
|
||||
$mapData->logging->slackRally = $this->isSlackChannelEnabled('slackChannelRally');
|
||||
$mapData->logging->slackWebHookURL = $this->slackWebHookURL;
|
||||
$mapData->logging->slackUsername = $this->slackUsername;
|
||||
$mapData->logging->slackIcon = $this->slackIcon;
|
||||
$mapData->logging->slackChannelHistory = $this->slackChannelHistory;
|
||||
$mapData->logging->slackChannelRally = $this->slackChannelRally;
|
||||
|
||||
// map mail logging
|
||||
$mapData->logging->mailRally = $this->isMailSendEnabled('RALLY_SET');
|
||||
|
||||
// map access
|
||||
$mapData->access = (object) [];
|
||||
$mapData->access->character = [];
|
||||
$mapData->access->corporation = [];
|
||||
$mapData->access->alliance = [];
|
||||
$mapData->access = (object) [];
|
||||
$mapData->access->character = [];
|
||||
$mapData->access->corporation = [];
|
||||
$mapData->access->alliance = [];
|
||||
|
||||
// get access object data -------------------------------------
|
||||
$mapData->created = (object) [];
|
||||
$mapData->created->created = strtotime($this->created);
|
||||
if(is_object($this->createdCharacterId)){
|
||||
$mapData->created->character = $this->createdCharacterId->getData();
|
||||
}
|
||||
|
||||
$mapData->updated = (object) [];
|
||||
$mapData->updated->updated = strtotime($this->updated);
|
||||
if(is_object($this->updatedCharacterId)){
|
||||
$mapData->updated->character = $this->updatedCharacterId->getData();
|
||||
}
|
||||
|
||||
// get access object data ---------------------------------------------------------------------------------
|
||||
if($this->isPrivate()){
|
||||
$characters = $this->getCharacters();
|
||||
$characterData = [];
|
||||
@@ -209,14 +270,14 @@ class MapModel extends BasicModel {
|
||||
$mapData->access->alliance = $allianceData;
|
||||
}
|
||||
|
||||
// merge all data ---------------------------------------------
|
||||
// merge all data -----------------------------------------------------------------------------------------
|
||||
$mapDataAll = (object) [];
|
||||
$mapDataAll->mapData = $mapData;
|
||||
|
||||
// map system data --------------------------------------------
|
||||
// map system data ----------------------------------------------------------------------------------------
|
||||
$mapDataAll->systems = $this->getSystemData();
|
||||
|
||||
// map connection data ----------------------------------------
|
||||
// map connection data ------------------------------------------------------------------------------------
|
||||
$mapDataAll->connections = $this->getConnectionData();
|
||||
|
||||
// max caching time for a map
|
||||
@@ -228,6 +289,70 @@ class MapModel extends BasicModel {
|
||||
return $mapDataAll;
|
||||
}
|
||||
|
||||
/**
|
||||
* validate name column
|
||||
* @param string $key
|
||||
* @param string $val
|
||||
* @return bool
|
||||
*/
|
||||
protected function validate_name(string $key, string $val): bool {
|
||||
$valid = true;
|
||||
if(mb_strlen($val) < 3){
|
||||
$valid = false;
|
||||
$this->throwValidationException($key);
|
||||
}
|
||||
return $valid;
|
||||
}
|
||||
|
||||
/**
|
||||
* validate Slack WebHook URL
|
||||
* @param string $key
|
||||
* @param string $val
|
||||
* @return bool
|
||||
*/
|
||||
protected function validate_slackWebHookURL(string $key, string $val): bool {
|
||||
$valid = true;
|
||||
if( !empty($val) ){
|
||||
if(
|
||||
!\Audit::instance()->url($val) ||
|
||||
parse_url($val, PHP_URL_HOST) !== 'hooks.slack.com'
|
||||
){
|
||||
$valid = false;
|
||||
$this->throwValidationException($key);
|
||||
}
|
||||
}
|
||||
return $valid;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $channel
|
||||
* @return string
|
||||
*/
|
||||
protected function set_slackChannelHistory($channel){
|
||||
return $this->formatSlackChannelName($channel);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $channel
|
||||
* @return string
|
||||
*/
|
||||
protected function set_slackChannelRally($channel){
|
||||
return $this->formatSlackChannelName($channel);
|
||||
}
|
||||
|
||||
/**
|
||||
* convert a Slack channel name into correct format
|
||||
* @param $channel
|
||||
* @return string
|
||||
*/
|
||||
private function formatSlackChannelName($channel){
|
||||
$channel = strtolower(str_replace(' ','', trim(trim((string)$channel), '#@')));
|
||||
if($channel){
|
||||
$channel = '#' . $channel;
|
||||
}
|
||||
return $channel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Event "Hook" function
|
||||
* @param self $self
|
||||
@@ -235,6 +360,7 @@ class MapModel extends BasicModel {
|
||||
*/
|
||||
public function afterInsertEvent($self, $pkeys){
|
||||
$self->clearCacheData();
|
||||
$self->logActivity('mapCreate');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -244,6 +370,9 @@ class MapModel extends BasicModel {
|
||||
*/
|
||||
public function afterUpdateEvent($self, $pkeys){
|
||||
$self->clearCacheData();
|
||||
|
||||
$activity = ($self->isActive()) ? 'mapUpdate' : 'mapDelete';
|
||||
$self->logActivity($activity);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -253,6 +382,8 @@ class MapModel extends BasicModel {
|
||||
*/
|
||||
public function afterEraseEvent($self, $pkeys){
|
||||
$self->clearCacheData();
|
||||
$self->logActivity('mapDelete');
|
||||
$self->deleteLogFile();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -701,49 +832,185 @@ class MapModel extends BasicModel {
|
||||
}
|
||||
|
||||
/**
|
||||
* delete this map and all dependencies
|
||||
* @param CharacterModel $characterModel
|
||||
* @param null $callback
|
||||
* @param string $action
|
||||
* @return Logging\LogInterface
|
||||
*/
|
||||
public function delete(CharacterModel $characterModel, $callback = null){
|
||||
public function newLog($action = ''): Logging\LogInterface{
|
||||
$logChannelData = $this->getLogChannelData();
|
||||
$logObjectData = $this->getLogObjectData();
|
||||
$log = (new Logging\MapLog($action, $logChannelData))->setTempData($logObjectData);
|
||||
|
||||
if( !$this->dry() ){
|
||||
// check if character has access
|
||||
if($this->hasAccess($characterModel)){
|
||||
// all map related tables will be deleted on cascade
|
||||
if(
|
||||
$this->erase() &&
|
||||
is_callable($callback)
|
||||
){
|
||||
$callback($this->_id);
|
||||
}
|
||||
// update map history *.log files -----------------------------------------------------------------------------
|
||||
if($this->isHistoryLogEnabled()){
|
||||
// check socket config
|
||||
if(Config::validSocketConnect()){
|
||||
$log->addHandler('zmq', 'json', $this->getSocketConfig());
|
||||
}else{
|
||||
// update log file local (slow)
|
||||
$log->addHandler('stream', 'json', $this->getStreamConfig());
|
||||
}
|
||||
}
|
||||
|
||||
// send map history to Slack channel --------------------------------------------------------------------------
|
||||
$slackChannelKey = 'slackChannelHistory';
|
||||
if($this->isSlackChannelEnabled($slackChannelKey)){
|
||||
$log->addHandler('slackMap', null, $this->getSlackWebHookConfig($slackChannelKey));
|
||||
$log->addHandlerGroup('slackMap');
|
||||
}
|
||||
|
||||
// update map activity ----------------------------------------------------------------------------------------
|
||||
$log->logActivity($this->isActivityLogEnabled());
|
||||
|
||||
return $log;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return MapModel
|
||||
*/
|
||||
public function getMap(): MapModel{
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* get object relevant data for model log channel
|
||||
* @return array
|
||||
*/
|
||||
public function getLogChannelData() : array{
|
||||
return [
|
||||
'channelId' => $this->_id,
|
||||
'channelName' => $this->name
|
||||
];
|
||||
}
|
||||
/**
|
||||
* get object relevant data for model log object
|
||||
* @return array
|
||||
*/
|
||||
public function getLogObjectData() : array{
|
||||
return [
|
||||
'objId' => $this->_id,
|
||||
'objName' => $this->name
|
||||
];
|
||||
}
|
||||
|
||||
protected function getLogFormatter(){
|
||||
return function(&$rowDataObj){
|
||||
unset($rowDataObj['extra']);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* check if "activity logging" is enabled for this map type
|
||||
* @return bool
|
||||
*/
|
||||
public function isActivityLogEnabled(){
|
||||
$f3 = self::getF3();
|
||||
$activityLogEnabled = false;
|
||||
public function isActivityLogEnabled(): bool {
|
||||
return $this->logActivity && (bool) Config::getMapsDefaultConfig($this->typeId->name)['log_activity_enabled'];
|
||||
}
|
||||
|
||||
if( $this->isAlliance() ){
|
||||
if( $f3->get('PATHFINDER.MAP.ALLIANCE.ACTIVITY_LOGGING') ){
|
||||
$activityLogEnabled = true;
|
||||
/**
|
||||
* check if "history logging" is enabled for this map type
|
||||
* @return bool
|
||||
*/
|
||||
public function isHistoryLogEnabled(): bool {
|
||||
return $this->logHistory && (bool) Config::getMapsDefaultConfig($this->typeId->name)['log_history_enabled'];
|
||||
}
|
||||
|
||||
/**
|
||||
* check if "Slack WebHook" is enabled for this map type
|
||||
* @param string $channel
|
||||
* @return bool
|
||||
* @throws PathfinderException
|
||||
*/
|
||||
public function isSlackChannelEnabled(string $channel): bool {
|
||||
$enabled = false;
|
||||
// check global Slack status
|
||||
if((bool)Config::getPathfinderData('slack.status')){
|
||||
// check global map default config for this channel
|
||||
switch($channel){
|
||||
case 'slackChannelHistory': $defaultMapConfigKey = 'send_history_slack_enabled'; break;
|
||||
case 'slackChannelRally': $defaultMapConfigKey = 'send_rally_slack_enabled'; break;
|
||||
default: throw new PathfinderException(sprintf(self::ERROR_SLACK_CHANNEL, $channel));
|
||||
}
|
||||
}elseif( $this->isCorporation() ){
|
||||
if( $f3->get('PATHFINDER.MAP.CORPORATION.ACTIVITY_LOGGING') ){
|
||||
$activityLogEnabled = true;
|
||||
}
|
||||
}elseif( $this->isPrivate() ){
|
||||
if( $f3->get('PATHFINDER.MAP.PRIVATE.ACTIVITY_LOGGING') ){
|
||||
$activityLogEnabled = true;
|
||||
|
||||
if((bool) Config::getMapsDefaultConfig($this->typeId->name)[$defaultMapConfigKey]){
|
||||
$config = $this->getSlackWebHookConfig($channel);
|
||||
if($config->slackWebHookURL && $config->slackChannel){
|
||||
$enabled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $activityLogEnabled;
|
||||
return $enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* check if "E-Mail" Log is enabled for this map
|
||||
* @param string $type
|
||||
* @return bool
|
||||
*/
|
||||
public function isMailSendEnabled(string $type): bool{
|
||||
$enabled = false;
|
||||
if((bool) Config::getMapsDefaultConfig($this->typeId->name)['send_rally_mail_enabled']){
|
||||
$enabled = Config::isValidSMTPConfig($this->getSMTPConfig($type));
|
||||
}
|
||||
|
||||
return $enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* get config for stream logging
|
||||
* @param bool $abs absolute path
|
||||
* @return \stdClass
|
||||
*/
|
||||
public function getStreamConfig(bool $abs = false): \stdClass{
|
||||
$config = (object) [];
|
||||
$config->stream = '';
|
||||
if( $this->getF3()->exists('PATHFINDER.HISTORY.LOG', $dir) ){
|
||||
$config->stream .= $abs ? $this->getF3()->get('ROOT') . '/' : './';
|
||||
$config->stream .= $dir . 'map/map_' . $this->_id . '.log';
|
||||
$config->stream = $this->getF3()->fixslashes($config->stream);
|
||||
}
|
||||
return $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* get config for Socket connection (e.g. where to send log data)
|
||||
* @return \stdClass
|
||||
*/
|
||||
public function getSocketConfig(): \stdClass{
|
||||
$config = (object) [];
|
||||
$config->uri = Config::getSocketUri();
|
||||
$config->streamConf = $this->getStreamConfig(true);
|
||||
return $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* get Config for Slack WebHook cURL calls
|
||||
* -> https://api.slack.com/incoming-webhooks
|
||||
* @param string $channel
|
||||
* @return \stdClass
|
||||
*/
|
||||
public function getSlackWebHookConfig(string $channel = ''): \stdClass{
|
||||
$config = (object) [];
|
||||
$config->slackWebHookURL = $this->slackWebHookURL;
|
||||
$config->slackUsername = $this->slackUsername;
|
||||
$config->slackIcon = $this->slackIcon;
|
||||
if($channel && $this->exists($channel)){
|
||||
$config->slackChannel = $this->$channel;
|
||||
}
|
||||
return $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* get Config for SMTP connection and recipient address
|
||||
* @param string $type
|
||||
* @param bool $addJson
|
||||
* @return \stdClass
|
||||
*/
|
||||
public function getSMTPConfig(string $type, bool $addJson = true): \stdClass{
|
||||
$config = Config::getSMTPConfig();
|
||||
$config->to = Config::getNotificationMail($type);
|
||||
$config->addJson = $addJson;
|
||||
return $config;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -782,22 +1049,31 @@ class MapModel extends BasicModel {
|
||||
return $scope;
|
||||
}
|
||||
|
||||
/**
|
||||
* get log file data
|
||||
* @param int $offset
|
||||
* @param int $limit
|
||||
* @return array
|
||||
*/
|
||||
public function getLogData(int $offset = FileHandler::LOG_FILE_OFFSET, int $limit = FileHandler::LOG_FILE_LIMIT): array {
|
||||
$streamConf = $this->getStreamConfig();
|
||||
return FileHandler::readLogFile($streamConf->stream, $offset, $limit, $this->getLogFormatter());
|
||||
}
|
||||
|
||||
/**
|
||||
* save a system to this map
|
||||
* @param SystemModel $system
|
||||
* @param CharacterModel $character
|
||||
* @param int $posX
|
||||
* @param int $posY
|
||||
* @param null|CharacterModel $character
|
||||
* @return mixed
|
||||
* @return false|ConnectionModel
|
||||
*/
|
||||
public function saveSystem( SystemModel $system, $posX = 10, $posY = 0, $character = null){
|
||||
public function saveSystem( SystemModel $system, CharacterModel $character, $posX = 10, $posY = 0){
|
||||
$system->setActive(true);
|
||||
$system->mapId = $this->id;
|
||||
$system->posX = $posX;
|
||||
$system->posY = $posY;
|
||||
$system->createdCharacterId = $character;
|
||||
$system->updatedCharacterId = $character;
|
||||
return $system->save();
|
||||
return $system->save($character);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -839,11 +1115,26 @@ class MapModel extends BasicModel {
|
||||
* save new connection
|
||||
* -> connection scope/type is automatically added
|
||||
* @param ConnectionModel $connection
|
||||
* @param CharacterModel $character
|
||||
* @return false|ConnectionModel
|
||||
*/
|
||||
public function saveConnection(ConnectionModel $connection){
|
||||
public function saveConnection(ConnectionModel $connection, CharacterModel $character){
|
||||
$connection->mapId = $this;
|
||||
return $connection->save();
|
||||
return $connection->save($character);
|
||||
}
|
||||
|
||||
/**
|
||||
* delete existing log file
|
||||
*/
|
||||
protected function deleteLogFile(){
|
||||
$config = $this->getStreamConfig();
|
||||
if(is_file($config->stream)){
|
||||
// try to set write access
|
||||
if(!is_writable($config->stream)){
|
||||
chmod($config->stream, 0666);
|
||||
}
|
||||
@unlink($config->stream);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -909,12 +1200,11 @@ class MapModel extends BasicModel {
|
||||
}
|
||||
|
||||
/**
|
||||
* save a map
|
||||
* @return mixed
|
||||
* @param CharacterModel|null $characterModel
|
||||
* @return false|ConnectionModel
|
||||
*/
|
||||
public function save(){
|
||||
|
||||
$mapModel = parent::save();
|
||||
public function save(CharacterModel $characterModel = null){
|
||||
$mapModel = parent::save($characterModel);
|
||||
|
||||
// check if map type has changed and clear access objects
|
||||
if( !$mapModel->dry() ){
|
||||
|
||||
@@ -8,11 +8,10 @@
|
||||
|
||||
namespace Model;
|
||||
|
||||
use controller\MailController;
|
||||
use DB\SQL\Schema;
|
||||
use lib\Config;
|
||||
use Lib\Logging;
|
||||
|
||||
class SystemModel extends BasicModel {
|
||||
class SystemModel extends AbstractMapTrackingModel {
|
||||
|
||||
const MAX_POS_X = 2300;
|
||||
const MAX_POS_Y = 498;
|
||||
@@ -124,7 +123,8 @@ class SystemModel extends BasicModel {
|
||||
'rallyPoke' => [
|
||||
'type' => Schema::DT_BOOL,
|
||||
'nullable' => false,
|
||||
'default' => 0
|
||||
'default' => 0,
|
||||
'activity-log' => true
|
||||
],
|
||||
'description' => [
|
||||
'type' => Schema::DT_VARCHAR512,
|
||||
@@ -142,28 +142,6 @@ class SystemModel extends BasicModel {
|
||||
'nullable' => false,
|
||||
'default' => 0
|
||||
],
|
||||
'createdCharacterId' => [
|
||||
'type' => Schema::DT_INT,
|
||||
'index' => true,
|
||||
'belongs-to-one' => 'Model\CharacterModel',
|
||||
'constraint' => [
|
||||
[
|
||||
'table' => 'character',
|
||||
'on-delete' => 'CASCADE'
|
||||
]
|
||||
]
|
||||
],
|
||||
'updatedCharacterId' => [
|
||||
'type' => Schema::DT_INT,
|
||||
'index' => true,
|
||||
'belongs-to-one' => 'Model\CharacterModel',
|
||||
'constraint' => [
|
||||
[
|
||||
'table' => 'character',
|
||||
'on-delete' => 'CASCADE'
|
||||
]
|
||||
]
|
||||
],
|
||||
'signatures' => [
|
||||
'has-many' => ['Model\SystemSignatureModel', 'systemId']
|
||||
],
|
||||
@@ -177,16 +155,16 @@ class SystemModel extends BasicModel {
|
||||
|
||||
/**
|
||||
* set an array with all data for a system
|
||||
* @param array $systemData
|
||||
* @param array $data
|
||||
*/
|
||||
public function setData($systemData){
|
||||
|
||||
foreach((array)$systemData as $key => $value){
|
||||
|
||||
if($key == 'created'){
|
||||
continue;
|
||||
}
|
||||
public function setData($data){
|
||||
unset($data['id']);
|
||||
unset($data['created']);
|
||||
unset($data['updated']);
|
||||
unset($data['createdCharacterId']);
|
||||
unset($data['updatedCharacterId']);
|
||||
|
||||
foreach((array)$data as $key => $value){
|
||||
if(!is_array($value)){
|
||||
if($this->exists($key)){
|
||||
$this->$key = $value;
|
||||
@@ -361,8 +339,6 @@ class SystemModel extends BasicModel {
|
||||
case 1:
|
||||
// new rally point set
|
||||
$rally = date('Y-m-d H:i:s', time());
|
||||
// flag system for mail poke -> after save()
|
||||
$this->virtual('newRallyPointSet', true);
|
||||
break;
|
||||
default:
|
||||
$rally = date('Y-m-d H:i:s', $rally);
|
||||
@@ -417,15 +393,6 @@ class SystemModel extends BasicModel {
|
||||
*/
|
||||
public function afterUpdateEvent($self, $pkeys){
|
||||
$self->clearCacheData();
|
||||
|
||||
// check if rally point mail should be send
|
||||
if(
|
||||
$self->newRallyPointSet &&
|
||||
$self->rallyPoke
|
||||
){
|
||||
$self->sendRallyPointMail();
|
||||
}
|
||||
|
||||
$activity = ($self->isActive()) ? 'systemUpdate' : 'systemDelete';
|
||||
$self->logActivity($activity);
|
||||
}
|
||||
@@ -441,23 +408,18 @@ class SystemModel extends BasicModel {
|
||||
}
|
||||
|
||||
/**
|
||||
* log character activity create/update/delete events
|
||||
* @param string $action
|
||||
* @return Logging\LogInterface
|
||||
*/
|
||||
protected function logActivity($action){
|
||||
if(
|
||||
$this->enableActivityLogging &&
|
||||
(
|
||||
$action === 'systemDelete' ||
|
||||
!empty($this->fieldChanges)
|
||||
) &&
|
||||
$this->get('mapId')->isActivityLogEnabled()
|
||||
){
|
||||
$characterId = $this->get('updatedCharacterId', true);
|
||||
$mapId = $this->get('mapId', true);
|
||||
public function newLog($action = ''): Logging\LogInterface{
|
||||
return $this->getMap()->newLog($action)->setTempData($this->getLogObjectData());
|
||||
}
|
||||
|
||||
parent::bufferActivity($characterId, $mapId, $action);
|
||||
}
|
||||
/**
|
||||
* @return MapModel
|
||||
*/
|
||||
public function getMap(): MapModel{
|
||||
return $this->get('mapId');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -599,6 +561,52 @@ class SystemModel extends BasicModel {
|
||||
return ($this->isWormhole() && $this->security === 'SH');
|
||||
}
|
||||
|
||||
/**
|
||||
* send rally point poke to various "APIs"
|
||||
* -> send to a Slack channel
|
||||
* -> send to an Email
|
||||
* @param array $rallyData
|
||||
* @param CharacterModel $characterModel
|
||||
*/
|
||||
public function sendRallyPoke(array $rallyData, CharacterModel $characterModel){
|
||||
// rally log needs at least one handler to be valid
|
||||
$isValidLog = false;
|
||||
$log = new Logging\RallyLog('rallySet', $this->getMap()->getLogChannelData());
|
||||
|
||||
// Slack poke -----------------------------------------------------------------------------
|
||||
$slackChannelKey = 'slackChannelRally';
|
||||
if(
|
||||
$rallyData['pokeSlack'] === true &&
|
||||
$this->getMap()->isSlackChannelEnabled($slackChannelKey)
|
||||
){
|
||||
$isValidLog = true;
|
||||
$log->addHandler('slackRally', null, $this->getMap()->getSlackWebHookConfig($slackChannelKey));
|
||||
}
|
||||
|
||||
// Mail poke ------------------------------------------------------------------------------
|
||||
$mailAddressKey = 'RALLY_SET';
|
||||
if(
|
||||
$rallyData['pokeMail'] === true &&
|
||||
$this->getMap()->isMailSendEnabled('RALLY_SET')
|
||||
){
|
||||
$isValidLog = true;
|
||||
$mailConf = $this->getMap()->getSMTPConfig($mailAddressKey, false);
|
||||
$log->addHandler('mail', 'mail', $mailConf);
|
||||
}
|
||||
|
||||
// Buffer log -----------------------------------------------------------------------------
|
||||
if($isValidLog){
|
||||
$log->setTempData($this->getLogObjectData(true));
|
||||
$log->setCharacter($characterModel);
|
||||
if( !empty($rallyData['message']) ){
|
||||
$log->setData([
|
||||
'message' => $rallyData['message']
|
||||
]);
|
||||
}
|
||||
$log->buffer();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* get static WH data for this system
|
||||
* -> any WH system has at least one static WH
|
||||
@@ -641,34 +649,27 @@ class SystemModel extends BasicModel {
|
||||
}
|
||||
|
||||
/**
|
||||
* send rally point information by mail
|
||||
* get object relevant data for model log
|
||||
* @param bool $fullData
|
||||
* @return array
|
||||
*/
|
||||
protected function sendRallyPointMail(){
|
||||
$recipient = Config::getNotificationMail('RALLY_SET');
|
||||
public function getLogObjectData($fullData = false) : array{
|
||||
$objectData = [
|
||||
'objId' => $this->_id,
|
||||
'objName' => $this->name
|
||||
];
|
||||
|
||||
if(
|
||||
$recipient &&
|
||||
\Audit::instance()->email($recipient)
|
||||
){
|
||||
$updatedCharacterId = (int) $this->get('updatedCharacterId', true);
|
||||
/**
|
||||
* @var $character CharacterModel
|
||||
*/
|
||||
$character = $this->rel('updatedCharacterId');
|
||||
$character->getById( $updatedCharacterId );
|
||||
if( !$character->dry() ){
|
||||
$body = [];
|
||||
$body[] = "Map:\t\t" . $this->mapId->name;
|
||||
$body[] = "System:\t\t" . $this->name;
|
||||
$body[] = "Region:\t\t" . $this->region;
|
||||
$body[] = "Security:\t" . $this->security;
|
||||
$body[] = "Character:\t" . $character->name;
|
||||
$body[] = "Time:\t\t" . date('g:i a; F j, Y', strtotime($this->rallyUpdated) );
|
||||
$bodyMsg = implode("\r\n", $body);
|
||||
|
||||
(new MailController())->sendRallyPoint($recipient, $bodyMsg);
|
||||
}
|
||||
if($fullData){
|
||||
$objectData['objAlias'] = $this->alias;
|
||||
$objectData['objRegion'] = $this->region;
|
||||
$objectData['objIsWormhole'] = $this->isWormhole();
|
||||
$objectData['objEffect'] = $this->effect;
|
||||
$objectData['objSecurity'] = $this->security;
|
||||
$objectData['objTrueSec'] = $this->trueSec;
|
||||
$objectData['objDescription'] = $this->description;
|
||||
}
|
||||
|
||||
return $objectData;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -9,8 +9,9 @@
|
||||
namespace Model;
|
||||
|
||||
use DB\SQL\Schema;
|
||||
use Lib\Logging;
|
||||
|
||||
class SystemSignatureModel extends BasicModel {
|
||||
class SystemSignatureModel extends AbstractMapTrackingModel {
|
||||
|
||||
protected $table = 'system_signature';
|
||||
|
||||
@@ -62,52 +63,29 @@ class SystemSignatureModel extends BasicModel {
|
||||
'type' => Schema::DT_VARCHAR128,
|
||||
'nullable' => false,
|
||||
'default' => '',
|
||||
'activity-log' => true
|
||||
'activity-log' => true,
|
||||
'validate' => true
|
||||
],
|
||||
'description' => [
|
||||
'type' => Schema::DT_VARCHAR512,
|
||||
'nullable' => false,
|
||||
'default' => '',
|
||||
'activity-log' => true
|
||||
],
|
||||
'createdCharacterId' => [
|
||||
'type' => Schema::DT_INT,
|
||||
'index' => true,
|
||||
'belongs-to-one' => 'Model\CharacterModel',
|
||||
'constraint' => [
|
||||
[
|
||||
'table' => 'character',
|
||||
'on-delete' => 'CASCADE'
|
||||
]
|
||||
]
|
||||
],
|
||||
'updatedCharacterId' => [
|
||||
'type' => Schema::DT_INT,
|
||||
'index' => true,
|
||||
'belongs-to-one' => 'Model\CharacterModel',
|
||||
'constraint' => [
|
||||
[
|
||||
'table' => 'character',
|
||||
'on-delete' => 'CASCADE'
|
||||
]
|
||||
]
|
||||
]
|
||||
];
|
||||
|
||||
protected $validate = [
|
||||
'name' => [
|
||||
'length' => [
|
||||
'min' => 3
|
||||
]
|
||||
]
|
||||
];
|
||||
|
||||
/**
|
||||
* set an array with all data for a system
|
||||
* @param $signatureData
|
||||
* @param $data
|
||||
*/
|
||||
public function setData($signatureData){
|
||||
foreach((array)$signatureData as $key => $value){
|
||||
public function setData($data){
|
||||
unset($data['id']);
|
||||
unset($data['created']);
|
||||
unset($data['updated']);
|
||||
unset($data['createdCharacterId']);
|
||||
unset($data['updatedCharacterId']);
|
||||
|
||||
foreach((array)$data as $key => $value){
|
||||
if(!is_array($value)){
|
||||
if($this->exists($key)){
|
||||
$this->$key = $value;
|
||||
@@ -121,7 +99,6 @@ class SystemSignatureModel extends BasicModel {
|
||||
* @return \stdClass
|
||||
*/
|
||||
public function getData(){
|
||||
|
||||
$signatureData = (object) [];
|
||||
$signatureData->id = $this->id;
|
||||
|
||||
@@ -187,6 +164,36 @@ class SystemSignatureModel extends BasicModel {
|
||||
return $validConnectionId;
|
||||
}
|
||||
|
||||
/**
|
||||
* validate name column
|
||||
* @param string $key
|
||||
* @param string $val
|
||||
* @return bool
|
||||
*/
|
||||
protected function validate_name(string $key, string $val): bool {
|
||||
$valid = true;
|
||||
if(mb_strlen($val) < 3){
|
||||
$valid = false;
|
||||
$this->throwValidationException($key);
|
||||
}
|
||||
return $valid;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $action
|
||||
* @return Logging\LogInterface
|
||||
*/
|
||||
public function newLog($action = ''): Logging\LogInterface{
|
||||
return $this->getMap()->newLog($action)->setTempData($this->getLogObjectData());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return MapModel
|
||||
*/
|
||||
public function getMap(): MapModel{
|
||||
return $this->get('systemId')->getMap();
|
||||
}
|
||||
|
||||
/**
|
||||
* get the connection (if attached)
|
||||
* @return \Model\ConnectionModel|null
|
||||
@@ -270,29 +277,14 @@ class SystemSignatureModel extends BasicModel {
|
||||
}
|
||||
|
||||
/**
|
||||
* log character activity create/update/delete events
|
||||
* @param string $action
|
||||
* get object relevant data for model log
|
||||
* @return array
|
||||
*/
|
||||
protected function logActivity($action){
|
||||
if($this->enableActivityLogging){
|
||||
/**
|
||||
* @var $map MapModel
|
||||
*/
|
||||
$map = $this->get('systemId')->get('mapId');
|
||||
|
||||
if(
|
||||
(
|
||||
$action === 'signatureDelete' ||
|
||||
!empty($this->fieldChanges)
|
||||
) &&
|
||||
$map->isActivityLogEnabled()
|
||||
){
|
||||
$characterId = $this->get('updatedCharacterId', true);
|
||||
$mapId = $map->_id;
|
||||
|
||||
parent::bufferActivity($characterId, $mapId, $action);
|
||||
}
|
||||
}
|
||||
public function getLogObjectData() : array{
|
||||
return [
|
||||
'objId' => $this->_id,
|
||||
'objName' => $this->name
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -9,9 +9,30 @@
|
||||
namespace Model\Universe;
|
||||
|
||||
|
||||
use DB\Database;
|
||||
use Model\BasicModel;
|
||||
|
||||
class BasicUniverseModel extends BasicModel {
|
||||
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;
|
||||
|
||||
protected $db = 'DB_UNIVERSE';
|
||||
|
||||
public static function getNew($model, $ttl = self::DEFAULT_TTL){
|
||||
$class = null;
|
||||
|
||||
$model = '\\' . __NAMESPACE__ . '\\' . $model;
|
||||
if(class_exists($model)){
|
||||
$db = Database::instance()->getDB('UNIVERSE');
|
||||
$class = new $model($db, null, null, $ttl);
|
||||
}else{
|
||||
throw new \Exception(sprintf(self::ERROR_INVALID_MODEL_CLASS, $model));
|
||||
}
|
||||
|
||||
return $class;
|
||||
}
|
||||
}
|
||||
145
app/main/model/universe/typemodel.php
Normal file
145
app/main/model/universe/typemodel.php
Normal file
@@ -0,0 +1,145 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: exodu
|
||||
* Date: 14.10.2017
|
||||
* Time: 15:56
|
||||
*/
|
||||
|
||||
namespace Model\Universe;
|
||||
|
||||
|
||||
use DB\SQL\Schema;
|
||||
|
||||
class TypeModel extends BasicUniverseModel {
|
||||
|
||||
protected $table = 'type';
|
||||
|
||||
protected $fieldConf = [
|
||||
'name' => [
|
||||
'type' => Schema::DT_VARCHAR128,
|
||||
'nullable' => false,
|
||||
'default' => ''
|
||||
],
|
||||
'description' => [
|
||||
'type' => Schema::DT_TEXT
|
||||
],
|
||||
'published' => [
|
||||
'type' => Schema::DT_BOOL,
|
||||
'nullable' => false,
|
||||
'default' => 1,
|
||||
'index' => true
|
||||
],
|
||||
'radius' => [
|
||||
'type' => Schema::DT_FLOAT,
|
||||
'nullable' => false,
|
||||
'default' => 0
|
||||
],
|
||||
'volume' => [
|
||||
'type' => Schema::DT_FLOAT,
|
||||
'nullable' => false,
|
||||
'default' => 0
|
||||
],
|
||||
'capacity' => [
|
||||
'type' => Schema::DT_FLOAT,
|
||||
'nullable' => false,
|
||||
'default' => 0
|
||||
],
|
||||
'mass' => [
|
||||
'type' => Schema::DT_FLOAT,
|
||||
'nullable' => false,
|
||||
'default' => 0
|
||||
],
|
||||
'groupId' => [
|
||||
'type' => Schema::DT_INT,
|
||||
'nullable' => false,
|
||||
'default' => 0
|
||||
],
|
||||
'marketGroupId' => [
|
||||
'type' => Schema::DT_INT,
|
||||
'nullable' => false,
|
||||
'default' => 0
|
||||
],
|
||||
'packagedVolume' => [
|
||||
'type' => Schema::DT_FLOAT,
|
||||
'nullable' => false,
|
||||
'default' => 0
|
||||
],
|
||||
'portionSize' => [
|
||||
'type' => Schema::DT_INT,
|
||||
'nullable' => false,
|
||||
'default' => 0
|
||||
],
|
||||
'graphicId' => [
|
||||
'type' => Schema::DT_INT,
|
||||
'nullable' => false,
|
||||
'default' => 0
|
||||
]
|
||||
];
|
||||
|
||||
/**
|
||||
* get shipData from object
|
||||
* -> more fields can be added in here if needed
|
||||
* @return \stdClass
|
||||
*/
|
||||
public function getShipData(): \stdClass {
|
||||
$shipData = (object) [];
|
||||
if(!$this->dry()){
|
||||
$shipData->typeId = $this->_id;
|
||||
$shipData->typeName = $this->name;
|
||||
$shipData->mass = $this->mass;
|
||||
}
|
||||
return $shipData;
|
||||
}
|
||||
|
||||
/**
|
||||
* load data from API into $this and save $this
|
||||
* @param int $id
|
||||
* @param array $additionalOptions
|
||||
*/
|
||||
protected function loadData(int $id, array $additionalOptions = []){
|
||||
$data = self::getF3()->ccpClient->getUniverseTypesData($id, $additionalOptions);
|
||||
if(!empty($data)){
|
||||
$this->copyfrom($data);
|
||||
$this->save();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* load object by $id
|
||||
* -> if $id not exists in DB -> query API
|
||||
* @param int $id
|
||||
* @param array $additionalOptions
|
||||
*/
|
||||
public function loadById(int $id, array $additionalOptions = []){
|
||||
/**
|
||||
* @var $model self
|
||||
*/
|
||||
$model = parent::getById($id);
|
||||
if($model->isOutdated()){
|
||||
$model->loadData($id, $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;
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,8 @@ use DB\SQL\Schema;
|
||||
use Controller;
|
||||
use Controller\Api\User as User;
|
||||
use Exception;
|
||||
use lib\Config;
|
||||
use Lib\Logging;
|
||||
|
||||
class UserModel extends BasicModel {
|
||||
|
||||
@@ -28,27 +30,20 @@ class UserModel extends BasicModel {
|
||||
'type' => Schema::DT_VARCHAR128,
|
||||
'nullable' => false,
|
||||
'default' => '',
|
||||
'index' => true
|
||||
'index' => true,
|
||||
'validate' => true
|
||||
],
|
||||
'email' => [
|
||||
'type' => Schema::DT_VARCHAR128,
|
||||
'nullable' => false,
|
||||
'default' => ''
|
||||
'default' => '',
|
||||
'validate' => true
|
||||
],
|
||||
'userCharacters' => [
|
||||
'has-many' => ['Model\UserCharacterModel', 'userId']
|
||||
]
|
||||
];
|
||||
|
||||
protected $validate = [
|
||||
'name' => [
|
||||
'length' => [
|
||||
'min' => 3,
|
||||
'max' => 50
|
||||
]
|
||||
]
|
||||
];
|
||||
|
||||
/**
|
||||
* get all data for this user
|
||||
* -> ! caution ! this function returns sensitive data! (e.g. email,..)
|
||||
@@ -93,23 +88,6 @@ class UserModel extends BasicModel {
|
||||
return $userData;
|
||||
}
|
||||
|
||||
/**
|
||||
* validate and set a email address for this user
|
||||
* -> empty email is allowed!
|
||||
* @param string $email
|
||||
* @return string
|
||||
*/
|
||||
public function set_email($email){
|
||||
if (
|
||||
!empty($email) &&
|
||||
\Audit::instance()->email($email) == false
|
||||
) {
|
||||
// no valid email address
|
||||
$this->throwValidationError('email');
|
||||
}
|
||||
return $email;
|
||||
}
|
||||
|
||||
/**
|
||||
* check if new user registration is allowed
|
||||
* @param UserModel $self
|
||||
@@ -119,10 +97,8 @@ class UserModel extends BasicModel {
|
||||
*/
|
||||
public function beforeInsertEvent($self, $pkeys){
|
||||
$registrationStatus = Controller\Controller::getRegistrationStatus();
|
||||
|
||||
switch($registrationStatus){
|
||||
case 0:
|
||||
$f3 = self::getF3();
|
||||
throw new Exception\RegistrationException('User registration is currently not allowed');
|
||||
break;
|
||||
case 1:
|
||||
@@ -133,6 +109,80 @@ class UserModel extends BasicModel {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param BasicModel $self
|
||||
* @param $pkeys
|
||||
*/
|
||||
public function afterEraseEvent($self, $pkeys){
|
||||
$this->sendDeleteMail();
|
||||
}
|
||||
|
||||
/**
|
||||
* send delete confirm mail to this user
|
||||
*/
|
||||
protected function sendDeleteMail(){
|
||||
if($this->isMailSendEnabled()){
|
||||
$log = new Logging\UserLog('userDelete', $this->getLogChannelData());
|
||||
$log->addHandler('mail', 'mail', $this->getSMTPConfig());
|
||||
$log->setMessage('Delete Account - {channelName}');
|
||||
$log->setData([
|
||||
'message' =>'Your account was successfully deleted.'
|
||||
]);
|
||||
$log->buffer();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* checks whether user has a valid email address and pathfinder has a valid SMTP config
|
||||
* @return bool
|
||||
*/
|
||||
protected function isMailSendEnabled() : bool{
|
||||
return Config::isValidSMTPConfig($this->getSMTPConfig());
|
||||
}
|
||||
|
||||
/**
|
||||
* get SMTP config for this user
|
||||
* @return \stdClass
|
||||
*/
|
||||
protected function getSMTPConfig() : \stdClass{
|
||||
$config = Config::getSMTPConfig();
|
||||
$config->to = $this->email;
|
||||
return $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* validate name column
|
||||
* @param string $key
|
||||
* @param string $val
|
||||
* @return bool
|
||||
*/
|
||||
protected function validate_name(string $key, string $val): bool {
|
||||
$valid = true;
|
||||
if(
|
||||
mb_strlen($val) < 3 ||
|
||||
mb_strlen($val) > 80
|
||||
){
|
||||
$valid = false;
|
||||
$this->throwValidationException($key);
|
||||
}
|
||||
return $valid;
|
||||
}
|
||||
|
||||
/**
|
||||
* validate email column
|
||||
* @param string $key
|
||||
* @param string $val
|
||||
* @return bool
|
||||
*/
|
||||
protected function validate_email(string $key, string $val): bool {
|
||||
$valid = true;
|
||||
if ( !empty($val) && \Audit::instance()->email($val) == false ){
|
||||
$valid = false;
|
||||
$this->throwValidationException($key);
|
||||
}
|
||||
return $valid;
|
||||
}
|
||||
|
||||
/**
|
||||
* check whether this character has already a user assigned to it
|
||||
* @return bool
|
||||
@@ -145,10 +195,10 @@ class UserModel extends BasicModel {
|
||||
/**
|
||||
* search for user by unique username
|
||||
* @param $name
|
||||
* @return array|FALSE
|
||||
* @return \DB\Cortex
|
||||
*/
|
||||
public function getByName($name){
|
||||
return $this->getByForeignKey('name', $name, [], 0);
|
||||
return $this->getByForeignKey('name', $name, []);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -165,17 +215,9 @@ class UserModel extends BasicModel {
|
||||
|
||||
if($this->_id === $currentSessionUser['ID']){
|
||||
// user matches session data
|
||||
$sessionCharacters = (array)$this->getF3()->get(User::SESSION_KEY_CHARACTERS);
|
||||
|
||||
if($characterId > 0){
|
||||
// search for specific characterData
|
||||
foreach($sessionCharacters as $characterData){
|
||||
if($characterId === (int)$characterData['ID']){
|
||||
$data = $characterData;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}elseif( !empty($sessionCharacters) ){
|
||||
$data = $this->findSessionCharacterData($characterId);
|
||||
}elseif( !empty($sessionCharacters = (array)$this->getF3()->get(User::SESSION_KEY_CHARACTERS)) ){
|
||||
// no character was requested ($requestedCharacterId = 0) AND session characters were found
|
||||
// -> get first matched character (e.g. user open browser tab)
|
||||
$data = $sessionCharacters[0];
|
||||
@@ -206,6 +248,26 @@ class UserModel extends BasicModel {
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* search in session data for $characterId
|
||||
* @param int $characterId
|
||||
* @return array
|
||||
*/
|
||||
public function findSessionCharacterData(int $characterId): array{
|
||||
$data = [];
|
||||
if($characterId){
|
||||
$sessionCharacters = (array)$this->getF3()->get(User::SESSION_KEY_CHARACTERS);
|
||||
// search for specific characterData
|
||||
foreach($sessionCharacters as $characterData){
|
||||
if($characterId === (int)$characterData['ID']){
|
||||
$data = $characterData;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* get all userCharacters models for a user
|
||||
* characters will be checked/updated on login by CCP API call
|
||||
@@ -292,4 +354,16 @@ class UserModel extends BasicModel {
|
||||
return $activeCharacters;
|
||||
}
|
||||
|
||||
/**
|
||||
* get object relevant data for model log channel
|
||||
* @return array
|
||||
*/
|
||||
public function getLogChannelData() : array{
|
||||
return [
|
||||
'channelId' => $this->_id,
|
||||
'channelName' => $this->name
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
[PATHFINDER]
|
||||
NAME = Pathfinder
|
||||
; installed version (used for CSS/JS cache busting)
|
||||
VERSION = v1.2.5
|
||||
VERSION = v1.3.0
|
||||
; contact information [optional]
|
||||
CONTACT = https://github.com/exodus4d
|
||||
; public contact email [optional]
|
||||
@@ -31,6 +31,11 @@ MODE_MAINTENANCE = 0
|
||||
CORPORATION =
|
||||
ALLIANCE =
|
||||
|
||||
; Slack API integration ===========================================================================
|
||||
[PATHFINDER.SLACK]
|
||||
; Global Slack API status, check PATHFINDER.MAP section for individual control (0=disabled, 1=enabled)
|
||||
STATUS = 1
|
||||
|
||||
; View ============================================================================================
|
||||
[PATHFINDER.VIEW]
|
||||
; static page templates
|
||||
@@ -55,29 +60,51 @@ ADMIN = templates/view/admin.html
|
||||
; - Max number of shared entities per map
|
||||
; MAX_SYSTEMS:
|
||||
; - Max number of active systems per map
|
||||
; ACTIVITY_LOGGING (0: disable, 1: enable):
|
||||
; - Whether user activity should be logged for a map type
|
||||
; - E.g. create/update/delete of systems/connections/signatures
|
||||
; LOG_ACTIVITY_ENABLED (0: disable, 1: enable):
|
||||
; - Whether user activity statistics can be anabled for a map type
|
||||
; - E.g. create/update/delete of systems/connections/signatures/...
|
||||
; LOG_HISTORY_ENABLED (0: disable, 1: enable):
|
||||
; - Whether map change history should be logged to separat *.log files
|
||||
; - see: [PATHFINDER.HISTORY] config section below
|
||||
; SEND_HISTORY_SLACK_ENABLED (0: disable, 1: enable):
|
||||
; - Send map updates to a Slack channel per map
|
||||
; SEND_RALLY_SLACK_ENABLED (0: disable, 1: enable):
|
||||
; - Send rally point pokes to a Slack channel per map
|
||||
; SEND_RALLY_Mail_ENABLED (0: disable, 1: enable):
|
||||
; - Send rally point pokes by mail
|
||||
; - see: [PATHFINDER.NOTIFICATION] section below
|
||||
[PATHFINDER.MAP.PRIVATE]
|
||||
LIFETIME = 30
|
||||
MAX_COUNT = 3
|
||||
MAX_SHARED = 10
|
||||
MAX_SYSTEMS = 50
|
||||
ACTIVITY_LOGGING = 1
|
||||
LIFETIME = 60
|
||||
MAX_COUNT = 3
|
||||
MAX_SHARED = 10
|
||||
MAX_SYSTEMS = 50
|
||||
LOG_ACTIVITY_ENABLED = 1
|
||||
LOG_HISTORY_ENABLED = 1
|
||||
SEND_HISTORY_SLACK_ENABLED = 0
|
||||
SEND_RALLY_SLACK_ENABLED = 1
|
||||
SEND_RALLY_Mail_ENABLED = 0
|
||||
|
||||
[PATHFINDER.MAP.CORPORATION]
|
||||
LIFETIME = 99999
|
||||
MAX_COUNT = 3
|
||||
MAX_SHARED = 3
|
||||
MAX_SYSTEMS = 100
|
||||
ACTIVITY_LOGGING = 1
|
||||
LIFETIME = 99999
|
||||
MAX_COUNT = 3
|
||||
MAX_SHARED = 3
|
||||
MAX_SYSTEMS = 100
|
||||
LOG_ACTIVITY_ENABLED = 1
|
||||
LOG_HISTORY_ENABLED = 1
|
||||
SEND_HISTORY_SLACK_ENABLED = 1
|
||||
SEND_RALLY_SLACK_ENABLED = 1
|
||||
SEND_RALLY_Mail_ENABLED = 0
|
||||
|
||||
[PATHFINDER.MAP.ALLIANCE]
|
||||
LIFETIME = 99999
|
||||
MAX_COUNT = 3
|
||||
MAX_SHARED = 2
|
||||
MAX_SYSTEMS = 100
|
||||
ACTIVITY_LOGGING = 0
|
||||
LIFETIME = 99999
|
||||
MAX_COUNT = 3
|
||||
MAX_SHARED = 2
|
||||
MAX_SYSTEMS = 100
|
||||
LOG_ACTIVITY_ENABLED = 0
|
||||
LOG_HISTORY_ENABLED = 1
|
||||
SEND_HISTORY_SLACK_ENABLED = 1
|
||||
SEND_RALLY_SLACK_ENABLED = 1
|
||||
SEND_RALLY_Mail_ENABLED = 0
|
||||
|
||||
; Route search ====================================================================================
|
||||
[PATHFINDER.ROUTE]
|
||||
@@ -87,7 +114,7 @@ SEARCH_DEPTH = 7000
|
||||
; default count of routes that will be checked (initial) when a system is selected (default: 2)
|
||||
SEARCH_DEFAULT_COUNT = 2
|
||||
; max count of routes that can be selected in "route settings" dialog (default: 4)
|
||||
MAX_Default_COUNT = 4
|
||||
MAX_DEFAULT_COUNT = 4
|
||||
; max count of routes that will be checked (MAX_COUNT + custom routes ) (default: 6)
|
||||
LIMIT = 6
|
||||
|
||||
@@ -151,8 +178,6 @@ LOGIN = login
|
||||
SESSION_SUSPECT = session_suspect
|
||||
; account deleted
|
||||
DELETE_ACCOUNT = account_delete
|
||||
; unauthorized request (HTTP 401)
|
||||
UNAUTHORIZED = unauthorized
|
||||
; admin action (e.g. kick, bann) log
|
||||
ADMIN = admin
|
||||
; TCP socket errors
|
||||
@@ -160,7 +185,15 @@ SOCKET_ERROR = socket_error
|
||||
; debug log for development
|
||||
DEBUG = debug
|
||||
|
||||
[PATHFINDER.HISTORY]
|
||||
; cache time for parsed log files (seconds) (default: 5)
|
||||
CACHE = 5
|
||||
; file folder for 'history' logs (e.g. map history) (default: history/)
|
||||
LOG = history/
|
||||
|
||||
; API =============================================================================================
|
||||
[PATHFINDER.API]
|
||||
CCP_IMAGE_SERVER = https://image.eveonline.com
|
||||
Z_KILLBOARD = https://zkillboard.com/api
|
||||
; GitHub Developer API
|
||||
GIT_HUB = https://api.github.com
|
||||
|
||||
@@ -30,7 +30,7 @@ ZMQ = 1.1.3
|
||||
; https://pecl.php.net/package/event
|
||||
EVENT = 2.3.0
|
||||
|
||||
; max execution time for requests
|
||||
; max execution time for requests (seconds)
|
||||
MAX_EXECUTION_TIME = 10
|
||||
|
||||
; max variable size for $_GET, $_POST and $_COOKIE
|
||||
@@ -39,6 +39,9 @@ MAX_EXECUTION_TIME = 10
|
||||
; PHP default = 1000
|
||||
MAX_INPUT_VARS = 3000
|
||||
|
||||
; Formatted HTML StackTraces
|
||||
HTML_ERRORS = 0
|
||||
|
||||
[REQUIREMENTS.LIBS]
|
||||
|
||||
ZMQ = 4.1.3
|
||||
|
||||
@@ -21,8 +21,12 @@
|
||||
}],
|
||||
"require": {
|
||||
"php-64bit": ">=7.0",
|
||||
"ext-zmq": "1.1.*",
|
||||
"ext-curl": ">=7.0",
|
||||
"ext-zmq": ">=1.1.3",
|
||||
"react/zmq": "0.3.*",
|
||||
"monolog/monolog": "1.*",
|
||||
"websoftwares/monolog-zmq-handler": "0.2.*",
|
||||
"swiftmailer/swiftmailer": "^6.0",
|
||||
"exodus4d/pathfinder_esi": "dev-develop as 0.0.x-dev"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,8 +21,12 @@
|
||||
}],
|
||||
"require": {
|
||||
"php-64bit": ">=7.0",
|
||||
"ext-zmq": "1.1.*",
|
||||
"ext-curl": ">=7.0",
|
||||
"ext-zmq": ">=1.1.3",
|
||||
"react/zmq": "0.3.*",
|
||||
"exodus4d/pathfinder_esi": "dev-master#v1.1.0"
|
||||
"monolog/monolog": "1.*",
|
||||
"websoftwares/monolog-zmq-handler": "0.2.*",
|
||||
"swiftmailer/swiftmailer": "^6.0",
|
||||
"exodus4d/pathfinder_esi": "dev-master#v1.2.0"
|
||||
}
|
||||
}
|
||||
|
||||
14
gulpfile.js
14
gulpfile.js
@@ -499,7 +499,7 @@ gulp.task('task:hintJS', () => {
|
||||
* concat/build JS files by modules
|
||||
*/
|
||||
gulp.task('task:concatJS', () => {
|
||||
let modules = ['login', 'mappage', 'setup', 'admin', 'notification'];
|
||||
let modules = ['login', 'mappage', 'setup', 'admin', 'notification', 'datatables.loader'];
|
||||
let srcModules = ['./js/app/*(' + modules.join('|') + ').js'];
|
||||
|
||||
return gulp.src(srcModules, {base: 'js'})
|
||||
@@ -854,11 +854,15 @@ gulp.task(
|
||||
'production',
|
||||
gulp.series(
|
||||
'task:configProduction',
|
||||
'task:cleanJsBuild',
|
||||
'task:cleanCssBuild',
|
||||
gulp.parallel(
|
||||
'task:buildJs',
|
||||
'task:watchCss'
|
||||
gulp.series(
|
||||
'task:cleanJsBuild',
|
||||
'task:buildJs'
|
||||
),
|
||||
gulp.series(
|
||||
'task:cleanCssBuild',
|
||||
'task:watchCss'
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
10
js/app.js
10
js/app.js
@@ -11,7 +11,7 @@ requirejs.config({
|
||||
|
||||
paths: {
|
||||
layout: 'layout',
|
||||
config: 'app/config', // path for "configuration" files dir
|
||||
conf: 'app/conf', // path for "config" files dir
|
||||
dialog: 'app/ui/dialog', // path for "dialog" files dir
|
||||
templates: '../../templates', // template dir
|
||||
img: '../../img', // images dir
|
||||
@@ -59,11 +59,13 @@ requirejs.config({
|
||||
tweenLite: 'lib/TweenLite.min',
|
||||
|
||||
// datatables // v1.10.12 DataTables - https://datatables.net
|
||||
'datatables.loader': './app/datatables.loader',
|
||||
'datatables.net': 'lib/datatables/DataTables-1.10.12/js/jquery.dataTables.min',
|
||||
'datatables.net-buttons': 'lib/datatables/Buttons-1.2.1/js/dataTables.buttons.min',
|
||||
'datatables.net-buttons-html': 'lib/datatables/Buttons-1.2.1/js/buttons.html5.min',
|
||||
'datatables.net-responsive': 'lib/datatables/Responsive-2.1.0/js/dataTables.responsive.min',
|
||||
'datatables.net-select': 'lib/datatables/Select-1.2.0/js/dataTables.select.min',
|
||||
'datatables.plugins.render.ellipsis': 'lib/datatables/plugins/render/ellipsis',
|
||||
|
||||
// notification plugin
|
||||
pnotify: 'lib/pnotify/pnotify', // v3.0.0 PNotify - notification core file - https://sciactive.com/pnotify/
|
||||
@@ -94,6 +96,9 @@ requirejs.config({
|
||||
customScrollbar: {
|
||||
deps: ['jquery', 'mousewheel']
|
||||
},
|
||||
'datatables.loader': {
|
||||
deps: ['jquery']
|
||||
},
|
||||
'datatables.net': {
|
||||
deps: ['jquery']
|
||||
},
|
||||
@@ -109,6 +114,9 @@ requirejs.config({
|
||||
'datatables.net-select': {
|
||||
deps: ['datatables.net']
|
||||
},
|
||||
'datatables.plugins.render.ellipsis': {
|
||||
deps: ['datatables.net']
|
||||
},
|
||||
xEditable: {
|
||||
deps: ['bootstrap']
|
||||
},
|
||||
|
||||
@@ -6,11 +6,7 @@ define([
|
||||
'jquery',
|
||||
'app/init',
|
||||
'app/util',
|
||||
'datatables.net',
|
||||
'datatables.net-buttons',
|
||||
'datatables.net-buttons-html',
|
||||
'datatables.net-responsive',
|
||||
'datatables.net-select'
|
||||
'datatables.loader'
|
||||
], function($, Init, Util) {
|
||||
|
||||
'use strict';
|
||||
|
||||
11
js/app/datatables.loader.js
Normal file
11
js/app/datatables.loader.js
Normal file
@@ -0,0 +1,11 @@
|
||||
define([
|
||||
'datatables.net',
|
||||
'datatables.net-buttons',
|
||||
'datatables.net-buttons-html',
|
||||
'datatables.net-responsive',
|
||||
'datatables.net-select'
|
||||
], (a, b) => {
|
||||
'use strict';
|
||||
|
||||
// all Datatables stuff is available...
|
||||
});
|
||||
@@ -31,6 +31,7 @@ define(['jquery'], function($) {
|
||||
deleteMap: 'api/map/delete', // ajax URL - delete map
|
||||
importMap: 'api/map/import', // ajax URL - import map
|
||||
getMapConnectionData: 'api/map/getConnectionData', // ajax URL - get connection data
|
||||
getMapLogData: 'api/map/getLogData', // ajax URL - get logs data
|
||||
// system API
|
||||
searchSystem: 'api/system/search', // ajax URL - search system by name
|
||||
saveSystem: 'api/system/save', // ajax URL - saves system to map
|
||||
@@ -38,6 +39,7 @@ define(['jquery'], function($) {
|
||||
getSystemGraphData: 'api/system/graphData', // ajax URL - get all system graph data
|
||||
getConstellationData: 'api/system/constellationData', // ajax URL - get system constellation data
|
||||
setDestination: 'api/system/setDestination', // ajax URL - set destination
|
||||
pokeRally: 'api/system/pokeRally', // ajax URL - send rally point pokes
|
||||
// connection API
|
||||
saveConnection: 'api/connection/save', // ajax URL - save new connection to map
|
||||
deleteConnection: 'api/connection/delete', // ajax URL - delete connection from map
|
||||
@@ -52,10 +54,6 @@ define(['jquery'], function($) {
|
||||
// GitHub API
|
||||
gitHubReleases: 'api/github/releases' // ajax URL - get release info from GitHub
|
||||
},
|
||||
url: {
|
||||
ccpImageServer: '//image.eveonline.com/', // CCP image Server
|
||||
zKillboard: '//zkillboard.com/api/' // killboard api
|
||||
},
|
||||
breakpoints: [
|
||||
{ name: 'desktop', width: Infinity },
|
||||
{ name: 'tablet', width: 1200 },
|
||||
|
||||
@@ -63,7 +63,7 @@ define([
|
||||
};
|
||||
|
||||
/**
|
||||
* enables some console.log() information
|
||||
* enables some debug output in console
|
||||
* @type {boolean}
|
||||
*/
|
||||
let debug = false;
|
||||
@@ -297,20 +297,29 @@ define([
|
||||
|
||||
// global dom remove listener -------------------------------------------------------------------
|
||||
// -> check whether the removed element had an event listener active and removes them.
|
||||
document.body.addEventListener ('DOMNodeRemoved', function(e){
|
||||
if(typeof e.target.getAttribute === 'function'){
|
||||
let eventNames = e.target.getAttribute(dataKeyEvents);
|
||||
if(eventNames){
|
||||
eventNames.split(',').forEach((event) => {
|
||||
let index = allEvents[event].elements.indexOf(e.target);
|
||||
if(index > -1){
|
||||
// remove element from event list
|
||||
allEvents[event].elements.splice(index, 1);
|
||||
new MutationObserver((mutations) => {
|
||||
mutations.forEach((mutation) => {
|
||||
if(mutation.type === 'childList'){
|
||||
for (let i = 0; i < mutation.removedNodes.length; i++){
|
||||
let removedNode = mutation.removedNodes[i];
|
||||
if(typeof removedNode.getAttribute === 'function'){
|
||||
let eventNames = removedNode.getAttribute(dataKeyEvents);
|
||||
if(eventNames){
|
||||
let events = eventNames.split(',');
|
||||
for(let j = 0; i < events.length; j++){
|
||||
let event = events[j];
|
||||
let index = allEvents[event].elements.indexOf(removedNode);
|
||||
if(index > -1){
|
||||
// remove element from event list
|
||||
allEvents[event].elements.splice(index, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}, false);
|
||||
});
|
||||
}).observe(document.body, { childList: true, subtree: true });
|
||||
|
||||
isInit = true;
|
||||
}
|
||||
|
||||
@@ -80,7 +80,7 @@ define([
|
||||
let showDialog = function(){
|
||||
// dialog content
|
||||
|
||||
requirejs(['text!templates/dialog/task_manager.html', 'mustache'], function(templateTaskManagerDialog, Mustache) {
|
||||
requirejs(['text!templates/dialog/task_manager.html', 'mustache', 'datatables.loader'], function(templateTaskManagerDialog, Mustache) {
|
||||
let data = {
|
||||
id: config.taskDialogId,
|
||||
dialogDynamicAreaClass: config.dialogDynamicAreaClass,
|
||||
|
||||
@@ -676,7 +676,8 @@ define([
|
||||
dataType: 'json',
|
||||
context: {
|
||||
cookieName: requestData.cookie,
|
||||
characterElement: characterElement
|
||||
characterElement: characterElement,
|
||||
browserTabId: Util.getBrowserTabId()
|
||||
}
|
||||
}).done(function(responseData, textStatus, request){
|
||||
this.characterElement.hideLoadingAnimation();
|
||||
@@ -698,9 +699,11 @@ define([
|
||||
let data = {
|
||||
link: this.characterElement.data('href'),
|
||||
cookieName: this.cookieName,
|
||||
browserTabId: this.browserTabId,
|
||||
character: responseData.character,
|
||||
authLabel: getCharacterAuthLabel(responseData.character.authStatus),
|
||||
authOK: responseData.character.authStatus === 'OK'
|
||||
authOK: responseData.character.authStatus === 'OK',
|
||||
hasActiveSession: responseData.character.hasActiveSession === true
|
||||
};
|
||||
|
||||
let content = Mustache.render(template, data);
|
||||
@@ -766,6 +769,12 @@ define([
|
||||
* main init "landing" page
|
||||
*/
|
||||
$(function(){
|
||||
// clear sessionStorage
|
||||
Util.clearSessionStorage();
|
||||
|
||||
// set default AJAX config
|
||||
Util.ajaxSetup();
|
||||
|
||||
// set Dialog default config
|
||||
Util.initDefaultBootboxConfig();
|
||||
|
||||
|
||||
@@ -28,9 +28,9 @@ define([
|
||||
overlayLocalJumpsClass: 'pf-map-overlay-local-jumps', // class for jump distance for table results
|
||||
|
||||
// dataTable
|
||||
tableImageCellClass: 'pf-table-image-cell', // class for table "image" cells
|
||||
tableActionCellClass: 'pf-table-action-cell', // class for table "action" cells
|
||||
tableActionCellIconClass: 'pf-table-action-icon-cell', // class for table "action" icon (icon is part of cell content)
|
||||
tableCellImageClass: 'pf-table-image-cell', // class for table "image" cells
|
||||
tableCellActionClass: 'pf-table-action-cell', // class for table "action" cells
|
||||
tableCellActionIconClass: 'pf-table-action-icon-cell', // class for table "action" icon (icon is part of cell content)
|
||||
|
||||
// toolbar
|
||||
toolbarClass: 'pf-map-overlay-toolbar', // class for toolbar - content
|
||||
@@ -288,236 +288,240 @@ define([
|
||||
* @returns {*}
|
||||
*/
|
||||
$.fn.initLocalOverlay = function(mapId){
|
||||
return this.each(function(){
|
||||
let parentElement = $(this);
|
||||
let parentElements = $(this);
|
||||
|
||||
let overlay = $('<div>', {
|
||||
class: [config.overlayClass, config.overlayLocalClass].join(' ')
|
||||
});
|
||||
require(['datatables.loader'], () => {
|
||||
parentElements.each(function(){
|
||||
let parentElement = $(this);
|
||||
|
||||
let content = $('<div>', {
|
||||
class: [ 'text-right', config.overlayLocalContentClass].join(' ')
|
||||
});
|
||||
let overlay = $('<div>', {
|
||||
class: [config.overlayClass, config.overlayLocalClass].join(' ')
|
||||
});
|
||||
|
||||
// crate new route table
|
||||
let table = $('<table>', {
|
||||
class: ['compact', 'order-column', config.overlayLocalTableClass].join(' ')
|
||||
});
|
||||
let content = $('<div>', {
|
||||
class: [ 'text-right', config.overlayLocalContentClass].join(' ')
|
||||
});
|
||||
|
||||
let overlayMain = $('<div>', {
|
||||
text: '',
|
||||
class: config.overlayLocalMainClass
|
||||
}).append(
|
||||
$('<i>', {
|
||||
class: ['fa', 'fa-chevron-down', 'fa-fw', 'pf-animate-rotate', config.overlayLocalTriggerClass].join(' ')
|
||||
}),
|
||||
$('<span>', {
|
||||
class: ['badge', 'txt-color', 'txt-color-red', config.overlayLocalUsersClass].join(' '),
|
||||
text: 0
|
||||
}),
|
||||
$('<div>', {
|
||||
class: config.overlayLocalJumpsClass
|
||||
// crate new route table
|
||||
let table = $('<table>', {
|
||||
class: ['compact', 'order-column', config.overlayLocalTableClass].join(' ')
|
||||
});
|
||||
|
||||
let overlayMain = $('<div>', {
|
||||
text: '',
|
||||
class: config.overlayLocalMainClass
|
||||
}).append(
|
||||
$('<i>', {
|
||||
class: ['fa', 'fa-chevron-down', 'fa-fw', 'pf-animate-rotate', config.overlayLocalTriggerClass].join(' ')
|
||||
}),
|
||||
$('<span>', {
|
||||
class: ['badge', 'txt-color', 'txt-color-red', config.overlayLocalUsersClass].join(' '),
|
||||
text: 0
|
||||
}),
|
||||
$('<div>', {
|
||||
class: config.overlayLocalJumpsClass
|
||||
}).append(
|
||||
$('<span>', {
|
||||
class: ['badge', 'txt-color', 'txt-color-grayLight'].join(' '),
|
||||
text: MapUtil.config.defaultLocalJumpRadius
|
||||
}).attr('title', 'jumps')
|
||||
)
|
||||
);
|
||||
|
||||
let headline = $('<div>', {
|
||||
class: config.overlayLocalHeadlineClass
|
||||
}).append(
|
||||
$('<span>', {
|
||||
class: ['badge', 'txt-color', 'txt-color-grayLight'].join(' '),
|
||||
text: MapUtil.config.defaultLocalJumpRadius
|
||||
}).attr('title', 'jumps')
|
||||
)
|
||||
);
|
||||
html: 'Nearby ',
|
||||
class: 'pull-left'
|
||||
}),
|
||||
$('<span>'),
|
||||
$('<span>'),
|
||||
$('<span>', {
|
||||
class: ['badge', ' txt-color', 'txt-color-red'].join(' '),
|
||||
text: 0
|
||||
})
|
||||
);
|
||||
|
||||
let headline = $('<div>', {
|
||||
class: config.overlayLocalHeadlineClass
|
||||
}).append(
|
||||
$('<span>', {
|
||||
html: 'Nearby ',
|
||||
class: 'pull-left'
|
||||
}),
|
||||
$('<span>'),
|
||||
$('<span>'),
|
||||
$('<span>', {
|
||||
class: ['badge', ' txt-color', 'txt-color-red'].join(' '),
|
||||
text: 0
|
||||
})
|
||||
);
|
||||
content.append(headline);
|
||||
content.append(table);
|
||||
// toolbar not used for now
|
||||
// content.append(initToolbar());
|
||||
|
||||
content.append(headline);
|
||||
content.append(table);
|
||||
// toolbar not used for now
|
||||
// content.append(initToolbar());
|
||||
overlay.append(overlayMain);
|
||||
overlay.append(content);
|
||||
|
||||
overlay.append(overlayMain);
|
||||
overlay.append(content);
|
||||
// set observer
|
||||
setOverlayObserver(overlay, mapId);
|
||||
|
||||
// set observer
|
||||
setOverlayObserver(overlay, mapId);
|
||||
parentElement.append(overlay);
|
||||
|
||||
parentElement.append(overlay);
|
||||
// init local table ---------------------------------------------------------------------------------------
|
||||
|
||||
// init local table ---------------------------------------------------------------------------------------
|
||||
table.on('draw.dt', function(e, settings){
|
||||
// init table tooltips
|
||||
$(this).find('td').initTooltips({
|
||||
container: 'body',
|
||||
placement: 'left'
|
||||
});
|
||||
|
||||
table.on('draw.dt', function(e, settings){
|
||||
// init table tooltips
|
||||
$(this).find('td').initTooltips({
|
||||
container: 'body',
|
||||
placement: 'left'
|
||||
});
|
||||
|
||||
// hide pagination in case of only one page
|
||||
let paginationElement = overlay.find('.dataTables_paginate');
|
||||
let pageElements = paginationElement.find('span .paginate_button');
|
||||
if(pageElements.length <= 1){
|
||||
paginationElement.hide();
|
||||
}else{
|
||||
paginationElement.show();
|
||||
}
|
||||
});
|
||||
|
||||
// table init complete
|
||||
table.on( 'init.dt', function (){
|
||||
// init table head tooltips
|
||||
$(this).initTooltips({
|
||||
container: 'body',
|
||||
placement: 'top'
|
||||
});
|
||||
});
|
||||
|
||||
let localTable = table.DataTable( {
|
||||
pageLength: 13, // hint: if pagination visible => we need space to show it
|
||||
paging: true,
|
||||
lengthChange: false,
|
||||
ordering: true,
|
||||
order: [ 0, 'asc' ],
|
||||
info: false,
|
||||
searching: false,
|
||||
hover: false,
|
||||
autoWidth: false,
|
||||
rowId: function(rowData) {
|
||||
return 'pf-local-row_' + rowData.id; // characterId
|
||||
},
|
||||
language: {
|
||||
emptyTable: '<span>You are alone</span>'
|
||||
},
|
||||
columnDefs: [
|
||||
{
|
||||
targets: 0,
|
||||
orderable: true,
|
||||
title: '<span title="jumps" data-toggle="tooltip"> </span>',
|
||||
width: '1px',
|
||||
className: ['pf-help-default', 'text-center'].join(' '),
|
||||
data: 'jumps',
|
||||
render: {
|
||||
_: function(data, type, row, meta){
|
||||
let value = data;
|
||||
if(type === 'display'){
|
||||
if(value === 0){
|
||||
value = '<i class="fa fa-map-marker"></i>';
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
},
|
||||
createdCell: function(cell, cellData, rowData, rowIndex, colIndex){
|
||||
let api = this.DataTable();
|
||||
initCellTooltip(api, cell, 'log.system.name');
|
||||
}
|
||||
},{
|
||||
targets: 1,
|
||||
orderable: false,
|
||||
title: '',
|
||||
width: '26px',
|
||||
className: ['pf-help-default', 'text-center', config.tableImageCellClass].join(' '),
|
||||
data: 'log.ship',
|
||||
render: {
|
||||
_: function(data, type, row, meta){
|
||||
let value = data.typeName;
|
||||
if(type === 'display'){
|
||||
value = '<img src="' + Init.url.ccpImageServer + 'Render/' + data.typeId + '_32.png"/>';
|
||||
}
|
||||
return value;
|
||||
}
|
||||
},
|
||||
createdCell: function(cell, cellData, rowData, rowIndex, colIndex){
|
||||
let api = this.DataTable();
|
||||
initCellTooltip(api, cell, 'log.ship.typeName');
|
||||
}
|
||||
}, {
|
||||
targets: 2,
|
||||
orderable: true,
|
||||
title: 'ship name',
|
||||
width: '80px',
|
||||
data: 'log.ship',
|
||||
render: {
|
||||
_: function(data, type, row, meta){
|
||||
let value = data.name;
|
||||
if(type === 'display'){
|
||||
value = '<div class="' + MapUtil.config.tableCellEllipsisClass + ' ' + MapUtil.config.tableCellEllipsis80Class + '">' + data.name + '</div>';
|
||||
}
|
||||
return value;
|
||||
},
|
||||
sort: 'name'
|
||||
}
|
||||
},{
|
||||
targets: 3,
|
||||
orderable: true,
|
||||
title: 'pilot',
|
||||
data: 'name',
|
||||
render: {
|
||||
_: function(data, type, row, meta){
|
||||
let value = data;
|
||||
if(type === 'display'){
|
||||
value = '<div class="' + MapUtil.config.tableCellEllipsisClass + ' ' + MapUtil.config.tableCellEllipsis90Class + '">' + data + '</div>';
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
},{
|
||||
targets: 4,
|
||||
orderable: false,
|
||||
title: '<i title="docked station" data-toggle="tooltip" class="fa fa-home text-right"></i>',
|
||||
width: '10px',
|
||||
className: ['pf-help-default'].join(' '),
|
||||
data: 'log.station',
|
||||
render: {
|
||||
_: function(data, type, row, meta){
|
||||
let value = '';
|
||||
if(
|
||||
type === 'display' &&
|
||||
data.id
|
||||
){
|
||||
value = '<i class="fa fa-home"></i>';
|
||||
}
|
||||
return value;
|
||||
}
|
||||
},
|
||||
createdCell: function(cell, cellData, rowData, rowIndex, colIndex){
|
||||
let api = this.DataTable();
|
||||
initCellTooltip(api, cell, 'log.station.name');
|
||||
}
|
||||
},{
|
||||
targets: 5,
|
||||
orderable: false,
|
||||
title: '<i title="open ingame" data-toggle="tooltip" class="fa fa-id-card text-right"></i>',
|
||||
width: '10px',
|
||||
className: [config.tableActionCellClass].join(' '),
|
||||
data: 'id',
|
||||
render: {
|
||||
_: function(data, type, row, meta){
|
||||
let value = data;
|
||||
if(type === 'display'){
|
||||
value = '<i class="fa fa-id-card ' + config.tableActionCellIconClass + '"></i>';
|
||||
}
|
||||
return value;
|
||||
}
|
||||
},
|
||||
createdCell: function(cell, cellData, rowData, rowIndex, colIndex){
|
||||
// open character information window (ingame)
|
||||
$(cell).on('click', { tableApi: this.DataTable(), cellData: cellData }, function(e){
|
||||
let cellData = e.data.tableApi.cell(this).data();
|
||||
Util.openIngameWindow(e.data.cellData);
|
||||
});
|
||||
}
|
||||
// hide pagination in case of only one page
|
||||
let paginationElement = overlay.find('.dataTables_paginate');
|
||||
let pageElements = paginationElement.find('span .paginate_button');
|
||||
if(pageElements.length <= 1){
|
||||
paginationElement.hide();
|
||||
}else{
|
||||
paginationElement.show();
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
// table init complete
|
||||
table.on( 'init.dt', function (){
|
||||
// init table head tooltips
|
||||
$(this).initTooltips({
|
||||
container: 'body',
|
||||
placement: 'top'
|
||||
});
|
||||
});
|
||||
|
||||
let localTable = table.DataTable( {
|
||||
pageLength: 13, // hint: if pagination visible => we need space to show it
|
||||
paging: true,
|
||||
lengthChange: false,
|
||||
ordering: true,
|
||||
order: [ 0, 'asc' ],
|
||||
info: false,
|
||||
searching: false,
|
||||
hover: false,
|
||||
autoWidth: false,
|
||||
rowId: function(rowData) {
|
||||
return 'pf-local-row_' + rowData.id; // characterId
|
||||
},
|
||||
language: {
|
||||
emptyTable: '<span>You are alone</span>'
|
||||
},
|
||||
columnDefs: [
|
||||
{
|
||||
targets: 0,
|
||||
orderable: true,
|
||||
title: '<span title="jumps" data-toggle="tooltip"> </span>',
|
||||
width: '1px',
|
||||
className: ['pf-help-default', 'text-center'].join(' '),
|
||||
data: 'jumps',
|
||||
render: {
|
||||
_: function(data, type, row, meta){
|
||||
let value = data;
|
||||
if(type === 'display'){
|
||||
if(value === 0){
|
||||
value = '<i class="fa fa-map-marker"></i>';
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
},
|
||||
createdCell: function(cell, cellData, rowData, rowIndex, colIndex){
|
||||
let api = this.DataTable();
|
||||
initCellTooltip(api, cell, 'log.system.name');
|
||||
}
|
||||
},{
|
||||
targets: 1,
|
||||
orderable: false,
|
||||
title: '',
|
||||
width: '26px',
|
||||
className: ['pf-help-default', 'text-center', config.tableCellImageClass].join(' '),
|
||||
data: 'log.ship',
|
||||
render: {
|
||||
_: function(data, type, row, meta){
|
||||
let value = data.typeName;
|
||||
if(type === 'display'){
|
||||
value = '<img src="' + Init.url.ccpImageServer + '/Render/' + data.typeId + '_32.png"/>';
|
||||
}
|
||||
return value;
|
||||
}
|
||||
},
|
||||
createdCell: function(cell, cellData, rowData, rowIndex, colIndex){
|
||||
let api = this.DataTable();
|
||||
initCellTooltip(api, cell, 'log.ship.typeName');
|
||||
}
|
||||
}, {
|
||||
targets: 2,
|
||||
orderable: true,
|
||||
title: 'ship name',
|
||||
width: '80px',
|
||||
data: 'log.ship',
|
||||
render: {
|
||||
_: function(data, type, row, meta){
|
||||
let value = data.name;
|
||||
if(type === 'display'){
|
||||
value = '<div class="' + MapUtil.config.tableCellEllipsisClass + ' ' + MapUtil.config.tableCellEllipsis80Class + '">' + data.name + '</div>';
|
||||
}
|
||||
return value;
|
||||
},
|
||||
sort: 'name'
|
||||
}
|
||||
},{
|
||||
targets: 3,
|
||||
orderable: true,
|
||||
title: 'pilot',
|
||||
data: 'name',
|
||||
render: {
|
||||
_: function(data, type, row, meta){
|
||||
let value = data;
|
||||
if(type === 'display'){
|
||||
value = '<div class="' + MapUtil.config.tableCellEllipsisClass + ' ' + MapUtil.config.tableCellEllipsis90Class + '">' + data + '</div>';
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
},{
|
||||
targets: 4,
|
||||
orderable: false,
|
||||
title: '<i title="docked station" data-toggle="tooltip" class="fa fa-home text-right"></i>',
|
||||
width: '10px',
|
||||
className: ['pf-help-default'].join(' '),
|
||||
data: 'log.station',
|
||||
render: {
|
||||
_: function(data, type, row, meta){
|
||||
let value = '';
|
||||
if(
|
||||
type === 'display' &&
|
||||
data.id
|
||||
){
|
||||
value = '<i class="fa fa-home"></i>';
|
||||
}
|
||||
return value;
|
||||
}
|
||||
},
|
||||
createdCell: function(cell, cellData, rowData, rowIndex, colIndex){
|
||||
let api = this.DataTable();
|
||||
initCellTooltip(api, cell, 'log.station.name');
|
||||
}
|
||||
},{
|
||||
targets: 5,
|
||||
orderable: false,
|
||||
title: '<i title="open ingame" data-toggle="tooltip" class="fa fa-id-card text-right"></i>',
|
||||
width: '10px',
|
||||
className: [config.tableCellActionClass].join(' '),
|
||||
data: 'id',
|
||||
render: {
|
||||
_: function(data, type, row, meta){
|
||||
let value = data;
|
||||
if(type === 'display'){
|
||||
value = '<i class="fa fa-id-card ' + config.tableCellActionIconClass + '"></i>';
|
||||
}
|
||||
return value;
|
||||
}
|
||||
},
|
||||
createdCell: function(cell, cellData, rowData, rowIndex, colIndex){
|
||||
// open character information window (ingame)
|
||||
$(cell).on('click', { tableApi: this.DataTable(), cellData: cellData }, function(e){
|
||||
let cellData = e.data.tableApi.cell(this).data();
|
||||
Util.openIngameWindow(e.data.cellData);
|
||||
});
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
@@ -945,14 +945,14 @@ define([
|
||||
mapContainer = $(mapContainer);
|
||||
|
||||
// add additional information for this map
|
||||
if(mapContainer.data('updated') !== mapConfig.config.updated){
|
||||
if(mapContainer.data('updated') !== mapConfig.config.updated.updated){
|
||||
mapContainer.data('name', mapConfig.config.name);
|
||||
mapContainer.data('scopeId', mapConfig.config.scope.id);
|
||||
mapContainer.data('typeId', mapConfig.config.type.id);
|
||||
mapContainer.data('typeName', mapConfig.config.type.name);
|
||||
mapContainer.data('icon', mapConfig.config.icon);
|
||||
mapContainer.data('created', mapConfig.config.created);
|
||||
mapContainer.data('updated', mapConfig.config.updated);
|
||||
mapContainer.data('created', mapConfig.config.created.created);
|
||||
mapContainer.data('updated', mapConfig.config.updated.updated);
|
||||
}
|
||||
|
||||
// get map data
|
||||
@@ -1320,11 +1320,10 @@ define([
|
||||
* @returns {boolean}
|
||||
*/
|
||||
let isValidSystem = function(systemData){
|
||||
|
||||
let isValid = true;
|
||||
|
||||
if(
|
||||
! systemData.hasOwnProperty('name') ||
|
||||
!systemData.hasOwnProperty('name') ||
|
||||
systemData.name.length === 0
|
||||
){
|
||||
return false;
|
||||
@@ -1382,37 +1381,51 @@ define([
|
||||
|
||||
/**
|
||||
* save a new system and add it to the map
|
||||
* @param map
|
||||
* @param requestData
|
||||
* @param sourceSystem
|
||||
* @param callback
|
||||
* @param context
|
||||
*/
|
||||
let saveSystem = function(map, requestData, sourceSystem, callback){
|
||||
let saveSystem = function(requestData, context){
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: Init.path.saveSystem,
|
||||
data: requestData,
|
||||
dataType: 'json',
|
||||
context: {
|
||||
map: map,
|
||||
sourceSystem: sourceSystem
|
||||
context: context
|
||||
}).done(function(responseData){
|
||||
let newSystemData = responseData.systemData;
|
||||
|
||||
if( !$.isEmptyObject(newSystemData) ){
|
||||
Util.showNotify({title: 'New system', text: newSystemData.name, type: 'success'});
|
||||
|
||||
// draw new system to map
|
||||
drawSystem(this.map, newSystemData, this.sourceSystem);
|
||||
|
||||
// re/arrange systems (prevent overlapping)
|
||||
MagnetizerWrapper.setElements(this.map);
|
||||
|
||||
if(this.onSuccess){
|
||||
this.onSuccess();
|
||||
}
|
||||
}
|
||||
}).done(function(newSystemData){
|
||||
Util.showNotify({title: 'New system', text: newSystemData.name, type: 'success'});
|
||||
|
||||
// draw new system to map
|
||||
drawSystem(this.map, newSystemData, this.sourceSystem);
|
||||
|
||||
// re/arrange systems (prevent overlapping)
|
||||
MagnetizerWrapper.setElements(this.map);
|
||||
|
||||
if(callback){
|
||||
callback();
|
||||
// show errors
|
||||
if(
|
||||
responseData.error &&
|
||||
responseData.error.length > 0
|
||||
){
|
||||
for(let i = 0; i < responseData.error.length; i++){
|
||||
let error = responseData.error[i];
|
||||
Util.showNotify({title: error.field + ' error', text: 'System: ' + error.message, type: error.type});
|
||||
}
|
||||
}
|
||||
}).fail(function( jqXHR, status, error) {
|
||||
let reason = status + ' ' + error;
|
||||
Util.showNotify({title: jqXHR.status + ': saveSystem', text: reason, type: 'warning'});
|
||||
$(document).setProgramStatus('problem');
|
||||
}).always(function(){
|
||||
if(this.onAlways){
|
||||
this.onAlways(this);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1542,8 +1555,18 @@ define([
|
||||
}
|
||||
};
|
||||
|
||||
saveSystem(map, requestData, sourceSystem, function(){
|
||||
bootbox.hideAll();
|
||||
this.find('.modal-content').showLoadingAnimation();
|
||||
|
||||
saveSystem(requestData, {
|
||||
map: map,
|
||||
sourceSystem: sourceSystem,
|
||||
systemDialog: this,
|
||||
onSuccess: () => {
|
||||
bootbox.hideAll();
|
||||
},
|
||||
onAlways: (context) => {
|
||||
context.systemDialog.find('.modal-content').hideLoadingAnimation();
|
||||
}
|
||||
});
|
||||
return false;
|
||||
}
|
||||
@@ -1694,7 +1717,8 @@ define([
|
||||
mapId: mapId,
|
||||
oldConnectionData: connectionData
|
||||
}
|
||||
}).done(function(newConnectionData){
|
||||
}).done(function(responseData){
|
||||
let newConnectionData = responseData.connectionData;
|
||||
|
||||
if( !$.isEmptyObject(newConnectionData) ){
|
||||
let updateCon = false;
|
||||
@@ -1739,6 +1763,16 @@ define([
|
||||
this.map.detach(this.connection, {fireEvent: false});
|
||||
}
|
||||
|
||||
// show errors
|
||||
if(
|
||||
responseData.error &&
|
||||
responseData.error.length > 0
|
||||
){
|
||||
for(let i = 0; i < responseData.error.length; i++){
|
||||
let error = responseData.error[i];
|
||||
Util.showNotify({title: error.field + ' error', text: 'System: ' + error.message, type: error.type});
|
||||
}
|
||||
}
|
||||
}).fail(function( jqXHR, status, error) {
|
||||
// remove this connection from map
|
||||
this.map.detach(this.connection, {fireEvent: false});
|
||||
@@ -1826,7 +1860,6 @@ define([
|
||||
let moduleData = {
|
||||
id: config.mapContextMenuId,
|
||||
items: [
|
||||
{icon: 'fa-street-view', action: 'info', text: 'information'},
|
||||
{icon: 'fa-plus', action: 'add_system', text: 'add system'},
|
||||
{icon: 'fa-object-ungroup', action: 'select_all', text: 'select all'},
|
||||
{icon: 'fa-filter', action: 'filter_scope', text: 'filter scope', subitems: [
|
||||
@@ -1834,6 +1867,10 @@ define([
|
||||
{subIcon: '', subAction: 'filter_stargate', subText: 'stargate'},
|
||||
{subIcon: '', subAction: 'filter_jumpbridge', subText: 'jumpbridge'}
|
||||
]},
|
||||
{icon: 'fa-sitemap', action: 'map', text: 'map', subitems: [
|
||||
{subIcon: 'fa-edit', subAction: 'map_edit', subText: 'edit map'},
|
||||
{subIcon: 'fa-street-view', subAction: 'map_info', subText: 'map info'},
|
||||
]},
|
||||
{divider: true, action: 'delete_systems'},
|
||||
{icon: 'fa-trash', action: 'delete_systems', text: 'delete systems'}
|
||||
]
|
||||
@@ -1905,7 +1942,7 @@ define([
|
||||
items: [
|
||||
{icon: 'fa-plus', action: 'add_system', text: 'add system'},
|
||||
{icon: 'fa-lock', action: 'lock_system', text: 'lock system'},
|
||||
{icon: 'fa-users', action: 'set_rally', text: 'set rally point'},
|
||||
{icon: 'fa-volume-up', action: 'set_rally', text: 'set rally point'},
|
||||
{icon: 'fa-tags', text: 'set status', subitems: systemStatus},
|
||||
{icon: 'fa-reply fa-rotate-180', text: 'waypoints', subitems: [
|
||||
{subIcon: 'fa-flag-checkered', subAction: 'set_destination', subText: 'set destination'},
|
||||
@@ -2731,8 +2768,12 @@ define([
|
||||
let selectedSystems = currentMapElement.getSelectedSystems();
|
||||
$.fn.showDeleteSystemDialog(currentMap, selectedSystems);
|
||||
break;
|
||||
case 'info':
|
||||
// open map info dialog
|
||||
case 'map_edit':
|
||||
// open map edit dialog tab
|
||||
$(document).triggerMenuEvent('ShowMapSettings', {tab: 'edit'});
|
||||
break;
|
||||
case 'map_info':
|
||||
// open map info dialog tab
|
||||
$(document).triggerMenuEvent('ShowMapInfo', {tab: 'information'});
|
||||
break;
|
||||
|
||||
@@ -2893,37 +2934,38 @@ define([
|
||||
let mapElement = $(this);
|
||||
let mapOverlay = mapElement.getMapOverlay('local');
|
||||
|
||||
let currentCharacterLog = Util.getCurrentCharacterLog();
|
||||
let currentMapData = Util.getCurrentMapData(userData.config.id);
|
||||
let clearLocal = true;
|
||||
if(userData && userData.config && userData.config.id){
|
||||
let currentMapData = Util.getCurrentMapData(userData.config.id);
|
||||
let currentCharacterLog = Util.getCurrentCharacterLog();
|
||||
let clearLocal = true;
|
||||
|
||||
if(
|
||||
currentMapData &&
|
||||
currentCharacterLog &&
|
||||
currentCharacterLog.system
|
||||
){
|
||||
let currentSystemData = currentMapData.data.systems.filter(function (system) {
|
||||
return system.systemId === currentCharacterLog.system.id;
|
||||
});
|
||||
if(
|
||||
currentMapData &&
|
||||
currentCharacterLog &&
|
||||
currentCharacterLog.system
|
||||
){
|
||||
let currentSystemData = currentMapData.data.systems.filter(function (system) {
|
||||
return system.systemId === currentCharacterLog.system.id;
|
||||
});
|
||||
|
||||
if(currentSystemData.length){
|
||||
// current user system is on this map
|
||||
currentSystemData = currentSystemData[0];
|
||||
if(currentSystemData.length){
|
||||
// current user system is on this map
|
||||
currentSystemData = currentSystemData[0];
|
||||
|
||||
// check for active users "nearby" (x jumps radius)
|
||||
let nearBySystemData = Util.getNearBySystemData(currentSystemData, currentMapData, MapUtil.config.defaultLocalJumpRadius);
|
||||
let nearByCharacterData = Util.getNearByCharacterData(nearBySystemData, userData.data.systems);
|
||||
// check for active users "nearby" (x jumps radius)
|
||||
let nearBySystemData = Util.getNearBySystemData(currentSystemData, currentMapData, MapUtil.config.defaultLocalJumpRadius);
|
||||
let nearByCharacterData = Util.getNearByCharacterData(nearBySystemData, userData.data.systems);
|
||||
|
||||
// update "local" table in overlay
|
||||
mapOverlay.updateLocalTable(currentSystemData, nearByCharacterData);
|
||||
clearLocal = false;
|
||||
// update "local" table in overlay
|
||||
mapOverlay.updateLocalTable(currentSystemData, nearByCharacterData);
|
||||
clearLocal = false;
|
||||
}
|
||||
}
|
||||
|
||||
if(clearLocal){
|
||||
mapOverlay.clearLocalTable();
|
||||
}
|
||||
}
|
||||
|
||||
if(clearLocal){
|
||||
mapOverlay.clearLocalTable();
|
||||
}
|
||||
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -18,7 +18,19 @@ define([
|
||||
y: 0
|
||||
},
|
||||
|
||||
systemActiveClass: 'pf-system-active' // class for an active system in a map
|
||||
systemActiveClass: 'pf-system-active', // class for an active system in a map
|
||||
|
||||
dialogRallyId: 'pf-rally-dialog', // id for "Rally point" dialog
|
||||
|
||||
dialogRallyPokeDesktopId: 'pf-rally-dialog-poke-desktop', // id for "desktop" poke checkbox
|
||||
dialogRallyPokeSlackId: 'pf-rally-dialog-poke-slack', // id for "Slack" poke checkbox
|
||||
dialogRallyPokeMailId: 'pf-rally-dialog-poke-mail', // id for "mail" poke checkbox
|
||||
dialogRallyMessageId: 'pf-rally-dialog-message', // id for "message" textarea
|
||||
|
||||
dialogRallyMessageDefault: '' +
|
||||
'I need some help!\n\n' +
|
||||
'- Potential PvP options around\n' +
|
||||
'- DPS and Logistic ships needed'
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -26,41 +38,105 @@ define([
|
||||
* @param system
|
||||
*/
|
||||
$.fn.showRallyPointDialog = (system) => {
|
||||
let mapData = Util.getCurrentMapData(system.data('mapid'));
|
||||
|
||||
requirejs(['text!templates/dialog/system_rally.html', 'mustache'], function(template, Mustache) {
|
||||
|
||||
let setCheckboxObserver = (checkboxes) => {
|
||||
checkboxes.each(function(){
|
||||
$(this).on('change', function(){
|
||||
// check all others
|
||||
let allUnchecked = true;
|
||||
checkboxes.each(function(){
|
||||
if(this.checked){
|
||||
allUnchecked = false;
|
||||
}
|
||||
});
|
||||
let textareaElement = $('#' + config.dialogRallyMessageId);
|
||||
if(allUnchecked){
|
||||
textareaElement.prop('disabled', true);
|
||||
}else{
|
||||
textareaElement.prop('disabled', false);
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
let sendPoke = (requestData, context) => {
|
||||
// lock dialog
|
||||
let dialogContent = context.rallyDialog.find('.modal-content');
|
||||
dialogContent.showLoadingAnimation();
|
||||
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: Init.path.pokeRally,
|
||||
data: requestData,
|
||||
dataType: 'json',
|
||||
context: context
|
||||
}).done(function(data){
|
||||
|
||||
}).fail(function( jqXHR, status, error) {
|
||||
let reason = status + ' ' + error;
|
||||
Util.showNotify({title: jqXHR.status + ': sendPoke', text: reason, type: 'warning'});
|
||||
}).always(function(){
|
||||
this.rallyDialog.find('.modal-content').hideLoadingAnimation();
|
||||
});
|
||||
};
|
||||
|
||||
let data = {
|
||||
notificationStatus: Init.notificationStatus.rallySet
|
||||
id: config.dialogRallyId,
|
||||
|
||||
dialogRallyPokeDesktopId: config.dialogRallyPokeDesktopId,
|
||||
dialogRallyPokeSlackId: config.dialogRallyPokeSlackId,
|
||||
dialogRallyPokeMailId: config.dialogRallyPokeMailId,
|
||||
dialogRallyMessageId: config.dialogRallyMessageId ,
|
||||
|
||||
desktopRallyEnabled: true,
|
||||
slackRallyEnabled: Boolean(Util.getObjVal(mapData, 'config.logging.slackRally')),
|
||||
mailRallyEnabled: Boolean(Util.getObjVal(mapData, 'config.logging.mailRally')),
|
||||
dialogRallyMessageDefault: config.dialogRallyMessageDefault,
|
||||
|
||||
systemId: system.data('id')
|
||||
};
|
||||
|
||||
let content = Mustache.render(template, data);
|
||||
|
||||
let rallyDialog = bootbox.dialog({
|
||||
message: content,
|
||||
title: 'Set rally point for "' + system.getSystemInfo( ['alias'] ) + '"',
|
||||
title: 'Set rally point in "' + system.getSystemInfo( ['alias'] ) + '"',
|
||||
buttons: {
|
||||
close: {
|
||||
label: 'cancel',
|
||||
className: 'btn-default'
|
||||
},
|
||||
setRallyPoke: {
|
||||
label: '<i class="fa fa-fw fa-volume-up"></i> set rally and poke',
|
||||
className: 'btn-primary',
|
||||
callback: function() {
|
||||
system.setSystemRally(1, {
|
||||
poke: true
|
||||
});
|
||||
system.markAsChanged();
|
||||
}
|
||||
},
|
||||
success: {
|
||||
label: '<i class="fa fa-fw fa-users"></i> set rally',
|
||||
label: '<i class="fa fa-fw fa-volume-up"></i> set rally point',
|
||||
className: 'btn-success',
|
||||
callback: function() {
|
||||
system.setSystemRally(1);
|
||||
let form = $('#' + config.dialogRallyId).find('form');
|
||||
// get form data
|
||||
let formData = form.getFormValues();
|
||||
|
||||
// update map
|
||||
system.setSystemRally(1, {
|
||||
poke: Boolean(formData.pokeDesktop)
|
||||
});
|
||||
system.markAsChanged();
|
||||
|
||||
// send poke data to server
|
||||
sendPoke(formData, {
|
||||
rallyDialog: this
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// after modal is shown ==================================================================================
|
||||
rallyDialog.on('shown.bs.modal', function(e){
|
||||
// set event for checkboxes
|
||||
setCheckboxObserver(rallyDialog.find(':checkbox'));
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -23,6 +23,9 @@ define([
|
||||
$(() => {
|
||||
Util.initPrototypes();
|
||||
|
||||
// clear sessionStorage
|
||||
//Util.clearSessionStorage();
|
||||
|
||||
// set default AJAX config
|
||||
Util.ajaxSetup();
|
||||
|
||||
@@ -63,7 +66,8 @@ define([
|
||||
Init.systemType = initData.systemType;
|
||||
Init.characterStatus = initData.characterStatus;
|
||||
Init.routes = initData.routes;
|
||||
Init.notificationStatus = initData.notificationStatus;
|
||||
Init.url = initData.url;
|
||||
Init.slack = initData.slack;
|
||||
Init.routeSearch = initData.routeSearch;
|
||||
Init.programMode = initData.programMode;
|
||||
|
||||
@@ -165,8 +169,7 @@ define([
|
||||
|
||||
if(jqXHR.responseJSON){
|
||||
// handle JSON
|
||||
let errorObj = $.parseJSON(jqXHR.responseText);
|
||||
|
||||
let errorObj = jqXHR.responseJSON;
|
||||
if(
|
||||
errorObj.error &&
|
||||
errorObj.error.length > 0
|
||||
@@ -182,7 +185,6 @@ define([
|
||||
}
|
||||
|
||||
$(document).trigger('pf:shutdown', {status: jqXHR.status, reason: reason, error: errorData});
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -9,12 +9,7 @@ define([
|
||||
'app/ui/system_graph',
|
||||
'app/ui/system_signature',
|
||||
'app/ui/system_route',
|
||||
'app/ui/system_killboard',
|
||||
'datatables.net',
|
||||
'datatables.net-buttons',
|
||||
'datatables.net-buttons-html',
|
||||
'datatables.net-responsive',
|
||||
'datatables.net-select'
|
||||
'app/ui/system_killboard'
|
||||
], function($, Init, Util, Map, MapUtil) {
|
||||
|
||||
'use strict';
|
||||
@@ -106,29 +101,31 @@ define([
|
||||
* @param tabContentElement
|
||||
*/
|
||||
let drawSystemModules = function(tabContentElement){
|
||||
let currentSystemData = Util.getCurrentSystemData();
|
||||
require(['datatables.loader'], () => {
|
||||
let currentSystemData = Util.getCurrentSystemData();
|
||||
|
||||
// get Table cell for system Info
|
||||
let firstCell = $(tabContentElement).find('.' + config.mapTabContentCellFirst);
|
||||
let secondCell = $(tabContentElement).find('.' + config.mapTabContentCellSecond);
|
||||
// get Table cell for system Info
|
||||
let firstCell = $(tabContentElement).find('.' + config.mapTabContentCellFirst);
|
||||
let secondCell = $(tabContentElement).find('.' + config.mapTabContentCellSecond);
|
||||
|
||||
// draw system info module
|
||||
firstCell.drawSystemInfoModule(currentSystemData.mapId, currentSystemData.systemData);
|
||||
// draw system info module
|
||||
firstCell.drawSystemInfoModule(currentSystemData.mapId, currentSystemData.systemData);
|
||||
|
||||
// draw system graph module
|
||||
firstCell.drawSystemGraphModule(currentSystemData.systemData);
|
||||
// draw system graph module
|
||||
firstCell.drawSystemGraphModule(currentSystemData.systemData);
|
||||
|
||||
// draw signature table module
|
||||
firstCell.drawSignatureTableModule(currentSystemData.mapId, currentSystemData.systemData);
|
||||
// draw signature table module
|
||||
firstCell.drawSignatureTableModule(currentSystemData.mapId, currentSystemData.systemData);
|
||||
|
||||
// draw system routes module
|
||||
secondCell.drawSystemRouteModule(currentSystemData.mapId, currentSystemData.systemData);
|
||||
// draw system routes module
|
||||
secondCell.drawSystemRouteModule(currentSystemData.mapId, currentSystemData.systemData);
|
||||
|
||||
// draw system killboard module
|
||||
secondCell.drawSystemKillboardModule(currentSystemData.systemData);
|
||||
// draw system killboard module
|
||||
secondCell.drawSystemKillboardModule(currentSystemData.systemData);
|
||||
|
||||
// set Module Observer
|
||||
setModuleObserver();
|
||||
// set Module Observer
|
||||
setModuleObserver();
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -295,7 +292,12 @@ define([
|
||||
let tabElement = $(this);
|
||||
|
||||
// set "main" data
|
||||
tabElement.data('map-id', options.id).data('updated', options.updated);
|
||||
tabElement.data('map-id', options.id);
|
||||
|
||||
// add updated timestamp (not available for "add" tab
|
||||
if(Util.getObjVal(options, 'updated.updated')){
|
||||
tabElement.data('updated', options.updated.updated);
|
||||
}
|
||||
|
||||
// change "tab" link
|
||||
tabElement.attr('href', '#' + config.mapTabIdPrefix + options.id);
|
||||
@@ -565,7 +567,7 @@ define([
|
||||
activeMapIds.push(mapId);
|
||||
|
||||
// check for map data change and update tab
|
||||
if(tabMapData.config.updated > tabElement.data('updated')){
|
||||
if(tabMapData.config.updated.updated > tabElement.data('updated')){
|
||||
tabElement.updateTabData(tabMapData.config);
|
||||
}
|
||||
}else{
|
||||
|
||||
@@ -198,8 +198,7 @@ define([
|
||||
getMenuHeadline('Information')
|
||||
).append(
|
||||
$('<a>', {
|
||||
class: 'list-group-item list-group-item-info',
|
||||
href: '#'
|
||||
class: 'list-group-item list-group-item-info'
|
||||
}).html(' Statistics').prepend(
|
||||
$('<i>',{
|
||||
class: 'fa fa-line-chart fa-fw'
|
||||
@@ -209,8 +208,7 @@ define([
|
||||
})
|
||||
).append(
|
||||
$('<a>', {
|
||||
class: 'list-group-item list-group-item-info',
|
||||
href: '#'
|
||||
class: 'list-group-item list-group-item-info'
|
||||
}).html(' Effect info').prepend(
|
||||
$('<i>',{
|
||||
class: 'fa fa-crosshairs fa-fw'
|
||||
@@ -220,8 +218,7 @@ define([
|
||||
})
|
||||
).append(
|
||||
$('<a>', {
|
||||
class: 'list-group-item list-group-item-info',
|
||||
href: '#'
|
||||
class: 'list-group-item list-group-item-info'
|
||||
}).html(' Jump info').prepend(
|
||||
$('<i>',{
|
||||
class: 'fa fa-space-shuttle fa-fw'
|
||||
@@ -233,8 +230,7 @@ define([
|
||||
getMenuHeadline('Settings')
|
||||
).append(
|
||||
$('<a>', {
|
||||
class: 'list-group-item',
|
||||
href: '#'
|
||||
class: 'list-group-item'
|
||||
}).html(' Account').prepend(
|
||||
$('<i>',{
|
||||
class: 'fa fa-user fa-fw'
|
||||
@@ -245,8 +241,7 @@ define([
|
||||
).append(
|
||||
$('<a>', {
|
||||
class: 'list-group-item hide', // trigger by js
|
||||
id: Util.config.menuButtonFullScreenId,
|
||||
href: '#'
|
||||
id: Util.config.menuButtonFullScreenId
|
||||
}).html(' Full screen').prepend(
|
||||
$('<i>',{
|
||||
class: 'glyphicon glyphicon-fullscreen',
|
||||
@@ -265,8 +260,7 @@ define([
|
||||
})
|
||||
).append(
|
||||
$('<a>', {
|
||||
class: 'list-group-item',
|
||||
href: '#'
|
||||
class: 'list-group-item'
|
||||
}).html(' Notification test').prepend(
|
||||
$('<i>',{
|
||||
class: 'fa fa-volume-up fa-fw'
|
||||
@@ -278,8 +272,7 @@ define([
|
||||
getMenuHeadline('Danger zone')
|
||||
).append(
|
||||
$('<a>', {
|
||||
class: 'list-group-item list-group-item-danger',
|
||||
href: '#'
|
||||
class: 'list-group-item list-group-item-danger'
|
||||
}).html(' Delete account').prepend(
|
||||
$('<i>',{
|
||||
class: 'fa fa-user-times fa-fw'
|
||||
@@ -289,8 +282,7 @@ define([
|
||||
})
|
||||
).append(
|
||||
$('<a>', {
|
||||
class: 'list-group-item list-group-item-warning',
|
||||
href: '#'
|
||||
class: 'list-group-item list-group-item-warning'
|
||||
}).html(' Logout').prepend(
|
||||
$('<i>',{
|
||||
class: 'fa fa-sign-in fa-fw'
|
||||
@@ -317,8 +309,7 @@ define([
|
||||
class: 'list-group'
|
||||
}).append(
|
||||
$('<a>', {
|
||||
class: 'list-group-item',
|
||||
href: '#'
|
||||
class: 'list-group-item'
|
||||
}).html(' Information').prepend(
|
||||
$('<i>',{
|
||||
class: 'fa fa-street-view fa-fw'
|
||||
@@ -327,12 +318,11 @@ define([
|
||||
$(document).triggerMenuEvent('ShowMapInfo', {tab: 'information'});
|
||||
})
|
||||
).append(
|
||||
getMenuHeadline('Settings')
|
||||
getMenuHeadline('Configuration')
|
||||
).append(
|
||||
$('<a>', {
|
||||
class: 'list-group-item',
|
||||
href: '#'
|
||||
}).html(' Configuration').prepend(
|
||||
class: 'list-group-item'
|
||||
}).html(' Settings').prepend(
|
||||
$('<i>',{
|
||||
class: 'fa fa-gears fa-fw'
|
||||
})
|
||||
@@ -342,8 +332,7 @@ define([
|
||||
).append(
|
||||
$('<a>', {
|
||||
class: 'list-group-item',
|
||||
id: Util.config.menuButtonGridId,
|
||||
href: '#'
|
||||
id: Util.config.menuButtonGridId
|
||||
}).html(' Grid snapping').prepend(
|
||||
$('<i>',{
|
||||
class: 'glyphicon glyphicon-th'
|
||||
@@ -357,8 +346,7 @@ define([
|
||||
).append(
|
||||
$('<a>', {
|
||||
class: 'list-group-item',
|
||||
id: Util.config.menuButtonMagnetizerId,
|
||||
href: '#'
|
||||
id: Util.config.menuButtonMagnetizerId
|
||||
}).html(' Magnetizing').prepend(
|
||||
$('<i>',{
|
||||
class: 'fa fa-magnet fa-fw'
|
||||
@@ -372,8 +360,7 @@ define([
|
||||
).append(
|
||||
$('<a>', {
|
||||
class: 'list-group-item',
|
||||
id: Util.config.menuButtonEndpointId,
|
||||
href: '#'
|
||||
id: Util.config.menuButtonEndpointId
|
||||
}).html(' Signatures').prepend(
|
||||
$('<i>',{
|
||||
class: 'fa fa-link fa-fw'
|
||||
@@ -388,8 +375,7 @@ define([
|
||||
getMenuHeadline('Help')
|
||||
).append(
|
||||
$('<a>', {
|
||||
class: 'list-group-item list-group-item-info',
|
||||
href: '#'
|
||||
class: 'list-group-item list-group-item-info'
|
||||
}).html(' Manual').prepend(
|
||||
$('<i>',{
|
||||
class: 'fa fa-book fa-fw'
|
||||
@@ -399,8 +385,7 @@ define([
|
||||
})
|
||||
).append(
|
||||
$('<a>', {
|
||||
class: 'list-group-item list-group-item-info',
|
||||
href: '#'
|
||||
class: 'list-group-item list-group-item-info'
|
||||
}).html(' Shortcuts').prepend(
|
||||
$('<i>',{
|
||||
class: 'fa fa-keyboard-o fa-fw'
|
||||
@@ -410,8 +395,7 @@ define([
|
||||
})
|
||||
).append(
|
||||
$('<a>', {
|
||||
class: 'list-group-item list-group-item-info',
|
||||
href: '#'
|
||||
class: 'list-group-item list-group-item-info'
|
||||
}).html(' Task-Manager').prepend(
|
||||
$('<i>',{
|
||||
class: 'fa fa-tasks fa-fw'
|
||||
@@ -423,8 +407,7 @@ define([
|
||||
getMenuHeadline('Danger zone')
|
||||
).append(
|
||||
$('<a>', {
|
||||
class: 'list-group-item list-group-item-danger',
|
||||
href: '#'
|
||||
class: 'list-group-item list-group-item-danger'
|
||||
}).html(' Delete map').prepend(
|
||||
$('<i>',{
|
||||
class: 'fa fa-trash fa-fw'
|
||||
@@ -482,11 +465,13 @@ define([
|
||||
});
|
||||
|
||||
// main menus
|
||||
$('.' + config.headMenuClass).on('click', function() {
|
||||
$('.' + config.headMenuClass).on('click', function(e) {
|
||||
e.preventDefault();
|
||||
slideMenu.slidebars.toggle('left');
|
||||
});
|
||||
|
||||
$('.' + config.headMapClass).on('click', function() {
|
||||
$('.' + config.headMapClass).on('click', function(e) {
|
||||
e.preventDefault();
|
||||
slideMenu.slidebars.toggle('right');
|
||||
});
|
||||
|
||||
@@ -872,7 +857,7 @@ define([
|
||||
animateHeaderElement(userInfoElement, function(){
|
||||
if(currentCharacterChanged){
|
||||
userInfoElement.find('span').text( newCharacterName );
|
||||
userInfoElement.find('img').attr('src', Init.url.ccpImageServer + 'Character/' + newCharacterId + '_32.jpg' );
|
||||
userInfoElement.find('img').attr('src', Init.url.ccpImageServer + '/Character/' + newCharacterId + '_32.jpg' );
|
||||
}
|
||||
// init "character switch" popover
|
||||
userInfoElement.initCharacterSwitchPopover(userData);
|
||||
@@ -894,7 +879,7 @@ define([
|
||||
// toggle element
|
||||
animateHeaderElement(userShipElement, function(){
|
||||
userShipElement.find('span').text( newShipName );
|
||||
userShipElement.find('img').attr('src', Init.url.ccpImageServer + 'Render/' + newShipId + '_32.png' );
|
||||
userShipElement.find('img').attr('src', Init.url.ccpImageServer + '/Render/' + newShipId + '_32.png' );
|
||||
}, showShipElement);
|
||||
|
||||
// set new id for next check
|
||||
@@ -1104,10 +1089,12 @@ define([
|
||||
if( statusElement.data('status') !== status ){
|
||||
// status has changed
|
||||
if(! programStatusInterval){
|
||||
// check if timer exists if not -> set default (in case of the "init" ajax call failed
|
||||
let programStatusVisible = Init.timer ? Init.timer.PROGRAM_STATUS_VISIBLE : 5000;
|
||||
|
||||
let timer = function(){
|
||||
// change status on first timer iteration
|
||||
if(programStatusCounter === Init.timer.PROGRAM_STATUS_VISIBLE){
|
||||
if(programStatusCounter === programStatusVisible){
|
||||
|
||||
statusElement.velocity('stop').velocity('fadeOut', {
|
||||
duration: Init.animationSpeed.headerLink,
|
||||
@@ -1134,8 +1121,8 @@ define([
|
||||
}
|
||||
};
|
||||
|
||||
if(! programStatusInterval){
|
||||
programStatusCounter = Init.timer.PROGRAM_STATUS_VISIBLE;
|
||||
if(!programStatusInterval){
|
||||
programStatusCounter = programStatusVisible;
|
||||
programStatusInterval = setInterval(timer, 1000);
|
||||
}
|
||||
}
|
||||
|
||||
130
js/app/render.js
130
js/app/render.js
@@ -11,7 +11,7 @@ define(['jquery', 'mustache'], function($, Mustache) {
|
||||
* @param functionName
|
||||
* @param config
|
||||
*/
|
||||
var initModule = function(functionName, config){
|
||||
let initModule = function(functionName, config){
|
||||
|
||||
if(
|
||||
typeof config.functions === 'object' &&
|
||||
@@ -26,7 +26,7 @@ define(['jquery', 'mustache'], function($, Mustache) {
|
||||
* @param config
|
||||
* @param data
|
||||
*/
|
||||
var showModule = function(config, data){
|
||||
let showModule = function(config, data){
|
||||
|
||||
// require module template
|
||||
requirejs(['text!templates/' + config.name + '.html'], function(template) {
|
||||
@@ -37,7 +37,7 @@ define(['jquery', 'mustache'], function($, Mustache) {
|
||||
$('#' + data.id).length === 0
|
||||
){
|
||||
|
||||
var content = Mustache.render(template, data);
|
||||
let content = Mustache.render(template, data);
|
||||
|
||||
// display module
|
||||
switch(config.link){
|
||||
@@ -62,8 +62,130 @@ define(['jquery', 'mustache'], function($, Mustache) {
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* convert JSON object into HTML highlighted string
|
||||
* @param obj
|
||||
*/
|
||||
let highlightJson = (obj) => {
|
||||
let multiplyString = (num, str) => {
|
||||
let sb = [];
|
||||
for (let i = 0; i < num; i++) {
|
||||
sb.push(str);
|
||||
}
|
||||
return sb.join('');
|
||||
};
|
||||
|
||||
let dateObj = new Date();
|
||||
let regexpObj = new RegExp();
|
||||
let tab = multiplyString(1, ' ');
|
||||
let isCollapsible = true;
|
||||
let quoteKeys = false;
|
||||
let expImageClicked = '(() => {let container=this.parentNode.nextSibling; container.style.display=container.style.display===\'none\'?\'inline\':\'none\'})();';
|
||||
|
||||
let checkForArray = function (obj) {
|
||||
return obj &&
|
||||
typeof obj === 'object' &&
|
||||
typeof obj.length === 'number' &&
|
||||
!(obj.propertyIsEnumerable('length'));
|
||||
};
|
||||
|
||||
let getRow = function (indent, data, isPropertyContent) {
|
||||
let tabs = '';
|
||||
for (let i = 0; i < indent && !isPropertyContent; i++) tabs += tab;
|
||||
if (data !== null && data.length > 0 && data.charAt(data.length - 1) !== '\n')
|
||||
data = data + '\n';
|
||||
return tabs + data;
|
||||
};
|
||||
|
||||
let formatLiteral = function (literal, quote, comma, indent, isArray, style) {
|
||||
if (typeof literal === 'string')
|
||||
literal = literal.split('<').join('<').split('>').join('>');
|
||||
let str = '<span class="' + style + '">' + quote + literal + quote + comma + '</span>';
|
||||
if (isArray) str = getRow(indent, str);
|
||||
return str;
|
||||
};
|
||||
|
||||
let formatFunction = function (indent, obj) {
|
||||
let tabs = '';
|
||||
for (let i = 0; i < indent; i++) tabs += tab;
|
||||
let funcStrArray = obj.toString().split('\n');
|
||||
let str = '';
|
||||
for (let i = 0; i < funcStrArray.length; i++) {
|
||||
str += ((i === 0) ? '' : tabs) + funcStrArray[i] + '\n';
|
||||
}
|
||||
return str;
|
||||
};
|
||||
|
||||
|
||||
let highlight = (obj, indent, addComma, isArray, isPropertyContent) => {
|
||||
let html = '';
|
||||
|
||||
let comma = (addComma) ? '<span class="pf-code-Comma">,</span> ' : '';
|
||||
let type = typeof obj;
|
||||
let clpsHtml = '';
|
||||
if (checkForArray(obj)) {
|
||||
if (obj.length === 0) {
|
||||
html += getRow(indent, '<span class="pf-code-ArrayBrace">[ ]</span>' + comma, isPropertyContent);
|
||||
} else {
|
||||
clpsHtml = isCollapsible ? '<span><i class="fa fa-fw fa-plus-square" onClick="' + expImageClicked + '"></i></span><span class="collapsible">' : '';
|
||||
html += getRow(indent, '<span class="pf-code-ArrayBrace">[</span>' + clpsHtml, isPropertyContent);
|
||||
for (let i = 0; i < obj.length; i++) {
|
||||
html += highlight(obj[i], indent + 1, i < (obj.length - 1), true, false);
|
||||
}
|
||||
clpsHtml = isCollapsible ? '</span>' : '';
|
||||
html += getRow(indent, clpsHtml + '<span class="pf-code-ArrayBrace">]</span>' + comma);
|
||||
}
|
||||
} else if (type === 'object') {
|
||||
if (obj === null) {
|
||||
html += formatLiteral('null', '', comma, indent, isArray, 'pf-code-Null');
|
||||
} else if (obj.constructor === dateObj.constructor) {
|
||||
html += formatLiteral('new Date(' + obj.getTime() + ') /*' + obj.toLocaleString() + '*/', '', comma, indent, isArray, 'Date');
|
||||
} else if (obj.constructor === regexpObj.constructor) {
|
||||
html += formatLiteral('new RegExp(' + obj + ')', '', comma, indent, isArray, 'RegExp');
|
||||
} else {
|
||||
let numProps = 0;
|
||||
for (let prop in obj) numProps++;
|
||||
if (numProps === 0) {
|
||||
html += getRow(indent, '<span class="pf-code-ObjectBrace">{ }</span>' + comma, isPropertyContent);
|
||||
} else {
|
||||
clpsHtml = isCollapsible ? '<span><i class="fa fa-fw fa-plus-square" onClick="' + expImageClicked + '"></i></span><span class="collapsible">' : '';
|
||||
html += getRow(indent, '<span class="pf-code-ObjectBrace">{</span>' + clpsHtml, isPropertyContent);
|
||||
let j = 0;
|
||||
for (let prop in obj) {
|
||||
if (obj.hasOwnProperty(prop)) {
|
||||
let quote = quoteKeys ? '"' : '';
|
||||
html += getRow(indent + 1, '<span class="pf-code-PropertyName">' + quote + prop + quote + '</span>: ' + highlight(obj[prop], indent + 1, ++j < numProps, false, true));
|
||||
}
|
||||
}
|
||||
clpsHtml = isCollapsible ? '</span>' : '';
|
||||
html += getRow(indent, clpsHtml + '<span class="pf-code-ObjectBrace">}</span>' + comma);
|
||||
}
|
||||
}
|
||||
} else if (type === 'number') {
|
||||
html += formatLiteral(obj, '', comma, indent, isArray, 'pf-code-Number');
|
||||
} else if (type === 'boolean') {
|
||||
html += formatLiteral(obj, '', comma, indent, isArray, 'pf-code-Boolean');
|
||||
} else if (type === 'function') {
|
||||
if (obj.constructor === regexpObj.constructor) {
|
||||
html += formatLiteral('new RegExp(' + obj + ')', '', comma, indent, isArray, 'RegExp');
|
||||
} else {
|
||||
obj = formatFunction(indent, obj);
|
||||
html += formatLiteral(obj, '', comma, indent, isArray, 'pf-code-Function');
|
||||
}
|
||||
} else if (type === 'undefined') {
|
||||
html += formatLiteral('undefined', '', comma, indent, isArray, 'pf-code-Null');
|
||||
} else {
|
||||
html += formatLiteral(obj.toString().split('\\').join('\\\\').split('"').join('\\"'), '"', comma, indent, isArray, 'pf-code-String');
|
||||
}
|
||||
|
||||
return html;
|
||||
};
|
||||
|
||||
return highlight(obj, 0, false, false, false);
|
||||
};
|
||||
|
||||
return {
|
||||
showModule: showModule
|
||||
showModule: showModule,
|
||||
highlightJson: highlightJson
|
||||
};
|
||||
});
|
||||
@@ -6,9 +6,10 @@ define([
|
||||
'jquery',
|
||||
'app/init',
|
||||
'app/util',
|
||||
'app/render',
|
||||
'bootbox',
|
||||
'app/map/util'
|
||||
], function($, Init, Util, bootbox, MapUtil) {
|
||||
], function($, Init, Util, Render, bootbox, MapUtil) {
|
||||
|
||||
'use strict';
|
||||
|
||||
@@ -19,23 +20,27 @@ define([
|
||||
// map info dialog/tabs
|
||||
dialogMapInfoSummaryId: 'pf-map-info-dialog-summary', // id for map "summary" container
|
||||
dialogMapInfoUsersId: 'pf-map-info-dialog-users', // id for map "user" container
|
||||
dialogMapInfoLogsId: 'pf-map-info-dialog-logs', // id for map "logs" container
|
||||
dialogMapInfoRefreshId: 'pf-map-info-dialog-refresh', // id for map "refresh" container
|
||||
|
||||
// "summary" container
|
||||
// dialog containers
|
||||
mapInfoId: 'pf-map-info', // id for map info
|
||||
mapInfoSystemsId: 'pf-map-info-systems', // id for map info systems box
|
||||
mapInfoConnectionsId: 'pf-map-info-connections', // id for map info connections box
|
||||
mapInfoUsersId: 'pf-map-info-users', // id for map info users box
|
||||
mapInfoLogsId: 'pf-map-info-logs', // id for map info logs box
|
||||
|
||||
mapInfoTableClass: 'pf-map-info-table', // class for data
|
||||
mapInfoLifetimeCounterClass: 'pf-map-info-lifetime-counter', // class for map lifetime counter
|
||||
|
||||
// dataTable
|
||||
tableImageCellClass: 'pf-table-image-cell', // class for table "image" cells
|
||||
tableImageSmallCellClass: 'pf-table-image-small-cell', // class for table "small image" cells
|
||||
tableActionCellClass: 'pf-table-action-cell', // class for table "action" cells
|
||||
tableCounterCellClass: 'pf-table-counter-cell', // class for table "counter" cells
|
||||
tableActionCellIconClass: 'pf-table-action-icon-cell', // class for table "action" icon (icon is part of cell content)
|
||||
tableToolsClass: 'pf-table-tools', // class for table "tools" section (e.g. Buttons)
|
||||
tableCellImageClass: 'pf-table-image-cell', // class for table "image" cells
|
||||
tableCellImageSmallClass: 'pf-table-image-small-cell', // class for table "small image" cells
|
||||
tableCellActionClass: 'pf-table-action-cell', // class for table "action" cells
|
||||
tableCellLinkClass: 'pf-table-link-cell', // class for table "links" cells
|
||||
tableCellCounterClass: 'pf-table-counter-cell', // class for table "counter" cells
|
||||
tableCellEllipsisClass: 'pf-table-cell-ellipses-auto', // class for table "ellipsis" cells
|
||||
tableCellActionIconClass: 'pf-table-action-icon-cell', // class for table "action" icon (icon is part of cell content)
|
||||
|
||||
loadingOptions: { // config for loading overlay
|
||||
icon: {
|
||||
@@ -56,16 +61,26 @@ define([
|
||||
btnOkIcon: 'fa fa-fw fa-close'
|
||||
};
|
||||
|
||||
/**
|
||||
* get image that marks a table cell as clickable
|
||||
* @returns {string}
|
||||
*/
|
||||
let getIconForInformationWindow = () => {
|
||||
return '<i class="fa fa-fw fa-id-card ' + config.tableCellActionIconClass + '" title="open ingame" data-toggle="tooltip"></i>';
|
||||
};
|
||||
|
||||
/**
|
||||
* loads the map info data into an element
|
||||
* @param mapData
|
||||
*/
|
||||
$.fn.loadMapInfoData = function(mapData){
|
||||
let mapElement = $(this);
|
||||
$.fn.initMapInfoData = function(mapData){
|
||||
let mapElement = $(this).empty();
|
||||
|
||||
mapElement.empty();
|
||||
mapElement.showLoadingAnimation(config.loadingOptions);
|
||||
|
||||
// get some more config values from this map. Which are not part of "mapData"
|
||||
let mapDataOrigin = Util.getCurrentMapData(mapData.config.id);
|
||||
|
||||
let countSystems = mapData.data.systems.length;
|
||||
let countConnections = mapData.data.connections.length;
|
||||
|
||||
@@ -80,11 +95,11 @@ define([
|
||||
}
|
||||
}
|
||||
|
||||
// check max map limits (e.g. max systems per map) ============================================================
|
||||
// check max map limits (e.g. max systems per map) ------------------------------------------------------------
|
||||
let percentageSystems = (100 / mapType.defaultConfig.max_systems) * countSystems;
|
||||
let maxSystemsClass = (percentageSystems < 90) ? 'txt-color-success' : (percentageSystems < 100) ? 'txt-color-warning' : 'txt-color-danger';
|
||||
|
||||
// build content ==============================================================================================
|
||||
// build content ----------------------------------------------------------------------------------------------
|
||||
|
||||
let dlElementLeft = $('<dl>', {
|
||||
class: 'dl-horizontal',
|
||||
@@ -115,13 +130,6 @@ define([
|
||||
class: 'dl-horizontal',
|
||||
css: {'float': 'right'}
|
||||
}).append(
|
||||
$('<dt>').text( 'Lifetime' )
|
||||
).append(
|
||||
$('<dd>', {
|
||||
class: config.mapInfoLifetimeCounterClass,
|
||||
text: mapData.config.created
|
||||
})
|
||||
).append(
|
||||
$('<dt>').text( 'Systems' )
|
||||
).append(
|
||||
$('<dd>', {
|
||||
@@ -131,6 +139,17 @@ define([
|
||||
$('<dt>').text( 'Connections' )
|
||||
).append(
|
||||
$('<dd>').text( countConnections )
|
||||
).append(
|
||||
$('<dt>').text( 'Lifetime' )
|
||||
).append(
|
||||
$('<dd>', {
|
||||
class: config.mapInfoLifetimeCounterClass,
|
||||
text: mapData.config.created
|
||||
})
|
||||
).append(
|
||||
$('<dt>').text( 'Created' )
|
||||
).append(
|
||||
$('<dd>').text( mapDataOrigin.config.created.character.name)
|
||||
);
|
||||
|
||||
mapElement.append(dlElementRight);
|
||||
@@ -145,14 +164,11 @@ define([
|
||||
* loads system info table into an element
|
||||
* @param mapData
|
||||
*/
|
||||
$.fn.loadSystemInfoTable = function(mapData){
|
||||
|
||||
let systemsElement = $(this);
|
||||
|
||||
systemsElement.empty();
|
||||
$.fn.initSystemInfoTable = function(mapData){
|
||||
let systemsElement = $(this).empty();
|
||||
|
||||
let systemTable = $('<table>', {
|
||||
class: ['compact', 'stripe', 'order-column', 'row-border', config.mapInfoTableClass].join(' ')
|
||||
class: ['compact', 'stripe', 'order-column', 'row-border'].join(' ')
|
||||
});
|
||||
systemsElement.append(systemTable);
|
||||
|
||||
@@ -292,7 +308,7 @@ define([
|
||||
systemsData.push(tempData);
|
||||
}
|
||||
|
||||
let systemsDataTable = systemTable.dataTable( {
|
||||
let systemsDataTable = systemTable.DataTable( {
|
||||
pageLength: 20,
|
||||
paging: true,
|
||||
lengthMenu: [[5, 10, 20, 50, -1], [5, 10, 20, 50, 'All']],
|
||||
@@ -343,7 +359,14 @@ define([
|
||||
}
|
||||
},{
|
||||
title: 'system',
|
||||
data: 'name'
|
||||
data: 'name',
|
||||
className: [config.tableCellLinkClass].join(' '),
|
||||
createdCell: function(cell, cellData, rowData, rowIndex, colIndex){
|
||||
// select system
|
||||
$(cell).on('click', function(e){
|
||||
Util.getMapModule().getActiveMap().triggerMenuEvent('SelectSystem', {systemId: rowData.id });
|
||||
});
|
||||
}
|
||||
},{
|
||||
title: 'alias',
|
||||
data: 'alias'
|
||||
@@ -401,7 +424,7 @@ define([
|
||||
title: 'updated',
|
||||
width: '80px',
|
||||
searchable: false,
|
||||
className: ['text-right', config.tableCounterCellClass, 'min-desktop'].join(' '),
|
||||
className: ['text-right', config.tableCellCounterClass, 'min-desktop'].join(' '),
|
||||
data: 'updated',
|
||||
createdCell: function(cell, cellData, rowData, rowIndex, colIndex){
|
||||
$(cell).initTimestampCounter();
|
||||
@@ -418,7 +441,7 @@ define([
|
||||
orderable: false,
|
||||
searchable: false,
|
||||
width: '10px',
|
||||
className: ['text-center', config.tableActionCellClass].join(' '),
|
||||
className: ['text-center', config.tableCellActionClass].join(' '),
|
||||
data: 'clear',
|
||||
createdCell: function(cell, cellData, rowData, rowIndex, colIndex) {
|
||||
let tempTableElement = this;
|
||||
@@ -444,11 +467,11 @@ define([
|
||||
|
||||
Util.showNotify({title: 'System deleted', text: rowData.name, type: 'success'});
|
||||
|
||||
// refresh connection table (connections might have changed) ==================
|
||||
// refresh connection table (connections might have changed) --------------
|
||||
let connectionsElement = $('#' + config.mapInfoConnectionsId);
|
||||
let mapDataNew = activeMap.getMapDataFromClient({forceData: true});
|
||||
|
||||
connectionsElement.loadConnectionInfoTable(mapDataNew);
|
||||
connectionsElement.initConnectionInfoTable(mapDataNew);
|
||||
}else{
|
||||
// error
|
||||
Util.showNotify({title: 'Failed to delete system', text: rowData.name, type: 'error'});
|
||||
@@ -472,13 +495,11 @@ define([
|
||||
* loads connection info table into an element
|
||||
* @param mapData
|
||||
*/
|
||||
$.fn.loadConnectionInfoTable = function(mapData){
|
||||
let connectionsElement = $(this);
|
||||
|
||||
connectionsElement.empty();
|
||||
$.fn.initConnectionInfoTable = function(mapData){
|
||||
let connectionsElement = $(this).empty();
|
||||
|
||||
let connectionTable = $('<table>', {
|
||||
class: ['compact', 'stripe', 'order-column', 'row-border', config.mapInfoTableClass].join(' ')
|
||||
class: ['compact', 'stripe', 'order-column', 'row-border'].join(' ')
|
||||
});
|
||||
connectionsElement.append(connectionTable);
|
||||
|
||||
@@ -489,7 +510,7 @@ define([
|
||||
connectionsElement.hideLoadingAnimation();
|
||||
});
|
||||
|
||||
// connections table ==========================================================================================
|
||||
// connections table ------------------------------------------------------------------------------------------
|
||||
|
||||
// prepare data for dataTables
|
||||
let connectionData = [];
|
||||
@@ -505,8 +526,10 @@ define([
|
||||
scope_sort: tempConnectionData.scope
|
||||
};
|
||||
|
||||
// source system name
|
||||
tempConData.source = tempConnectionData.sourceName;
|
||||
tempConData.source = {
|
||||
id: tempConnectionData.source,
|
||||
name: tempConnectionData.sourceName,
|
||||
};
|
||||
|
||||
// connection
|
||||
let connectionClasses = [];
|
||||
@@ -519,8 +542,10 @@ define([
|
||||
|
||||
tempConData.connection = '<div class="pf-fake-connection ' + connectionClasses + '"></div>';
|
||||
|
||||
|
||||
tempConData.target = tempConnectionData.targetName;
|
||||
tempConData.target = {
|
||||
id: tempConnectionData.target,
|
||||
name: tempConnectionData.targetName,
|
||||
};
|
||||
|
||||
tempConData.updated = tempConnectionData.updated;
|
||||
|
||||
@@ -557,7 +582,14 @@ define([
|
||||
}
|
||||
},{
|
||||
title: 'source system',
|
||||
data: 'source'
|
||||
data: 'source.name',
|
||||
className: [config.tableCellLinkClass].join(' '),
|
||||
createdCell: function(cell, cellData, rowData, rowIndex, colIndex){
|
||||
// select system
|
||||
$(cell).on('click', function(e){
|
||||
Util.getMapModule().getActiveMap().triggerMenuEvent('SelectSystem', {systemId: rowData.source.id });
|
||||
});
|
||||
}
|
||||
},{
|
||||
title: 'connection',
|
||||
width: '80px',
|
||||
@@ -567,12 +599,19 @@ define([
|
||||
data: 'connection'
|
||||
}, {
|
||||
title: 'target system',
|
||||
data: 'target'
|
||||
data: 'target.name',
|
||||
className: [config.tableCellLinkClass].join(' '),
|
||||
createdCell: function(cell, cellData, rowData, rowIndex, colIndex){
|
||||
// select system
|
||||
$(cell).on('click', function(e){
|
||||
Util.getMapModule().getActiveMap().triggerMenuEvent('SelectSystem', {systemId: rowData.target.id });
|
||||
});
|
||||
}
|
||||
},{
|
||||
title: 'updated',
|
||||
width: '80px',
|
||||
searchable: false,
|
||||
className: ['text-right', config.tableCounterCellClass].join(' '),
|
||||
className: ['text-right', config.tableCellCounterClass].join(' '),
|
||||
data: 'updated',
|
||||
createdCell: function(cell, cellData, rowData, rowIndex, colIndex){
|
||||
$(cell).initTimestampCounter();
|
||||
@@ -589,7 +628,7 @@ define([
|
||||
orderable: false,
|
||||
searchable: false,
|
||||
width: '10px',
|
||||
className: ['text-center', config.tableActionCellClass].join(' '),
|
||||
className: ['text-center', config.tableCellActionClass].join(' '),
|
||||
data: 'clear',
|
||||
createdCell: function(cell, cellData, rowData, rowIndex, colIndex) {
|
||||
let tempTableElement = this;
|
||||
@@ -621,13 +660,11 @@ define([
|
||||
* loads user info table into an element
|
||||
* @param mapData
|
||||
*/
|
||||
$.fn.loadUsersInfoTable = function(mapData){
|
||||
let usersElement = $(this);
|
||||
|
||||
usersElement.empty();
|
||||
$.fn.initUsersInfoTable = function(mapData){
|
||||
let usersElement = $(this).empty();
|
||||
|
||||
let userTable = $('<table>', {
|
||||
class: ['compact', 'stripe', 'order-column', 'row-border', config.mapInfoTableClass].join(' ')
|
||||
class: ['compact', 'stripe', 'order-column', 'row-border'].join(' ')
|
||||
});
|
||||
usersElement.append(userTable);
|
||||
|
||||
@@ -644,11 +681,7 @@ define([
|
||||
});
|
||||
});
|
||||
|
||||
let getIconForInformationWindow = () => {
|
||||
return '<i class="fa fa-fw fa-id-card ' + config.tableActionCellIconClass + '" title="open ingame" data-toggle="tooltip"></i>';
|
||||
};
|
||||
|
||||
// users table ================================================================================================
|
||||
// users table ------------------------------------------------------------------------------------------------
|
||||
// prepare users data for dataTables
|
||||
let currentMapUserData = Util.getCurrentMapUserData( mapData.config.id );
|
||||
let usersData = [];
|
||||
@@ -688,13 +721,13 @@ define([
|
||||
width: '26px',
|
||||
orderable: false,
|
||||
searchable: false,
|
||||
className: ['pf-help-default', 'text-center', config.tableImageCellClass].join(' '),
|
||||
className: ['pf-help-default', 'text-center', config.tableCellImageClass].join(' '),
|
||||
data: 'log.ship',
|
||||
render: {
|
||||
_: function(data, type, row, meta){
|
||||
let value = data;
|
||||
if(type === 'display'){
|
||||
value = '<img src="' + Init.url.ccpImageServer + 'Render/' + value.typeId + '_32.png" title="' + value.typeName + '" data-toggle="tooltip" />';
|
||||
value = '<img src="' + Init.url.ccpImageServer + '/Render/' + value.typeId + '_32.png" title="' + value.typeName + '" data-toggle="tooltip" />';
|
||||
}
|
||||
return value;
|
||||
}
|
||||
@@ -721,13 +754,13 @@ define([
|
||||
width: '26px',
|
||||
orderable: false,
|
||||
searchable: false,
|
||||
className: [config.tableImageCellClass].join(' '),
|
||||
className: [config.tableCellImageClass].join(' '),
|
||||
data: 'id',
|
||||
render: {
|
||||
_: function(data, type, row, meta){
|
||||
let value = data;
|
||||
if(type === 'display'){
|
||||
value = '<img src="' + Init.url.ccpImageServer + 'Character/' + value + '_32.jpg" />';
|
||||
value = '<img src="' + Init.url.ccpImageServer + '/Character/' + value + '_32.jpg" />';
|
||||
}
|
||||
return value;
|
||||
}
|
||||
@@ -737,7 +770,7 @@ define([
|
||||
title: 'pilot',
|
||||
orderable: true,
|
||||
searchable: true,
|
||||
className: [config.tableActionCellClass].join(' '),
|
||||
className: [config.tableCellActionClass].join(' '),
|
||||
data: 'name',
|
||||
render: {
|
||||
_: function(data, type, row, meta){
|
||||
@@ -761,13 +794,13 @@ define([
|
||||
width: '26px',
|
||||
orderable: false,
|
||||
searchable: false,
|
||||
className: [config.tableImageCellClass, config.tableImageSmallCellClass].join(' '),
|
||||
className: [config.tableCellImageClass, config.tableCellImageSmallClass].join(' '),
|
||||
data: 'corporation',
|
||||
render: {
|
||||
_: function(data, type, row, meta){
|
||||
let value = data;
|
||||
if(type === 'display'){
|
||||
value = '<img src="' + Init.url.ccpImageServer + 'Corporation/' + value.id + '_32.png" />';
|
||||
value = '<img src="' + Init.url.ccpImageServer + '/Corporation/' + value.id + '_32.png" />';
|
||||
}
|
||||
return value;
|
||||
}
|
||||
@@ -777,7 +810,7 @@ define([
|
||||
title: 'corporation',
|
||||
orderable: true,
|
||||
searchable: true,
|
||||
className: [config.tableActionCellClass].join(' '),
|
||||
className: [config.tableCellActionClass].join(' '),
|
||||
data: 'corporation',
|
||||
render: {
|
||||
_: function (data, type, row, meta) {
|
||||
@@ -821,6 +854,316 @@ define([
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* loads logs table into an element
|
||||
* @param mapData
|
||||
*/
|
||||
$.fn.initLogsInfoTable = function(mapData){
|
||||
let logsElement = $(this).empty();
|
||||
|
||||
/**
|
||||
* ajax load function for log fdata
|
||||
* @param requestData
|
||||
* @param context
|
||||
*/
|
||||
let getLogsData = (requestData, context) => {
|
||||
context.logsElement.showLoadingAnimation(config.loadingOptions);
|
||||
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: Init.path.getMapLogData,
|
||||
data: requestData,
|
||||
dataType: 'json',
|
||||
context: context
|
||||
}).done(function(data){
|
||||
this.callback(data, context);
|
||||
}).fail(function( jqXHR, status, error) {
|
||||
let reason = status + ' ' + error;
|
||||
Util.showNotify({title: jqXHR.status + ': loadLogs', text: reason, type: 'warning'});
|
||||
}).always(function(){
|
||||
this.logsElement.hideLoadingAnimation();
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* callback function after ajax response with log data
|
||||
* @param responseData
|
||||
* @param context
|
||||
*/
|
||||
let updateTableDataCallback = (responseData, context) => {
|
||||
let newLogCount = responseData.data.length;
|
||||
|
||||
if(newLogCount > 0){
|
||||
let pageInfoOld = context.tableApi.page.info();
|
||||
|
||||
// add new rows
|
||||
context.tableApi.rows.add(responseData.data).draw();
|
||||
|
||||
let newPageIndex = 0;
|
||||
if(pageInfoOld.recordsDisplay === 0){
|
||||
Util.showNotify({title: 'New logs loaded', text: newLogCount + ' most recent logs added', type: 'success'});
|
||||
}else{
|
||||
// get new pageInfo (new max page count)
|
||||
let pageInfoNew = context.tableApi.page.info();
|
||||
newPageIndex = Math.max(0, pageInfoNew.pages - 1);
|
||||
Util.showNotify({title: 'More logs loaded', text: newLogCount + ' older logs added', type: 'info'});
|
||||
}
|
||||
|
||||
// get to last page (pageIndex starts at zero) -> check if last page > 0
|
||||
context.tableApi.page(newPageIndex).draw(false);
|
||||
}else{
|
||||
Util.showNotify({title: 'No logs found', text: 'No more entries', type: 'danger'});
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
// init logs table --------------------------------------------------------------------------------------------
|
||||
let logTable = $('<table>', {
|
||||
class: ['compact', 'stripe', 'order-column', 'row-border', 'pf-table-fixed'].join(' ')
|
||||
});
|
||||
logsElement.append(logTable);
|
||||
|
||||
let serverDate = Util.getServerTime();
|
||||
let serverHours = serverDate.setHours(0,0,0,0);
|
||||
|
||||
let logDataTable = logTable.DataTable({
|
||||
pageLength: 25,
|
||||
paging: true,
|
||||
lengthMenu: [[10, 25, 50, 100], [10, 25, 50, 100]],
|
||||
pagingType: 'full_numbers',
|
||||
ordering: false,
|
||||
autoWidth: false,
|
||||
searching: true,
|
||||
hover: false,
|
||||
data: [],
|
||||
language: {
|
||||
emptyTable: 'No logs available',
|
||||
zeroRecords: 'No logs found',
|
||||
lengthMenu: 'Show _MENU_ rows',
|
||||
info: 'Showing _START_ to _END_ of _TOTAL_ rows'
|
||||
},
|
||||
columnDefs: [
|
||||
{
|
||||
targets: 0,
|
||||
title: '<span title="action" data-toggle="tooltip"> </span>',
|
||||
width: 12,
|
||||
data: 'context.tag',
|
||||
render: {
|
||||
_: function(data, type, row, meta){
|
||||
let value = data;
|
||||
if(type === 'display'){
|
||||
let className = 'txt-color-' + data;
|
||||
value = '<i class="fa fa-circle fa-fw txt-color ' + className + '"></i>';
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
},{
|
||||
targets: 1,
|
||||
name: 'timestamp',
|
||||
title: '<i class="fa fa-lg fa-fw fa-clock-o"></i>',
|
||||
width: 100,
|
||||
className: ['text-right'].join(' '),
|
||||
data: 'datetime.date',
|
||||
render: {
|
||||
_: function(data, type, row, meta){
|
||||
// strip microseconds
|
||||
let logDateString = data.substring(0, 19) ;
|
||||
let logDate = new Date(logDateString.replace(/-/g, '/'));
|
||||
data = Util.convertDateToString(logDate, true);
|
||||
|
||||
// check whether log is new (today) ->
|
||||
if(logDate.setHours(0,0,0,0) === serverHours) {
|
||||
// replace dd/mm/YYYY
|
||||
data = 'today' + data.substring(10);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
}
|
||||
},{
|
||||
targets: 2,
|
||||
title: 'level',
|
||||
width: 40,
|
||||
data: 'level_name'
|
||||
},{
|
||||
targets: 3,
|
||||
title: 'channel',
|
||||
className: [config.tableCellEllipsisClass].join(' '),
|
||||
width: 40,
|
||||
data: 'channel'
|
||||
},{
|
||||
targets: 4,
|
||||
title: 'message',
|
||||
width: 115,
|
||||
data: 'message',
|
||||
render: {
|
||||
_: function(data, type, row, meta){
|
||||
let value = data;
|
||||
if(type === 'display'){
|
||||
let className = 'txt-color-';
|
||||
if(Util.getObjVal(row, 'context.tag')){
|
||||
className += row.context.tag;
|
||||
}
|
||||
value = '<span class="txt-color ' + className + '">' + value + '</span>';
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
},{
|
||||
targets: 5,
|
||||
title: '',
|
||||
width: 26,
|
||||
searchable: false,
|
||||
className: [config.tableCellImageClass].join(' '),
|
||||
data: 'context.data.character.id' ,
|
||||
render: {
|
||||
_: function(data, type, row, meta){
|
||||
let value = data;
|
||||
if(type === 'display'){
|
||||
value = '<img src="' + Init.url.ccpImageServer + '/Character/' + value + '_32.jpg" />';
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
},{
|
||||
targets: 6,
|
||||
title: 'pilot',
|
||||
width: 110,
|
||||
className: [config.tableCellActionClass].join(' '),
|
||||
data: 'context.data.character.name',
|
||||
render: {
|
||||
_: function(data, type, row, meta){
|
||||
let value = data;
|
||||
if(type === 'display'){
|
||||
value += ' ' + getIconForInformationWindow();
|
||||
}
|
||||
return value;
|
||||
}
|
||||
},
|
||||
createdCell: function(cell, cellData, rowData, rowIndex, colIndex){
|
||||
// open character information window (ingame)
|
||||
$(cell).on('click', { tableApi: this.DataTable() }, function(e) {
|
||||
let rowData = e.data.tableApi.row(this).data();
|
||||
Util.openIngameWindow(rowData.context.data.character.id);
|
||||
});
|
||||
}
|
||||
},{
|
||||
targets: 7,
|
||||
title: 'context',
|
||||
className: [config.tableCellEllipsisClass].join(' '),
|
||||
data: 'context.data.formatted'
|
||||
},{
|
||||
targets: 8,
|
||||
title: '<i class="fa fa-lg fa-code text-right"></i>',
|
||||
width: 12,
|
||||
className: [config.tableCellActionClass].join(' '),
|
||||
data: 'context.data',
|
||||
render: {
|
||||
_: function(data, type, row, meta){
|
||||
let value = data;
|
||||
if(type === 'display'){
|
||||
// txt-color-redDarker
|
||||
value = '<i class="fa fa-code ' + config.tableCellActionIconClass + '"></i>';
|
||||
}
|
||||
return value;
|
||||
}
|
||||
},
|
||||
createdCell: function(cell, cellData, rowData, rowIndex, colIndex){
|
||||
// unset formatted string (to much content)
|
||||
|
||||
if(cellData.formatted){
|
||||
// clone data before delete() values
|
||||
cellData = Object.assign({}, cellData);
|
||||
delete(cellData.formatted);
|
||||
}
|
||||
|
||||
let jsonHighlighted = Render.highlightJson(cellData);
|
||||
let content = '<pre><code>' + jsonHighlighted + '</code></pre>';
|
||||
|
||||
// open popover with raw log data
|
||||
$(cell).popover({
|
||||
placement: 'left',
|
||||
html: true,
|
||||
trigger: 'hover',
|
||||
content: content,
|
||||
container: 'body',
|
||||
title: 'Raw data',
|
||||
delay: {
|
||||
show: 180,
|
||||
hide: 0
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
],
|
||||
initComplete: function(settings){
|
||||
let tableApi = this.api();
|
||||
|
||||
// empty table is ready -> load logs
|
||||
getLogsData({
|
||||
mapId: mapData.config.id
|
||||
}, {
|
||||
tableApi: tableApi,
|
||||
callback: updateTableDataCallback,
|
||||
logsElement: logsElement
|
||||
});
|
||||
},
|
||||
drawCallback: function(settings){
|
||||
let tableApi = this.api();
|
||||
|
||||
// en/disable "load more" button ----------------------------------------------------------------------
|
||||
let tableInfo = tableApi.page.info();
|
||||
let isLastPage = (tableInfo.pages === 0 || tableInfo.page === tableInfo.pages - 1);
|
||||
tableApi.button(0).enable(isLastPage);
|
||||
|
||||
// adjust "timestamp" column width --------------------------------------------------------------------
|
||||
let timestampColumn = tableApi.column('timestamp:name').header();
|
||||
let timestampColumnCells = tableApi.cells(undefined, 'timestamp:name', {page: 'current', order:'current'});
|
||||
|
||||
let hasOldLogs = timestampColumnCells.render( 'display' ).reduce((hasOldLogs, cellValue) => {
|
||||
return (hasOldLogs === false && !cellValue.startsWith('today')) ? true : hasOldLogs;
|
||||
}, false);
|
||||
|
||||
if(hasOldLogs){
|
||||
$(timestampColumn).css({width: '100px'});
|
||||
}else{
|
||||
$(timestampColumn).css({width: '80px'});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// ------------------------------------------------------------------------------------------------------------
|
||||
// add dataTable buttons (extension)
|
||||
logsElement.append($('<div>', {
|
||||
class: config.tableToolsClass
|
||||
}));
|
||||
|
||||
let buttons = new $.fn.dataTable.Buttons( logDataTable, {
|
||||
buttons: [
|
||||
{
|
||||
className: 'btn btn-sm btn-default',
|
||||
text: '<i class="fa fa-fw fa-plus"></i> load more',
|
||||
enabled: false,
|
||||
action: function ( e, dt, node, config ) {
|
||||
let pageInfo = dt.page.info();
|
||||
|
||||
getLogsData({
|
||||
mapId: mapData.config.id,
|
||||
limit: pageInfo.length,
|
||||
offset: pageInfo.recordsTotal
|
||||
}, {
|
||||
tableApi: dt,
|
||||
callback: updateTableDataCallback,
|
||||
logsElement: logsElement
|
||||
});
|
||||
}
|
||||
}
|
||||
]
|
||||
} );
|
||||
|
||||
logDataTable.buttons().container().appendTo( $(this).find('.' + config.tableToolsClass));
|
||||
};
|
||||
|
||||
/**
|
||||
* shows the map information modal dialog
|
||||
* @param options
|
||||
@@ -830,21 +1173,29 @@ define([
|
||||
let mapData = activeMap.getMapDataFromClient({forceData: true});
|
||||
|
||||
if(mapData !== false){
|
||||
requirejs(['text!templates/dialog/map_info.html', 'mustache'], function(template, Mustache) {
|
||||
// "log" tab -> get "Origin", not all config options are set in mapData
|
||||
let mapDataOrigin = Util.getCurrentMapData(mapData.config.id);
|
||||
|
||||
requirejs(['text!templates/dialog/map_info.html', 'mustache', 'datatables.loader'], (template, Mustache) => {
|
||||
|
||||
let data = {
|
||||
dialogSummaryContainerId: config.dialogMapInfoSummaryId,
|
||||
dialogUsersContainerId: config.dialogMapInfoUsersId,
|
||||
dialogLogsContainerId: config.dialogMapInfoLogsId,
|
||||
dialogRefreshContainerId: config.dialogMapInfoRefreshId,
|
||||
dialogNavigationClass: config.dialogNavigationClass,
|
||||
mapInfoId: config.mapInfoId,
|
||||
mapInfoSystemsId: config.mapInfoSystemsId,
|
||||
mapInfoConnectionsId: config.mapInfoConnectionsId,
|
||||
mapInfoUsersId: config.mapInfoUsersId,
|
||||
mapInfoLogsId: config.mapInfoLogsId,
|
||||
|
||||
logHistoryEnabled: Boolean(Util.getObjVal(mapDataOrigin, 'config.logging.history')),
|
||||
|
||||
// default open tab ----------
|
||||
openTabInformation: options.tab === 'information',
|
||||
openTabActivity: options.tab === 'activity'
|
||||
openTabActivity: options.tab === 'activity',
|
||||
openTabLog: options.tab === 'log'
|
||||
};
|
||||
|
||||
let content = Mustache.render(template, data);
|
||||
@@ -865,39 +1216,51 @@ define([
|
||||
});
|
||||
|
||||
mapInfoDialog.on('shown.bs.modal', function(e) {
|
||||
// modal on open
|
||||
|
||||
let mapElement = $('#' + config.mapInfoId);
|
||||
let systemsElement = $('#' + config.mapInfoSystemsId);
|
||||
let connectionsElement = $('#' + config.mapInfoConnectionsId);
|
||||
let usersElement = $('#' + config.mapInfoUsersId);
|
||||
|
||||
|
||||
// set refresh button observer
|
||||
$('#' + config.dialogMapInfoRefreshId).on('click', function(){
|
||||
$('#' + config.dialogMapInfoRefreshId).on('click', function(e){
|
||||
let menuAction = $(this).attr('data-action');
|
||||
if(menuAction === 'refresh'){
|
||||
// get new map data
|
||||
let mapData = activeMap.getMapDataFromClient({forceData: true});
|
||||
// find active tab
|
||||
let activeTabLink = $(this).parents('.navbar').find('.navbar-header.pull-left li.active a');
|
||||
if(activeTabLink.attr('href') === '#' + config.dialogMapInfoLogsId){
|
||||
$('#' + config.mapInfoLogsId).initLogsInfoTable(mapDataOrigin);
|
||||
}
|
||||
|
||||
mapElement.loadMapInfoData(mapData);
|
||||
systemsElement.loadSystemInfoTable(mapData);
|
||||
connectionsElement.loadConnectionInfoTable(mapData);
|
||||
usersElement.loadUsersInfoTable(mapData);
|
||||
mapElement.initMapInfoData(mapData);
|
||||
systemsElement.initSystemInfoTable(mapData);
|
||||
connectionsElement.initConnectionInfoTable(mapData);
|
||||
usersElement.initUsersInfoTable(mapData);
|
||||
}
|
||||
});
|
||||
|
||||
// load map data
|
||||
mapElement.loadMapInfoData(mapData);
|
||||
mapElement.initMapInfoData(mapData);
|
||||
|
||||
// load system table
|
||||
systemsElement.loadSystemInfoTable(mapData);
|
||||
systemsElement.initSystemInfoTable(mapData);
|
||||
|
||||
// load connection table
|
||||
connectionsElement.loadConnectionInfoTable(mapData);
|
||||
connectionsElement.initConnectionInfoTable(mapData);
|
||||
|
||||
// load users table
|
||||
usersElement.loadUsersInfoTable(mapData);
|
||||
usersElement.initUsersInfoTable(mapData);
|
||||
|
||||
});
|
||||
|
||||
// events for tab change
|
||||
mapInfoDialog.find('.navbar a').on('shown.bs.tab', function(e){
|
||||
if($(e.target).attr('href') === '#' + config.dialogMapInfoLogsId){
|
||||
// "log" tab
|
||||
let mapDataOrigin = Util.getCurrentMapData(mapData.config.id);
|
||||
$('#' + config.mapInfoLogsId).initLogsInfoTable(mapDataOrigin);
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@@ -24,6 +24,15 @@ define([
|
||||
deleteEolConnectionsId: 'pf-map-dialog-delete-connections-eol', // id for "deleteEOLConnections" checkbox
|
||||
persistentAliasesId: 'pf-map-dialog-persistent-aliases', // id for "persistentAliases" checkbox
|
||||
|
||||
logHistoryId: 'pf-map-dialog-history', // id for "history logging" checkbox
|
||||
logActivityId: 'pf-map-dialog-activity', // id for "activity" checkbox
|
||||
|
||||
slackWebHookURLId: 'pf-map-dialog-slack-url', // id for Slack "webHookUrl"
|
||||
slackUsernameId: 'pf-map-dialog-slack-username', // id for Slack "username"
|
||||
slackIconId: 'pf-map-dialog-slack-icon', // id for Slack "icon"
|
||||
slackChannelHistoryId: 'pf-map-dialog-slack-channel-history', // id for Slack channel "history"
|
||||
slackChannelRallyId: 'pf-map-dialog-slack-channel-rally', // id for Slack channel "rally"
|
||||
|
||||
characterSelectId: 'pf-map-dialog-character-select', // id for "character" select
|
||||
corporationSelectId: 'pf-map-dialog-corporation-select', // id for "corporation" select
|
||||
allianceSelectId: 'pf-map-dialog-alliance-select', // id for "alliance" select
|
||||
@@ -67,7 +76,7 @@ define([
|
||||
|
||||
requirejs([
|
||||
'text!templates/dialog/map.html',
|
||||
'text!templates/form/map_settings.html',
|
||||
'text!templates/form/map.html',
|
||||
'mustache'
|
||||
], function(templateMapDialog, templateMapSettings, Mustache) {
|
||||
|
||||
@@ -96,10 +105,10 @@ define([
|
||||
formInfoContainerClass: Util.config.formInfoContainerClass
|
||||
};
|
||||
|
||||
// render "new map" tab content -------------------------------------------
|
||||
// render "new map" tab content -----------------------------------------------------------------------
|
||||
let contentNewMap = Mustache.render(templateMapSettings, data);
|
||||
|
||||
// render "edit map" tab content ------------------------------------------
|
||||
// render "edit map" tab content ----------------------------------------------------------------------
|
||||
let contentEditMap = Mustache.render(templateMapSettings, data);
|
||||
contentEditMap = $(contentEditMap);
|
||||
|
||||
@@ -111,6 +120,19 @@ define([
|
||||
let deleteEolConnections = true;
|
||||
let persistentAliases = true;
|
||||
|
||||
let logActivity = true;
|
||||
let logHistory = true;
|
||||
|
||||
let slackWebHookURL = '';
|
||||
let slackUsername = '';
|
||||
let slackIcon = '';
|
||||
let slackChannelHistory = '';
|
||||
let slackChannelRally = '';
|
||||
let slackEnabled = false;
|
||||
let slackHistoryEnabled = false;
|
||||
let slackRallyEnabled = false;
|
||||
let slackSectionShow = false;
|
||||
|
||||
if(mapData !== false){
|
||||
// set current map information
|
||||
contentEditMap.find('input[name="id"]').val( mapData.config.id );
|
||||
@@ -126,9 +148,26 @@ define([
|
||||
deleteExpiredConnections = mapData.config.deleteExpiredConnections;
|
||||
deleteEolConnections = mapData.config.deleteEolConnections;
|
||||
persistentAliases = mapData.config.persistentAliases;
|
||||
|
||||
logActivity = mapData.config.logging.activity;
|
||||
logHistory = mapData.config.logging.history;
|
||||
|
||||
slackWebHookURL = mapData.config.logging.slackWebHookURL;
|
||||
slackUsername = mapData.config.logging.slackUsername;
|
||||
slackIcon = mapData.config.logging.slackIcon;
|
||||
slackChannelHistory = mapData.config.logging.slackChannelHistory;
|
||||
slackChannelRally = mapData.config.logging.slackChannelRally;
|
||||
slackEnabled = Boolean(Util.getObjVal(Init, 'slack.status'));
|
||||
slackHistoryEnabled = slackEnabled && Boolean(Util.getObjVal(Init.mapTypes, mapData.config.type.name + '.defaultConfig.send_history_slack_enabled'));
|
||||
slackRallyEnabled = slackEnabled && Boolean(Util.getObjVal(Init.mapTypes, mapData.config.type.name + '.defaultConfig.send_rally_slack_enabled'));
|
||||
slackSectionShow = (slackEnabled && slackWebHookURL.length > 0);
|
||||
|
||||
// remove "#" from Slack channels
|
||||
slackChannelHistory = slackChannelHistory.indexOf('#') === 0 ? slackChannelHistory.substr(1) : slackChannelHistory;
|
||||
slackChannelRally = slackChannelRally.indexOf('#') === 0 ? slackChannelRally.substr(1) : slackChannelRally;
|
||||
}
|
||||
|
||||
// render main dialog -----------------------------------------------------
|
||||
// render main dialog ---------------------------------------------------------------------------------
|
||||
data = {
|
||||
id: config.newMapDialogId,
|
||||
mapData: mapData,
|
||||
@@ -162,6 +201,26 @@ define([
|
||||
deleteEolConnections: deleteEolConnections,
|
||||
persistentAliases: persistentAliases,
|
||||
|
||||
logHistoryId: config.logHistoryId,
|
||||
logActivityId: config.logActivityId,
|
||||
logActivity: logActivity,
|
||||
logHistory: logHistory,
|
||||
|
||||
slackWebHookURLId: config.slackWebHookURLId,
|
||||
slackUsernameId: config.slackUsernameId,
|
||||
slackIconId: config.slackIconId,
|
||||
slackChannelHistoryId: config.slackChannelHistoryId,
|
||||
slackChannelRallyId: config.slackChannelRallyId,
|
||||
slackWebHookURL: slackWebHookURL,
|
||||
slackUsername: slackUsername,
|
||||
slackIcon: slackIcon,
|
||||
slackChannelHistory: slackChannelHistory,
|
||||
slackChannelRally: slackChannelRally,
|
||||
slackEnabled: slackEnabled,
|
||||
slackHistoryEnabled: slackHistoryEnabled,
|
||||
slackRallyEnabled: slackRallyEnabled,
|
||||
slackSectionShow: slackSectionShow,
|
||||
|
||||
characterSelectId: config.characterSelectId,
|
||||
corporationSelectId: config.corporationSelectId,
|
||||
allianceSelectId: config.allianceSelectId,
|
||||
@@ -244,6 +303,15 @@ define([
|
||||
// get form data
|
||||
let formData = form.getFormValues();
|
||||
|
||||
// add value prefixes (Slack channels)
|
||||
let tmpVal;
|
||||
if(typeof (tmpVal = Util.getObjVal(formData, 'slackChannelHistory')) === 'string' && tmpVal.length){
|
||||
formData.slackChannelHistory = '#' + tmpVal;
|
||||
}
|
||||
if(typeof (tmpVal = Util.getObjVal(formData, 'slackChannelRally')) === 'string' && tmpVal.length){
|
||||
formData.slackChannelRally = '#' + tmpVal;
|
||||
}
|
||||
|
||||
// checkbox fix -> settings tab
|
||||
if( form.find('#' + config.deleteExpiredConnectionsId).length ){
|
||||
formData.deleteExpiredConnections = formData.hasOwnProperty('deleteExpiredConnections') ? parseInt( formData.deleteExpiredConnections ) : 0;
|
||||
@@ -254,6 +322,15 @@ define([
|
||||
if( form.find('#' + config.persistentAliasesId).length ){
|
||||
formData.persistentAliases = formData.hasOwnProperty('persistentAliases') ? parseInt( formData.persistentAliases ) : 0;
|
||||
}
|
||||
if( form.find('#' + config.persistentAliasesId).length ){
|
||||
formData.persistentAliases = formData.hasOwnProperty('persistentAliases') ? parseInt( formData.persistentAliases ) : 0;
|
||||
}
|
||||
if( form.find('#' + config.logHistoryId).length ){
|
||||
formData.logHistory = formData.hasOwnProperty('logHistory') ? parseInt( formData.logHistory ) : 0;
|
||||
}
|
||||
if( form.find('#' + config.logActivityId).length ){
|
||||
formData.logActivity = formData.hasOwnProperty('logActivity') ? parseInt( formData.logActivity ) : 0;
|
||||
}
|
||||
|
||||
let requestData = {formData: formData};
|
||||
|
||||
@@ -264,8 +341,6 @@ define([
|
||||
dataType: 'json'
|
||||
}).done(function(responseData){
|
||||
|
||||
dialogContent.hideLoadingAnimation();
|
||||
|
||||
if(responseData.error.length){
|
||||
form.showFormMessage(responseData.error);
|
||||
}else{
|
||||
@@ -287,6 +362,8 @@ define([
|
||||
Util.showNotify({title: jqXHR.status + ': saveMap', text: reason, type: 'warning'});
|
||||
$(document).setProgramStatus('problem');
|
||||
|
||||
}).always(function() {
|
||||
dialogContent.hideLoadingAnimation();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -297,10 +374,13 @@ define([
|
||||
});
|
||||
|
||||
|
||||
// after modal is shown =======================================================================
|
||||
// after modal is shown ===============================================================================
|
||||
mapInfoDialog.on('shown.bs.modal', function(e){
|
||||
mapInfoDialog.initTooltips();
|
||||
|
||||
// manually trigger the "show" event for the initial active tab (not triggered by default...)
|
||||
mapInfoDialog.find('.navbar li.active a[data-toggle=tab]').trigger('shown.bs.tab');
|
||||
|
||||
// prevent "disabled" tabs from being clicked... "bootstrap" bugFix...
|
||||
mapInfoDialog.find('.navbar a[data-toggle=tab]').on('click', function(e){
|
||||
if ($(this).hasClass('disabled')){
|
||||
@@ -323,17 +403,12 @@ define([
|
||||
form.showFormMessage([{type: 'warning', message: 'No maps found. Create a new map before you can start'}]);
|
||||
}
|
||||
|
||||
// init select fields in case "settings" tab is open by default
|
||||
if(options.tab === 'settings'){
|
||||
initSettingsSelectFields(mapInfoDialog);
|
||||
}
|
||||
|
||||
// init "download tab" ========================================================================
|
||||
// init "download tab" ============================================================================
|
||||
let downloadTabElement = mapInfoDialog.find('#' + config.dialogMapDownloadContainerId);
|
||||
if(downloadTabElement.length){
|
||||
// tab exists
|
||||
|
||||
// export map data ------------------------------------------------------------------------
|
||||
// export map data ----------------------------------------------------------------------------
|
||||
downloadTabElement.find('#' + config.buttonExportId).on('click', { mapData: mapData }, function(e){
|
||||
|
||||
let exportForm = $('#' + config.dialogMapExportFormId);
|
||||
@@ -364,7 +439,7 @@ define([
|
||||
}
|
||||
});
|
||||
|
||||
// import map data ------------------------------------------------------------------------
|
||||
// import map data ----------------------------------------------------------------------------
|
||||
// check if "FileReader" API is supported
|
||||
let importFormElement = downloadTabElement.find('#' + config.dialogMapImportFormId);
|
||||
if(window.File && window.FileReader && window.FileList && window.Blob){
|
||||
@@ -477,16 +552,22 @@ define([
|
||||
}
|
||||
});
|
||||
|
||||
// events for tab change
|
||||
// events for tab change ------------------------------------------------------------------------------
|
||||
mapInfoDialog.find('.navbar a').on('shown.bs.tab', function(e){
|
||||
let modalDialog = mapInfoDialog.find('div.modal-dialog');
|
||||
let selectElementCharacter = mapInfoDialog.find('#' + config.characterSelectId);
|
||||
let selectElementCorporation = mapInfoDialog.find('#' + config.corporationSelectId);
|
||||
let selectElementAlliance = mapInfoDialog.find('#' + config.allianceSelectId);
|
||||
|
||||
|
||||
if($(e.target).attr('href') === '#' + config.dialogMapSettingsContainerId){
|
||||
// "settings" tab
|
||||
// "settings" tab -> resize modal
|
||||
modalDialog.toggleClass('modal-lg', true);
|
||||
initSettingsSelectFields(mapInfoDialog);
|
||||
}else{
|
||||
// resize modal
|
||||
modalDialog.toggleClass('modal-lg', false);
|
||||
|
||||
if( $(selectElementCharacter).data('select2') !== undefined ){
|
||||
$(selectElementCharacter).select2('destroy');
|
||||
}
|
||||
@@ -611,29 +692,38 @@ define([
|
||||
* @param mapData
|
||||
*/
|
||||
$.fn.showDeleteMapDialog = function(mapData){
|
||||
|
||||
let mapName = mapData.config.name;
|
||||
let mapNameStr = '<span class="txt-color txt-color-danger">' + mapName + '</span>';
|
||||
|
||||
let mapDeleteDialog = bootbox.confirm('Delete map "' + mapName + '"?', function(result){
|
||||
if(result){
|
||||
let data = {mapData: mapData.config};
|
||||
let mapDeleteDialog = bootbox.confirm({
|
||||
message: 'Delete map "' + mapNameStr + '"?',
|
||||
buttons: {
|
||||
confirm: {
|
||||
label: '<i class="fa fa-trash fa-fw"></i> delete map',
|
||||
className: 'btn-danger'
|
||||
}
|
||||
},
|
||||
callback: function(result){
|
||||
if(result){
|
||||
let data = {mapData: mapData.config};
|
||||
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: Init.path.deleteMap,
|
||||
data: data,
|
||||
dataType: 'json'
|
||||
}).done(function(data){
|
||||
Util.showNotify({title: 'Map deleted', text: 'Map: ' + mapName, type: 'success'});
|
||||
}).fail(function( jqXHR, status, error) {
|
||||
let reason = status + ' ' + error;
|
||||
Util.showNotify({title: jqXHR.status + ': deleteMap', text: reason, type: 'warning'});
|
||||
$(document).setProgramStatus('problem');
|
||||
}).always(function() {
|
||||
$(mapDeleteDialog).modal('hide');
|
||||
});
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: Init.path.deleteMap,
|
||||
data: data,
|
||||
dataType: 'json'
|
||||
}).done(function(data){
|
||||
Util.showNotify({title: 'Map deleted', text: 'Map: ' + mapName, type: 'success'});
|
||||
}).fail(function( jqXHR, status, error) {
|
||||
let reason = status + ' ' + error;
|
||||
Util.showNotify({title: jqXHR.status + ': deleteMap', text: reason, type: 'warning'});
|
||||
$(document).setProgramStatus('problem');
|
||||
}).always(function() {
|
||||
$(mapDeleteDialog).modal('hide');
|
||||
});
|
||||
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -8,7 +8,8 @@ define([
|
||||
'app/init',
|
||||
'app/util',
|
||||
'app/render',
|
||||
'bootbox'
|
||||
'bootbox',
|
||||
'peityInlineChart'
|
||||
], function($, Init, Util, Render, bootbox) {
|
||||
'use strict';
|
||||
|
||||
@@ -25,7 +26,7 @@ define([
|
||||
// stats/dataTable
|
||||
statsContainerId: 'pf-stats-dialog-container', // class for statistics container (dynamic ajax content)
|
||||
statsTableId: 'pf-stats-table', // id for statistics table element
|
||||
tableImageCellClass: 'pf-table-image-cell', // class for table "image" cells
|
||||
tableCellImageClass: 'pf-table-image-cell', // class for table "image" cells
|
||||
|
||||
// charts
|
||||
statsLineChartClass: 'pf-line-chart' // class for inline chart elements
|
||||
@@ -36,7 +37,9 @@ define([
|
||||
* @param dialogElement
|
||||
*/
|
||||
let initStatsTable = function(dialogElement){
|
||||
let columnNumberWidth = 35;
|
||||
let columnNumberWidth = 28;
|
||||
let cellPadding = 4;
|
||||
let lineChartWidth = columnNumberWidth + (2 * cellPadding);
|
||||
let lineColor = '#477372';
|
||||
|
||||
// render function for inline-chart columns
|
||||
@@ -71,7 +74,7 @@ define([
|
||||
lengthMenu: [[10, 20, 30, 50], [10, 20, 30, 50]],
|
||||
paging: true,
|
||||
ordering: true,
|
||||
order: [ 16, 'desc' ],
|
||||
order: [ 20, 'desc' ],
|
||||
info: true,
|
||||
searching: true,
|
||||
hover: false,
|
||||
@@ -97,11 +100,11 @@ define([
|
||||
orderable: false,
|
||||
searchable: false,
|
||||
width: 26,
|
||||
className: ['text-center', config.tableImageCellClass].join(' '),
|
||||
className: ['text-center', config.tableCellImageClass].join(' '),
|
||||
data: 'character',
|
||||
render: {
|
||||
_: function(data, type, row, meta){
|
||||
return '<img src="' + Init.url.ccpImageServer + 'Character/' + data.id + '_32.jpg" />';
|
||||
return '<img src="' + Init.url.ccpImageServer + '/Character/' + data.id + '_32.jpg" />';
|
||||
}
|
||||
}
|
||||
},{
|
||||
@@ -134,7 +137,7 @@ define([
|
||||
searchable: false,
|
||||
width: columnNumberWidth,
|
||||
className: ['text-right', 'hidden-xs', 'hidden-sm'].join(' '),
|
||||
data: 'systemCreate',
|
||||
data: 'mapCreate',
|
||||
render: {
|
||||
_: renderInlineChartColumn
|
||||
}
|
||||
@@ -145,7 +148,7 @@ define([
|
||||
searchable: false,
|
||||
width: columnNumberWidth,
|
||||
className: ['text-right', 'hidden-xs', 'hidden-sm'].join(' '),
|
||||
data: 'systemUpdate',
|
||||
data: 'mapUpdate',
|
||||
render: {
|
||||
_: renderInlineChartColumn
|
||||
}
|
||||
@@ -156,7 +159,7 @@ define([
|
||||
searchable: false,
|
||||
width: columnNumberWidth,
|
||||
className: ['text-right', 'hidden-xs', 'hidden-sm'].join(' '),
|
||||
data: 'systemDelete',
|
||||
data: 'mapDelete',
|
||||
render: {
|
||||
_: renderInlineChartColumn
|
||||
}
|
||||
@@ -166,7 +169,7 @@ define([
|
||||
searchable: false,
|
||||
width: 20,
|
||||
className: ['text-right', 'separator-right'].join(' ') ,
|
||||
data: 'systemSum',
|
||||
data: 'mapSum',
|
||||
render: {
|
||||
_: renderNumericColumn
|
||||
}
|
||||
@@ -177,7 +180,7 @@ define([
|
||||
searchable: false,
|
||||
width: columnNumberWidth,
|
||||
className: ['text-right', 'hidden-xs', 'hidden-sm'].join(' '),
|
||||
data: 'connectionCreate',
|
||||
data: 'systemCreate',
|
||||
render: {
|
||||
_: renderInlineChartColumn
|
||||
}
|
||||
@@ -188,7 +191,7 @@ define([
|
||||
searchable: false,
|
||||
width: columnNumberWidth,
|
||||
className: ['text-right', 'hidden-xs', 'hidden-sm'].join(' '),
|
||||
data: 'connectionUpdate',
|
||||
data: 'systemUpdate',
|
||||
render: {
|
||||
_: renderInlineChartColumn
|
||||
}
|
||||
@@ -199,7 +202,7 @@ define([
|
||||
searchable: false,
|
||||
width: columnNumberWidth,
|
||||
className: ['text-right', 'hidden-xs', 'hidden-sm'].join(' '),
|
||||
data: 'connectionDelete',
|
||||
data: 'systemDelete',
|
||||
render: {
|
||||
_: renderInlineChartColumn
|
||||
}
|
||||
@@ -208,8 +211,8 @@ define([
|
||||
title: 'Σ ',
|
||||
searchable: false,
|
||||
width: 20,
|
||||
className: ['text-right', 'separator-right'].join(' '),
|
||||
data: 'connectionSum',
|
||||
className: ['text-right', 'separator-right'].join(' ') ,
|
||||
data: 'systemSum',
|
||||
render: {
|
||||
_: renderNumericColumn
|
||||
}
|
||||
@@ -220,7 +223,7 @@ define([
|
||||
searchable: false,
|
||||
width: columnNumberWidth,
|
||||
className: ['text-right', 'hidden-xs', 'hidden-sm'].join(' '),
|
||||
data: 'signatureCreate',
|
||||
data: 'connectionCreate',
|
||||
render: {
|
||||
_: renderInlineChartColumn
|
||||
}
|
||||
@@ -231,7 +234,7 @@ define([
|
||||
searchable: false,
|
||||
width: columnNumberWidth,
|
||||
className: ['text-right', 'hidden-xs', 'hidden-sm'].join(' '),
|
||||
data: 'signatureUpdate',
|
||||
data: 'connectionUpdate',
|
||||
render: {
|
||||
_: renderInlineChartColumn
|
||||
}
|
||||
@@ -242,7 +245,7 @@ define([
|
||||
searchable: false,
|
||||
width: columnNumberWidth,
|
||||
className: ['text-right', 'hidden-xs', 'hidden-sm'].join(' '),
|
||||
data: 'signatureDelete',
|
||||
data: 'connectionDelete',
|
||||
render: {
|
||||
_: renderInlineChartColumn
|
||||
}
|
||||
@@ -252,12 +255,55 @@ define([
|
||||
searchable: false,
|
||||
width: 20,
|
||||
className: ['text-right', 'separator-right'].join(' '),
|
||||
data: 'signatureSum',
|
||||
data: 'connectionSum',
|
||||
render: {
|
||||
_: renderNumericColumn
|
||||
}
|
||||
},{
|
||||
targets: 16,
|
||||
title: '<span title="created" data-toggle="tooltip">C </span>',
|
||||
orderable: false,
|
||||
searchable: false,
|
||||
width: columnNumberWidth,
|
||||
className: ['text-right', 'hidden-xs', 'hidden-sm'].join(' '),
|
||||
data: 'signatureCreate',
|
||||
render: {
|
||||
_: renderInlineChartColumn
|
||||
}
|
||||
},{
|
||||
targets: 17,
|
||||
title: '<span title="updated" data-toggle="tooltip">U </span>',
|
||||
orderable: false,
|
||||
searchable: false,
|
||||
width: columnNumberWidth,
|
||||
className: ['text-right', 'hidden-xs', 'hidden-sm'].join(' '),
|
||||
data: 'signatureUpdate',
|
||||
render: {
|
||||
_: renderInlineChartColumn
|
||||
}
|
||||
},{
|
||||
targets: 18,
|
||||
title: '<span title="deleted" data-toggle="tooltip">D </span>',
|
||||
orderable: false,
|
||||
searchable: false,
|
||||
width: columnNumberWidth,
|
||||
className: ['text-right', 'hidden-xs', 'hidden-sm'].join(' '),
|
||||
data: 'signatureDelete',
|
||||
render: {
|
||||
_: renderInlineChartColumn
|
||||
}
|
||||
},{
|
||||
targets: 19,
|
||||
title: 'Σ ',
|
||||
searchable: false,
|
||||
width: 20,
|
||||
className: ['text-right', 'separator-right'].join(' '),
|
||||
data: 'signatureSum',
|
||||
render: {
|
||||
_: renderNumericColumn
|
||||
}
|
||||
},{
|
||||
targets: 20,
|
||||
title: 'Σ ',
|
||||
searchable: false,
|
||||
width: 20,
|
||||
@@ -277,17 +323,17 @@ define([
|
||||
},
|
||||
drawCallback: function(settings){
|
||||
this.api().rows().nodes().to$().each(function(i, row){
|
||||
$(row).find('.' + config.statsLineChartClass).peity('line', {
|
||||
$($(row).find('.' + config.statsLineChartClass)).peity('line', {
|
||||
fill: 'transparent',
|
||||
height: 18,
|
||||
min: 0,
|
||||
width: 50
|
||||
width: lineChartWidth
|
||||
});
|
||||
});
|
||||
},
|
||||
footerCallback: function ( row, data, start, end, display ) {
|
||||
let api = this.api();
|
||||
let sumColumnIndexes = [7, 11, 15, 16];
|
||||
let sumColumnIndexes = [7, 11, 15, 19, 20];
|
||||
|
||||
// column data for "sum" columns over this page
|
||||
let pageTotalColumns = api
|
||||
@@ -310,7 +356,16 @@ define([
|
||||
|
||||
statsTable.on('order.dt search.dt', function(){
|
||||
statsTable.column(0, {search:'applied', order:'applied'}).nodes().each(function(cell, i){
|
||||
$(cell).html( (i + 1) + '. ');
|
||||
let rowCount = i + 1;
|
||||
let content = '';
|
||||
switch(rowCount){
|
||||
case 1: content = '<i class="fa fa-fw fa-trophy txt-color txt-color-gold"></i>'; break;
|
||||
case 2: content = '<i class="fa fa-fw fa-trophy txt-color txt-color-silver"></i>'; break;
|
||||
case 3: content = '<i class="fa fa-fw fa-trophy txt-color txt-color-bronze"></i>'; break;
|
||||
default: content = rowCount + '. ';
|
||||
}
|
||||
|
||||
$(cell).html(content);
|
||||
});
|
||||
}).draw();
|
||||
|
||||
@@ -413,6 +468,9 @@ define([
|
||||
let currentWeek = weekStart;
|
||||
|
||||
let formattedWeeksData = {
|
||||
mapCreate: [],
|
||||
mapUpdate: [],
|
||||
mapDelete: [],
|
||||
systemCreate: [],
|
||||
systemUpdate: [],
|
||||
systemDelete: [],
|
||||
@@ -422,6 +480,7 @@ define([
|
||||
signatureCreate: [],
|
||||
signatureUpdate: [],
|
||||
signatureDelete: [],
|
||||
mapSum: 0,
|
||||
systemSum: 0,
|
||||
connectionSum: 0,
|
||||
signatureSum: 0
|
||||
@@ -433,6 +492,16 @@ define([
|
||||
if(weeksData.hasOwnProperty( yearWeekProp )){
|
||||
let weekData = weeksData[ yearWeekProp ];
|
||||
|
||||
// map ----------------------------------------------------------------------------------
|
||||
formattedWeeksData.mapCreate.push( weekData.mapCreate );
|
||||
formattedWeeksData.mapSum += parseInt( weekData.mapCreate );
|
||||
|
||||
formattedWeeksData.mapUpdate.push( weekData.mapUpdate );
|
||||
formattedWeeksData.mapSum += parseInt( weekData.mapUpdate );
|
||||
|
||||
formattedWeeksData.mapDelete.push( weekData.mapDelete );
|
||||
formattedWeeksData.mapSum += parseInt( weekData.mapDelete );
|
||||
|
||||
// system -------------------------------------------------------------------------------
|
||||
formattedWeeksData.systemCreate.push( weekData.systemCreate );
|
||||
formattedWeeksData.systemSum += parseInt( weekData.systemCreate );
|
||||
@@ -463,6 +532,11 @@ define([
|
||||
formattedWeeksData.signatureDelete.push( weekData.signatureDelete );
|
||||
formattedWeeksData.signatureSum += parseInt( weekData.signatureDelete );
|
||||
}else{
|
||||
// map -------------------------------------------------------------------------------
|
||||
formattedWeeksData.mapCreate.push(0);
|
||||
formattedWeeksData.mapUpdate.push(0);
|
||||
formattedWeeksData.mapDelete.push(0);
|
||||
|
||||
// system -------------------------------------------------------------------------------
|
||||
formattedWeeksData.systemCreate.push(0);
|
||||
formattedWeeksData.systemUpdate.push(0);
|
||||
@@ -487,6 +561,11 @@ define([
|
||||
}
|
||||
}
|
||||
|
||||
// map ---------------------------------------------------------------------------------------
|
||||
formattedWeeksData.mapCreate = formattedWeeksData.mapCreate.join(',');
|
||||
formattedWeeksData.mapUpdate = formattedWeeksData.mapUpdate.join(',');
|
||||
formattedWeeksData.mapDelete = formattedWeeksData.mapDelete.join(',');
|
||||
|
||||
// system ---------------------------------------------------------------------------------------
|
||||
formattedWeeksData.systemCreate = formattedWeeksData.systemCreate.join(',');
|
||||
formattedWeeksData.systemUpdate = formattedWeeksData.systemUpdate.join(',');
|
||||
@@ -515,6 +594,19 @@ define([
|
||||
name: data.name,
|
||||
lastLogin: data.lastLogin
|
||||
},
|
||||
mapCreate: {
|
||||
type: 'C',
|
||||
data: formattedWeeksData.mapCreate
|
||||
},
|
||||
mapUpdate: {
|
||||
type: 'U',
|
||||
data: formattedWeeksData.mapUpdate
|
||||
},
|
||||
mapDelete: {
|
||||
type: 'D',
|
||||
data: formattedWeeksData.mapDelete
|
||||
},
|
||||
mapSum: formattedWeeksData.mapSum,
|
||||
systemCreate: {
|
||||
type: 'C',
|
||||
data: formattedWeeksData.systemCreate
|
||||
@@ -554,7 +646,8 @@ define([
|
||||
data: formattedWeeksData.signatureDelete
|
||||
},
|
||||
signatureSum: formattedWeeksData.signatureSum,
|
||||
totalSum: formattedWeeksData.systemSum + formattedWeeksData.connectionSum + formattedWeeksData.signatureSum
|
||||
totalSum: formattedWeeksData.mapSum + formattedWeeksData.systemSum +
|
||||
formattedWeeksData.connectionSum + formattedWeeksData.signatureSum
|
||||
};
|
||||
|
||||
formattedData.push(rowData);
|
||||
@@ -602,18 +695,18 @@ define([
|
||||
* @param type
|
||||
* @returns {boolean}
|
||||
*/
|
||||
let isTabTypeEnabled = function(type){
|
||||
let isTabTypeEnabled = (type) => {
|
||||
let enabled = false;
|
||||
|
||||
switch(type){
|
||||
case 'private':
|
||||
if(Init.mapTypes.private.defaultConfig.activity_logging){
|
||||
if( Boolean(Util.getObjVal(Init.mapTypes, type + '.defaultConfig.log_activity_enabled')) ){
|
||||
enabled = true;
|
||||
}
|
||||
break;
|
||||
case 'corporation':
|
||||
if(
|
||||
Init.mapTypes.corporation.defaultConfig.activity_logging &&
|
||||
Boolean(Util.getObjVal(Init.mapTypes, type + '.defaultConfig.log_activity_enabled')) &&
|
||||
Util.getCurrentUserInfo('corporationId')
|
||||
){
|
||||
enabled = true;
|
||||
@@ -621,7 +714,7 @@ define([
|
||||
break;
|
||||
case 'alliance':
|
||||
if(
|
||||
Init.mapTypes.alliance.defaultConfig.activity_logging &&
|
||||
Boolean(Util.getObjVal(Init.mapTypes, type + '.defaultConfig.log_activity_enabled')) &&
|
||||
Util.getCurrentUserInfo('allianceId')
|
||||
){
|
||||
enabled = true;
|
||||
@@ -636,15 +729,46 @@ define([
|
||||
* show activity stats dialog
|
||||
*/
|
||||
$.fn.showStatsDialog = function(){
|
||||
requirejs(['text!templates/dialog/stats.html', 'mustache', 'peityInlineChart'], function(template, Mustache) {
|
||||
requirejs(['text!templates/dialog/stats.html', 'mustache', 'datatables.loader'], function(template, Mustache) {
|
||||
// get current statistics map settings
|
||||
let logActivityEnabled = false;
|
||||
let activeMap = Util.getMapModule().getActiveMap();
|
||||
if(activeMap){
|
||||
let activeMapId = activeMap.data('id');
|
||||
let activeMapData = Util.getCurrentMapData(activeMapId);
|
||||
if(activeMapData){
|
||||
logActivityEnabled = Boolean(Util.getObjVal(activeMapData, 'config.logging.activity'));
|
||||
}
|
||||
}
|
||||
|
||||
// check which dialog tab is default active
|
||||
let enablePrivateTab = isTabTypeEnabled('private');
|
||||
let enableCorporationTab = isTabTypeEnabled('corporation');
|
||||
let enableAllianceTab = isTabTypeEnabled('alliance');
|
||||
|
||||
let activePrivateTab = false;
|
||||
let activeCorporationTab = false;
|
||||
let activeAllianceTab = false;
|
||||
|
||||
if(enableCorporationTab){
|
||||
activeCorporationTab = true;
|
||||
}else if(enableAllianceTab){
|
||||
activeAllianceTab = true;
|
||||
}else if(enablePrivateTab){
|
||||
activePrivateTab = true;
|
||||
}
|
||||
|
||||
let data = {
|
||||
id: config.statsDialogId,
|
||||
dialogNavigationClass: config.dialogNavigationClass,
|
||||
dialogNavLiClass: config.dialogNavigationListItemClass,
|
||||
enablePrivateTab: isTabTypeEnabled('private'),
|
||||
enableCorporationTab: isTabTypeEnabled('corporation'),
|
||||
enableAllianceTab: isTabTypeEnabled('alliance'),
|
||||
enablePrivateTab: enablePrivateTab,
|
||||
enableCorporationTab: enableCorporationTab,
|
||||
enableAllianceTab: enableAllianceTab,
|
||||
activePrivateTab: activePrivateTab,
|
||||
activeCorporationTab: activeCorporationTab,
|
||||
activeAllianceTab: activeAllianceTab,
|
||||
logActivityEnabled: logActivityEnabled,
|
||||
statsContainerId: config.statsContainerId,
|
||||
statsTableId: config.statsTableId,
|
||||
dialogNavigationOffsetClass: config.dialogNavigationOffsetClass,
|
||||
@@ -670,7 +794,6 @@ define([
|
||||
// model events
|
||||
statsDialog.on('show.bs.modal', function(e) {
|
||||
let dialogElement = $(e.target);
|
||||
|
||||
initStatsTable(dialogElement);
|
||||
});
|
||||
|
||||
|
||||
@@ -197,15 +197,15 @@ define([
|
||||
|
||||
switch(options.type){
|
||||
case 'character':
|
||||
imagePath = Init.url.ccpImageServer + 'Character/' + data.id + '_32.jpg';
|
||||
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';
|
||||
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';
|
||||
imagePath = Init.url.ccpImageServer + '/Alliance/' + data.id + '_32.png';
|
||||
previewContent = '<img src="' + imagePath + '" style="max-width: 100%" />';
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ define([
|
||||
], function($, Init, Util, Morris) {
|
||||
'use strict';
|
||||
|
||||
var config = {
|
||||
let config = {
|
||||
// module info
|
||||
moduleClass: 'pf-module', // class for each module
|
||||
|
||||
@@ -53,8 +53,8 @@ define([
|
||||
* @param option
|
||||
* @returns {string}
|
||||
*/
|
||||
var getInfoForGraph = function(graphKey, option){
|
||||
var info = '';
|
||||
let getInfoForGraph = function(graphKey, option){
|
||||
let info = '';
|
||||
|
||||
if(config.systemGraphLabels.hasOwnProperty(graphKey)){
|
||||
info = config.systemGraphLabels[graphKey][option];
|
||||
@@ -69,14 +69,14 @@ define([
|
||||
* @param graphKey
|
||||
* @param graphData
|
||||
*/
|
||||
var initGraph = function(graphElement, graphKey, graphData, eventLine){
|
||||
let initGraph = function(graphElement, graphKey, graphData, eventLine){
|
||||
|
||||
if(graphData.length > 0){
|
||||
var labelYFormat = function(y){
|
||||
let labelYFormat = function(y){
|
||||
return Math.round(y);
|
||||
};
|
||||
|
||||
var graphConfig = {
|
||||
let graphConfig = {
|
||||
element: graphElement,
|
||||
data: graphData,
|
||||
xkey: 'x',
|
||||
@@ -121,24 +121,24 @@ define([
|
||||
* @param parentElement
|
||||
* @param systemData
|
||||
*/
|
||||
var drawModule = function(parentElement, systemData){
|
||||
let drawModule = function(parentElement, systemData){
|
||||
|
||||
// graph data is available for k-space systems
|
||||
if(systemData.type.id === 2){
|
||||
var requestData = {
|
||||
let requestData = {
|
||||
systemIds: [systemData.systemId]
|
||||
};
|
||||
|
||||
// calculate time offset until system created
|
||||
var serverData = Util.getServerTime();
|
||||
let serverData = Util.getServerTime();
|
||||
|
||||
var timestampNow = Math.floor(serverData.getTime() / 1000);
|
||||
var timeSinceUpdate = timestampNow - systemData.updated;
|
||||
let timestampNow = Math.floor(serverData.getTime() / 1000);
|
||||
let timeSinceUpdate = timestampNow - systemData.updated;
|
||||
|
||||
var timeInHours = Math.floor(timeSinceUpdate / 3600);
|
||||
var timeInMinutes = Math.floor((timeSinceUpdate % 3600) / 60);
|
||||
var timeInMinutesPercent = ( timeInMinutes / 60 ).toFixed(2);
|
||||
var eventLine = timeInHours + timeInMinutesPercent;
|
||||
let timeInHours = Math.floor(timeSinceUpdate / 3600);
|
||||
let timeInMinutes = Math.floor((timeSinceUpdate % 3600) / 60);
|
||||
let timeInMinutesPercent = ( timeInMinutes / 60 ).toFixed(2);
|
||||
let eventLine = timeInHours + timeInMinutesPercent;
|
||||
|
||||
// graph is from right to left -> convert event line
|
||||
eventLine = 23 - eventLine;
|
||||
@@ -152,7 +152,7 @@ define([
|
||||
|
||||
if( Object.keys(systemGraphsData).length > 0 ){
|
||||
// create new (hidden) module container
|
||||
var moduleElement = $('<div>', {
|
||||
let moduleElement = $('<div>', {
|
||||
class: [config.moduleClass, config.systemGraphModuleClass].join(' '),
|
||||
css: {opacity: 0}
|
||||
});
|
||||
@@ -165,7 +165,7 @@ define([
|
||||
}
|
||||
|
||||
// row element
|
||||
var rowElement = $('<div>', {
|
||||
let rowElement = $('<div>', {
|
||||
class: 'row'
|
||||
});
|
||||
moduleElement.append(rowElement);
|
||||
@@ -173,15 +173,15 @@ define([
|
||||
$.each(systemGraphsData, function(systemId, graphsData){
|
||||
$.each(graphsData, function(graphKey, graphData){
|
||||
|
||||
var colElement = $('<div>', {
|
||||
let colElement = $('<div>', {
|
||||
class: ['col-xs-12', 'col-sm-6', 'col-md-4'].join(' ')
|
||||
});
|
||||
|
||||
var headlineElement = $('<h5>').text( getInfoForGraph(graphKey, 'headline') );
|
||||
let headlineElement = $('<h5>').text( getInfoForGraph(graphKey, 'headline') );
|
||||
|
||||
colElement.append(headlineElement);
|
||||
|
||||
var graphElement = $('<div>', {
|
||||
let graphElement = $('<div>', {
|
||||
class: config.systemGraphClass
|
||||
});
|
||||
|
||||
@@ -203,7 +203,7 @@ define([
|
||||
});
|
||||
}
|
||||
}).fail(function( jqXHR, status, error) {
|
||||
var reason = status + ' ' + error;
|
||||
let reason = status + ' ' + error;
|
||||
Util.showNotify({title: jqXHR.status + ': System graph data', text: reason, type: 'warning'});
|
||||
$(document).setProgramStatus('problem');
|
||||
});
|
||||
@@ -218,10 +218,10 @@ define([
|
||||
*/
|
||||
$.fn.drawSystemGraphModule = function(systemData){
|
||||
|
||||
var parentElement = $(this);
|
||||
let parentElement = $(this);
|
||||
|
||||
// check if module already exists
|
||||
var moduleElement = parentElement.find('.' + config.systemGraphModuleClass);
|
||||
let moduleElement = parentElement.find('.' + config.systemGraphModuleClass);
|
||||
|
||||
if(moduleElement.length > 0){
|
||||
moduleElement.velocity('transition.slideDownOut', {
|
||||
|
||||
@@ -30,9 +30,10 @@ define([
|
||||
};
|
||||
|
||||
/**
|
||||
* get label element with given content
|
||||
*
|
||||
* @param text
|
||||
* @returns {*|XMLList}
|
||||
* @param options
|
||||
* @returns {jQuery}
|
||||
*/
|
||||
let getLabel = function(text, options){
|
||||
let label = $('<span>', {
|
||||
@@ -42,7 +43,6 @@ define([
|
||||
return label;
|
||||
};
|
||||
|
||||
|
||||
let showKillmails = function(moduleElement, killboardData){
|
||||
|
||||
// show number of killMails
|
||||
@@ -61,7 +61,7 @@ define([
|
||||
break;
|
||||
}
|
||||
|
||||
moduleElement.append( $('<h5>').text(i + 'h ago'));
|
||||
moduleElement.append( $('<h5>').text( i ? i + 'h ago' : 'recent'));
|
||||
|
||||
let killMailData = killboardData.tableData[i].killmails;
|
||||
|
||||
@@ -77,25 +77,25 @@ define([
|
||||
|
||||
let killData = killMailData[j];
|
||||
|
||||
let linkUrl = '//zkillboard.com/kill/' + killData.killID + '/';
|
||||
let victimImageUrl = Init.url.ccpImageServer + 'Type/' + killData.victim.shipTypeID + '_64.png';
|
||||
let killDate = getDateObjectByTimeString(killData.killTime);
|
||||
let linkUrl = '//zkillboard.com/kill/' + killData.killmail_id + '/';
|
||||
let victimImageUrl = Init.url.ccpImageServer + '/Type/' + killData.victim.ship_type_id + '_64.png';
|
||||
let killDate = Util.convertDateToUTC(new Date(killData.killmail_time));
|
||||
let killDateString = Util.convertDateToString(killDate);
|
||||
let killLossValue = Util.formatPrice( killData.zkb.totalValue );
|
||||
|
||||
// check for ally
|
||||
let victimAllyLogoUrl = '';
|
||||
let displayAlly = 'none';
|
||||
if(killData.victim.allianceID > 0){
|
||||
victimAllyLogoUrl = Init.url.ccpImageServer + 'Alliance/' + killData.victim.allianceID + '_32.png';
|
||||
if(killData.victim.alliance_id > 0){
|
||||
victimAllyLogoUrl = Init.url.ccpImageServer + '/Alliance/' + killData.victim.alliance_id + '_32.png';
|
||||
displayAlly = 'block';
|
||||
}
|
||||
|
||||
// check for corp
|
||||
let victimCorpLogoUrl = '';
|
||||
let displayCorp = 'none';
|
||||
if(killData.victim.corporationID > 0){
|
||||
victimCorpLogoUrl = Init.url.ccpImageServer + 'Corporation/' + killData.victim.corporationID + '_32.png';
|
||||
if(killData.victim.corporation_id > 0){
|
||||
victimCorpLogoUrl = Init.url.ccpImageServer + '/Corporation/' + killData.victim.corporation_id + '_32.png';
|
||||
displayCorp = 'inline';
|
||||
}
|
||||
|
||||
@@ -126,7 +126,7 @@ define([
|
||||
text: killData.victim.characterName
|
||||
}).prepend(
|
||||
$('<small>', {
|
||||
text: killDateString + ' - '
|
||||
text: killDateString
|
||||
})
|
||||
).prepend(
|
||||
$('<img>', {
|
||||
@@ -324,7 +324,7 @@ define([
|
||||
wSpaceLinkModifier = 'w-space/';
|
||||
}
|
||||
|
||||
let url = Init.url.zKillboard;
|
||||
let url = Init.url.zKillboard + '/';
|
||||
url += 'no-items/' + wSpaceLinkModifier + 'no-attackers/solarSystemID/' + systemData.systemId + '/pastSeconds/' + timeFrameInSeconds + '/';
|
||||
|
||||
killboardGraphElement.showLoadingAnimation();
|
||||
@@ -338,16 +338,14 @@ define([
|
||||
// the API wont return more than 200KMs ! - remember last bar block with complete KM information
|
||||
let lastCompleteDiffHourData = 0;
|
||||
|
||||
|
||||
// loop kills and count kills by hour
|
||||
for (let i = 0; i < kbData.length; i++) {
|
||||
let killmailData = kbData[i];
|
||||
|
||||
let killDate = getDateObjectByTimeString(killmailData.killTime);
|
||||
let killDate = Util.convertDateToUTC(new Date(killmailData.killmail_time));
|
||||
|
||||
// get time diff
|
||||
let timeDiffMin = Math.round(( serverDate - killDate ) / 1000 / 60);
|
||||
let timeDiffHour = Math.round(timeDiffMin / 60);
|
||||
let timeDiffHour = Math.floor(timeDiffMin / 60);
|
||||
|
||||
// update chart data
|
||||
if (chartData[timeDiffHour]) {
|
||||
@@ -420,22 +418,11 @@ define([
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* transform timestring
|
||||
* @param timeString
|
||||
* @returns {Date}
|
||||
*/
|
||||
let getDateObjectByTimeString = function(timeString){
|
||||
let match = timeString.match(/^(\d+)-(\d+)-(\d+) (\d+)\:(\d+)\:(\d+)$/);
|
||||
let date = new Date(match[1], match[2] - 1, match[3], match[4], match[5], match[6]);
|
||||
|
||||
return date;
|
||||
};
|
||||
|
||||
/**
|
||||
* get module element
|
||||
* @param parentElement
|
||||
* @param systemData
|
||||
* @returns {*|HTMLElement}
|
||||
* @returns {*|jQuery|HTMLElement}
|
||||
*/
|
||||
let getModule = function(parentElement, systemData){
|
||||
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
define([
|
||||
'jquery',
|
||||
'app/init',
|
||||
'config/system_effect',
|
||||
'config/signature_type',
|
||||
'conf/system_effect',
|
||||
'conf/signature_type',
|
||||
'bootbox',
|
||||
'localForage',
|
||||
'velocity',
|
||||
@@ -264,7 +264,8 @@ define([
|
||||
errors[i].field.length > 0
|
||||
){
|
||||
let formField = formElement.find('[name="' + errors[i].field + '"]');
|
||||
formField.parents('.form-group').removeClass('has-success').addClass('has-error');
|
||||
let formGroup = formField.parents('.form-group').removeClass('has-success').addClass('has-error');
|
||||
let formHelp = formGroup.find('.help-block').text(errors[i].message);
|
||||
}
|
||||
|
||||
}else if(errors[i].type === 'warning'){
|
||||
@@ -559,6 +560,7 @@ define([
|
||||
|
||||
let data = {
|
||||
id: config.headCharacterSwitchId,
|
||||
browserTabId: getBrowserTabId(),
|
||||
routes: Init.routes,
|
||||
userData: userData,
|
||||
otherCharacters: $.grep( userData.characters, function( character ) {
|
||||
@@ -729,17 +731,24 @@ define([
|
||||
|
||||
let defaultOptions = {
|
||||
dismissible: true,
|
||||
messageId: 'pf-alert-' + Math.random().toString(36).substring(7),
|
||||
messageTypeClass: messageTypeClass,
|
||||
messageTextClass: messageTextClass
|
||||
messageTextClass: messageTextClass,
|
||||
insertElement: 'replace'
|
||||
};
|
||||
|
||||
defaultOptions = $.extend(defaultOptions, config);
|
||||
|
||||
let content = Mustache.render(template, defaultOptions);
|
||||
|
||||
containerElement.html(content);
|
||||
switch(defaultOptions.insertElement){
|
||||
case 'replace': containerElement.html(content); break;
|
||||
case 'prepend': containerElement.prepend(content); break;
|
||||
case 'append': containerElement.append(content); break;
|
||||
default: console.error('insertElement: %s is not specified!', defaultOptions.insertElement);
|
||||
}
|
||||
|
||||
containerElement.children().first().velocity('stop').velocity('fadeIn');
|
||||
//containerElement.children().first().velocity('stop').velocity('fadeIn');
|
||||
$('#' + defaultOptions.messageId).velocity('stop').velocity('fadeIn');
|
||||
|
||||
});
|
||||
};
|
||||
@@ -1072,6 +1081,20 @@ define([
|
||||
return Init.currentUserData;
|
||||
};
|
||||
|
||||
/**
|
||||
* get a unique ID for each tab
|
||||
* -> store ID in session storage
|
||||
*/
|
||||
let getBrowserTabId = () => {
|
||||
let key = 'tabId';
|
||||
let tabId = sessionStorage.getItem(key);
|
||||
if(tabId === null){
|
||||
tabId = Math.random().toString(36).substr(2, 5);
|
||||
sessionStorage.setItem(key, tabId);
|
||||
}
|
||||
return tabId;
|
||||
};
|
||||
|
||||
/**
|
||||
* set default jQuery AJAX configuration
|
||||
*/
|
||||
@@ -1081,6 +1104,9 @@ define([
|
||||
// Add custom application headers on "same origin" requests only!
|
||||
// -> Otherwise a "preflight" request is made, which will "probably" fail
|
||||
if(settings.crossDomain === false){
|
||||
// Add browser tab information
|
||||
xhr.setRequestHeader('Pf-Tab-Id', getBrowserTabId()) ;
|
||||
|
||||
// add current character data to ANY XHR request (HTTP HEADER)
|
||||
// -> This helps to identify multiple characters on multiple browser tabs
|
||||
let userData = getCurrentUserData();
|
||||
@@ -2058,34 +2084,59 @@ define([
|
||||
};
|
||||
|
||||
/**
|
||||
* Create Date as UTC
|
||||
* clear session Storage
|
||||
* -> otherwise a tab refresh does not clear sessionStorage!
|
||||
*/
|
||||
let clearSessionStorage = () => {
|
||||
if(sessionStorage){
|
||||
sessionStorage.clear();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Create Date() as UTC
|
||||
* @param date
|
||||
* @returns {Date}
|
||||
*/
|
||||
let createDateAsUTC = function(date) {
|
||||
let createDateAsUTC = function(date){
|
||||
return new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate(), date.getHours(), date.getMinutes(), date.getSeconds()));
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert Date to UTC (!important function!)
|
||||
* Convert Date() to UTC (!important function!)
|
||||
* @param date
|
||||
* @returns {Date}
|
||||
*/
|
||||
let convertDateToUTC = function(date) {
|
||||
let convertDateToUTC = function(date){
|
||||
return new Date(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(), date.getUTCHours(), date.getUTCMinutes(), date.getUTCSeconds());
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert Date to Time String
|
||||
* Convert Date() to Time String
|
||||
* @param date
|
||||
* @param showSeconds
|
||||
* @returns {string}
|
||||
*/
|
||||
let convertDateToString = function(date){
|
||||
let convertDateToString = function(date, showSeconds){
|
||||
let dateString = ('0'+ (date.getMonth() + 1 )).slice(-2) + '/' + ('0'+date.getDate()).slice(-2) + '/' + date.getFullYear();
|
||||
let timeString = ('0' + date.getHours()).slice(-2) + ':' + ('0'+date.getMinutes()).slice(-2);
|
||||
timeString += (showSeconds) ? ':' + ('0'+date.getSeconds()).slice(-2) : '';
|
||||
return dateString + ' ' + timeString;
|
||||
};
|
||||
|
||||
/**
|
||||
* get deep json object value if exists
|
||||
* -> e.g. key = 'first.last.third' string
|
||||
* @param obj
|
||||
* @param key
|
||||
* @returns {*}
|
||||
*/
|
||||
let getObjVal = (obj, key) => {
|
||||
return key.split('.').reduce((o, x) => {
|
||||
return (typeof o === 'undefined' || o === null) ? o : o[x];
|
||||
}, obj);
|
||||
};
|
||||
|
||||
/**
|
||||
* get document path
|
||||
* -> www.pathfinder.com/pathfinder/ -> /pathfinder
|
||||
@@ -2197,11 +2248,15 @@ define([
|
||||
getNearBySystemData: getNearBySystemData,
|
||||
getNearByCharacterData: getNearByCharacterData,
|
||||
setDestination: setDestination,
|
||||
convertDateToUTC: convertDateToUTC,
|
||||
convertDateToString: convertDateToString,
|
||||
getOpenDialogs: getOpenDialogs,
|
||||
openIngameWindow: openIngameWindow,
|
||||
formatPrice: formatPrice,
|
||||
getLocalStorage: getLocalStorage,
|
||||
clearSessionStorage: clearSessionStorage,
|
||||
getBrowserTabId: getBrowserTabId,
|
||||
getObjVal: getObjVal,
|
||||
getDocumentPath: getDocumentPath,
|
||||
redirect: redirect,
|
||||
logout: logout
|
||||
|
||||
2
js/lib/bootbox.min.js
vendored
2
js/lib/bootbox.min.js
vendored
File diff suppressed because one or more lines are too long
@@ -93,13 +93,26 @@ jQuery.fn.dragToSelect = function (conf) {
|
||||
return this;
|
||||
}
|
||||
|
||||
var parentOffset = parent.offset();
|
||||
var parentDim = {
|
||||
left: parentOffset.left,
|
||||
top: parentOffset.top,
|
||||
width: parent.width(),
|
||||
height: parent.height()
|
||||
};
|
||||
var parentDim = {
|
||||
left: 0,
|
||||
top: 0,
|
||||
width: 10,
|
||||
height: 10
|
||||
};
|
||||
|
||||
// set parent dimensions
|
||||
// -> should be updated in case of left/right menu is open
|
||||
var setParentDimensions = (parent) => {
|
||||
var parentOffset = parent.offset();
|
||||
parentDim = {
|
||||
left: parentOffset.left,
|
||||
top: parentOffset.top,
|
||||
width: parent.width(),
|
||||
height: parent.height()
|
||||
};
|
||||
}
|
||||
|
||||
setParentDimensions(parent);
|
||||
|
||||
// Current origin of select box
|
||||
var selectBoxOrigin = {
|
||||
@@ -343,6 +356,7 @@ jQuery.fn.dragToSelect = function (conf) {
|
||||
// Do the right stuff then return this --------------------------------------------------------
|
||||
|
||||
selectBox.mousemove(function(e){
|
||||
setParentDimensions(parent);
|
||||
lastMousePosition.x = e.pageX;
|
||||
lastMousePosition.y = e.pageY;
|
||||
e.preventDefault();
|
||||
@@ -353,7 +367,6 @@ jQuery.fn.dragToSelect = function (conf) {
|
||||
e.which === 1 && // left mouse down
|
||||
e.target === realParent[0] // prevent while dragging a system :)
|
||||
) {
|
||||
|
||||
// Make sure user isn't clicking scrollbar (or disallow clicks far to the right actually)
|
||||
if ((e.pageX + 20) > jQuery(document.body).width()) {
|
||||
return;
|
||||
@@ -366,6 +379,7 @@ jQuery.fn.dragToSelect = function (conf) {
|
||||
|
||||
e.preventDefault();
|
||||
}).mousemove(function(e){
|
||||
setParentDimensions(parent);
|
||||
lastMousePosition.x = e.pageX;
|
||||
lastMousePosition.y = e.pageY;
|
||||
e.preventDefault();
|
||||
|
||||
6
js/lib/validator.min.js
vendored
6
js/lib/validator.min.js
vendored
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user