map/system/connection update functionality (backend)

This commit is contained in:
exodus4d
2015-02-28 14:08:24 +01:00
parent 503a31c48e
commit 417961cd1e
54 changed files with 7173 additions and 337 deletions

110
.idea/dataSources.ids generated
View File

@@ -3573,6 +3573,55 @@
<column name="password_expired" sqlType="ENUM" length="2" precision="0" nullable="false" jdbcType="1" def="J04n"/>
<primary-key name="PRIMARY" columns="Host,User"/>
</table>
<table name="connection_scope" schema="" catalog="pathfinder" type="TABLE">
<column name="id" sqlType="INT" length="10" precision="0" nullable="false" jdbcType="4" autoIncrement="true"/>
<column name="created" sqlType="TIMESTAMP" length="19" precision="0" nullable="false" jdbcType="93" def="Q1VSUkVOVF9USU1FU1RBTVA="/>
<column name="updated" sqlType="TIMESTAMP" length="19" precision="0" nullable="false" jdbcType="93" def="Q1VSUkVOVF9USU1FU1RBTVA=" version="true"/>
<column name="active" sqlType="TINYINT" length="3" precision="0" nullable="false" jdbcType="-6" def="MQ=="/>
<column name="name" sqlType="VARCHAR" length="255" precision="0" nullable="false" jdbcType="12"/>
<column name="label" sqlType="VARCHAR" length="255" precision="0" nullable="false" jdbcType="12"/>
<primary-key name="PRIMARY" columns="id"/>
<index name="active" unique="false" columns="active"/>
<index name="created" unique="false" columns="created"/>
<index name="updated" unique="false" columns="updated"/>
</table>
<table name="map" schema="" catalog="pathfinder" type="TABLE">
<column name="id" sqlType="INT" length="10" precision="0" nullable="false" jdbcType="4" autoIncrement="true"/>
<column name="created" sqlType="DATETIME" length="19" precision="0" nullable="false" jdbcType="93" def="Q1VSUkVOVF9USU1FU1RBTVA="/>
<column name="updated" sqlType="TIMESTAMP" length="19" precision="0" nullable="false" jdbcType="93" def="Q1VSUkVOVF9USU1FU1RBTVA=" version="true"/>
<column name="active" sqlType="TINYINT" length="3" precision="0" nullable="false" jdbcType="-6" def="MQ=="/>
<column name="scopeId" sqlType="TINYINT" length="3" precision="0" nullable="false" jdbcType="-6"/>
<column name="typeId" sqlType="TINYINT" length="3" precision="0" nullable="false" jdbcType="-6"/>
<column name="name" sqlType="VARCHAR" length="255" precision="0" nullable="false" jdbcType="12"/>
<column name="icon" sqlType="VARCHAR" length="255" precision="0" nullable="false" jdbcType="12"/>
<primary-key name="PRIMARY" columns="id"/>
<index name="active" unique="false" columns="active"/>
<index name="created" unique="false" columns="created"/>
<index name="updated" unique="false" columns="updated"/>
</table>
<table name="map_scope" schema="" catalog="pathfinder" type="TABLE">
<column name="id" sqlType="INT" length="10" precision="0" nullable="false" jdbcType="4" autoIncrement="true"/>
<column name="created" sqlType="DATETIME" length="19" precision="0" nullable="false" jdbcType="93" def="Q1VSUkVOVF9USU1FU1RBTVA="/>
<column name="updated" sqlType="TIMESTAMP" length="19" precision="0" nullable="false" jdbcType="93" def="Q1VSUkVOVF9USU1FU1RBTVA=" version="true"/>
<column name="active" sqlType="TINYINT" length="3" precision="0" nullable="false" jdbcType="-6" def="MQ=="/>
<column name="name" sqlType="VARCHAR" length="255" precision="0" nullable="false" jdbcType="12"/>
<column name="label" sqlType="VARCHAR" length="255" precision="0" nullable="false" jdbcType="12"/>
<primary-key name="PRIMARY" columns="id"/>
</table>
<table name="map_type" schema="" catalog="pathfinder" type="TABLE">
<column name="id" sqlType="INT" length="10" precision="0" nullable="false" jdbcType="4" autoIncrement="true"/>
<column name="created" sqlType="DATETIME" length="19" precision="0" nullable="false" jdbcType="93" def="Q1VSUkVOVF9USU1FU1RBTVA="/>
<column name="updated" sqlType="TIMESTAMP" length="19" precision="0" nullable="false" jdbcType="93" def="Q1VSUkVOVF9USU1FU1RBTVA=" version="true"/>
<column name="active" sqlType="TINYINT" length="3" precision="0" nullable="false" jdbcType="-6" def="MQ=="/>
<column name="name" sqlType="VARCHAR" length="255" precision="0" nullable="false" jdbcType="12"/>
<column name="label" sqlType="VARCHAR" length="255" precision="0" nullable="false" jdbcType="12"/>
<column name="class" sqlType="VARCHAR" length="255" precision="0" nullable="false" jdbcType="12"/>
<column name="classTab" sqlType="VARCHAR" length="255" precision="0" nullable="false" jdbcType="12"/>
<primary-key name="PRIMARY" columns="id"/>
<index name="active" unique="false" columns="active"/>
<index name="created" unique="false" columns="created"/>
<index name="updated" unique="false" columns="updated"/>
</table>
<table name="sessions" schema="" catalog="pathfinder" type="TABLE">
<column name="session_id" sqlType="VARCHAR" length="40" precision="0" nullable="false" jdbcType="12" def="Jyc="/>
<column name="data" sqlType="TEXT" length="65535" precision="0" nullable="true" jdbcType="-1"/>
@@ -3582,16 +3631,73 @@
<column name="stamp" sqlType="INT" length="10" precision="0" nullable="true" jdbcType="4"/>
<primary-key name="PRIMARY" columns="session_id"/>
</table>
<table name="user" schema="" catalog="pathfinder" type="TABLE">
<table name="system" schema="" catalog="pathfinder" type="TABLE">
<column name="id" sqlType="INT" length="10" precision="0" nullable="false" jdbcType="4" autoIncrement="true"/>
<column name="created" sqlType="TIMESTAMP" length="19" precision="0" nullable="false" jdbcType="93" def="Q1VSUkVOVF9USU1FU1RBTVA="/>
<column name="updated" sqlType="TIMESTAMP" length="19" precision="0" nullable="false" jdbcType="93" def="Q1VSUkVOVF9USU1FU1RBTVA=" version="true"/>
<column name="active" sqlType="TINYINT" length="3" precision="0" nullable="false" jdbcType="-6" def="MQ=="/>
<column name="mapId" sqlType="INT" length="10" precision="0" nullable="false" jdbcType="4"/>
<column name="systemId" sqlType="INT" length="10" precision="0" nullable="false" jdbcType="4"/>
<column name="name" sqlType="VARCHAR" length="255" precision="0" nullable="false" jdbcType="12"/>
<column name="alias" sqlType="VARCHAR" length="255" precision="0" nullable="false" jdbcType="12"/>
<column name="effect" sqlType="VARCHAR" length="100" precision="0" nullable="false" jdbcType="12"/>
<column name="typeId" sqlType="INT" length="10" precision="0" nullable="false" jdbcType="4"/>
<column name="security" sqlType="VARCHAR" length="10" precision="0" nullable="false" jdbcType="12"/>
<column name="trueSec" sqlType="DECIMAL" length="2" precision="1" nullable="false" jdbcType="3"/>
<column name="statusId" sqlType="INT" length="10" precision="0" nullable="false" jdbcType="4"/>
<column name="locked" sqlType="TINYINT" length="3" precision="0" nullable="false" jdbcType="-6" def="MA=="/>
<column name="rally" sqlType="TINYINT" length="3" precision="0" nullable="false" jdbcType="-6" def="MA=="/>
<column name="posX" sqlType="INT" length="10" precision="0" nullable="false" jdbcType="4" def="MA=="/>
<column name="posY" sqlType="INT" length="10" precision="0" nullable="false" jdbcType="4" def="MA=="/>
<primary-key name="PRIMARY" columns="id"/>
<index name="active" unique="false" columns="active"/>
<index name="created" unique="false" columns="created"/>
<index name="locked" unique="false" columns="locked"/>
<index name="mapId" unique="false" columns="mapId"/>
<index name="rally" unique="false" columns="rally"/>
<index name="statusId" unique="false" columns="statusId"/>
<index name="systemId" unique="false" columns="systemId"/>
<index name="typeId" unique="false" columns="typeId"/>
<index name="updated" unique="false" columns="updated"/>
</table>
<table name="system_status" schema="" catalog="pathfinder" type="TABLE">
<column name="id" sqlType="INT" length="10" precision="0" nullable="false" jdbcType="4" autoIncrement="true"/>
<column name="created" sqlType="TIMESTAMP" length="19" precision="0" nullable="false" jdbcType="93" def="Q1VSUkVOVF9USU1FU1RBTVA="/>
<column name="updated" sqlType="TIMESTAMP" length="19" precision="0" nullable="false" jdbcType="93" def="Q1VSUkVOVF9USU1FU1RBTVA=" version="true"/>
<column name="active" sqlType="TINYINT" length="3" precision="0" nullable="false" jdbcType="-6" def="MQ=="/>
<column name="name" sqlType="VARCHAR" length="255" precision="0" nullable="false" jdbcType="12"/>
<column name="label" sqlType="VARCHAR" length="255" precision="0" nullable="false" jdbcType="12"/>
<column name="class" sqlType="VARCHAR" length="255" precision="0" nullable="false" jdbcType="12"/>
<primary-key name="PRIMARY" columns="id"/>
<index name="active" unique="false" columns="active"/>
<index name="created" unique="false" columns="created"/>
<index name="updated" unique="false" columns="updated"/>
</table>
<table name="system_type" schema="" catalog="pathfinder" type="TABLE">
<column name="id" sqlType="INT" length="10" precision="0" nullable="false" jdbcType="4" autoIncrement="true"/>
<column name="created" sqlType="TIMESTAMP" length="19" precision="0" nullable="false" jdbcType="93" def="Q1VSUkVOVF9USU1FU1RBTVA="/>
<column name="updated" sqlType="TIMESTAMP" length="19" precision="0" nullable="false" jdbcType="93" def="Q1VSUkVOVF9USU1FU1RBTVA=" version="true"/>
<column name="active" sqlType="TINYINT" length="3" precision="0" nullable="false" jdbcType="-6" def="MQ=="/>
<column name="name" sqlType="VARCHAR" length="255" precision="0" nullable="false" jdbcType="12"/>
<primary-key name="PRIMARY" columns="id"/>
<index name="active" unique="false" columns="active"/>
<index name="created" unique="false" columns="created"/>
<index name="updated" unique="false" columns="updated"/>
</table>
<table name="user" schema="" catalog="pathfinder" type="TABLE">
<column name="id" sqlType="INT" length="10" precision="0" nullable="false" jdbcType="4" autoIncrement="true"/>
<column name="created" sqlType="DATETIME" length="19" precision="0" nullable="false" jdbcType="93" def="Q1VSUkVOVF9USU1FU1RBTVA="/>
<column name="updated" sqlType="TIMESTAMP" length="19" precision="0" nullable="false" jdbcType="93" def="Q1VSUkVOVF9USU1FU1RBTVA=" version="true"/>
<column name="lastLogin" sqlType="TIMESTAMP" length="19" precision="0" nullable="false" jdbcType="93" def="JzAwMDAtMDAtMDAgMDA6MDA6MDAn"/>
<column name="active" sqlType="TINYINT" length="3" precision="0" nullable="false" jdbcType="-6" def="MQ=="/>
<column name="name" sqlType="VARCHAR" length="255" precision="0" nullable="false" jdbcType="12"/>
<column name="password" sqlType="VARCHAR" length="255" precision="0" nullable="false" jdbcType="12"/>
<primary-key name="PRIMARY" columns="id"/>
<index name="created" unique="false" columns="created,updated,lastLogin,active"/>
<index name="id" unique="true" columns="id"/>
<index name="name" unique="true" columns="name"/>
<index name="created" unique="false" columns="created"/>
<index name="lastLogin" unique="false" columns="lastLogin"/>
<index name="updated" unique="false" columns="updated"/>
</table>
<table name="wh_statics" schema="" catalog="pathfinder" type="TABLE">
<column name="id" sqlType="INT" length="10" precision="0" nullable="false" jdbcType="4" autoIncrement="true"/>

2
.idea/dataSources.xml generated
View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="DataSourceManagerImpl" format="xml" hash="3578562769">
<component name="DataSourceManagerImpl" format="xml" hash="3779592507">
<data-source source="LOCAL" name="Pathfinder" uuid="da3cf616-be39-4091-94bd-76d70f41765d">
<driver-ref>mysql</driver-ref>
<synchronize>true</synchronize>

View File

@@ -18,6 +18,7 @@
<w>killmail</w>
<w>mouseover</w>
<w>nonblock</w>
<w>onerror</w>
<w>pnotify</w>
<w>raphaël</w>
<w>revalidate</w>

54
app/app/cortex.php Normal file
View File

@@ -0,0 +1,54 @@
<?php
namespace App;
class Cortex extends Controller
{
function get()
{
$f3 = \Base::instance();
$f3->set('AUTOLOAD', $f3->get('AUTOLOAD').';app/cortex/');
$f3->set('QUIET', false);
$dbs = array(
'sql' => new \DB\SQL('mysql:host=localhost;port=3306;dbname=fatfree', 'fatfree', ''),
// 'sql-sqlite' => new \DB\SQL('sqlite:data/sqlite.db'),
// 'sql-pgsql' => new \DB\SQL('pgsql:host=localhost;dbname=fatfree', 'fatfree', 'fatfree'),
'jig' => new \DB\Jig('data/'),
'mongo' => new \DB\Mongo('mongodb://localhost:27017', 'testdb'),
// 'sqlsrv2012' => new \DB\SQL('sqlsrv:SERVER=LOCALHOST\SQLEXPRESS2012;Database=fatfree','fatfree', 'fatfree'),
// 'sqlsrv2008' => new \DB\SQL('sqlsrv:SERVER=LOCALHOST\SQLEXPRESS2008;Database=fatfree','fatfree', 'fatfree'),
);
$results = array();
// Test Syntax
foreach ($dbs as $type => $db) {
$test = new \Test_Syntax();
$results = array_merge((array) $results, (array) $test->run($db, $type));
}
// Test Relations
foreach ($dbs as $type => $db) {
$f3->set('DB',$db);
$test = new \Test_Relation();
$results = array_merge((array) $results, (array) $test->run($db, $type));
}
// Test Filter
foreach ($dbs as $type => $db) {
$f3->set('DB',$db);
$test = new \Test_Filter();
$results = array_merge((array) $results, (array) $test->run($db, $type));
}
// Further Common Tests
if (isset($dbs['sql'])) {
$test = new \Test_Common();
$f3->set('DB', $dbs['sql']);
$results = array_merge((array) $results, (array) $test->run());
}
$f3->set('results', $results);
}
}

View File

