Merge pull request #85 from exodus4d/develop

v1.0.0RC1
This commit is contained in:
Mark Friedrich
2016-01-22 18:53:49 +01:00
187 changed files with 6367 additions and 2237 deletions

1
.gitignore vendored
View File

@@ -49,3 +49,4 @@ Temporary Items
.idea
.sass-cache
.usage
*.gz

View File

@@ -1,7 +1,10 @@
# Enable rewrite engine and route requests to framework
# HTTPS over SSL version
# Information: https://github.com/exodus4d/pathfinder/wiki/Apache
# Enable rewrite engine and route requests to framework ===========================================
RewriteEngine On
# HTTP to HTTPS ----------------------------------------------------------------
# HTTP to HTTPS ===================================================================================
RewriteCond %{HTTPS} off
RewriteCond %{HTTP_HOST} !^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$
RewriteCond %{HTTP_HOST} !=localhost
@@ -11,22 +14,25 @@ RewriteCond %{HTTP_HOST} !=localhost
# the subsequent rule will catch it.
RewriteRule .* https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
# Now, rewrite any request to the wrong domain to use www.
# Rewrite NONE www. to force www. =================================================================
RewriteCond %{HTTP_HOST} !^www\.
# skip "localhost" (dev environment)...
RewriteCond %{HTTP_HOST} !=localhost
# skip IP calls (dev environment)
RewriteCond %{HTTP_HOST} !^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$
# rewrite everything else to "https://" and "www."
RewriteRule .* https://www.%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
# Some servers require you to specify the `RewriteBase` directive
# In such cases, it should be the path (relative to the document root)
# containing this .htaccess file
#
#RewriteBase /app/
# containing this .htaccess file:
# RewriteBase /app/
# Protect system files ============================================================================
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteRule ^(lib|tmp)\/|\.(ini|php)$ - [R=404]
# Rewrite "everything" to index.php (dispatcher) ==================================================
RewriteCond %{REQUEST_FILENAME} !-l
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
@@ -34,18 +40,19 @@ RewriteRule .* index.php [L,QSA]
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization},L]
# PHP global Vars
# PHP global Vars (can be set in php.ini as well,...) =============================================
php_value max_input_vars 5000
php_value suhosin.get.max_vars 5000
php_value suhosin.post.max_vars 5000
php_value suhosin.request.max_vars 5000
# PHP error logs
# Activate PHP error log ==========================================================================
php_flag log_errors on
# php_value error_log "/www/htdocs/w0128162/www.pathfinder.exodus4d.de/logs/php_errors.log"
# php_value error_log "/www/htdocs/www.pathfinder-w.space/logs/php_errors.log"
# caching
# Cache Header ====================================================================================
# You should not change anything in here!
# New versioned files come with a unique path (e.g. ../js/v1.0.0/..) to force client cache busting.
<ifmodule mod_expires.c>
# fonts
<Filesmatch "\.(eot|woff2|woff|ttf|ttf|svg)$">
@@ -83,6 +90,4 @@ php_flag log_errors on
ExpiresActive on
ExpiresDefault "access plus 1 week"
</Filesmatch>
</ifmodule>
</ifmodule>

82
.htaccess_HTTP Normal file
View File

@@ -0,0 +1,82 @@
# HTTP version
# Information: https://github.com/exodus4d/pathfinder/wiki/Apache
# Enable rewrite engine and route requests to framework ===========================================
RewriteEngine On
# Rewrite NONE www. to force www. =================================================================
RewriteCond %{HTTP_HOST} !^www\.
# skip "localhost" (dev environment)...
RewriteCond %{HTTP_HOST} !=localhost
# skip IP calls (dev environment) e.g. 127.0.0.1
RewriteCond %{HTTP_HOST} !^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$
# rewrite everything else to "http://" and "www."
RewriteRule .* http://www.%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
# Some servers require you to specify the `RewriteBase` directive
# In such cases, it should be the path (relative to the document root)
# containing this .htaccess file:
# RewriteBase /app/
# Protect system files ============================================================================
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteRule ^(lib|tmp)\/|\.(ini|php)$ - [R=404]
# Rewrite "everything" to index.php (dispatcher) ==================================================
RewriteCond %{REQUEST_FILENAME} !-l
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule .* index.php [L,QSA]
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization},L]
# PHP global Vars (can be set in php.ini as well,...) =============================================
php_value max_input_vars 5000
php_value suhosin.get.max_vars 5000
php_value suhosin.post.max_vars 5000
php_value suhosin.request.max_vars 5000
# Activate PHP error log ==========================================================================
php_flag log_errors on
php_value error_log "/www/htdocs/w0128162/www.pathfinder-dev.exodus4d.de/logs/php_errors.log"
# Cache Header ====================================================================================
# You should not change anything in here!
# New versioned files come with a unique path (e.g. ../js/v1.0.0/..) to force client cache busting.
<ifmodule mod_expires.c>
# fonts
<Filesmatch "\.(eot|woff2|woff|ttf|ttf|svg)$">
ExpiresActive on
ExpiresDefault "access plus 1 month"
Header append Cache-Control "public"
</Filesmatch>
# images/vector graphics
<Filesmatch "\.(jpg|jpeg|png|gif|swf|ico|svg)$">
ExpiresActive on
ExpiresDefault "access plus 1 year"
Header append Cache-Control "public"
FileETag None
Header unset ETag
</Filesmatch>
# css
<Filesmatch "\.(css)$">
ExpiresActive on
ExpiresDefault "access plus 1 month"
</Filesmatch>
## js/source maps
<Filesmatch "\.(js|map)$">
ExpiresActive on
ExpiresDefault "access plus 1 year"
Header append Cache-Control "public"
FileETag None
Header unset ETag
</Filesmatch>
# html templates
<Filesmatch "\.(htm|html)$">
ExpiresActive on
ExpiresDefault "access plus 1 week"
</Filesmatch>
</ifmodule>

View File

