substates for the statesystem, many optimizations

This commit is contained in:
Ulf Gebhardt 2015-04-10 00:17:14 +02:00
parent 8ff0efe6ba
commit b3414fc398
11 changed files with 207 additions and 83 deletions

View File

@ -7,11 +7,11 @@ class SYS_PAGE_GROUP extends \SYSTEM\DB\QP {
//pg
'SELECT * FROM '.\SYSTEM\DBD\system_page::NAME_PG
.' WHERE "'.\SYSTEM\DBD\system_page::FIELD_GROUP.'" = $1'
.' AND "'.\SYSTEM\DBD\system_page::FIELD_ID.'" = $2'
.' ORDER BY "'.\SYSTEM\DBD\system_page::FIELD_ID.'"',
.' AND "'.\SYSTEM\DBD\system_page::FIELD_STATE.'" = $2'
.' ORDER BY "'.\SYSTEM\DBD\system_page::FIELD_ID.'" ASC;',
//mys
'SELECT * FROM '.\SYSTEM\DBD\system_page::NAME_MYS
.' WHERE `'.\SYSTEM\DBD\system_page::FIELD_GROUP.'` = ?'
.' AND `'.\SYSTEM\DBD\system_page::FIELD_ID.'` = ?'
.' ORDER BY '.\SYSTEM\DBD\system_page::FIELD_ID
.' AND `'.\SYSTEM\DBD\system_page::FIELD_STATE.'` = ?'
.' ORDER BY '.\SYSTEM\DBD\system_page::FIELD_ID.' ASC;'
);}}

View File

@ -1,11 +1,16 @@
CREATE TABLE `system_page` (
`id` INT(10) UNSIGNED NOT NULL,
`name` CHAR(50) NOT NULL COLLATE 'utf8_unicode_ci',
`group` INT(10) UNSIGNED NOT NULL,
`id` CHAR(50) NOT NULL,
`div` CHAR(50) NOT NULL,
`url` TEXT NOT NULL,
`func` CHAR(50) NOT NULL,
`php_class` CHAR(50) NOT NULL,
PRIMARY KEY (`id`, `div`, `group`)
`state` CHAR(50) NOT NULL COLLATE 'utf8_unicode_ci',
`parent_id` INT(10) UNSIGNED NOT NULL,
`type` INT(10) UNSIGNED NOT NULL DEFAULT '0',
`div` CHAR(50) NOT NULL COLLATE 'utf8_unicode_ci',
`url` TEXT NOT NULL COLLATE 'utf8_unicode_ci',
`func` CHAR(50) NOT NULL COLLATE 'utf8_unicode_ci',
`php_class` CHAR(50) NOT NULL COLLATE 'utf8_unicode_ci',
PRIMARY KEY (`id`, `group`, `state`, `name`)
)
COLLATE='utf8_unicode_ci'
ENGINE=InnoDB;
ENGINE=InnoDB
;

View File