@@ -0,0 +1,27 @@
<?php
class AuthorModel extends \DB\Cortex {
protected
$fieldConf = array(
'name' => array(
'type' => \DB\SQL\Schema::DT_VARCHAR256
),
'mail' => array(
'type' => \DB\SQL\Schema::DT_VARCHAR256
),
'website' => array(
'type' => \DB\SQL\Schema::DT_VARCHAR256
),
'news' => array(
'has-many' => array('\NewsModel','author'),
),
'profile' => array(
'has-one' => array('\ProfileModel','author'),
),
),
// $primary = 'aid',
$table = 'author',
$db = 'DB';
}

View File

@@ -0,0 +1,28 @@
<?php
class NewsModel extends \DB\Cortex {
protected
$fieldConf = array(
'title' => array(
'type' => \DB\SQL\Schema::DT_VARCHAR128
),
'text' => array(
'type' => \DB\SQL\Schema::DT_TEXT
),
'author' => array(
'belongs-to-one' => '\AuthorModel',
),
'tags' => array(
'belongs-to-many' => '\TagModel',
),
'tags2' => array(
'has-many' => array('\TagModel','news','news_tags'),
// 'has-many' => array('\TagModel','news'),
),
),
// $primary='nid',
$table = 'news',
$db = 'DB';
}

View File

@@ -0,0 +1,21 @@
<?php
class ProfileModel extends \DB\Cortex {
protected
$fieldConf = array(
'message' => array(
'type' => \DB\SQL\Schema::DT_TEXT
),
'image' => array(
'type' => \DB\SQL\Schema::DT_VARCHAR256
),
'author' => array(
'belongs-to-one' => '\AuthorModel'
)
),
// $primary = 'profile_id',
$table = 'profile',
$db = 'DB';
}

View File

@@ -0,0 +1,18 @@
<?php
class TagModel extends \DB\Cortex {
protected
$fieldConf = array(
'title' => array(
'type' => \DB\SQL\Schema::DT_VARCHAR128
),
'news' => array(
'has-many' => array('\NewsModel','tags2','news_tags'),
),
),
// $primary = 'tid',
$table = 'tags',
$db = 'DB';
}

View File

@@ -0,0 +1,125 @@
<?php
use App\Controller;
class Test_Common {
function run()
{
$test = new \Test();
/** @var \Base $f3 */
$f3 = \Base::instance();
$news = new NewsModel();
$news->load();
$dummy = array(
'title'=>'copy test',
'text'=>'Lorem ipsum dolor sit amet.',
'author'=>1,
'tags'=>array(3)
);
$f3->set('record1', $dummy);
$news->copyto('record2');
$test->expect(
$f3->exists('record2'),
'copyto: raw record copied to hive'
);
$news->reset();
$news->copyfrom('record1');
$test->expect(
$news->title = 'copy test' &&
$news->text = 'Lorem ipsum dolor sit amet.',
'copyfrom: hydrate from hive key'
);
$test->expect(
$news->author instanceof AuthorModel
&& !$news->author->dry() &&
$news->tags instanceof \DB\CortexCollection,
'copyfrom: relations hydrated successful'
);
$test->expect(
$news->get('author',true) == 1,
'get raw data from relational field'
);
$news->reset();
$news->copyfrom('record2','title;author');
$test->expect(
$news->title = 'Responsive Images' &&
$news->get('author',true) == 2 &&
$news->text == NULL,
'copyfrom: limit fields with split-able string'
);
$news->reset();
$news->copyfrom('record2',array('title'));
$test->expect(
$news->title = 'Responsive Images' && $news->text == NULL,
'copyfrom: limit fields by array'
);
$news->reset();
$news->copyfrom($dummy,function($fields) {
return array_intersect_key($fields,array_flip(array('title')));
});
$test->expect(
$news->title = 'copy test',
'copyfrom: copy from array instead of hive key'
);
$test->expect(
$news->title = 'copy test' && $news->text == NULL,
'copyfrom: limit fields by callback function'
);
$all = $news->find();
$allTitle = $all->getAll('title');
$test->expect(
count($allTitle) == 3 &&
$allTitle[0] == 'Responsive Images' &&
$allTitle[1] == 'CSS3 Showcase' &&
$allTitle[2] == 'Touchable Interfaces',
'collection getAll returns all values of selected field'
);
$newsByID = $all->getBy('_id');
$test->expect(
array_keys($newsByID) == array(1,2,3),
'collection getBy sorts by given field'
);
$newsByAuthorID = $all->getBy('author',true);
$test->expect(
array_keys($newsByAuthorID) == array(2, 1) &&
count($newsByAuthorID[2]) == 2 &&
count($newsByAuthorID[1]) == 1,
'collection getBy nested sort by author'
);
$allTitle = array();
foreach($all as $record)
$allTitle[] = $record->title;
$test->expect(
count($allTitle) == 3 &&
$allTitle[0] == 'Responsive Images' &&
$allTitle[1] == 'CSS3 Showcase' &&
$allTitle[2] == 'Touchable Interfaces',
'collection is traversable'
);
///////////////////////////////////
return $test->results();
}
}

View File

@@ -0,0 +1,302 @@
<?php
use App\Controller;
class Test_Filter {
function run($db,$type)
{
$test = new \Test();
// setup
///////////////////////////////////
$author = new \AuthorModel();
$news = new \NewsModel();
$profile = new \ProfileModel();
$tag = new \TagModel();
$ac=$author::resolveConfiguration();
$author_pk = (is_int(strpos($type,'sql'))?$ac['primary']:'_id');
$nc=$news::resolveConfiguration();
$news_pk = (is_int(strpos($type,'sql'))?$nc['primary']:'_id');
$tc=$tag::resolveConfiguration();
$tag_pk = (is_int(strpos($type,'sql'))?$tc['primary']:'_id');
$authorIDs = $author->find()->getAll('_id');
$all = $news->find();
$newsIDs = $all->getAll('_id');
$profileIDs = $profile->find()->getAll('_id');
$tagIDs = $tag->find()->getAll('_id');
// add another relation
$news->load(array('title = ?','CSS3 Showcase'));
$news->author = $author->load(array($author_pk.' = ?',$authorIDs[0]));
$news->save();
$news->reset();
$author->reset();
// has-filter on belongs-to relation
///////////////////////////////////
$result = $author->has('news', array('title like ?', '%Image%'))->afind();
$test->expect(
count($result) == 1 &&
$result[0]['name'] == 'Johnny English',
$type.': has filter on many-to-one field'
);
$test->expect(
count($result[0]['news']) == 2 &&
$result[0]['news'][0]['title'] == 'Responsive Images' &&
$result[0]['news'][1]['title'] == 'CSS3 Showcase',
$type.': has filter does not prune relation set'
);
$result = $news->has('author', array('name = ?', 'Johnny English'))->afind();
$test->expect(
count($result) == 2 && // has 2 news
$result[0]['title'] == 'Responsive Images' &&
$result[1]['title'] == 'CSS3 Showcase',
$type.': has filter on one-to-many field'
);
// add another profile
$profile->message = 'Beam me up, Scotty!';
$profile->author = $authorIDs[2];
$profile->save();
$profile->reset();
$result = $author->has('profile',array('message LIKE ?','%Scotty%'))->afind();
$test->expect(
count($result) == 1 &&
$result[0]['name'] == 'James T. Kirk' &&
$result[0]['profile']['message'] == 'Beam me up, Scotty!',
$type.': has filter on one-to-one field'
);
$result = $profile->has('author',array('name LIKE ?','%Kirk%'))->afind();
$test->expect(
count($result) == 1 &&
$result[0]['message'] == 'Beam me up, Scotty!' &&
$result[0]['author']['name'] == 'James T. Kirk',
$type.': has filter on one-to-one field, inverse'
);
// add mm tags
$news->load(array('title = ?','Responsive Images'));
$news->tags2 = array($tagIDs[0],$tagIDs[1]);
$news->save();
$news->load(array('title = ?','CSS3 Showcase'));
$news->tags2 = array($tagIDs[1],$tagIDs[2]);
$news->save();
$news->reset();
$result = $news->has('tags2',array('title like ?','%Design%'))->find();
$test->expect(
count($result) == 1 &&
$result[0]['title'] == 'Responsive Images',
$type.': has filter on many-to-many field'
);
$result = $news->has('tags2',array('title = ?','Responsive'))->find();
$test->expect(
count($result) == 2 &&
$result[0]['title'] == 'Responsive Images' &&
$result[1]['title'] == 'CSS3 Showcase',
$type.': has filter on many-to-many field, additional test'
);
$result = $tag->has('news',array('title = ?','Responsive Images'))->find();
$test->expect(
count($result) == 2 &&
$result[0]['title'] == 'Web Design' &&
$result[1]['title'] == 'Responsive',
$type.': has filter on many-to-many field, inverse'
);
// add another tag
$news->load(array('title = ?', 'Touchable Interfaces'));
$news->tags2 = array($tagIDs[1]);
$news->save();
$news->reset();
$tag->has('news',array('text LIKE ? and title LIKE ?', '%Lorem%', '%Interface%'));
$result = $tag->find();
$test->expect(
count($result) == 1 &&
$result[0]['title'] == 'Responsive',
$type.': has filter with multiple conditions'
);
$news->has('tags2', array('title = ? OR title = ?', 'Usability', 'Web Design'));
$result = $news->afind(array('text = ?', 'Lorem Ipsun'));
$test->expect(
count($result) == 1 &&
$result[0]['title'] == 'Responsive Images',
$type.': find with condition and has filter'
);
$news->load(array('title = ?', 'Responsive Images'));
$news->author = $authorIDs[1];
$news->save();
$news->reset();
$news->has('tags2', array('title = ? OR title = ?', 'Usability', 'Web Design'));
$news->has('author', array('name = ?', 'Ridley Scott'));
$result = $news->afind();
$test->expect(
count($result) == 1 &&
$result[0]['title'] == 'Responsive Images',
$type.': find with multiple has filters on different relations'
);
// add another news to author 2
$news->load(array($news_pk.' = ?',$newsIDs[2]));
$news->author = $authorIDs[1];
$news->save();
$news->reset();
$news->has('author', array('name = ?', 'Ridley Scott'));
$news->load();
$res = array();
while (!$news->dry()) {
$res[] = $news->title;
$news->next();
}
$test->expect(
count($res) == 2 &&
$res[0] == 'Responsive Images' &&
$res[1] == 'Touchable Interfaces'
,
$type.': has filter in load context'
);
$news->reset();
$news->fields(array('title'));
$news->load();
$test->expect(
!empty($news->title) &&
empty($news->author) &&
empty($news->text) &&
empty($news->tags) &&
empty($news->tags2),
$type.': use a whitelist to restrict fields'
);
unset($news);
$news = new \NewsModel();
$news->fields(array('title','tags','tags2','author'),true);
$news->load();
$test->expect(
empty($news->title) &&
empty($news->author) &&
!empty($news->text) &&
empty($news->tags) &&
empty($news->tags2),
$type.': use a blacklist to restrict fields'
);
unset($news);
$news = new \NewsModel();
$news->fields(array('tags.title'));
$news->load();
$test->expect(
!empty($news->tags[0]->title) &&
empty($news->tags[0]->news),
$type.': set restricted fields to related mappers'
);
$news->filter('tags2',null,array('order'=>'title ASC'));
$news->load(array('title = ?','Responsive Images'));
$test->expect(
$news->tags2[0]->title == 'Responsive' &&
$news->tags2[1]->title == 'Web Design',
$type.': filter with sorting of related records'
);
// get all tags sorted by their usage in news articles
$tag->reset();
$tag->countRel('news');
$result = $tag->find(null,array('order'=>'count_news DESC, title'))->castAll(0);
$test->expect(
$result[0]['title'] == 'Responsive' &&
$result[0]['count_news'] == 3 &&
$result[1]['title'] == 'Usability' &&
$result[1]['count_news'] == 1 &&
$result[2]['title'] == 'Web Design' &&
$result[2]['count_news'] == 1,
$type.': count and sort on many-to-many relation'
);
// get all authors sorted by the amount of news they have written
$author->reset();
$author->countRel('news');
$result = $author->find(null,array('order'=>'count_news DESC'))->castAll(0);
$test->expect(
$result[0]['name'] == 'Ridley Scott' &&
$result[0]['count_news'] == 2 &&
$result[1]['name'] == 'Johnny English' &&
$result[1]['count_news'] == 1 &&
$result[2]['name'] == 'James T. Kirk' &&
$result[2]['count_news'] == null,
$type.': count and sort on one-to-many relation'
);
$tag->reset();
$tag->countRel('news');
$result = $tag->find(null,array('order'=>'count_news DESC, title DESC','limit'=>1,'offset'=>1))->castAll(0);
$test->expect(
$result[0]['title'] == 'Web Design' &&
$result[0]['count_news'] == 1,
$type.': apply limit and offset on aggregated collection'
);
$author->reset();
$author->countRel('news');
$author->has('news',array('text like ?','%Lorem%'));
$result = $author->find()->castAll(0);
$test->expect(
count($result) == 1 &&
$result[0]['name'] == 'Ridley Scott' &&
$result[0]['count_news'] == 2 ,
$type.': has-filter and 1:M relation counter'
);
$author->reset();
$id = $author->load()->next()->_id;
$tag->reset();
$tag->countRel('news');
$tag->has('news',array('author = ?',$id));
$result = $tag->find(null,array('order'=>'count_news desc'))->castAll(0);
$test->expect(
count($result) == 2 &&
$result[0]['title'] == 'Responsive' &&
$result[0]['count_news'] == 3 &&
$result[1]['title'] == 'Web Design' &&
$result[1]['count_news'] == 1,
$type.': has-filter and M:M relation counter'
);
///////////////////////////////////
return $test->results();
}
}

View File