@@ -1,29 +1,38 @@
## *PATHFINDER*
Mapping tool for [*EVE ONLINE*](https://www.eveonline.com)
- Project [https://www.pathfinder.exodus4d.de](https://www.pathfinder.exodus4d.de)
- Project [https://www.pathfinder-w.space](https://www.pathfinder-w.space)
- Official Forum post [https://forums.eveonline.com](https://forums.eveonline.com/default.aspx?g=posts&m=6021776#post6021776)
- Screenshots [imgur.com](http://imgur.com/a/k2aVa)
- Video [youtube.com](https://www.youtube.com/channel/UC7HU7XEoMbqRwqxDTbMjSPg)
- Community [google +](https://plus.google.com/u/0/b/110257318165279088853/110257318165279088853)
- Licence [MIT](http://opensource.org/licenses/MIT)
##### IMPORTANT Information
**This project is still in beta phase and is not officially released! Feel free to check the code for bugs and security issues. Issues should be reported in the [Issue](https://github.com/exodus4d/pathfinder/issues) section.**
##### IMPORTANT Information:
**The setup and installation process in ``1.0.0RC1`` and is not backwards compatible with previous beta releases (check wiki)!**
If you are looking for installation help, please check the [wiki](https://github.com/exodus4d/pathfinder/wiki) (DRAFT). More information will be added once the beta is over and the first stable build is released.
**Feel free to check the code for bugs and security issues.
Issues should be reported in the [Issue](https://github.com/exodus4d/pathfinder/issues) section.**
If you are looking for installation help, please check the [wiki](https://github.com/exodus4d/pathfinder/wiki).
More information will be added once the beta is over and the first stable build is released.
## Project structure
```
|-- (0755) app --> backend [*.php]
|-- (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
|-- pathfinder.ini --> config pathfinder
|-- routes.ini --> config routes
|-- 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 )
@@ -42,7 +51,7 @@ If you are looking for installation help, please check the [wiki](https://github
|-- ...
|-- (0777) tmp --> cache folder
|-- ...
|-- (0755) .htaccess --> reroute/caching rules
|-- (0755) .htaccess --> reroute/caching rules ("Apache" only!)
|-- (0755) index.php
--------------------------

View File

@@ -1,36 +1,47 @@
; Global Framework Config
[globals]
; Default Verbosity level of the stack trace.
; Assign values between 0 to 3 for increasing verbosity levels. Check "PATHFINDER" config for overwriting
DEBUG = 0
; Assign values between 0 to 3 for increasing verbosity levels. Check (environment.ini) config for overwriting
DEBUG = 0
; If TRUE, the framework, after having logged stack trace and errors, stops execution (die without any status) when a non-fatal error is detected.
HALT = FALSE
; If TRUE, the framework, after having logged stack trace and errors, stops execution
; -> (die without any status) when a non-fatal error is detected.
HALT = FALSE
; Timezone to use. Sync program with eve server time
TZ = "UTC"
TZ = UTC
; Cache backend. Can handle Memcache module, APC, WinCache, XCache and a filesystem-based cache.
CACHE = TRUE
CACHE = TRUE
; Callback functions ===================================================================================
ONERROR = "Controller\Controller->showError"
; Callback functions ==============================================================================
ONERROR = Controller\Controller->showError
UNLOAD = "Controller\Controller->unload"
UNLOAD = Controller\Controller->unload
; Path configurations ==================================================================================
; Path configurations =============================================================================
; relative to "BASE" dir
; Temporary folder for cache, filesystem locks, compiled F3 templates, etc.
TEMP = tmp/
TEMP = tmp/
; Log file folder
LOGS = logs/
LOGS = logs/
; Search path for user interface files used by the View and Template classes' render() method.
UI = public/
UI = public/
; Search path(s) for user-defined PHP classes that the framework will attempt to autoload at runtime
AUTOLOAD = app/main/
AUTOLOAD = app/main/
; path to favicons folder
FAVICON = /favicon
; load additional config files
[configs]
app/routes.ini = true
app/environment.ini = true
app/pathfinder.ini = true
app/requirements.ini = true
app/cron.ini = true

View File

@@ -21,4 +21,4 @@ deactivateMapData = Cron\MapUpdate->deactivateMapData, @hourly
deleteMapData = Cron\MapUpdate->deleteMapData, @downtime
; delete character log data
deleteLogData = Cron\CharacterUpdate->deleteLogData, @instant
deleteLogData = Cron\CharacterUpdate->deleteLogData, @hourly

64
app/environment.ini Normal file
View File

@@ -0,0 +1,64 @@
; Environment Config
[ENVIRONMENT]
; project environment (DEVELOP, PRODUCTION).
; This effects: DB connection, Mail-Server connection
; configuration below
SERVER = DEVELOP
[ENVIRONMENT.DEVELOP]
; base dir (Default: "auto-detect"
BASE =
; deployment URL e.g. http://localhost
URL = http://pathfinder.local
; Verbosity level of the stack trace
DEBUG = 3
; main db
DB_DNS = mysql:host=localhost;port=3306;dbname=
DB_NAME = pathfinder
DB_USER = root
DB_PASS =
; EVE-Online CCP Database export
DB_CCP_DNS = mysql:host=localhost;port=3306;dbname=
DB_CCP_NAME = eve_parallax_min
DB_CCP_USER = root
DB_CCP_PASS =
; SMTP settings
SMTP_HOST = localhost
SMTP_PORT = 25
SMTP_SCHEME = ""
SMTP_USER = pathfinder
SMTP_PASS = root
SMTP_FROM = pathfinder@localhost.com
SMTP_ERROR = pathfinder@localhost.com
[ENVIRONMENT.PRODUCTION]
BASE = /www/htdocs/www.pathfinder-w.space
; deployment URL
URL = https://www.pathfinder-w.space
; Verbosity level of the stack trace
DEBUG = 0
; main db
DB_DNS = mysql:host=localhost;port=3306;dbname=
DB_NAME =
DB_USER =
DB_PASS =
; EVE-Online CCP Database export
DB_CCP_DNS = mysql:host=localhost;port=3306;dbname=
DB_CCP_NAME =
DB_CCP_USER =
DB_CCP_PASS =
; SMTP settings
SMTP_HOST = localhost
SMTP_PORT = 25
SMTP_SCHEME = TLS
SMTP_USER =
SMTP_PASS =
SMTP_FROM = registration@pathfinder-w.space
SMTP_ERROR = admin@pathfinder-w.space

View File

@@ -6,27 +6,33 @@ class Cron extends \Prefab {
const
E_Undefined='Undefined property: %s::$%s',
E_Invalid='"%s" is not a valid name: it should only contain alphanumeric characters',
E_NotFound='Job %s doesn\' exist',
E_Callable='Job %s cannot be called';
//@}
//@{ Log message
const
L_Execution='%s (%.3F s)';
//@}
/** @var bool */
public $log=FALSE;
/** @var bool */
public $cli=TRUE;
/** @var bool */
public $web=FALSE;
/** @var string */
public $clipath;
/** @var bool */
public $silent=TRUE;
/** @var string Script path */
public $script='index.php';
/** @var string PHP CLI path */
protected $binary;
/** @var array */
protected $jobs=array();
/** @var bool */
protected $async=FALSE;
/** @var array */
protected $presets=array(
'yearly'=>'0 0 1 1 *',
@@ -37,6 +43,20 @@ class Cron extends \Prefab {
'hourly'=>'0 * * * *',
);
/**
* Set binary path after checking that it can be executed and is CLI
* @param string $path
* @return string
*/
function binary($path) {
if (function_exists('exec')) {
exec($path.' -v 2>&1',$out,$ret);
if ($ret==0 && preg_match('/cli/',@$out[0],$out))
$this->binary=$path;
}
return $this->binary;
}
/**
* Schedule a job
* @param string $job
@@ -67,7 +87,6 @@ class Cron extends \Prefab {
function isDue($job,$time) {
if (!isset($this->jobs[$job]) || !$parts=$this->parseExpr($this->jobs[$job][1]))
return FALSE;
foreach($this->parseTimestamp($time) as $i=>$k)
if (!in_array($k,$parts[$i]))
return FALSE;
@@ -78,51 +97,50 @@ class Cron extends \Prefab {
* Execute a job
* @param string $job
* @param bool $async
* @return bool TRUE = job has been executed / FALSE = job has been delegated to a background process
*/
function execute($job,$async=TRUE) {
if (!isset($this->jobs[$job]))
return;
user_error(sprintf(self::E_NotFound,$job),E_USER_ERROR);
$f3=\Base::instance();
if (is_string($func=$this->jobs[$job][0])){
if (is_string($func=$this->jobs[$job][0]))
$func=$f3->grab($func);
}
if (!is_callable($func))
return;
if ($async && $this->async) {
user_error(sprintf(self::E_Callable,$job),E_USER_ERROR);
if ($async && isset($this->binary)) {
// PHP docs: If a program is started with this function, in order for it to continue running in the background,
// the output of the program must be redirected to a file or another output stream.
// Failing to do so will cause PHP to hang until the execution of the program ends.
$dir='';
$file='index.php';
if ($this->clipath) {
$dir=dirname($this->clipath);
$file=basename($this->clipath);
}
$dir=dirname($this->script);
$file=basename($this->script);
if (@$dir[0]!='/')
$dir=getcwd().'/'.$dir;
exec(sprintf('cd "%s";php %s /cron/%s > /dev/null 2>/dev/null &',$dir,$file,$job));
} else {
$start=microtime(TRUE);
call_user_func_array($func,array($f3));
if ($this->log) {
$log=new Log('cron.log');
$log->write(sprintf(self::L_Execution,$job,microtime(TRUE)-$start));
}
exec(sprintf('cd "%s";%s %s /cron/%s > /dev/null 2>/dev/null &',$dir,$this->binary,$file,$job));
return FALSE;
}
$start=microtime(TRUE);
call_user_func_array($func,array($f3));
if ($this->log) {
$log=new Log('cron.log');
$log->write(sprintf(self::L_Execution,$job,microtime(TRUE)-$start));
}
return TRUE;
}
/**
* Run scheduler, i.e executes all due jobs at a given time
* @param int $time
* @param bool $async
* @return array List of executed jobs
*/
function run($time=NULL,$async=TRUE) {
if (!isset($time))
$time=time();
$exec=array();
foreach(array_keys($this->jobs) as $job)
if ($this->isDue($job,$time))
$this->execute($job,$async);
$exec[$job]=$this->execute($job,$async);
return $exec;
}
/**
@@ -131,16 +149,18 @@ class Cron extends \Prefab {
* @param array $params
*/
function route($f3,$params) {
if (PHP_SAPI=='cli'?!$this->cli:!$this->web)
if (PHP_SAPI!='cli' && !$this->web)
$f3->error(404);
if (isset($params['job']))
$this->execute($params['job'],FALSE);
else{
// IMPORTANT! async does not work on Windows
// -> my development environment is Windows :((
$async = FALSE;
$this->run(NULL, $async);
$exec=isset($params['job'])?
array($params['job']=>$this->execute($params['job'],FALSE)):
$this->run();
if (!$this->silent) {
if (PHP_SAPI!='cli')
header('Content-Type: text/plain');
if (!$exec)
die('Nothing to do');
foreach($exec as $job=>$ok)
echo sprintf('%s [%s]',$job,$ok?'OK':'async')."\r\n";
}
}
@@ -195,8 +215,10 @@ class Cron extends \Prefab {
//! Read-only public properties
function __get($name) {
if (in_array($name,array('jobs','async','presets')))
if (in_array($name,array('binary','jobs','presets')))
return $this->$name;
if ($name=='clipath') // alias for script [deprecated]
return $this->script;
trigger_error(sprintf(self::E_Undefined,__CLASS__,$name));
}
@@ -204,13 +226,13 @@ class Cron extends \Prefab {
function __construct() {
$f3=\Base::instance();
$config=(array)$f3->get('CRON');
foreach(array('log','cli','web') as $k)
if (isset($config[$k]))
$this->$k=(bool)$config[$k];
foreach(array('clipath') as $k)
if (isset($config[$k]))
$this->$k=(string)$config[$k];
foreach(array('log','web','script','silent') as $k)
if (isset($config[$k])) {
settype($config[$k],gettype($this->$k));
$this->$k=$config[$k];
}
if (isset($config['binary']))
$this->binary($config['binary']);
if (isset($config['jobs']))
foreach($config['jobs'] as $job=>$arr) {
$handler=array_shift($arr);
@@ -219,10 +241,11 @@ class Cron extends \Prefab {
if (isset($config['presets']))
foreach($config['presets'] as $name=>$expr)
$this->preset($name,is_array($expr)?implode(',',$expr):$expr);
if (function_exists('exec') && exec('php -r "echo 1+3;"')=='4')
$this->async=TRUE;
if ($this->cli || $this->web)
$f3->route(array('GET /cron','GET /cron/@job'),array($this,'route'));
if (!isset($this->binary))
foreach(array('php','php-cli') as $path) // try to guess the binary name
if ($this->binary($path))
break;
$f3->route(array('GET /cron','GET /cron/@job'),array($this,'route'));
}
}

View File

@@ -18,7 +18,7 @@
* https://github.com/ikkez/F3-Sugar/
*
* @package DB
* @version 1.4.0
* @version 1.4.1-dev
* @since 24.04.2012
* @date 04.06.2015
*/
@@ -111,7 +111,8 @@ class Cortex extends Cursor {
$this->primary = '_id';
elseif (!$this->primary)
$this->primary = 'id';
if (!$this->table && !$this->fluid)
$this->table = $this->getTable();
if (!$this->table)
trigger_error(self::E_NO_TABLE);
$this->ttl = $ttl ?: 60;
if (!$this->rel_ttl)
@@ -119,7 +120,7 @@ class Cortex extends Cursor {
$this->_ttl = $this->rel_ttl ?: 0;
if (static::$init == TRUE) return;
if ($this->fluid)
static::setup($this->db,$this->getTable(),array());
static::setup($this->db,$this->table,array());
$this->initMapper();
}
@@ -264,7 +265,7 @@ class Cortex extends Cursor {
*/
public function getTable()
{
if (!$this->table && $this->fluid)
if (!$this->table && ($this->fluid || static::$init))
$this->table = strtolower(get_class($this));
return $this->table;
}
@@ -305,30 +306,30 @@ class Cortex extends Cursor {
// check m:m relation
if (array_key_exists('has-many', $field)) {
// m:m relation conf [class,to-key,from-key]
if (!is_array($relConf = $field['has-many'])) {
unset($fields[$key]);
continue;
}
$rel = $relConf[0]::resolveConfiguration();
// check if foreign conf matches m:m
if (array_key_exists($relConf[1],$rel['fieldConf'])
&& !is_null($rel['fieldConf'][$relConf[1]])
&& $relConf['hasRel'] == 'has-many') {
// compute mm table name
$mmTable = isset($relConf[2]) ? $relConf[2] :
static::getMMTableName(
$rel['table'], $relConf[1], $table, $key,
$rel['fieldConf'][$relConf[1]]['has-many']);
if (!in_array($mmTable,$schema->getTables())) {
$mmt = $schema->createTable($mmTable);
$mmt->addColumn($relConf[1])->type($relConf['relFieldType']);
$mmt->addColumn($key)->type($field['type']);
$index = array($relConf[1],$key);
sort($index);
$mmt->addIndex($index);
$mmt->build();
if (is_array($relConf = $field['has-many'])) {
$rel = $relConf[0]::resolveConfiguration();
// check if foreign conf matches m:m
if (array_key_exists($relConf[1],$rel['fieldConf'])
&& !is_null($rel['fieldConf'][$relConf[1]])
&& $relConf['hasRel'] == 'has-many') {
// compute mm table name
$mmTable = isset($relConf[2]) ? $relConf[2] :
static::getMMTableName(
$rel['table'], $relConf[1], $table, $key,
$rel['fieldConf'][$relConf[1]]['has-many']);
if (!in_array($mmTable,$schema->getTables())) {
$mmt = $schema->createTable($mmTable);
$mmt->addColumn($relConf[1])->type($relConf['relFieldType']);
$mmt->addColumn($key)->type($field['type']);
$index = array($relConf[1],$key);
sort($index);
$mmt->addIndex($index);
$mmt->build();
}
}
}
unset($fields[$key]);
continue;
}
// skip virtual fields with no type
if (!array_key_exists('type', $field)) {
@@ -468,7 +469,7 @@ class Cortex extends Cursor {
// compute mm table name
$mmTable = isset($conf[2]) ? $conf[2] :
static::getMMTableName($conf['relTable'],
$conf['relField'], $this->getTable(), $key, $fConf);
$conf['relField'], $this->table, $key, $fConf);
$this->fieldConf[$key]['has-many']['refTable'] = $mmTable;
} else
$mmTable = $conf['refTable'];
@@ -697,7 +698,7 @@ class Cortex extends Cursor {
$fromConf[1] = $rel['primary'];
$hasJoin[] = $this->_hasJoin_sql($key,$rel['table'],$hasCond,$filter,$options);
} elseif ($result = $this->_hasRefsIn($key,$has_filter,$has_options,$ttl))
$addToFilter = array($key.' IN ?', $result);
$addToFilter = array($key.' IN ?', $result);
break;
default:
trigger_error(self::E_HAS_COND);
@@ -711,7 +712,7 @@ class Cortex extends Cursor {
$filter[0] .= ' and ';
$cond = array_shift($addToFilter);
if ($this->dbsType=='sql')
$cond = $this->_sql_quoteCondition($cond,$this->db->quotekey($this->getTable()));
$cond = $this->_sql_quoteCondition($cond,$this->db->quotekey($this->table));
$filter[0] .= '('.$cond.')';
$filter = array_merge($filter, $addToFilter);
}
@@ -962,7 +963,7 @@ class Cortex extends Cursor {
$filter = array($whereClause);
elseif (!empty($filter[0]))
$filter[0] = '('.$this->_sql_quoteCondition($filter[0],
$this->db->quotekey($this->table)).') and '.$whereClause;
$this->db->quotekey($this->table)).') and '.$whereClause;
$filter = array_merge($filter, $cond[0]);
}
if ($cond[1] && isset($cond[1]['group'])) {
@@ -1024,14 +1025,29 @@ class Cortex extends Cursor {
{
if (array_key_exists($key, $this->relFilter) &&
!empty($this->relFilter[$key][0]))
{
$filter = $this->relFilter[$key][0];
$crit[0] .= ' and '.array_shift($filter);
$crit = array_merge($crit, $filter);
}
$crit=$this->mergeFilter(array($this->relFilter[$key][0],$crit));
return $crit;
}
/**
* merge multiple filters
* @param $filters
* @param string $glue
* @return array
*/
public function mergeFilter($filters,$glue='and') {
$crit = array();
$params = array();
if ($filters) {
foreach($filters as $filter) {
$crit[] = array_shift($filter);
$params = array_merge($params,$filter);
}
array_unshift($params,'( '.implode(' ) '.$glue.' ( ',$crit).' )');
}
return $params;
}
/**
* returns the option condition for a relation filter, if defined
* @param string $key
@@ -1047,12 +1063,14 @@ class Cortex extends Cursor {
/**
* Delete object/s and reset ORM
* @param $filter
* @return void
* @return bool
*/
public function erase($filter = null)
{
$filter = $this->queryParser->prepareFilter($filter, $this->dbsType);
if (!$filter && $this->emit('beforeerase')!==false) {
if (!$filter) {
if ($this->emit('beforeerase')===false)
return false;
if ($this->fieldConf) {
foreach($this->fieldConf as $field => $conf)
if (isset($conf['has-many']) &&
@@ -1064,6 +1082,7 @@ class Cortex extends Cursor {
$this->emit('aftererase');
} elseif($filter)
$this->mapper->erase($filter);
return true;
}
/**
@@ -1072,6 +1091,14 @@ class Cortex extends Cursor {
**/
function save()
{
// update changed collections
$fields = $this->fieldConf;
if ($fields)
foreach($fields as $key=>$conf)
if (!empty($this->fieldsCache[$key]) && $this->fieldsCache[$key] instanceof CortexCollection
&& $this->fieldsCache[$key]->hasChanged())
$this->set($key,$this->fieldsCache[$key]->getAll('_id',true));
// perform event & save operations
if ($new = $this->dry()) {
if ($this->emit('beforeinsert')===false)
return false;
@@ -1081,14 +1108,6 @@ class Cortex extends Cursor {
return false;
$result=$this->update();
}
// update changed collections
$fields = $this->fieldConf;
if ($fields)
foreach($fields as $key=>$conf)
if (!empty($this->fieldsCache[$key]) && $this->fieldsCache[$key] instanceof CortexCollection
&& $this->fieldsCache[$key]->hasChanged())
$this->set($key,$this->fieldsCache[$key]->getAll('_id',true));
// m:m save cascade
if (!empty($this->saveCsd)) {
foreach($this->saveCsd as $key => $val) {
@@ -1164,12 +1183,12 @@ class Cortex extends Cursor {
trigger_error('Cannot add direct relational counter.');
} elseif($this->fieldConf[$key]['relType'] == 'has-many') {
$relConf=$this->fieldConf[$key]['has-many'];
if ($relConf['hasRel']=='has-many') {
if ($relConf['hasRel']=='has-many') {
// many-to-many
if ($this->dbsType == 'sql') {
$mmTable = $this->mmTable($relConf,$key);
$filter = array($this->db->quotekey($mmTable).'.'.$this->db->quotekey($relConf['relField'])
.' = '.$this->db->quotekey($this->getTable()).'.'.$this->db->quotekey($this->primary));
.' = '.$this->db->quotekey($this->table).'.'.$this->db->quotekey($this->primary));
$from=$mmTable;
if (array_key_exists($key, $this->relFilter) &&
!empty($this->relFilter[$key][0])) {
@@ -1196,7 +1215,7 @@ class Cortex extends Cursor {
$fKey=$this->db->quotekey($fConf['primary']);
$rKey=$this->db->quotekey($relConf[1]);
$pKey=$this->db->quotekey($this->primary);
$table=$this->db->quotekey($this->getTable());
$table=$this->db->quotekey($this->table);
$crit = $fTable.'.'.$rKey.' = '.$table.'.'.$pKey;
$filter = $this->mergeWithRelFilter($key,array($crit));
$filter = $this->queryParser->prepareFilter($filter,$this->dbsType,$this->fieldConf);
@@ -1204,7 +1223,7 @@ class Cortex extends Cursor {
if (count($filter)>0)
$this->preBinds+=$filter;
$this->set('count_'.$key,'(select count('.$fTable.'.'.$fKey.') from '.$fTable.' where '.
$crit.' group by '.$fTable.'.'.$rKey.')');
$crit.' group by '.$fTable.'.'.$rKey.')');
} else {
// count rel
$this->countFields[]=$key;
@@ -1240,7 +1259,7 @@ class Cortex extends Cursor {
&& isset($this->fieldConf[$key]['type'])) {
$type = $this->fieldConf[$key]['type'];
$date = ($this->dbsType=='sql' && preg_match('/mssql|sybase|dblib|odbc|sqlsrv/',
$this->db->driver())) ? 'Ymd' : 'Y-m-d';
$this->db->driver())) ? 'Ymd' : 'Y-m-d';
if ($type == Schema::DT_DATETIME || Schema::DT_TIMESTAMP)
$this->set($key,date($date.' H:i:s'));
elseif ($type == Schema::DT_DATE)
@@ -1307,6 +1326,7 @@ class Cortex extends Cursor {
$val = $this->emit('set_'.$key, $val);
$val = $this->getForeignKeysArray($val,'_id',$key);
$this->saveCsd[$key] = $val; // array of keys
$this->fieldsCache[$key] = $val;
return $val;
} elseif ($relConf['hasRel'] == 'belongs-to-one') {
// TODO: many-to-one, bidirectional, inverse way
@@ -1495,7 +1515,8 @@ class Cortex extends Cursor {
trigger_error(sprintf(self::E_REL_CONF_INC, $key));
$rel = $this->getRelInstance($fromConf[0],null,$key,true);
$relFieldConf = $rel->getFieldConfiguration();
$relType = key($relFieldConf[$fromConf[1]]);
$relType = isset($relFieldConf[$fromConf[1]]['belongs-to-one']) ?
'belongs-to-one' : 'has-many';
// one-to-*, bidirectional, inverse way
if ($relType == 'belongs-to-one') {
$toConf = $relFieldConf[$fromConf[1]]['belongs-to-one'];
@@ -1810,7 +1831,7 @@ class Cortex extends Cursor {
$rd = isset($rel_depths[$key]) ? $rel_depths[$key] : $rel_depths['*'];
if ((is_array($rd) || $rd >= 0) && $type=preg_grep('/[belongs|has]-(to-)*[one|many]/',
array_keys($this->fieldConf[$key]))) {
$relType=$type[0];
$relType=current($type);
// cast relations
$val = (($relType == 'belongs-to-one' || $relType == 'belongs-to-many')
&& !$mp->exists($key)) ? NULL : $mp->get($key);
@@ -1852,7 +1873,7 @@ class Cortex extends Cursor {
/**
* cast a related collection of mappers
* @param string|array $key array of mapper objects, or field name
* @param string $key field name
* @param int $rel_depths depths to resolve relations
* @return array array of associative arrays
*/
@@ -1991,7 +2012,7 @@ class Cortex extends Cursor {
function exists($key, $relField = false) {
if (!$this->dry() && $key == '_id') return true;
return $this->mapper->exists($key) ||
($relField && isset($this->fieldConf[$key]['relType']));
($relField && isset($this->fieldConf[$key]['relType']));
}
/**
@@ -2131,7 +2152,10 @@ class CortexQueryParser extends \Prefab {
}
unset($part);
}
array_unshift($ncond, implode(' ', $parts));
array_unshift($ncond, array_reduce($parts,function($out,$part){
return $out.((!$out||in_array($part,array('(',')'))
||preg_match('/\($/',$out))?'':' ').$part;
},''));
break;
default:
trigger_error(self::E_ENGINEERROR);
@@ -2202,6 +2226,7 @@ class CortexQueryParser extends \Prefab {
if (is_int(strpos($part, '?'))) {
$val = array_shift($args);
preg_match('/(@\w+)/i', $part, $match);
$skipVal=false;
// find like operator
if (is_int(strpos($upart = strtoupper($part), ' @LIKE '))) {
if ($not = is_int($npos = strpos($upart, '@NOT')))
@@ -2209,16 +2234,18 @@ class CortexQueryParser extends \Prefab {
$val = $this->_likeValueToRegEx($val);
$part = ($not ? '!' : '').'preg_match(?,'.$match[0].')';
} // find IN operator
else if (is_int($pos = strpos($upart, ' @IN '))) {
elseif (is_int($pos = strpos($upart, ' @IN '))) {
if ($not = is_int($npos = strpos($upart, '@NOT')))
$pos = $npos;
$part = ($not ? '!' : '').'in_array('.substr($part, 0, $pos).
',array(\''.implode('\',\'', $val).'\'))';
unset($val);
$skipVal=true;
}
// add existence check
$part = '(isset('.$match[0].') && '.$part.')';
if (isset($val))
$part = ($val===null && !$skipVal)
? '(array_key_exists(\''.ltrim($match[0],'@').'\',$_row) && '.$part.')'
: '(isset('.$match[0].') && '.$part.')';
if (!$skipVal)
$ncond[] = $val;
} elseif ($count >= 1) {
// field comparison
@@ -2331,7 +2358,7 @@ class CortexQueryParser extends \Prefab {
$opr = str_replace(array('<>', '<', '>', '!', '='),
array('$ne', '$lt', '$gt', '$n', 'e'), $match[0]);
$var = array($opr => (strtolower($var) == 'null') ? null :
(is_object($var) ? $var : (is_numeric($var) ? $var + 0 : $var)));
(is_object($var) ? $var : (is_numeric($var) ? $var + 0 : $var)));
}
return array($key => $var);
}
@@ -2347,14 +2374,14 @@ class CortexQueryParser extends \Prefab {
$lC = substr($var, -1, 1);
// %var% -> /var/
if ($var[0] == '%' && $lC == '%')
$var = '/'.substr($var, 1, -1).'/';
$var = substr($var, 1, -1);
// var% -> /^var/
elseif ($lC == '%')
$var = '/^'.substr($var, 0, -1).'/';
$var = '^'.substr($var, 0, -1);
// %var -> /var$/
elseif ($var[0] == '%')
$var = '/'.substr($var, 1).'$/';
return $var;
$var = substr($var, 1).'$';
return '/'.$var.'/iu';
}
/**
@@ -2513,11 +2540,12 @@ class CortexCollection extends \ArrayIterator {
{
$out = array();
foreach ($this->getArrayCopy() as $model) {
if ($model->exists($prop,true)) {
if ($model instanceof Cortex && $model->exists($prop,true)) {
$val = $model->get($prop, $raw);
if (!empty($val))
$out[] = $val;
}
} elseif($raw)
$out[] = $model;
}
return $out;
}

View File

@@ -13,12 +13,12 @@
* | | < | <| -__|-- __|
* |__|__|__||__|__|_____|_____|
*
* Copyright (c) 2014 by ikkez
* Copyright (c) 2015 by ikkez
* Christian Knuth <ikkez0n3@gmail.com>
* https://github.com/ikkez/F3-Sugar/
*
* @package DB
* @version 2.1.1
* @version 2.2.0-dev
**/
@@ -30,76 +30,86 @@ class Schema extends DB_Utils {
public
$dataTypes = array(
'BOOLEAN' => array('mysql|sqlite2?|pgsql' => 'BOOLEAN',
'mssql|sybase|dblib|odbc|sqlsrv' => 'bit',
'ibm' => 'numeric(1,0)',
),
'INT1' => array('mysql' => 'TINYINT UNSIGNED',
'sqlite2?' => 'integer',
'mssql|sybase|dblib|odbc|sqlsrv' => 'tinyint',
'pgsql|ibm' => 'smallint',
),
'INT2' => array('mysql' => 'SMALLINT',
'sqlite2?' => 'integer',
'pgsql|ibm|mssql|sybase|dblib|odbc|sqlsrv' => 'smallint',
),
'INT4' => array('sqlite2?|pgsql|sybase|odbc|sqlsrv|imb' => 'integer',
'mysql|mssql|dblib' => 'int',
),
'INT8' => array('sqlite2?' => 'integer',
'pgsql|mysql|mssql|sybase|dblib|odbc|sqlsrv|imb' => 'bigint',
),
'FLOAT' => array('mysql|sqlite2?' => 'FLOAT',
'pgsql' => 'double precision',
'mssql|sybase|dblib|odbc|sqlsrv' => 'float',
'imb' => 'decfloat'
),
'DOUBLE' => array('mysql|sqlite2?|ibm' => 'DOUBLE',
'pgsql|sybase|odbc|sqlsrv' => 'double precision',
'mssql|dblib' => 'decimal',
),
'VARCHAR128' => array('mysql|pgsql|sqlite2?|ibm|mssql|sybase|dblib|odbc|sqlsrv' => 'varchar(128)',
),
'VARCHAR256' => array('mysql|pgsql|sqlite2?|ibm|mssql|sybase|dblib|odbc|sqlsrv' => 'varchar(255)',
),
'VARCHAR512' => array('mysql|pgsql|sqlite2?|ibm|mssql|sybase|dblib|odbc|sqlsrv' => 'varchar(512)',
),
'TEXT' => array('mysql|sqlite2?|pgsql|mssql' => '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)',
),
'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',
),
'TIMESTAMP' => array('mysql|ibm' => 'timestamp',
'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)',
),
'BOOLEAN' => array('mysql' => 'tinyint(1)',
'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',
),
'INT2' => array('mysql' => 'smallint(6)',
'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',
),
'INT8' => array('sqlite2?' => 'integer(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'
),
'DOUBLE' => array('mysql|sqlite2?|ibm' => 'DOUBLE',
'pgsql' => 'double precision',
'mssql|dblib|sybase|odbc|sqlsrv' => 'decimal',
),
'VARCHAR128' => array('mysql|sqlite2?|ibm|mssql|sybase|dblib|odbc|sqlsrv' => 'varchar(128)',
'pgsql' => 'character varying(128)',
),
'VARCHAR256' => array('mysql|sqlite2?|ibm|mssql|sybase|dblib|odbc|sqlsrv' => 'varchar(255)',
'pgsql' => 'character varying(255)',
),
'VARCHAR512' => array('mysql|sqlite2?|ibm|mssql|sybase|dblib|odbc|sqlsrv' => 'varchar(512)',
'pgsql' => 'character varying(512)',
),
'TEXT' => array('mysql|sqlite2?|pgsql|mssql' => '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)',
),
'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',
),
'TIMESTAMP' => array('mysql|ibm' => 'timestamp',
'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)',
),
),
$defaultTypes = array(
'CUR_STAMP' => array('mysql' => 'CURRENT_TIMESTAMP',
'mssql|sybase|dblib|odbc|sqlsrv' => 'getdate()',
'pgsql' => 'LOCALTIMESTAMP(0)',
'sqlite2?' => "(datetime('now','localtime'))",
),
);
'CUR_STAMP' => array('mysql' => 'CURRENT_TIMESTAMP',
'mssql|sybase|dblib|odbc|sqlsrv' => 'getdate()',
'pgsql' => 'LOCALTIMESTAMP(0)',
'sqlite2?' => "(datetime('now','localtime'))",
),
);
public
$name;
public static
$strict = FALSE;
/** @var \Base */
protected $fw;
@@ -257,12 +267,47 @@ class Schema extends DB_Utils {
return ($exec) ? $this->db->exec($query) : $query;
}
/**
* clear a table
* @param $name
* @param bool $exec
* @return array|bool|FALSE|int|string
*/
public function truncateTable($name, $exec = true) {
if (is_object($name) && $name instanceof TableBuilder)
$name = $name->name;
$cmd = array(
'mysql|ibm|pgsql|sybase|dblib|mssql|sqlsrv|odbc' =>
'TRUNCATE TABLE '.$this->db->quotekey($name).';',
'sqlite2?' => array(
'DELETE FROM '.$this->db->quotekey($name).';',
'UPDATE SQLITE_SEQUENCE SET seq = 0 WHERE name = '.$this->db->quotekey($name).';',
),
);
$query = $this->findQuery($cmd);
return ($exec) ? $this->db->exec($query) : $query;
}
/**
* check if a data type is compatible with a given column definition
* @param string $colType (i.e: BOOLEAN)
* @param string $colDef (i.e: tinyint(1))
* @return int
*/
public function isCompatible($colType,$colDef) {
$raw_type=$this->findQuery($this->dataTypes[strtoupper($colType)]);
preg_match_all('/(?P<type>\w+)($|\((?P<length>(\d+|(.*)))\))/', $raw_type, $match);
return (bool) preg_match_all('/'.preg_quote($match['type'][0]).'($|\('.
preg_quote($match['length'][0]).'\))/i',$colDef);
}
}
abstract class TableBuilder extends DB_Utils {
protected $columns, $pkeys, $queries, $increments, $rebuild_cmd, $suppress;
public $name, $schema;
public $name;
/** @var Schema */
public $schema;
const
TEXT_NoDefaultForTEXT = "Column `%s` of type TEXT can't have a default value.",
@@ -499,6 +544,7 @@ class TableModifier extends TableBuilder {
*/
public function build($exec = TRUE)
{
// check if table exists
if (!in_array($this->name, $this->schema->getTables()))
trigger_error(sprintf(self::TEXT_TableNotExisting, $this->name));
@@ -511,6 +557,7 @@ class TableModifier extends TableBuilder {
$this->queries = array();
// add new columns
foreach ($this->columns as $cname => $column) {
/** @var Column $column */
// not nullable fields should have a default value, when altering a table
if ($column->default === false && $column->nullable === false) {
trigger_error(sprintf(self::TEXT_NotNullFieldNeedsDefault, $column->name));
@@ -573,7 +620,7 @@ class TableModifier extends TableBuilder {
$after[$column->after][] = $cname;
// find rename commands
$rename = (!empty($this->rebuild_cmd) && array_key_exists('rename',$this->rebuild_cmd))
? $this->rebuild_cmd['rename'] : array();
? $this->rebuild_cmd['rename'] : array();
// get primary-key fields
foreach ($existing_columns as $key => $col)
if ($col['pkey'])
@@ -617,7 +664,7 @@ class TableModifier extends TableBuilder {
unset($indexes[$col]);
}
}
}
}
// create new table
$oname = $this->name;
$this->queries[] = $this->rename($oname.'_temp', false);
@@ -627,8 +674,13 @@ class TableModifier extends TableBuilder {
$colName = array_key_exists($name, $rename) ? $rename[$name] : $name;
// update column datatype
if (array_key_exists('update',$this->rebuild_cmd)
&& in_array($name,array_keys($this->rebuild_cmd['update'])))
$col['type']=$this->rebuild_cmd['update'][$name];
&& in_array($name,array_keys($this->rebuild_cmd['update']))) {
$cdat = $this->rebuild_cmd['update'][$name];
if ($cdat instanceof Column)
$col = $cdat->getColumnArray();
else
$col['type'] = $cdat;
}
$newTable->addColumn($colName, $col)->passThrough();
// add new fields with after flag
if (array_key_exists($name,$after))
@@ -644,7 +696,7 @@ class TableModifier extends TableBuilder {
// add existing indexes
foreach (array_reverse($indexes) as $name=>$conf) {
if (is_int(strpos($name, '___')))
list($tname,$name) = explode('___', $name);
list($tname,$name) = explode('___', $name);
if (is_int(strpos($name, '__')))
$name = explode('__', $name);
if ($exec) {
@@ -683,11 +735,11 @@ class TableModifier extends TableBuilder {
$triggerName = $this->db->quotekey($this->name.'_insert');
$queries[] = "DROP TRIGGER IF EXISTS $triggerName;";
$queries[] = 'CREATE TRIGGER '.$triggerName.' AFTER INSERT ON '.$table.
' WHEN (NEW.'.$pkey.' IS NULL) BEGIN'.
' UPDATE '.$table.' SET '.$pkey.' = ('.
' select coalesce( max( '.$pkey.' ), 0 ) + 1 from '.$table.
') WHERE ROWID = NEW.ROWID;'.
' END;';
' WHEN (NEW.'.$pkey.' IS NULL) BEGIN'.
' UPDATE '.$table.' SET '.$pkey.' = ('.
' select coalesce( max( '.$pkey.' ), 0 ) + 1 from '.$table.
') WHERE ROWID = NEW.ROWID;'.
' END;';
return $queries;
}
@@ -705,9 +757,9 @@ class TableModifier extends TableBuilder {
foreach ($schema as $name => &$cols) {
$default = ($cols['default'] === '') ? null : $cols['default'];
if (!is_null($default) && (
(is_int(strpos($curdef=$this->findQuery($this->schema->defaultTypes['CUR_STAMP']),
$default)) || is_int(strpos($default,$curdef)))
|| $default == "('now'::text)::timestamp(0) without time zone"))
(is_int(strpos($curdef=$this->findQuery($this->schema->defaultTypes['CUR_STAMP']),
$default)) || is_int(strpos($default,$curdef)))
|| $default == "('now'::text)::timestamp(0) without time zone"))
{
$default = 'CUR_STAMP';
} elseif (!is_null($default)) {
@@ -729,6 +781,17 @@ class TableModifier extends TableBuilder {
return $schema;
}
/**
* check if a data type is compatible with an existing column type
* @param string $colType (i.e: BOOLEAN)
* @param string $column (i.e: active)
* @return bool
*/
public function isCompatible($colType,$column) {
$cols = $this->getCols(true);
return $this->schema->isCompatible($colType,$cols[$column]['type']);
}
/**
* removes a column from a table
* @param string $name
@@ -771,7 +834,7 @@ class TableModifier extends TableBuilder {
trigger_error('cannot rename column. it does not exist.');
if (in_array($new_name, array_keys($existing_columns)))
trigger_error('cannot rename column. new column already exist.');
if (preg_match('/sqlite2?/', $this->db->driver()))
// SQlite does not support drop or rename column directly
$this->rebuild_cmd['rename'][$name] = $new_name;
@@ -801,28 +864,55 @@ class TableModifier extends TableBuilder {
/**
* modifies column datatype
* @param $name
* @param $datatype
* @param string $name
* @param string|Column $datatype
* @param bool $force
* @return bool
*/
public function updateColumn($name, $datatype, $force = false)
{
if ($datatype instanceof Column) {
$col = $datatype;
$datatype = $col->type;
$force = $col->passThrough;
}
if(!$force)
$datatype = $this->findQuery($this->schema->dataTypes[strtoupper($datatype)]);
$table = $this->db->quotekey($this->name);
$column = $this->db->quotekey($name);
if (preg_match('/sqlite2?/', $this->db->driver())){
$this->rebuild_cmd['update'][$name] = $datatype;
$this->rebuild_cmd['update'][$name] = isset($col)?$col:$datatype;
} else {
$dat = isset($col) ? $col->getColumnQuery() :
$column.' '.$datatype;
$cmd = array(
'mysql' =>
"ALTER TABLE $table MODIFY COLUMN $column $datatype;",
'pgsql' =>
"ALTER TABLE $table ALTER COLUMN $column TYPE $datatype;",
'sqlsrv|mssql|sybase|dblib|ibm' =>
"ALTER TABLE $table ALTER COLUMN $column $datatype;",
'mysql' =>
"ALTER TABLE $table MODIFY COLUMN $dat;",
'pgsql' =>
"ALTER TABLE $table ALTER COLUMN $column TYPE $datatype;",
'sqlsrv|mssql|sybase|dblib|ibm' =>
"ALTER TABLE $table ALTER COLUMN $column $datatype;",
);
if (isset($col)) {
$cmd['pgsql'] = array($cmd['pgsql']);
$cmd['pgsql'][] = "ALTER TABLE $table ALTER COLUMN $column SET DEFAULT ".$col->getDefault().";";
if ($col->nullable)
$cmd['pgsql'][] = "ALTER TABLE $table ALTER COLUMN $column DROP NOT NULL;";
else
$cmd['pgsql'][] = "ALTER TABLE $table ALTER COLUMN $column SET NOT NULL;";
$df_key = 'DF_'.$this->name.'_'.$name;
$cmd['sqlsrv|mssql|sybase|dblib|ibm'] = array(
"ALTER TABLE $table ALTER COLUMN $column $datatype ".$col->getNullable().";",
"DECLARE @ConstraintName nvarchar(200)
SELECT @ConstraintName = Name FROM SYS.DEFAULT_CONSTRAINTS WHERE PARENT_OBJECT_ID = OBJECT_ID('$this->name')
AND PARENT_COLUMN_ID = (SELECT column_id FROM sys.columns WHERE NAME = N'$name'
AND object_id = OBJECT_ID(N'$this->name'))
IF @ConstraintName IS NOT NULL
EXEC('ALTER TABLE $this->name DROP CONSTRAINT ' + @ConstraintName)
",
"ALTER TABLE $table ADD CONSTRAINT $df_key DEFAULT ".$col->getDefault()." FOR $column;",
);
}
$this->queries[] = $this->findQuery($cmd);
}
}
@@ -937,10 +1027,10 @@ class TableModifier extends TableBuilder {
class Column extends DB_Utils {
public $name, $type, $nullable, $default, $after, $index, $unique, $passThrough, $pkey;
protected $table, $schema;
protected $table, $schema, $type_val;
const
TEXT_NoDataType = 'The specified datatype %s is not defined in %s driver',
TEXT_NoDataType = 'The specified datatype %s is not defined in %s driver. Add passThrough option to enforce this datatype.',
TEXT_CurrentStampDataType = 'Current timestamp as column default is only possible for TIMESTAMP datatype';
/**
@@ -1072,14 +1162,24 @@ class Column extends DB_Utils {
return $this;
}
/**
* feed column from array or hive key
* @param string|array $args
*/
public function copyfrom($args) {
if (($args || \Base::instance()->exists($args,$args))
&& is_array($args))
foreach ($args as $arg => $val)
$this->{$arg} = $val;
}
/**
* returns an array of this column configuration
* @return array
*/
public function getColumnArray()
{
public function getColumnArray() {
$fields = array('name','type','passThrough','default','nullable',
'index','unique','after','pkey');
'index','unique','after','pkey');
$fields = array_flip($fields);
foreach($fields as $key => &$val)
$val = $this->{$key};
@@ -1087,55 +1187,55 @@ class Column extends DB_Utils {
return $fields;
}
/**
* return resolved column datatype
* @return bool|string
*/
public function getTypeVal() {
if (!$this->type)
trigger_error(sprintf('Cannot build a column query for `%s`: no column type set',$this->name));
if ($this->passThrough)
$this->type_val = $this->type;
else {
$this->type_val = $this->findQuery($this->schema->dataTypes[strtoupper($this->type)]);
if (!$this->type_val) {
if (Schema::$strict) {
trigger_error(sprintf(self::TEXT_NoDataType, strtoupper($this->type),
$this->db->driver()));
return FALSE;
} else {
// auto pass-through if not found
$this->type_val = $this->type;
}
}
}
return $this->type_val;
}
/**
* generate SQL column definition query
* @return bool|string
*/
public function getColumnQuery()
{
if (!$this->type)
trigger_error(sprintf('Cannot build a column query for `%s`: no column type set',$this->name));
public function getColumnQuery() {
// prepare column types
if ($this->passThrough)
$type_val = $this->type;
else {
$type_val = $this->findQuery($this->schema->dataTypes[strtoupper($this->type)]);
if (!$type_val) {
trigger_error(sprintf(self::TEXT_NoDataType, strtoupper($this->type),
$this->db->driver()));
return FALSE;
}
}
$type_val = $this->getTypeVal();
// build query
$query = $this->db->quotekey($this->name).' '.$type_val.' '.
($this->nullable ? 'NULL' : 'NOT NULL');
$this->getNullable();
// unify default for booleans
if (preg_match('/bool/i', $type_val) && $this->default!==null)
$this->default = (int) $this->default;
// default value
if ($this->default !== false) {
$def_cmds = array(
'sqlite2?|mysql|pgsql|mssql|sybase|dblib|odbc|sqlsrv' => 'DEFAULT',
'sqlite2?|mysql|pgsql' => 'DEFAULT',
'mssql|sybase|dblib|odbc|sqlsrv' => 'constraint DF_'.$this->table->name.'_'.$this->name.' DEFAULT',
'ibm' => 'WITH DEFAULT',
);
$def_cmd = $this->findQuery($def_cmds).' ';
// timestamp default
if ($this->default === Schema::DF_CURRENT_TIMESTAMP) {
// check for right datatpye
$stamp_type = $this->findQuery($this->schema->dataTypes['TIMESTAMP']);
if ($this->type != 'TIMESTAMP' && // TODO: check that condition
($this->passThrough && strtoupper($this->type) != strtoupper($stamp_type))
)
trigger_error(self::TEXT_CurrentStampDataType);
$def_cmd .= $this->findQuery($this->schema->defaultTypes[strtoupper($this->default)]);
} else {
// static defaults
$pdo_type = preg_match('/int|bool/i', $type_val, $parts) ?
constant('\PDO::PARAM_'.strtoupper($parts[0])) : \PDO::PARAM_STR;
$def_cmd .= ($this->default === NULL ? 'NULL' :
$this->db->quote(htmlspecialchars($this->default, ENT_QUOTES,
$this->f3->get('ENCODING')), $pdo_type));
}
$def_cmd = $this->findQuery($def_cmds).' '.$this->getDefault();
$query .= ' '.$def_cmd;
}
if (!empty($this->after)) {
if (!empty($this->after) && $this->table instanceof TableModifier) {
// `after` feature only works for mysql
if (preg_match('/mysql/', $this->db->driver())) {
$after_cmd = 'AFTER '.$this->db->quotekey($this->after);
@@ -1144,6 +1244,39 @@ class Column extends DB_Utils {
}
return $query;
}
/**
* return query part for nullable
* @return string
*/
public function getNullable() {
return $this->nullable ? 'NULL' : 'NOT NULL';
}
/**
* return query part for default value
* @return string
*/
public function getDefault() {
// timestamp default
if ($this->default === Schema::DF_CURRENT_TIMESTAMP) {
// check for right datatpye
$stamp_type = $this->findQuery($this->schema->dataTypes['TIMESTAMP']);
if ($this->type != 'TIMESTAMP' &&
($this->passThrough && strtoupper($this->type) != strtoupper($stamp_type))
)
trigger_error(self::TEXT_CurrentStampDataType);
return $this->findQuery($this->schema->defaultTypes[strtoupper($this->default)]);
} else {
// static defaults
$type_val = $this->getTypeVal();
$pdo_type = preg_match('/int|bool/i', $type_val, $parts) ?
constant('\PDO::PARAM_'.strtoupper($parts[0])) : \PDO::PARAM_STR;
return ($this->default === NULL ? 'NULL' :
$this->db->quote(htmlspecialchars($this->default, ENT_QUOTES,
$this->f3->get('ENCODING')), $pdo_type));
}
}
}
@@ -1164,19 +1297,11 @@ class DB_Utils {
* @param $cmd array
* @return bool|string
*/
protected function findQuery($cmd)
{
$match = FALSE;
public function findQuery($cmd) {
foreach ($cmd as $backend => $val)
if (preg_match('/'.$backend.'/', $this->db->driver())) {
$match = TRUE;
break;
}
if (!$match) {
trigger_error(sprintf(self::TEXT_ENGINE_NOT_SUPPORTED, $this->db->driver()));
return FALSE;
}
return $val;
if (preg_match('/'.$backend.'/', $this->db->driver()))
return $val;
trigger_error(sprintf(self::TEXT_ENGINE_NOT_SUPPORTED, $this->db->driver()));
}
public function __construct(SQL $db) {

216
app/lib/sheet.php Normal file
View File

@@ -0,0 +1,216 @@
<?php
/**
* Sheet - CSV and Excel tools
*
* The contents of this file are subject to the terms of the GNU General
* Public License Version 3.0. You may not use this file except in
* compliance with the license. Any of the license terms and conditions
* can be waived if you get permission from the copyright holder.
*
* (c) Christian Knuth
*
* @date: 16.03.2015
* @version 0.4.1
*/
class Sheet extends \Prefab {
/**
* multiline-aware CSV parser
* @param $filepath
* @param string $delimiter
* @param string $enclosure
* @return array|bool
*/
public function parseCSV($filepath,$delimiter=";",$enclosure='"') {
if (!is_file($filepath)) {
user_error('File not found: '.$filepath);
return false;
}
$data = \Base::instance()->read($filepath,true);
if(!preg_match_all('/((?:.*?)'.$delimiter.'(?:'.$enclosure.'.*?'.
$enclosure.'|['.$delimiter.'(?:\d|\.|\/)*\d])*\n)/s',$data,$matches))
user_error('no rows found');
$out = array_map(function($val) use($delimiter,$enclosure) {
return str_getcsv($val,$delimiter,$enclosure);
},$matches[0]);
return $out;
}
/**
* use specified headers or first row as label for each row item key
* @param $rows
* @param null $headers
* @return array
*/
public function applyHeader($rows,$headers=null) {
if (!$headers)
$headers=array_shift($rows);
return array_map(function($row) use($headers) {
return array_combine(array_values($headers),array_values($row));
},$rows);
}
/**
* build and return xls file data
* @param $rows
* @param $headers
* @return string
*/
public function dumpXLS($rows,$headers) {
$numColumns = count($headers);
$numRows = count($rows);
foreach($headers as $key=>$val)
if (is_numeric($key)) {
$headers[$val]=ucfirst($val);
unset($headers[$key]);
}
$xls = $this->xlsBOF();
for ($i = 0; $i <= $numRows; $i++) {
for ($c = 0; $c <= $numColumns; $c++) {
$ckey = key($headers);
$val='';
if ($i==0)
$val = current($headers);
elseif (isset($rows[$i-1][$ckey]))
$val = trim($rows[$i-1][$ckey]);
if (is_array($val))
$val = json_encode($val);
$xls.= (is_int($val)
|| (ctype_digit($val) && ($val[0]!='0' && strlen($val)>1)))
? $this->xlsWriteNumber($i,$c,$val)
: $this->xlsWriteString($i,$c,utf8_decode($val));
next($headers);
}
reset($headers);
}
$xls .= $this->xlsEOF();
return $xls;
}
/**
* render xls file and send to HTTP client
* @param $rows
* @param $headers
* @param $filename
*/
function renderXLS($rows,$headers,$filename) {
$data = $this->dumpXLS($rows,$headers);
header("Expires: 0");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header('Content-Type: application/xls');
header("Content-Disposition: attachment;filename=".$filename);
header("Content-Transfer-Encoding: binary");
echo $data;
exit();
}
/**
* start file
* @return string
*/
protected function xlsBOF() {
return pack("ssssss", 0x809, 0x8, 0x0, 0x10, 0x0, 0x0);
}
/**
* end file
* @return string
*/
protected function xlsEOF() {
return pack("ss", 0x0A, 0x00);
}
/**
* put number
* @param $row
* @param $col
* @param $val
* @return string
*/
protected function xlsWriteNumber($row, $col, $val) {
$out = pack("sssss", 0x203, 14, $row, $col, 0x0);
$out.= pack("d", $val);
return $out;
}
/**
* put string
* @param $row
* @param $col
* @param $val
* @return string
*/
protected function xlsWriteString($row, $col, $val ) {
$l = strlen($val);
$out = pack("ssssss", 0x204, 8+$l, $row, $col, 0x0, $l);
$out.= $val;
return $out;
}
/**
* build and return CSV data sheet
* @param $rows
* @param $headers
* @param string $delimiter
* @param string $enclosure
* @param bool $encloseAll
* @return string
*/
public function dumpCSV($rows,$headers,$delimiter=';',$enclosure='"',$encloseAll=true) {
$numColumns = count($headers);
$numRows = count($rows);
foreach($headers as $key=>$val)
if (is_numeric($key)) {
$headers[$val]=ucfirst($val);
unset($headers[$key]);
}
$out = array();
for ($i = 0; $i <= $numRows; $i++) {
$line = array();
for ($c = 0; $c <= $numColumns; $c++) {
$ckey = key($headers);
$field='';
if ($i==0)
$field = current($headers);
elseif (isset($rows[$i-1][$ckey]))
$field = trim($rows[$i-1][$ckey]);
if (is_array($field))
$field = json_encode($field);
if (empty($field) && $field !== 0)
$line[] = '';
elseif ($encloseAll || preg_match('/(?:'.preg_quote($delimiter, '/').'|'.
preg_quote($enclosure, '/').'|\s)/', $field))
$line[] = $enclosure.str_replace($enclosure, $enclosure.$enclosure, $field).$enclosure;
else
$line[] = $field;
next($headers);
}
$out[] = implode($delimiter, $line);
reset($headers);
}
return implode("\n",$out);
}
/**
* send CSV file to client
* @param $rows
* @param $headers
* @param $filename
*/
function renderCSV($rows,$headers,$filename) {
$data = $this->dumpCSV($rows,$headers);
header("Expires: 0");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header('Content-Type: text/csv;charset=UTF-16LE');
header("Content-Disposition: attachment;filename=".$filename);
header("Content-Transfer-Encoding: binary");
echo "\xFF"."\xFE".mb_convert_encoding($data, 'UTF-16LE', 'UTF-8');
exit();
}
}

View File

@@ -11,10 +11,6 @@ use Model;
class AccessController extends Controller {
function __construct() {
parent::__construct();
}
/**
* event handler
* @param $f3

View File

@@ -58,7 +58,7 @@ class Access extends \Controller\AccessController {
$accessList = $accessModel->find( array(
"LOWER(name) LIKE :token AND " .
"active = 1 AND " .
"sharing = 1 ",
"shared = 1 ",
':token' => '%' . $searchToken . '%'
));

View File

@@ -0,0 +1,82 @@
<?php
/**
* Created by PhpStorm.
* User: exodus4d
* Date: 16.01.16
* Time: 03:34
*/
namespace Controller\Api;
use Model;
use Controller;
/**
* Github controller
* Class Route
* @package Controller\Api
*/
class GitHub extends Controller\Controller {
/**
* get HTTP request options for API (curl) request
* @return array
*/
protected function getRequestOptions(){
$requestOptions = [
'timeout' => 8,
'method' => 'GET',
'user_agent' => $this->getUserAgent(),
'follow_location' => false // otherwise CURLOPT_FOLLOWLOCATION will fail
];
return $requestOptions;
}
/**
* get release information from GitHub
* @param $f3
*/
public function releases($f3){
$cacheKey = 'CACHE_GITHUB_RELEASES';
$ttl = 60 * 30; // 30min
$releaseCount = 5;
if( !$f3->exists($cacheKey) ){
$apiPath = $this->getF3()->get('PATHFINDER.API.GIT_HUB') . '/repos/exodus4d/pathfinder/releases';
// build request URL
$options = $this->getRequestOptions();
$apiResponse = \Web::instance()->request($apiPath, $options );
if($apiResponse['body']){
// request succeeded -> format "Markdown" to "HTML"
// result is JSON formed
$releasesData = (array)json_decode($apiResponse['body']);
// check max release count
if(count($releasesData) > $releaseCount){
$releasesData = array_slice($releasesData, 0, $releaseCount);
}
$md = \Markdown::instance();
foreach($releasesData as &$releaseData){
if(isset($releaseData->body)){
$releaseData->body = $md->convert( $releaseData->body );
}
}
$f3->set($cacheKey, $releasesData, $ttl);
}else{
// request failed -> cache failed result (respect API request limit)
$f3->set($cacheKey, false, 60 * 5);
}
}
// set 503 if service unavailable or temp cached data = false
if( !$f3->get($cacheKey) ){
$f3->status(503);
}
echo json_encode($f3->get($cacheKey));
}
}

View File

@@ -318,7 +318,7 @@ class Map extends \Controller\AccessController {
if(
!$tempUser->dry() &&
$tempUser->sharing == 1 // check if map sharing is enabled
$tempUser->shared == 1 // check if map shared is enabled
){
$map->setAccess($tempUser);
}
@@ -355,7 +355,7 @@ class Map extends \Controller\AccessController {
if(
!$tempCorporation->dry() &&
$tempCorporation->sharing == 1 // check if map sharing is enabled
$tempCorporation->shared == 1 // check if map shared is enabled
){
$map->setAccess($tempCorporation);
}
@@ -393,7 +393,7 @@ class Map extends \Controller\AccessController {
if(
!$tempAlliance->dry() &&
$tempAlliance->sharing == 1 // check if map sharing is enabled
$tempAlliance->shared == 1 // check if map shared is enabled
){
$map->setAccess($tempAlliance);
}
@@ -465,7 +465,7 @@ class Map extends \Controller\AccessController {
$responseTTL = $f3->get('PATHFINDER.TIMER.UPDATE_SERVER_MAP.DELAY') / 1000;
$mapData = (array)$f3->get('POST.mapData');
$user = $this->_getUser(0);
$user = $this->_getUser();
$return = (object) [];
$return->error = [];
@@ -628,7 +628,7 @@ class Map extends \Controller\AccessController {
$return = (object) [];
$return->error = [];
$user = $this->_getUser(0);
$user = $this->_getUser();
if($user){

View File

@@ -21,7 +21,7 @@ class Route extends \Controller\AccessController {
* cache time for static jump data
* @var int
*/
private $jumpDataCacheTime = 0;
private $jumpDataCacheTime = 86400;
/**
* array system information grouped by systemId
@@ -41,19 +41,6 @@ class Route extends \Controller\AccessController {
*/
private $idArray = [];
function __construct() {
parent::__construct();
// set cache time for static jump data
$this->jumpDataCacheTime = 60 * 60 * 24;
// set static system jump data
$this->setSystemJumpData();
}
/**
* set static system jump data for this instance
* the data is fixed and should not change
@@ -61,20 +48,22 @@ class Route extends \Controller\AccessController {
private function setSystemJumpData(){
$cacheKey = 'staticJumpData';
$f3 = $this->getF3();
$cacheKeyNamedArray = $cacheKey . '.nameArray';
$cacheKeyJumpArray = $cacheKey . '.jumpArray';
$cacheKeyIdArray = $cacheKey . '.idArray';
if(
$this->f3->exists($cacheKeyNamedArray) &&
$this->f3->exists($cacheKeyJumpArray) &&
$this->f3->exists($cacheKeyIdArray)
$f3->exists($cacheKeyNamedArray) &&
$f3->exists($cacheKeyJumpArray) &&
$f3->exists($cacheKeyIdArray)
){
// get cached values
$this->nameArray = $this->f3->get($cacheKeyNamedArray);
$this->jumpArray = $this->f3->get($cacheKeyJumpArray);
$this->idArray = $this->f3->get($cacheKeyIdArray);
$this->nameArray = $f3->get($cacheKeyNamedArray);
$this->jumpArray = $f3->get($cacheKeyJumpArray);
$this->idArray = $f3->get($cacheKeyIdArray);
}else{
// nothing cached
@@ -84,27 +73,29 @@ class Route extends \Controller\AccessController {
$rows = $pfDB->exec($query, null, $this->jumpDataCacheTime);
foreach($rows as $row){
$regionId = $row['regionId'];
$constId = $row['constellationId'];
$systemName = strtoupper($row['systemName']);
$systemId = $row['systemId'];
$secStatus = $row['trueSec'];
if(count($rows) > 0){
foreach($rows as $row){
$regionId = $row['regionId'];
$constId = $row['constellationId'];
$systemName = strtoupper($row['systemName']);
$systemId = $row['systemId'];
$secStatus = $row['trueSec'];
$this->nameArray[$systemId][0] = $systemName;
$this->nameArray[$systemId][1] = $regionId;
$this->nameArray[$systemId][2] = $constId;
$this->nameArray[$systemId][3] = $secStatus;
$this->nameArray[$systemId][0] = $systemName;
$this->nameArray[$systemId][1] = $regionId;
$this->nameArray[$systemId][2] = $constId;
$this->nameArray[$systemId][3] = $secStatus;
$this->idArray[strtoupper($systemName)] = $systemId;
$this->idArray[strtoupper($systemName)] = $systemId;
$this->jumpArray[$systemName]= explode(":", strtoupper($row['jumpNodes']));
array_push($this->jumpArray[$systemName],$systemId);
$this->jumpArray[$systemName]= explode(":", strtoupper($row['jumpNodes']));
array_push($this->jumpArray[$systemName],$systemId);
}
$f3->set($cacheKeyNamedArray, $this->nameArray, $this->jumpDataCacheTime);
$f3->set($cacheKeyJumpArray, $this->jumpArray, $this->jumpDataCacheTime);
$f3->set($cacheKeyIdArray, $this->idArray, $this->jumpDataCacheTime);
}
$this->f3->set($cacheKeyNamedArray, $this->nameArray, $this->jumpDataCacheTime);
$this->f3->set($cacheKeyJumpArray, $this->jumpArray, $this->jumpDataCacheTime);
$this->f3->set($cacheKeyIdArray, $this->idArray, $this->jumpDataCacheTime);
}
}
@@ -202,6 +193,7 @@ class Route extends \Controller\AccessController {
* This function is just for setting up the cache table 'system_neighbour' which is used
* for system jump calculation. Call this function manually if CCP adds Systems/Stargates
*/
/*
private function setupSystemJumpTable(){
$pfDB = $this->getDB('PF');
@@ -269,7 +261,7 @@ class Route extends \Controller\AccessController {
}
}
}
*/
/**
* find a route between two systems (system names)
* $searchDepth for recursive route search (5000 would be best but slow)
@@ -291,6 +283,9 @@ class Route extends \Controller\AccessController {
!empty($systemFrom) &&
!empty($systemTo)
){
$this->setSystemJumpData();
$from = strtoupper( $systemFrom );
$to = strtoupper( $systemTo );
@@ -377,7 +372,14 @@ class Route extends \Controller\AccessController {
}else{
// no cached route data found
$foundRoutData = $this->findRoute($routeData['systemFrom'], $routeData['systemTo']);
$f3->set($cacheKey, $foundRoutData, $this->jumpDataCacheTime);
// cache if route was found
if(
isset($foundRoutData['routePossible']) &&
$foundRoutData['routePossible'] === true
){
$f3->set($cacheKey, $foundRoutData, $this->jumpDataCacheTime);
}
$return->routesData[] = $foundRoutData;
}

View File

@@ -48,7 +48,7 @@ class User extends Controller\Controller{
$user->updateApiData();
// route user to map app
$return->reroute = self::getEnvironmentData('URL') . $f3->alias('map');
$return->reroute = rtrim(self::getEnvironmentData('URL'), '/') . $f3->alias('map');
}
echo json_encode($return);
@@ -108,16 +108,19 @@ class User extends Controller\Controller{
){
$reason = $data['reason'];
$img = new \Image();
$im = imagecreatetruecolor(1, 1);
$colorText = imagecolorallocate($im, 102, 200, 79);
$colorBG = imagecolorallocate($im, 49, 51, 53);
$img = new \Image();
$imgDump = $img->captcha(
'fonts/oxygen-bold-webfont.ttf',
14,
6,
'SESSION.' . $reason,
'',
'0x66C84F',
'0x313335'
$colorText,
$colorBG
)->dump();
$return->img = $f3->base64( $imgDump, 'image/png');
@@ -157,8 +160,7 @@ class User extends Controller\Controller{
*/
public function logOut($f3){
$this->deleteLog($f3);
return parent::logOut($f3);
parent::logOut($f3);
}
/**
@@ -196,7 +198,7 @@ class User extends Controller\Controller{
}
}
$user->sharing = $privateSharing;
$user->shared = $privateSharing;
$user->save();
// update corp/ally ---------------------------------------------------------------
@@ -208,12 +210,12 @@ class User extends Controller\Controller{
$alliance = $activeUserCharacter->getCharacter()->getAlliance();
if(is_object($corporation)){
$corporation->sharing = $corporationSharing;
$corporation->shared = $corporationSharing;
$corporation->save();
}
if(is_object($alliance)){
$alliance->sharing = $allianceSharing;
$alliance->shared = $allianceSharing;
$alliance->save();
}
}
@@ -279,10 +281,10 @@ class User extends Controller\Controller{
}
/**
* save/update user data
* save/update user account data
* @param $f3
*/
public function saveConfig($f3){
public function saveAccount($f3){
$data = $f3->get('POST');
$return = (object) [];
@@ -298,9 +300,6 @@ class User extends Controller\Controller{
// check for new user
$loginAfterSave = false;
// send registration mail
$sendRegistrationMail = false;
// valid registration key Model is required for new registration
// if "invite" feature is enabled
$registrationKeyModel = false;
@@ -335,7 +334,6 @@ class User extends Controller\Controller{
// new user registration
$user = $mapType = Model\BasicModel::getNew('UserModel');
$loginAfterSave = true;
$sendRegistrationMail = true;
// set username
if(
@@ -366,9 +364,6 @@ class User extends Controller\Controller{
$settingsData['password'] == $settingsData['password_confirm']
){
$user->password = $settingsData['password'];
// pw changed -> send mail
$sendRegistrationMail = true;
}
}else{
// captcha was send but not valid -> return error
@@ -465,18 +460,12 @@ class User extends Controller\Controller{
$this->logUserIn( $user->name, $settingsData['password'] );
// return reroute path
$return->reroute = self::getEnvironmentData('URL') . $this->f3->alias('map');
$return->reroute = rtrim(self::getEnvironmentData('URL'), '/') . $this->f3->alias('map');
}
// get fresh updated user object
$user = $this->_getUser(0);
$newUserData = $user->getData();
// send registration mail with account information
if($sendRegistrationMail){
$this->sendRegistration($user, $settingsData['password']);
}
}
}catch(Exception\ValidationException $e){
$validationError = (object) [];
@@ -499,23 +488,6 @@ class User extends Controller\Controller{
echo json_encode($return);
}
/**
* send registration mail to user
* @param $user
* @param $password
* @return mixed
*/
protected function sendRegistration($user, $password){
$msg = 'Username: ' . $user->name . '<br>';
$msg .= 'Password: ' . $password . '<br>';
$mailController = new MailController();
$status = $mailController->sendRegistration($user->email, $msg);
return $status;
}
/**
* send mail with registration key
* -> check INVITE in pathfinder.ini

View File

@@ -16,20 +16,20 @@ class AppController extends Controller {
* @param $f3
*/
public function showLandingpage($f3) {
// page title
$f3->set('pageTitle', 'Login');
// main page content
$f3->set('pageContent','templates/view/landingpage.html');
$f3->set('pageContent', $f3->get('PATHFINDER.VIEW.LANDINGPAGE'));
// body element class
$this->f3->set('bodyClass', 'pf-body pf-landing');
$f3->set('bodyClass', 'pf-body pf-landing');
// landing page is always IGB trusted
$f3->set('trusted', 1);
// JS main file
$f3->set('jsView', 'landingpage');
$this->setTemplate('templates/view/index.html');
}
}

View File

@@ -16,22 +16,6 @@ use Model;
*/
class CcpApiController extends Controller{
/**
* get a custom userAgent string for API calls
* (recommended by CCP)
* @return string
*/
protected function getUserAgent(){
$userAgent = '';
$userAgent .= $this->f3->get('PATHFINDER.NAME');
$userAgent .= ' - ' . $this->f3->get('PATHFINDER.VERSION');
$userAgent .= ' | ' . $this->f3->get('PATHFINDER.CONTACT');
$userAgent .= ' (' . $_SERVER['SERVER_NAME'] . ')';
return $userAgent;
}
/**
* get HTTP request options for API (curl) request
* @return array
@@ -55,7 +39,7 @@ class CcpApiController extends Controller{
*/
public function requestCharacters($keyID, $vCode){
$apiPath = $this->f3->get('PATHFINDER.API.CCP_XML') . '/account/APIKeyInfo.xml.aspx';
$apiPath = $this->getF3()->get('PATHFINDER.API.CCP_XML') . '/account/APIKeyInfo.xml.aspx';
$xml = false;

View File

@@ -15,14 +15,6 @@ class Controller {
protected $f3;
private $template;
function __construct(){
$this->f3 = \Base::instance();
// initiate DB connection
DB\Database::instance('PF');
}
/**
* @param mixed $template
*/
@@ -37,6 +29,20 @@ class Controller {
return $this->template;
}
/**
* set global f3 instance
* @param null $f3
* @return null|static
*/
protected function getF3($f3 = null){
if(is_object($f3)){
$this->f3 = $f3;
}else{
$this->f3 = \Base::instance();
}
return $this->f3;
}
/**
* event handler for all "views"
@@ -44,24 +50,35 @@ class Controller {
* @param $f3
*/
function beforeroute($f3) {
$this->getF3($f3);
// initiate DB connection
DB\Database::instance('PF');
// init user session
$this->initSession();
// check if user is in game
$f3->set('isIngame', self::isIGB() );
if( !$f3->get('AJAX') ){
// set page parameters for static page render
// check if user is in game (IGB active)
$f3->set('isIngame', self::isIGB() );
// js path (build/minified or raw uncompressed files)
$f3->set('pathJs', 'public/js/' . $f3->get('PATHFINDER.VERSION') );
// js path (build/minified or raw uncompressed files)
$f3->set('pathJs', 'public/js/' . $f3->get('PATHFINDER.VERSION') );
$this->setTemplate( $f3->get('PATHFINDER.VIEW.INDEX') );
}
}
/**
* event handler
* event handler after routing
* -> render view
*/
function afterroute() {
if($this->template){
echo \Template::instance()->render( $this->template );
public function afterroute($f3){
if($this->getTemplate()){
// Ajax calls don´t need a page render..
// this happens on client side
echo \Template::instance()->render( $this->getTemplate() );
}
}
@@ -79,7 +96,9 @@ class Controller {
*/
protected function initSession(){
// init DB Session (not file based)
new \DB\SQL\Session($this->getDB('PF'));
if( $this->getDB('PF') instanceof \DB\SQL){
new \DB\SQL\Session($this->getDB('PF'));
}
}
/**
@@ -95,7 +114,7 @@ class Controller {
$userId = (int)$this->f3->get('SESSION.user.id');
if($userId > 0){
$userModel = Model\BasicModel::getNew('UserModel');
$userModel = Model\BasicModel::getNew('UserModel', $ttl);
$userModel->getById($userId, $ttl);
if( !$userModel->dry() ){
@@ -120,9 +139,17 @@ class Controller {
// redirect to landing page
$f3->reroute('@landing');
}else{
$params = $f3->get('POST');
$return = (object) [];
$return->reroute = self::getEnvironmentData('URL') . $f3->alias('landing');
$return->error[] = $this->getUserLoggedOffError();
if(
isset($params['reroute']) &&
(bool)$params['reroute']
){
$return->reroute = rtrim(self::getEnvironmentData('URL'), '/') . $f3->alias('landing');
}else{
// no reroute -> errors can be shown
$return->error[] = $this->getUserLoggedOffError();
}
echo json_encode($return);
die();
@@ -208,11 +235,16 @@ class Controller {
static function getRequestHeaders(){
$headers = [];
if(function_exists('apache_request_headers') ){
$serverData = self::getServerData();
if(
function_exists('apache_request_headers') &&
$serverData->type === 'apache'
){
// Apache Webserver
$headers = apache_request_headers();
}else{
// Other webserver, e.g. nginx
// Other webserver, e.g. Nginx
// Unfortunately this "fallback" does not work for me (Apache)
// Therefore we can´t use this for all servers
// https://github.com/exodus4d/pathfinder/issues/58
@@ -222,10 +254,54 @@ class Controller {
}
}
}
return $headers;
}
/**
* get some server information
* @param int $ttl cache time (default: 1h)
* @return object
*/
static function getServerData($ttl = 3600){
$f3 = \Base::instance();
$cacheKey = 'PF_SERVER_INFO';
if( !$f3->exists($cacheKey) ){
$serverData = (object) [];
$serverData->type = 'unknown';
$serverData->version = 'unknown';
$serverData->requiredVersion = 'unknown';
$serverData->phpInterfaceType = php_sapi_name();
if(strpos(strtolower($_SERVER['SERVER_SOFTWARE']), 'nginx' ) !== false){
// Nginx server
$serverSoftwareArgs = explode('/', strtolower( $_SERVER['SERVER_SOFTWARE']) );
$serverData->type = reset($serverSoftwareArgs);
$serverData->version = end($serverSoftwareArgs);
$serverData->requiredVersion = $f3->get('REQUIREMENTS.SERVER.NGINX.VERSION');
}elseif(strpos(strtolower($_SERVER['SERVER_SOFTWARE']), 'apache' ) !== false){
// Apache server
$serverData->type = 'apache';
$serverData->requiredVersion = $f3->get('REQUIREMENTS.SERVER.APACHE.VERSION');
// try to get the apache version...
if(function_exists('apache_get_version')){
// function does not exists if PHP is running as CGI/FPM module!
$matches = preg_split('/[\s,\/ ]+/', strtolower( apache_get_version() ) );
if(count($matches) > 1){
$serverData->version = $matches[1];
}
}
}
// cache data for one day
$f3->set($cacheKey, $serverData, $ttl);
}
return $f3->get($cacheKey);
}
/**
* check if the current request was send from inGame
* @return bool
@@ -242,7 +318,6 @@ class Controller {
return $isIGB;
}
/**
* get error object is a user is not found/logged of
* @return object
@@ -290,72 +365,155 @@ class Controller {
*/
static function getEnvironmentData($key){
$f3 = \Base::instance();
$environment = $f3->get('PATHFINDER.ENVIRONMENT.SERVER');
$environmentKey = 'PATHFINDER.ENVIRONMENT[' . $environment . '][' . $key . ']';
$environment = self::getEnvironment();
$environmentKey = 'ENVIRONMENT[' . $environment . '][' . $key . ']';
$data = null;
if( $f3->exists($environmentKey) ){
$data = $f3->get($environmentKey);
}
return $data;
}
/**
* get current server environment status
* -> "DEVELOP" or "PRODUCTION"
* @return mixed
*/
static function getEnvironment(){
$f3 = \Base::instance();
return $f3->get('ENVIRONMENT.SERVER');
}
/**
* function is called on each error
* check if current server is "PRODUCTION"
* @return bool
*/
static function isProduction(){
return self::getEnvironment() == 'PRODUCTION';
}
/**
* get required MySQL variable value
* @param $key
* @return mixed|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;
}
/**
* get a program URL by alias
* -> if no $alias given -> get "default" route (index.php)
* @param null $alias
* @return bool
*/
protected function getRouteUrl($alias = null){
$url = false;
if(!empty($alias)){
// check given alias is a valid (registered) route
if(array_key_exists($alias, $this->getF3()->get('ALIASES'))){
$url = $this->getF3()->alias($alias);
}
}elseif($this->getF3()->get('ALIAS')){
// get current URL
$url = $this->getF3()->alias( $this->getF3()->get('ALIAS') );
}else{
// get main (index.php) URL
$url = $this->getF3()->alias('landing');
}
return $url;
}
/**
* get a custom userAgent string for API calls
* @return string
*/
protected function getUserAgent(){
$userAgent = '';
$userAgent .= $this->getF3()->get('PATHFINDER.NAME');
$userAgent .= ' - ' . $this->getF3()->get('PATHFINDER.VERSION');
$userAgent .= ' | ' . $this->getF3()->get('PATHFINDER.CONTACT');
$userAgent .= ' (' . $_SERVER['SERVER_NAME'] . ')';
return $userAgent;
}
/**
* onError() callback function
* -> on AJAX request -> return JSON with error information
* -> on HTTP request -> render error page
* @param $f3
*/
public function showError($f3){
// set HTTP status
$errorCode = $f3->get('ERROR.code');
if(!empty($errorCode)){
$f3->status($errorCode);
}
if($f3->get('AJAX')){
// error on ajax call
header('Content-type: application/json');
// 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');
$return = (object) [];
$error = (object) [];
$error->type = 'error';
$error->code = $errorCode;
$error->status = $f3->get('ERROR.status');
$error->message = $f3->get('ERROR.text');
// 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;
echo json_encode($return);
}else{
echo $f3->get('ERROR.text');
// append stack trace for greater debug level
if( $f3->get('DEBUG') === 3){
$error->trace = $f3->get('ERROR.trace');
}
die();
// 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{
// 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('pageContent', $f3->get('PATHFINDER.STATUS.4XX'));
}elseif( preg_match('/^5[0-9]{2}$/', $error->code) ){
$f3->set('pageContent', $f3->get('PATHFINDER.STATUS.5XX'));
}
echo \Template::instance()->render( $f3->get('PATHFINDER.VIEW.INDEX') );
die();
}
}
/**
* Callback for framework "unload"
* -> config.ini
* check -> config.ini
*/
public function unload(){
public function unload($f3){
return true;
}
}

View File

@@ -27,21 +27,6 @@ class MailController extends \SMTP{
$this->set('Content-Type', 'text/html; charset=ISO-8859-1');
}
/**
* send registration mail
* @param $to
* @param $msg
* @return bool
*/
public function sendRegistration($to, $msg){
$this->set('To', '<' . $to . '>');
$this->set('From', 'Pathfinder <' . Controller::getEnvironmentData('SMTP_FROM') . '>');
$this->set('Subject', 'Account information');
$status = $this->send($msg);
return $status;
}
/**
* send invite key mail
* @param $to

View File

@@ -10,25 +10,22 @@ namespace Controller;
class MapController extends \Controller\AccessController {
function __construct() {
parent::__construct();
}
public function showMap($f3) {
// page title
$f3->set('pageTitle', 'Maps');
// main page content
$f3->set('pageContent', false);
// body element class
$this->f3->set('bodyClass', 'pf-body');
$f3->set('bodyClass', 'pf-body');
// set trust attribute to template
$this->f3->set('trusted', (int)self::isIGBTrusted());
$f3->set('trusted', (int)self::isIGBTrusted());
// JS main file
$this->f3->set('jsView', 'mappage');
$this->setTemplate('templates/view/index.html');
$f3->set('jsView', 'mappage');
}
}

View File

@@ -0,0 +1,639 @@
<?php
/**
* Created by PhpStorm.
* User: Exodus
* Date: 22.11.2015
* Time: 10:59
*/
namespace Controller;
use DB;
use DB\SQL;
use DB\SQL\MySQL as MySQL;
use Model;
class Setup extends Controller {
protected $databases = [
'PF' => [
'info' => [],
'models' => [
'Model\UserModel',
'Model\AllianceModel',
'Model\CorporationModel',
'Model\MapModel',
'Model\MapScopeModel',
'Model\MapTypeModel',
'Model\SystemTypeModel',
'Model\SystemStatusModel',
'Model\SystemNeighbourModel',
'Model\WormholeModel',
'Model\RegistrationKeyModel',
'Model\CharacterStatusModel',
'Model\ConnectionScopeModel',
'Model\UserMapModel',
'Model\AllianceMapModel',
'Model\CorporationMapModel',
'Model\UserApiModel',
'Model\UserCharacterModel',
'Model\CharacterModel',
'Model\CharacterLogModel',
'Model\SystemModel',
'Model\SystemWormholeModel',
'Model\ConnectionModel',
'Model\SystemSignatureModel',
'Model\SystemShipKillModel',
'Model\SystemPodKillModel',
'Model\SystemFactionKillModel',
'Model\SystemJumpModel'
],
'tables' => []
],
'CCP' => [
'info' => [],
'models' => [],
'tables' => [
'invTypes',
'mapConstellations',
'mapDenormalize',
'mapLocationWormholeClasses',
'mapRegions',
'mapSolarSystemJumps',
'mapSolarSystems'
]
]
];
/**
* event handler for all "views"
* some global template variables are set in here
* @param $f3
*/
function beforeroute($f3) {
// page title
$f3->set('pageTitle', 'Setup');
// main page content
$f3->set('pageContent', $f3->get('PATHFINDER.VIEW.SETUP'));
// body element class
$f3->set('bodyClass', 'pf-body pf-landing');
// js path (build/minified or raw uncompressed files)
$f3->set('pathJs', 'public/js/' . $f3->get('PATHFINDER.VERSION') );
}
public function afterroute($f3) {
// js view (file)
$f3->set('jsView', 'setup');
// render view
echo \Template::instance()->render( $f3->get('PATHFINDER.VIEW.INDEX') );
}
/**
* main setup route handler
* works as dispatcher for setup functions
* @param $f3
*/
public function init($f3){
$params = $f3->get('GET');
// enables automatic column fix
$fixColumns = false;
// bootstrap database from model class definition
if(
isset($params['db']) &&
!empty($params['db'])
){
$this->bootstrapDB($params['db']);
// reload page
// -> remove GET param
$f3->reroute('@setup');
return;
}elseif(
isset($params['fixCols']) &&
!empty($params['fixCols'])
){
$fixColumns = true;
}
// set server information for page render
$f3->set('serverInformation', $this->getServerInformation($f3));
// set requirement check information for page render
$f3->set('checkRequirements', $this->checkRequirements($f3));
// set database connection information for page render
$f3->set('checkDatabase', $this->checkDatabase($f3, $fixColumns));
}
/**
* get server information
* @param $f3
* @return array
*/
protected function getServerInformation($f3){
$serverInfo = [
'time' => [
'label' => 'Time',
'value' => date('Y/m/d H:i:s') . ' - (' . date_default_timezone_get() . ')'
],
'os' => [
'label' => 'OS',
'value' => php_uname('s')
],
'name' => [
'label' => 'Host name',
'value' => php_uname('n')
],
'release' => [
'label' => 'Release name',
'value' => php_uname('r')
],
'version' => [
'label' => 'Version info',
'value' => php_uname('v')
],
'machine' => [
'label' => 'Machine type',
'value' => php_uname('m')
]
];
return $serverInfo;
}
/**
* check all required backend requirements
* (Fat Free Framework)
* @param $f3
* @return array
*/
protected function checkRequirements($f3){
// server type ------------------------------------------------------------------
$serverData = self::getServerData(0);
$checkRequirements = [
'serverType' => [
'label' => 'Server type',
'version' => $serverData->type,
'check' => true
],
'serverVersion' => [
'label' => 'Server version',
'required' => $serverData->requiredVersion,
'version' => $serverData->version,
'check' => version_compare( $serverData->version, $serverData->requiredVersion, '>='),
'tooltip' => 'If not specified, please check your \'ServerTokens\' server config. (not critical)'
],
'phpInterface' => [
'label' => 'PHP interface type',
'version' => $serverData->phpInterfaceType,
'check' => empty($serverData->phpInterfaceType) ? false : true
],
'php' => [
'label' => 'PHP',
'required' => $f3->get('REQUIREMENTS.PHP.VERSION'),
'version' => phpversion(),
'check' => version_compare( phpversion(), $f3->get('REQUIREMENTS.PHP.VERSION'), '>=')
],
'pcre' => [
'label' => 'PCRE',
'required' => $f3->get('REQUIREMENTS.PHP.PCRE_VERSION'),
'version' => strstr(PCRE_VERSION, ' ', true),
'check' => version_compare( strstr(PCRE_VERSION, ' ', true), $f3->get('REQUIREMENTS.PHP.PCRE_VERSION'), '>=')
],
'gd' => [
'label' => 'GD Library (for Image plugin)',
'required' => 'installed',
'version' => (extension_loaded('gd') && function_exists('gd_info')) ? 'installed' : 'not installed',
'check' => (extension_loaded('gd') && function_exists('gd_info'))
],
'curl' => [
'label' => 'cURL (for Web plugin)',
'required' => 'installed',
'version' => (extension_loaded('curl') && function_exists('curl_version')) ? 'installed' : 'not installed',
'check' => (extension_loaded('curl') && function_exists('curl_version'))
]
];
if($serverData->type != 'nginx'){
// default msg if module status not available
$modNotFoundMsg = 'Module status can not be identified. '
. 'This can happen if PHP runs as \'FastCGI\'. Please check manual! ';
// mod_rewrite check ------------------------------------------------------------
$modRewriteCheck = false;
$modRewriteVersion = 'disabled';
$modRewriteTooltip = false;
if(function_exists('apache_get_modules')){
if(in_array('mod_rewrite',apache_get_modules())){
$modRewriteCheck = true;
$modRewriteVersion = 'enabled';
}
}else{
// e.g. Nginx server
$modRewriteVersion = 'unknown';
$modRewriteTooltip = $modNotFoundMsg;
}
$checkRequirements['mod_rewrite'] = [
'label' => 'mod_rewrite',
'required' => 'enabled',
'version' => $modRewriteVersion,
'check' => $modRewriteCheck,
'tooltip' => $modRewriteTooltip
];
// mod_headers check ------------------------------------------------------------
$modHeadersCheck = false;
$modHeadersVersion = 'disabled';
$modHeadersTooltip = false;
if(function_exists('apache_get_modules')){
if(in_array('mod_headers',apache_get_modules())){
$modHeadersCheck = true;
$modHeadersVersion = 'enabled';
}
}else{
// e.g. Nginx server
$modHeadersVersion = 'unknown';
$modHeadersTooltip = $modNotFoundMsg;
}
$checkRequirements['mod_headers'] = [
'label' => 'mod_headers',
'required' => 'enabled',
'version' => $modHeadersVersion,
'check' => $modHeadersCheck,
'tooltip' => $modHeadersTooltip
];
}
return $checkRequirements;
}
/**
* get database connection information
* @param $f3
* @param bool|false $exec
* @return array
*/
protected function checkDatabase($f3, $exec = false){
foreach($this->databases as $dbKey => $dbData){
$dbLabel = '';
$dbName = '';
$dbUser = '';
$dbConfig = [];
// DB connection status
$dbConnected = false;
// DB type (e.g. MySql,..)
$dbDriver = 'unknown';
// enable database ::setup() function in UI
$dbSetupEnable = false;
// check of everything is OK (connection, tables, columns, indexes,..)
$dbStatusCheckCount = 0;
// db queries for column fixes (types, indexes, unique)
$dbColumnQueries = [];
// tables that should exist in this DB
$requiredTables = [];
// check DB for valid connection
$db = DB\Database::instance()->getDB($dbKey);
switch($dbKey){
case 'PF':
$dbLabel = 'Pathfinder';
$dbName = Controller::getEnvironmentData('DB_NAME');
$dbUser = Controller::getEnvironmentData('DB_USER');
// enable (table) setup for this DB
$dbSetupEnable = true;
// get table data from model
foreach($dbData['models'] as $model){
$tableConfig = call_user_func($model . '::resolveConfiguration');
$requiredTables[$tableConfig['table']] = [
'model' => $model,
'name' => $tableConfig['table'],
'fieldConf' => $tableConfig['fieldConf'],
'exists' => false,
'empty' => true,
'foreignKeys' => []
];
}
break;
case 'CCP':
$dbLabel = 'EVE-Online [SDE]';
$dbName = Controller::getEnvironmentData('DB_CCP_NAME');
$dbUser = Controller::getEnvironmentData('DB_CCP_USER');
// get table model from static table array
foreach($dbData['tables'] as $tableName){
$requiredTables[$tableName] = [
'exists' => false,
'empty' => true
];
}
break;
}
if($db){
// db connect was successful
$dbConnected = true;
$dbDriver = $db->driver();
$dbConfig = $this->checkDBConfig($f3, $db);
// get tables
$schema = new SQL\Schema($db);
$currentTables = $schema->getTables();
// check each table for changes
foreach($requiredTables as $requiredTableName => $data){
$tableExists = false;
$tableEmpty = true;
// Check if table status is OK (no errors/warnings,..)
$tableStatusCheckCount = 0;
$currentColumns = [];
if(in_array($requiredTableName, $currentTables)){
// Table exists
$tableExists = true;
// get existing table columns and column related constraints (if exists)
$tableModifierTemp = new MySQL\TableModifier($requiredTableName, $schema);
$currentColumns = $tableModifierTemp->getCols(true);
// get row count
$countRes = $db->exec("SELECT COUNT(*) `num` FROM " . $db->quotekey($requiredTableName) );
$tableEmpty = $countRes[0]['num'] > 0 ? false : true;
}else{
// table missing
$dbStatusCheckCount++;
$tableStatusCheckCount++;
}
foreach((array)$data['fieldConf'] as $columnName => $fieldConf){
$columnStatusCheck = true;
$foreignKeyStatusCheck = true;
$requiredTables[$requiredTableName]['fieldConf'][$columnName]['requiredType'] = $fieldConf['type'];
$requiredTables[$requiredTableName]['fieldConf'][$columnName]['requiredIndex'] = ($fieldConf['index']) ? '1' : '0';
$requiredTables[$requiredTableName]['fieldConf'][$columnName]['requiredUnique'] = ($fieldConf['unique']) ? '1' : '0';
if(array_key_exists($columnName, $currentColumns)){
// column exists
// get tableModifier -> possible column update
$tableModifier = new MySQL\TableModifier($requiredTableName, $schema);
// get new column and copy Schema from existing column
$col = new MySQL\Column($columnName, $tableModifier);
$col->copyfrom($currentColumns[$columnName]);
$currentColType = $currentColumns[$columnName]['type'];
$currentColIndexData = call_user_func($data['model'] . '::indexExists', [$columnName]);
$currentColIndex = is_array($currentColIndexData);
$hasIndex = ($currentColIndex) ? '1' : '0';
$hasUnique = ($currentColIndexData['unique']) ? '1' : '0';
$changedType = false;
$changedUnique = false;
$changedIndex = false;
// set (new) column information -------------------------------------------------------
$requiredTables[$requiredTableName]['fieldConf'][$columnName]['exists'] = true;
$requiredTables[$requiredTableName]['fieldConf'][$columnName]['currentType'] = $currentColType;
$requiredTables[$requiredTableName]['fieldConf'][$columnName]['currentIndex'] = $hasIndex;
$requiredTables[$requiredTableName]['fieldConf'][$columnName]['currentUnique'] = $hasUnique;
// check constraint -------------------------------------------------------------------
if(isset($fieldConf['constraint'])){
// add or update constraints
foreach((array)$fieldConf['constraint'] as $constraintData){
$constraint = $col->newConstraint($constraintData);
$foreignKeyExists = $col->constraintExists($constraint);
$requiredTables[$requiredTableName]['foreignKeys'][] = [
'exists' => $foreignKeyExists,
'keyName' => $constraint->getConstraintName()
];
$col->addConstraint($constraint);
if(!$foreignKeyExists){
$tableStatusCheckCount++;
$foreignKeyStatusCheck = false;
}
}
}
// check type changed -----------------------------------------------------------------
if(
$fieldConf['type'] !== 'JSON' &&
!$schema->isCompatible($fieldConf['type'], $currentColType)
){
// column type has changed
$changedType = true;
$columnStatusCheck = false;
$tableStatusCheckCount++;
}
// check if column unique changed -----------------------------------------------------
$indexUpdate = false;
$indexKey = (bool)$hasIndex;
$indexUnique = (bool)$hasUnique;
if($currentColIndexData['unique'] != $fieldConf['unique']){
$changedUnique = true;
$columnStatusCheck = false;
$tableStatusCheckCount++;
$indexUpdate = true;
$indexUnique =(bool)$fieldConf['unique'];
}
// check if column index changed ------------------------------------------------------
if($currentColIndex != $fieldConf['index']){
$changedIndex = true;
$columnStatusCheck = false;
$tableStatusCheckCount++;
$indexUpdate = true;
$indexKey = (bool) $fieldConf['index'];
}
// build table with changed columns ---------------------------------------------------
if(!$columnStatusCheck || !$foreignKeyStatusCheck){
if(!$columnStatusCheck ){
// IMPORTANT: setType is always required! Even if type has not changed
$col->type($fieldConf['type']);
// update/change/delete index/unique keys
if($indexUpdate){
if($hasIndex){
$tableModifier->dropIndex($columnName);
}
if($indexKey){
$tableModifier->addIndex($columnName, $indexUnique);
}
}
$tableModifier->updateColumn($columnName, $col);
}
$buildStatus = $tableModifier->build($exec);
if(
is_array($buildStatus) ||
is_string($buildStatus)
){
// query strings for change available
$dbColumnQueries = array_merge($dbColumnQueries, (array)$buildStatus);
}
}
// set (new) column information -------------------------------------------------------
$requiredTables[$requiredTableName]['fieldConf'][$columnName]['changedType'] = $changedType;
$requiredTables[$requiredTableName]['fieldConf'][$columnName]['changedUnique'] = $changedUnique;
$requiredTables[$requiredTableName]['fieldConf'][$columnName]['changedIndex'] = $changedIndex;
}elseif(
!isset($fieldConf['has-manny']) &&
isset($fieldConf['type'])
){
// column not exists but it is required!
// columns that do not match this criteria ("mas-manny") are "virtual" fields
// and can be ignored
$requiredTables[$requiredTableName]['fieldConf'][$columnName]['currentType'] = '';
$columnStatusCheck = false;
$tableStatusCheckCount++;
}
$requiredTables[$requiredTableName]['fieldConf'][$columnName]['statusCheck'] = $columnStatusCheck;
}
$dbStatusCheckCount += $tableStatusCheckCount;
$requiredTables[$requiredTableName]['empty'] = $tableEmpty;
$requiredTables[$requiredTableName]['exists'] = $tableExists;
$requiredTables[$requiredTableName]['statusCheckCount'] = $tableStatusCheckCount;
}
}else{
// DB connection failed
$dbStatusCheckCount++;
}
if($exec){
$f3->reroute('@setup');
}
$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
];
}
return $this->databases;
}
/** check MySQL params
* @param $f3
* @param $db
* @return array
*/
protected function checkDBConfig($f3, $db){
// some db like "Maria DB" have some strange version strings....
$dbVersionString = $db->version();
$dbVersionParts = explode('-', $dbVersionString);
$dbVersion = 'unknown';
foreach($dbVersionParts as $dbVersionPart){
// check if this is a valid version number
// hint: MariaDB´s version is always the last valid version number...
if( version_compare( $dbVersionPart, '0.0.1', '>=' ) > 0 ){
$dbVersion = $dbVersionPart;
}
}
$dbConfig = [
'version' => [
'label' => 'DB version',
'required' => $f3->get('REQUIREMENTS.MYSQL.VERSION'),
'version' => $dbVersion,
'check' => version_compare($dbVersion, $f3->get('REQUIREMENTS.MYSQL.VERSION'), '>=' )
]
];
// get specific MySQL config Value
$getDBConfigValue = function($db, $param){
$result = $db->exec([
//"USE " . $db->name(),
"SHOW VARIABLES LIKE '" . strtolower($param) . "'"
]);
$tmpResult = reset($result);
return !empty($result)? end($tmpResult) : 'unknown';
};
$mySQLConfigParams = $f3->get('REQUIREMENTS.MYSQL.VARS');
foreach($mySQLConfigParams as $param => $requiredValue){
$value = $getDBConfigValue($db, $param);
$dbConfig[] = [
'label' => strtolower($param),
'required' => $requiredValue,
'version' => $value,
'check' => !empty($requiredValue) ? ($requiredValue == $value) : true
];
}
return $dbConfig;
}
/**
* init the complete database
* - create tables
* - create indexes
* - set default static values
* @param $dbKey
* @return array
*/
protected function bootstrapDB($dbKey){
$db = DB\Database::instance()->getDB($dbKey);
$checkTables = [];
if($db){
// set/change default "character set" and "collation"
$db->exec('ALTER DATABASE ' . $db->name()
. ' CHARACTER SET ' . self::getRequiredMySqlVariables('CHARACTER_SET_DATABASE')
. ' COLLATE ' . self::getRequiredMySqlVariables('COLLATION_DATABASE')
);
// setup tables
foreach($this->databases[$dbKey]['models'] as $modelClass){
$checkTables[] = call_user_func($modelClass . '::setup');
}
}
return $checkTables;
}
}

View File

@@ -145,6 +145,7 @@ class CcpSystemsUpdate {
$sql = "UPDATE
" . $tableName . "
SET
updated = now(),
value24 = value23,
value23 = value22,
value22 = value21,

View File

@@ -30,36 +30,35 @@ class Database extends \Prefab {
// "Hive" Key for DB storage
$dbHiveKey = $this->getDbHiveKey($database);
if($database === 'CCP'){
// CCP DB
$dns = Controller\Controller::getEnvironmentData('DB_CCP_DNS');
$name = Controller\Controller::getEnvironmentData('DB_CCP_NAME');
$user = Controller\Controller::getEnvironmentData('DB_CCP_USER');
$password = Controller\Controller::getEnvironmentData('DB_CCP_PASS');
}else{
// Pathfinder(PF) DB
$dns = Controller\Controller::getEnvironmentData('DB_DNS');
$name = Controller\Controller::getEnvironmentData('DB_NAME');
$user = Controller\Controller::getEnvironmentData('DB_USER');
$password = Controller\Controller::getEnvironmentData('DB_PASS');
}
// check if DB connection already exists
if(
!$f3->exists( $dbHiveKey ) ||
$name !== $f3->get( $dbHiveKey )->name()
){
if( !$f3->exists( $dbHiveKey ) ){
if($database === 'CCP'){
// CCP DB
$dns = Controller\Controller::getEnvironmentData('DB_CCP_DNS');
$name = Controller\Controller::getEnvironmentData('DB_CCP_NAME');
$user = Controller\Controller::getEnvironmentData('DB_CCP_USER');
$password = Controller\Controller::getEnvironmentData('DB_CCP_PASS');
}else{
// Pathfinder(PF) DB
$dns = Controller\Controller::getEnvironmentData('DB_DNS');
$name = Controller\Controller::getEnvironmentData('DB_NAME');
$user = Controller\Controller::getEnvironmentData('DB_USER');
$password = Controller\Controller::getEnvironmentData('DB_PASS');
}
$db = $this->connect($dns, $name, $user, $password);
// set DB timezone to UTC +00:00 (eve server time)
$db->exec('SET @@session.time_zone = "+00:00";');
if( !is_null($db) ){
// set DB timezone to UTC +00:00 (eve server time)
$db->exec('SET @@session.time_zone = "+00:00";');
// disable innoDB schema (relevant vor MySql 5.5)
// not necessary for MySql > v.5.6
// $db->exec('SET GLOBAL innodb_stats_on_metadata = OFF;');
// set default storage engine
$db->exec('SET @@session.default_storage_engine = "' .
Controller\Controller::getRequiredMySqlVariables('DEFAULT_STORAGE_ENGINE') . '"');
// store DB object
$f3->set($dbHiveKey, $db);
// store DB object
$f3->set($dbHiveKey, $db);
}
return $db;
}else{
@@ -104,6 +103,8 @@ class Database extends \Prefab {
*/
protected function connect($dns, $name, $user, $password){
$db = null;
try {
$db = new SQL(
$dns . $name,
@@ -115,6 +116,7 @@ class Database extends \Prefab {
);
}catch(\PDOException $e){
// DB connection error
// -> log it
LogController::getLogger('error')->write($e->getMessage());
}

View File

@@ -0,0 +1,333 @@
<?php
/**
* Created by PhpStorm.
* User: Exodus
* Date: 19.12.2015
* Time: 12:47
*
* This is an "on top" extension for "Schema Builder"
* see: https://github.com/ikkez/f3-schema-builder
*
* Features:
* - FOREIGN KEY CONSTRAINTS (single column key)
*/
namespace DB\SQL\MySQL;
use DB\SQL;
class TableModifier extends SQL\TableModifier {
const TEXT_ConstraintNotValid = 'Constraint `%s` is not valid';
/**
* return table foreign key constraints as assoc array
* -> if §constraint is passed, constraints are limited to that column
* @param null| \DB\SQL\MySQL\Constraint $constraint
* @return Constraint[]
*/
public function listConstraint($constraint = null){
$constraintName = '%';
$keys = [];
if($constraint instanceof Constraint){
// list constraints for given column in this table
$constraintName = $constraint->getConstraintName() . '%';
$keys = $constraint->getKeys();
}
$this->db->exec("USE information_schema");
$constraintsData = $this->db->exec("
SELECT
*
FROM
referential_constraints
WHERE
constraint_schema = :db AND
table_name = :table AND
constraint_name LIKE :constraint_name
", [
':db' => $this->db->name(),
':table' => $this->name,
':constraint_name' => $constraintName
]);
// switch back to current DB
$this->db->exec("USE " . $this->db->name());
$constraints = [];
foreach($constraintsData as $data){
$constraints[$data['CONSTRAINT_NAME']] = new Constraint($this, $keys, $data['REFERENCED_TABLE_NAME'] );
}
return $constraints;
}
/**
* checks whether a constraint name exists or not
* -> does not check constraint params
* @param \DB\SQL\MySQL\Constraint $constraint
* @return bool
*/
public function constraintExists($constraint){
$constraints = $this->listConstraint();
return array_key_exists($constraint->getConstraintName(), $constraints);
}
/**
* drop foreign key constraint
* @param \DB\SQL\MySQL\Constraint $constraint
*/
public function dropConstraint($constraint){
if($constraint->isValid()){
$this->queries[] = "ALTER TABLE " . $this->db->quotekey($this->name) . "
DROP FOREIGN KEY " . $this->db->quotekey($constraint->getConstraintName());
}else{
trigger_error(sprintf(self::TEXT_ConstraintNotValid, 'table: ' . $this->name . ' constraintName: ' . $constraint->getConstraintName()));
}
}
/**
* Add/Update foreign key constraint
* @param \DB\SQL\MySQL\Constraint $constraint
*/
public function addConstraint($constraint){
if($constraint->isValid()){
if($this->constraintExists($constraint)){
// drop constraint and re-add in case something has changed
$this->dropConstraint($constraint);
}
$this->queries[] = "
ALTER TABLE " . $this->db->quotekey($this->name) . "
ADD CONSTRAINT " . $this->db->quotekey($constraint->getConstraintName()) . "
FOREIGN KEY (" . implode(', ', $constraint->getKeys()) . ")
REFERENCES " . $this->db->quotekey($constraint->getReferencedTable()) . " (" . implode(', ', $constraint->getReferencedCols()) . ")
ON DELETE " . $constraint->getOnDelete() . "
ON UPDATE " . $constraint->getOnUpdate();
}else{
trigger_error(sprintf(self::TEXT_ConstraintNotValid, 'table: ' . $this->name . ' constraintName: ' . $constraint->getConstraintName()));
}
}
}
/**
* Class Column
* @package DB\SQL\MySQL
*/
class Column extends SQL\Column {
const TEXT_TableNameMissing = 'Table name missing for FOREIGN KEY in `%s`';
/**
* ass constraint to this column
* @param Constraint $constraint
*/
public function addConstraint(Constraint $constraint){
$this->table->addConstraint($constraint);
}
/**
* @see \DB\SQL\MySQL\TableModifier->constraintExists();
* @param Constraint $constraint
* @return mixed
*/
public function constraintExists(Constraint $constraint){
return $this->table->constraintExists($constraint);
}
/**
* get a new column based constraint
* $constraintData['table'] => referenceTable name (required)
* $constraintData['id'] => referenceColumns (optional) default: ['id']
* $constraintData['on-delete'] => ON DELETE action (optional) default: see \DB\SQL\MySQL\Constraint const
* $constraintData['on-update'] => ON UPDATE action (optional) default: see \DB\SQL\MySQL\Constraint const
*
* @param array $constraintData
* @return \DB\SQL\MySQL\Constraint
*/
public function newConstraint($constraintData){
$constraint = null;
if(isset($constraintData['table'])){
if(isset($constraintData['column'])){
$constraintData['column'] = (array)$constraintData['column'];
}else{
$constraintData['column'] = ['id'];
}
$constraint = new Constraint($this->table, $this->name, $constraintData['table'], $constraintData['column']);
if(isset($constraintData['on-delete'])){
$constraint->setOnDelete($constraintData['on-delete']);
}
if(isset($constraintData['on-update'])){
$constraint->setOnUpdate($constraintData['on-update']);
}
}else{
trigger_error(sprintf(self::TEXT_TableNameMissing, $this->table->name . '->' . $this->name));
}
return $constraint;
}
}
class Constraint {
// available actions
const ACTIONS_DELETE = ['RESTRICT', 'CASCADE', 'SET NULL', 'NO ACTION'];
const ACTIONS_UPDATE = ['RESTRICT', 'CASCADE', 'SET NULL', 'NO ACTION'];
// default actions
const ACTION_DELETE = 'RESTRICT';
const ACTION_UPDATE = 'RESTRICT';
const TEXT_ActionNotSupported = 'Constraint action `%s` is not supported.';
protected $table;
protected $keys = [];
protected $referencedTable = '';
protected $referencedCols = [];
protected $onDelete = self::ACTION_DELETE;
protected $onUpdate = self::ACTION_UPDATE;
/**
* Constraint constructor.
* @param SQL\TableBuilder $table
* @param array $keys
* @param string $referencedTable
* @param array $referencedCols
*/
public function __construct(SQL\TableBuilder $table, $keys = [], $referencedTable = '', $referencedCols = ['id']){
$this->table = &$table;
$this->setKeys($keys);
$this->setReferencedTable($referencedTable);
$this->setReferencedCols($referencedCols);
}
/**
* @param mixed $keys
*/
public function setKeys($keys){
$this->keys = (array)$keys;
}
/**
* @param mixed $referencedTable
*/
public function setReferencedTable($referencedTable){
$this->referencedTable = $referencedTable;
}
/**
* @param mixed $referencedCols
*/
public function setReferencedCols($referencedCols){
$this->referencedCols = (array)$referencedCols;
}
/**
* @param string $onDelete
*/
public function setOnDelete($onDelete){
if( in_array($onDelete, self::ACTIONS_DELETE) ){
$this->onDelete = $onDelete;
}else{
trigger_error(sprintf(self::TEXT_ActionNotSupported, $onDelete));
}
}
/**
* @param string $onUpdate
*/
public function setOnUpdate($onUpdate){
if( in_array($onUpdate, self::ACTIONS_UPDATE) ){
$this->onUpdate = $onUpdate;
}else{
trigger_error(sprintf(self::TEXT_ActionNotSupported, $onUpdate));
}
}
/**
* @return array
*/
public function getKeys(){
return $this->keys;
}
/**
* @return string
*/
public function getReferencedTable(){
return $this->referencedTable;
}
/**
* @return array
*/
public function getReferencedCols(){
return $this->referencedCols;
}
/**
* @return string
*/
public function getOnDelete(){
return $this->onDelete;
}
/**
* @return string
*/
public function getOnUpdate(){
return $this->onUpdate;
}
/**
* get a constraint name for this table.
* This can either be used to generate unique constraint names for foreign keys in parent tables
* or generate a "part" of a name. e.g. for db-Query all constraints of this table (ignore columns)
* by "LIKE" selecting "information_schema"
* -> To get a certain constraint or generate a unique constraint, ALL params are required!
* @return string
*/
public function getConstraintName(){
$constraintName = 'fk_' . $this->table->name;
if(!empty($this->getKeys())){
$constraintName .= '___' . implode('__', $this->getKeys());
if(!empty($this->getReferencedTable())){
$constraintName .= '___' . $this->getReferencedTable();
if(!empty($this->getReferencedCols())){
$constraintName .= '___' . implode('__', $this->getReferencedCols());
}
}
}
return $constraintName;
}
/**
* checks if constraint is valid
* -> all required members must be set!
* @return bool
*/
public function isValid(){
$valid = false;
if(
!empty($this->getKeys()) &&
!empty($this->getReferencedTable()) &&
!empty($this->getReferencedCols())
){
$valid = true;
}
return $valid;
}
}

View File

@@ -8,18 +8,40 @@
namespace Model;
use DB\SQL\Schema;
class AllianceMapModel extends BasicModel {
protected $table = 'alliance_map';
protected $fieldConf = [
'active' => [
'type' => Schema::DT_BOOL,
'nullable' => false,
'default' => true,
'index' => true
],
'allianceId' => [
'belongs-to-one' => 'Model\AllianceModel'
'type' => Schema::DT_INT,
'index' => true,
'belongs-to-one' => 'Model\AllianceModel',
'constraint' => [
[
'table' => 'alliance',
'on-delete' => 'CASCADE'
]
]
],
'mapId' => [
'belongs-to-one' => 'Model\MapModel'
'type' => Schema::DT_INT,
'index' => true,
'belongs-to-one' => 'Model\MapModel',
'constraint' => [
[
'table' => 'map',
'on-delete' => 'CASCADE'
]
]
]
];
@@ -32,4 +54,21 @@ class AllianceMapModel extends BasicModel {
// clear map cache as well
$this->mapId->clearCacheData();
}
/**
* overwrites parent
* @param null $db
* @param null $table
* @param null $fields
* @return bool
*/
public static function setup($db=null, $table=null, $fields=null){
$status = parent::setup($db,$table,$fields);
if($status === true){
$status = parent::setMultiColumnIndex(['allianceId', 'mapId'], true);
}
return $status;
}
}

View File

@@ -8,11 +8,29 @@
namespace Model;
use DB\SQL\Schema;
class AllianceModel extends BasicModel {
protected $table = 'alliance';
protected $fieldConf = [
'active' => [
'type' => Schema::DT_BOOL,
'nullable' => false,
'default' => true,
'index' => true
],
'name' => [
'type' => Schema::DT_VARCHAR128,
'nullable' => false,
'default' => ''
],
'shared' => [
'type' => Schema::DT_BOOL,
'nullable' => false,
'default' => 0
],
'allianceCharacters' => [
'has-many' => ['Model\CharacterModel', 'allianceId']
],
@@ -30,7 +48,7 @@ class AllianceModel extends BasicModel {
$allianceData->id = $this->id;
$allianceData->name = $this->name;
$allianceData->sharing = $this->sharing;
$allianceData->shared = $this->shared;
return $allianceData;
}

View File

@@ -35,6 +35,13 @@ class BasicModel extends \DB\Cortex {
*/
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
@@ -48,10 +55,23 @@ class BasicModel extends \DB\Cortex {
*/
private $dataCacheKeyPrefix = 'DATACACHE';
/**
* enables data export for this table
* -> can be overwritten in child models
* @var bool
*/
public static $enableDataExport = false;
/**
* enables data import for this table
* -> can be overwritten in child models
* @var bool
*/
public static $enableDataImport = false;
public function __construct($db = NULL, $table = NULL, $fluid = NULL, $ttl = 0){
// add static fields to this mapper
$this->addStaticFieldConfig();
parent::__construct($db, $table, $fluid, $ttl);
@@ -120,18 +140,24 @@ class BasicModel extends \DB\Cortex {
*/
private function addStaticFieldConfig(){
if(is_array($this->fieldConf)){
// add static fields to this mapper
// static tables (fixed data) do not require them...
if($this->addStaticFields){
$staticFieldConfig = [
'created' => [
'type' => Schema::DT_TIMESTAMP
'type' => Schema::DT_TIMESTAMP,
'default' => Schema::DF_CURRENT_TIMESTAMP,
'index' => true
],
'updated' => [
'type' => Schema::DF_CURRENT_TIMESTAMP
'type' => Schema::DT_TIMESTAMP,
'default' => Schema::DF_CURRENT_TIMESTAMP,
'index' => true
]
];
$this->fieldConf = array_merge($this->fieldConf, $staticFieldConfig);
$this->fieldConf = array_merge($staticFieldConfig, $this->fieldConf);
}
}
@@ -263,13 +289,7 @@ class BasicModel extends \DB\Cortex {
* @return bool
*/
public function isActive(){
$isActive = false;
if($this->active === 1){
$isActive = true;
}
return $isActive;
return (bool)$this->active;
}
/**
@@ -395,6 +415,16 @@ class BasicModel extends \DB\Cortex {
}
}
/**
* get the current class name
* -> namespace not included
* @return string
*/
public static function getClassName(){
$parts = explode('\\', static::class);
return end($parts);
}
/**
* factory for all Models
* @param $model
@@ -429,7 +459,171 @@ class BasicModel extends \DB\Cortex {
*/
public static function log($text){
Controller\LogController::getLogger('debug')->write($text);
}
/**
* export and download table data as *.csv
* this is primarily used for static tables
* @return bool
*/
public function exportData(){
$status = false;
if(static::$enableDataExport){
$tableModifier = static::getTableModifier();
$headers = $tableModifier->getCols();
// just get the records with existing columns
// -> no "virtual" fields or "new" columns
$this->fields($headers);
$allRecords = $this->find();
if($allRecords){
$tableData = $allRecords->castAll();
// format data -> "id" must be first key
foreach($tableData as &$rowData){
$rowData = [$this->primary => $rowData['_id']] + $rowData;
unset($rowData['_id']);
}
$sheet = \Sheet::instance();
$data = $sheet->dumpCSV($tableData, $headers);
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Content-Type: text/csv;charset=UTF-8');
header('Content-Disposition: attachment;filename=' . $this->getTable() . '.csv');
echo $data;
exit();
}
}
return $status;
}
/**
* import table data from a *.csv file
* @return bool
*/
public function importData(){
$status = false;
if(static::$enableDataImport){
$filePath = 'export/sql/' . $this->getTable() . '.csv';
if(is_file($filePath)){
$handle = @fopen($filePath, 'r');
$keys = array_map('lcfirst', array_filter(fgetcsv($handle, 0, ';')));
if(count($keys) > 0){
$tableData = [];
while (!feof($handle)) {
$tableData[] = array_combine($keys, array_filter(fgetcsv($handle, 0, ';')));
}
// import row data
$status = $this->importStaticData($tableData);
$this->getF3()->status(202);
}else{
$this->getF3()->error(502, 'File could not be read');
}
}else{
$this->getF3()->error(404, 'File not found: ' . $filePath);
}
}
return $status;
}
/**
* insert/update static data into this table
* WARNING: rows will be deleted if not part of $tableData !
* @param array $tableData
* @return array
*/
protected function importStaticData($tableData = []){
$rowIDs = [];
$addedCount = 0;
$updatedCount = 0;
$deletedCount = 0;
foreach($tableData as $rowData){
// search for existing record and update columns
$this->getById($rowData['id']);
if($this->dry()){
$addedCount++;
}else{
$updatedCount++;
}
$this->copyfrom($rowData);
$this->save();
$rowIDs[] = $this->id;
$this->reset();
}
// remove old data
$oldRows = $this->find('id NOT IN (' . implode(',', $rowIDs) . ')');
if($oldRows){
foreach($oldRows as $oldRow){
$oldRow->erase();
$deletedCount++;
}
}
return ['added' => $addedCount, 'updated' => $updatedCount, 'deleted' => $deletedCount];
}
/**
* get tableModifier class for this table
* @return bool|DB\SQL\TableModifier
*/
public static function getTableModifier(){
$df = parent::resolveConfiguration();
$schema = new Schema($df['db']);
$tableModifier = $schema->alterTable( $df['table'] );
return $tableModifier;
}
/**
* Check whether a (multi)-column index exists or not on a table
* related to this model
* @param array $fields
* @return bool|array
*/
public static function indexExists(array $fields=array()){
$tableModifier = self::getTableModifier();
$df = parent::resolveConfiguration();
$check = false;
$indexKey = $df['table'] . '___' . implode('__', $fields);
$indexList = $tableModifier->listIndex();
if(array_key_exists( $indexKey, $indexList)){
$check = $indexList[$indexKey];
}
return $check;
}
/**
* set a multi-column index for this table
* @param array $fields
* @param bool $unique
* @param int $length
* @return bool
*/
public static function setMultiColumnIndex(array $fields=array(), $unique = false, $length = 20){
$status = false;
$tableModifier = self::getTableModifier();
if( self::indexExists($fields) === false ){
$tableModifier->addIndex($fields, $unique, $length);
$buildStatus = $tableModifier->build();
if($buildStatus === 0){
$status = true;
}
}
return $status;
}
}

View File

@@ -8,14 +8,53 @@
namespace Model;
use DB\SQL\Schema;
class CharacterLogModel extends BasicModel {
protected $table = 'character_log';
protected $fieldConf = [
'active' => [
'type' => Schema::DT_BOOL,
'nullable' => false,
'default' => true,
'index' => true
],
'characterId' => [
'belongs-to-one' => 'Model\CharacterModel'
'type' => Schema::DT_INT,
'index' => true,
'unique' => true,
'belongs-to-one' => 'Model\CharacterModel',
'constraint' => [
[
'table' => 'character',
'on-delete' => 'CASCADE'
]
]
],
'systemId' => [
'type' => Schema::DT_INT,
'index' => true
],
'systemName' => [
'type' => Schema::DT_VARCHAR128,
'nullable' => false,
'default' => ''
],
'shipId' => [
'type' => Schema::DT_INT,
'index' => true
],
'shipName' => [
'type' => Schema::DT_VARCHAR128,
'nullable' => false,
'default' => ''
],
'shipTypeName' => [
'type' => Schema::DT_VARCHAR128,
'nullable' => false,
'default' => ''
]
];

View File

@@ -8,17 +8,54 @@
namespace Model;
use DB\SQL\Schema;
class CharacterModel extends BasicModel {
protected $table = 'character';
protected $fieldConf = [
'active' => [
'type' => Schema::DT_BOOL,
'nullable' => false,
'default' => true,
'index' => true
],
'name' => [
'type' => Schema::DT_VARCHAR128,
'nullable' => false,
'default' => ''
],
'corporationId' => [
'belongs-to-one' => 'Model\CorporationModel'
'type' => Schema::DT_INT,
'index' => true,
'belongs-to-one' => 'Model\CorporationModel',
'constraint' => [
[
'table' => 'corporation',
'on-delete' => 'SET NULL'
]
]
],
'allianceId' => [
'belongs-to-one' => 'Model\AllianceModel'
'type' => Schema::DT_INT,
'index' => true,
'belongs-to-one' => 'Model\AllianceModel',
'constraint' => [
[
'table' => 'alliance',
'on-delete' => 'SET NULL'
]
]
],
'factionId' => [
'type' => Schema::DT_INT,
'index' => true
],
'factionName' => [
'type' => Schema::DT_VARCHAR128,
'nullable' => false,
'default' => ''
],
'characterLog' => [
'has-one' => ['Model\CharacterLogModel', 'characterId']

View File

@@ -8,8 +8,65 @@
namespace Model;
use DB\SQL\Schema;
class CharacterStatusModel extends BasicModel {
protected $table = 'character_status';
protected $fieldConf = [
'active' => [
'type' => Schema::DT_BOOL,
'nullable' => false,
'default' => true,
'index' => true
],
'name' => [
'type' => Schema::DT_VARCHAR128,
'nullable' => false,
'default' => ''
],
'class' => [
'type' => Schema::DT_VARCHAR128,
'nullable' => false,
'default' => ''
]
];
protected static $tableData = [
[
'id' => 1,
'name' => 'corporation',
'class' => 'pf-user-status-corp'
],
[
'id' => 2,
'name' => 'alliance',
'class' => 'pf-user-status-ally'
],
[
'id' => 3,
'name' => 'own',
'class' => 'pf-user-status-own'
]
];
/**
* overwrites parent
* @param null $db
* @param null $table
* @param null $fields
* @return bool
*/
public static function setup($db=null, $table=null, $fields=null){
$status = parent::setup($db,$table,$fields);
// set static default values
if($status === true){
$model = self::getNew(self::getClassName(), 0);
$model->importStaticData(self::$tableData);
}
return $status;
}
}

View File

@@ -8,21 +8,56 @@
namespace Model;
use DB\SQL\Schema;
class ConnectionModel extends BasicModel{
protected $table = 'connection';
protected $fieldConf = [
'active' => [
'type' => Schema::DT_BOOL,
'nullable' => false,
'default' => true,
'index' => true
],
'mapId' => [
'belongs-to-one' => 'Model\MapModel'
'type' => Schema::DT_INT,
'index' => true,
'belongs-to-one' => 'Model\MapModel',
'constraint' => [
[
'table' => 'map',
'on-delete' => 'CASCADE'
]
]
],
'source' => [
'belongs-to-one' => 'Model\SystemModel'
'type' => Schema::DT_INT,
'index' => true,
'belongs-to-one' => 'Model\SystemModel',
'constraint' => [
[
'table' => 'system',
'on-delete' => 'CASCADE'
]
]
],
'target' => [
'belongs-to-one' => 'Model\SystemModel'
'type' => Schema::DT_INT,
'index' => true,
'belongs-to-one' => 'Model\SystemModel',
'constraint' => [
[
'table' => 'system',
'on-delete' => 'CASCADE'
]
]
],
'scope' => [
'type' => Schema::DT_VARCHAR128,
'nullable' => false,
'default' => ''
],
'type' => [
'type' => self::DT_JSON
@@ -114,4 +149,20 @@ class ConnectionModel extends BasicModel{
$this->mapId->clearCacheData();
}
/**
* overwrites parent
* @param null $db
* @param null $table
* @param null $fields
* @return bool
*/
public static function setup($db=null, $table=null, $fields=null){
$status = parent::setup($db,$table,$fields);
if($status === true){
$status = parent::setMultiColumnIndex(['source', 'target', 'scope']);
}
return $status;
}
}

View File

@@ -8,9 +8,74 @@
namespace Model;
use DB\SQL\Schema;
class ConnectionScopeModel extends BasicModel{
protected $table = 'connection_scope';
protected $fieldConf = [
'active' => [
'type' => Schema::DT_BOOL,
'nullable' => false,
'default' => true,
'index' => true
],
'name' => [
'type' => Schema::DT_VARCHAR128,
'nullable' => false,
'default' => ''
],
'label' => [
'type' => Schema::DT_VARCHAR128,
'nullable' => false,
'default' => ''
],
'connectorDefinition' => [
'type' => Schema::DT_VARCHAR256,
'nullable' => false,
'default' => ''
]
];
protected static $tableData = [
[
'id' => 1,
'name' => 'wh',
'label' => 'wormhole',
'connectorDefinition' => '[ "Bezier", { "curviness": 40 } ]'
],
[
'id' => 2,
'name' => 'stargate',
'label' => 'stargate',
'connectorDefinition' => '[ "Flowchart", { "stub": [20, 20], "gap": 0, "cornerRadius": 5, "alwaysRespectStubs": false } ]'
],
[
'id' => 3,
'name' => 'jumpbridge',
'label' => 'jumpbridge',
'connectorDefinition' => '[ "Straight", { "stub": [5, 5], "gap": 0 } ]'
]
];
/**
* overwrites parent
* @param null $db
* @param null $table
* @param null $fields
* @return bool
*/
public static function setup($db=null, $table=null, $fields=null){
$status = parent::setup($db,$table,$fields);
// set static default values
if($status === true){
$model = self::getNew(self::getClassName(), 0);
$model->importStaticData(self::$tableData);
}
return $status;
}
}

View File

@@ -8,17 +8,40 @@
namespace Model;
use DB\SQL\Schema;
class CorporationMapModel extends BasicModel {
protected $table = 'corporation_map';
protected $fieldConf = [
'active' => [
'type' => Schema::DT_BOOL,
'nullable' => false,
'default' => true,
'index' => true
],
'corporationId' => [
'belongs-to-one' => 'Model\CorporationModel'
'type' => Schema::DT_INT,
'index' => true,
'belongs-to-one' => 'Model\CorporationModel',
'constraint' => [
[
'table' => 'corporation',
'on-delete' => 'CASCADE'
]
]
],
'mapId' => [
'belongs-to-one' => 'Model\MapModel'
'type' => Schema::DT_INT,
'index' => true,
'belongs-to-one' => 'Model\MapModel',
'constraint' => [
[
'table' => 'map',
'on-delete' => 'CASCADE'
]
]
]
];
@@ -32,4 +55,20 @@ class CorporationMapModel extends BasicModel {
$this->mapId->clearCacheData();
}
/**
* overwrites parent
* @param null $db
* @param null $table
* @param null $fields
* @return bool
*/
public static function setup($db=null, $table=null, $fields=null){
$status = parent::setup($db,$table,$fields);
if($status === true){
$status = parent::setMultiColumnIndex(['corporationId', 'mapId'], true);
}
return $status;
}
}

View File

@@ -8,11 +8,29 @@
namespace Model;
use DB\SQL\Schema;
class CorporationModel extends BasicModel {
protected $table = 'corporation';
protected $fieldConf = [
'active' => [
'type' => Schema::DT_BOOL,
'nullable' => false,
'default' => true,
'index' => true
],
'name' => [
'type' => Schema::DT_VARCHAR128,
'nullable' => false,
'default' => ''
],
'shared' => [
'type' => Schema::DT_BOOL,
'nullable' => false,
'default' => 0
],
'corporationCharacters' => [
'has-many' => ['Model\CharacterModel', 'corporationId']
],
@@ -30,7 +48,7 @@ class CorporationModel extends BasicModel {
$cooperationData->id = $this->id;
$cooperationData->name = $this->name;
$cooperationData->sharing = $this->sharing;
$cooperationData->shared = $this->shared;
return $cooperationData;
}

View File

@@ -8,16 +8,51 @@
namespace Model;
use DB\SQL\Schema;
class MapModel extends BasicModel {
protected $table = 'map';
protected $fieldConf = [
'active' => [
'type' => Schema::DT_BOOL,
'nullable' => false,
'default' => true,
'index' => true,
'after' => 'updated'
],
'scopeId' => [
'belongs-to-one' => 'Model\MapScopeModel'
'type' => Schema::DT_INT,
'index' => true,
'belongs-to-one' => 'Model\MapScopeModel',
'constraint' => [
[
'table' => 'map_scope',
'on-delete' => 'CASCADE'
]
]
],
'typeId' => [
'belongs-to-one' => 'Model\MapTypeModel'
'type' => Schema::DT_INT,
'index' => true,
'belongs-to-one' => 'Model\MapTypeModel',
'constraint' => [
[
'table' => 'map_type',
'on-delete' => 'CASCADE'
]
]
],
'name' => [
'type' => Schema::DT_VARCHAR128,
'nullable' => false,
'default' => ''
],
'icon' => [
'type' => Schema::DT_VARCHAR128,
'nullable' => false,
'default' => ''
],
'systems' => [
'has-many' => ['Model\SystemModel', 'mapId']
@@ -31,8 +66,8 @@ class MapModel extends BasicModel {
'mapCorporations' => [
'has-many' => ['Model\CorporationMapModel', 'mapId']
],
'mapAlliances' => ['has-many' => [
'Model\AllianceMapModel', 'mapId']
'mapAlliances' => [
'has-many' => ['Model\AllianceMapModel', 'mapId']
]
];

View File

@@ -8,9 +8,55 @@
namespace Model;
use DB\SQL\Schema;
class MapScopeModel extends BasicModel{
protected $table = 'map_scope';
protected $fieldConf = [
'active' => [
'type' => Schema::DT_BOOL,
'nullable' => false,
'default' => true,
'index' => true
],
'name' => [
'type' => Schema::DT_VARCHAR128,
'nullable' => false,
'default' => ''
],
'label' => [
'type' => Schema::DT_VARCHAR128,
'nullable' => false,
'default' => ''
]
];
protected static $tableData = [
[
'id' => 1,
'name' => 'wh',
'label' => 'w-space'
]
];
/**
* overwrites parent
* @param null $db
* @param null $table
* @param null $fields
* @return bool
*/
public static function setup($db=null, $table=null, $fields=null){
$status = parent::setup($db,$table,$fields);
// set static default values
if($status === true){
$model = self::getNew(self::getClassName(), 0);
$model->importStaticData(self::$tableData);
}
return $status;
}
}

View File

@@ -8,9 +8,96 @@
namespace Model;
use DB\SQL\Schema;
class MapTypeModel extends BasicModel{
protected $table = 'map_type';
protected $fieldConf = [
'active' => [
'type' => Schema::DT_BOOL,
'nullable' => false,
'default' => true,
'index' => true
],
'name' => [
'type' => Schema::DT_VARCHAR128,
'nullable' => false,
'default' => ''
],
'label' => [
'type' => Schema::DT_VARCHAR128,
'nullable' => false,
'default' => ''
],
'class' => [
'type' => Schema::DT_VARCHAR128,
'nullable' => false,
'default' => ''
],
'classTab' => [
'type' => Schema::DT_VARCHAR128,
'nullable' => false,
'default' => ''
]
];
protected static $tableData = [
[
'id' => 1,
'name' => 'standard',
'label' => '',
'class' => '',
'classTab' => 'pf-map-type-tab-default'
],
[
'id' => 2,
'name' => 'private',
'label' => 'private',
'class' => 'pf-map-type-private',
'classTab' => 'pf-map-type-tab-private'
],
[
'id' => 3,
'name' => 'corporation',
'label' => 'corporation',
'class' => 'pf-map-type-corporation',
'classTab' => 'pf-map-type-tab-corporation'
],
[
'id' => 4,
'name' => 'alliance',
'label' => 'alliance',
'class' => 'pf-map-type-alliance',
'classTab' => 'pf-map-type-tab-alliance'
],
[
'id' => 5,
'name' => 'globald',
'label' => 'global',
'class' => 'pf-map-type-global',
'classTab' => 'pf-map-type-tab-global'
]
];
/**
* overwrites parent
* @param null $db
* @param null $table
* @param null $fields
* @return bool
*/
public static function setup($db=null, $table=null, $fields=null){
$status = parent::setup($db,$table,$fields);
// set static default values
if($status === true){
$model = self::getNew(self::getClassName(), 0);
$model->importStaticData(self::$tableData);
}
return $status;
}
}

View File

@@ -8,9 +8,42 @@
namespace Model;
use DB\SQL\Schema;
class RegistrationKeyModel extends BasicModel {
protected $table = 'registration_key';
protected $fieldConf = [
'active' => [
'type' => Schema::DT_BOOL,
'nullable' => false,
'default' => true,
'index' => true
],
'ip' => [
'type' => Schema::DT_VARCHAR128,
'nullable' => false,
'default' => ''
],
'used' => [
'type' => Schema::DT_BOOL,
'nullable' => false,
'default' => 0
],
'email' => [
'type' => Schema::DT_VARCHAR128,
'nullable' => false,
'default' => '',
'index' => true
],
'registrationKey' => [
'type' => Schema::DT_VARCHAR128,
'nullable' => false,
'default' => '',
'index' => true,
'unique' => true
]
];
}

View File

@@ -0,0 +1,42 @@
<?php
/**
* Created by PhpStorm.
* User: exodus4d
* Date: 25.11.15
* Time: 22:11
*/
namespace Model;
use DB\SQL\Schema;
class SystemApiBasicModel extends BasicModel {
public function __construct($db = NULL, $table = NULL, $fluid = NULL, $ttl = 0){
$this->addStaticKillFieldConfig();
parent::__construct($db, $table, $fluid, $ttl);
}
/**
* extent the fieldConf Array with static fields for each table
*/
private function addStaticKillFieldConfig(){
if(is_array($this->fieldConf)){
$staticFieldConfig = [];
for($i = 1; $i <= 24; $i++){
$staticFieldConfig['value' . $i] = [
'type' => Schema::DT_INT,
'nullable' => false,
'default' => 0,
];
}
$this->fieldConf = array_merge($this->fieldConf, $staticFieldConfig);
}
}
}

View File

@@ -8,8 +8,23 @@
namespace Model;
use DB\SQL\Schema;
class SystemFactionKillModel extends BasicModel {
class SystemFactionKillModel extends SystemApiBasicModel {
protected $table = 'system_kills_factions';
protected $fieldConf = [
'active' => [
'type' => Schema::DT_BOOL,
'nullable' => false,
'default' => true,
'index' => true
],
'systemId' => [
'type' => Schema::DT_INT,
'index' => true,
'unique' => true
]
];
}

View File

@@ -8,8 +8,23 @@
namespace Model;
use DB\SQL\Schema;
class SystemJumpModel extends BasicModel {
class SystemJumpModel extends SystemApiBasicModel {
protected $table = 'system_jumps';
protected $fieldConf = [
'active' => [
'type' => Schema::DT_BOOL,
'nullable' => false,
'default' => true,
'index' => true
],
'systemId' => [
'type' => Schema::DT_INT,
'index' => true,
'unique' => true
]
];
}

View File

@@ -8,6 +8,7 @@
namespace Model;
use DB\SQL\Schema;
class SystemModel extends BasicModel {
@@ -17,24 +18,144 @@ class SystemModel extends BasicModel {
protected $table = 'system';
protected $fieldConf = [
'active' => [
'type' => Schema::DT_BOOL,
'nullable' => false,
'default' => true,
'index' => true
],
'mapId' => [
'belongs-to-one' => 'Model\MapModel'
'type' => Schema::DT_INT,
'index' => true,
'belongs-to-one' => 'Model\MapModel',
'constraint' => [
[
'table' => 'map',
'on-delete' => 'CASCADE'
]
]
],
'systemId' => [
'type' => Schema::DT_INT,
'index' => true,
],
'name' => [
'type' => Schema::DT_VARCHAR128,
'nullable' => false,
'default' => ''
],
'alias' => [
'type' => Schema::DT_VARCHAR128,
'nullable' => false,
'default' => ''
],
'regionId' => [
'type' => Schema::DT_INT,
'index' => true,
],
'region' => [
'type' => Schema::DT_VARCHAR128,
'nullable' => false,
'default' => ''
],
'constellationId' => [
'type' => Schema::DT_INT,
'index' => true,
],
'constellation' => [
'type' => Schema::DT_VARCHAR128,
'nullable' => false,
'default' => ''
],
'effect' => [
'type' => Schema::DT_VARCHAR128,
'nullable' => false,
'default' => ''
],
'typeId' => [
'belongs-to-one' => 'Model\SystemTypeModel'
'type' => Schema::DT_INT,
'index' => true,
'belongs-to-one' => 'Model\SystemTypeModel',
'constraint' => [
[
'table' => 'system_type',
'on-delete' => 'CASCADE'
]
]
],
'security' => [
'type' => Schema::DT_VARCHAR128,
'nullable' => false,
'default' => ''
],
'trueSec' => [
'type' => Schema::DT_FLOAT,
'nullable' => false,
'default' => 1
],
'statusId' => [
'belongs-to-one' => 'Model\SystemStatusModel'
'type' => Schema::DT_INT,
'nullable' => false,
'default' => 1,
'index' => true,
'belongs-to-one' => 'Model\SystemStatusModel',
'constraint' => [
[
'table' => 'system_status',
'on-delete' => 'CASCADE'
]
]
],
'locked' => [
'type' => Schema::DT_BOOL,
'nullable' => false,
'default' => 0
],
'rally' => [
'type' => Schema::DT_BOOL,
'nullable' => false,
'default' => 0
],
'description' => [
'type' => Schema::DT_VARCHAR512,
'nullable' => false,
'default' => ''
],
'posX' => [
'type' => Schema::DT_INT,
'nullable' => false,
'default' => 0
],
'posY' => [
'type' => Schema::DT_INT,
'nullable' => false,
'default' => 0
],
'createdCharacterId' => [
'belongs-to-one' => 'Model\CharacterModel'
'type' => Schema::DT_INT,
'index' => true,
'belongs-to-one' => 'Model\CharacterModel',
'constraint' => [
[
'table' => 'character',
'on-delete' => 'CASCADE'
]
]
],
'updatedCharacterId' => [
'belongs-to-one' => 'Model\CharacterModel'
'type' => Schema::DT_INT,
'index' => true,
'belongs-to-one' => 'Model\CharacterModel',
'constraint' => [
[
'table' => 'character',
'on-delete' => 'CASCADE'
]
]
],
'signatures' => [
'has-many' => ['Model\SystemSignatureModel', 'systemId']
],
]
];
/**
@@ -333,4 +454,21 @@ class SystemModel extends BasicModel {
$this->mapId->clearCacheData();
}
/**
* overwrites parent
* @param null $db
* @param null $table
* @param null $fields
* @return bool
*/
public static function setup($db=null, $table=null, $fields=null){
$status = parent::setup($db,$table,$fields);
if($status === true){
$status = parent::setMultiColumnIndex(['mapId', 'systemId'], true);
}
return $status;
}
}

View File

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

View File

@@ -8,8 +8,23 @@
namespace Model;
use DB\SQL\Schema;
class SystemPodKillModel extends BasicModel {
class SystemPodKillModel extends SystemApiBasicModel {
protected $table = 'system_kills_pods';
protected $fieldConf = [
'active' => [
'type' => Schema::DT_BOOL,
'nullable' => false,
'default' => true,
'index' => true
],
'systemId' => [
'type' => Schema::DT_INT,
'index' => true,
'unique' => true
]
];
}

View File

@@ -8,8 +8,24 @@
namespace Model;
use DB\SQL\Schema;
class SystemShipKillModel extends BasicModel {
class SystemShipKillModel extends SystemApiBasicModel {
protected $table = 'system_kills_ships';
protected $fieldConf = [
'active' => [
'type' => Schema::DT_BOOL,
'nullable' => false,
'default' => true,
'index' => true
],
'systemId' => [
'type' => Schema::DT_INT,
'index' => true,
'unique' => true
]
];
}

View File

@@ -8,20 +8,73 @@
namespace Model;
use DB\SQL\Schema;
class SystemSignatureModel extends BasicModel {
protected $table = 'system_signature';
protected $fieldConf = [
'active' => [
'type' => Schema::DT_BOOL,
'nullable' => false,
'default' => true,
'index' => true
],
'systemId' => [
'belongs-to-one' => 'Model\SystemModel'
'type' => Schema::DT_INT,
'index' => true,
'belongs-to-one' => 'Model\SystemModel',
'constraint' => [
[
'table' => 'system',
'on-delete' => 'CASCADE'
]
]
],
'groupId' => [
'type' => Schema::DT_INT,
'nullable' => false,
'default' => 1,
'index' => true,
],
'typeId' => [
'type' => Schema::DT_INT,
'nullable' => false,
'default' => 1,
'index' => true,
],
'name' => [
'type' => Schema::DT_VARCHAR128,
'nullable' => false,
'default' => ''
],
'description' => [
'type' => Schema::DT_VARCHAR512,
'nullable' => false,
'default' => ''
],
'createdCharacterId' => [
'belongs-to-one' => 'Model\CharacterModel'
'type' => Schema::DT_INT,
'index' => true,
'belongs-to-one' => 'Model\CharacterModel',
'constraint' => [
[
'table' => 'character',
'on-delete' => 'CASCADE'
]
]
],
'updatedCharacterId' => [
'belongs-to-one' => 'Model\CharacterModel'
'type' => Schema::DT_INT,
'index' => true,
'belongs-to-one' => 'Model\CharacterModel',
'constraint' => [
[
'table' => 'character',
'on-delete' => 'CASCADE'
]
]
]
];
@@ -114,4 +167,21 @@ class SystemSignatureModel extends BasicModel {
}
}
}
/**
* overwrites parent
* @param null $db
* @param null $table
* @param null $fields
* @return bool
*/
public static function setup($db=null, $table=null, $fields=null){
$status = parent::setup($db,$table,$fields);
if($status === true){
$status = parent::setMultiColumnIndex(['systemId', 'typeId', 'groupId']);
}
return $status;
}
}

View File

@@ -8,8 +8,91 @@
namespace Model;
use DB\SQL\Schema;
class SystemStatusModel extends BasicModel {
protected $table = 'system_status';
protected $fieldConf = [
'active' => [
'type' => Schema::DT_BOOL,
'nullable' => false,
'default' => true,
'index' => true
],
'name' => [
'type' => Schema::DT_VARCHAR128,
'nullable' => false,
'default' => ''
],
'label' => [
'type' => Schema::DT_VARCHAR128,
'nullable' => false,
'default' => ''
],
'class' => [
'type' => Schema::DT_VARCHAR128,
'nullable' => false,
'default' => ''
]
];
protected static $tableData = [
[
'id' => 1,
'name' => 'unknown',
'label' => 'unknown',
'class' => 'pf-system-status-unknown'
],
[
'id' => 2,
'name' => 'friendly',
'label' => 'friendly',
'class' => 'pf-system-status-friendly'
],
[
'id' => 3,
'name' => 'occupied',
'label' => 'occupied',
'class' => 'pf-system-status-occupied'
],
[
'id' => 4,
'name' => 'hostile',
'label' => 'hostile',
'class' => 'pf-system-status-hostile'
],
[
'id' => 5,
'name' => 'empty',
'label' => 'empty',
'class' => 'pf-system-status-empty'
],
[
'id' => 6,
'name' => 'unscanned',
'label' => 'unscanned',
'class' => 'pf-system-status-unscanned'
]
];
/**
* overwrites parent
* @param null $db
* @param null $table
* @param null $fields
* @return bool
*/
public static function setup($db=null, $table=null, $fields=null){
$status = parent::setup($db,$table,$fields);
// set static default values
if($status === true){
$model = self::getNew(self::getClassName(), 0);
$model->importStaticData(self::$tableData);
}
return $status;
}
}

View File

@@ -8,8 +8,53 @@
namespace Model;
use DB\SQL\Schema;
class SystemTypeModel extends BasicModel {
protected $table = 'system_type';
protected $fieldConf = [
'active' => [
'type' => Schema::DT_BOOL,
'nullable' => false,
'default' => true,
'index' => true
],
'name' => [
'type' => Schema::DT_VARCHAR128,
'nullable' => false,
'default' => ''
]
];
protected static $tableData = [
[
'id' => 1,
'name' => 'w-space'
],
[
'id' => 2,
'name' => 'k-space'
]
];
/**
* overwrites parent
* @param null $db
* @param null $table
* @param null $fields
* @return bool
*/
public static function setup($db=null, $table=null, $fields=null){
$status = parent::setup($db,$table,$fields);
// set static default values
if($status === true){
$model = self::getNew(self::getClassName(), 0);
$model->importStaticData(self::$tableData);
}
return $status;
}
}

View File

@@ -8,15 +8,28 @@
namespace Model;
use DB\SQL\Schema;
class SystemWormholeModel extends BasicModel {
protected $table = 'system_wormhole';
protected $fieldConf = [
'constellationId' => [
'type' => Schema::DT_INT,
'index' => true,
],
'wormholeId' => [
'belongs-to-one' => 'Model\WormholeModel'
]
'type' => Schema::DT_INT,
'index' => true,
'belongs-to-one' => 'Model\WormholeModel',
'constraint' => [
[
'table' => 'wormhole',
'on-delete' => 'CASCADE'
]
]
],
];
/**

View File

@@ -7,6 +7,8 @@
*/
namespace Model;
use DB\SQL\Schema;
use Controller;
class UserApiModel extends BasicModel {
@@ -14,12 +16,36 @@ class UserApiModel extends BasicModel {
protected $table = 'user_api';
protected $fieldConf = [
'active' => [
'type' => Schema::DT_BOOL,
'nullable' => false,
'default' => true,
'index' => true
],
'userId' => [
'belongs-to-one' => 'Model\UserModel'
'type' => Schema::DT_INT,
'index' => true,
'belongs-to-one' => 'Model\UserModel',
'constraint' => [
[
'table' => 'user',
'on-delete' => 'CASCADE'
]
]
],
'keyId' => [
'type' => Schema::DT_INT,
'index' => true,
'unique' => true
],
'vCode' => [
'type' => Schema::DT_VARCHAR128,
'nullable' => false,
'default' => '',
],
'userCharacters' => [
'has-many' => ['Model\UserCharacterModel', 'apiId']
]
],
];
/**

View File

@@ -8,20 +8,57 @@
namespace Model;
use DB\SQL\Schema;
class UserCharacterModel extends BasicModel {
protected $table = 'user_character';
protected $fieldConf = [
'active' => [
'type' => Schema::DT_BOOL,
'nullable' => false,
'default' => true,
'index' => true
],
'userId' => [
'belongs-to-one' => 'Model\UserModel'
'type' => Schema::DT_INT,
'index' => true,
'belongs-to-one' => 'Model\UserModel',
'constraint' => [
[
'table' => 'user',
'on-delete' => 'CASCADE'
]
]
],
'apiId' => [
'belongs-to-one' => 'Model\UserApiModel'
'type' => Schema::DT_INT,
'index' => true,
'belongs-to-one' => 'Model\UserApiModel',
'constraint' => [
[
'table' => 'user_api',
'on-delete' => 'CASCADE'
]
]
],
'characterId' => [
'belongs-to-one' => 'Model\CharacterModel'
'type' => Schema::DT_INT,
'index' => true,
'belongs-to-one' => 'Model\CharacterModel',
'constraint' => [
[
'table' => 'character',
'on-delete' => 'CASCADE'
]
]
],
'isMain' => [
'type' => Schema::DT_BOOLEAN,
'nullable' => false,
'default' => false,
'index' => true
]
];
@@ -98,4 +135,24 @@ class UserCharacterModel extends BasicModel {
return $this->characterId;
}
/**
* overwrites parent
* @param null $db
* @param null $table
* @param null $fields
* @return bool
*/
public static function setup($db=null, $table=null, $fields=null){
$status = parent::setup($db,$table,$fields);
if($status === true){
$status = parent::setMultiColumnIndex(['userId', 'apiId', 'characterId'], true);
if($status === true){
$status = parent::setMultiColumnIndex(['userId', 'apiId']);
}
}
return $status;
}
}

View File

@@ -8,17 +8,40 @@
namespace Model;
use DB\SQL\Schema;
class UserMapModel extends BasicModel {
protected $table = 'user_map';
protected $fieldConf = [
'active' => [
'type' => Schema::DT_BOOL,
'nullable' => false,
'default' => true,
'index' => true
],
'userId' => [
'belongs-to-one' => 'Model\UserModel'
'type' => Schema::DT_INT,
'index' => true,
'belongs-to-one' => 'Model\UserModel',
'constraint' => [
[
'table' => 'user',
'on-delete' => 'CASCADE'
]
]
],
'mapId' => [
'belongs-to-one' => 'Model\MapModel'
'type' => Schema::DT_INT,
'index' => true,
'belongs-to-one' => 'Model\MapModel',
'constraint' => [
[
'table' => 'map',
'on-delete' => 'CASCADE'
]
]
]
];
@@ -32,4 +55,21 @@ class UserMapModel extends BasicModel {
$this->mapId->clearCacheData();
}
/**
* overwrites parent
* @param null $db
* @param null $table
* @param null $fields
* @return bool
*/
public static function setup($db=null, $table=null, $fields=null){
$status = parent::setup($db,$table,$fields);
if($status === true){
$status = parent::setMultiColumnIndex(['userId', 'mapId'], true);
}
return $status;
}
}

View File

@@ -18,7 +18,38 @@ class UserModel extends BasicModel {
protected $fieldConf = [
'lastLogin' => [
'type' => Schema::DT_TIMESTAMP
'type' => Schema::DT_TIMESTAMP,
'index' => true
],
'active' => [
'type' => Schema::DT_BOOL,
'nullable' => false,
'default' => true,
'index' => true
],
'name' => [
'type' => Schema::DT_VARCHAR128,
'nullable' => false,
'default' => '',
'index' => true,
'unique' => true
],
'email' => [
'type' => Schema::DT_VARCHAR128,
'nullable' => false,
'default' => '',
'index' => true,
'unique' => true
],
'password' => [
'type' => Schema::DT_VARCHAR128,
'nullable' => false,
'default' => ''
],
'shared' => [
'type' => Schema::DT_BOOL,
'nullable' => false,
'default' => '0'
],
'apis' => [
'has-many' => ['Model\UserApiModel', 'userId']
@@ -64,8 +95,8 @@ class UserModel extends BasicModel {
// add sensitive user data
$userData->email = $this->email;
// user sharing info
$userData->sharing = $this->sharing;
// user shared info
$userData->shared = $this->shared;
// api data
$APIs = $this->getAPIs();
@@ -180,7 +211,6 @@ class UserModel extends BasicModel {
* @return array
*/
public function getMaps(){
$f3 = self::getF3();
$this->filter(

View File

@@ -8,10 +8,54 @@
namespace Model;
use DB\SQL\Schema;
class WormholeModel extends BasicModel {
protected $table = 'wormhole';
protected $fieldConf = [
'name' => [
'type' => Schema::DT_VARCHAR128,
'nullable' => false,
'default' => '',
'index' => true,
'unique' => true
],
'security' => [
'type' => Schema::DT_VARCHAR128,
'nullable' => false,
'default' => ''
],
'massTotal' => [
'type' => Schema::DT_VARCHAR128,
'nullable' => false,
'default' => ''
],
'massIndividual' => [
'type' => Schema::DT_VARCHAR128,
'nullable' => false,
'default' => ''
],
'massRegeneration' => [
'type' => Schema::DT_VARCHAR128,
'nullable' => false,
'default' => ''
],
'maxStableTime' => [
'type' => Schema::DT_INT,
'nullable' => false,
'default' => 1,
'index' => true,
]
];
/**
* No static columns added
* @var bool
*/
protected $addStaticFields = false;
/**
* format mass values
* - no decimal separator
@@ -57,4 +101,5 @@ class WormholeModel extends BasicModel {
return $systemStaticData;
}
}

View File

@@ -1,161 +1,107 @@
; Main Config
[PATHFINDER]
NAME = "PATHFINDER"
NAME = PATHFINDER
; installed version (used for CSS/JS cache busting)
VERSION = "v0.0.16"
VERSION = v1.0.0RC1
; contact information (DO NOT CHANGE)
CONTACT = "https://github.com/exodus4d"
CONTACT = https://github.com/exodus4d
; source code (DO NOT CHANGE)
REPO = "https://github.com/exodus4d/pathfinder"
REPO = https://github.com/exodus4d/pathfinder
; Max number of maps an entity can create
MAX_MAPS_PRIVATE = 3
MAX_MAPS_CORPORATION = 3
MAX_MAPS_ALLIANCE = 3
MAX_MAPS_PRIVATE = 3
MAX_MAPS_CORPORATION = 3
MAX_MAPS_ALLIANCE = 3
; Max number of shared entities per map
MAX_SHARED_USER = 10
MAX_SHARED_CORPORATION = 3
MAX_SHARED_ALLIANCE = 2
MAX_SHARED_USER = 10
MAX_SHARED_CORPORATION = 3
MAX_SHARED_ALLIANCE = 2
[PATHFINDER.ENVIRONMENT]
; project environment ("DEVELOP", "PRODUCTION").
; This affects: DB connection, JS build path
SERVER = "DEVELOP"
; show warning on "login" form if /setup route is active
; DO NOT disable this warning unless /setup route is protected by e.g. WebAuth
SHOW_SETUP_WARNING = 1
[PATHFINDER.ENVIRONMENT.DEVELOP]
BASE = /exodus4d/pathfinder
; deployment URL (what you type in the browser
URL = http://localhost/exodus4d/pathfinder
; Verbosity level of the stack trace
DEBUG = 3
; main db
DB_DNS = mysql:host=localhost;port=3306;dbname=
DB_NAME = pathfinder
DB_USER = root
DB_PASS =
; EVE-Online CCP Database export
DB_CCP_DNS = mysql:host=localhost;port=3306;dbname=
DB_CCP_NAME = eve_parallax_min
DB_CCP_USER = root
DB_CCP_PASS =
; SMTP settings
SMTP_HOST = localhost
SMTP_PORT = 25
SMTP_SCHEME = ""
SMTP_USER = pathfinder
SMTP_PASS = root
SMTP_FROM = pathfinder@localhost.com
SMTP_ERROR = pathfinder@localhost.com
[PATHFINDER.ENVIRONMENT.PRODUCTION]
BASE = /www/htdocs/w0128162/www.pathfinder.exodus4d.de
; deployment URL (what you type in the browser
URL = https://www.pathfinder.exodus4d.de
; Verbosity level of the stack trace
DEBUG = 0
; main db
DB_DNS = mysql:host=localhost;port=3306;dbname=
DB_NAME =
DB_USER =
DB_PASS =
; EVE-Online CCP Database export
DB_CCP_DNS = mysql:host=localhost;port=3306;dbname=
DB_CCP_NAME =
DB_CCP_USER =
DB_CCP_PASS =
; SMTP settings
SMTP_HOST = localhost
SMTP_PORT = 25
SMTP_SCHEME = TLS
SMTP_USER =
SMTP_PASS =
SMTP_FROM = registration.pathfinder@exodus4d.de
SMTP_ERROR = pathfinder@exodus4d.de
; ======================================================================================================
; REGISTRATION ====================================================================================
[PATHFINDER.REGISTRATION]
; registration status (0=disabled, 1=enabled)
STATUS = 1
; registration status (for new users) (0=disabled, 1=enabled)
STATUS = 1
; disabled message
MSG_DISABLED = "User registration is currently not allowed"
; use the invite system e.g. beta testing. A "registration key" is required (0=disabled, 1=enabled)
INVITE = 0
MSG_DISABLED = User registration is currently not allowed
; use the invite system (for new users) e.g. beta testing. A "registration key" is required (0=disabled, 1=enabled)
INVITE = 0
; the limit of registration keys. Increase it to hand out more keys
INVITE_LIMIT = 50
INVITE_LIMIT = 50
; ======================================================================================================
; Lifetime for map types (days)
; View ============================================================================================
[PATHFINDER.VIEW]
; static page templates
INDEX = templates/view/index.html
SETUP = templates/view/setup.html
LANDINGPAGE = templates/view/landingpage.html
; HTTP status pages ===============================================================================
[PATHFINDER.STATUS]
; error pages
4XX = templates/status/4xx.html
5XX = templates/status/5xx.html
; MAP =============================================================================================
[PATHFINDER.MAP.PRIVATE]
LIFETIME = 7
LIFETIME = 7
[PATHFINDER.MAP.CORPORATION]
LIFETIME = 99999
LIFETIME = 99999
[PATHFINDER.MAP.ALLIANCE]
LIFETIME = 99999
; ======================================================================================================
[PATHFINDER.CACHE]
LIFETIME = 99999
; cache character log informations in seconds. This is ignored if ship/system switch was detected
CHARACTER_LOG = 300
; cache time for all system data within a constellation (this will never change) 30d
CONSTELLATION_SYSTEMS = 2592000
; ======================================================================================================
; TIMER ===========================================================================================
[PATHFINDER.TIMER]
; login time (minutes)
LOGGED = 240
LOGGED = 240
; double click timer (ms)
DBL_CLICK = 250
DBL_CLICK = 250
; time for status change visibility in header (ms)
PROGRAM_STATUS_VISIBLE = 5000
PROGRAM_STATUS_VISIBLE = 5000
; main map update ping (ajax) (ms)
[PATHFINDER.TIMER.UPDATE_SERVER_MAP]
DELAY = 5000
EXECUTION_LIMIT = 200
DELAY = 5000
EXECUTION_LIMIT = 200
; update client map data (ms)
[PATHFINDER.TIMER.UPDATE_CLIENT_MAP]
EXECUTION_LIMIT = 50
EXECUTION_LIMIT = 50
; map user update ping (ajax) (ms)
[PATHFINDER.TIMER.UPDATE_SERVER_USER_DATA]
DELAY = 5000
EXECUTION_LIMIT = 200
DELAY = 5000
EXECUTION_LIMIT = 200
; update client user data (ms)
[PATHFINDER.TIMER.UPDATE_CLIENT_USER_DATA]
EXECUTION_LIMIT = 50
EXECUTION_LIMIT = 50
; ======================================================================================================
; CACHE ===========================================================================================
[PATHFINDER.CACHE]
; cache character log informations in seconds. This is ignored if ship/system switch was detected
CHARACTER_LOG = 300
; cache time for all system data within a constellation (this will never change) 30d
CONSTELLATION_SYSTEMS = 2592000
; LOGGING =========================================================================================
[PATHFINDER.LOGFILES]
; just for manuel debug during development
DEBUG = "debug"
DEBUG = debug
; user login information
LOGIN = "login"
LOGIN = login
; account deleted
DELETE_ACCOUNT = "delete_account"
DELETE_ACCOUNT = delete_account
; API =============================================================================================
[PATHFINDER.API]
; Path for CCPs XML APIv2
CCP_XML = "https://api.eveonline.com"
CCP_XML = https://api.eveonline.com
GIT_HUB = https://api.github.com

37
app/requirements.ini Normal file
View File

@@ -0,0 +1,37 @@
; Requirements Config (Do not change!)
[REQUIREMENTS]
[REQUIREMENTS.SERVER]
; Apache
APACHE.VERSION = 2.5
; Nginx
NGINX.VERSION = 1.9
[REQUIREMENTS.PHP]
; recommended is >= 5.6
VERSION = 5.6
; "Perl-Compatible Regular Expressions"
; usually shipped with PHP package,
; but needs to be additionally updated on CentOS or Red Hat systems
PCRE_VERSION = 8.02
[REQUIREMENTS.MYSQL]
; min MySQL Version
; newer "deviation" of MySQL like "MariaDB" > 10.1 are recommended
VERSION = 5.6
[REQUIREMENTS.MYSQL.VARS]
; MySql variables. Values are auto. set as 'SESSION' vars
; https://dev.mysql.com/doc/refman/5.5/en/show-variables.html
DEFAULT_STORAGE_ENGINE = InnoDB
CHARACTER_SET_DATABASE = utf8
CHARACTER_SET_CLIENT = utf8
CHARACTER_SET_RESULTS = utf8
CHARACTER_SET_CONNECTION = utf8
COLLATION_DATABASE = utf8_general_ci
COLLATION_CONNECTION = utf8_general_ci
FOREIGN_KEY_CHECKS = ON

View File

@@ -1,4 +1,7 @@
[routes]
; DB setup setup
; IMPORTANT: remove/comment this line after setup/update is finished!
GET @setup: /setup = Controller\Setup->init, 0
; static routes (main views) default cache: 86400
GET|POST @landing: /= Controller\AppController->showLandingpage, 0

View File

@@ -87,6 +87,11 @@
excludeShallow: [
'app'
]
},{
name: 'setup',
excludeShallow: [
'app'
]
},{
name: 'app/notification',
excludeShallow: [

596
export/sql/pathfinder.sql Normal file

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

12
favicon/browserconfig.xml Normal file
View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<browserconfig>
<msapplication>
<tile>
<square70x70logo src="/mstile-70x70.png"/>
<square150x150logo src="/mstile-150x150.png"/>
<square310x310logo src="/mstile-310x310.png"/>
<wide310x150logo src="/mstile-310x150.png"/>
<TileColor>#da532c</TileColor>
</tile>
</msapplication>
</browserconfig>

BIN
favicon/favicon-160x160.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

BIN
favicon/favicon-16x16.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
favicon/favicon-192x192.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

BIN
favicon/favicon-32x32.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

BIN
favicon/favicon-96x96.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

BIN
favicon/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
favicon/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 199 KiB

BIN
favicon/mstile-144x144.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

BIN
favicon/mstile-150x150.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

BIN
favicon/mstile-310x150.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

BIN
favicon/mstile-310x310.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
favicon/mstile-70x70.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

View File

@@ -3,8 +3,10 @@ var gulp = require('gulp-param')(require('gulp'), process.argv);
var jshint = require('gulp-jshint');
var notify = require('gulp-notify');
var plumber = require('gulp-plumber');
var gzip = require('gulp-gzip');
var gulpif = require('gulp-if');
var clean = require('gulp-clean');
var critical = require('critical');
var runSequence = require('run-sequence');
var exec = require('child_process').exec;
@@ -21,12 +23,16 @@ var _src = {
JS_LIBS: './js/lib/**/*',
JS_BUILD: './build_js',
JS_DIST: './public/js',
CSS_SRC: './public/css/*.css',
CSS_DIST: './public/css',
PACKAGE: './package.json',
CACHE: './tmp/**/*.*'
};
// Gulp plumber error handler
var onError = function(err) {
'use strict';
console.log(err);
};
@@ -45,6 +51,7 @@ var tagVersion = null;
* RequireJS build task using the r.js optimizer.
*/
gulp.task('requirejs', ['jshint'], function() {
'use strict';
var rjsPath = path.resolve(__dirname, './node_modules/requirejs/bin/r.js');
var oPath = path.resolve(__dirname, './build.js');
@@ -59,8 +66,9 @@ gulp.task('requirejs', ['jshint'], function() {
}
runSequence(
'copyBuildFiles',
'removeBuildFiles'
'copyJSBuildFiles',
'removeBuildFiles',
'gzipJS'
);
});
});
@@ -73,6 +81,8 @@ gulp.task('requirejs', ['jshint'], function() {
* http://jshint.com/docs/options/
*/
gulp.task('jshint', function(){
'use strict';
return gulp.src([
_src.JS_SRC,
'!' + _src.JS_LIBS
@@ -95,7 +105,8 @@ gulp.task('jshint', function(){
* Copy optimized/uglyfied js files from "js_build" folder to "public/js/x.x.x/*" folder
* for release deployment (cache busting)
*/
gulp.task('copyBuildFiles', ['removeDistFiles'], function () {
gulp.task('copyJSBuildFiles', ['removeDistFiles'], function () {
'use strict';
// raw files
var source = _src.JS_SRC;
@@ -113,11 +124,11 @@ gulp.task('copyBuildFiles', ['removeDistFiles'], function () {
gulp.dest( _src.JS_DIST + '/' + tagVersion )
)
).pipe(notify({
icon: path.resolve(__dirname, _src.ICON),
title: 'Copy JS to dist',
message: 'Task complete',
onLast: true
}));
icon: path.resolve(__dirname, _src.ICON),
title: 'Copy JS to dist',
message: 'Task complete',
onLast: true
}));
});
/**
@@ -125,7 +136,6 @@ gulp.task('copyBuildFiles', ['removeDistFiles'], function () {
*/
gulp.task('removeBuildFiles', function () {
'use strict';
return gulp.src( _src.JS_BUILD ).pipe( clean( _src.JS_BUILD ) );
});
@@ -134,16 +144,42 @@ gulp.task('removeBuildFiles', function () {
*/
gulp.task('removeDistFiles', function () {
'use strict';
var dist = _src.JS_DIST + '/' + tagVersion;
return gulp.src(dist).pipe( clean(dist) );
});
/**
* create *.gz version from minimized *.css
*/
gulp.task('gzipCSS', function() {
'use strict';
return gulp.src(_src.CSS_SRC)
.pipe(gzip({
gzipOptions: { level: 8 }
}))
.pipe(gulp.dest(_src.CSS_DIST));
});
/**
* create *.gz version from minimized *.js
*/
gulp.task('gzipJS', function() {
'use strict';
return gulp.src(_src.JS_DIST + '/' + tagVersion + '/**/*.js')
.pipe(gzip({
gzipOptions: { level: 8 }
}))
.pipe(gulp.dest(_src.JS_DIST + '/' + tagVersion));
});
/*******************************************/
// Watch
// execute only during continuous development!
gulp.task('watch', function(tag) {
gulp.task('watchJSFiles', function(tag) {
'use strict';
if(tag){
tagVersion = tag;
@@ -152,19 +188,20 @@ gulp.task('watch', function(tag) {
gulp.watch([
_src.JS_SRC,
'!' + _src.JS_LIBS,
], ['jshint', 'copyBuildFiles']);
], ['jshint', 'copyJSBuildFiles']);
});
/**
* clear all backend (fat free framework) cache files
*/
gulp.task('clearCache', function() {
gulp.task('watchCSSFiles', function(tag) {
'use strict';
return gulp.src( _src.CACHE ).pipe( clean() );
});
if(tag){
tagVersion = tag;
}
gulp.watch([
_src.CSS_SRC,
], ['gzipCSS']);
});
/*******************************************/
// Default Tasks
@@ -175,6 +212,7 @@ gulp.task('clearCache', function() {
* WARNING: DO NOT REMOVE THIS TASK!!!
*/
gulp.task('production', function(tag) {
'use strict';
if(tag !== null){
tagVersion = tag;
@@ -182,6 +220,7 @@ gulp.task('production', function(tag) {
// use run-sequence until gulp v4.0 is released
runSequence(
'gzipCSS',
'requirejs'
);
}
@@ -193,14 +232,44 @@ gulp.task('production', function(tag) {
* WARNING: DO NOT REMOVE THIS TASK!!!
*/
gulp.task('default', function(tag) {
'use strict';
if(tag){
tagVersion = tag;
}
runSequence(
'gzipCSS',
'jshint',
'copyBuildFiles',
'watch'
'copyJSBuildFiles',
'watchJSFiles',
'watchCSSFiles'
);
});
/*
// This removes all CSS styles "above the fold" from *.css and inlines them
// -> to improve pagespeed. The remaining (main) css file will be lazy loaded afterwards...
// https://github.com/addyosmani/critical
gulp.task('critical', function (cb) {
critical.generate({
inline: true,
base: './',
src: './public/templates/view/index.html',
dest: './public/templates/view/index-critical.html',
extract: true,
minify: true,
width: 2560,
height: 1440
});
});
*/
/**
* clear all backend (fat free framework) cache files
*/
gulp.task('clearCache', function() {
'use strict';
return gulp.src( _src.CACHE ).pipe( clean() );
});

View File

@@ -1,19 +1,9 @@
<?php
$f3 = require('app/lib/base.php');
// load main config
$f3->config('app/config.ini');
// load route config
$f3->config('app/routes.ini');
// load pathfinder config
$f3->config('app/pathfinder.ini');
// load cron config
$f3->config('app/cron.ini');
// set base dir
$f3->set('BASE', \Controller\Controller::getEnvironmentData('BASE'));

View File

@@ -19,6 +19,7 @@ requirejs.config({
// main views
landingpage: './app/landingpage', // initial start "landing page" view
mappage: './app/mappage', // initial start "map page" view
setup: './app/setup', // initial start "setup page" view
jquery: 'lib/jquery-1.11.3.min', // v1.11.3 jQuery
bootstrap: 'lib/bootstrap.min', // v3.3.0 Bootstrap js code - http://getbootstrap.com/javascript/
@@ -27,7 +28,7 @@ requirejs.config({
velocity: 'lib/velocity.min', // v1.2.2 animation engine - http://julian.com/research/velocity/
velocityUI: 'lib/velocity.ui.min', // v5.0.4 plugin for velocity - http://julian.com/research/velocity/#uiPack
slidebars: 'lib/slidebars', // v0.10 Slidebars - side menu plugin http://plugins.adchsm.me/slidebars/
jsPlumb: 'lib/dom.jsPlumb-1.7.6-min', // v1.7.6 jsPlumb (Vanilla)- main map draw plugin https://jsplumbtoolkit.com/
jsPlumb: 'lib/jsPlumb-2.0.5-min', // v2.0.5 jsPlumb (Vanilla)- main map draw plugin https://jsplumbtoolkit.com/
farahey: 'lib/farahey-0.5', // v0.5 jsPlumb "magnetizing" extension - https://github.com/jsplumb/farahey
customScrollbar: 'lib/jquery.mCustomScrollbar.concat.min', // v3.0.9 Custom scroll bars - http://manos.malihu.gr/
datatables: 'lib/datatables/jquery.dataTables.min', // v1.10.7 DataTables - https://datatables.net/
@@ -51,6 +52,7 @@ requirejs.config({
blueImpGalleryBootstrap: 'lib/bootstrap-image-gallery', // v3.1.1 Bootstrap extension for Blue Imp Gallery - https://blueimp.github.io/Bootstrap-Image-Gallery/
bootstrapConfirmation: 'lib/bootstrap-confirmation', // v1.0.1 Bootstrap extension for inline confirm dialog - https://github.com/tavicu/bs-confirmation
bootstrapToggle: 'lib/bootstrap2-toggle.min', // v2.2.0 Bootstrap Toggle (Checkbox) - http://www.bootstraptoggle.com/
lazyload: 'lib/jquery.lazyload.min', // v1.9.5 LazyLoader images - http://www.appelsiini.net/projects/lazyload
// header animation
easePack: 'lib/EasePack.min',
@@ -142,6 +144,9 @@ requirejs.config({
},
bootstrapToggle: {
deps : ['jquery']
},
lazyload: {
deps : ['jquery']
}
}
});

View File

@@ -15,7 +15,7 @@ define(['jquery'], function($) {
logIn: 'api/user/logIn', // ajax URL - login
logOut: 'api/user/logOut', // ajax URL - logout
deleteLog: 'api/user/deleteLog', // ajax URL - delete character log
saveUserConfig: 'api/user/saveConfig', // ajax URL - saves custom configuration
saveUserConfig: 'api/user/saveAccount', // ajax URL - saves/update user account
saveSharingConfig: 'api/user/saveSharingConfig', // ajax URL - save "sharing settings" dialog
deleteAccount: 'api/user/deleteAccount', // ajax URL - delete Account data
// access API
@@ -43,7 +43,9 @@ define(['jquery'], function($) {
saveSignatureData: 'api/signature/save', // ajax URL - save signature data for system
deleteSignatureData: 'api/signature/delete', // ajax URL - delete signature data for system
// route API
searchRoute: 'api/route/search' // ajax URL - search system routes
searchRoute: 'api/route/search', // ajax URL - search system routes
// GitHub API
gitHubReleases: 'api/github/releases' // ajax URL - get release info from GitHub
},
url: {
ccpImageServer: 'https://image.eveonline.com/', // CCP image Server

View File

@@ -10,20 +10,23 @@ define([
'app/ccp',
'blueImpGallery',
'bootbox',
'lazyload',
'app/ui/header',
'app/ui/logo',
'app/ui/demo_map',
'dialog/account_settings',
'dialog/notification',
'dialog/manual',
'dialog/releases',
'dialog/credit'
], function($, Init, Util, Render, CCP, Gallery, bootbox) {
'use strict';
var config = {
// header
splashOverlayClass: 'pf-splash', // class for "splash" overlay
// header
headerContainerId: 'pf-header-container', // id for header container
logoContainerId: 'pf-logo-container', // id for main header logo container
headHeaderMapId: 'pf-header-map', // id for header image (svg animation)
@@ -37,7 +40,8 @@ define([
// navigation
navigationElementId: 'pf-navbar', // id for navbar element
navigationLinkManualClass: 'pf-navbar-manual', // class for "manual" trigger link
navigationLinkLicenseClass : 'pf-navbar-license', // class for "license" trigger link
navigationLinkLicenseClass: 'pf-navbar-license', // class for "license" trigger link
navigationVersionLinkClass: 'pf-navbar-version-info', // class for "version information"
// login form
loginFormId: 'pf-login-form', // id for login form
@@ -47,6 +51,7 @@ define([
// gallery
galleryId: 'pf-gallery', // id for gallery container
galleryThumbImageClass: 'pf-landing-image-preview', // class for gallery thumb images
galleryThumbContainerId: 'pf-landing-gallery-thumb-container', // id for gallery thumb images
galleryCarouselId: 'pf-landing-gallery-carousel', // id for "carousel" element
@@ -66,7 +71,11 @@ define([
e.preventDefault();
// logout current user (if there e.g. to register a second account)
Util.logout();
Util.logout({
ajaxData: {
reroute: 0
}
});
// show register/settings dialog
$.fn.showSettingsDialog({
@@ -75,7 +84,6 @@ define([
});
});
// login buttons ------------------------------------------------
var loginForm = $('#' + config.loginFormId);
@@ -103,7 +111,6 @@ define([
data: loginData,
dataType: 'json'
}).done(function(data){
// login error
if(data.error !== undefined){
$('.' + config.splashOverlayClass).hideSplashOverlay();
@@ -125,6 +132,11 @@ define([
}
});
// releases -----------------------------------------------------
$('.' + config.navigationVersionLinkClass).on('click', function(e){
$.fn.releasesDialog();
});
// manual -------------------------------------------------------
$('.' + config.navigationLinkManualClass).on('click', function(e){
e.preventDefault();
@@ -397,6 +409,35 @@ define([
});
};
var initYoutube = function(){
$(".youtube").each(function() {
// Based on the YouTube ID, we can easily find the thumbnail image
$(this).css('background-image', 'url(https://i.ytimg.com/vi/' + this.id + '/sddefault.jpg)');
// Overlay the Play icon to make it look like a video player
$(this).append($('<div/>', {'class': 'play'}));
$(document).delegate('#'+this.id, 'click', function() {
// Create an iFrame with autoplay set to true
var iframe_url = "https://www.youtube.com/embed/" + this.id + "?autoplay=1&autohide=1";
if ($(this).data('params')) iframe_url+='&'+$(this).data('params');
// The height and width of the iFrame should be the same as parent
var iframe = $('<iframe/>', {
frameborder: '0',
src: iframe_url,
width: $(this).width(),
height: $(this).height(),
class: 'pricing-big'
});
// Replace the YouTube thumbnail with YouTube HTML5 Player
$(this).replaceWith(iframe);
});
});
};
/**
* init scrollspy for navigation bar
@@ -444,7 +485,7 @@ define([
};
/**
* main init landing page
* main init "landing" page
*/
$(function(){
@@ -454,6 +495,7 @@ define([
// show log off message
var isLogOut = location.search.split('logout')[1];
if(isLogOut !== undefined){
// show logout dialog
var options = {
buttons: {
@@ -488,8 +530,10 @@ define([
showRequestRegistrationKeyDialog();
}
// init scrollspy
initScrollspy();
// init "lazy loading" for images
$('.' + config.galleryThumbImageClass).lazyload({
threshold : 300
});
// hide splash loading animation
$('.' + config.splashOverlayClass).hideSplashOverlay();
@@ -497,9 +541,15 @@ define([
// init carousel
initCarousel();
// init scrollspy
initScrollspy();
// init gallery
initGallery();
// init youtube videos
initYoutube();
// show logo -> hide animation in IGB
if( !CCP.isInGameBrowser() ){
$('#' + config.logoContainerId).drawLogo(function(){
@@ -511,7 +561,6 @@ define([
}, false);
}
setPageObserver();
});

View File

@@ -98,19 +98,23 @@ define([
dragOptions:{
},
connectionsDetachable: true, // dragOptions are set -> allow detaching them
maxConnections: 10 // due to isTarget is true, this is the max count of !out!-going connections
maxConnections: 10, // due to isTarget is true, this is the max count of !out!-going connections
// isSource:true,
anchor: 'Continuous'
},
target: {
filter: '.' + config.systemHeadNameClass,
isSource:true,
//isTarget:true,
allowLoopback: false, // loopback connections are not allowed
//allowLoopback: false, // loopback connections are not allowed
cssClass: config.endpointTargetClass,
dropOptions: {
tolerance: 'touch',
hoverClass: config.systemActiveClass,
activeClass: 'dragActive',
}
activeClass: 'dragActive'
},
// isTarget:true,
// uniqueEndpoint: false,
anchor: 'Continuous'
},
connectionTypes: Init.connectionTypes
};
@@ -317,7 +321,6 @@ define([
/**
* show/hide systems tooltip
* @param systems
* @param show
* @param options
*/
@@ -350,7 +353,7 @@ define([
}else if(show === 'show'){
// check if tooltip is currently visible
var tooltipActive = (system.attr('aria-describedby') !== undefined ? true : false);
var tooltipActive = (system.attr('aria-describedby') !== undefined);
if(options === undefined){
options = {};
@@ -858,9 +861,9 @@ define([
mapElement.getMapOverlay('timer').startMapUpdateCounter();
var systemElements = mapElement.find('.' + config.systemClass);
var endpointElements = mapElement.find('._jsPlumb_endpoint');
var connectorElements = mapElement.find('._jsPlumb_connector');
var overlayElements = mapElement.find('._jsPlumb_overlay, .tooltip');
var endpointElements = mapElement.find('.jsplumb-endpoint');
var connectorElements = mapElement.find('.jsplumb-connector');
var overlayElements = mapElement.find('.jsplumb-overlay, .tooltip');
// if map empty (no systems), execute callback and return
// no visual effects in IGB (glitches)
@@ -1004,9 +1007,9 @@ define([
/**
* draw a system with its data to a map
* @param map object
* @param map
* @param systemData
* @param {String[]} optional Systems for connection
* @param connectedSystem
*/
var drawSystem = function(map, systemData, connectedSystem){
@@ -1165,7 +1168,6 @@ define([
/**
* update z-index for a system (dragged systems should be always on top)
* @param system
*/
$.fn.updateSystemZIndex = function(){
return this.each(function(){
@@ -1333,6 +1335,7 @@ define([
/**
* delete a connection and all related data
* @param connections
* @param callback
*/
$.fn.deleteConnections = function(connections, callback){
if(connections.length > 0){
@@ -1660,7 +1663,7 @@ define([
{subIcon: 'fa-info', subAction: 'ingame_show_info', subText: 'show info'},
{subDivider: true, action: 'ingame'},
{subIcon: 'fa-flag', subAction: 'ingame_add_waypoint', subText: 'add waypoint'},
{subIcon: 'fa-flag-checkered', subAction: 'ingame_set_destination', subText: 'set destination'},
{subIcon: 'fa-flag-checkered', subAction: 'ingame_set_destination', subText: 'set destination'}
]},
{divider: true, action: 'delete_system'},
{icon: 'fa-eraser', action: 'delete_system', text: 'delete system'}
@@ -1746,32 +1749,31 @@ define([
// show tooltip
dragSystem.toggleSystemTooltip('show', {show: true});
// mark as "changed"
dragSystem.markAsChanged();
// set new position for popover edit field (system name)
var newPosition = dragSystem.position();
var placement = 'top';
if(newPosition.top < 100){
placement = 'bottom';
}
if(newPosition.left < 100){
placement = 'right';
}
dragSystem.find('.' + config.systemHeadNameClass).editable('option', 'placement', placement);
// drag system is not always selected
var selectedSystems = mapContainer.getSelectedSystems().get();
selectedSystems = selectedSystems.concat( dragSystem.get() );
selectedSystems = $.unique( selectedSystems );
for(var i = 0; i < selectedSystems.length; i++){
var tempSystem = $(selectedSystems[i]);
// set all selected systems as "changes" for update
tempSystem.markAsChanged();
// set new position for popover edit field (system name)
var tempPosition = tempSystem.position();
var placement = 'top';
if(tempPosition.top < 100){
placement = 'bottom';
}
if(tempPosition.left < 100){
placement = 'right';
}
tempSystem.find('.' + config.systemHeadNameClass).editable('option', 'placement', placement);
// repaint connections -> just in case something fails...
map.revalidate( tempSystem.attr('id') );
}
}
});
@@ -1964,7 +1966,7 @@ define([
break;
case 'delete_system':
// confirm dialog
var systemDeleteDialog = bootbox.confirm('Delete system and all its connections?', function(result) {
bootbox.confirm('Delete system and all its connections?', function(result) {
if(result){
var systemName = currentSystem.getSystemInfo(['alias']);
deleteSystems(map, [currentSystem], function(){
@@ -2130,12 +2132,8 @@ define([
* @returns {*}
*/
$.fn.getSelectedSystems = function(){
var mapElement = $(this);
var systems = mapElement.find('.' + config.systemSelectedClass);
return systems;
return mapElement.find('.' + config.systemSelectedClass);
};
/**
@@ -2436,7 +2434,6 @@ define([
}
});
// catch events =========================================================
// toggle global map option (e.g. "grid snap", "magnetization"
@@ -3362,15 +3359,14 @@ define([
var newJsPlumbInstance = jsPlumb.getInstance({
Anchor: 'Continuous', // anchors on each site
Container: null, // will be set as soon as container is connected to DOM
PaintStyle:{
PaintStyle: {
lineWidth: 4, // width of a Connector's line. An integer.
strokeStyle: 'red', // color for a Connector
outlineColor: 'red', // color of the outline for an Endpoint or Connector. see fillStyle examples.
outlineWidth: 2 // width of the outline for an Endpoint or Connector. An integer.
},
Connector:[ 'Bezier', { curviness: 40 } ], // default connector style (this is not used!) all connections have their own style (by scope)
Endpoints: [ [ 'Dot', { radius: 5 } ], [ 'Dot', { radius: 5 } ] ],
// Endpoint: 'Blank', // does not work... :(
Connector: [ 'Bezier', { curviness: 40 } ], // default connector style (this is not used!) all connections have their own style (by scope)
Endpoint: [ 'Dot', { radius: 5 } ],
ReattachConnections: false, // re-attach connection if dragged with mouse to "nowhere"
Scope: Init.defaultMapScope, // default map scope for connections
LogEnabled: true

View File

@@ -16,6 +16,9 @@ define([
'use strict';
/**
* main init "map" page
*/
$(function(){
// load page
$('body').loadPageStructure();

View File

@@ -481,7 +481,8 @@ define([
var moduleData = {
id: config.pageFooterId,
footerLicenceLinkClass: config.footerLicenceLinkClass
footerLicenceLinkClass: config.footerLicenceLinkClass,
currentYear: new Date().getFullYear()
};
var headRendered = Mustache.render(TplFooter, moduleData);
@@ -612,7 +613,11 @@ define([
$(document).on('pf:menuLogout', function(e, data){
// logout
Util.logout();
Util.logout({
ajaxData: {
reroute: 1
}
});
return false;
});

53
js/app/setup.js Normal file
View File

@@ -0,0 +1,53 @@
/**
* Main landingPage application
*/
define([
'jquery',
'app/init',
'app/util'
], function($, Init, Util) {
'use strict';
var config = {
splashOverlayClass: 'pf-splash' // class for "splash" overlay
};
/**
* set page observer
*/
var setPageObserver = function(){
// collapse ---------------------------------------
$('body').find('[data-toggle="collapse"]').css({cursor: 'pointer'}).on('click', function(){
$(this).find('.pf-animate-rotate').toggleClass('right');
});
// buttons ----------------------------------------
$('body').find('.btn').on('click', function(e){
$('.' + config.splashOverlayClass).showSplashOverlay();
});
// tooltips ---------------------------------------
$('body').initTooltips();
// change url (remove logout parameter)
if (history.pushState) {
history.pushState({}, '', location.protocol + '//' + location.host + location.pathname);
}
};
/**
* main init "setup" page
*/
$(function(){
// show app information in browser console --------
Util.showVersionInfo();
// hide splash loading animation ------------------
$('.' + config.splashOverlayClass).hideSplashOverlay();
setPageObserver();
});
});

View File

@@ -225,20 +225,17 @@ define([
currentActiveLink.addClass('btn-primary');
currentActiveTab.next('li').find('a').tab('show');
};
// validate form
form.validator('validate');
var formValid = form.isValidForm();
if(!formValid){
currentActiveTab.removeClass('disabled');
currentActiveLink.removeClass('btn-default btn-primary');
currentActiveLink.addClass('btn-danger');
}else{
var tabFormValues = form.getFormValues();
if(! $.isEmptyObject(tabFormValues) ){
@@ -299,7 +296,7 @@ define([
selectCharacterDialog.find('.modal-content').hideLoadingAnimation();
var reason = status + ' ' + error;
Util.showNotify({title: jqXHR.status + ': saveConfig', text: reason, type: 'error'});
Util.showNotify({title: jqXHR.status + ': saveAccount', text: reason, type: 'error'});
// set new captcha for any request
// captcha is required for sensitive data (not for all)

View File

@@ -0,0 +1,100 @@
/**
* releases dialog (GitHub API repository information)
*/
define([
'jquery',
'app/init',
'app/util',
'app/render',
'bootbox'
], function($, Init, Util, Render, bootbox) {
'use strict';
var config = {
releasesDialogClass: 'pf-releases-dialog' // class for "Releases" dialog
};
/**
* load release information in dialog
* @param releasesDialog
*/
var loadDialogData = function(releasesDialog){
// lock dialog
var dialogContent = releasesDialog.find('.modal-content');
dialogContent.showLoadingAnimation();
$.ajax({
type: 'POST',
url: Init.path.gitHubReleases,
// data: updatedMapData,
dataType: 'json'
}).done(function(releasesData){
requirejs(['text!templates/ui/timeline_element.html', 'mustache'], function(template, Mustache) {
for(var i = 0; i < releasesData.length; i++){
var releaseData = releasesData[i];
// template vars
var data = {
isFirst: (i === 0),
isOdd: (i % 2 !== 0),
releaseDate: releaseData.published_at.substr(0, 10),
releaseData: releaseData
};
var content = Mustache.render(template, data);
releasesDialog.find('ul.timeline').append(content);
}
// console.log()
$('.timeline > li').velocity('transition.expandIn', {
stagger: 300,
duration: 240,
//display: 'auto',
complete: function(){
}
});
});
}).fail(function( jqXHR, status, error) {
var reason = status + ' ' + jqXHR.status + ': ' + error;
Util.showNotify({title: jqXHR.status + ': login', text: reason, type: 'error'});
}).always(function() {
dialogContent.hideLoadingAnimation();
});
};
/**
* show releases dialog
*/
$.fn.releasesDialog = function(){
requirejs(['text!templates/dialog/releases.html', 'mustache'], function(template, Mustache) {
var data = {
test: 'blaBla'
};
var content = Mustache.render(template, data);
var releasesDialog = bootbox.dialog({
className: config.releasesDialogClass,
title: 'Releases',
size: 'large',
message: content
});
// after modal is shown =======================================================================
releasesDialog.on('shown.bs.modal', function(e) {
loadDialogData(releasesDialog);
});
});
};
});

View File

@@ -72,7 +72,7 @@ define([
}
},
trust: {
label: '<i class="fa fa-fw fa-lock"></i> trust set',
label: '<i class="fa fa-fw fa-lock"></i> set "Trust"',
className: 'btn-primary',
callback: function(){
var dialog = $(this);

View File

@@ -489,7 +489,6 @@ define([
tooltipData.created.character &&
tooltipData.updated.character
){
var createdData = tooltipData.created;
var updatedData = tooltipData.updated;
@@ -1626,7 +1625,10 @@ define([
var currentUrl = document.URL;
if(url !== currentUrl){
if(params.length > 0){
if(
params &&
params.length > 0
){
url += '?' + params.join('&');
}
window.location = url;
@@ -1635,16 +1637,24 @@ define([
/**
* send logout request
* @param params
*/
var logout = function(){
var logout = function(params){
var data = {};
if(
params &&
params.ajaxData
){
data = params.ajaxData;
}
$.ajax({
type: 'POST',
url: Init.path.logOut,
data: {},
data: data,
dataType: 'json'
}).done(function(data){
if(data.reroute !== undefined){
if(data.reroute){
redirect(data.reroute, ['logout']);
}
}).fail(function( jqXHR, status, error) {

View File

@@ -5,5 +5,4 @@
* Copyright 2014 Min Hur, The New York Times Company
* Licensed under MIT
* ======================================================================== */
+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.toggle"),f="object"==typeof b&&b;e||d.data("bs.toggle",e=new c(this,f)),"string"==typeof b&&e[b]&&e[b]()})}var c=function(b,c){this.$element=a(b),this.options=a.extend({},this.defaults(),c),this.render()};c.VERSION="2.2.0",c.DEFAULTS={on:"On",off:"Off",onstyle:"primary",offstyle:"default",size:"normal",style:"",width:null,height:null},c.prototype.defaults=function(){return{on:this.$element.attr("data-on")||c.DEFAULTS.on,off:this.$element.attr("data-off")||c.DEFAULTS.off,onstyle:this.$element.attr("data-onstyle")||c.DEFAULTS.onstyle,offstyle:this.$element.attr("data-offstyle")||c.DEFAULTS.offstyle,size:this.$element.attr("data-size")||c.DEFAULTS.size,style:this.$element.attr("data-style")||c.DEFAULTS.style,width:this.$element.attr("data-width")||c.DEFAULTS.width,height:this.$element.attr("data-height")||c.DEFAULTS.height}},c.prototype.render=function(){this._onstyle="btn-"+this.options.onstyle,this._offstyle="btn-"+this.options.offstyle;var b="large"===this.options.size?"btn-large":"small"===this.options.size?"btn-small":"mini"===this.options.size?"btn-mini":"",c=a('<label class="btn">').html(this.options.on).addClass(this._onstyle+" "+b),d=a('<label class="btn">').html(this.options.off).addClass(this._offstyle+" "+b+" active"),e=a('<span class="toggle-handle btn btn-default">').addClass(b),f=a('<div class="toggle-group">').append(c,d,e),g=a('<div class="toggle btn" data-toggle="toggle">').addClass(this.$element.prop("checked")?this._onstyle:this._offstyle+" off").addClass(b).addClass(this.options.style);this.$element.wrap(g),a.extend(this,{$toggle:this.$element.parent(),$toggleOn:c,$toggleOff:d,$toggleGroup:f}),this.$toggle.append(f);var h=this.options.width||Math.max(c.width(),d.width())+e.outerWidth()/2,i=this.options.height||Math.max(c.height(),d.height());c.addClass("toggle-on"),d.addClass("toggle-off"),this.$toggle.css({width:h,height:i}),this.options.height&&(c.css("line-height",c.height()+"px"),d.css("line-height",d.height()+"px")),this.update(!0),this.trigger(!0)},c.prototype.toggle=function(){this.$element.prop("checked")?this.off():this.on()},c.prototype.on=function(a){return this.$element.prop("disabled")?!1:(this.$toggle.removeClass(this._offstyle+" off").addClass(this._onstyle),this.$element.prop("checked",!0),void(a||this.trigger()))},c.prototype.off=function(a){return this.$element.prop("disabled")?!1:(this.$toggle.removeClass(this._onstyle).addClass(this._offstyle+" off"),this.$element.prop("checked",!1),void(a||this.trigger()))},c.prototype.enable=function(){this.$toggle.removeAttr("disabled"),this.$element.prop("disabled",!1)},c.prototype.disable=function(){this.$toggle.attr("disabled","disabled"),this.$element.prop("disabled",!0)},c.prototype.update=function(a){this.$element.prop("disabled")?this.disable():this.enable(),this.$element.prop("checked")?this.on(a):this.off(a)},c.prototype.trigger=function(b){this.$element.off("change.bs.toggle"),b||this.$element.change(),this.$element.on("change.bs.toggle",a.proxy(function(){this.update()},this))},c.prototype.destroy=function(){this.$element.off("change.bs.toggle"),this.$toggleGroup.remove(),this.$element.removeData("bs.toggle"),this.$element.unwrap()};var d=a.fn.bootstrapToggle;a.fn.bootstrapToggle=b,a.fn.bootstrapToggle.Constructor=c,a.fn.toggle.noConflict=function(){return a.fn.bootstrapToggle=d,this},a(function(){a("input[type=checkbox][data-toggle^=toggle]").bootstrapToggle()}),a(document).on("click.bs.toggle","div[data-toggle^=toggle]",function(b){var c=a(this).find("input[type=checkbox]");c.bootstrapToggle("toggle"),b.preventDefault()})}(jQuery);
//# sourceMappingURL=bootstrap2-toggle.min.js.map
+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.toggle"),f="object"==typeof b&&b;e||d.data("bs.toggle",e=new c(this,f)),"string"==typeof b&&e[b]&&e[b]()})}var c=function(b,c){this.$element=a(b),this.options=a.extend({},this.defaults(),c),this.render()};c.VERSION="2.2.0",c.DEFAULTS={on:"On",off:"Off",onstyle:"primary",offstyle:"default",size:"normal",style:"",width:null,height:null},c.prototype.defaults=function(){return{on:this.$element.attr("data-on")||c.DEFAULTS.on,off:this.$element.attr("data-off")||c.DEFAULTS.off,onstyle:this.$element.attr("data-onstyle")||c.DEFAULTS.onstyle,offstyle:this.$element.attr("data-offstyle")||c.DEFAULTS.offstyle,size:this.$element.attr("data-size")||c.DEFAULTS.size,style:this.$element.attr("data-style")||c.DEFAULTS.style,width:this.$element.attr("data-width")||c.DEFAULTS.width,height:this.$element.attr("data-height")||c.DEFAULTS.height}},c.prototype.render=function(){this._onstyle="btn-"+this.options.onstyle,this._offstyle="btn-"+this.options.offstyle;var b="large"===this.options.size?"btn-large":"small"===this.options.size?"btn-small":"mini"===this.options.size?"btn-mini":"",c=a('<label class="btn">').html(this.options.on).addClass(this._onstyle+" "+b),d=a('<label class="btn">').html(this.options.off).addClass(this._offstyle+" "+b+" active"),e=a('<span class="toggle-handle btn btn-default">').addClass(b),f=a('<div class="toggle-group">').append(c,d,e),g=a('<div class="toggle btn" data-toggle="toggle">').addClass(this.$element.prop("checked")?this._onstyle:this._offstyle+" off").addClass(b).addClass(this.options.style);this.$element.wrap(g),a.extend(this,{$toggle:this.$element.parent(),$toggleOn:c,$toggleOff:d,$toggleGroup:f}),this.$toggle.append(f);var h=this.options.width||Math.max(c.width(),d.width())+e.outerWidth()/2,i=this.options.height||Math.max(c.height(),d.height());c.addClass("toggle-on"),d.addClass("toggle-off"),this.$toggle.css({width:h,height:i}),this.options.height&&(c.css("line-height",c.height()+"px"),d.css("line-height",d.height()+"px")),this.update(!0),this.trigger(!0)},c.prototype.toggle=function(){this.$element.prop("checked")?this.off():this.on()},c.prototype.on=function(a){return this.$element.prop("disabled")?!1:(this.$toggle.removeClass(this._offstyle+" off").addClass(this._onstyle),this.$element.prop("checked",!0),void(a||this.trigger()))},c.prototype.off=function(a){return this.$element.prop("disabled")?!1:(this.$toggle.removeClass(this._onstyle).addClass(this._offstyle+" off"),this.$element.prop("checked",!1),void(a||this.trigger()))},c.prototype.enable=function(){this.$toggle.removeAttr("disabled"),this.$element.prop("disabled",!1)},c.prototype.disable=function(){this.$toggle.attr("disabled","disabled"),this.$element.prop("disabled",!0)},c.prototype.update=function(a){this.$element.prop("disabled")?this.disable():this.enable(),this.$element.prop("checked")?this.on(a):this.off(a)},c.prototype.trigger=function(b){this.$element.off("change.bs.toggle"),b||this.$element.change(),this.$element.on("change.bs.toggle",a.proxy(function(){this.update()},this))},c.prototype.destroy=function(){this.$element.off("change.bs.toggle"),this.$toggleGroup.remove(),this.$element.removeData("bs.toggle"),this.$element.unwrap()};var d=a.fn.bootstrapToggle;a.fn.bootstrapToggle=b,a.fn.bootstrapToggle.Constructor=c,a.fn.toggle.noConflict=function(){return a.fn.bootstrapToggle=d,this},a(function(){a("input[type=checkbox][data-toggle^=toggle]").bootstrapToggle()}),a(document).on("click.bs.toggle","div[data-toggle^=toggle]",function(b){var c=a(this).find("input[type=checkbox]");c.bootstrapToggle("toggle"),b.preventDefault()})}(jQuery);

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