@ -5,8 +5,12 @@ class system_page {
const NAME_PG = 'system.page';
const NAME_MYS = 'system_page';
const FIELD_GROUP = 'group';
const FIELD_ID = 'id';
const FIELD_NAME = 'name';
const FIELD_GROUP = 'group';
const FIELD_STATE = 'state';
const FIELD_PARENT_ID = 'parent_id';
const FIELD_TYPE = 'type';
const FIELD_DIV = 'div';
const FIELD_URL = 'url';
const FIELD_FUNC = 'func';

View File

@ -28,6 +28,9 @@ class files {
}else{
\SYSTEM\HEADER::FILE($id);}
//Allow Caching for all files
header('Cache-Control: public;');
if(!self::file_get_contents_chunked(self::$folders[$cat].$id,4096,function($chunk,&$handle,$iteration){echo $chunk;})){
throw new \SYSTEM\LOG\ERROR("Could not transfere File.");}
return;

View File

@ -10,13 +10,13 @@ function SYSTEM(endpoint,group,start_state,hash_change){
this.endpoint = endpoint;
this.group = group;
this.pages = null;
this.start_state = start_state;
this.hash_change = hash_change;
this.state = {};
this.state_info = {};
this.state_js = {};
this.state_css = {};
this.start_state = start_state;
this.hash_change = hash_change;
this.hashchange();
$(window).bind('hashchange', this.hashchange);
@ -25,70 +25,66 @@ SYSTEM.prototype.hashchange = function () {
system.go_state(system.start_state);
//user callback
if(system.hash_change){
system.hash_change(system.cur_state());}
system.hash_change(system.cur_state().split('(')[0]);}
};
SYSTEM.prototype.handle_call_pages_data = function (entry,id,forced,cached) {
SYSTEM.prototype.handle_call_pages_page = function (html,entry,id,forced,cached) {
var url = entry['url']+(window.location.search.substr(1) ? '&'+window.location.search.substr(1) : '' );
//check loaded state of div - reload only if required
if(forced || this.state[entry['div']] !== url){
//load pages
$.ajax({
async: false,
data: {},
dataType: 'html',
url: url,
success: function(data){
if($(entry['div']).length){
$(entry['div']).html(data);
system.log_info('load page: '+id+entry['div']+' '+url+' - success');
} else {
system.log_error('load page: '+id+entry['div']+' '+url+' - div not found');
}},
error: function(XMLHttpRequest, textStatus, errorThrown){system.log_error(errorThrown);}
});
//load css
for(var i=0; i < entry['css'].length; i++){
this.load_css(entry['css'][i],forced);}
//load js
var call_func = true;
var loaded = 0;
for(var i=0; i < entry['js'].length; i++){
if(forced || !this.state_js[entry['js'][i]]){
this.log_info('load js: '+entry['js'][i]+(forced ? ' - forced' : ''));
$.getScript(entry['js'][i])
.done(function(response, status, jqxhr) {
system.log_info('load js: '+status);
if(loaded++ === entry['js'].length-1){
var fn = window[entry['func']];
if(call_func && typeof fn === 'function'){
call_func = false;
fn();
system.log_info('call func: '+entry['func']);
} else {
system.log_error('call func: '+entry['func']+' - fail');
}
if($(entry['div']).length){
$(entry['div']).html(html);
this.log_info('load page: '+id+entry['div']+' '+url+' - success');
} else {
this.log_error('load page: '+id+entry['div']+' '+url+' - div not found');
}
//load css
for(var i=0; i < entry['css'].length; i++){
this.load_css(entry['css'][i],forced);}
//load js
var call_func = true;
var loaded = 0;
for(var i=0; i < entry['js'].length; i++){
if(forced || !this.state_js[entry['js'][i]]){
this.log_info('load js: '+entry['js'][i]+(forced ? ' - forced' : ''));
$.getScript(entry['js'][i])
.done(function(response, status, jqxhr) {
system.log_info('load js: '+status);
if(loaded++ === entry['js'].length-1){
var fn = window[entry['func']];
if(call_func && typeof fn === 'function'){
call_func = false;
fn();
system.log_info('call func: '+entry['func']);
} else {
system.log_error('call func: '+entry['func']+' - fail');
}
})
.fail(function( jqxhr, settings, exception ) {
system.log_error( "Something went wrong"+exception );
});
this.state_js[entry['js'][i]] = true;
} else {
this.log_info('load js: '+entry['js'][i]+' - cached');
if(loaded++ === entry['js'].length-1){
var fn = window[entry['func']];
if(call_func && typeof fn === 'function'){
call_func = false;
fn();
this.log_info('call func: '+entry['func']);
} else {
this.log_error('call func: '+entry['func']+' - fail');
}
})
.fail(function( jqxhr, settings, exception ) {
system.log_error( "Something went wrong"+exception );
});
this.state_js[entry['js'][i]] = true;
} else {
this.log_info('load js: '+entry['js'][i]+' - cached');
if(loaded++ === entry['js'].length-1){
var fn = window[entry['func']];
if(call_func && typeof fn === 'function'){
call_func = false;
fn();
this.log_info('call func: '+entry['func']);
} else {
this.log_error('call func: '+entry['func']+' - fail');
}
}
}
//update state
this.state[entry['div']] = url;
}
//update state
this.state[entry['div']] = url;
}
SYSTEM.prototype.handle_call_pages_entry = function (entry,id,forced,cached) {
var url = entry['url']+(window.location.search.substr(1) ? '&'+window.location.search.substr(1) : '' );
//check loaded state of div - reload only if required
if(forced || this.state[entry['div']] !== url){
//load page
this.call_url(url,function(data){system.handle_call_pages_page(data,entry,id,forced,cached);},{},'html',true);
} else {
this.log_info('load page: '+id+entry['div']+' '+url+' - skipped - already loaded');
}
@ -96,6 +92,10 @@ SYSTEM.prototype.handle_call_pages_data = function (entry,id,forced,cached) {
//internal function to handle pagestate results
SYSTEM.prototype.handle_call_pages = function (data,id,forced,cached) {
if(data['status']){
//clear old state
this.state = {}
this.state_info = {}
this.log_info('load pages: endpoint '+this.endpoint+' group:'+this.group+' state:'+id+' - '+(cached ? 'cached ' : (forced ? 'forced' : 'success')));
//state not found?
if(data['result'].length === 0){
@ -108,18 +108,21 @@ SYSTEM.prototype.handle_call_pages = function (data,id,forced,cached) {
if(id !== this.cur_state()){
window.history.pushState(null, "", '#!'+id);}
data['result'].forEach(
function(entry) { system.handle_call_pages_data(entry,id,forced,cached);});
function(entry) { system.handle_call_pages_entry(entry,id,forced,cached);});
} else {
this.log_info('Problem with your Pages: '+data['result']['message']);
}
};
//send a call to the endpoint
SYSTEM.prototype.call = function(call,success,data,data_type,async){
this.call_url(this.endpoint+'?'+call,success,data,data_type,async);
};
SYSTEM.prototype.call_url = function(url,success,data,data_type,async){
$.ajax({
async: async,
data: data,
dataType: data_type,
url: this.endpoint+'?'+call,
url: url,
success: success,
error: function(XMLHttpRequest, textStatus, errorThrown){system.log_error(call+' '+XMLHttpRequest+' '+textStatus+' '+errorThrown);}
});

View File

@ -2,14 +2,23 @@
namespace SYSTEM\PAGE;
class State {
public static function get($group,$state,$returnasjson=true){
$state = \explode(';', $state);
//seperate state from vars
$state_vars = \explode(';', $state);
//parse substates
$state_all = \explode('(', $state_vars[0]);
$state_name = $state_all[0];
$substate = substr($state_vars[0], strlen($state_name));
$substate = self::parse_substate($substate);
//vars
$vars = array();
for($i=1;$i<count($state);$i++){
$var = \explode('.',$state[$i]);
$vars[$var[0]] = $var[1];}
$result = array();
$res = \SYSTEM\DBD\SYS_PAGE_GROUP::QQ(array($group,$state[0]));
$res = \SYSTEM\DBD\SYS_PAGE_GROUP::QQ(array($group,$state_name));
while($row = $res->next()){
if(!self::is_loaded($row,$substate,$state_name) && $row['type'] == 1){
continue;}
$row['url'] = \SYSTEM\PAGE\replace::replace($row['url'], $vars);
$row['url'] = \SYSTEM\PAGE\replace::clean($row['url']);
//clean url of empty variables
@ -23,5 +32,104 @@ class State {
$row['php_class'] = '';
$result[] = $row;
}
return $returnasjson ? \SYSTEM\LOG\JsonResult::toString($result) : $result;}
return $returnasjson ? \SYSTEM\LOG\JsonResult::toString($result) : $result;
}
public static function parse_substate($substate){
return (new ParensParser())->parse($substate);
}
private static function is_loaded($row,&$substate,$state_name,$parent_id = -1){
for($i=0;$i<count($substate);$i++){
if($row['name'] == $state_name){
$substate[$i]['parent_id'] = $row['id'];}
if($substate[$i]['name'] == $row['name'] && $substate[$i]['parent_id'] == $row['parent_id']){
$substate[$i]['parent_id'] = $parent_id;
return true;
}
if(array_key_exists('parent_id', $substate[$i])){
if(self::is_loaded($row,$substate[$i]['sub'],$state_name,$substate[$i]['parent_id'])){
return true;}
}
}
return false;
}
}
class ParensParser
{
// something to keep track of parens nesting
protected $stack = null;
// current level
protected $current = null;
// input string to parse
protected $string = null;
// current character offset in string
protected $position = null;
// start of text-buffer
protected $buffer_start = null;
public function parse($string)
{
if (!$string) {
// no string, no data
return array();
}
if ($string[0] == '(') {
// killer outer parens, as they're unnecessary
$string = substr($string, 1, -1);
}
$this->current = array();
$this->stack = array();
$this->string = $string;
$this->length = strlen($this->string);
// look at each character
for ($this->position=0; $this->position < $this->length; $this->position++) {
switch ($this->string[$this->position]) {
case '(':
$this->push();
// push current scope to the stack an begin a new scope
array_push($this->stack, $this->current);
$this->current = array();
break;
case ')':
$this->push();
// save current scope
$t = $this->current;
// get the last scope from stack
$this->current = array_pop($this->stack);
// add just saved scope to current scope
$this->current[count($this->current)-1]['sub'] = $t;
break;
case '|':
// make each word its own token
$this->push();
break;
default:
// remember the offset to do a string capture later
// could've also done $buffer .= $string[$position]
// but that would just be wasting resources…
if ($this->buffer_start === null) {
$this->buffer_start = $this->position;
}
}
}
$this->push();
return $this->current;
}
protected function push()
{
if ($this->buffer_start !== null) {
// extract string from buffer start to current position
$buffer = substr($this->string, $this->buffer_start, $this->position - $this->buffer_start);
// clean buffer
$this->buffer_start = null;
// throw token into current scope
$this->current[] = array('name' => $buffer);
}
}
}

View File

@ -1 +1 @@
<a href="javascript:system.language('${lang}');"><img src="./api.php?call=files&amp;cat=saistart_sys_sai&amp;id=flag_${lang}.png" alt="${lang}"></a>&nbsp;
<a href="javascript:system.language('${lang}');"><img src="./api.php?call=files&amp;cat=saistart_sys_sai&amp;id=flag_${lang}.png" alt="${lang}" width="16" height="11"></a>&nbsp;

View File

@ -298,7 +298,8 @@ class saimod_sys_log extends \SYSTEM\SAI\SaiModule {
//print_r($r);
$r['class_row'] = self::tablerow_class($r['class']);
$r['time'] = self::time_elapsed_string(strtotime($r['time']));
$r['message'] = substr($r['message'],0,255);
$r['message'] = htmlspecialchars(substr($r['message'],0,255));
$r['request_uri'] = htmlspecialchars($r['request_uri']);
$table .= \SYSTEM\PAGE\replace::replaceFile(\SYSTEM\SERVERPATH(new \SYSTEM\PSAI(),'modules/saimod_sys_log/tpl/saimod_sys_log_table_row.tpl'),$r);
}
$vars = array();

View File

@ -47,6 +47,8 @@ class saimod_sys_todo extends \SYSTEM\SAI\SaiModule {
//$row['report_type'] = self::reporttype($row['type']);
$row['state_string'] = self::state($row['count']);
$row['state_btn'] = self::statebtn($row['count']);
$row['message'] = htmlspecialchars($row['message']);
$row['request_uri'] = htmlspecialchars($row['request_uri']);
if($row['type'] == \SYSTEM\DBD\system_todo::FIELD_TYPE_USER){
$row['message'] = str_replace("\r", '<br/>', $row['message']);
$result_user .= \SYSTEM\PAGE\replace::replaceFile(\SYSTEM\SERVERPATH(new \SYSTEM\PSAI(),'modules/saimod_sys_todo/tpl/todo_user_list_element.tpl'), $row);

View File

@ -29,8 +29,6 @@ class saistart_sys_sai extends \SYSTEM\SAI\SaiModule {
$vars = array();
$vars['project_name'] = \SYSTEM\CONFIG\config::get(\SYSTEM\CONFIG\config_ids::SYS_SAI_CONFIG_PROJECT);
$vars['project_url'] = \SYSTEM\CONFIG\config::get(\SYSTEM\CONFIG\config_ids::SYS_CONFIG_PATH_BASEURL);
$vars['todo_entries'] = \SYSTEM\SAI\saimod_sys_todo::sai_mod__SYSTEM_SAI_saimod_sys_todo_action_todolist();
$vars['log_entries'] = \SYSTEM\SAI\saimod_sys_log::sai_mod__SYSTEM_SAI_saimod_sys_log_action_filter();
$vars['analytics'] = \SYSTEM\SAI\saimod_sys_log::analytics();
$user = \SYSTEM\SECURITY\Security::getUser();
$vars['username'] = $user->username;

View File

@ -48,10 +48,10 @@
<h2 class="muted"><a href="#!todo">Todo</a></h2>
<b>Status:</b> ${project_count}/${project_all}<br/>
<b>Progress:</b> ${project}%
${todo_entries}
<div id="todo_entries"></div>
</div>
<div class="well" id="log">
<h2 class="muted"><a href="#!log">Log</a></h2>
<h4 class="muted">100 Latest Log Entries</h4>
${log_entries}
<div id="log_entries"></div>
</div>