@@ -0,0 +1,335 @@
<?php
use App\Controller;
class Test_Relation {
/**
* unify results for better comparison
*/
private function getResult($result)
{
$out = array();
foreach ($result as $row) {
if(is_object($row))
$row = $row->cast();
unset($row['_id']);
unset($row['id']);
unset($row['aid']);
unset($row['uid']);
unset($row['nid']);
unset($row['tid']);
unset($row['pid']);
unset($row['profile_id']);
foreach ($row as $col => $val) {
if (empty($val) || is_null($val))
unset($row[$col]);
}
$out[] = $row;
}
return $out;
}
function run($db,$type)
{
$test = new \Test();
// clear existing data
\AuthorModel::setdown();
\TagModel::setdown();
\NewsModel::setdown();
\ProfileModel::setdown();
// setup models
\AuthorModel::setup();
\TagModel::setup();
\NewsModel::setup();
\ProfileModel::setup();
// setup Author
///////////////////////////////////
$author_id = array();
$author = new \AuthorModel();
$ac=$author::resolveConfiguration();
$author_pk = (is_int(strpos($type,'sql'))?$ac['primary']:'_id');
$author->name = 'Johnny English';
$author->save();
$author_id[] = $author->_id;
$author->reset();
$author->name = 'Ridley Scott';
$author->save();
$author_id[] = $author->_id;
$author->reset();
$author->name = 'James T. Kirk';
$author->save();
$author_id[] = $author->_id;
$author->reset();
$allauthors = $author->find()->castAll();
$allauthors = $this->getResult($allauthors);
$test->expect(
json_encode($allauthors) ==
'[{"name":"Johnny English"},{"name":"Ridley Scott"},{"name":"James T. Kirk"}]',
$type.': all AuthorModel items created'
);
// setup Tags
///////////////////////////////////
$tag_id = array();
$tag = new \TagModel();
$tc=$tag::resolveConfiguration();
$tag_pk = (is_int(strpos($type,'sql'))?$tc['primary']:'_id');
$tag->title = 'Web Design';
$tag->save();
$tag_id[] = $tag->_id;
$tag->reset();
$tag->title = 'Responsive';
$tag->save();
$tag_id[] = $tag->_id;
$tag->reset();
$tag->title = 'Usability';
$tag->save();
$tag_id[] = $tag->_id;
$tag->reset();
$allTags = $this->getResult($tag->find());
$test->expect(
json_encode($allTags) ==
'[{"title":"Web Design"},{"title":"Responsive"},{"title":"Usability"}]',
$type.': all TagModel items created'
);
// setup News
///////////////////////////////////
$news_id = array();
$news = new \NewsModel();
$nc=$news::resolveConfiguration();
$news_pk = (is_int(strpos($type,'sql'))?$nc['primary']:'_id');
$news->title = 'Responsive Images';
$news->text = 'Lorem Ipsun';
$news->save();
$news_id[] = $news->_id;
$news->reset();
$news->title = 'CSS3 Showcase';
$news->text = 'News Text 2';
$news->save();
$news_id[] = $news->_id;
$news->reset();
$news->title = 'Touchable Interfaces';
$news->text = 'Lorem Foo';
$news->save();
$news_id[] = $news->_id;
$news->reset();
$allnews = $this->getResult($news->find());
$test->expect(
json_encode($allnews) ==
'[{"title":"Responsive Images","text":"Lorem Ipsun"},{"title":"CSS3 Showcase","text":"News Text 2"},{"title":"Touchable Interfaces","text":"Lorem Foo"}]',
$type.': all NewsModel items created'
);
// belongs-to author relation
///////////////////////////////////
$author->load();
$news->load(array($news_pk.' = ?',$news_id[0]));
$news->author = $author;
$news->save();
$news->reset();
$news->load(array($news_pk.' = ?', $news_id[0]));
$test->expect(
$news->author->name == 'Johnny English',
$type.': belongs-to-one: author relation created'
);
$news->author = NULL;
$news->save();
$news->reset();
$news->load(array($news_pk.' = ?', $news_id[0]));
$test->expect(
empty($news->author),
$type.': belongs-to-one: author relation released'
);
$news->author = $author->_id;
$news->save();
$news->reset();
$news->load(array($news_pk.' = ?', $news_id[0]));
$test->expect(
$news->author->name == 'Johnny English',
$type.': belongs-to-one: relation created by raw id'
);
// belongs-to-many tag relation
///////////////////////////////////
$tag1 = new \TagModel();
$tag1->load(array($tag_pk.' = ?', $tag_id[0]));
$tag2 = new \TagModel();
$tag2->load(array($tag_pk.' = ?', $tag_id[1]));
$news->tags = array($tag1,$tag2);
$news->save();
$news->reset();
$news->load(array($news_pk.' = ?', $news_id[0]));
$test->expect(
$news->tags[0]->title == 'Web Design' && $news->tags[1]->title == 'Responsive',
$type.': belongs-to-many: relations created with array of mapper objects'
);
$news->reset();
$news->load(array($news_pk.' = ?', $news_id[1]));
$news->tags = array($tag_id[1],$tag_id[2]);
$news->save();
$news->reset();
$news->load(array($news_pk.' = ?', $news_id[1]));
$test->expect(
$news->tags[0]->title == 'Responsive' && $news->tags[1]->title == 'Usability',
$type.': belongs-to-many: relations created with array of IDs'
);
$news->tags = null;
$news->save();
$news->reset();
$news->load(array($news_pk.' = ?', $news_id[1]));
$test->expect(
empty($news->tags),
$type.': belongs-to-many: relations released'
);
$tag->reset();
$news->load(array($news_pk.' = ?', $news_id[1]));
$news->tags = $tag->load(array($tag_pk.' != ?',$tag_id[0]));
$news->save();
$news->reset();
$news->load(array($news_pk.' = ?', $news_id[1]));
$test->expect(
$news->tags[0]->title == 'Responsive' && $news->tags[1]->title == 'Usability',
$type.': belongs-to-many: relations created with hydrated mapper'
);
$news->reset();
$tag->reset();
$news->load(array($news_pk.' = ?', $news_id[2]));
$news->tags = $tag_id[0].';'.$tag_id[2];
$news->save();
$news->reset();
$news->load(array($news_pk.' = ?', $news_id[2]));
$test->expect(
$news->tags[0]->title == 'Web Design' && $news->tags[1]->title == 'Usability',
$type.': belongs-to-many: relations created with split-able string'
);
$test->expect(
is_object($news->tags) && $news->tags instanceof \DB\CortexCollection,
$type.': belongs-to-many: result is collection'
);
// has-one relation
///////////////////////////////////
$profile = new ProfileModel();
$pc=$profile::resolveConfiguration();
$profile_pk = (is_int(strpos($type,'sql'))?$pc['primary']:'_id');
$profile->message = 'Hello World';
$profile->author = $author->load(array($author_pk.' = ?',$author_id[0]));
$profile->save();
$profile_id = $profile->_id;
$profile->reset();
$author->reset();
$author->load(array($author_pk.' = ?', $author_id[0]));
$profile->load(array($profile_pk.' = ?', $profile_id));
$test->expect(
$author->profile->message == 'Hello World' &&
$profile->author->name == "Johnny English",
$type.': has-one: relation assigned'
);
$profile->reset();
$profile->message = 'I\'m feeling lucky';
$profile->image = 'lolcat.jpg';
$author->reset();
$author->load(array($author_pk.' = ?',$author_id[1]));
$author->profile = $profile;
$author->save();
$profile->reset();
$author->reset();
$author->load(array($author_pk.' = ?', $author_id[1]));
$test->expect(
$author->profile->message == 'I\'m feeling lucky',
$type.': has-one: inverse relation'
);
// has-many relation
///////////////////////////////////
$author->load(array($author_pk.' = ?', $author_id[0]));
$result = $this->getResult($author->news);
$test->expect(
$result[0]['title'] == "Responsive Images" &&
$result[0]['tags'][0]['title'] == 'Web Design' &&
$result[0]['tags'][1]['title'] == 'Responsive',
$type.': has-many inverse relation'
);
// many to many relation
///////////////////////////////////
$news->load(array($news_pk.' = ?',$news_id[0]));
$news->tags2 = array($tag_id[0],$tag_id[1]);
$news->save();
$news->reset();
$news->load(array($news_pk.' = ?',$news_id[0]));
$test->expect(
$news->tags2[0]['title'] == 'Web Design' &&
$news->tags2[1]['title'] == 'Responsive',
$type.': many-to-many relation created'
);
$test->expect(
is_object($news->tags2) && $news->tags2 instanceof \DB\CortexCollection,
$type.': many-to-many: result is collection'
);
$news->load(array($news_pk.' = ?', $news_id[0]));
$news->tags2 = NULL;
$news->save();
$news->reset();
$news->load(array($news_pk.' = ?', $news_id[0]));
$test->expect(
is_null($news->tags2),
$type.': many-to-many relation released'
);
$all = $news->find();
$test->expect(
$all[1]->tags2 === NULL
&& $all[2]->author === NULL,
$type.': empty relations are NULL'
);
$arr = $news->cast();
$test->expect(
is_array($arr['tags']),
$type.': collection becomes array in casted model'
);
if ($type == 'mongo') {
$test->expect(
is_string($arr['_id']),
$type.': id becomes string in casted model'
);
}
///////////////////////////////////
return $test->results();
}
}

View File

@@ -0,0 +1,364 @@
<?php
use App\Controller;
class Test_Syntax {
function run($db,$type)
{
$test = new \Test();
$tname = 'test_cortex';
\DB\Cortex::setdown($db, $tname);
$fields = array(
'title' => array('type' => \DB\SQL\Schema::DT_TEXT),
'num1' => array('type' => \DB\SQL\Schema::DT_INT4),
'num2' => array('type' => \DB\SQL\Schema::DT_INT4),
);
\DB\Cortex::setup($db, $tname, $fields);
// adding some testing data
$cx = new \DB\Cortex($db, $tname);
$cx->title = 'bar1';
$cx->save();
$cx->reset();
$cx->title = 'baz2';
$cx->num1 = 1;
$cx->save();
$cx->reset();
$cx->title = 'foo3';
$cx->num1 = 4;
$cx->save();
$cx->reset();
$cx->title = 'foo4';
$cx->num1 = 3;
$cx->save();
$cx->reset();
$cx->title = 'foo5';
$cx->num1 = 3;
$cx->num2 = 5;
$cx->save();
$cx->reset();
$cx->title = 'foo6';
$cx->num1 = 3;
$cx->num2 = 1;
$cx->save();
$cx->reset();
$cx->title = 'foo7';
$cx->num1 = 3;
$cx->num2 = 10;
$cx->save();
$cx->reset();
$cx->title = 'foo8';
$cx->num1 = 5;
$cx->save();
$cx->reset();
$cx->title = 'foo9';
$cx->num1 = 8;
$cx->save();
$cx->reset();
$result = $this->getResult($cx->find());
$expected = array(
0 => array(
'title' => 'bar1',
),
1 => array(
'num1' => 1,
'title' => 'baz2',
),
2 => array(
'num1' => 4,
'title' => 'foo3',
),
3 => array(
'num1' => 3,
'title' => 'foo4',
),
4 => array(
'num1' => 3,
'num2' => 5,
'title' => 'foo5',
),
5 => array(
'num1' => 3,
'num2' => 1,
'title' => 'foo6',
),
6 => array(
'num1' => 3,
'num2' => 10,
'title' => 'foo7',
),
7 => array(
'num1' => 5,
'title' => 'foo8',
),
8 => array(
'num1' => 8,
'title' => 'foo9',
),
);
$test->expect(
json_encode($result) == json_encode($expected),
$type.': init mapper, adding records'
);
// operator =
$result = $this->getResult($cx->find(array('title = ?', 'foo7')));
$expected = array(
0 => array(
'num1' => 3,
'num2' => 10,
'title' => 'foo7',
),
);
$test->expect(
json_encode($result) == json_encode($expected),
$type.': operator check: ='
);
// operator >
$result = $this->getResult($cx->find(array('num1 > ?', 4)));
$expected = array(
0 => array(
'num1' => 5,
'title' => 'foo8',
),
1 => array(
'num1' => 8,
'title' => 'foo9',
),
);
$test->expect(
json_encode($result) == json_encode($expected),
$type.': operator check: >'
);
// operator >=
$result = $this->getResult($cx->find(array('num1 >= ?', 5)));
$test->expect(
json_encode($result) == json_encode($expected),
$type.': operator check: >='
);
// operator <
$result = $this->getResult($cx->find(array('num2 < ?', 2)));
$expected = array(
0 => array(
'num1' => 3,
'num2' => 1,
'title' => 'foo6',
),
);
$test->expect(
json_encode($result) == json_encode($expected),
$type.': operator check: <'
);
// operator <=
$result = $this->getResult($cx->find(array('num2 <= ?', 1)));
$test->expect(
json_encode($result) == json_encode($expected),
$type.': operator check: <='
);
// operator without binding
$result = $this->getResult($cx->find(array('num1 > 4')));
$expected = array(
0 => array(
'num1' => 5,
'title' => 'foo8',
),
1 => array(
'num1' => 8,
'title' => 'foo9',
),
);
$test->expect(
json_encode($result) == json_encode($expected),
$type.': operator without binding'
);
// field comparision
$result = $this->getResult($cx->find(
array('num2 > num1', 1)));
$expected = array(
0 => array(
'num1' => 3,
'num2' => 5,
'title' => 'foo5',
),
1 => array(
'num1' => 3,
'num2' => 10,
'title' => 'foo7',
),
);
$test->expect(
json_encode($result) == json_encode($expected),
$type.': check field comparision'
);
// lookahead search
$result = $this->getResult($cx->find(array('title like ?', '%o6')));
$expected = array(
0 => array(
'num1' => 3,
'num2' => 1,
'title' => 'foo6',
),
);
$test->expect(
json_encode($result) == json_encode($expected),
$type.': lookahead search'
);
// lookbehind search
$result = $this->getResult($cx->find(array('title like ?', 'bar%')));
$expected = array(
0 => array(
'title' => 'bar1',
),
);
$test->expect(
json_encode($result) == json_encode($expected),
$type.': lookbehind search'
);
// full search
$result = $this->getResult($cx->find(array('title like ?', '%a%')));
$expected = array(
0 => array(
'title' => 'bar1',
),
1 => array(
'num1' => 1,
'title' => 'baz2',
),
);
$test->expect(
json_encode($result) == json_encode($expected),
$type.': full search'
);
// negated search
$result = $this->getResult($cx->find(array('title not like ?', 'foo%')));
$test->expect(
json_encode($result) == json_encode($expected),
$type.': negated search'
);
// AND / OR chaining
$result = $this->getResult($cx->find(
array('(num2 < ? AND num1 > ?) OR title like ?', 2, 1, '%o9')));
$expected = array(
0 => array(
'num1' => 3,
'num2' => 1,
'title' => 'foo6',
),
1 => array(
'num1' => 8,
'title' => 'foo9',
),
);
$test->expect(
json_encode($result) == json_encode($expected),
$type.': check logical operator chaining'
);
// check limit
$result = $this->getResult($cx->find(
null, array('limit' => '2')));
$expected = array(
0 => array(
'title' => 'bar1',
),
1 => array(
'num1' => 1,
'title' => 'baz2',
),
);
$test->expect(
json_encode($result) == json_encode($expected),
$type.': check limit'
);
// check order
$result = $this->getResult($cx->find(
array('num2 >= ?', 1), array('order' => 'num2 desc')));
$expected = array(
0 => array(
'num1' => 3,
'num2' => 10,
'title' => 'foo7',
),
1 => array(
'num1' => 3,
'num2' => 5,
'title' => 'foo5',
),
2 => array(
'num1' => 3,
'num2' => 1,
'title' => 'foo6',
),
);
$test->expect(
json_encode($result) == json_encode($expected),
$type.': check order'
);
// IN search
$rc = $cx->find(array('num1 IN ?',array(4,5,8)));
$result = $rc->getAll('title');
sort($result);
$test->expect(
json_encode($result) == json_encode(array('foo3','foo8','foo9')),
$type.': IN operator'
);
$rc = $cx->find(array('num1 IN ? && num2 > ? && num2 NOT IN ?',array(3,4),1,array(10)));
$result = $rc->getAll('title');
$test->expect(
json_encode($result) == json_encode(array('foo5')),
$type.': enhanced IN, NOT IN operator'
);
///////////////////////////////////
return $test->results();
}
/**
* unify results for better comparison
*/
private function getResult($result)
{
$out = array();
foreach ($result as $row) {
$row = $row->cast();
unset($row['_id']);
unset($row['id']);
ksort($row);
foreach ($row as $col => $val) {
if (empty($val) || is_null($val))
unset($row[$col]);
}
$out[] = $row;
}
return $out;
}
}

544
app/app/schema.php Normal file
View File

@@ -0,0 +1,544 @@
<?php
namespace App;
class Schema extends Controller
{
private
$roundTime = 0,
$current_test = 1,
$current_engine,
$f3,
$test,
$tname;
private function getTime()
{
$time = microtime(TRUE) - $this->f3->get('timer') - $this->roundTime;
$this->roundTime = microtime(TRUE) - $this->f3->get('timer');
return ' [ '.sprintf('%.3f', $time).'s ]';
}
private function getTestDesc($text)
{
return $this->getTime().' '.$this->current_engine.': #'.$this->current_test++.' - '.$text;
}
function get()
{
$this->f3 = \Base::instance();
$this->test = new \Test;
$this->f3->set('QUIET', false);
$this->f3->set('CACHE', false);
$dbs = array(
/*'mysql' => new \DB\SQL(
'mysql:host=localhost;port=3306;dbname=fatfree', 'fatfree', ''
),*/
'sqlite' => new \DB\SQL(
'sqlite::memory:'
// 'sqlite:db/sqlite.db'
),
/*'pgsql' => new \DB\SQL(
'pgsql:host=localhost;dbname=fatfree', 'fatfree', 'fatfree'
),*/
/*'sqlsrv2012' => new \DB\SQL(
'sqlsrv:SERVER=LOCALHOST\SQLEXPRESS2012;Database=fatfree','fatfree', 'fatfree'
),*/
/*'sqlsrv2008' => new \DB\SQL(
'sqlsrv:SERVER=LOCALHOST\SQLEXPRESS2008;Database=fatfree','fatfree', 'fatfree'
)*/
);
$this->roundTime = microtime(TRUE) - \Base::instance()->get('timer');
$this->tname = 'test_table';
foreach ($dbs as $type => $db) {
$this->current_engine = $type;
$this->runTestSuite($db);
$this->current_test = 1;
}
$this->f3->set('results', $this->test->results());
}
private function runTestSuite($db)
{
$schema = new \DB\SQL\Schema($db);
$schema->dropTable($this->tname);
// create table
$table = $schema->createTable($this->tname);
$table = $table->build();
$result = $schema->getTables();
$this->test->expect(
in_array($this->tname, $result),
$this->getTestDesc('create default table')
);
unset($result);
$this->test->expect(
$table instanceof \DB\SQL\TableModifier,
$this->getTestDesc('$table->build() returns TableModifier')
);
// drop table
$table->drop();
$this->test->expect(
in_array($this->tname, $schema->getTables()) == false,
$this->getTestDesc('drop table')
);
unset($table);
// create table with columns
$table = $schema->createTable($this->tname);
$table->addColumn('title')->type($schema::DT_VARCHAR128);
$table->addColumn('number')->type($schema::DT_INT4);
$table = $table->build();
$r1 = $schema->getTables();
$r2 = $table->getCols();
$this->test->expect(
in_array($this->tname, $r1) && in_array('id', $r2)
&& in_array('title', $r2) && in_array('number', $r2),
$this->getTestDesc('create new table with additional columns')
);
unset($r1,$r2);
// testing all datatypes
foreach (array_keys($schema->dataTypes) as $index => $field) {
// testing column type
$table->addColumn('column_'.$index)->type($field);
$table->build();
$r1 = $table->getCols();
$this->test->expect(
in_array('column_'.$index, $r1),
$this->getTestDesc('adding column ['.$field.'], nullable')
);
}
unset($r1);
// adding some testing data
$mapper = new \DB\SQL\Mapper($db, $this->tname);
$mapper->column_7 = 'hello world';
$mapper->save();
$mapper->reset();
$result = $mapper->findone(array('column_7 = ?', 'hello world'))->cast();
unset($mapper);
$this->test->expect(
$result['column_7'] == 'hello world',
$this->getTestDesc('mapping dummy data')
);
// default value text, not nullable
$table->addColumn('text_default_not_null')
->type($schema::DT_VARCHAR128)
->nullable(false)->defaults('foo bar');
$table->build();
$r1 = $table->getCols(true);
$this->test->expect(
in_array('text_default_not_null', array_keys($r1)) &&
$r1['text_default_not_null']['default'] == 'foo bar' &&
$r1['text_default_not_null']['nullable'] == false,
$this->getTestDesc('adding column [VARCHAR128], not nullable with default value')
);
unset($r1);
// some testing dummy data
$mapper = new \DB\SQL\Mapper($db, $this->tname);
$mapper->column_7 = 'tanduay';
$mapper->save();
$mapper->reset();
$result = $mapper->findone(array('column_7 = ?','tanduay'))->cast();
$this->test->expect(
$result['column_7'] == 'tanduay' &&
$result['text_default_not_null'] == 'foo bar',
$this->getTestDesc('mapping dummy data')
);
unset($mapper,$result);
// default value numeric, not nullable
$table->addColumn('int_default_not_null')
->type($schema::DT_INT4)->nullable(false)->defaults(123);
$table->build();
$r1 = $table->getCols(true);
$this->test->expect(
in_array('int_default_not_null', array_keys($r1)) &&
$r1['int_default_not_null']['default'] == 123 &&
$r1['int_default_not_null']['nullable'] == false,
$this->getTestDesc('adding column [INT4], not nullable with default value')
);
unset($r1);
// adding testing data
$mapper = new \DB\SQL\Mapper($db, $this->tname);
$mapper->column_7 = 'test3';
$mapper->save();
$mapper->reset();
$r1 = $mapper->findone(array('column_7 = ?','test3'))->cast();
$this->test->expect(
$r1['column_7'] == 'test3' &&
$r1['int_default_not_null'] == 123,
$this->getTestDesc('mapping dummy data')
);
unset($mapper,$r1);
// default value text, nullable
$table->addColumn('text_default_nullable')
->type($schema::DT_VARCHAR128)
->defaults('foo bar');
$table->build();
$r1 = $table->getCols(true);
$this->test->expect(
in_array('text_default_nullable', array_keys($r1)) &&
$r1['text_default_nullable']['default'] == 'foo bar',
$this->getTestDesc('adding column [VARCHAR128], nullable with default value')
);
unset($r1);
// adding some dummy data
$mapper = new \DB\SQL\Mapper($db, $this->tname);
$mapper->column_7 = 'test4';
$mapper->save();
$mapper->reset();
$mapper->column_7 = 'test5';
$mapper->text_default_nullable = null;
$mapper->save();
$mapper->reset();
$result = $mapper->find(array('column_7 = ? OR column_7 = ?','test4','test5'));
foreach ($result as &$r)
$r = $r->cast();
$this->test->expect(
array_key_exists(0, $result) && array_key_exists(1, $result) &&
$result[0]['column_7'] == 'test4' && $result[0]['text_default_nullable'] == 'foo bar' &&
$result[1]['column_7'] == 'test5' && $result[1]['text_default_nullable'] === null,
$this->getTestDesc('mapping dummy data')
);
unset($mapper, $result);
// default value numeric, nullable
$table->addColumn('int_default_nullable')->type($schema::DT_INT4)->defaults(123);
$table->build();
$r1 = $table->getCols(true);
$this->test->expect(
in_array('int_default_nullable', array_keys($r1)) == true &&
$r1['int_default_nullable']['default'] == 123,
$this->getTestDesc('adding column [INT4], nullable with default value')
);
unset($r1);
// adding dummy data
$mapper = new \DB\SQL\Mapper($db, $this->tname);
$mapper->column_7 = 'test6';
$mapper->save();
$mapper->reset();
$mapper->column_7 = 'test7';
$mapper->int_default_nullable = null;
$mapper->save();
$mapper->reset();
$result = $mapper->find(array('column_7 = ? OR column_7 = ?', 'test6', 'test7'));
foreach ($result as &$r)
$r = $r->cast();
$this->test->expect(
array_key_exists(0, $result) && array_key_exists(1, $result) &&
$result[0]['column_7'] == 'test6' && $result[0]['int_default_nullable'] === 123 &&
$result[1]['column_7'] == 'test7' && $result[1]['int_default_nullable'] === null,
$this->getTestDesc('mapping dummy data')
);
unset($mapper, $result);
// current timestamp
$table->addColumn('stamp')
->type($schema::DT_TIMESTAMP)
->nullable(false)
->defaults($schema::DF_CURRENT_TIMESTAMP);
$table->build();
$r1 = $table->getCols(true);
$this->test->expect(
in_array('stamp', array_keys($r1)) &&
$r1['stamp']['default'] == $schema::DF_CURRENT_TIMESTAMP,
$this->getTestDesc(
'adding column [TIMESTAMP], not nullable with current_timestamp default value')
);
unset($r1);
// datetime nullable
$table->addColumn('datetime')->type_datetime()->nullable(true);
$table->build();
$r1 = $table->getCols(true);
$this->test->expect(
in_array('datetime', array_keys($r1)) &&
$r1['datetime']['nullable'] == true,
$this->getTestDesc(
'adding column [DATETIME], nullable, no default')
);
unset($r1);
// adding dummy data
$mapper = new \DB\SQL\Mapper($db, $this->tname);
$mapper->column_7 = 'test_datetime';
$mapper->datetime = NULL;
$mapper->save();
$mapper->reset();
$result = $mapper->find(array('column_7 = ?', 'test_datetime'));
foreach ($result as &$r)
$r = $r->cast();
$this->test->expect(
array_key_exists(0, $result) && $result[0]['column_7'] == 'test_datetime' &&
$result[0]['datetime'] === null,
$this->getTestDesc('mapping dummy data')
);
unset($mapper, $result);
// rename column
$table->renameColumn('text_default_not_null', 'title123');
$table->build();
$r1 = $table->getCols();
$this->test->expect(
in_array('title123', $r1) && !in_array('text_default_not_null', $r1),
$this->getTestDesc('renaming column')
);
unset($r1);
// adding dummy data
$mapper = new \DB\SQL\Mapper($db, $this->tname);
$mapper->title123 = 'test8';
$mapper->save();
$mapper->reset();
$result = $mapper->findone(array('title123 = ?','test8'));
$this->test->expect(
!$result->dry(),
$this->getTestDesc('mapping dummy data')
);
$table->renameColumn('title123', 'text_default_not_null');
$table->build();
unset($result,$mapper);
// remove column
$table->dropColumn('column_1');
$table->build();
$r1 = $table->getCols();
$this->test->expect(
!in_array('column_1', $r1),
$this->getTestDesc('removing column')
);
unset($r1);
// rename table
$schema->dropTable('test123');
$table->rename('test123');
$result = $schema->getTables();
$this->test->expect(
in_array('test123', $result) && !in_array($this->tname, $result),
$this->getTestDesc('renaming table')
);
$table->rename($this->tname);
unset($result);
// check record count
$mapper = new \DB\SQL\Mapper($db, $this->tname);
$this->test->expect(
count($mapper->find()) == 9,
$this->getTestDesc('check record count')
);
unset($mapper);
// adding composite primary keys
$table->addColumn('version')->type($schema::DT_INT4)->nullable(false)->defaults(1);
$table->primary(array('id', 'version'));
$table->build();
$r1 = $table->getCols(true);
$this->test->expect(!empty($r1) && isset($r1['version']) &&
$r1['id']['pkey'] == true && $r1['version']['pkey'] == true,
$this->getTestDesc('adding composite primary-keys')
);
unset($r1);
// check record count
$mapper = new \DB\SQL\Mapper($db, $this->tname);
$this->test->expect(
count($mapper->find()) == 9,
$this->getTestDesc('check record count')
);
unset($mapper);
// drop table
$schema->dropTable($this->tname);
$this->test->expect(
!in_array($this->tname, $schema->getTables()),
$this->getTestDesc('drop table')
);
// adding composite primary keys
$table = $schema->createTable($this->tname);
$table->addColumn('version')->type($schema::DT_INT4)
->defaults(1)->nullable(false);
$table->primary(array('id', 'version'));
$table = $table->build();
$r1 = $table->getCols(true);
$this->test->expect(!empty($r1) &&
$r1['id']['pkey'] == true && $r1['version']['pkey'] == true,
$this->getTestDesc('creating new table with composite key')
);
$this->test->expect(!empty($r1) &&
$r1['version']['default'] == '1',
$this->getTestDesc('default value on composite primary key')
);
unset($r1);
// more fields to composite primary key table
$table->addColumn('title')->type($schema::DT_VARCHAR256);
$table->addColumn('title2')->type($schema::DT_TEXT);
$table->addColumn('title_notnull')
->type($schema::DT_VARCHAR128)->nullable(false)->defaults("foo");
$table->build();
$r1 = $table->getCols(true);
$this->test->expect(
array_key_exists('title', $r1) &&
array_key_exists('title_notnull', $r1) &&
$r1['id']['pkey'] == true && $r1['version']['pkey'] == true,
$this->getTestDesc('adding more fields to composite pk table')
);
unset($r1);
// testing primary keys with inserted data
$mapper = new \DB\SQL\Mapper($db, $this->tname);
$mapper->title = 'test1';
$mapper->save();
$mapper->reset();
$mapper->id = 1;
$mapper->title = 'nullable';
$mapper->version = 2;
$mapper->save();
$mapper->reset();
$mapper->title = 'test3';
$mapper->title2 = 'foobar';
$mapper->title_notnull = 'bar';
$mapper->save();
$result = array_map(array($mapper,'cast'),$mapper->find());
$cpk_expected = array(
0=>array(
'id' => 1,
'version' => 1,
'title' => 'test1',
'title2' => NULL,
'title_notnull' => 'foo',
),
1=>array(
'id' => 1,
'version' => 2,
'title' => 'nullable',
'title2' => NULL,
'title_notnull' => 'foo',
),
2=>array(
'id' => 2,
'version' => 1,
'title' => 'test3',
'title2' => 'foobar',
'title_notnull' => 'bar',
),
);
foreach ($result as &$r)
ksort($r);
foreach ($cpk_expected as &$r)
ksort($r);
$this->test->expect(
json_encode($result) == json_encode($cpk_expected),
$this->getTestDesc('adding items with composite primary-keys')
);
$schema->dropTable($this->tname);
// indexes
$table = $schema->createTable($this->tname);
$table->addColumn('rawtest', array('type' => $schema::DT_VARCHAR256, 'default' => 'foo'));
$table->addColumn('text')->type($schema::DT_TEXT);
$table->addColumn('foo')->type($schema::DT_VARCHAR128)->index();
$table = $table->build();
$r1 = $table->getCols(true);
$this->test->expect(
isset($r1['rawtest']) && $r1['rawtest']['default'] = 'foo',
$this->getTestDesc('adding column with options array')
);
$indexes = $table->listIndex();
$this->test->expect(
isset($indexes[$table->name.'___foo']),
$this->getTestDesc('column index on table creation')
);
$table->addColumn('bar')->type($schema::DT_VARCHAR128)->index(true);
$table->addColumn('baz')->type($schema::DT_VARCHAR128);
$table->addIndex(array('foo', 'baz'));
$table->build();
$indexes = $table->listIndex();
$this->test->expect(
isset($indexes[$table->name.'___bar']),
$this->getTestDesc('column index on table alteration')
);
$this->test->expect(
isset($indexes[$table->name.'___bar']) && $indexes[$table->name.'___bar']['unique'] == true,
$this->getTestDesc('unique index')
);
$this->test->expect(
isset($indexes[$table->name.'___foo__baz']),
$this->getTestDesc('index on combined columns')
);
if($this->current_engine == 'sqlite') {
$table->dropColumn('rawtest');
$table->build();
$indexes = $table->listIndex();
$this->test->expect(
isset($indexes[$table->name.'___foo__baz']) && isset($indexes[$table->name.'___bar'])
&& $indexes[$table->name.'___bar']['unique'],
$this->getTestDesc('preserve indexes after table rebuild')
);
}
$table->dropIndex($table->name.'___bar');
$table->build();
$indexes = $table->listIndex();
$this->test->expect(
!array_key_exists($table->name.'___bar',$indexes),
$this->getTestDesc('drop index')
);
// update column
$table->updateColumn('bar',$schema::DT_TEXT);
$table->build();
$r1 = $table->getCols(true);
$this->test->expect(
array_key_exists('bar', $r1) && $r1['bar']['type'] == 'text',
$this->getTestDesc('update column')
);
// create table with text not nullable column
$table2 = $schema->createTable($this->tname.'_notnulltext');
$table2->addColumn('desc')->type($schema::DT_TEXT)->nullable(false);
$table2 = $table2->build();
$r1 = $schema->getTables();
$r2 = $table2->getCols(true);
$this->test->expect(
in_array($this->tname.'_notnulltext', $r1) && array_key_exists('desc', $r2)
&& $r2['desc']['nullable']==false,
$this->getTestDesc('create new table with not nullable text column')
);
$table2->drop();
}
}

View File

@@ -1,7 +1,12 @@
[globals]
; Verbosity level of the stack trace. Assign values between 0 to 3 for increasing verbosity levels
DEBUG = 2
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
ONERROR = "Controller\MapController->showError"
; Path to the index.php main/front controller.
BASE = /exodus4d/pathfinder

2509
app/lib/db/cortex.php Normal file

File diff suppressed because it is too large Load Diff

1186
app/lib/db/sql/schema.php Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -7,8 +7,7 @@
*/
namespace Controller;
use Model\User;
use Model;
class AccessController extends Controller {
@@ -21,19 +20,55 @@ class AccessController extends Controller {
*/
function beforeroute() {
parent::beforeroute();
$isLoggedIn = $this->_isLoggedIn();
if($this->isLoggedIn()){
if($isLoggedIn){
$accessRoute = true;
}else{
$userName = 'abcdefghijklmnopqrst';
$password = 'password';
// try to verify user
$accessRoute = $this->verify('exodus 4d', 'test');
$accessRoute = $this->_verifyUser($userName, $password);
if(!$accessRoute){
// add new User
try{
$this->_registerUser($userName, $password);
}catch(\Exception\ValidationException $e){
// registration failed
$this->f3->error($e->getCode(), $e->getMessage());
}
}
}
if(!$accessRoute){
if(
!$this->f3->get('AJAX') &&
!$accessRoute
){
$this->f3->reroute('/login');
}
parent::beforeroute();
}
/**
* stores a new user in database
* @param $username
* @param $password
* @return null
*/
private function _registerUser($username, $password){
$user = Model\BasicModel::getNew('UserModel');
$user->name = $username;
$user->password = $user::generatePasswordHash($password);
$user->save();
return $user;
}
/**
@@ -42,32 +77,19 @@ class AccessController extends Controller {
* @param $password
* @return bool
*/
private function verify($userName, $password) {
private function _verifyUser($userName, $password) {
$verify = false;
// check if user is already logged in
$isLoggedId = $this->isLoggedIn();
$user = Model\BasicModel::getNew('UserModel');
if($isLoggedId){
$user->getByName($userName);
$isValid = $user->verify($password);
if($isValid === true){
$this->_logIn($user);
$verify = true;
}else{
$user = new \Model\UserModel($this->f3->get('DB'));
$auth = new \Auth($user, array('id' => 'name', 'pw' => 'password'));
$loginResult = $auth->login($userName, $password);
if($loginResult){
// login
// two step user authentication
$user->getByAuth($userName, $password);
if(! $user->dry()){
$this->logIn($user);
$verify = true;
}
}
}
return $verify;
@@ -77,7 +99,7 @@ class AccessController extends Controller {
* checks weather a user is currently logged in
* @return bool
*/
private function isLoggedIn(){
private function _isLoggedIn(){
$loggedIn = false;
@@ -98,11 +120,11 @@ class AccessController extends Controller {
}else{
// log out
// get user model
$user = new \Model\UserModel($this->f3->get('DB'));
$user = Model\BasicModel::getNew('UserModel');
$user->getById($this->f3->get('SESSION.user.id'));
if(! $user->dry()){
$this->logOut($user);
$this->_logOut($user);
}
}
}
@@ -113,22 +135,36 @@ class AccessController extends Controller {
/**
* @param $user
*/
private function logOut($user){
$this->f3->clear('SESSION.user');
private function _logOut($user){
$this->f3->clear('SESSION');
}
/**
* log user in by mapper obj
* @param $user
*/
private function logIn($user){
private function _logIn($user){
// user verified -> set Session login
new \DB\SQL\Session($this->f3->get('DB'));
$dateTime = new \DateTime();
$this->f3->set('SESSION.user.time', $dateTime->getTimestamp());
$this->f3->set('SESSION.user.name', $user->name);
$this->f3->set('SESSION.user.name', $user->name);
$this->f3->set('SESSION.user.id', $user->id);
}
/**
*
* @return bool|null
*/
protected function _getUser(){
$user = Model\BasicModel::getNew('UserModel');
$user->getById($this->f3->get('SESSION.user.id'));
if($user->dry()){
$user = false;
}
return $user;
}

View File

@@ -31,14 +31,16 @@ class Controller {
$f3 = \Base::instance();
$this->f3 = $f3;
// init DB
$this->setDB('PF');
}
/**
* event handler
*/
function beforeroute() {
// init DB
$this->setDB('PF');
}
/**
@@ -51,6 +53,10 @@ class Controller {
}
}
/**
* set/change DB connection
* @param $type
*/
protected function setDB($type){
if($type === 'CCP'){
@@ -70,7 +76,6 @@ class Controller {
}
$this->f3->set('DB', $db);
}

View File

@@ -8,14 +8,46 @@
namespace Controller;
class MapController extends AccessController {
class MapController extends Controller {
function __construct() {
parent::__construct();
}
public function showMap() {
$this->setTemplate('templates/view/map.html');
}
/**
* function is called on each error
* @param $f3
*/
public function showError($f3){
print_r($f3->get('ERROR'));
// set HTTP status
if(!empty($f3->get('ERROR.code'))){
$f3->status($f3->get('ERROR.code'));
}
if($f3->get('AJAX')){
header('Content-type: application/json');
// error on ajax call
$errorData = [
'status' => $f3->get('ERROR.status'),
'code' => $f3->get('ERROR.code'),
'text' => $f3->get('ERROR.text')
];
echo json_encode($errorData);
}else{
echo 'TODO: static errors';
}
die();
}
}

View File

@@ -0,0 +1,197 @@
<?php
/**
* Created by PhpStorm.
* User: exodus4d
* Date: 16.02.15
* Time: 20:23
*/
namespace Controller\Api;
use Model;
class Map extends \Controller\AccessController {
/**
* event handler
*/
function beforeroute() {
// set header for all routes
header('Content-type: application/json');
parent::beforeroute();
}
/**
* Get all required static config data for program initialization
* @param $f3
*/
public function init($f3){
// expire time in seconds
$expireTimeHead = 60 * 60 * 24;
$expireTimeSQL = 60 * 60 * 24;
$f3->expire($expireTimeHead);
$initData = [];
// get all available map types -------------------------------------
$mapType = Model\BasicModel::getNew('MapTypeModel');
$rows = $mapType->find('active = 1', null, $expireTimeSQL);
$mapTypeData = [];
foreach((array)$rows as $rowData){
$data = [
'id' => $rowData->id,
'label' => $rowData->label,
'class' => $rowData->class,
'classTab' => $rowData->classTab
];
$mapTypeData[$rowData->name] = $data;
}
$initData['mapTypes'] = $mapTypeData;
// get all available map scopes ------------------------------------
$mapScope = Model\BasicModel::getNew('MapScopeModel');
$rows = $mapScope->find('active = 1', null, $expireTimeSQL);
$mapScopeData = [];
foreach((array)$rows as $rowData){
$data = [
'id' => $rowData->id,
'label' => $rowData->label
];
$mapScopeData[$rowData->name] = $data;
}
$initData['mapScopes'] = $mapScopeData;
// get all available system status ------------------------------------
$systemStatus = Model\BasicModel::getNew('SystemStatusModel');
$rows = $systemStatus->find('active = 1', null, $expireTimeSQL);
$systemScopeData = [];
foreach((array)$rows as $rowData){
$data = [
'id' => $rowData->id,
'label' => $rowData->label,
'class' => $rowData->class
];
$systemScopeData[$rowData->name] = $data;
}
$initData['systemStatus'] = $systemScopeData;
// get all available system types -------------------------------------
$systemType = Model\BasicModel::getNew('SystemTypeModel');
$rows = $systemType->find('active = 1', null, $expireTimeSQL);
$systemTypeData = [];
foreach((array)$rows as $rowData){
$data = [
'id' => $rowData->id,
'name' => $rowData->name
];
$systemTypeData[$rowData->name] = $data;
}
$initData['systemType'] = $systemTypeData;
// get available connection scopes ---------------------------------
$connectionScope = Model\BasicModel::getNew('ConnectionScopeModel');
$rows = $connectionScope->find('active = 1', null, $expireTimeSQL);
$connectionScopeData = [];
foreach((array)$rows as $rowData){
$data = [
'id' => $rowData->id,
'label' => $rowData->label
];
$connectionScopeData[$rowData->name] = $data;
}
$initData['connectionScopes'] = $connectionScopeData;
echo json_encode($initData);
}
/**
* update map data api
* function is called continuously
* @param $f3
*/
public function updateData($f3){
$mapData = (array)$f3->get('POST.mapData');
$user = $this->_getUser();
$map = Model\BasicModel::getNew('MapModel');
$system = Model\BasicModel::getNew('SystemModel');
$connection = Model\BasicModel::getNew('ConnectionModel');
foreach($mapData as $data){
$config = $data['config'];
$systems = $data['data']['systems'];
$connections = $data['data']['connections'];
// update map data ---------------------------------------------
$map->getById($config['id']);
// update map on change
if( (int)$config['updated'] > strtotime($map->updated)){
$map->setData($config);
$map->save();
}
// get system data -----------------------------------------------
foreach($systems as $systemData){
$system->getById($systemData['id']);
if((int)$systemData['updated'] > strtotime($system->updated)){
$systemData['mapId'] = $map;
$system->setData($systemData);
$system->save();
}
$system->reset();
}
// get connection data -------------------------------------------
foreach($connections as $connectionData){
$connection->getById($connectionData['id']);
if((int)$connectionData['updated'] > strtotime($connection->updated)){
$connectionData['mapId'] = $map;
$connection->setData($connectionData);
$connection->save();
}
$connection->reset();
}
$map->reset();
}
// get map data ======================================================
$activeMaps = $user->getMaps();
$newData = [];
foreach($activeMaps as $activeMap){
$newData[] = [
'config' => $activeMap->getData(),
'data' => [
'systems' => $activeMap->getSystemData(),
'connections' => $activeMap->getConnectionData(),
]
];
}
echo json_encode($newData);
}
}

View File

@@ -8,8 +8,9 @@
namespace Controller\Api;
use Data\Mapper as Mapper;
use Model;
class Systems extends \Controller\AccessController {
class System extends \Controller\AccessController {
private $mainQuery = "SELECT
map_sys.constellationID connstallation_id,
@@ -87,7 +88,12 @@ class Systems extends \Controller\AccessController {
return $query;
}
public function getDataById($f3, $params){
/**
* get system data by systemId
* @param $f3
* @param $params
*/
public function getById($f3, $params){
// switch DB
$this->setDB('CCP');
@@ -110,7 +116,15 @@ class Systems extends \Controller\AccessController {
// format result
$mapper = new Mapper\CcpSystemsMapper($rows);
$data = $mapper->getData();
$ccpData = $mapper->getData();
// switch DB
$this->setDB('PF');
$system = Model\BasicModel::getNew('SystemModel');
$system->setData(reset($ccpData));
$data = $system->getData();
echo json_encode($data);
}

View File

@@ -57,16 +57,23 @@ class CcpSystemsMapper extends \RecursiveArrayIterator {
// format functions
self::$map['type'] = function($iterator){
// TODO refactore
$type = 'wh';
$typeId = 1;
if(
$iterator['security'] == 7 ||
$iterator['security'] == 8 ||
$iterator['security'] == 9
){
$type = 'k-space';
$typeId = 2;
}
return $type;
return [
'id' => $typeId,
'name' => $type
];
};
iterator_apply($this, 'self::recursiveIterator', array($this));
@@ -92,8 +99,6 @@ class CcpSystemsMapper extends \RecursiveArrayIterator {
// check for mapping key
if(array_key_exists($iterator->key(), self::$map)){
$removeOldEntry = false;
if(is_array(self::$map[$iterator->key()])){
// a -> array mapping

View File

@@ -0,0 +1,21 @@
<?php
/**
* Created by PhpStorm.
* User: exodus4d
* Date: 21.02.15
* Time: 00:41
*/
namespace Exception;
class BaseException extends \Exception {
const
VALIDATION_FAILED = 403;
public function __construct($message, $code = 0){
parent::__construct($message, $code);
}
}

View File

@@ -0,0 +1,17 @@
<?php
/**
* Created by PhpStorm.
* User: exodus4d
* Date: 21.02.15
* Time: 00:12
*/
namespace Exception;
class ValidationException extends BaseException {
public function __construct($message, $code = 0){
parent::__construct($message, $code);
}
}

View File

@@ -0,0 +1,178 @@
<?php
/**
* Created by PhpStorm.
* User: exodus4d
* Date: 16.02.15
* Time: 22:11
*/
namespace Model;
use Exception;
class BasicModel extends \DB\Cortex{
protected $db = 'DB';
/**
* field validation array
* @var array
*/
protected $validate = [];
/**
* overwrites magic __set() mainly used for validation
* @param string $col
* @param \scalar $val
* @return mixed|void
* @throws \Exception\ValidationException
*/
public function __set($col, $val){
// never set updated field
if($col == 'updated'){
return;
}
// trim all values
if(is_string($val)){
$val = trim($val);
}
$valid = $this->_validateField($col, $val);
if(!$valid){
throw new Exception\ValidationException('Field validation: "' . $this->table . '->' . $col . '" not valid', Exception\BaseException::VALIDATION_FAILED);
}else{
parent::__set($col, $val);
}
}
/**
* validates a table column based on validation settings
* @param $col
* @param $val
* @return bool
*/
private function _validateField($col, $val){
$valid = true;
if(array_key_exists($col, $this->validate)){
$fieldValidationOptions = $this->validate[$col];
foreach($fieldValidationOptions as $validateKey => $validateOption ){
if(is_array($fieldValidationOptions[$validateKey])){
$fieldSubValidationOptions = $fieldValidationOptions[$validateKey];
foreach($fieldSubValidationOptions as $validateSubKey => $validateSubOption ){
switch($validateKey){
case 'length':
switch($validateSubKey){
case 'min';
if(strlen($val) < $validateSubOption){
$valid = false;
}
break;
case 'max';
if(strlen($val) > $validateSubOption){
$valid = false;
}
break;
}
break;
}
}
}else{
switch($validateKey){
case 'regex':
$valid = (bool)preg_match($fieldValidationOptions[$validateKey], $val);
break;
}
}
// a validation rule failed
if(!$valid){
break;
}
}
}
return $valid;
}
/**
* get single dataSet by id
* @param $id
* @return array|FALSE
*/
public function getById($id) {
return $this->getByForeignKey('id', (int)$id, array('limit' => 1));
}
/**
* get dataSet by foreign column
* @param $key
* @param $id
* @param $options
* @return array|FALSE
*/
public function getByForeignKey($key, $id, $options = array()){
$querySet = [];
$query = [];
if($this->exists($key)){
$query[] = $key . " = :" . $key;
$querySet[':' . $key] = $id;
}
// check active column
if($this->exists('active')){
$query[] = "active = :active";
$querySet[':active'] = 1;
}
array_unshift($querySet, implode(' AND ', $query));
return $this->load( $querySet, $options );
}
/**
* get multiple model obj that have an 1->m relation to this model
* @param $model
* @param $foreignKey
* @return mixed
*/
public function getRelatedModels($model, $foreignKey){
$model = self::getNew($model);
$relatedModels = $model->find(array($foreignKey . ' = ? AND active = 1', $this->id), null, 10);
return $relatedModels;
}
/**
* factory for all Models
* @param $model
* @return null
* @throws \Exception
*/
public static function getNew($model){
$class = null;
$model = '\\' . __NAMESPACE__ . '\\' . $model;
if(class_exists($model)){
$class = new $model();
}else{
throw new \Exception('No model class found');
}
return $class;
}
}

View File

@@ -0,0 +1,58 @@
<?php
/**
* Created by PhpStorm.
* User: exodus4d
* Date: 26.02.15
* Time: 21:12
*/
namespace Model;
class ConnectionModel extends BasicModel{
protected $table = 'connection';
protected $fieldConf = array(
'mapId' => array(
'belongs-to-one' => 'Model\MapModel'
),
'type' => array(
'type' => self::DT_JSON
)
);
/**
* set an array with all data for a system
* @param $systemData
*/
public function setData($systemData){
foreach((array)$systemData as $key => $value){
if(!is_array($value)){
if($this->exists($key)){
$this->$key = $value;
}
}elseif($key == 'type'){
// json field
$this->$key = $value;
}
}
}
public function getData(){
$connectionData = [
'id' => $this->id,
'source' => $this->source,
'target' => $this->target,
'scope' => $this->scope,
'type' => $this->type,
'updated' => strtotime($this->updated)
];
return $connectionData;
}
}

View File

@@ -0,0 +1,16 @@
<?php
/**
* Created by PhpStorm.
* User: exodus4d
* Date: 17.02.15
* Time: 20:01
*/
namespace Model;
class ConnectionScopeModel extends BasicModel{
protected $table = 'connection_scope';
}

123
app/main/model/MapModel.php Normal file
View File

@@ -0,0 +1,123 @@
<?php
/**
* Created by PhpStorm.
* User: exodus4d
* Date: 16.02.15
* Time: 22:10
*/
namespace Model;
class MapModel extends BasicModel{
protected $table = 'map';
protected $fieldConf = array(
'scopeId' => array(
'belongs-to-one' => 'Model\MapScopeModel'
),
'typeId' => array(
'belongs-to-one' => 'Model\MapTypeModel'
)
);
protected $validate = [
'name' => [
'length' => [
'min' => 3
]
],
'icon' => [
'length' => [
'min' => 3
]
],
'scopeId' => [
'regex' => '/^[1-9]+$/'
],
'typeId' => [
'regex' => '/^[1-9]+$/'
],
];
public function setData($data){
foreach((array)$data as $key => $value){
if(!is_array($value)){
if($this->exists($key)){
$this->$key = $value;
}
}else{
// special array data
if($key == 'scope'){
$this->scopeId = $value['id'];
}elseif($key == 'type'){
$this->typeId = $value['id'];
}
}
}
}
/**
* get map data for for response
* @return array
*/
public function getData(){
$mapData = [
'id' => $this->id,
'name' => $this->name,
'scope' => [
'id' => $this->scopeId->id,
'name' => $this->scopeId->name,
'label' => $this->scopeId->label
],
'type' => [
'id' => $this->typeId->id,
'name' => $this->typeId->name
],
'icon' => $this->icon,
'updated' => strtotime($this->updated)
];
return $mapData;
}
/**
* get all system data for all systems in this map
* @return array
*/
public function getSystemData(){
$systems = $this->getRelatedModels('SystemModel', 'mapId');
$systemData = [];
foreach($systems as $system){
$systemData[] = $system->getData();
}
return $systemData;
}
/**
* get all connection data in this map
* @return array
*/
public function getConnectionData(){
$connections = $this->getRelatedModels('ConnectionModel', 'mapId');
$connectionData = [];
foreach($connections as $connection){
$connectionData[] = $connection->getData();
}
return $connectionData;
}
}

View File

@@ -0,0 +1,16 @@
<?php
/**
* Created by PhpStorm.
* User: exodus4d
* Date: 17.02.15
* Time: 20:01
*/
namespace Model;
class MapScopeModel extends BasicModel{
protected $table = 'map_scope';
}

View File

@@ -0,0 +1,16 @@
<?php
/**
* Created by PhpStorm.
* User: exodus4d
* Date: 17.02.15
* Time: 20:01
*/
namespace Model;
class MapTypeModel extends BasicModel{
protected $table = 'map_type';
}

View File

@@ -0,0 +1,107 @@
<?php
/**
* Created by PhpStorm.
* User: exodus4d
* Date: 23.02.15
* Time: 23:56
*/
namespace Model;
class SystemModel extends BasicModel {
protected $table = 'system';
protected $fieldConf = array(
'mapId' => array(
'belongs-to-one' => 'Model\MapModel'
),
'typeId' => array(
'belongs-to-one' => 'Model\SystemTypeModel'
),
'statusId' => array(
'belongs-to-one' => 'Model\SystemStatusModel'
)
);
/**
* set an array with all data for a system
* @param $systemData
*/
public function setData($systemData){
foreach((array)$systemData as $key => $value){
if(!is_array($value)){
if($this->exists($key)){
$this->$key = $value;
}
}else{
// special array data
if($key == 'type'){
$this->typeId = $value['id'];
}elseif($key == 'constellation'){
$this->constellationId = $value['id'];
$this->constellation = $value['name'];
}elseif($key == 'region'){
$this->regionId = $value['id'];
$this->region = $value['name'];
}elseif($key == 'type'){
$this->typeId = $value['id'];
}elseif($key == 'status'){
$this->statusId = $value['id'];
}elseif($key == 'position'){
$this->posX = $value['x'];
$this->posY = $value['y'];
}
}
}
}
/**
* get map data for for response
* @return array
*/
public function getData(){
$systemData = [
'id' => $this->id,
'mapId' => is_object($this->mapId) ? $this->mapId->id : 0,
'systemId' => $this->systemId,
'name' => $this->name,
'alias' => $this->alias,
'effect' => $this->effect,
'security' => $this->security,
'trueSec' => $this->trueSec,
'region' => [
'id' => $this->regionId,
'name' => $this->region
],
'constellation' => [
'id' => $this->constellationId,
'name' => $this->constellation
],
'type' => [
'id' => $this->typeId->id,
'name' => $this->typeId->name
],
'status' => [
'id' => is_object($this->statusId) ? $this->statusId->id : 0,
'name' => is_object($this->statusId) ? $this->statusId->name : ''
],
'locked' => $this->locked,
'rally' => $this->rally,
'position' => [
'x' => $this->posX,
'y' => $this->posY
],
'updated' => strtotime($this->updated)
];
return $systemData;
}
}

View File

@@ -0,0 +1,15 @@
<?php
/**
* Created by PhpStorm.
* User: exodus4d
* Date: 24.02.15
* Time: 21:53
*/
namespace Model;
class SystemStatusModel extends BasicModel {
protected $table = 'system_status';
}

View File

@@ -0,0 +1,15 @@
<?php
/**
* Created by PhpStorm.
* User: exodus4d
* Date: 25.02.15
* Time: 21:59
*/
namespace Model;
class SystemTypeModel extends BasicModel {
protected $table = 'system_type';
}

View File

@@ -0,0 +1,24 @@
<?php
/**
* Created by PhpStorm.
* User: exodus4d
* Date: 28.02.15
* Time: 11:57
*/
namespace Model;
class UserMapModel extends BasicModel {
protected $table = 'user_map';
protected $fieldConf = array(
'mapId' => array(
'belongs-to-one' => 'Model\MapModel'
)
);
}

View File

@@ -8,19 +8,75 @@
namespace Model;
class UserModel extends \DB\SQL\Mapper {
class UserModel extends BasicModel {
private $tableName = 'user';
protected $table = 'user';
function __construct(\DB\SQL $db) {
parent::__construct($db, $this->tableName);
protected $validate = [
'name' => [
'length' => [
'min' => 5,
'max' => 20
],
'regex' => '/^[ \w-_]+$/'
],
'password' => [
'length' => [
'min' => 5,
'max' => 255
]
]
];
/**
* generate password hash
* @param $password
* @return FALSE|string
*/
public static function generatePasswordHash($password){
// generate random id (23 chars)
$salt = uniqid('', true);
return \Bcrypt::instance()->hash($password, $salt);
}
public function getById($id) {
return $this->load( array('id=?', $id) );
/**
* search for user by unique username
* @param $name
* @return array|FALSE
*/
public function getByName($name){
return $this->getByForeignKey('name', $name);
}
public function getByAuth($name, $password){
return $this->load( array('name=? AND password=?', $name, $password) );
/**
* verify a user by his wassword
* @param $password
* @return bool
*/
public function verify($password){
$valid = false;
if(! $this->dry()){
$valid = (bool) \Bcrypt::instance()->verify($password, $this->password);
}
return $valid;
}
/**
* get all assessable map models for a single user
* @return array
*/
public function getMaps(){
$userMaps = $this->getRelatedModels('UserMapModel', 'userId');
$maps = [];
foreach($userMaps as $userMap){
$maps[] = $userMap->mapId;
}
return $maps;
}
}

View File

@@ -5,5 +5,5 @@ GET|POST /= Controller\MapController->showMap
GET|POST /login= Controller\Controller->showLogin
; api routes
GET /api/@controller/@action = Controller\Api\@controller->@action
GET /api/@controller/@action/@arg1 = Controller\Api\@controller->@action
GET|POST /api/@controller/@action = Controller\Api\@controller->@action
GET|POST /api/@controller/@action/@arg1 = Controller\Api\@controller->@action

View File

@@ -2,6 +2,9 @@
$f3 = require('app/lib/base.php');
// sync program with eve server time
date_default_timezone_set('UTC');
// load configuration
$f3->config('app/config.cfg');

View File

@@ -27,6 +27,7 @@ requirejs.config({
hoverIntent: 'lib/jquery.hoverIntent.minified', // v1.8.0 Hover intention - http://cherne.net/brian/resources/jquery.hoverIntent.html
fullScreen: 'lib/jquery.fullscreen.min', // v0.5.0 Full screen mode - https://github.com/private-face/jquery.fullscreen
select2: 'lib/select2.min', // v4.0.0 Drop Down customization - https://select2.github.io/
validator: 'lib/validator.min', // v0.7.2 Validator for Bootstrap 3 - https://github.com/1000hz/bootstrap-validator
pnotify: 'lib/pnotify/pnotify.core', // v2.0.1 PNotify - notification core file
@@ -93,6 +94,9 @@ requirejs.config({
select2: {
deps : ['jquery'],
exports: 'Select2'
},
validator: {
deps : ['jquery', 'bootstrap']
}
}
});

View File

@@ -22,8 +22,10 @@ define(['jquery'], function($) {
},
path: {
img: 'public/img/', // path for images
searchSystems: 'api/systems/search', // ajax path - search system by name
getSystem: 'api/systems/getDataById' // ajax path - get system by id
searchSystems: 'api/system/search', // ajax URL - search system by name
getSystem: 'api/system/getById', // ajax URL - get system by id
initMap: 'api/map/init', // ajax URL - get static data
updateMapData: 'api/map/updateData' // ajax URL - main map update call
},
url: {
zKillboard: 'https://zkillboard.com/api/', // killboard api
@@ -49,29 +51,6 @@ define(['jquery'], function($) {
label: 'error'
}
},
// map types
mapTypes: {
standard: {
class: '',
classTab: 'pf-map-type-tab-default',
label: ''
},
private: {
class: 'pf-map-type-private',
classTab: 'pf-map-type-tab-private',
label: 'Private'
},
alliance: {
class: 'pf-map-type-alliance',
classTab: 'pf-map-type-tab-alliance',
label: 'Alliance'
},
global: {
class: 'pf-map-type-global',
classTab: 'pf-map-type-tab-global',
label: 'Global'
}
},
// system effects
systemEffects: {
@@ -173,33 +152,6 @@ define(['jquery'], function($) {
class: 'pf-system-security-1-0'
}
},
// system status
systemStatus: {
unknown: {
class: 'pf-system-status-unknown',
label: 'unknown'
},
friendly: {
class: 'pf-system-status-friendly',
label: 'friendly'
},
occupied: {
class: 'pf-system-status-occupied',
label: 'occupied'
},
hostile: {
class: 'pf-system-status-hostile',
label: 'hostile'
},
empty: {
class: 'pf-system-status-empty',
label: 'empty'
},
unscanned: {
class: 'pf-system-status-unscanned',
label: 'unscanned'
}
},
// system info
systemInfo: {
rally: {
@@ -215,17 +167,6 @@ define(['jquery'], function($) {
},
// map scopes
defaultMapScope: 'wh', // default scope for connection
mapScopes: { // available scopes for a connection
wh: {
label: 'wormhole'
},
stargate: {
label: 'stargate'
},
jumpbridge: {
label: 'jumpbridge'
}
},
// map connection types
connectionTypes: {
jumpbridge: {

View File

@@ -37,9 +37,18 @@ define([
config: {
id: 99,
name: 'Polaris',
scope: 'wormhole',
scope: {
id: 1,
name: 'wh',
label: 'w-space'
},
icon: 'fa-globe',
type: 'alliance' // global, alliance, private
type: {
id: 1,
name: 'alliance',
label: 'Alliance'
},
updated: 1424545904
},
data: {
systems: [
@@ -49,12 +58,26 @@ define([
name: 'J150020',
alias: 'Polaris',
effect: 'magnetar',
type: 'wh',
type: {
id: 1,
name: 'wh'
},
security: 'C6',
trueSec: -1,
status: 'friendly',
locked: '1',
rally: '0',
region: {
id: '11000030',
name: 'F-R00030'
},
constellation: {
id: '21000298',
name: 'F-C00298'
},
status: {
id: 2,
name: 'friendly'
},
locked: 1,
rally: 0,
position: {
x: 8,
y: 300
@@ -66,10 +89,24 @@ define([
name: 'J115844',
alias: '',
effect: 'wolfRyet',
type: 'wh',
type: {
id: 1,
name: 'wh'
},
security: 'C6',
trueSec: -1,
status: 'empty',
region: {
id: '11000030',
name: 'F-R00030'
},
constellation: {
id: '21000298',
name: 'F-C00298'
},
status: {
id: 5,
name: 'empty'
},
position: {
x: 25,
y: 40
@@ -82,10 +119,24 @@ define([
name: 'J155207',
alias: '',
effect: 'wolfRyet',
type: 'wh',
type: {
id: 1,
name: 'wh'
},
security: 'C6',
trueSec: -1,
status: '',
region: {
id: '11000030',
name: 'F-R00030'
},
constellation: {
id: '21000301',
name: 'F-C00301'
},
status: {
id: 1,
name: 'unknown'
},
locked: '1',
rally: '1',
position: {
@@ -101,8 +152,22 @@ define([
effect: 'pulsar',
security: 'C3',
trueSec: -1,
type: 'wh',
status: 'hostile',
region: {
id: '11000030',
name: 'F-R00030'
},
constellation: {
id: '21000303',
name: 'F-C00303'
},
type: {
id: 1,
name: 'wh'
},
status: {
id: 4,
name: 'hostile'
},
position: {
x: 40,
y: 160
@@ -117,15 +182,21 @@ define([
security: 'L',
trueSec: 0.3,
region: {
id: '10000036',
id: 10000036,
name: 'Devoid'
},
constellation: {
id: '20000436',
id: 20000436,
name: 'Jayai'
},
type: 'k-space',
status: '',
type: {
id: 2,
name: 'k-space'
},
status: {
id: 1,
name: 'unknown'
},
position: {
x: 280,
y: 250
@@ -140,15 +211,21 @@ define([
security: 'H',
trueSec: 0.9,
region: {
id: '10000002',
id: 10000002,
name: 'The Forge'
},
constellation: {
id: '20000020',
id: 20000020,
name: 'Kimotoro'
},
type: 'k-space',
status: '',
type: {
id: 2,
name: 'k-space'
},
status: {
id: 1,
name: 'unknown'
},
position: {
x: 400,
y: 150
@@ -163,15 +240,21 @@ define([
security: 'C1',
trueSec: -1,
region: {
id: '11000002',
id: 11000002,
name: 'A-R00002'
},
constellation: {
id: '21000002',
id: 21000002,
name: 'A-C00002'
},
type: 'wh',
status: 'occupied',
type: {
id: 1,
name: 'wh'
},
status: {
id: 3,
name: 'occupied'
},
position: {
x: 600,
y: 75
@@ -186,15 +269,21 @@ define([
security: 'H',
trueSec: 0.9,
region: {
id: '10000002',
id: 10000002,
name: 'The Forge'
},
constellation: {
id: '20000020',
name: 'Kimotoro'
},
type: 'k-space',
status: 'unscanned',
type: {
id: 2,
name: 'k-space'
},
status: {
id: 6,
name: 'unscanned'
},
position: {
x: 550,
y: 200
@@ -209,15 +298,21 @@ define([
security: '0.0',
trueSec: -0.1,
region: {
id: '10000012',
id: 10000012,
name: 'Curse'
},
constellation: {
id: '20000150',
name: 'Sound'
},
type: 'k-space',
status: '',
type: {
id: 2,
name: 'k-space'
},
status: {
id: 1,
name: 'unknown'
},
position: {
x: 500,
y: 300
@@ -316,46 +411,85 @@ define([
config: {
name: 'Providence',
id: 2,
scope: 'wormhole',
scope: {
id: 1,
name: 'wh',
label: 'w-space'
},
icon: 'fa-bookmark',
type: 'global'
type: {
id: 3,
name: 'global',
label: 'global'
},
updated: 1424545903
},
data: {
systems: [
{
id: 50,
systemId: 31002378,
name: 'J150020',
id: 755,
systemId: 30000144,
name: 'Perimeter',
alias: '',
effect: 'magnetar',
security: 'C6',
type: 'wh',
status: 'friendly',
effect: '',
security: 'H',
trueSec: 0.9,
region: {
id: 10000002,
name: 'The Forge'
},
constellation: {
id: '20000020',
name: 'Kimotoro'
},
type: {
id: 2,
name: 'k-space'
},
status: {
id: 6,
name: 'unscanned'
},
position: {
x: 5,
x: 550,
y: 200
},
updated: 1420903681
},{
id: 51,
systemId: 31002375,
name: 'J115844',
id: 8555,
systemId: 30001028,
name: 'RMOC-W',
alias: '',
effect: 'wolfRyet',
security: 'C6',
type: 'wh',
status: 'empty',
effect: '',
security: '0.0',
trueSec: -0.1,
region: {
id: 10000012,
name: 'Curse'
},
constellation: {
id: '20000150',
name: 'Sound'
},
type: {
id: 2,
name: 'k-space'
},
status: {
id: 1,
name: 'unknown'
},
position: {
x: 60,
y: 65
x: 500,
y: 300
},
updated: 1420903681
}
],
connections: [{
id: 23,
source: 50,
target: 51,
source: 755,
target: 8555,
type: [
'wh_fresh'
],
@@ -368,9 +502,18 @@ define([
config: {
name: 'Exodus 4D',
id: 3,
scope: 'wormhole',
scope: {
id: 1,
name: 'wh',
label: 'w-space'
},
icon: 'fa-sitemap',
type: 'private'
type: {
id: 2,
name: 'private',
label: 'private'
},
updated: 1424545903
},
data: {
systems: [],
@@ -472,8 +615,34 @@ define([
var mapModule = $(this);
var mapDataUpdateActive = true; // allow update "map data"
var userDataUpdateActive = true; // allow update "user data"
// map init load static data ==================================
$.getJSON( Init.path.initMap, function( initData ) {
Init.mapTypes = initData.mapTypes;
Init.mapScopes = initData.mapScopes;
Init.connectionScopes = initData.connectionScopes;
Init.systemStatus = initData.systemStatus;
Init.systemType = initData.systemType;
console.log(Init.systemType);
// init map module
mapModule.initMapModule();
}).fail(function( jqXHR, status, error) {
var reason = status + ' ' + jqXHR.status + ': ' + error;
Util.emergencyShutdown(reason);
});
});
$.fn.initMapModule = function(){
var mapModule = $(this);
var mapUpdateKey = 'mapUpdate';
var mapUpdateDelay = Init.timer[mapUpdateKey].delay;
@@ -506,17 +675,36 @@ define([
// get updated map data
Util.timeStart(mapModuleDatakey);
var newMapData = mapModule.getMapModuleData();
var updatedMapData = mapModule.getMapModuleData();
var mapDataLogDuration = Util.timeStop(mapModuleDatakey);
console.log(newMapData)
// log execution time
Util.log(mapModuleDatakey, {duration: mapDataLogDuration, description: 'getMapModuleData'});
//
setTimeout(function(){
triggerMapUpdatePing(mapData);
}, mapUpdateDelay);
// wrap array to object
updatedMapData = {mapData: updatedMapData};
// store updatedMapData
$.ajax({
type: 'POST',
url: Init.path.updateMapData,
data: updatedMapData,
dataType: 'json'
}).done(function(data){
mapData = data;
// init new trigger
setTimeout(function(){
triggerMapUpdatePing(mapData);
}, mapUpdateDelay);
}).fail(function( jqXHR, status, error) {
var reason = status + ': ' + error;
console.log(error);
Util.showNotify({title: jqXHR.status + ': updatedMapData', text: reason, type: 'warning'});
$(document).setProgramStatus('problem');
});
};
triggerMapUpdatePing(mapData);
@@ -524,29 +712,28 @@ console.log(newMapData)
// ping for user data update -------------------------------------------------------
var triggerUserUpdatePing = function(userData){
// prevent multiple requests simultaneously
if(userDataUpdateActive === true){
$(document).setProgramStatus('online');
$(document).setProgramStatus('online');
userDataUpdateActive = false;
Util.timeStart(mapUserUpdateKey);
mapModule.updateMapModuleData(userData);
var duration = Util.timeStop(mapUserUpdateKey);
Util.timeStart(mapUserUpdateKey);
userDataUpdateActive = mapModule.updateMapModuleData(userData);
var duration = Util.timeStop(mapUserUpdateKey);
// log execution time
Util.log(mapUserUpdateKey, {duration: duration, description:'updateMapModuleData'});
}else{
// not finished in time -> to slow or error
$(document).setProgramStatus('problem');
}
// log execution time
Util.log(mapUserUpdateKey, {duration: duration, description:'updateMapModuleData'});
// init new trigger
setTimeout(function(){
triggerUserUpdatePing(tempUserData);
}, mapUserUpdateDelay);
};
setInterval(triggerUserUpdatePing, mapUserUpdateDelay, tempUserData);
});
// start user update trigger after map loaded
setTimeout(function(){
triggerUserUpdatePing(tempUserData);
}, 2000);
};
});

View File

@@ -5,6 +5,7 @@ define([
'app/render',
'bootbox',
'app/ccp',
'validator',
'jsPlumb',
'customScrollbar',
'dragToSelect',
@@ -19,6 +20,7 @@ define([
// TODO: remove temp ID counter
tempId: 100,
zIndexCounter: 110,
logTimerCount: 3, // map log timer in seconds
newSystemOffset: {
x: 150,
y: 0
@@ -58,7 +60,8 @@ define([
systemContextMenuId: 'pf-map-system-contextmenu',
// dialogs
systemDialogId: 'pf-system-dialog',
systemDialogId: 'pf-system-dialog', // id for system dialog
systemDialogSelectClass: 'pf-system-dialog-select', // id for system select Element
// system security classes
systemSec: 'pf-system-sec',
@@ -470,19 +473,18 @@ define([
$.fn.setSystemStatus = function(status){
var system = $(this);
var statusId = Util.getStatusInfoForSystem(status, 'id');
var statusClass = Util.getStatusInfoForSystem(status, 'class');
var statusLabel = Util.getStatusInfoForSystem(status, 'label');
for(var property in Init.classes.systemStatus) {
if (Init.classes.systemStatus.hasOwnProperty(property)) {
system.removeClass( Init.classes.systemStatus[property].class );
for(var property in Init.systemStatus) {
if (Init.systemStatus.hasOwnProperty(property)) {
system.removeClass( Init.systemStatus[property].class );
}
}
// add new class
system.data('status', statusLabel);
system.data('statusId', statusId);
system.addClass( statusClass );
};
/**
@@ -574,14 +576,20 @@ define([
system.find('.' + config.systemHeadNameClass).attr('data-value', systemName);
// set system status
system.setSystemStatus(data.status);
system.setSystemStatus(data.status.name);
system.data('id', parseInt(data.id));
system.data('systemId', parseInt(data.systemId));
system.data('name', data.name);
system.data('type', data.type);
system.data('typeId', data.type.id);
system.data('effect', data.effect);
system.data('security', data.security);
system.data('trueSec', parseFloat(data.trueSec));
system.data('regionId', parseInt(data.region.id));
system.data('region', data.region.name);
system.data('constellationId', parseInt(data.constellation.id));
system.data('constellation', data.constellation.name);
system.data('updated', data.updated);
system.attr('data-mapid', mapContainer.data('id'));
@@ -644,9 +652,10 @@ define([
// add additional information
mapContainer.data('name', mapConfig.config.name);
mapContainer.data('scope', mapConfig.config.scope);
mapContainer.data('scopeId', mapConfig.config.scope.id);
mapContainer.data('typeId', mapConfig.config.type.id);
mapContainer.data('icon', mapConfig.config.icon);
mapContainer.data('type', mapConfig.config.type);
mapContainer.data('updated', mapConfig.config.updated);
// get map data
var mapData = mapContainer.getMapData();
@@ -1090,7 +1099,7 @@ define([
var mapOverlay = $(this);
var counterChart = mapOverlay.getMapCounter();
var seconds = 10;
var seconds = config.logTimerCount;
var fadeEffectDuration = 200;
// get counter interval (in case there is an active one)
@@ -1214,7 +1223,7 @@ define([
// connection have the default map Scope scope
var scope = map.Defaults.Scope;
if(connectionData.scope){
scope = connectionData.scope;
scope = connectionData.scope.name;
}
var connection = map.connect({
@@ -1222,7 +1231,8 @@ define([
target: config.systemIdPrefix + mapId + '-' + connectionData.target,
scope: scope,
parameters: {
connectionId: connectionId
connectionId: connectionId,
updated: connectionData.updated
}
/* experimental (straight connections)
anchors: [
@@ -1435,7 +1445,7 @@ define([
var initSystemContextMenu = function(){
var systemStatus = [];
$.each(Init.classes.systemStatus, function(status, statusData){
$.each(Init.systemStatus, function(status, statusData){
var tempStatus = {
subIcon: 'fa-circle',
subIconClass: statusData.class,
@@ -2307,7 +2317,7 @@ define([
case 'scope_jumpbridge':
var newScope = action.split('_')[1];
var newScopeName = Util.getScopeInfoForMap( newScope, 'label');
var newScopeName = Util.getScopeInfoForConnection( newScope, 'label');
bootbox.confirm('Change scope from ' + activeScopeName + ' to ' + newScopeName + '?', function(result) {
if(result){
@@ -2392,7 +2402,7 @@ define([
// format system status for form select
var systemStatus = {};
$.each(Init.classes.systemStatus, function(status, statusData){
$.each(Init.systemStatus, function(status, statusData){
//statusData.status = status;
//systemStatus.push(statusData);
systemStatus[status] = statusData.label;
@@ -2401,8 +2411,8 @@ define([
var data = {
id: config.systemDialogId,
system: 'lalala',
status: systemStatus
status: systemStatus,
selectClass: config.systemDialogSelectClass
};
requirejs(['text!templates/modules/system_dialog.html', 'mustache'], function(template, Mustache) {
@@ -2424,72 +2434,79 @@ define([
}
},
success: {
label: 'Add system',
label: '<i class="fa fa-fw fa-sun-o"></i> add system',
className: 'btn-primary',
callback: function () {
callback: function (e) {
// get form Values
var form = $('#' + config.systemDialogId).find('form');
var systemDialogData = $(form).getFormValues();
// validate form
form.validator('validate');
// check weather the form is valid
var formValid = form.isValidForm();
if(formValid === false){
// don't close dialog
return false;
}
// get system information
var requestUrl = Init.path.getSystem + '/' + parseInt( systemDialogData.systemId );
$.getJSON( requestUrl, function( systemData ) {
if(systemData.length === 1){
mapContainer.getMapOverlay().startMapUpdateCounter();
mapContainer.getMapOverlay().startMapUpdateCounter();
// merge dialog data and backend data
var newSystemData = $.extend(systemData, systemDialogData);
systemData = systemData.shift();
var currentX = 0;
var currentY = 0;
// merge dialog data and backend data
var newSystemData = $.extend(systemData, systemDialogData);
var newPositon = {
x: 0,
y: 0
};
var currentX = 0;
var currentY = 0;
var sourceSystem = null;
var newPositon = {
x: 0,
y: 0
// add new position
if(options.sourceSystem !== undefined){
sourceSystem = options.sourceSystem;
// related system is available
currentX = sourceSystem.css('left');
currentY = sourceSystem.css('top');
// remove "px"
currentX = parseInt( currentX.substring(0, currentX.length - 2) );
currentY = parseInt( currentY.substring(0, currentY.length - 2) );
newPositon = {
x: currentX + config.newSystemOffset.x,
y: currentY + config.newSystemOffset.y
};
}else{
// check mouse cursor position (add system to map)
newPositon = {
x: options.position.x,
y: options.position.y
};
var sourceSystem = null;
// add new position
if(options.sourceSystem !== undefined){
sourceSystem = options.sourceSystem;
// related system is available
currentX = sourceSystem.css('left');
currentY = sourceSystem.css('top');
// remove "px"
currentX = parseInt( currentX.substring(0, currentX.length - 2) );
currentY = parseInt( currentY.substring(0, currentY.length - 2) );
newPositon = {
x: currentX + config.newSystemOffset.x,
y: currentY + config.newSystemOffset.y
};
}else{
// check mouse cursor position (add system to map)
newPositon = {
x: options.position.x,
y: options.position.y
};
}
newSystemData.position = newPositon;
// draw new system to map
drawSystem(map, newSystemData, sourceSystem);
}
newSystemData.position = newPositon;
// draw new system to map
drawSystem(map, newSystemData, sourceSystem);
}).fail(function( jqXHR, status, error) {
var reason = status + ': ' + error;
Util.showNotify({title: jqXHR.status + ': System search failed', text: reason, type: 'warning'});
});
}
@@ -2497,12 +2514,14 @@ define([
}
});
// init dialog
systemDialog.on('shown.bs.modal', function(e) {
var modalContent = $('#' + config.systemDialogId);
// init system select live search
var selectElement = $(".js-example-basic-single");
var selectElement = modalContent.find('.' + config.systemDialogSelectClass);
$.when(
selectElement.select2({
ajax: {
@@ -2542,11 +2561,12 @@ define([
}
},
minimumInputLength: 2,
placeholder: 'Name',
placeholder: 'Jita',
allowClear: true
})
).done(function(){
$('#testId').css({'display': 'block'});
// open select
selectElement.select2('open');
});
});
@@ -2561,7 +2581,8 @@ define([
emptytext: 'unknown',
onblur: 'submit',
showbuttons: false,
source: systemStatus
source: systemStatus,
inputclass: config.systemDialogSelectClass
});
});
@@ -2674,11 +2695,16 @@ define([
){
// map config ---------------------------------
var mapConfig = {};
mapConfig.id = mapElement.data('id');
mapConfig.id = parseInt( mapElement.data('id') );
mapConfig.name = mapElement.data('name');
mapConfig.scope = mapElement.data('scope');
mapConfig.scope = {
id: parseInt( mapElement.data('scopeId') )
};
mapConfig.icon = mapElement.data('icon');
mapConfig.type = mapElement.data('type');
mapConfig.type = {
id: parseInt( mapElement.data('typeId') )
};
mapConfig.updated = parseInt( mapElement.data('updated') );
mapData.config = mapConfig;
// map data -----------------------------------
@@ -2691,19 +2717,31 @@ define([
for(var i = 0; i < systems.length; i++){
// systems data ------------------------------------
var tempSystem = $(systems[i]);
var systemData = {};
systemData.id = parseInt( tempSystem.data('id') );
systemData.systemId = parseInt( tempSystem.data('systemId') );
systemData.name = tempSystem.data('name');
systemData.alias = tempSystem.getSystemInfo(['alias']);
systemData.type = tempSystem.data('type');
systemData.status = tempSystem.data('status');
systemData.effect = tempSystem.data('effect');
systemData.type = {
id: tempSystem.data('typeId')
};
systemData.security = tempSystem.data('security');
systemData.trueSec = tempSystem.data('trueSec');
systemData.locked = parseInt( tempSystem.data('locked') );
systemData.rally = parseInt( tempSystem.data('rally') );
systemData.region = {
id: tempSystem.data('regionId'),
name: tempSystem.data('region')
};
systemData.constellation = {
id: tempSystem.data('constellationId'),
name: tempSystem.data('constellation')
};
systemData.status = {
id: tempSystem.data('statusId')
};
systemData.locked = tempSystem.data('locked') ? 1 : 0;
systemData.rally = tempSystem.data('rally') ? 1 : 0;
systemData.currentUser = tempSystem.data('currentUser');
systemData.updated = parseInt( tempSystem.data('updated') );
systemData.userCount = (tempSystem.data('userCount') ? parseInt( tempSystem.data('userCount') ) : 0);
@@ -2739,6 +2777,8 @@ define([
var target = $(tempConnection.target);
var connectionId = tempConnection.getParameter('connectionId');
var updated = tempConnection.getParameter('updated');
var connection = {
id: connectionId,
@@ -2747,7 +2787,8 @@ define([
target: target.data('id'),
targetName: target.data('name'),
scope: tempConnection.scope,
type: tempConnection.getType()
type: tempConnection.getType(),
updated: updated
};
// add to cache
@@ -2772,6 +2813,7 @@ define([
if(typeof activeInstances[mapId] !== 'object'){
// create new instance
jsPlumb.Defaults.LogEnabled = true;
var newJsPlumbInstance = jsPlumb.getInstance({
Container: null, // will be set as soon as container is connected to DOM
PaintStyle:{

View File

@@ -65,12 +65,6 @@ define([
sigTableEditSigTypeSelect: 'pf-sig-table-edit-type-select', // class for editable fields (select)
sigTableEditSigNameSelect: 'pf-sig-table-edit-name-select', // class for editable fields (select)
sigTableCounterClass: 'pf-sig-table-counter', // class for signature table counter
// map scopes
mapScopes: [
{scope: 'wormhole', label: 'W-Space'}
]
};
var cache = {
@@ -498,7 +492,7 @@ define([
var signatureTable = moduleElement.find('.' + config.sigTableMainClass);
var signatureDataTable = $(signatureTable).DataTable();
// TODO save NEW sigantures and get them back with NEW ID :)
// TODO save NEW signatures and get them back with NEW ID :)
var systemData = tempFunctionGetSystemData();
// fake data for new signature table entry
@@ -2007,7 +2001,7 @@ define([
var tabOptions = {
id: parseInt( data.config.id ),
tabClasses: [config.mapTabClass, Util.getInfoForMap( data.config.type, 'classTab') ],
tabClasses: [config.mapTabClass, Util.getInfoForMap( data.config.type.name, 'classTab') ],
contentClasses: [config.mapTabContentClass],
active: false,
icon: data.config.icon,
@@ -2052,7 +2046,7 @@ define([
var tabOptions = {
id: parseInt( data.config.id ),
tabClasses: [config.mapTabClass, Util.getInfoForMap( data.config.type, 'classTab') ],
tabClasses: [config.mapTabClass, Util.getInfoForMap( data.config.type.name, 'classTab') ],
contentClasses: [config.mapTabContentClass],
active: activeTab,
icon: data.config.icon,

View File

@@ -563,13 +563,13 @@ define([
requirejs(['text!templates/modules/map_dialog.html', 'mustache'], function(template, Mustache) {
var data = {
id: config.newMapDialogId,
scope: config.mapScopes,
id: Init.newMapDialogId,
scope: Util.getMapScopes(),
type: Util.getMapTypes(),
icon: Util.getMapIcons(),
formData: formData
};
console.log(data);
var content = Mustache.render(template, data);
var dialogTitle = 'New map';
@@ -946,10 +946,8 @@ define([
}).velocity('fadeIn', {
duration: Init.animationSpeed.headerLink
});
}
programStatusCounter--;
if(programStatusCounter <= 0){
@@ -961,21 +959,10 @@ define([
if(! programStatusInterval){
programStatusCounter = Init.timer.programStatusVisible;
programStatusInterval = setInterval(timer, 1000);
}
}
}
}
};

View File

@@ -204,6 +204,24 @@ define([
return formData;
};
/**
* checks weather a bootstrap form is valid or not
* validation plugin does not provide a proper function for this
* @returns {boolean}
*/
$.fn.isValidForm = function(){
var form = $(this);
var valid = false;
var errorElements = form.find('.has-error');
if(errorElements.length === 0){
valid = true;
}
return valid;
};
/**
* checks if an element is currently visible in viewport
@@ -342,14 +360,12 @@ define([
var mapTypes = [];
$.each(Init.classes.mapTypes, function(prop, data){
$.each(Init.mapTypes, function(prop, data){
// skip "default" type -> just for 'add' icon
if(data.label.length > 0){
var tempData = {
type: prop,
label: data.label
};
var tempData = data;
tempData.name = prop;
mapTypes.push(tempData);
}
@@ -368,8 +384,8 @@ define([
var mapInfo = '';
if(Init.classes.mapTypes.hasOwnProperty(mapType)){
mapInfo = Init.classes.mapTypes[mapType][option];
if(Init.mapTypes.hasOwnProperty(mapType)){
mapInfo = Init.mapTypes[mapType][option];
}
return mapInfo;
@@ -383,7 +399,9 @@ define([
var scopes = [];
$.each(Init.mapScopes, function(prop, data){
scopes.push(prop);
var tempData = data;
tempData.name = prop;
scopes.push(tempData);
});
return scopes;
@@ -406,6 +424,23 @@ define([
return scopeInfo;
};
/**
* get some scope info for a given info string
* @param info
* @param option
* @returns {string}
*/
var getScopeInfoForConnection = function(info, option){
var scopeInfo = '';
if(Init.connectionScopes.hasOwnProperty(info)){
scopeInfo = Init.connectionScopes[info][option];
}
return scopeInfo;
};
/**
* get some system info for a given info string (e.g. rally class)
* @param info
@@ -544,8 +579,8 @@ define([
var statusInfo = '';
if( Init.classes.systemStatus.hasOwnProperty(status) ){
statusInfo = Init.classes.systemStatus[status][option];
if( Init.systemStatus.hasOwnProperty(status) ){
statusInfo = Init.systemStatus[status][option];
}
return statusInfo;
@@ -749,6 +784,7 @@ define([
getInfoForMap: getInfoForMap,
getMapScopes: getMapScopes,
getScopeInfoForMap: getScopeInfoForMap,
getScopeInfoForConnection: getScopeInfoForConnection,
getInfoForSystem: getInfoForSystem,
getEffectInfoForSystem: getEffectInfoForSystem,
getSystemEffectData: getSystemEffectData,

9
js/lib/validator.min.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -19,7 +19,7 @@
<div class="col-sm-4">
<select name="scope" id="scope" class="form-control">
{{#scope}}
<option value="{{scope}}">{{label}}</option>
<option value="{{id}}">{{label}}</option>
{{/scope}}
</select>
</div>
@@ -27,7 +27,7 @@
<div class="col-sm-5">
<select name="type" id="type" class="form-control">
{{#type}}
<option value="{{type}}">{{label}}</option>
<option value="{{id}}">{{label}}</option>
{{/type}}
</select>
</div>

View File

@@ -1,21 +1,57 @@
<div class="row" id="{{id}}">
<div class="col-sm-12">
<form role="form">
<div class="form-group">
<label class="col-sm-2 control-label" for="form_system">System</label>
<div class="col-sm-10">
<div class="input-group">
<select id="form_system" name="systemId" style="width: 200px;" class="js-example-basic-single"/>
<form role="form" class="form-horizontal">
<div class="row">
<div class="col-sm-8">
<div class="form-group">
<label class="col-sm-2 control-label" for="form_system">System</label>
<div class="col-sm-10">
<div class="input-group">
<label for="form_system"></label>
<select id="form_system" name="systemId" class="form-control {{selectClass}}" data-error="Choose a valid system" required/>
<span class="help-block with-errors">Search system name</span>
</div>
</div>
</div>
</div>
<div class="col-sm-4">
<div class="form-group" style="margin-bottom: 0;">
<div class="col-sm-offset-2 col-sm-10">
<div class="checkbox">
<label for="form_lock">
<input id="form_lock" name="locked" value="1" type="checkbox">
Lock System
</label>
<div class="help-block with-errors"></div>
</div>
<div class="checkbox">
<label for="form_rally">
<input id="form_rally" name="rally" value="1" type="checkbox">
Rally pooint
</label>
<div class="help-block with-errors"></div>
</div>
</div>
</div>
<span class="help-block">Enter system name</span>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label" for="form_status">Status</label>
<div class="col-sm-10">
<a class="pf-editable pf-editable-system-status" href="#" id="form_status" data-type="select" data-name="status"></a>
<div class="row">
<div class="col-sm-8">
<div class="form-group">
<label class="col-sm-2 control-label" for="form_status">Status</label>
<div class="col-sm-6" style="min-height: 32px;">
<a class="pf-editable pf-editable-system-status" href="#" id="form_status" data-type="select" data-name="status"></a>
</div>
</div>
</div>
</div>
</form>
</div>
</div>

View File

@@ -291,7 +291,7 @@ input[type="checkbox"],
@include form-control-validation($state-warning-text, $state-warning-text, $state-warning-bg);
}
.has-error {
@include form-control-validation($state-danger-text, $state-danger-text, $state-danger-bg);
@include form-control-validation($red, $red, $state-danger-bg);
}
@@ -314,7 +314,7 @@ input[type="checkbox"],
display: block; // account for any element using help-block
margin-top: 5px;
margin-bottom: 10px;
color: lighten($text-color, 25%); // lighten the text some for contrast
color: lighten($text-color, 10%); // lighten the text some for contrast
}

View File

@@ -880,11 +880,19 @@
border-color: $border-color;
@include box-shadow(inset 0 1px 1px rgba(0,0,0,.075)); // Redeclare so transitions work
&:focus {
border-color: darken($border-color, 10%);
// border-color: darken($border-color, 5%);
$shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 6px lighten($border-color, 20%);
@include box-shadow($shadow);
}
}
// select2 style
.select2-selection{
border: 1px solid !important;
border-color: $border-color !important;
@include box-shadow(inset 0 1px 1px rgba(0,0,0,.075)); // Redeclare so transitions work
}
// Set validation states also for addons
.input-group-addon {
color: $text-color;

View File

@@ -2,7 +2,7 @@
.modal-content {
// data tables ===================================================
// data tables ==========================================
.dataTable, .table{
font-size: 10px;
font-family: $font-family-bold;
@@ -16,6 +16,12 @@
overflow: auto;
}
// ad system dialog =======================================
.pf-system-dialog-select{
width: 270px !important;
}
// map info dialog ========================================
.pf-dialog-dynamic-area{
padding: 10px;

View File

@@ -118,6 +118,8 @@ $mapWidth: 2500px;
background-color: $gray-dark;
font-family: $font-family-bold;
z-index: 100;
will-change: all, transform;
//opacity: 0; // trigger by js
border: {
width: 2px;

View File

@@ -6,6 +6,11 @@
margin-bottom: 0; /* overwrites bootstrap margin */
}
// quick fix
.editableform .form-group{
margin-left: 0 !important;
}
.editableform .control-group {
margin-bottom: 0; /* overwrites bootstrap margin */
white-space: nowrap; /* prevent wrapping buttons on new line */

View File

@@ -79,7 +79,7 @@ input[type="color"]:focus,
//-webkit-box-shadow: none !important;
//-moz-box-shadow: none !important;
//box-shadow: none !important;
box-shadow: inset -1px 1px 6px 0 rgba(0, 0, 0, 0.8) !important;
box-shadow: inset -1px 1px 5px 0 rgba(0, 0, 0, 0.8) !important;
}
.input-sm,