From: Alan Date: Thu, 18 Apr 2024 04:19:57 +0000 (+0800) Subject: fix #8131 - chinese translations X-Git-Url: http://git.roojs.org/?p=Pman.Core;a=commitdiff_plain;h=HEAD;hp=856f0078b5f3b900b0fdd758fea09edbbe5e20b7 fix #8131 - chinese translations --- diff --git a/.roobuilder.jcfg b/.roobuilder.jcfg new file mode 100644 index 00000000..b1099f66 --- /dev/null +++ b/.roobuilder.jcfg @@ -0,0 +1,13 @@ +{ + "name" : "Pman.Core", + "xtype" : "Roo", + "fn" : "3ab3f691edabeed20ba58b844cde0cc6", + "runhtml" : "", + "rootURL" : "http://localhost/web.Texon/", + "base_template" : "roo.builder.html", + "html_gen" : "", + "DBTYPE" : "", + "DBNAME" : "", + "DBUSERNAME" : "", + "DBPASSWORD" : "" +} \ No newline at end of file diff --git a/Asset.php b/Asset.php index eb5c0538..3e49f8eb 100644 --- a/Asset.php +++ b/Asset.php @@ -26,6 +26,7 @@ class Pman_Core_Asset extends Pman { var $types = array( 'css' => 'text/css', 'js' => 'text/javascript', + 'map' => 'application/json' ); function getAuth() @@ -44,17 +45,23 @@ class Pman_Core_Asset extends Pman { $this->jerr("invalid url"); } + $ext = $bits[0]; + if (preg_match('/\.map$/',$_SERVER['REQUEST_URI'])) { + $ext = 'map'; + } + $s = str_replace('/', '-', $bits[1]); + $ui = posix_getpwuid(posix_geteuid()); $ff = HTML_FlexyFramework::get(); $compile = self::getCompileDir($bits[0], '', false); - $fn = $compile . '/'. $s .'.'. $bits[0]; + $fn = $compile . '/'. $s .'.'. $ext; if (!file_exists($fn)) { - header('Content-Type: '. $this->types[$bits[0]]); + header('Content-Type: '. $this->types[$ext]); echo "// compiled file not found = $fn"; exit; @@ -105,7 +112,7 @@ class Pman_Core_Asset extends Pman { $fh = fopen($fn,'r'); fpassthru($fh); fclose($fh); - $content = $data; + } @@ -151,6 +158,7 @@ class Pman_Core_Asset extends Pman { return false; } + if (file_exists($compile_dir)) { return $compile_dir; } diff --git a/AssetTrait.php b/AssetTrait.php index 6e8627ea..0067d6f2 100644 --- a/AssetTrait.php +++ b/AssetTrait.php @@ -78,8 +78,8 @@ trait Pman_Core_AssetTrait { $ff = HTML_FlexyFramework::get(); - if (!empty($ff->Pman['isDev']) && !empty($_REQUEST['isDev'])) { - echo "\n"; + if (empty($compiledir) || (!empty($ff->Pman['isDev']) && !empty($_REQUEST['isDev']))) { + echo "\n"; $this->assetArrayToHtml($files,'js'); return; } @@ -260,7 +260,7 @@ trait Pman_Core_AssetTrait { file_put_contents($compiledir.'/'.$output , $x->minify( $this->baseURL.$asset)); clearstatcache(); if (!file_exists($compiledir.'/'.$output) || - !filesize($compiledir.'/'.$routput)) { + !filesize($compiledir.'/'.$output)) { echo "\n"; $this->assetArrayToHtml($files,'css'); return; @@ -283,18 +283,45 @@ trait Pman_Core_AssetTrait { function outputSCSS($smod) { - // we cant output non-cached versions of this.... + // we cant output non-cached versions of this.... + + // this doesnt really look like it would work! + $this->outputSCSSDir("{$this->rootDir}/Pman/{$smod}/scss/{$smod}.scss", $smod); + + } + /* + * Pman projects - expect + * /Pman/MyProject/scss/MyProject.less << + * this should contain includes for the others? + * @import "fonts.less"; + * .... + * Then all the files go here. + * /Pman/MyProject/scss/*.less + * + * For a Non Pman project + * send: + * /MyProject/scss/base.less << could be anything really... + * + * + */ + + + function outputSCSSDir($file, $smod= '') + { + + $ff = HTML_FlexyFramework::get(); - $fp = "{$this->rootDir}/Pman/$smod/scss/{$smod}.scss"; - // var_dump($fp); - if (!file_exists($fp)) { + $asset = $ff->project == 'Pman' ? '/Core/Asset/css/' : '/Asset/css/'; + + + if (!file_exists($file)) { return; } - $ar = glob(dirname($fp) . '/*.scss'); - $maxtime = 0; + $ar = glob(dirname($file). '/*.scss'); + $maxtime = filemtime($file); foreach($ar as $fn) { - $maxtime=max($maxtime, filemtime($fn)); + $maxtime= max($maxtime, filemtime($fn)); } @@ -309,17 +336,14 @@ trait Pman_Core_AssetTrait { mkdir($compiledir,0700,true); } - - - $output = date('Y-m-d-H-i-s-', $maxtime). $smod .'-'.md5(serialize(array($this->baseURL, $ar))) .'.css'; + $output = date('Y-m-d-H-i-s-', $maxtime). $smod .'-less-'.md5(serialize(array($this->baseURL, $ar))) .'.css'; - $asset = $ff->project == 'Pman' ? '/Core/Asset/css/' : '/Asset/css/'; + // where are we going to write all of this.. // This has to be done via a - - + if ( !file_exists($compiledir.'/'.$output) || !filesize($compiledir.'/'.$output)) { @@ -334,26 +358,26 @@ trait Pman_Core_AssetTrait { die("INSTALL sassc"); } - $fd = dirname($fp); - + $ver = `$sassc --version`; - $bits = explode("\n", $ver); + $bits = explode("\n", trim($ver)); foreach($bits as $b) { + $lr = explode(":", $b); $vers[trim($lr[0])] = trim($lr[1]); } $sm = $vers['sass'] > 3.4 ? ' --sourcemap=auto ' : '--sourcemap'; - $cmd = "{$sassc} --style=compressed {$sm} -I {$fd} -I {$this->rootDir}/roojs1/scss/bootstrap $smod.scss {$compiledir}/{$output}"; + $cmd = "{$sassc} --style=compressed {$sm} -I ". dirname($file) . " -I {$this->rootDir}/roojs1/scss/bootstrap ". basename($file) . " {$compiledir}/{$output}"; //echo "$cmd\n"; echo `$cmd`; `$cmd`; clearstatcache(); if (!file_exists($compiledir.'/'.$output) || - !filesize($compiledir.'/'.$routput)) { + !filesize($compiledir.'/'.$output)) { echo "\n"; - echo "\n"; + echo "\n"; return; } @@ -371,4 +395,8 @@ trait Pman_Core_AssetTrait { } + + + + } \ No newline at end of file diff --git a/Config.php b/Config.php new file mode 100644 index 00000000..d6dc5e61 --- /dev/null +++ b/Config.php @@ -0,0 +1,117 @@ +overlayDefaults($cfg); + + if (!empty($this->memory_limit)) { + $mem = ini_get('memory_limit'); + if (php_sapi_name() != 'cli' && $this->to_bytes($mem) < $this->to_bytes($this->memory_limit)) { + trigger_error("increase the memory limit settings to 2048M or more", E_USER_ERROR); + } + + } + + $this->verifyExtensions(); + + + if (!isset($cfg['Pman']['timezone'])) { + trigger_error("timezone needs setting in Pman[timezone]", E_USER_ERROR); + } + if ($cfg['Pman']['timezone'] != ini_get('date.timezone')) { + trigger_error("timezone needs setting in php.ini date.timezone = " . $cfg['Pman']['timezone'], E_USER_ERROR); + } + + + return $cfg; + } + + function to_bytes($val) { + $val = trim($val); + $last = strtolower($val[strlen($val)-1]); + switch($last) { + // The 'G' modifier is available since PHP 5.1.0 + case 'g': + $val = substr($val, 0, -1); + $val *= 1024; + case 'm': + $val = substr($val, 0, -1); + $val *= 1024; + $val = substr($val, 0, -1); + case 'k': + $val *= 1024; + } + + return $val; + } + function overlayDefaults($cfg) + { + if (isset($this->defaults['disable']) && is_array($this->defaults['disable']) ) { + $this->defaults['disable'] = implode(',', $this->defaults['disable']); + } + foreach($this->defaults as $k=>$v) { + if (is_array($v)) { + + if (!isset($cfg[$k])) { + $cfg[$k] = $v; + continue; + } + + foreach($v as $kk=>$vv) { + if (isset($cfg[$k][$kk])) { + continue; + } + + $cfg[$k][$kk] = $vv; + } + } + + if (!isset($cfg[$k])) { + $cfg[$k] = $v; + } + } + + return $cfg; + } + function verifyExtensions() + { + $error = array(); + + foreach ($this->required_extensions as $e){ + + if(empty($e) || extension_loaded($e)) { + continue; + } + + $error[] = "Error: Please install php extension: {$e}"; + } + + if(empty($error)){ + return true; + } + trigger_error("Missing Extensions: \n" . implode('\n', $error), E_USER_ERROR); + } + +} diff --git a/DataObjects/Core_company.php b/DataObjects/Core_company.php index ac634497..4ae8fb3e 100644 --- a/DataObjects/Core_company.php +++ b/DataObjects/Core_company.php @@ -35,10 +35,17 @@ class Pman_Core_DataObjects_Core_Company extends DB_DataObject public $country; // string(4) not_null public $is_system; // int(2) + + public $comptype_id; + public $address1; + public $address2; + public $address3; + + /* the code above is auto generated do not remove the tag below */ ###END_AUTOCODE - function applyFilters($q, $au) + function applyFilters($q, $au, $roo) { $tn = $this->tableName(); @@ -79,7 +86,7 @@ class Pman_Core_DataObjects_Core_Company extends DB_DataObject } if (!empty($q['query']['comptype'])) { - $this->whereAddIn('comptype', explode(',', $q['query']['comptype']), 'string'); + $this->whereAddIn($tn.'.comptype', explode(',', $q['query']['comptype']), 'string'); } @@ -280,7 +287,7 @@ class Pman_Core_DataObjects_Core_Company extends DB_DataObject $roo->jok('OK'); } - $roo->jerr('EXIST'); + $roo->jerror('NOTICE-EXIST-CHECK','EXIST'); } } @@ -320,7 +327,7 @@ class Pman_Core_DataObjects_Core_Company extends DB_DataObject $roo->jok('OK'); } - $roo->jerr('EXIST'); + $roo->jerror('NOTICE-EXIST-CHECK','EXIST'); } if(!empty($q['_merge_id'])){ @@ -497,12 +504,38 @@ class Pman_Core_DataObjects_Core_Company extends DB_DataObject $companies->insert(); $companies->onInsert(array(), $roo); } + + function owner() + { + if (empty($this->owner_id)) { + return false; + } + static $cache = false; + if ($cache !== false && isset($cache[$this->owner_id])) { + return $cache[$this->owner_id]; + } + $o = DB_DataObject::factory('core_company'); + if (!$o->get($this->owner_id)) { + return false; + } + $cache[$this->owner_id] = $o; + return $o; + + } + + /// look up the company which is the system owner... static function lookupOwner() { + static $cache = false; + if ($cache !== false) { + return clone($cache); // no updating this object.. + } + $enum = DB_DataObject::Factory('core_enum')->lookup('COMPTYPE', 'OWNER' ); $companies = DB_DataObject::factory('core_company'); $companies->comptype_id = $enum; if ($companies->find(true)) { + $cache = clone($companies); return $companies; } return false; diff --git a/DataObjects/Core_domain.php b/DataObjects/Core_domain.php index 89b4587d..3c50da42 100644 --- a/DataObjects/Core_domain.php +++ b/DataObjects/Core_domain.php @@ -15,5 +15,22 @@ class Pman_Core_DataObjects_Core_domain extends DB_DataObject /* the code above is auto generated do not remove the tag below */ ###END_AUTOCODE - + function loadOrCreate($dom) + { + // should we validate domain? + static $cache = array(); + if (isset($cache[$dom])) { + return $cache[$dom]; + } + + $cd = DB_DataObject::Factory($this->tableName()); + if ($cd->get('domain', $dom)) { + $cache[$dom] = $cd; + return $cd; + } + $cd->domain = $dom; + $cd->insert(); + $cache[$dom] = $cd; + return $cd; + } } diff --git a/DataObjects/Core_email.php b/DataObjects/Core_email.php index cbaba212..02ce3427 100644 --- a/DataObjects/Core_email.php +++ b/DataObjects/Core_email.php @@ -23,9 +23,7 @@ class Pman_Core_DataObjects_Core_email extends DB_DataObject public $active; public $bcc_group_id; public $test_class; - - - + /* the code above is auto generated do not remove the tag below */ ###END_AUTOCODE @@ -61,6 +59,7 @@ class Pman_Core_DataObjects_Core_email extends DB_DataObject bcc_group_id = {$cgm}.group_id ) AS bcc_group_member_count "); + if (!empty($_REQUEST['_hide_system_emails'])) { $this->whereAddIn("!{$this->tableName()}.name", array('EVENT_ERRORS_REPORT'), 'string'); @@ -75,7 +74,7 @@ class Pman_Core_DataObjects_Core_email extends DB_DataObject $i->ontable = $this->tableName(); $i->find(); while ($i->fetch()){ - $i->beforeDelete(); + $i->beforeDelete(array(), $roo); $i->delete(); } } @@ -295,7 +294,7 @@ class Pman_Core_DataObjects_Core_email extends DB_DataObject $html->item(0)->appendChild($element); } - $this->plaintext = str_replace("{unsubscribe_link}", $unsubscribe, $this->plaintext); + $this->plaintext = str_replace("{unsubscribe_link}", $unsubscribe, empty($this->plaintext) ? '' : $this->plaintext); } @@ -381,7 +380,15 @@ class Pman_Core_DataObjects_Core_email extends DB_DataObject } $contents['rcpts'] = $admin; } - + if (empty($contents['rcpts']) && $this->to_group_id > 0) { + $members = $this->to_group()->members(); + $contents['rcpts'] = array(); + foreach($this->to_group()->members() as $m) { + $contents['rcpts'][] = $m->email; + } + //var_dump($contents['rcpts']); + + } //subject replacement if(empty($contents['subject'])){ $contents['subject'] = $this->subject; @@ -709,4 +716,11 @@ Content-Transfer-Encoding: 7bit } + + function to_group() + { + $g = DB_DataObject::Factory('core_group'); + $g->get($this->to_group_id); + return $g; + } } diff --git a/DataObjects/Core_enum.php b/DataObjects/Core_enum.php index f6092018..a2d08ca1 100644 --- a/DataObjects/Core_enum.php +++ b/DataObjects/Core_enum.php @@ -16,7 +16,9 @@ class Pman_Core_DataObjects_Core_enum extends DB_DataObject public $seqid; // int(11) not_null multiple_key public $seqmax; // int(11) not_null multiple_key public $display_name; - + public $is_system_enum; + + /* the code above is auto generated do not remove the tag below */ ###END_AUTOCODE @@ -254,6 +256,12 @@ class Pman_Core_DataObjects_Core_enum extends DB_DataObject function lookupCreate($etype,$name, $display_name=false) { + static $cache = array(); + $ckey = json_encode(array($etype, $name)); + if (isset($cache[$ckey])) { + return $cache[$ckey]; + } + // check $ce = DB_DataObject::Factory('core_enum'); $ce->setFrom(array( @@ -269,11 +277,14 @@ class Pman_Core_DataObjects_Core_enum extends DB_DataObject $ce->etype = $etype; $ce->name = $name; if ($ce->find(true)) { + $cache[$ckey] = $ce->id; return $ce->id; } $ce->active = 1; $ce->display_name = $display_name === false ? $ce->name : $display_name; - return $ce->insert(); + $ret = $ce->insert(); + $cache[$ckey] = $ret; + return $ret; } @@ -379,7 +390,7 @@ class Pman_Core_DataObjects_Core_enum extends DB_DataObject if (empty($t->name) && $t->name != 0) { print_R($data); - die("ERROR: invalid name used for core_enum\n\n"); + die("ERROR: invalid name used for core_enum\n\n" ); } if (!$t->count()) { diff --git a/DataObjects/Core_group.php b/DataObjects/Core_group.php index 613a77d0..54f83f55 100644 --- a/DataObjects/Core_group.php +++ b/DataObjects/Core_group.php @@ -45,6 +45,10 @@ class Pman_Core_DataObjects_Core_group extends DB_DataObject $v = $this->escape($q['query']['name_starts']); $this->whereAdd("{$this->tableName()}.name like '{$v}%'"); } + if (!empty($q['query']['name_contains'])) { + $v = $this->escape($q['query']['name_contains']); + $this->whereAdd("{$this->tableName()}.name like '%{$v}%'"); + } if(!empty($q['_count_member_by_name'])){ @@ -76,17 +80,17 @@ class Pman_Core_DataObjects_Core_group extends DB_DataObject $this->selectAdd(" ( - SELECT COUNT(${cgm}.user_id) + SELECT COUNT({$cgm}.user_id) FROM {$cgm} LEFT JOIN {$cp} ON - ${cp}.id = {$cgm}.user_id + {$cp}.id = {$cgm}.user_id WHERE - ${cgm}.group_id = {$this->tableName()}.id + {$cgm}.group_id = {$this->tableName()}.id AND - ${cp}.active = 1 + {$cp}.active = 1 ) AS group_member_count "); @@ -115,7 +119,8 @@ class Pman_Core_DataObjects_Core_group extends DB_DataObject $g->addMember($uid,$roo); break; case 'sub': - $g->removeMember($uid); + + $g->removeMember($uid, $roo); break; default: $roo->jerr('invalid action'); @@ -124,8 +129,18 @@ class Pman_Core_DataObjects_Core_group extends DB_DataObject $roo->jok('updated'); } + if (isset($q['display_name']) && !isset($q['name']) && !$this->is_system) { + $this->name = $q['display_name']; + } } + + function beforeUpdate($old, $q,$roo) + { + if (isset($q['display_name']) && empty($q['name']) && !$this->is_system) { + $this->name = $q['display_name']; + } + } function beforeDelete() @@ -216,12 +231,16 @@ class Pman_Core_DataObjects_Core_group extends DB_DataObject } } - function removeMember($person) + function removeMember($person, $roo) { $gm = DB_Dataobject::factory('core_group_member'); $gm->group_id = $this->id; $gm->user_id = is_object($person) ? $person->id : $person; - + $au = $roo->getAuthUser(); + if ($gm->group()->name == 'Administrators' && $gm->user_id = $au->id) { + $roo->jerr("You can not remove yourself from the admin group"); + } + if ($gm->find(true)) { $gm->delete(); } diff --git a/DataObjects/Core_group_member.php b/DataObjects/Core_group_member.php index ecbb2db6..a652273f 100755 --- a/DataObjects/Core_group_member.php +++ b/DataObjects/Core_group_member.php @@ -42,6 +42,14 @@ class Pman_Core_DataObjects_Core_group_member extends DB_DataObject } + function group() + { + $grp = DB_DataObject::factory('core_group'); + $grp->get($this->group_id); + return $grp; + + } + /** * Get a list of memberships for a person * @param Pman_Core_DataObjects_Person $person who @@ -88,7 +96,9 @@ class Pman_Core_DataObjects_Core_group_member extends DB_DataObject function checkPerm($lvl, $au) { - return false; + // not sure if this is correct - but we need it on texon + return $au->hasPerm("Core.Staff", $lvl); + } } diff --git a/DataObjects/Core_holiday.php b/DataObjects/Core_holiday.php index b4531339..cd26445d 100644 --- a/DataObjects/Core_holiday.php +++ b/DataObjects/Core_holiday.php @@ -20,9 +20,26 @@ class Pman_Core_DataObjects_Core_holiday extends DB_DataObject static $map = array( - 'hk' => 'http://www.1823.gov.hk/common/ical/gc/en.ics' + 'hk' => 'http://www.1823.gov.hk/common/ical/gc/en.ics', + 'cny' => 'https://raw.githubusercontent.com/andrewlkho/cny.ics/master/cny.ics', // CNY ); + + function applyFilters($q, $au, $roo) + { + if (isset($q['date_from'])) { + $dt = date("Y-m-d",strtotime($q['date_from'])); + $this->whereAdd("holiday_date > '$dt'"); + } + if (isset($q['date_to'])) { + $dt = date("Y-m-d",strtotime($q['date_to'])); + $this->whereAdd("holiday_date < '$dt'"); + } + + + } + + function updateHolidays($country) { @@ -43,7 +60,14 @@ class Pman_Core_DataObjects_Core_holiday extends DB_DataObject - $data = file_get_contents("http://www.1823.gov.hk/common/ical/gc/en.ics"); + $data = file_get_contents("https://www.1823.gov.hk/common/ical/gc/en.ics", false, + stream_context_create(array( + "ssl"=>array( + "verify_peer"=>false, + "verify_peer_name"=>false, + ), + )) + ); $vevents = explode('BEGIN:VEVENT', $data); @@ -65,13 +89,15 @@ class Pman_Core_DataObjects_Core_holiday extends DB_DataObject $fmt = substr($matches[1], 0, 4) . "-" . substr($matches[1], 4, 2) . "-" . substr($matches[1], 6, 2); $end_dt = date('Y-m-d', strtotime($fmt)); } - + if(preg_match('/^SUMMARY[^:]*:(.*)/', $line, $matches)){ + $name = trim($matches[1]); + } } if(empty($start_dt) || empty($end_dt)){ continue; } - + //DB_DataObject::DebugLevel(1); //var_dump($start_dt); var_dump($end_dt); exit; for ($i = strtotime($start_dt); $i < strtotime($end_dt) ; $i += (60 * 60 * 24)) { @@ -81,7 +107,13 @@ class Pman_Core_DataObjects_Core_holiday extends DB_DataObject $d->holiday_date = date('Y-m-d', $i); if (!$d->count()) { $d->insert(); - } + + } else { + $d->find(true); + $dd = clone($d); + $d->name = $name; + $d->update($dd); + } } diff --git a/DataObjects/Core_notify.php b/DataObjects/Core_notify.php index 2cc4a129..2570c860 100644 --- a/DataObjects/Core_notify.php +++ b/DataObjects/Core_notify.php @@ -43,8 +43,8 @@ class Pman_Core_DataObjects_Core_notify extends DB_DataObject public $evtype; // event type (or method to call)fall public $act_start; public $person_table; - - + public $to_email; + /* the code above is auto generated do not remove the tag below */ ###END_AUTOCODE @@ -237,6 +237,11 @@ class Pman_Core_DataObjects_Core_notify extends DB_DataObject function applyFilters($q, $au, $roo) { + + if (!empty($q['search']['contains'])) { + $this->whereAdd("join_event_id_id.remarks LIKE '%".$this->escape($q['search']['contains']) ."%'"); + + } if (isset($q['ontable']) && !in_array($q['ontable'], array('Person', 'Events', 'core_watch'))) { // this will only work on tables not joined to ours. @@ -274,6 +279,7 @@ class Pman_Core_DataObjects_Core_notify extends DB_DataObject break; case 'PENDING'; $this->whereAdd('event_id = 0 OR (event_id > 0 AND act_when > NOW() )'); + $this->whereAdd("sent < '2000-01-01'"); break; case 'OPENED'; @@ -339,5 +345,24 @@ class Pman_Core_DataObjects_Core_notify extends DB_DataObject return true; } + // after called do not rely on content as it includes NOW() + function flagDone($event,$msgid) + { + $ww = clone($this); + if(strtotime($this->act_when) > strtotime("NOW")){ + $this->act_when = $this->sqlValue('NOW()'); + } + $this->sent = empty($this->sent) || strtotime($this->sent) < 1 ? $this->sqlValue('NOW()') :$this->sent; // do not update if sent..... + $this->msgid = $msgid; + $this->event_id = $event->id; + $this->update($ww); + } + + function flagLater($when) + { + $ww = clone($this); + $this->act_when = $when; + $this->update($ww); + } } diff --git a/DataObjects/Core_notify_blacklist.php b/DataObjects/Core_notify_blacklist.php new file mode 100644 index 00000000..305f0afc --- /dev/null +++ b/DataObjects/Core_notify_blacklist.php @@ -0,0 +1,73 @@ +query(" + DELETE FROM {$this->tableName()} where added_dt < NOW() - INTERVAL 1 WEEK + "); + + } + +} \ No newline at end of file diff --git a/DataObjects/Core_notify_sender.php b/DataObjects/Core_notify_sender.php new file mode 100644 index 00000000..881902c9 --- /dev/null +++ b/DataObjects/Core_notify_sender.php @@ -0,0 +1,136 @@ +mailbox . '@' . $from_addr->host; + + $ns = DB_DataObject::Factory($this->tableName()); + $ns->setFrom(array( + 'email' => $from, + 'poolname' => $notify->tableName() + )); + if (!$ns->find(true)) { + $ns->is_active = 1; + $ns->priority = 1; + $ns->insert(); + return false; + } + return $ns; + } + + + function emailToAddr($email) + { + require_once 'Mail/RFC822.php'; + $parser = new Mail_RFC822(); + $addresses = $parser->parseAddressList( $email['headers']['From'], 'localhost', false); + if (is_a($addresses, 'PEAR_Error')) { + return false; + } + return $addresses[0]; + //print_r($email['headers']['From']); print_R($from_addr);exit; + + + } + + + function filterEmail($email, $notify) + { + // + if (empty($email['headers']['From']) || empty($notify)) { + return $email; + } + $bits = explode('@', $notify->to_email); + $to_dom = DB_DataObject::factory('core_domain')->loadOrCreate($bits[1]); + + $from_addr = $this->emailToAddr($email); + + $ns = $this->emailAddrToSender($from_addr, $notify); + if ($ns == false) { + return $email; + } + // is it blacklisted? + $bl = DB_DAtaObject::Factory('core_notify_sender_blacklist'); + $bl->setFrom(array( + 'sender_id'=> $ns->id, + 'domain_id' => $to_dom->id + )); + if (!$bl->count()) { + return $email; + } + // it's blacklisted.. + // try finding alternative. + $bl = DB_DAtaObject::Factory('core_notify_sender_blacklist'); + $bl->domain_id = $to_dom->id; + $bad_ids = $bl->fetchAll('sender_id'); + + $ns = DB_DataObject::Factory($this->tableName()); + $ns->setFrom(array( + 'poolname' => $notify->tableName(), + 'is_active' => 1, + )); + $ns->whereAddIn('!id', $bad_ids, 'int'); + if (!$ns->count()){ + return $email; // no alternative available + } + $ns->limit(1); + $ns->find(true); + $email['headers']['From'] = $from_addr->personal . ' <' . $ns->email .'>'; + + return $email; + + //check blacklist for + + + + } + + function checkSmtpResponse($email, $notify, $errmsg) + { + $bl = DB_DataObject::factory('core_notify_sender_blacklist'); + if (!$bl->messageIsBlacklisted($errmsg)) { + return; // ok. + } + // create a record. + + $bits = explode('@', $notify->to_email); + $to_dom = DB_DataObject::factory('core_domain')->loadOrCreate($bits[1]); + + + + $from_addr = $this->emailToAddr($email); + $ns = $this->emailAddrToSender($from_addr, $notify); + + $bl->setFrom(array( + 'sender_id' => $ns->id, + 'domain_id' => $to_dom->id, + )); + if ($bl->count()) { + return; + } + $bl->error_str = $errmsg; + $bl->added_dt = $bl->sqlValue('NOW()'); + $bl->insert(); + + } + +} \ No newline at end of file diff --git a/DataObjects/Core_notify_sender_blacklist.php b/DataObjects/Core_notify_sender_blacklist.php new file mode 100644 index 00000000..7ada6439 --- /dev/null +++ b/DataObjects/Core_notify_sender_blacklist.php @@ -0,0 +1,43 @@ +query(" + DELETE FROM {$this->tableName()} where added_dt < NOW() - INTERVAL 1 WEEK + "); + + } + +} \ No newline at end of file diff --git a/DataObjects/Core_notify_server.php b/DataObjects/Core_notify_server.php new file mode 100644 index 00000000..33b73d14 --- /dev/null +++ b/DataObjects/Core_notify_server.php @@ -0,0 +1,384 @@ +addQueueSize(); + } + } + + + function addQueueSize() + { + // look for database links for server_id (which should find core_notify + others..) + $cn = get_class(DB_DataObject::factory('core_notify')); + $tables = array(); + foreach($this->databaseLinks() as $tbl => $kv) { + foreach($kv as $k=>$v) { + if ($v != 'core_notify_server:id') { + continue; + } + + $test = DB_DAtaObject::factory($tbl); + if (!is_a($test, $cn)) { + break; + } + $tables[] = $tbl; + } + } + if (empty($tables)) { + die("OOPS - no tables for notify_server references"); + } + $totals = array(); + foreach($tables as $t) { + $totals[] = " + COALESCE((SELECT + count(id) + FROM + $t + WHERE + server_id = core_notify_server.id + AND + sent < '1970-01-01' OR sent IS NULL + and + event_id = 0 + ),0) + "; + } + $this->selectAdd("(" . implode(" + ", $totals) . ") as in_queue "); + + + + + + } + + + // most services should call this first.. + + function getCurrent($notify, $force = false) + { + static $current = false;; + + if ($current !== false) { + return $current; + } + + $ns = DB_DataObject::factory('core_notify_server'); + if (!$ns->count()) { + $ns->id = 0; + return $ns; + } + + + $ns->poolname = $notify->poolname; + $ns->is_active = 1; + $ns->hostname = gethostname(); + $ns->limit(1); + if ($ns->find(true)) { + $current = $ns; + return $ns; + } + if (!$force) { + $notify->jerr("Server not found for this server " . gethostname() . " in core_notify_server" ); + } + // fallback to any server - if we are using force. (this is so helo will work...) + + $ns = DB_DataObject::factory('core_notify_server'); + $ns->is_active = 1; + $ns->hostname = gethostname(); + if (!$ns->find(true)) { + $notify->jerr("Server not found for this server " . gethostname() . " in core_notify_server" ); + } + $current = $ns; + return $ns; + } + + + function isFirstServer() + { + if (!$this->id) { + return true; + } + + $servers = $this->availableServers(); + if (empty($servers)) { + return false; + } + // only run this on the first server... + return $this->id == $servers[0]->id; + } + + + // called on current server. + function assignQueues($notify) + { + if (!$this->id) { + return true; + } + + $servers = $this->availableServers(); + $ids = array(); + $up = array(); + foreach($servers as $s) { + $ids[] = $s->id; + } + + + if (empty($ids)) { + $notify->jerr("no configured servers in core_notify_server for poolname = {$notify->poolname}"); + + } + + // only run this on the first server... + if ($this->id != $ids[0]) { + return; + } + foreach($ids as $rn) { + $up[$rn] = array(); + } + + $num_servers = count($ids); + + if ($num_servers == 1) { + $p = DB_DataObject::factory($notify->table); + $p->query(" + UPDATE + {$notify->table} + SET + server_id = {$ids[0]} + WHERE + sent < '2000-01-01' + and + event_id = 0 + and + act_start < NOW() + INTERVAL 3 HOUR + and + server_id != {$ids[0]} + "); + return; + } + + + + $p = DB_DataObject::factory($notify->table); + $p->whereAdd(" + sent < '2000-01-01' + and + event_id = 0 + and + act_start < NOW() + INTERVAL 3 HOUR + and + server_id NOT IN (" . implode(",", $ids) . ") + "); + $p->orderBy('act_when asc'); //? + $total_add = $p->count(); + if ($total_add < 1) { + return; + } + + $to_add = $p->fetchAll('id'); + + $p = DB_DataObject::factory($notify->table); + $p->whereAdd(" + sent < '2000-01-01' + and + event_id = 0 + + and + server_id IN (" . implode(",", $ids) . ") + "); + $p->selectAdd(); + $p->selectAdd('server_id, count(id) as n'); + $p->groupBy('server_id'); + $in_q = $p->fetchAll('server_id', 'n'); + + // if queue is empty it will not get allocated anything. + foreach($ids as $sid) { + if (!isset($in_q[$sid])) { + $in_q[$sid] = 0; + } + } + $totalq = 0; + foreach($in_q as $sid => $n) { + $totalq += $n; + } + + + // new average queue + $target_len = floor( ($totalq + $total_add) / $num_servers ); + + foreach($in_q as $sid => $cq) { + if ( $cq > $target_len) { + continue; + } + $up[ $sid ] = array_slice($to_add, 0, $target_len - $cq); + } + + // add the reminder evently + foreach($to_add as $n=>$i) { + + $up[ $ids[$n % $num_servers] ][] = $i; + } + + // distribution needs to go to ones that have the shortest queues. - so to balance out the queues + + + + foreach($up as $sid => $nids) { + if (empty($nids)) { + continue; + } + $p = DB_DataObject::factory($notify->table); + $p->query(" + UPDATE + {$notify->table} + SET + server_id = $sid + WHERE + id IN (". implode(',', $nids). ')' + ); + } + + DB_DataObject::factory("core_notify_blacklist")->prune(); + + } + // called on current server. + + function availableServers() + { + $ns = DB_DataObject::factory('core_notify_server'); + $ns->poolname = $this->poolname; + $ns->is_active = 1; + $ns->orderBy('id ASC'); + return $ns->fetchAll(); + + } + + function updateNotifyToNextServer( $cn , $when = false, $allow_same = false) + { + if (!$this->id) { + return; + } + + // fixme - this should take into account blacklisted - and return false if no more servers are available + $email = empty($cn->to_email) ? ($cn->person() ? $cn->person()->email : $cn->to_email) : $cn->to_email; + + $w = DB_DataObject::factory($cn->tableName()); + $w->get($cn->id); + + $servers = $this->availableServers(); + $start = 0; + foreach($servers as $i => $s) { + if ($s->id == $this->id) { + $start = $i; + } + } + + $offset = ($start + 1) % count($servers); + $good = false; + while ($offset != $start) { + $s = $servers[$offset]; + if (!$s->isBlacklisted($email)) { + $good = $s; + break; + } + $offset = ($offset + 1) % count($servers); + var_dump($offset); + } + if ($good == false && $allow_same) { + $good = $this; + } + + if ($good == false) { + return false; + } + + + // next server.. + $pp = clone($w); + $w->server_id = $good->id; + $w->act_when = $when === false ? $w->sqlValue('NOW() + INTERVAL 1 MINUTE') : $when; + $w->update($pp); + return true; + } + + + function isBlacklisted($email) + { + if (!$this->id) { + return false; + } + + // return current server id.. + static $cache = array(); + // get the domain.. + $ea = explode('@',$email); + $dom = strtolower(array_pop($ea)); + if (isset( $cache[$this->id . '-'. $dom])) { + return $cache[$this->id . '-'. $dom]; + } + + $cd = DB_DataObject::factory('core_domain')->loadOrCreate($dom); + + $bl = DB_DataObject::factory('core_notify_blacklist'); + $bl->server_id = $this->id; + $bl->domain_id = $cd->id; + if ($bl->count()) { + $cache[$this->id . '-'. $dom] = true; + return true; + } + + return false; + } + function initHelo() + { + if (!$this->id) { + return; + } + $ff = HTML_FlexyFramework::get(); + $ff->Mail['helo'] = $this->helo; + + } + function checkSmtpResponse($errmsg, $core_domain) + { + if (!$this->id) { + return false; + } + $bl = DB_DataObject::factory('core_notify_blacklist'); + $bl->server_id = $this->id; + $bl->domain_id = $core_domain->id; + if ($bl->count()) { + return true; + } + // is it a blacklist message + if (!$bl->messageIsBlacklisted($errmsg)) { + return false; + } + $bl->error_str = $errmsg; + $bl->added_dt = $bl->sqlValue("NOW()"); + $bl->insert(); + return true; + + } + +} \ No newline at end of file diff --git a/DataObjects/Core_person.php b/DataObjects/Core_person.php index b45eef49..4208198e 100644 --- a/DataObjects/Core_person.php +++ b/DataObjects/Core_person.php @@ -48,6 +48,8 @@ class Pman_Core_DataObjects_Core_person extends DB_DataObject public $phone_direct; // varchar(32) NOT NULL DEFAULT ''; public $countries; // VARCHAR(128) NULL; + public $language; + /* the code above is auto generated do not remove the tag below */ ###END_AUTOCODE @@ -252,11 +254,14 @@ class Pman_Core_DataObjects_Core_person extends DB_DataObject function isAuth() { // do not start a session if we are using http auth... - if (empty($_SERVER['PHP_AUTH_USER']) && php_sapi_name() != "cli") { - @session_start(); - } - + // we have a situation where the app is behind a http access and is also login + // need to work out a way to handle that. $ff= HTML_FlexyFramework::get(); + if (php_sapi_name() != "cli" && (empty($_SERVER['PHP_AUTH_USER']) || !empty($ff->disable_http_auth))) { + @session_start(); + } + + $sesPrefix = $this->sesPrefix(); @@ -278,14 +283,16 @@ class Pman_Core_DataObjects_Core_person extends DB_DataObject } unset($_SESSION[get_class($this)][$sesPrefix .'-auth']); unset($_SESSION[get_class($this)][$sesPrefix .'-timeout']); - setcookie('Pman.timeout', -1, time() + (30*60), '/'); + //setcookie('Pman.timeout', -1, time() + (30*60), '/'); return false; } // http basic auth.. $u = DB_DataObject::factory($this->tableName()); - if (!empty($_SERVER['PHP_AUTH_USER']) + if (empty($ff->disable_http_auth) // http auth requests should not have this... + && + !empty($_SERVER['PHP_AUTH_USER']) && !empty($_SERVER['PHP_AUTH_PW']) && @@ -299,6 +306,10 @@ class Pman_Core_DataObjects_Core_person extends DB_DataObject self::$authUser = $u; return true; } + + // at this point all http auth stuff is done, so we can init session + + //die("test init"); if (!$this->canInitializeSystem()) { // die("can not init"); @@ -334,7 +345,7 @@ class Pman_Core_DataObjects_Core_person extends DB_DataObject if (empty($_SERVER['PATH_INFO']) || $_SERVER['PATH_INFO'] == '/Login') { $auto_auth_allow = false; } - //var_dump($auto_auth_allow); + //var_dump($auto_auth_allow); // local auth - $default_admin = false; if ($auto_auth_allow) { @@ -384,6 +395,9 @@ class Pman_Core_DataObjects_Core_person extends DB_DataObject $u = DB_DataObject::factory($this->tableName()); $u->whereAdd(' LENGTH(passwd) > 0'); $n = $u->count(); + if (empty($_SESSION[get_class($this)]) || !is_array($_SESSION[get_class($this)])) { + $_SESSION[get_class($this)] = array(); + } $_SESSION[get_class($this)][$sesPrefix .'-empty'] = $n; if (class_exists('PEAR')) { $error = PEAR::getStaticProperty('DB_DataObject','lastError'); @@ -420,7 +434,7 @@ class Pman_Core_DataObjects_Core_person extends DB_DataObject if (isset($_SESSION[get_class($this)][$sesPrefix .'-auth'])) { $_SESSION[get_class($this)][$sesPrefix .'-auth-timeout'] = time() + (30*60); // eg. 30 minutes - setcookie('Pman.timeout', time() + (30*60), time() + (30*60), '/'); + //setcookie('Pman.timeout', time() + (30*60), time() + (30*60), '/'); } // not really sure why it's cloned.. return clone (self::$authUser); @@ -500,7 +514,7 @@ class Pman_Core_DataObjects_Core_person extends DB_DataObject $d = $p->toArray(); $_SESSION[get_class($this)][$sesPrefix .'-auth-timeout'] = time() + (30*60); // eg. 30 minutes - setcookie('Pman.timeout', time() + (30*60), time() + (30*60), '/'); + //setcookie('Pman.timeout', time() + (30*60), time() + (30*60), '/'); //var_dump(array(get_class($this),$sesPrefix .'-auth')); $_SESSION[get_class($this)][$sesPrefix .'-auth'] = serialize((object)$d); @@ -520,11 +534,8 @@ class Pman_Core_DataObjects_Core_person extends DB_DataObject $this->isAuth(); // force session start.. $sesPrefix = $this->sesPrefix(); - $_SESSION[get_class($this)][$sesPrefix .'-auth-timeout'] = -1; - $_SESSION[get_class($this)][$sesPrefix .'-auth'] = ""; - self::$authUser = false; } @@ -692,6 +703,9 @@ class Pman_Core_DataObjects_Core_person extends DB_DataObject $uu = clone($this); $this->lang = $val; $this->update($uu); + if(!empty(self::$authUser) && self::$authUser->id == $this->id) { + self::$authUser->lang = $this->lang; + } return $this->lang; } @@ -753,18 +767,28 @@ class Pman_Core_DataObjects_Core_person extends DB_DataObject $oath_require = $s->lookup('core', 'two_factor_auth_required'); $aur['require_oath'] = $oath_require ? $oath_require->val : 0; - $aur['core_person_settings'] = array(); - - $core_person_settings = DB_DataObject::factory('core_person_settings'); - $core_person_settings->setFrom(array( - 'person_id' => $this->id - )); - - $aur['core_person_settings'] = $core_person_settings->fetchAll('scope', 'data'); + $aur['core_person_settings'] = $this->settings(); return $aur; } + function settings($return_obj = false) + { + $cs = DB_DataObject::factory('core_person_settings'); + $cs->setFrom(array( + 'person_id' => $this->id + )); + return $return_obj ? $cs->fetchAll() : $cs->fetchAll('scope', 'data');; + } + function toRooSingleArray($authUser, $request) + { + $ret = $this->toArray(); + foreach( $this->settings() as $k=>$v) { + $ret['core_person_settings['. $k .']'] = $v; + } + + return $ret; + } // ----------PERMS------ ---------------- function getPerms() { @@ -1004,11 +1028,30 @@ class Pman_Core_DataObjects_Core_person extends DB_DataObject )" ); } + if(!empty($q['in_group_starts'])){ + + $v = $this->escape($q['in_group_starts']); + + $this->whereAdd(" + $tn_p.id IN ( + SELECT + DISTINCT(user_id) FROM $tn_gm + LEFT JOIN + $tn_g + ON + $tn_g.id = $tn_gm.group_id + WHERE + $tn_g.name LIKE '{$v}%' + )" + ); + } + + // #2307 Search Country!! if (!empty($q['query']['in_country'])) { // DB_DataObject::debugLevel(1); - $inc = $q['query']['in_country']; + $inc = $this->escape($q['query']['in_country']); $this->whereAdd("$tn_p.countries LIKE '%{$inc}%'"); } @@ -1074,6 +1117,15 @@ class Pman_Core_DataObjects_Core_person extends DB_DataObject {$this->tableName()}.name LIKE '%{$this->escape($q['query']['name'])}%' "); } + + if(!empty($q['query']['name_or_email'])){ + $v = $this->escape($q['query']['name_or_email']); + $this->whereAdd(" + {$this->tableName()}.name LIKE '%{$v}%' + OR + {$this->tableName()}.email LIKE '%{$v}%' + "); + } if(!empty($q['query']['name_starts'])){ $this->whereAdd(" {$this->tableName()}.name LIKE '{$this->escape($q['query']['name_starts'])}%' @@ -1153,7 +1205,7 @@ class Pman_Core_DataObjects_Core_person extends DB_DataObject if (!$roo->hasPerm('Core.Projects_All', 'S')) { $peps = $p->people($pids); - $this->whereAddIn("{$tn}.id", $peps, 'int'); + $this->whereAddIn("{$this->tableName()}.id", $peps, 'int'); } } @@ -1203,7 +1255,7 @@ class Pman_Core_DataObjects_Core_person extends DB_DataObject COALESCE(( SELECT - GROUP_CONCAT( core_group.name separator '\n') + GROUP_CONCAT( CASE WHEN core_group.display_name = '' THEN core_group.name ELSE core_group.display_name END separator '\n') FROM core_group_member LEFT JOIN @@ -1212,6 +1264,8 @@ class Pman_Core_DataObjects_Core_person extends DB_DataObject core_group.id = core_group_member.group_id WHERE core_group_member.user_id = core_person.id + ORDER BY + core_group.display_name ASC ), '') as member_of"); } @@ -1331,10 +1385,18 @@ class Pman_Core_DataObjects_Core_person extends DB_DataObject // determine if it's staff!!! $owncomp = DB_DataObject::Factory('core_company'); $owncomp->get('comptype', 'OWNER'); - $isStaff = ($au->company_id == $owncomp->id); - + $editor_is_staff = ($au->company_id == $owncomp->id); + + if (!$editor_is_staff) { + // non staff editing should not user roo/isPerm? + return false; // no permission if user is not staff!? + + } + + $this_is_staff = ($this->company_id == $owncomp->id); - if (!$isStaff) { + /* + if (!$this_is_staff ) { // - can not change company!!! if ($changes && @@ -1358,7 +1420,7 @@ class Pman_Core_DataObjects_Core_person extends DB_DataObject //return $this->company_id == $au->company_id; } - + */ // yes, only owner company can mess with this... @@ -1369,11 +1431,13 @@ class Pman_Core_DataObjects_Core_person extends DB_DataObject // extra case change passwod? case 'P': //??? password // standard perms -- for editing + if the user is dowing them selves.. - $ret = $isStaff ? $au->hasPerm("Core.Staff", "E") : $au->hasPerm("Core.Person", "E"); - return $ret || $au->id == $this->id; + $ret = $this_is_staff ? $au->hasPerm("Core.Staff", "E") : $au->hasPerm("Core.Person", "E"); + return $ret || $au->id == $this->id; // can change own data? default: - return $isStaff ? $au->hasPerm("Core.Staff", $lvl) : $au->hasPerm("Core.Person", $lvl); + return $this_is_staff ? $au->hasPerm("Core.Staff", $lvl) : $au->hasPerm("Core.Person", $lvl); + + } return false; @@ -1381,8 +1445,20 @@ class Pman_Core_DataObjects_Core_person extends DB_DataObject function beforeInsert($req, $roo) { + if (!empty($req['_bulk_update_passwords'])) { + $this->bulkUpdatePasswords($req['_bulk_update_passwords'], $roo); + return; + } + $p = DB_DataObject::factory('core_person'); if ($roo->authUser->id > -1 || $p->count() > 1) { + $pp = DB_DataObject::factory('core_person'); + $pp->whereAdd('LOWER(email) = "' . $pp->escape(strtolower(trim($this->email))) . '"'); + if ($pp->count()){ + $roo->jerror("NOTICE-DUPE-EMAIL", "that email already exists in the database"); + } + + return; } $c = DB_DataObject::Factory('core_company'); @@ -1395,6 +1471,11 @@ class Pman_Core_DataObjects_Core_person extends DB_DataObject $this->company_id = $c->id; $this->email = trim($this->email); + + + + + } function onInsert($req, $roo) @@ -1428,9 +1509,45 @@ class Pman_Core_DataObjects_Core_person extends DB_DataObject $pd->company_id = $this->company_id; $pd->insert(); } - + if (!empty($req['core_person_settings'])) { + $this->updateSettings($req['core_person_settings'], $roo); + } } + function onUpdate($old, $req,$roo, $event) + { + if (!empty($req['core_person_settings'])) { + $this->updateSettings($req['core_person_settings'], $roo); + } + } + + // there should really be a registry of valid scope values!? + function updateSettings($ar, $roo) + { + //DB_DataObject::debugLevel(1); + $old = array(); + foreach($this->settings(true) as $o) { + $old[$o->scope] = $o; + } + foreach($ar as $k=>$v) { + if (isset($old[$k])) { + $oo = clone($old[$k]); + $old[$k]->data = $v; + $old[$k]->update($oo); + continue; + } + $cs = DB_DataObject::Factory('core_person_settings'); + $cs->setFrom(array( + 'person_id' =>$this->id, + 'scope' => $k, + 'data' => $v + )); + $cs->insert(); + } + // we dont delete old stuff.... + } + + function importFromArray($roo, $persons, $opts) { if (empty($opts['prefix'])) { @@ -1507,9 +1624,7 @@ class Pman_Core_DataObjects_Core_person extends DB_DataObject $ff= HTML_FlexyFramework::get(); $appname = empty($ff->appNameShort) ? $ff->project : $ff->project . '-' . $ff->appNameShort; - $dname = method_exists($this, 'getDatabaseConnection') ? $this->getDatabaseConnection()->dsn['database'] : $this->databaseNickname(); - $sesPrefix = $appname.'-' .get_class($this) .'-' . $dname; return $sesPrefix; @@ -1518,9 +1633,7 @@ class Pman_Core_DataObjects_Core_person extends DB_DataObject function loginPublic() // used where??? { $this->isAuth(); // force session start.. - $db = $this->getDatabaseConnection(); - $ff = HTML_FlexyFramework::get(); if(empty($ff->Pman) || empty($ff->Pman['login_public'])){ @@ -1540,6 +1653,16 @@ class Pman_Core_DataObjects_Core_person extends DB_DataObject function beforeUpdate($old, $q, $roo) { $this->email = trim($this->email); + + $p = DB_DataObject::factory('core_person'); + if ($roo->authUser->id > -1 || $p->count() > 1) { + $pp = DB_DataObject::factory('core_person'); + $pp->whereAdd('LOWER(email) = "' . $pp->escape(strtolower(trim($this->email))) . '"'); + $pp->whereAdd('id != ' . $old->id); + if ($pp->count()){ + $roo->jerror("NOTICE-DUPE-EMAIL", "that email already exists in the database"); + } + } } function generateOathKey() @@ -1606,6 +1729,62 @@ class Pman_Core_DataObjects_Core_person extends DB_DataObject return $content; } - + function bulkUpdatePasswords($data, $roo) + { + + if ( !$roo->hasPerm("Core.Staff", "E")) { + $roo->jerr("permission denied"); + } + $rows = explode("\n",$data); + $upd = array(); + $bad = array(); + + foreach($rows as $i=>$row) { + if (!strlen(trim($row))) { + continue; + } + $bits = preg_split('/\s+/', trim($row)); + if (count($bits) != 2) { + $bad[] = "Invalid line: {$row}"; + continue; + } + // validate. + $upd[strtolower($bits[0])] = $bits[1]; + + } + if (empty($upd)) { + + $roo->jerr(empty($bad) ? "No rows to update": ("ERRORS: ". implode("\n", $bad))); + return; + } + // next fetch them all. + $p = DB_DataObject::factory('core_person'); + $p->whereAddIn('email', array_keys($upd), 'string'); + foreach($p->fetchAll() as $p) { + $map[strtolower($p->email)] = $p; + } + foreach($upd as $k=>$nv) { + if (!isset($map[$k])) { + $bad[] = "Missing account with email: " . $k; + continue; + } + if ($map[$k]->id == $roo->authUser->id) { + $bad[] = "You can not update your own password here: " . $k; + continue; + } + } + if (!empty($bad)) { + $roo->jerr("ERRORS: ". implode("\n", $bad)); + return; + } + foreach($map as $k => $p) { + $pp = clone($p); + $p->setPassword($upd[$k]); + $p->update($pp); + } + $roo->jok("Updated"); + + + } } diff --git a/DataObjects/Core_project.php b/DataObjects/Core_project.php index 3611b98d..5cc1f559 100644 --- a/DataObjects/Core_project.php +++ b/DataObjects/Core_project.php @@ -27,6 +27,8 @@ class Pman_Core_DataObjects_Core_project extends DB_DataObject public $languages; // string(128) not_null public $agency_id; // int(11) not_null public $updated_dt; // datetime(19) not_null binary + public $deleted_by; // INT + public $deleted_dt; // datetime(19) not_null binary /* the code above is auto generated do not remove the tag below */ @@ -89,7 +91,10 @@ class Pman_Core_DataObjects_Core_project extends DB_DataObject function applyFilters($q, $au, $roo) { - + if (empty($q['_show_deleted']) && empty($q['_is_update_request'])) { + $this->whereAdd('core_project.deleted_by = 0'); + } + $tn = $this->tableName(); if (!empty($q['query']['project_search'])) { $s = $this->escape($q['query']['project_search']); @@ -130,7 +135,7 @@ class Pman_Core_DataObjects_Core_project extends DB_DataObject //&& $au->hasPerm('Documents.Documents','S') << this is dependant on the doc modules - if (!$au->hasPerm('Core.Projects_All','S') ) { + if (php_sapi_name() != 'cli' && !$au->hasPerm('Core.Projects_All','S') ) { @@ -174,8 +179,22 @@ class Pman_Core_DataObjects_Core_project extends DB_DataObject } + + function beforeUpdate($old, $q, $roo) + { + if (!empty($q['_flag_deleted'])) { + //DB_DataObject::DebugLevel(1); + $this->deleted_by = $roo->getAuthUser()->id; + $this->deleted_dt = date("Y-m-d H:i:s"); + } + if (!empty($q['_flag_undeleted'])) { + $this->deleted_by = 0; + $this->deleted_dt = '1000-01-01 00:00:00'; + } + } + - function onInsert($request,$roo) + function onInsert($request,$roo,$event) { $oo = clone($this); if (empty($this->code)) { @@ -186,7 +205,7 @@ class Pman_Core_DataObjects_Core_project extends DB_DataObject } } - function onUpdate($old, $request, $roo) + function onUpdate($old, $request, $roo,$event) { $oo = clone($this); if (empty($this->code)) { @@ -321,4 +340,23 @@ class Pman_Core_DataObjects_Core_project extends DB_DataObject return $au->hasPerm("Core.Projects_Member_Of",$lvl) || $au->hasPerm("Core.Projects_All",$lvl); } + static $cache = array(); + function cacheLoad($id) + { + if (isset(self::$cache[$id])) { + return self::$cache[$id]; + } + $n = $this->factorySelf(); + $n->get($id); + $n->cacheSave(); + return $n; + } + + function cacheSave() + { + self::$cache[$this->id] = $this; + } + + + } diff --git a/DataObjects/Core_project_group.php b/DataObjects/Core_project_group.php new file mode 100644 index 00000000..99a52cc9 --- /dev/null +++ b/DataObjects/Core_project_group.php @@ -0,0 +1,15 @@ +jerr("no direct access for setting encrypted data at present?"); + } + // we store column data in here now - so it has to be insertable. + //exit; } function getKeyDirectory() diff --git a/DataObjects/Core_template.php b/DataObjects/Core_template.php index 03f9b97c..e64c9a8c 100644 --- a/DataObjects/Core_template.php +++ b/DataObjects/Core_template.php @@ -58,10 +58,45 @@ class Pman_Core_DataObjects_Core_template extends DB_DataObject } + function beforeUpdate($old, $q, $roo) + { + if (!empty($q['_rescan'])){ + if ($this->filetype != 'html') { + $roo->jerr("can not update a php source file currently - TDOD"); + } + $pg = HTML_FlexyFramework::get()->page; + + $this->syncTemplatePage(array( + 'template_dir' => $pg->rootDir . '/'. str_replace('.', '/', $this->view_name). '/templates', + 'template' => $this->template, + 'base' => $this->view_name, + 'force' => true + )); + // update the different langage versions of this page. + $x = DB_Dataobject::Factory('core_templatestr'); + $x->selectAdd(); + $x->selectAdd('distinct(lang) as lang'); + $x->whereAdd("lang != ''"); + $langs = $x->fetchAll('lang'); + foreach($langs as $l) { + $x = DB_Dataobject::Factory('core_templatestr'); + $x->syncLang($l, $this->id); + } + + + $roo->jok("updated -" . $this->template); + } + } + + + + /* + * USED? - this is in updateBJSTemplate ? * @param base (should be full path to template directory) * @param subdir = empty for top or subpath. */ + /* function syncTemplateDir($base = false, $subdir = '', $force = false) { echo "syncTemplateDir: $base , $subdir, $force \n"; @@ -153,15 +188,17 @@ class Pman_Core_DataObjects_Core_template extends DB_DataObject } } } - - /* compile a html template + */ + /* compile a html template - called by UpdateBjsTemplates - scan Pman Templates * * @param template_dir << the path to the template dir ... Pman/XXX/template ... * @param template << name of template used by name field) * @param base << view name (module ? + templates?) + * @param force << optional - forces even if database is newer. * * */ + function syncTemplatePage($pgdata) { //print_r($pgdata); @@ -172,7 +209,7 @@ class Pman_Core_DataObjects_Core_template extends DB_DataObject ini_set('memory_limit', '512M'); //var_dump($n); - $n= $pgdata['template']; // remove trailing slash.. + $n = $pgdata['template']; // remove trailing slash.. $fopts = HTML_FlexyFramework::get()->HTML_Template_Flexy; $opts = HTML_FlexyFramework::get()->Pman_Core; @@ -207,27 +244,31 @@ class Pman_Core_DataObjects_Core_template extends DB_DataObject $tmpl->view_name = $pgdata['base']; if ($tmpl->get('template', $pgdata['template'])) { - if (strtotime($tmpl->updated) >= filemtime($flexy->resolvePath ($pgdata['template']))) { + clearstatcache(); + if (strtotime($tmpl->updated) >= filemtime($flexy->resolvePath ($pgdata['template']) . '/'. $pgdata['template'])) { if ($tmpl->is_deleted != 0 || $tmpl->filetype != 'html') { $oo = clone($tmpl); $tmpl->is_deleted = 0; $tmpl->filetype = 'html'; $tmpl->update($oo); } - - - - return $tmpl; + if (empty($pgdata['force'])) { + // echo "SKIP NO UPDATE: " . $pgdata['template'] ."\n"; + // echo $flexy->resolvePath ($pgdata['template']). ':'. $tmpl->updated . ">=" . date('Y-m-d H:i:s',filemtime($flexy->resolvePath ($pgdata['template']))) . "\n"; + return $tmpl; + } } } - + //die("got here"); try { $r = $flexy->compile($pgdata['template']); + + } catch(Exception $e) { $old = clone($tmpl); - $tmpl->updated = date('Y-m-d H:i:s',filemtime($flexy->resolvePath ($pgdata['template']))); + $tmpl->updated = date('Y-m-d H:i:s',filemtime($flexy->resolvePath ($pgdata['template']) . '/'. $pgdata['template'])); if ($tmpl->id) { $tmpl->is_deleted = 0; $tmpl->filetype = 'html'; @@ -238,15 +279,15 @@ class Pman_Core_DataObjects_Core_template extends DB_DataObject $tmpl->lang = 'en'; $tmpl->insert(); } - - + //echo "SKIP: " . $pgdata['template'] ."\n"; + // echo "SKIP - exception\n"; print_r($e); return false; } if (is_a($r,'PEAR_Error')) { - - // echo $r->toString(). "\n"; + //echo "SKIP: " . $pgdata['template'] ."\n"; + //echo $r->toString(). "\n"; return $r; } @@ -262,7 +303,7 @@ class Pman_Core_DataObjects_Core_template extends DB_DataObject $tmpl->view_name = $pgdata['base']; - + //echo $pgdata['template'] ."\n"; if (!$tmpl->get('template', $pgdata['template'])) { $tmpl->is_deleted = 0; $tmpl->filetype = 'html'; @@ -307,7 +348,14 @@ class Pman_Core_DataObjects_Core_template extends DB_DataObject return clone($tmpl); } - function syncPhpGetText($pgdata) + + // allow reuse in cms templatstr + function factoryStr() + { + return DB_DataObject::factory('core_templatestr'); + } + + function syncFileWord($pgdata, $filetype) { $tmpl = DB_DataObject::Factory($this->tableName()); $tmpl->view_name = $pgdata['base']; @@ -315,73 +363,103 @@ class Pman_Core_DataObjects_Core_template extends DB_DataObject if ($tmpl->get('template', $pgdata['template'])) { if (strtotime($tmpl->updated) >= filemtime( $tmpl->currentTemplate )) { - if ($tmpl->is_deleted != 0 || $tmpl->filetype != 'html') { + if ($tmpl->is_deleted != 0 || $tmpl->filetype != $filetype) { $oo = clone($tmpl); $tmpl->is_deleted = 0; - $tmpl->filetype = 'php'; + $tmpl->filetype = $filetype; $tmpl->update($oo); } return $tmpl; } } + $words = array(); - - $ar = token_get_all(file_get_contents( $tmpl->currentTemplate )); - foreach( $ar as $i=> $tok) { - if (!is_array($tok) || $tok[0] != T_CONSTANT_ENCAPSED_STRING) { - continue; - } - if ($i < 2) { - continue; - } - if (is_array($ar[$i-1]) || $ar[$i-1] != '(') { - continue; - } - if (!is_array($ar[$i-2]) || $ar[$i-2][1] != '_') { - continue; - } - $ct = $tok[1][0]; - $words[] = str_replace('\\'. $ct, $ct, trim($tok[1] , $ct)); - + + switch($filetype) { + case "php": + $ar = token_get_all(file_get_contents( $tmpl->currentTemplate )); + foreach( $ar as $i=> $tok) { + if (!is_array($tok) || $tok[0] != T_CONSTANT_ENCAPSED_STRING) { + continue; + } + if ($i < 2) { + continue; + } + if (is_array($ar[$i-1]) || $ar[$i-1] != '(') { + continue; + } + if (!is_array($ar[$i-2]) || $ar[$i-2][1] != '_') { + continue; + } + $ct = $tok[1][0]; + $words[] = str_replace('\\'. $ct, $ct, trim($tok[1] , $ct)); + + } + break; + case "js": + $fc = file_get_contents( $tmpl->currentTemplate ); + + preg_match_all('/\._\("([^"]+)"\)/', $fc, $outd); + $words = $outd[1]; + + preg_match_all('/\._\(\'([^\']+)\'\)/', $fc, $outs); + + // ?? seriously adding two arrays? + $words = array_diff(array_merge($words, $outs[1]), array_intersect($words, $outs[1])); + break; + case "xml": + $words = $pgdata['words']; + break; } - // create the template... - - - if (!$tmpl->id) { - - $tmpl->template = $pgdata['template']; - $tmpl->lang = 'en'; /// ??? hard coded?? - $tmpl->filetype = 'php'; + + $words = array_unique($words); + + if(empty($words)) { + return; + } + + if ($tmpl->id) { + $oo = clone($tmpl); $tmpl->is_deleted = 0; + $tmpl->filetype = $filetype; $tmpl->updated = date('Y-m-d H:i:s', filemtime($tmpl->currentTemplate)); - $tmpl->insert(); + $tmpl->update($oo); } else { - $xx =clone($tmpl); - $tmpl->filetype = 'php'; $tmpl->is_deleted = 0; - $tmpl->lang = 'en'; /// ??? hard coded?? + $tmpl->filetype = $filetype; + $tmpl->lang = 'en'; $tmpl->updated = date('Y-m-d H:i:s', filemtime($tmpl->currentTemplate)); - $tmpl->update($xx); - } - - $words = array_unique($words); - - if (!count($words)) { - return; + $tmpl->insert(); } - - + $tmpl->words = $words; - - $x = DB_DataObject::Factory('core_templatestr'); - $x->syncTemplateWords($tmpl); - - + + $this->factoryStr()->syncTemplateWords($tmpl); + return $tmpl; - - - } + + function syncPhpGetText($pgdata) + { + return $this->syncFileWord($pgdata, 'php'); + } + + /** + * plain JS files use ._(....) to flag + * it does not support quoted strings or anything really + * very simple strings only + */ + + function syncJsWords($pgdata) + { + return $this->syncFileWord($pgdata, 'js'); + } + + function syncPowerpointXMLText($pgdata) + { + return $this->syncFileWord($pgdata, 'xml'); + } + /* SELECT LOWER( CONCAT( @@ -400,7 +478,7 @@ WHERE ( function genGetText($clsname, $lang=false) { - static $done = false; + static $done = array(); $clsname = strtolower($clsname); textdomain($clsname); @@ -411,12 +489,12 @@ WHERE ( if (!empty($done[$clsname.':'.$lang])) { - return; // already sent headers and everything. + return true; // already sent headers and everything. } putenv("LANGUAGE=$lang"); - if ($lang != 'en' && !setlocale(LC_ALL, $lang)) { - if (!setlocale(LC_ALL, $lang.'.UTF8')) { + if ($lang != 'en') { + if (!setlocale(LC_ALL, $lang.'.UTF-8')) { $ff->page->jerr("Language is not available {$lang}"); } } @@ -435,7 +513,7 @@ WHERE ( $d->filetype = 'php'; if (! $d->find(true) ){ $done[$clsname.':'.$lang] = true; - return; + return false; } $user = 'www-data'; // ?? do we need other ones $compileDir = ini_get('session.save_path') .'/' . @@ -464,24 +542,25 @@ WHERE ( $done[$clsname.':'.$lang] = 1; // do we need to compile the file.. - $ts = DB_DataObject::Factory('core_templatestr'); - $ts->selectAdd('MAX(updated) as updated'); + $ts = $this->factoryStr(); + $ts->selectAdd('COALESCE(MAX(updated), "1000-01-01") as updated'); $ts->lang = $lang; $ts->template_id = $d->id; if (!$ts->find(true)) { // then in theory there are no translations - return; + return false; } if (file_exists($fname) && strtotime($ts->updated) < filemtime($fname)) { - return; // file exists and is newer than our updated line. + return $fname; // file exists and is newer than our updated line. } //DB_DataObject::debugLevel(1); - $ts = DB_DataObject::Factory('core_templatestr'); + $ts = $this->factoryStr(); $ts->autoJoin(); - $ts->selectAdd('join_src_id_id.txt as src_id_txt, core_templatestr.txt as txt'); + $ts->selectAdd("join_src_id_id.txt as src_id_txt, {$ts->tableName()}.txt as txt"); $ts->lang = $lang; $ts->template_id = $d->id; + $ts->whereAdd("LENGTH(join_src_id_id.txt) > 0 AND LENGTH({$ts->tableName()}.txt) > 0"); $words = $ts->fetchAll('src_id_txt', 'txt' ); if (!file_exists($fdir)) { @@ -518,7 +597,7 @@ WHERE ( - return; + return $fname; require_once 'File/Gettext.php'; $gt = File_Gettext::factory('MO', $fname); diff --git a/DataObjects/Core_templatestr.php b/DataObjects/Core_templatestr.php index 88a9785a..a8007505 100644 --- a/DataObjects/Core_templatestr.php +++ b/DataObjects/Core_templatestr.php @@ -84,7 +84,6 @@ class Pman_Core_DataObjects_Core_templatestr extends DB_DataObject */ function onTableChange($roo, $obj, $chg) { - $ff = HTML_FlexyFramework::get()->Pman_Core; if(empty($ff['DataObjects_Core_templatestr']['tables'])){ @@ -95,18 +94,42 @@ class Pman_Core_DataObjects_Core_templatestr extends DB_DataObject return; } $cols = $ff['DataObjects_Core_templatestr']['tables'][$tn]; - - + + $used = array(); foreach($cols as $c) { + + if(strpos($c, ',') !== false) { + $arr = explode(',', $c); + $c = $arr[0]; + $cond = $arr[1]; + + $ar = explode('=', $cond); + $key = $ar[0]; + + // skip if condition not fulfilled + if($obj->{$ar[0]} != $ar[1]) { + continue; + } + } + + // skip if empty string + if(empty($obj->$c)) { + continue; + } + $x = $this->factory($this->tableName()); $x->on_id = $obj->pid(); $x->on_table = $tn; $x->on_col = $c; $x->lang = ''; /// eg. base language.. $up = $x->find(true); - if ($up && $x->txt == $obj->$c) { - continue; // update an no change.. + + // skip when no change + if($up && $x->txt == $obj->$c) { + $used[] = $x->id; + continue; } + $x->active = 1; $x->src_id = 0; $x->txt = $obj->$c; @@ -115,6 +138,23 @@ class Pman_Core_DataObjects_Core_templatestr extends DB_DataObject $x->updated = date('Y-m-d H:i:s', strtotime("NOW")); $up ? $x->update() : $x->insert(); } + + // make sure the used one are active + if(count($used)) { + $t = DB_DataObject::factory($this->tableName()); + // activate the aprent data + $t->query("UPDATE core_templatestr + SET active = 1 WHERE id in (" . implode(',' ,$used) . ") + "); + // activate the child data + $t->query("UPDATE core_templatestr + SET active = 1 + WHERE + src_id IN (". implode(',' ,$used) . ") + AND + lang != '' + "); + } } @@ -261,9 +301,8 @@ class Pman_Core_DataObjects_Core_templatestr extends DB_DataObject //print_r($words);exit; // grab original $tt = DB_DataObject::factory($this->tableName()); - - - $t = DB_DataObject::factory($this->tableName()); + + $t = clone($tt); $t->template_id = $tmpl->id; $t->whereAdd("lang = ''"); @@ -290,6 +329,11 @@ class Pman_Core_DataObjects_Core_templatestr extends DB_DataObject $v = trim($v); + + // skip empty words + if(empty($v)) { + continue; + } $md = $keyvalue ? $k : md5($v); @@ -316,7 +360,8 @@ class Pman_Core_DataObjects_Core_templatestr extends DB_DataObject $active[] = $cur[$md]; // we have it already? - - $tt->query("UPDATE {$this->tableName()} + $t = DB_DataObject::factory($this->tableName()); + $t->query("UPDATE {$this->tableName()} SET active= 1 WHERE id = ".$cur[$md]); @@ -443,18 +488,23 @@ class Pman_Core_DataObjects_Core_templatestr extends DB_DataObject // find all the id's from lang that have not been generated.. - //find the origanal - $t = DB_DataObject::factory($tn); - $t->whereAdd("lang = ''"); - $t->active = 1; - //old code, this did not support the on_table -// $id_tmp = $t->fetchAll('id','template_id'); -// $ids = array_keys($id_tmp); - $id_tmp = array(); - //new code for support the sync tables - foreach($t->fetchAll() as $ori){ - $id_tmp[$ori->id] = $ori; + static $id_tmp = false; + + if ($id_tmp == false) { + //find the origanal + $t = DB_DataObject::factory($tn); + $t->whereAdd("lang = ''"); + $t->active = 1; + + //old code, this did not support the on_table + // $id_tmp = $t->fetchAll('id','template_id'); + // $ids = array_keys($id_tmp); + $id_tmp = array(); + //new code for support the sync tables + foreach($t->fetchAll() as $ori){ + $id_tmp[$ori->id] = $ori; + } } $ids = array_keys($id_tmp); @@ -465,25 +515,33 @@ class Pman_Core_DataObjects_Core_templatestr extends DB_DataObject //$t->active = 1; $got = $t->fetchAll('src_id'); $missing = array_diff($ids, $got); - foreach($missing as $id) { - - $t = DB_DataObject::factory($tn); - $t->setFrom(array( - 'src_id' => $id, - 'txt' => '', - 'lang' => $lang, - 'updated' => date('Y-m-d H:i:s', strtotime("NOW")), - 'template_id'=> $id_tmp[$id]->template_id, - 'on_table' => $id_tmp[$id]->on_table, - 'on_id' => $id_tmp[$id]->on_id, - 'on_col' => $id_tmp[$id]->on_col, - 'active' => 1, - // no md5um - )); - $t->insert(); - } - + if (empty($missing)) { + return; + } + $t = DB_DataObject::factory($tn); + $q = "CREATE TEMPORARY TABLE core_templatestr_insert SELECT + id as src_id, + '' as txt, + '$lang' as lang, + NOW() as updated, + template_id, + on_table, + on_id, + on_col, + 1 as active + FROM core_templatestr + WHERE + id IN (". implode(',', $missing) . ") + "; + //echo $q; exit; + DB_DataObject::factory($tn)->query($q); + $q = "INSERT INTO $tn (src_id, txt, lang, updated, template_id, on_table,on_id, on_col, active) SELECT * FROM core_templatestr_insert"; + DB_DataObject::factory($tn)->query($q); + $q = "DROP TEMPORARY TABLE core_templatestr_insert"; + DB_DataObject::factory($tn)->query($q); + + } @@ -604,9 +662,21 @@ class Pman_Core_DataObjects_Core_templatestr extends DB_DataObject } - // determine if a complied template need recompling function translateChanged($flexy) + { + + $date = $this->lastUpdated($flexy); + if ($date === false) { + return false; + } + $utime = file_exists($flexy->compiledTemplate) ? filemtime( $flexy->compiledTemplate) : 0; + return strtotime($date) > $utime; + } + + // determine if a complied template need recompling + + function lastUpdated($flexy) { //return true; // var_dump('check changed?'); @@ -616,12 +686,14 @@ class Pman_Core_DataObjects_Core_templatestr extends DB_DataObject //var_dump($flexy->compiledTemplate); $utime = file_exists($flexy->compiledTemplate) ? filemtime( $flexy->compiledTemplate) : 0; - + static $cache = array(); // cache of templates.. $ff = HTML_FlexyFramework::get(); $view_name = isset($ff->Pman_Core['view_name']) ? $ff->Pman_Core['view_name'] : false; + // find which of the template directories was actually used for the template. + $tempdir = ''; foreach($flexy->options['templateDir'] as $td) { if (substr($flexy->currentTemplate, 0, strlen($td)) == $td) { @@ -633,6 +705,7 @@ class Pman_Core_DataObjects_Core_templatestr extends DB_DataObject $tmpname = substr($flexy->currentTemplate, strlen($td) +1); + // we do not have any record of this template.. if (isset($cache[$tmpname]) && $cache[$tmpname] === false) { return false; } @@ -666,11 +739,67 @@ class Pman_Core_DataObjects_Core_templatestr extends DB_DataObject $x->lang = $flexy->options['locale']; $x->active = 1; $x->template_id = $tmpl->id; - $x->whereAdd("updated > '". date('Y-m-d H:i:s', $utime)."'"); - - return $x->count() ? true : false; + //$x->whereAdd("updated > '". date('Y-m-d H:i:s', $utime)."'"); + if ($x->count() < 1) { + return false; // we don't have any record of it. + } + $x->selectAdd(); + $x->selectAdd('max(updated) as max_updated'); + $x->find(true); + return $x->max_updated; } + function toRooArray($req) { + $ret = $this->toArray(); + + if (empty($req['csvCols'])) { + return $ret; + } + + // for download + + // translations for table columns + if(!empty($ret['on_table']) && !empty($ret['on_id']) && !empty($ret['on_col'])) { + $ret['template_id_view_name'] = 'database'; + $ret['template_id_template'] = $ret['on_table'] . ':' . $ret['on_col']; + } + + return $ret; + } + + function postListFilter($ar, $au, $req) + { + if (empty($req['csvCols'])) { + return $ar; + } + + // for download + + $ret = array(); + + foreach($ar as $v) { + if(empty($v['on_table']) || empty($v['on_id']) || empty($v['on_col'])) { + $ret[] = $v; + continue; + } + + // translations for table columns + // avoid duplicate (same src_id_mdsum, same on_table, same on_col, but different on_id) + + $key = $v['on_table'] . ':' . $v['on_col'] . ':' . $v['src_id_mdsum']; + + if(!empty($ret[$key])) { + continue; + } + + $ret[$key] = $v; + } + + $ret = array_values($ret); + + return $ret; + + } } diff --git a/DataObjects/Core_watch.php b/DataObjects/Core_watch.php index e41d10d8..2e207ff2 100644 --- a/DataObjects/Core_watch.php +++ b/DataObjects/Core_watch.php @@ -282,6 +282,7 @@ class Pman_Core_DataObjects_Core_watch extends DB_DataObject $n->trigger_event_id = $event->id; $n->person_id = $watch->person_id; $n->watch_id = $watch->id; + $n->evtype = $watch->medium; // does this watch already have a flag... $nf = clone($n); @@ -295,9 +296,7 @@ class Pman_Core_DataObjects_Core_watch extends DB_DataObject $n->act_start( empty($n->act_start) ? date("Y-m-d H:i:s") : $n->act_start ); $n->insert(); } - - - + } function initDatabase($roo, $data) { foreach($data as $d) { diff --git a/DataObjects/Events.php b/DataObjects/Events.php index e8682ac8..a04484c4 100644 --- a/DataObjects/Events.php +++ b/DataObjects/Events.php @@ -54,7 +54,6 @@ class Pman_Core_DataObjects_Events extends DB_DataObject $this->selectAs(); $this->selectAs($jt, 'person_id_%s', 'join_person_id_id'); - if (method_exists($jt,'nameColumn')) { $this->selectAdd("join_person_id_id.{$jt->nameColumn()} as person_id_name"); } @@ -150,7 +149,7 @@ class Pman_Core_DataObjects_Events extends DB_DataObject //DB_DataObject::DebugLevel(1); $joins = explode(',',$q['_join']); - $this->selectAdd(); // ??? + //$this->selectAdd(); // ??? << this wipes out the default options $distinct = false; foreach($joins as $t) { @@ -160,7 +159,8 @@ class Pman_Core_DataObjects_Events extends DB_DataObject continue; } $jtn = $x->tableName(); - $jk = array_shift($x->keys()); + $jks = $x->keys(); + $jk = array_shift($jks); $this->_join .= " LEFT JOIN {$jtn} as join_on_id_{$jtn} ON {$tn}.on_id = join_on_id_{$jtn}.{$jk} @@ -475,18 +475,39 @@ class Pman_Core_DataObjects_Events extends DB_DataObject static function writeEventLogExtra($data) { self::$extra_data = $data; } + static $files = array(); + + /** + * Add a file to the output log + * this needs to be called before addEvent, and the data format should be the same as $_FILES + * eg. + * + * DB_DataObject::factory('Events')->addFile(array( + * 'tmp_name' => real file location + * 'name' => real file name + * 'type' => mimetype + * 'size' => filesize + * + * )) + * + */ + function addFile($data) + { + self::$files[] = $data; + } - function logDir() + function logDir($user = false) { $ff = HTML_FlexyFramework::get(); - if (function_exists('posix_getpwuid')) { - $uinfo = posix_getpwuid( posix_getuid () ); - - $user = $uinfo['name']; - } else { - $user = getenv('USERNAME'); // windows. - } - + if ($user === false) { + if (function_exists('posix_getpwuid')) { + $uinfo = posix_getpwuid( posix_getuid () ); + + $user = $uinfo['name']; + } else { + $user = getenv('USERNAME'); // windows. + } + } if (!empty($ff->Pman['storedir'])) { @@ -516,6 +537,7 @@ class Pman_Core_DataObjects_Events extends DB_DataObject if (!file_exists(dirname($file))) { @mkdir(dirname($file),0700,true); // this might fail if it does not have correct permissions.. + clearstatcache(); if (!file_exists(dirname($file))) { //print_r($this); die("Events:: writeEventLog: could not create $file - permissons are not correct\n"); // fatal, otherwise we loop!? @@ -534,9 +556,10 @@ class Pman_Core_DataObjects_Events extends DB_DataObject $i=0; $files = array(); + - $i = 0; - foreach ($_FILES as $k=>$f){ + + foreach (array_merge($_FILES, self::$files) as $k=>$f){ // does not handle any other file[] arrary very well.. if (empty($f['tmp_name']) || !file_exists($f['tmp_name'])) { continue; @@ -644,8 +667,13 @@ class Pman_Core_DataObjects_Events extends DB_DataObject $date = date('/Y/m/d/', strtotime($this->event_when)); $file = $logdir. $date. $this->id . ".json"; - if (!file_exists(dirname($file))) { - return false; + if (!file_exists($file)) { + + // try looking www-dir.. + $file = $this->logDir('www-data'). $date. $this->id . ".json"; + if (!file_exists($file)) { + return false; + } } return $file; diff --git a/DataObjects/Images.php b/DataObjects/Images.php index 4c4feed8..d866e481 100644 --- a/DataObjects/Images.php +++ b/DataObjects/Images.php @@ -38,6 +38,12 @@ class Pman_Core_DataObjects_Images extends DB_DataObject $tn.filename LIKE '%{$this->escape($q['search']['filename'])}%' OR $tn.title LIKE '%{$this->escape($q['search']['filename'])}%' "); } + + if(!empty($q['_to_base64']) && !empty($q['image_id'])) { + $i = DB_DataObject::factory("Images"); + $i->get($q['image_id']); + $roo->jok($i->toBase64()); + } } @@ -53,7 +59,7 @@ class Pman_Core_DataObjects_Images extends DB_DataObject $o = $this->object(); //print_r($o); - if (method_exists($o, 'checkPerm')) { + if ($o && method_exists($o, 'checkPerm')) { // edit permissions on related object needed... return $o->checkPerm( $lvl == 'S' ? 'S' : 'E' , $au); @@ -195,15 +201,79 @@ class Pman_Core_DataObjects_Images extends DB_DataObject * @return - target file name */ function getStoreName() + { + return self::staticGetStoreName($this); + + } + + static function staticGetStoreName($o) { $opts = HTML_FlexyFramework::get()->Pman; - $fn = preg_replace('/[^a-z0-9\.]+/i', '_', $this->filename); + $fn = preg_replace('/[^a-z0-9\.]+/i', '_', $o->filename); return implode( '/', array( - $opts['storedir'], '_images_', date('Y/m', strtotime($this->created)), $this->id . '-'. $fn + $opts['storedir'], '_images_', date('Y/m', strtotime($o->created)), $o->id . '-'. $fn )); - } - + + /** + * does the files exist? + */ + function exists() + { + return self::staticExists($this); + } + + static function staticExists($o) + { + clearstatcache(); + $ret = file_exists(self::staticGetStoreName($o)); + if (!$ret) { + return self::staticCanFix($o); + } + return $ret; + } + + /** + * the getStorename code got changed, and some old files may not end up with the correct name anymore. + * this tries to fix it. + * + */ + function canFix() + { + return self::staticCanFix($this); + } + + static function staticCanFix($o) + { + // look for the image in the folder, with matching id. + // this is problematic.. + $fn = self::staticGetStoreName($o); + if (file_exists($fn . '-really-missing')) { + return false; + } + if (!file_exists(dirname($fn))) { + return false; + } + foreach( scandir(dirname($fn)) as $n) { + if (empty($n) || $n[0] == '.') { + continue; + } + $bits = explode('-', $n); + if ($bits[0] != $o->id) { + continue; + } + if (preg_match('/\.[0-9]+x[0-9]]+\.jpeg$/', $n)) { + continue; + } + copy(dirname($fn). '/'. $n, $fn); + clearstatcache(); + return true; + } + // fixme - flag it as bad + touch($fn . '-really-missing'); + } + + /** * deletes all the image instances of it... * @@ -212,14 +282,20 @@ class Pman_Core_DataObjects_Images extends DB_DataObject function beforeDelete($dependants_array, $roo) { + if (!empty($dependants_array)) { + return; + } + $opts = HTML_FlexyFramework::get()->Pman; $deldir = $opts['storedir']. '/_deleted_images_'; + clearstatcache(); if (!file_exists( $deldir )) { - mkdir($deldir, 0755); + @mkdir($deldir, 0755); // not sure why we are erroring here.. after checking - maybe permissions? } $fn = $this->getStoreName(); $b = basename($fn); + clearstatcache(); if (file_exists($fn)) { if (file_exists($deldir . '/'. $b)) { @@ -238,12 +314,14 @@ class Pman_Core_DataObjects_Images extends DB_DataObject $dh = opendir($d); while (false !== ($fn = readdir($dh))) { if (substr($fn, 0, strlen($b)) == $b) { - + clearstatcache(); if (file_exists($deldir . '/'. $fn)) { unlink($d. '/'. $fn); continue; } - rename($d. '/'. $fn, $deldir .'/'. $fn); + if (file_exists($d. '/'. $fn)) { + rename($d. '/'. $fn, $deldir .'/'. $fn); + } } } @@ -483,7 +561,7 @@ class Pman_Core_DataObjects_Images extends DB_DataObject } - $ret['shorten_name'] = $ret['filename'] = $this->shorten_name(); + $ret['shorten_name'] = $this->shorten_name(); return $ret; } @@ -495,11 +573,14 @@ class Pman_Core_DataObjects_Images extends DB_DataObject * * */ - function URL($size , $provider = '/Images/Thumb', $baseURL=false) + function URL($size , $provider = '/Images/Thumb', $baseURL=false, $to_type=false) { if (!$this->id) { return 'about:blank'; } + if (!$this->exists()) { + return 'about:missing'; + } $shorten_name = $this->shorten_name(); @@ -519,24 +600,29 @@ class Pman_Core_DataObjects_Images extends DB_DataObject //$size = min(1024, (int) $size); // the size should 200x150 to convert $sizear = preg_split('/(x|c)/', $size); - if(empty($sizear[1])){ - $sizear[1] = 0; + if(!isset($sizear[1])){ + $sizear[1] = 0; // 0x with '0' is a box? why } + $size = implode(strpos($size,'c') > -1 ? 'c' : 'x', $sizear); // print_r($size); $fc = $this->toFileConvert(); // print_r($size); // exit; - $mt = $this->mimetype; + $mt = $to_type === false ? $this->mimetype : $to_type; if (!preg_match('#^image/#i',$mt)) { $mt = 'image/jpeg'; } - $fc->convert($mt, $size); + $cn = $fc->convert($mt, $size); + $shorten_name = $this->shorten_name(basename($cn)); return $baseURL . $provider . "/$size/{$this->id}/{$shorten_name}"; // -- this breaks the rss feed #image-{$this->id}"; } - + /** + * + * tries to get an image from then URL - not always has based... - also from the normal url + */ function getFromHashURL($url) { $id = false; @@ -546,8 +632,12 @@ class Pman_Core_DataObjects_Images extends DB_DataObject $id = $matches[1]; } else if (preg_match('#Images/([0-9]+)/#', $url, $matches)) { $id = $matches[1]; + } else if (preg_match('#images[^/]+/([0-9]+)/#i', $url, $matches)) { + // supports images.xxxxx.com/{number}/name... + $id = $matches[1]; + } else if (preg_match('#Thumb/[^/]+/([0-9]+)/#', $url, $matches)) { + $id = $matches[1]; } - if ($id === false || $id < 1) { return false; } @@ -560,13 +650,14 @@ class Pman_Core_DataObjects_Images extends DB_DataObject } - function shorten_name() + function shorten_name($fn = false) { if(empty($this->filename)) { return; } + $fn = $fn === false ? $this->filename : $fn; - $filename = explode('.', $this->filename); + $filename = explode('.', $fn); $ext = array_pop($filename); $name = preg_replace("/[^A-Z0-9.]+/i", '-', implode('-', $filename)) ; @@ -583,7 +674,7 @@ class Pman_Core_DataObjects_Images extends DB_DataObject * * */ - function toHTML($size, $provider = '/Images/Thumb') + function toHTML($size, $provider = '/Images/Thumb', $extra = '') { @@ -598,12 +689,12 @@ class Pman_Core_DataObjects_Images extends DB_DataObject } if (empty($sz[1])) { $ratio = empty($this->width) ? 1 : $this->height/ ($this->width *1.0); - $sy = $ratio * $sx; + $sy = intval($ratio * $sx); } else { $sy = $sz[1]; } // create it? - $extra = ''; + if (strlen($this->title)) { $extra = ' title="'. htmlspecialchars($this->title) . '"'; } @@ -642,6 +733,8 @@ class Pman_Core_DataObjects_Images extends DB_DataObject */ function toFileConvert() { + $fn = $this->getStoreName(); + require_once 'File/Convert.php'; $fc = new File_Convert($this->getStoreName(), $this->mimetype); return $fc; @@ -825,13 +918,31 @@ class Pman_Core_DataObjects_Images extends DB_DataObject function createFromData($data) { - $this->mimetype= strtolower($this->mimetype); + if (0 === strpos($data, "data:")) { + // data:image/png;base64, + $data = substr($data,5); + $bits = explode(";", $data); + $this->mimetype = $bits[0]; + } + static $imgid = 1; + if (empty($this->filename)) { + require_once 'File/MimeType.php'; + $y = new File_MimeType(); + $this->filename = 'image-'.$imgid++.'.'.$y->toExt($this->mimetype); + } + + + $this->mimetype = strtolower($this->mimetype); + if ($this->mimetype == 'image/jpg') { + $this->mimetype = 'image/jpeg'; + } + $explode_mimetype = explode('/', $this->mimetype); if (array_shift($explode_mimetype) == 'image') { - $imgs = @getimagesize($data); + $imgs = @getimagesize('data://'. $data); if (!empty($imgs) && !empty($imgs[0]) && !empty($imgs[1])) { list($this->width , $this->height) = $imgs; @@ -855,7 +966,7 @@ class Pman_Core_DataObjects_Images extends DB_DataObject } file_put_contents($f, file_get_contents("data://" . $data)); - + //var_dump($f);exit; $o = clone($this); $this->filesize = filesize($f); @@ -877,7 +988,7 @@ class Pman_Core_DataObjects_Images extends DB_DataObject } $file = $this->getStoreName(); - + if(!file_exists($file)){ return false; } diff --git a/DataObjects/core.sql b/DataObjects/core.sql deleted file mode 100644 index a0047eba..00000000 --- a/DataObjects/core.sql +++ /dev/null @@ -1,21 +0,0 @@ - - --- file is not really used any more.. - - --- // core comapy types - use core enums (Company Type) -DROP TABLE IF EXISTS core_company_type; - - - --- // old core image type - merged into enum. -DROP TABLE IF EXISTS core_image_type; - - - - - - - --- ---------------------------- - diff --git a/DataObjects/pman.links.ini b/DataObjects/pman.links.ini index 6e1c70df..76038805 100644 --- a/DataObjects/pman.links.ini +++ b/DataObjects/pman.links.ini @@ -24,6 +24,7 @@ logo_id = Images:id owner_id = core_person:id main_office_id = core_office:id comptype_id = core_enum:id +parent_id = core_company:id [core_office] company_id = core_company:id @@ -34,7 +35,7 @@ agency_id = core_company:id team_id = core_group:id open_by = core_person:id owner_id = core_person:id - +deleted_by = core_person:id [core_group] leader = core_person:id @@ -64,12 +65,28 @@ watch_id = core_watch:id trigger_person_id = core_person:id trigger_event_id = Events:id domain_id = core_domain:id +server_id = core_notify_server:id + [core_notify_recur] person_id = core_person:id last_event_id = Events:id method_id = core_enum:id + +[core_notify_blacklist] +server_id = core_notify_server:id +domain_id = core_domain:id + + +[core_notify_sender_blacklist] +domain_id = core_domain:id +sender_id = core_notify_sender:id + + + + + [core_email] owner_id = core_person:id bcc_group_id = core_group:id @@ -92,6 +109,11 @@ template_id = core_template:id [core_template_element] template_id = core_template:id +[core_project_group] +project_id = core_project:id +group_id = core_group:id + + [database__render] core_project = name core_company= name diff --git a/DatabaseColumns.php b/DatabaseColumns.php index fd674a30..703b1fae 100644 --- a/DatabaseColumns.php +++ b/DatabaseColumns.php @@ -11,10 +11,10 @@ class Pman_Core_DatabaseColumns extends Pman { $au = $this->getAuthUser(); if (!$au) { - $this->jerr("Not authenticated", array('authFailure' => true)); + $this->jerror("LOGIN-NOAUTH", "Not authenticated", array('authFailure' => true)); } if (!$au->pid() ) { // not set up yet.. - $this->jerr("Not authenticated", array('authFailure' => true)); + $this->jerror("LOGIN-NOAUTH", "Not authenticated", array('authFailure' => true)); } diff --git a/GnumericToExcel.php b/GnumericToExcel.php index a14633e7..feb940c3 100644 --- a/GnumericToExcel.php +++ b/GnumericToExcel.php @@ -30,7 +30,7 @@ class Pman_Core_GnumericToExcel extends Pman function get($v, $opts=array()) { - + } function post($fname) { @@ -66,7 +66,7 @@ class Pman_Core_GnumericToExcel extends Pman exit; } - $ext = '.xls'; + $ext = 'xls'; $outfmt = 'Gnumeric_Excel:excel_biff8'; $mime = 'application/vnd.ms-excel'; @@ -98,7 +98,7 @@ class Pman_Core_GnumericToExcel extends Pman " --export-type={$outfmt} " . $srcTmp . ' ' . $targetTmp . ' 2>&1'; // echo $cmd; - //passthru($cmd);exit; + //passthru($cmd);exit; //exit; $out = `$cmd`; clearstatcache(); @@ -111,23 +111,31 @@ class Pman_Core_GnumericToExcel extends Pman if (!empty($_POST['format']) && $_POST['format']=='xlsx') { require_once 'File/Convert.php'; $cc = new File_Convert($targetTmp,'application/vnd.ms-excel'); - $targetTmp = $cc->convert('application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'); + if (empty($targetTmp)) { + $this->jerr("convert to xlsx failed"); + } + $mime = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'; $ext = "xlsx"; } - - - - - - - // unlink($srcTmp); if (empty($fname)) { $fname = basename($targetTmp); } $fname .= preg_match('/\.' . $ext . '/i', $fname) ? '' : ('.' . $ext); // make sure it ends in xls.. + + DB_DataObject::factory('Events')->addFile(array( + 'tmp_name' => $targetTmp, + 'name' => $fname, + 'type' => $mime, + 'size' => filesize($targetTmp) + )); + + $this->addEvent("DOWNLOAD", false, $fname ); + + // unlink($srcTmp); + header('Content-type: ' . $mime); header('Content-Disposition: attachment; filename="' .addslashes($fname). '"'); header('Content-length: '. filesize($targetTmp)); diff --git a/GroupCountries.php b/GroupCountries.php index e694d8d8..fdd8fca9 100644 --- a/GroupCountries.php +++ b/GroupCountries.php @@ -15,7 +15,7 @@ class Pman_Core_GroupCountries extends Pman parent::getAuth(); // load company! $au = $this->getAuthUser(); if (!$au) { - $this->jerr("Not authenticated", array('authFailure' => true)); + $this->jerror("LOGIN-NOAUTH", "Not authenticated", array('authFailure' => true)); } if ($au->company()->comptype != 'OWNER') { $this->jerr("Permission Denied" ); diff --git a/GroupMembers.php b/GroupMembers.php index f501042e..ce061d7e 100644 --- a/GroupMembers.php +++ b/GroupMembers.php @@ -15,7 +15,7 @@ class Pman_Core_GroupMembers extends Pman parent::getAuth(); // load company! $au = $this->getAuthUser(); if (!$au) { - $this->jerr("Not authenticated", array('authFailure' => true)); + $this->jerror("LOGIN-NOAUTH", "Not authenticated", array('authFailure' => true)); } if ($au->company()->comptype != 'OWNER') { $this->jerr("Permission Denied" ); diff --git a/Heartbeat.php b/Heartbeat.php new file mode 100644 index 00000000..88d46074 --- /dev/null +++ b/Heartbeat.php @@ -0,0 +1,63 @@ +post($req); + die("POST only"); + } + + function post($req) + { + $this->initErrorHandling(); + + if ($this->database_is_locked()) { + die("FAILED"); + } + + + $cd = DB_DataObject::Factory('core_enum'); + $cd->setFrom(array( + 'etype' => 'heartbeat', + 'name' => 'last_update' + )); + if (!$cd->count()) { + $cd->display_name = date("Y-m-d H:i:s"); + $cd->insert(); + die("OK - HEARTBEAT WORKING"); + } + $cd->find(true); + $cc = clone($cd); + if ( (time() - strtotime($cc->display_name)) < 30) { + die("OK - HEARTBEAT WORKING"); + } + + $cd->display_name = date("Y-m-d H:i:s"); + $cd->update($cc); + die("OK - HEARTBEAT WORKING"); + } + + + function onPearError($err) + { + // print_r($err); + die("FAILED"); + } + function onException($err) + { + // print_r($err); + die("FAILED"); + } + + +} \ No newline at end of file diff --git a/I18n.php b/I18n.php index 5f70c7a4..bab0b2d3 100644 --- a/I18n.php +++ b/I18n.php @@ -62,7 +62,7 @@ class Pman_Core_I18n extends Pman //return true; $au = $this->getAuthUser(); //if (!$au) { - // $this->jerr("Not authenticated", array('authFailure' => true)); + // $this->jerror("LOGIN-NOAUTH", "Not authenticated", array('authFailure' => true)); //} $this->authUser = $au; @@ -86,8 +86,7 @@ class Pman_Core_I18n extends Pman $this->sessionState(0); $lang = !$this->authUser || empty($this->authUser->lang ) ? 'en' : $this->authUser->lang; - - /// verify the selected language.. + /// verify the selected language.. $i = DB_DataObject::Factory('I18n'); $i->ltype = 'l'; // string(1) not_null multiple_key $i->lkey = $lang; // string(8) not_null @@ -235,7 +234,13 @@ class Pman_Core_I18n extends Pman if (empty($k)) { return '??'; } - $fo = &PEAR::getStaticProperty('HTML_Template_Flexy','options'); + + if(class_exists('HTML_FlexyFramework2', false)) { + $fo = HTML_FlexyFramework2::get()->HTML_Template_Flexy; + } + else { + $fo = &PEAR::getStaticProperty('HTML_Template_Flexy','options'); + } $fallback_lang = empty($fo['locale']) ? 'en' : $fo['locale']; diff --git a/Images.php b/Images.php index bfda2826..bcedb003 100644 --- a/Images.php +++ b/Images.php @@ -78,6 +78,8 @@ class Pman_Core_Images extends Pman var $method = 'inline'; var $page = false; var $is_local = false; + var $size; + function get($s, $opts=array()) // determin what to serve!!!! { @@ -85,6 +87,8 @@ class Pman_Core_Images extends Pman //if (!empty($_GET['_post'])) { // return $this->post(); //} + + $this->is_local = (!empty($_SERVER['HTTP_HOST']) && $_SERVER['HTTP_HOST'] == 'localhost') ? true : false; @@ -126,7 +130,7 @@ class Pman_Core_Images extends Pman } if (strpos($id,':') > 0) { // id format tablename:id:-imgtype - + //DB_DataObject::debugLevel(1); if (!$this->authUser) { $this->imgErr("not-authenticated-using-colon-format",$s); @@ -183,7 +187,8 @@ class Pman_Core_Images extends Pman $img = DB_DataObjecT::factory('Images'); - if (!$id || !$img->get($id)) { + if (!$id || !$img->get($id) || !$img->exists()) { + //print_r($img); die("HERE"); $this->imgErr("image has been removed or deleted.",$s); } @@ -217,8 +222,9 @@ class Pman_Core_Images extends Pman } function imgErr($reason,$path) { - header('Location: ' . $this->rootURL . '/Pman/templates/images/file-broken.png?reason=' . - urlencode($reason) .'&path='.urlencode($path)); + header('Location: ' . $this->rootURL . '/Pman/templates/images/file-broken.png?reason=' . urlencode($reason) ); + header('X-Error: ' . $reason . ':' . $path); + echo $reason . ':' . $path; exit; } @@ -282,23 +288,27 @@ class Pman_Core_Images extends Pman function serve($img) { - $this->sessionState(0); // turn off session... - locking... + $this->sessionState(0); // turn off session... - locking... require_once 'File/Convert.php'; - if (!file_exists($img->getStoreName())) { -// print_r($img);exit; - header('Location: ' . $this->rootURL . '/Pman/templates/images/file-broken.png?reason=' . - urlencode("Original file was missing : " . $img->getStoreName())); - + if (!$img->exists()) { + $this->imgErr("serve = missing-image", $img->getStoreName()); + } // print_r($img);exit; $x = $img->toFileConvert(); if (empty($this->as_mimetype) || $img->mimetype == 'image/gif') { $this->as_mimetype = $img->mimetype; } + + if (!$this->thumb) { + if ($x->mimetype == $this->as_mimetype) { + $x->serveOnly($this->method, $img->filename); + exit; + } $x->convert( $this->as_mimetype); - $x->serve($this->method); + $x->serve($this->method, $img->filename); exit; } //echo "SKALING? $this->size"; @@ -323,6 +333,7 @@ class Pman_Core_Images extends Pman if (!file_exists($fn)) { $this->validateSize(); } + if(!empty($this->page) && !is_nan($this->page * 1)){ $x->convert( $this->as_mimetype, $this->size, 0, $this->page); @@ -394,7 +405,7 @@ class Pman_Core_Images extends Pman */ - static function replaceImageURLS($html) + static function replaceImageURLS($html, $obj = false) { $ff = HTML_FlexyFramework::get(); @@ -404,61 +415,142 @@ class Pman_Core_Images extends Pman //var_dump($ff->Pman_Images['public_baseURL']); $baseURL = $ff->Pman_Images['public_baseURL']; - preg_match_all('/]+>/i',$html, $result); - //print_r($result); - $matches = array_unique($result[0]); - foreach($matches as $img) { - $imatch = array(); - preg_match_all('/(width|height|src)="([^"]*)"/i',$img, $imatch); - // build a keymap - $attr = array(); - - foreach($imatch[1] as $i=>$key) { - $attr[$key] = $imatch[2][$i]; - } - // does it contain baseURL??? --- well what about relative paths... - //print_R($attr); - - if (empty($attr['src'])) { + libxml_use_internal_errors(true); + $dom = new DOMDocument(); + $dom->loadHTML("
{$html}
"); + $imgs = $dom->getElementsByTagName('img'); + + + foreach($imgs as $img) { + $src = $img->getAttribute('src'); + if (!$src|| !strlen(trim($src))) { continue; } - if (0 !== strpos($attr['src'], $baseURL)) { - // it starts with our 'new' baseURL? - $html = self::replaceImgUrl($html, $baseURL, $img, $attr, 'src' ); + + if (0 === strpos($src, 'data:')) { + if (!$obj) { + HTML_FlexyFramework::get()->page->jerr("no object to attach data url"); + } + + self::replaceDataUrl($baseURL, $img, $obj); continue; } - if (false !== strpos($attr['src'], '//') && false === strpos($attr['src'], $baseURL)) { - // contains an absolute path.. that is probably not us... + + + if (false !== strpos($src, '//') && false === strpos($src, $baseURL)) { + // contains an absolute path.. and not our baseURL. continue; } + + $img->setAttribute('src', self::domImgUrl($baseURL, $img)); + // what about mailto or data... - just ignore?? for images... - $html = self::replaceImgUrl($html, $baseURL, $img, $attr, 'src' ); } - + $anchors = $dom->getElementsByTagName('a'); $result = array(); preg_match_all('/]+>/i',$html, $result); $matches = array_unique($result[0]); - foreach($matches as $img) { - $imatch = array(); - preg_match_all('/(href)="([^"]*)"/i',$img, $imatch); - // build a keymap - $attr = array(); - - foreach($imatch[1] as $i=>$key) { - $attr[$key] = $imatch[2][$i]; - } - if (!isset($attr['href']) || 0 !== strpos($attr['href'], $baseURL)) { + foreach($anchors as $anc) { + $href = $anc->getAttribute('href'); + if (!empty($href) || 0 !== strpos($href, $baseURL)) { continue; } - $html = self::replaceImgUrl($html, $baseURL, $img, $attr, 'href' ); + $anc->setAttribute('href', self::domImgUrl($baseURL, $href)); } + + $inner = $dom->getElementById("tmp_dom_wrapper"); + $html = ''; + foreach ($inner->childNodes as $child) { + $html .= ($dom->saveHTML($child)); + } return $html; } + + static function domImgUrl($baseURL, $dom) + { + $url = $dom; + if (!is_string($url)) { + $url = $dom->getAttribute('src'); + } + $umatch = false; + if(!preg_match('#/(Images|Images/Thumb/[a-z0-9]+|Images/Download)/([0-9]+)/(.*)$#', $url, $umatch)) { + return $url; + } + $id = $umatch[2]; + $hash = ''; + + if (!empty($umatch[3]) && strpos($umatch[3],'#')) { + $hh = explode('#',$umatch[3]); + $hash = '#'. array_pop($hh); + } + + + $img = DB_DataObject::factory('Images'); + if (!$img->get($id)) { + return $url; + } + $type = explode('/', $umatch[1]); + $thumbsize = -1; + + if (count($type) > 2 && $type[1] == 'Thumb') { + $thumbsize = $type[2]; + $provider = '/Images/Thumb'; + } else { + $provider = '/'.$umatch[1]; + } + + $w = is_string($dom) ? false : $dom->getAttribute('width'); + $h = is_string($dom) ? false : $dom->getAttribute('height'); + + if (!is_string($dom) && (!empty($w) || !empty($h) ) && is_numeric($w) && is_numeric($h)) + { + // no support for %... + $thumbsize = + (empty($w) ? '0' : intval($w) * 1) . + 'x' . + (empty($h) ? '0' : intval($h) * 1); + $provider = '/Images/Thumb'; + + } + + if ($thumbsize !== -1) { + // change in size.. + // need to regenerate it.. + + $type = array('Images', 'Thumb', $thumbsize); + + $fc = $img->toFileConvert(); + // make sure it's available.. + $fc->convert($img->mimetype, $thumbsize); + + + } else { + $provider = $provider == 'Images/Thumb' ? 'Images' : $provider; + } + + + // finally replace the original TAG with the new version.. + + return $img->URL($thumbsize, $provider, $baseURL) . $hash ; + + + } + + static function replaceDataUrl($baseURL, $img, $obj) + { + $d = DB_DataObject::Factory('Images'); + $d->object($obj); + + + $d->createFromData($img->getAttribute('src')); + $img->setAttribute('src', $d->URL(-1, '/Images' , $baseURL)); + } + static function replaceImgUrl($html, $baseURL, $tag, $attr, $attr_name) { @@ -579,4 +671,8 @@ class Pman_Core_Images extends Pman } } + + + + } diff --git a/Import/Core_email.php b/Import/Core_email.php index db526e76..c198d274 100644 --- a/Import/Core_email.php +++ b/Import/Core_email.php @@ -38,6 +38,14 @@ class Pman_Core_Import_Core_email extends Pman 'min' => 0, 'max' => 0, ), + 'raw_content' => array( + 'desc' => 'Raw contents of email (used by API) - not by Command line', + 'short' => 'R', + 'default' => '', + 'min' => 0, + 'max' => 0, + ) + ); function getAuth() @@ -49,34 +57,41 @@ class Pman_Core_Import_Core_email extends Pman } } - function get($part = '', $opts=array()) { + function get($part = '', $opts=array()) + { $this->updateOrCreateEmail($part, $opts, false); } - function updateOrCreateEmail($part='', $opts, $cm = false, $mapping = false){ + function updateOrCreateEmail($part='', $opts= array(), $cm = false, $mapping = false){ // DB_DataObject::debugLevel(1); - $template_name = preg_replace('/\.[a-z]+$/i', '', basename($opts['file'])); - - if (!file_exists($opts['file'])) { - $this->jerr("file does not exist : " . $opts['file']); - } - - if (!empty($opts['master']) && !file_exists($opts['master'])) { - $this->jerr("master file does not exist : " . $opts['master']); - } - - - if (empty($cm)) { - $cm = DB_dataObject::factory('core_email'); - $ret = $cm->get('name',$template_name); - if($ret && empty($opts['update'])) { - $this->jerr("use --update to update the template.."); + if (empty($opts['raw_content'])) { + $template_name = preg_replace('/\.[a-z]+$/i', '', basename($opts['file'])); + + if (!file_exists($opts['file'])) { + $this->jerr("file does not exist : " . $opts['file']); + } + + + if (!empty($opts['master']) && !file_exists($opts['master'])) { + $this->jerr("master file does not exist : " . $opts['master']); + } + + + if (empty($cm)) { + $cm = DB_dataObject::factory('core_email'); + $ret = $cm->get('name',$template_name); + if($ret && empty($opts['update'])) { + $this->jerr("use --update to update the template.."); + } } + $mailtext = file_get_contents($opts['file']); + } else { + $template_name = $opts['name']; + $mailtext = $opts['raw_content']; } - $mailtext = file_get_contents($opts['file']); if (!empty($opts['master'])) { $body = $mailtext; @@ -95,6 +110,10 @@ class Pman_Core_Import_Core_email extends Pman $decoder = new Mail_mimeDecode($mailtext); $parts = $decoder->getSendArray(); + $structure = $decoder->decode(array( + 'include_bodies' => true, + 'decode_bodies' => true + )); if (is_a($parts,'PEAR_Error')) { echo $parts->toString() . "\n"; exit; @@ -103,22 +122,62 @@ class Pman_Core_Import_Core_email extends Pman $headers = $parts[1]; $from = new Mail_RFC822(); $from_str = $from->parseAddressList($headers['From']); + if (is_a($from_str,'PEAR_Error')) { + echo $from_str->toString() . "\n"; + exit; + } + $from_name = trim($from_str[0]->personal, '"'); $from_email = $from_str[0]->mailbox . '@' . $from_str[0]->host; - if (!empty($opts['use-file'])) { - $parts[2] = ''; + $bodyhtml = ''; + $bodytext = ''; + if (empty($opts['use-file'])) { + + switch($structure->ctype_primary .'/'. $structure->ctype_secondary ) { + case 'multipart/alternative': + foreach($structure->parts as $p) { + switch($p->ctype_primary .'/'. $p->ctype_secondary ) { + case 'text/plain': + $bodytext = $p->body; + break; + + case 'text/html': + $bodyhtml = $p->body; + break; + // no default... + } + + } + break; + case 'text/plain': + $bodytext = $parts[2]; + break; + + case 'text/html': + + $bodyhtml = $parts[2]; + break; + default: + var_dump($structure->ctype_primary .'/'. $structure->ctype_secondary ); + die("UNKNOWN TYPE"); + } + + } + + if ($cm->id) { $cc =clone($cm); $cm->setFrom(array( - 'bodytext' => !empty($opts['use-file']) ? '' : $parts[2], + 'bodytext' => $bodyhtml, + 'plaintext' => $bodytext, 'updated_dt' => date('Y-m-d H:i:s'), 'use_file' => !empty($opts['use-file']) ? realpath($opts['file']) : '', )); @@ -131,7 +190,8 @@ class Pman_Core_Import_Core_email extends Pman 'from_email' => $from_email, 'subject' => $headers['Subject'], 'name' => $template_name, - 'bodytext' => !empty($opts['use-file']) ? '' : $parts[2], + 'bodytext' => $bodyhtml, + 'plaintext' => $bodytext, 'updated_dt' => date('Y-m-d H:i:s'), 'created_dt' => date('Y-m-d H:i:s'), 'use_file' => !empty($opts['use-file']) ? realpath($opts['file']) : '', diff --git a/Import/Core_holiday.php b/Import/Core_holiday.php index 71b6aad4..e01c11c3 100644 --- a/Import/Core_holiday.php +++ b/Import/Core_holiday.php @@ -1,8 +1,8 @@ updateHolidays('hk'); - + $d->updateHolidays('cny'); } function log($str) diff --git a/Import/Timezone.php b/Import/Timezone.php new file mode 100644 index 00000000..5c0f4b98 --- /dev/null +++ b/Import/Timezone.php @@ -0,0 +1,79 @@ +cli) { + die("cli only"); + } + } + + function get($part = '', $opts=array()) + { + $ce = DB_DataObject::factory('core_enum'); + $ce->query(" + SELECT + *, TIME_FORMAT(TIMEDIFF(NOW(), CONVERT_TZ(NOW(), Name, 'UTC')), '%H:%i') as timeOffset + FROM + mysql.time_zone_name + WHERE + Name LIKE '%/%' + AND + Name NOT LIKE '%/%/%' + AND + Name NOT LIKE 'right%' + AND + Name NOT LIKE 'posix%' + AND + Name NOT LIKE 'Etc%' + ORDER BY + SUBSTRING_INDEX(Name, '/', 1) ASC, + timeoffset ASC, + Name ASC + "); + + $values = array(); + + $regions = array(); + $areas = array(); + + while($ce->fetch()) { + $ar = explode('/', $ce->Name); + $region = $ar[0]; + $area = str_replace('_', ' ', $ar[1]); + + if(!in_array($region, $regions)) { + $regions[] = $region; + $values[] = "('Timezone.Region', '" . $region . "', 1, 0, 0, '" . $region . "', 0)"; + } + + if(!in_array($area, $areas)) { + $areas[] = $area; + $values[] = "('Timezone.Area', '" . $area . "', 1, 0, 0, '" . $area . "', 0)"; + } + } + + $sql = " + INSERT INTO + core_enum (etype, name, active, seqid, seqmax, display_name, is_system_enum) + VALUES + " . implode(", \n", $values) . " + "; + + $ce = DB_DataObject::factory('core_enum'); + $ce->query($sql); + + $this->jok('DONE'); + } +} \ No newline at end of file diff --git a/JsCompile.php b/JsCompile.php index a5b91590..089db58d 100644 --- a/JsCompile.php +++ b/JsCompile.php @@ -199,50 +199,18 @@ class Pman_Core_JsCompile extends Pman function packCssCore($files, $output) { - - $o = HTML_FlexyFramework::get()->Pman_Core; - - if (empty($o['cssminify']) || !file_exists($o['cssminify'])) { - echo ''; - return false; - } - require_once 'System.php'; + - $seed= System::which('seed'); - $gjs = System::which('gjs'); + echo ''; + return false; + // if we did.. use this? - if (!$seed && !$gjs) { - echo ''; - return false; + //require_once 'HTML/CSS/Minify.php'; + //$x = new HTML_CSS_Minify(substr($relpath,0,-1), $dir, $relfiles); - } - $targetm = file_exists($output) ? filemtime($output) : 0; - $max = 0; - $ofiles = array(); - foreach($files as $f => $mt) { - $max = max($max,$mt); - $ofiles[] = escapeshellarg($f); - } - if ($max < $targetm) { - return true; - } - if (!file_exists(dirname($output))) { - mkdir(dirname($output), 0755, true); - } - $eoutput = escapeshellarg($output); - $cmd = $seed ? - ("$seed {$o['cssminify']} $eoutput " . implode($ofiles, ' ')) : - ("$gjs {$o['cssminify']} -- -- $eoutput " . implode($ofiles, ' ')); - //echo "
$cmd\n"; echo `$cmd`; exit;
-        `$cmd`;
+          //  file_put_contents($compiledir.'/'.$output , $x->minify( $this->baseURL.$asset));
         
         
-        // we should do more checking.. return val etc..
-        if (file_exists($output) && ($max < filemtime($output) ) ) {
-            return true;
-        }
-        return false;
-        
     }
     /**
      * wrapper arround packer...
@@ -260,10 +228,7 @@ class Pman_Core_JsCompile  extends Pman
             return false;
         }
         
-        $o = HTML_FlexyFramework::get()->Pman_Core;
-        if (isset($o['packseed'])) {
-            return $this->packSeed($files,$output,$translation_base);
-        }
+        // if packer is running, then dont compile - just output onebyone...
         
         
         require_once 'System.php';
@@ -287,6 +252,23 @@ class Pman_Core_JsCompile  extends Pman
             return true;
         }
         
+        
+        
+        $pg = System::which('pgrep');
+        if ($pg == '') {
+            echo '';
+            return false;
+        }
+        
+        $cmd = "$pg roojspacker";
+        $res = `$cmd`;
+        $out = empty($res) ? '' : trim($res);
+        if (strlen($out) > 0) {
+            echo '';
+            return false;
+        }
+         
+        
         if (file_exists($output)) {
             unlink($output);
         }
@@ -342,6 +324,15 @@ class Pman_Core_JsCompile  extends Pman
         
     }
     
+    
+    function packIsRunning()
+    {
+        require_once 'System.php';
+      
+        
+    }
+    
+    
     // depricated verison using seed.
     function packSeed($files, $output, $translation_base=false)
     {
diff --git a/JsTemplate.php b/JsTemplate.php
index ec79d0c4..302b1c6d 100644
--- a/JsTemplate.php
+++ b/JsTemplate.php
@@ -65,7 +65,7 @@ class Pman_Core_JsTemplate extends Pman {
          
             $dir =  $this->rootDir .'/'.$pdir .  $mod . '/jtemplates';
             if (!file_exists($dir)) {
-                echo '// missing directory '. htmlspecialchars($dir) ."\n";
+                //echo '// missing directory '. htmlspecialchars($dir) ."\n";
                 continue;
             }
             // got a directory..
diff --git a/RooJsonOutputTrait.php b/JsonOutputTrait.php
similarity index 62%
rename from RooJsonOutputTrait.php
rename to JsonOutputTrait.php
index c307d27d..394eaf85 100644
--- a/RooJsonOutputTrait.php
+++ b/JsonOutputTrait.php
@@ -1,6 +1,8 @@
 cli;
-        
         if ($cli) {
             echo "OK: " .$str . "\n";
             exit;
         }
-        require_once 'Services/JSON.php';
-        $json = new Services_JSON();
         
         $retHTML = isset($_SERVER['CONTENT_TYPE']) && 
                 preg_match('#multipart/form-data#i', $_SERVER['CONTENT_TYPE']);
@@ -33,37 +32,94 @@ trait Pman_Core_RooJsonOutputTrait {
             echo "";
             // encode html characters so they can be read..
             echo  str_replace(array('<','>'), array('\u003c','\u003e'),
-                        $json->encodeUnsafe(array('success'=> true, 'data' => $str)));
+                        $this->jsencode(array('success'=> true, 'data' => $str), false));
             echo "";
             exit;
         }
         
         
-        echo  $json->encode(array('success'=> true, 'data' => $str));
+        echo  $this->jsencode(array('success'=> true, 'data' => $str),true);
         
         exit;
+        
     }
     
     
-    function jerr($str, $errors=array(), $content_type = false)
+      /**
+     * ---------------- Standard JSON outputers. - used everywhere
+     * JSON error - simple error with logging.
+     * @see Pman::jerror
+     */
+    
+    function jerr($str, $errors=array(), $content_type = false) // standard error reporting..
     {
-        if ($this->transObj) {
-            $this->transObj->query('ROLLBACK');
-        }
-        
         return $this->jerror('ERROR', $str,$errors,$content_type);
     }
     
+    function jnotice($type, $str, $errors=array(), $content_type = false)
+    {
+        return $this->jerror('NOTICE-' . $type, $str, $errors, $content_type);
+    }
+    
+     /**
+     * jerrAuth: standard auth failure - with data that let's the UI know..
+     */
+    function jerrAuth()
+    {
+        $au = $this->authUser;
+        if ($au) {
+            // is it an authfailure?
+            $this->jerror("LOGIN-NOPERM", "Permission denied to view this resource", array('authFailure' => true));
+        }
+        $this->jerror("LOGIN-NOAUTH", "Not authenticated", array('authFailure' => true));
+    }
+     
+     
+    /**
+     * Recomended JSON error indicator
+     *
+     * 
+     * @param string $type  - normally 'ERROR' - you can use this to track error types.
+     * @param string $message - error message displayed to user.
+     * @param array $errors - optioanl data to pass to front end.
+     * @param string $content_type - use text/plain to return plan text - ?? not sure why...
+     *
+     */
+    
     function jerror($type, $str, $errors=array(), $content_type = false) // standard error reporting..
     {
-        if ($type !== false) {
+        if ($this->transObj) {
+            $this->transObj->query('ROLLBACK');
+        }
+        
+        $cli = HTML_FlexyFramework::get()->cli;
+        if ($cli) {
+            echo "ERROR: " .$str . "\n"; // print the error first, as DB might fail..
+        }
+        $pman = HTML_FlexyFramework::get();
+        
+       
+        
+
+        
+        if ($type !== false  &&  empty($pman->nodatabase)) {
+            
+            if(!empty($errors)){
+                DB_DataObject::factory('Events')->writeEventLogExtra($errors);
+            }
+            // various codes that are acceptable.
+            // 
+            if (!preg_match('/^(ERROR|NOTICE|LOG)/', $type )) {
+                $type = 'ERROR-' . $type;
+            }
+            
             $this->addEvent($type, false, $str);
+            
         }
          
         $cli = HTML_FlexyFramework::get()->cli;
         if ($cli) {
-            echo "ERROR: " .$str . "\n";
-            exit;
+            exit(1); // cli --- exit code to stop shell execution if necessary.
         }
         
         
@@ -74,8 +130,7 @@ trait Pman_Core_RooJsonOutputTrait {
             exit;
         } 
         
-        require_once 'Services/JSON.php';
-        $json = new Services_JSON();
+     // log all errors!!!
         
         $retHTML = isset($_SERVER['CONTENT_TYPE']) && 
                 preg_match('#multipart/form-data#i', $_SERVER['CONTENT_TYPE']);
@@ -88,17 +143,18 @@ trait Pman_Core_RooJsonOutputTrait {
             $retHTML = isset($_REQUEST['returnHTML']) && $_REQUEST['returnHTML'] !='NO';
         }
         
+        
         if ($retHTML) {
             header('Content-type: text/html');
             echo "";
-            echo  $json->encodeUnsafe(array(
+            echo  $this->jsencode(array(
                     'success'=> false, 
                     'errorMsg' => $str,
                     'message' => $str, // compate with exeption / loadexception.
 
                     'errors' => $errors ? $errors : true, // used by forms to flag errors.
                     'authFailure' => !empty($errors['authFailure']),
-                ));
+                ), false);
             echo "";
             exit;
         }
@@ -116,19 +172,33 @@ trait Pman_Core_RooJsonOutputTrait {
                 
         }
         
-        echo $json->encode(array(
+        echo $this->jsencode(array(
             'success'=> false, 
-            'data'=> array(), 
+            'data'=> array(),
+            'code' => $type,
             'errorMsg' => $str,
             'message' => $str, // compate with exeption / loadexception.
             'errors' => $errors ? $errors : true, // used by forms to flag errors.
             'authFailure' => !empty($errors['authFailure']),
-        ));
+        ),true);
+        
         
         exit;
         
     }
     
+     
+   
+    
+    
+    
+    /**
+     * output data for grids or tree
+     * @ar {Array} ar Array of data
+     * @total {Number|false} total number of records (or false to return count(ar)
+     * @extra {Array} extra key value list of data to pass as extra data.
+     * 
+     */
     function jdata($ar,$total=false, $extra=array(), $cachekey = false)
     {
         // should do mobile checking???
@@ -136,8 +206,7 @@ trait Pman_Core_RooJsonOutputTrait {
             $total = count($ar);
         }
         $extra=  $extra ? $extra : array();
-        require_once 'Services/JSON.php';
-        $json = new Services_JSON();
+        
         
         $retHTML = isset($_SERVER['CONTENT_TYPE']) && 
                 preg_match('#multipart/form-data#i', $_SERVER['CONTENT_TYPE']);
@@ -153,11 +222,11 @@ trait Pman_Core_RooJsonOutputTrait {
         if ($retHTML) {
             
             header('Content-type: text/html');
-            echo "";
+            echo "'), array('\u003c','\u003e'),
-                        $json->encodeUnsafe(array('success' =>  true, 'total'=> $total, 'data' => $ar) + $extra));
-            echo "";
+                        $this->jsencode(array('success' =>  true, 'total'=> $total, 'data' => $ar) + $extra, false));
+            echo "]]>";
             exit;
         }
         
@@ -181,7 +250,7 @@ trait Pman_Core_RooJsonOutputTrait {
         }
         
       
-        $ret =  $json->encode(array('success' =>  true, 'total'=> $total, 'data' => $ar) + $extra);  
+        $ret =  $this->jsencode(array('success' =>  true, 'total'=> $total, 'data' => $ar) + $extra,true);  
         
         if (!empty($cachekey)) {
             
@@ -191,10 +260,13 @@ trait Pman_Core_RooJsonOutputTrait {
             }
             file_put_contents($fn, $ret);
         }
+        
         echo $ret;
         exit;
     }
     
+    
+   
     /** a daily cache **/
     function jdataCache($cachekey)
     {
@@ -207,4 +279,24 @@ trait Pman_Core_RooJsonOutputTrait {
         return false;
         
     }
+    
+     function jsencode($v, $header = false)
+    {
+        if ($header) {
+            header("Content-type: text/javascript");
+        }
+        if (function_exists("json_encode")) {
+            $ret=  json_encode($v);
+            if ($ret !== false) {
+                return $ret;
+            }
+        }
+        require_once 'Services/JSON.php';
+        $js = new Services_JSON();
+        return $js->encodeUnsafe($v);
+        
+        
+        
+    }
+    
 }
\ No newline at end of file
diff --git a/Lock.php b/Lock.php
index b0e7ea9a..661381fa 100644
--- a/Lock.php
+++ b/Lock.php
@@ -40,7 +40,7 @@ class Pman_Core_Lock extends Pman
     {
          $au = $this->getAuthUser();
         if (!$au) {
-             $this->jerr("Not authenticated", array('authFailure' => true));
+             $this->jerror("LOGIN-NOAUTH", "Not authenticated", array('authFailure' => true));
         }
         $this->authUser = $au;
         // check that it's a supplier!!!! 
@@ -77,7 +77,7 @@ class Pman_Core_Lock extends Pman
             $this->jok("No lock exists"); // been deleted before.. probably ok..
         }
         
-        if ($curlock->person_id != $this->authUser->id) {
+        if ($curlock->person_id != $this->authUser->id && empty($_REQUEST['force'])) {
             // this is an error conditon..
             $this->jerr("Lock id is invalid");
         }
diff --git a/Mailer.php b/Mailer.php
index 2b3c2746..a79169f3 100644
--- a/Mailer.php
+++ b/Mailer.php
@@ -65,7 +65,7 @@
 class Pman_Core_Mailer {
     var $debug          = 0;
     var $page           = false; /* usually a html_flexyframework_page */
-    var $contents       = false; /* object or array */
+    var $contents       = array(); /* object or array */
     var $template       = false; /* string */
     var $replaceImages  = false; /* boolean */
     var $rcpts   = false;
@@ -73,6 +73,8 @@ class Pman_Core_Mailer {
     var $locale = false; // eg. 'en' or 'zh_HK'
     var $urlmap = array();
     
+    var $htmlbody;
+    var $textbody;
     
     var $html_locale = false; // eg. 'en' or 'zh_HK'
     var $images         = array(); // generated list of cid images for sending
@@ -105,7 +107,12 @@ class Pman_Core_Mailer {
     }
      
     /**
-     * ---------------- Global Tools ---------------   
+     * ---------------- Global Tools ---------------
+     *
+     * applies this variables to a object
+     * msgid
+     * HTTP_HOIST
+     * 
      */
     
     function toData()
@@ -120,13 +127,15 @@ class Pman_Core_Mailer {
         
         $content->msgid = empty($content->msgid ) ? md5(time() . rand()) : $content->msgid ;
         
+        // content can override this now
         $ff = HTML_FlexyFramework::get();
         $http_host = isset($_SERVER["HTTP_HOST"]) ? $_SERVER["HTTP_HOST"] : 'pman.HTTP_HOST.not.set';
         if (isset($ff->Pman['HTTP_HOST']) && $http_host != 'localhost') {
             $http_host  = $ff->Pman['HTTP_HOST'];
         }
-        
-        $content->HTTP_HOST = $http_host;
+        if (empty($content->HTTP_HOST )) {
+            $content->HTTP_HOST = $http_host;
+        }
         
         // this should be done by having multiple template sources...!!!
         
@@ -137,16 +146,23 @@ class Pman_Core_Mailer {
             'site_prefix' => false,
             'multiSource' => true,
         );
-        if (!empty($this->templateDir)) {
-            $tmp_opts['templateDir'] = $this->templateDir;
-        }
+        
         $fopts = HTML_FlexyFramework::get()->HTML_Template_Flexy;
+        
+        //print_R($fopts);exit;
         if (!empty($fopts['DB_DataObject_translator'])) {
             $tmp_opts['DB_DataObject_translator'] = $fopts['DB_DataObject_translator'];
         }
         if (!empty($fopts['locale'])) {
             $tmp_opts['locale'] = $fopts['locale'];
         }
+        if (!empty($fopts['templateDir'])) {
+            $tmp_opts['templateDir'] = $fopts['templateDir'];
+        }
+        // override.
+        if (!empty($this->templateDir)) {
+            $tmp_opts['templateDir'] = $this->templateDir;
+        }
         
         // local opt's overwrite
         if (!empty($this->locale)) {
@@ -189,12 +205,13 @@ class Pman_Core_Mailer {
             
         }
         $tmp_opts['nonHTML'] = true;
+        //$tmp_opts['debug'] = true;
         
-        
-        //print_R($tmp_opts);
+        // print_R($tmp_opts);
         // $tmp_opts['force'] = true;
         
         $template = new HTML_Template_Flexy(  $tmp_opts );
+        
         $template->compile('mail/'. $templateFile.'.txt');
         
         /* use variables from this object to ouput data. */
@@ -241,7 +258,9 @@ class Pman_Core_Mailer {
                 unset($parts[1]['Content-Type']);
             }
             $mime->setTXTBody($parts[2]);
+            $this->textbody = $parts[2];
             $mime->setHTMLBody($htmlbody);
+            
 //            var_dump($mime);exit;
             foreach($this->images as $cid=>$cdata) { 
             
@@ -269,8 +288,10 @@ class Pman_Core_Mailer {
                 if(preg_match('/text\/html/', $header['Content-Type'])){
                     $mime->setHTMLBody($parts[2]);
                     $mime->setTXTBody('This message is in HTML only');
+                    $this->textbody = 'This message is in HTML only';
                 }else{
                     $mime->setTXTBody($parts[2]);
+                    $this->textbody = $parts[2];
                     $mime->setHTMLBody('
'.htmlspecialchars($parts[2]).'
'); } } @@ -365,6 +386,12 @@ class Pman_Core_Mailer { } $oe = error_reporting(E_ALL & ~E_NOTICE & ~E_STRICT); + if ($this->debug) { + print_r(array( + 'rcpts' => $rcpts, + 'email' => $email + )); + } $ret = $mail->send($rcpts,$email['headers'],$email['body']); error_reporting($oe); if ($ret === true) { diff --git a/MessagePreview.php b/MessagePreview.php index 6a7e94fe..384cdbd9 100644 --- a/MessagePreview.php +++ b/MessagePreview.php @@ -6,6 +6,10 @@ class Pman_Core_MessagePreview extends Pman { var $masterTemplate = 'mail/MessagePreview.html'; + + var $showHtml; + var $msg; + function getAuth() { if (HTML_FlexyFramework::get()->cli) { @@ -21,17 +25,59 @@ class Pman_Core_MessagePreview extends Pman function get($v, $opts=array()) { - if(empty($_REQUEST['_id']) || empty($_REQUEST['_table'])){ + if((empty($_REQUEST['_id']) && empty($_REQUEST['template_name']) )|| empty($_REQUEST['_table'])){ $this->jerr('Missing Options'); } $mlq = DB_DataObject::factory($_REQUEST['_table']); + if (!empty($_REQUEST['template_name'])) { + $res = $mlq->get('name', $_REQUEST['template_name']); + } else { + $res = $mlq->get($_REQUEST['_id']); + } + if (!$res) { + $this->jerr("invalid id/name"); + } - $mlq->get($_REQUEST['_id']); + $this->showHtml = isset($_REQUEST['_as_html']) ? true : false; - $this->msg = $mlq; + + if (isset($_REQUEST['ontable']) && !empty($_REQUEST['onid']) && !empty($_REQUEST['evtype'])) { + $tn = preg_replace('/[^a-z_]+/i', '', $_REQUEST['ontable']); + + $t = DB_DataObject::factory($tn); + if (!is_a($t, 'DB_DataObject') && !is_a($t, 'PDO_DataObject')) { + $this->jerr("invalid URL"); + } + if (!$t->get($_REQUEST['onid'])) { + $this->jerr("invalid id"); + } + if (!method_exists($t,'notify'.$_REQUEST['evtype'])) { + $this->jerr("invalid evtype"); + } + + $m = 'notify'.$_REQUEST['evtype']; + $this->msg = (object)$t->$m('test@test.com', false, false, false); + // print_R($this->msg->mailer ); + $this->msg->subject = $this->msg->headers['Subject']; + $this->msg->from_email = $mlq->from_email; + $this->msg->from_name = $mlq->from_name; + $this->msg->plaintext = $this->msg->mailer->textbody ; + $this->msg->bodytext = $this->msg->mailer->htmlbody; + $this->msg->rcpts = $this->msg->mailer->rcpts; + // htmlbody + //$this->plaintext = + //$data->subject = $data['Subject; + + + - $this->showHtml = isset($_REQUEST['_as_html']) ? true : false; + return; + } + + $this->msg = $mlq; + $this->msg->rcpts = "send to "; + } diff --git a/Notify.php b/Notify.php index fd582092..3fd4c5b4 100644 --- a/Notify.php +++ b/Notify.php @@ -97,22 +97,49 @@ class Pman_Core_Notify extends Pman var $max_to_domain = 10; /** - * @var $maxruntime - maximum time a child is allowed to run - defaut 2 minutes + * @var $maxruntime - maximum seconds a child is allowed to run - defaut 2 minutes */ var $maxruntime = 120; + /** + * @var {Boolean} log_events - default true if events should be logged. + */ + var $log_events = true; + /** + * @var {Number} try_again_minutes how long after failing to try again default = 30 if max runtime fails + */ + var $try_again_minutes = 30; + + /** + * @var {String} table - the table that the class will query for notification events + */ var $table = 'core_notify'; + /** + * @var {String} target - the application that will run for each Row in the table (eg. Pman/Core/NotifySend) + */ var $target = 'Core/NotifySend'; + + + var $evtype = ''; // any notification... // this script should only handle EMAIL notifications.. + + var $server; // core_notify_server + + var $poolname = 'core'; + + var $opts; var $force = false; + + var $clear_interval = '1 WEEK'; // how long to clear the old queue of items. + function getAuth() { $ff = HTML_FlexyFramework::get(); if (!$ff->cli) { die("access denied"); } - HTML_FlexyFramework::ensureSingle($_SERVER["SCRIPT_NAME"] .'|'. __FILE__, $this); + HTML_FlexyFramework::ensureSingle($_SERVER["SCRIPT_NAME"] .'|'. __FILE__ .'|'. (empty($_SERVER['argv'][1]) ? '': $_SERVER['argv'][1]), $this); return true; } @@ -144,9 +171,19 @@ class Pman_Core_Notify extends Pman } } - + var $queue = array(); + var $domain_queue = array(); // false to use nextquee + var $next_queue = array(); + + function get($r,$opts=array()) { + + // if ($this->database_is_locked()) { + // die("LATER - DATABASE IS LOCKED"); + //} + + $this->parseArgs($opts); //date_default_timezone_set('UTC'); @@ -154,11 +191,30 @@ class Pman_Core_Notify extends Pman $this->generateNotifications(); - - //DB_DataObject::debugLevel(1); + //DB_DataObject::debugLevel(1); $w = DB_DataObject::factory($this->table); $total = 0; + + + $ff = HTML_FlexyFramework::get(); + + + $this->server = DB_DataObject::Factory('core_notify_server')->getCurrent($this); + + $this->server->assignQueues($this); + + + $this->clearOld(); + + + if (!empty($this->evtype)) { + $w->evtype = $this->evtype; + } + + $w->server_id = $this->server->id; + + if (!empty($opts['old'])) { // show old and new... @@ -175,6 +231,7 @@ class Pman_Core_Notify extends Pman if (!$this->force) { $w->whereAdd('act_when < NOW()'); // eg.. not if future.. } + $w->orderBy('act_when ASC'); // oldest first. $total = min($w->count(), $opts['limit']); @@ -183,14 +240,18 @@ class Pman_Core_Notify extends Pman $w->limit($opts['limit']); // we can run 1000 ... } - if (!empty($this->evtype)) { - $w->evtype = $this->evtype; - } + + + $w->autoJoin(); - $w->find(); + $total = $w->find(); + + if (empty($total)) { + $this->logecho("Nothing In Queue - DONE"); + exit; + } - $ar = array(); // $w->fetchAll(); if (!empty($opts['list'])) { @@ -206,46 +267,59 @@ class Pman_Core_Notify extends Pman } //echo "BATCH SIZE: ". count($ar) . "\n"; - $pushed = array(); - $requeue = array(); + + while (true) { - if ($w->fetch()) { - $ar[] = clone($w); + // only add if we don't have any queued up.. + if (empty($this->queue) && $w->fetch()) { + $this->queue[] = clone($w); $total--; } + + $this->logecho("BATCH SIZE: Queue=". count($this->queue) . " TOTAL = " . $total ); - $this->logecho("BATCH SIZE: ". (count($ar) + $total) ); - - if (empty($ar)) { - $this->logecho("COMPLETED MAIN QUEUE - running deleted"); - - if (empty($pushed)) { - break; + if (empty($this->queue)) { + $this->logecho("COMPLETED MAIN QUEUE - running maxed out domains"); + if ($this->domain_queue !== false) { + $this->queue = $this->remainingDomainQueue(); + + continue; } - $ar = $pushed; - $pushed = false; - continue; + break; // nothing more in queue.. and no remaining one } - $p = array_shift($ar); + $p = array_shift($this->queue); if (!$this->poolfree()) { - array_unshift($ar,$p); /// put it back on.. + array_unshift($this->queue,$p); /// put it back on.. sleep(3); continue; } - $email = $p->person() ? $p->person()->email : $p->to_email; + // not sure what happesn if person email and to_email is empty!!? + $email = empty($p->to_email) ? ($p->person() ? $p->person()->email : $p->to_email) : $p->to_email; - if ($this->poolHasDomain($email) > $this->max_to_domain) { - - if ($pushed === false) { - // we only try once to requeue.. - $requeue[] = $p; - continue; + $black = $this->server->isBlacklisted($email); + if ($black !== false) { + $this->logecho("Blacklisted - try giving it to next server"); + if (false === $this->server->updateNotifyToNextServer($p)) { + $ev = $this->addEvent('NOTIFY', $p, 'BLACKLISTED FROM our DB'); + // we dont have an althenative server to update it with. + $this->logecho("Blacklisted - next server did not work - try again in 30 mins"); + $this->server->updateNotifyToNextServer($w, date("Y-m-d H:i:s", strtotime('NOW + 30 MINUTES')),true); + // $this->errorHandler( $ev->remarks); + } - $pushed[] = $p; + continue; + } + + + if ($this->poolHasDomain($email) > $this->max_to_domain) { + // push it to a 'domain specific queue' + $this->logecho("REQUEING - maxed out that domain - {$email}"); + $this->pushQueueDomain($p, $email); + //sleep(3); continue; } @@ -255,26 +329,39 @@ class Pman_Core_Notify extends Pman + } + $this->logecho("REQUEUING all emails that maxed out:" . count($this->next_queue)); + if (!empty($this->next_queue)) { + + foreach($this->next_queue as $p) { + if (false === $this->server->updateNotifyToNextServer($p)) { + $p->updateState("????"); + } + } } + + $this->logecho("QUEUE COMPLETE - waiting for pool to end"); // we should have a time limit here... while(count($this->pool)) { $this->poolfree(); - sleep(3); + sleep(3); } - foreach($requeue as $p) { - $pp = clone($p); - $p->act_when = $p->sqlValue('NOW + INTERVAL 1 MINUTE'); - $p->update($pp); - - } + $this->logecho("DONE"); exit; } + + + + // this sequentially distributes requeued emails.. - to other servers. (can exclude current one if we have that flagged.) + + + function generateNotifications() { // this should check each module for 'GenerateNotifications.php' class.. @@ -309,9 +396,9 @@ class Pman_Core_Notify extends Pman } + - - function run($id, $email, $cmdOpts="") + function run($id, $email='', $cmdOpts="") { static $renice = false; @@ -321,13 +408,15 @@ class Pman_Core_Notify extends Pman } // phpinfo();exit; - $tnx = tempnam(ini_get('session.save_path'),'stdout'); - unlink($tnx); - $tn = $tnx . '.stdout'; + + + $tn = $this->tempName('stdout', true); + $tne = $this->tempName('stderr', true); $descriptorspec = array( 0 => array("pipe", 'r'), // stdin is a pipe that the child will read from 1 => array("file", $tn, 'w'), // stdout is a pipe that the child will write to - 2 => array("pipe", "w") // stderr is a file to write to + 2 => array("file", $tne, 'w'), // stderr is a file to write to + // 2 => array("pipe", "w") // stderr is a file to write to ); static $php = false; @@ -350,10 +439,11 @@ class Pman_Core_Notify extends Pman $pipe = array(); - $this->logecho("call proc_open $cmd"); + //$this->logecho("call proc_open $cmd"); if ($this->max_pool_size === 1) { + $this->logecho("call passthru [{$email}] $cmd"); passthru($cmd); return; } @@ -375,6 +465,7 @@ class Pman_Core_Notify extends Pman 'proc' => $p, 'pid' => $info['pid'], 'out' => $tn, + 'oute' => $tne, 'cmd' => $cmd, 'email' => $email, 'pipes' => $pipes, @@ -383,7 +474,7 @@ class Pman_Core_Notify extends Pman ); - $this->logecho("RUN ({$info['pid']}) $cmd "); + $this->logecho("RUN [{$email}] ({$info['pid']}) $cmd "); } function poolfree() @@ -394,6 +485,8 @@ class Pman_Core_Notify extends Pman foreach($this->pool as $p) { //echo "CHECK PID: " . $p['pid'] . "\n"; + + $info = proc_get_status($p['proc']); //var_dump($info); @@ -415,18 +508,21 @@ class Pman_Core_Notify extends Pman proc_terminate($p['proc'], 9); //fclose($p['pipes'][1]); fclose($p['pipes'][0]); - fclose($p['pipes'][2]); - $this->logecho("TERMINATING: ({$p['pid']}) " . $p['cmd'] . " : " . file_get_contents($p['out'])); + + $this->logecho("TERMINATING: ({$p['pid']}) {$p['email']} " . $p['cmd'] . " : " . file_get_contents($p['out']) . " : " . file_get_contents($p['oute'])); @unlink($p['out']); + @unlink($p['oute']); + // schedule again $w = DB_DataObject::factory($this->table); $w->get($p['notify_id']); $ww = clone($w); - $this->addEvent('NOTIFY', $w, 'TERMINATED - TIMEOUT'); - $w->act_when = date('Y-m-d H:i:s', strtotime('NOW + 30 MINUTES')); + if ($this->log_events) { + $this->addEvent('NOTIFY', $w, 'TERMINATED - TIMEOUT'); + } + $w->act_when = date('Y-m-d H:i:s', strtotime("NOW + {$this->try_again_minutes} MINUTES")); $w->update($ww); - continue; } @@ -434,20 +530,28 @@ class Pman_Core_Notify extends Pman continue; } fclose($p['pipes'][0]); - fclose($p['pipes'][2]); //echo "CLOSING: ({$p['pid']}) " . $p['cmd'] . " : " . file_get_contents($p['out']) . "\n"; //fclose($p['pipes'][1]); proc_close($p['proc']); - - + sleep(1); + clearstatcache(); + if (file_exists('/proc/'. $p['pid'])) { + $this->logecho("proc PID={$p['pid']} still here - trying to wait"); + pcntl_waitpid($p['pid'], $status, WNOHANG); + } + //clearstatcache(); //if (file_exists('/proc/'.$p['pid'])) { // $pool[] = $p; // continue; //} - $this->logecho("ENDED: ({$p['pid']}) " . $p['cmd'] . " : " . file_get_contents($p['out']) ); + $this->logecho("ENDED: ({$p['pid']}) {$p['email']} " . $p['cmd'] . " : " . file_get_contents($p['out']) . " : " . file_get_contents($p['oute'])); @unlink($p['out']); + @unlink($p['oute']); + // at this point we could pop onto the queue the + $this->popQueueDomain($p['email']); + //unlink($p['out']); } $this->logecho("POOL SIZE: ". count($pool) ); @@ -466,9 +570,11 @@ class Pman_Core_Notify extends Pman function poolHasDomain($email) { $ret = 0; - $dom = strtolower(array_pop(explode('@',$email))); + $ea = explode('@',$email); + $dom = strtolower(array_pop($ea)); foreach($this->pool as $p) { - $mdom = strtolower(array_pop(explode('@',$p['email']))); + $ea = explode('@',$p['email']); + $mdom = strtolower(array_pop($ea)); if ($mdom == $dom) { $ret++; } @@ -476,6 +582,77 @@ class Pman_Core_Notify extends Pman return $ret; } + function popQueueDomain($email) + { + $ea = explode('@',$email); + $dom = strtolower(array_pop($ea)); + if (empty($this->domain_queue[$dom])) { + return; + } + array_unshift($this->queue, array_shift($this->domain_queue[$dom])); + + } + + function pushQueueDomain($e, $email) + { + if ($this->domain_queue === false) { + $this->next_queue[] = $e; + return; + } + + $ea = explode('@',$email); + $dom = strtolower(array_pop($ea)); + if (!isset($this->domain_queue[$dom])) { + $this->domain_queue[$dom] = array(); + } + $this->domain_queue[$dom][] = $e; + } + function remainingDomainQueue() + { + $ret = array(); + foreach($this->domain_queue as $dom => $ar) { + $ret = array_merge($ret, $ar); + } + $this->domain_queue = false; + return $ret; + } + function clearOld() + { + if ($this->server->isFirstServer()) { + + $p = DB_DataObject::factory($this->table); + $p->whereAdd(" + sent < '2000-01-01' + and + event_id = 0 + and + act_start < NOW() - INTERVAL {$this->clear_interval} + "); + // $p->limit(1000); + if ($p->count()) { + $ev = $this->addEvent('NOTIFY', false, "RETRY TIME EXCEEDED"); + $p = DB_DataObject::factory($this->table); + $p->query(" + UPDATE + {$this->table} + SET + sent = NOW(), + msgid = '', + event_id = {$ev->id} + WHERE + sent < '2000-01-01' + and + event_id = 0 + and + act_start < NOW() - INTERVAL {$this->clear_interval} + LIMIT + 1000 + "); + + } + } + } + function output() { @@ -486,4 +663,4 @@ class Pman_Core_Notify extends Pman { echo date("Y-m-d H:i:s - ") . $str . "\n"; } -} \ No newline at end of file +} diff --git a/NotifyAction.php b/NotifyAction.php index e3b9af5c..968eace3 100644 --- a/NotifyAction.php +++ b/NotifyAction.php @@ -16,7 +16,7 @@ class Pman_Core_NotifyAction extends Pman { $au = $this->getAuthUser(); if (!$au) { - $this->jerr("Not authenticated", array('authFailure' => true)); + $this->jerror("LOGIN-NOAUTH", "Not authenticated", array('authFailure' => true)); } // workflow only applicable to owner company.. if ($au->company()->comptype != 'OWNER') { diff --git a/NotifySend.php b/NotifySend.php index 91afa5a1..f94ff33d 100644 --- a/NotifySend.php +++ b/NotifySend.php @@ -71,6 +71,8 @@ class Pman_Core_NotifySend extends Pman ); var $table = 'core_notify'; var $error_handler = 'die'; + var $poolname = 'core'; + var $server; // core_notify_server function getAuth() { @@ -85,7 +87,9 @@ class Pman_Core_NotifySend extends Pman function get($id,$opts=array()) { - + //if ($this->database_is_locked()) { + // die("LATER - DATABASE IS LOCKED"); + // } //print_r($opts); if (!empty($opts['DB_DataObject-debug'])) { DB_DataObject::debugLevel($opts['DB_DataObject-debug']); @@ -101,11 +105,18 @@ class Pman_Core_NotifySend extends Pman if (!$w->get($id)) { $this->errorHandler("invalid id\n"); } - if (!$force && strtotime($w->act_when) < strtotime($w->sent)) { - - - $this->errorHandler("send repeat to early\n"); + + if (!$force && !empty($w->sent) && strtotime($w->act_when) < strtotime($w->sent)) { + + $this->errorHandler("already sent - repeat to early\n"); } + + $this->server = DB_DataObject::Factory('core_notify_server')->getCurrent($this, $force); + if (!$force && $w->server_id != $this->server->id) { + $this->errorHandler("Server id does not match - use force to try again\n"); + } + + if (!empty($opts['debug'])) { print_r($w); $ff = HTML_FlexyFramework::get(); @@ -115,31 +126,37 @@ class Pman_Core_NotifySend extends Pman HTML_FlexyFramework::get()->Core_Mailer['debug'] = true; } - $sent = (empty($w->sent) || preg_match('/^0000/', $w->sent)) ? false : true; + $sent = (empty($w->sent) || strtotime( $w->sent) < 100 ) ? false : true; if (!$force && (!empty($w->msgid) || $sent)) { $ww = clone($w); - if (!$sent) { - $w->sent = $w->sent == '0000-00-00 00:00:00' ? $w->sqlValue('NOW()') :$w->sent; // do not update if sent..... + if (!$sent) { // fix sent. + $w->sent = strtotime( $w->sent) < 100 ? $w->sqlValue('NOW()') :$w->sent; // do not update if sent..... $w->update($ww); } $this->errorHandler("message has been sent already.\n"); } + // we have a bug with msgid not getting filled. + $cev = DB_DataObject::Factory('Events'); + $cev->on_table = $this->table; + $cev->on_id = $w->id; + $cev->whereAdd("action IN ('NOTIFYSENT', 'NOTIFYFAIL')"); + $cev->limit(1); + if ($cev->count()) { + $cev->find(true); + $w->flagDone($cev, $cev->action == 'NOTIFYSENT' ? 'alreadysent' : ''); + $this->errorHandler( $cev->action . " (fix old) ". $cev->remarks); + } + + $o = $w->object(); if ($o === false) { - - $ev = $this->addEvent('NOTIFY', $w, - "Notification event cleared (underlying object does not exist)" );; - $ww = clone($w); - $w->sent = $w->sent == '0000-00-00 00:00:00' ? $w->sqlValue('NOW()') :$w->sent; // do not update if sent..... - $w->msgid = ''; - $w->event_id = $ev->id; - $w->update($ww); - $this->errorHandler(date('Y-m-d h:i:s ') . - "Notification event cleared (underlying object does not exist)" - ."\n"); + + $ev = $this->addEvent('NOTIFY', $w, "Notification event cleared (underlying object does not exist)" ); + $w->flagDone($ev, ''); + $this->errorHandler( $ev->remarks); } @@ -147,19 +164,17 @@ class Pman_Core_NotifySend extends Pman $p = $w->person(); if (isset($p->active) && empty($p->active)) { - $ev = $this->addEvent('NOTIFY', $w, - "Notification event cleared (not user not active any more)" );; - $ww = clone($w); - $w->sent = $w->sent == '0000-00-00 00:00:00' ? $w->sqlValue('NOW()') :$w->sent; // do not update if sent..... - $w->msgid = ''; - $w->event_id = $ev->id; - $w->update($ww); - $this->errorHandler(date('Y-m-d h:i:s ') . - "Notification event cleared (not user not active any more)" - ."\n"); - $this->errorHandler("message has been sent already.\n"); + $ev = $this->addEvent('NOTIFY', $w, "Notification event cleared (not user not active any more)" );; + $w->flagDone($ev, ''); + $this->errorHandler( $ev->remarks); } + // has it failed mutliple times.. + if (!empty($w->field) && isset($p->{$w->field .'_fails'}) && $p->{$w->field .'_fails'} > 9) { + $ev = $this->addEvent('NOTIFY', $w, "Notification event cleared (user has to many failures)" );; + $w->flagDone($ev, ''); + $this->errorHandler( $ev->remarks); + } // let's work out the last notification sent to this user.. $l = DB_DataObject::factory($this->table); @@ -169,9 +184,11 @@ class Pman_Core_NotifySend extends Pman 'onid' => $w->onid, ); // only newer version of the database us this.. - $personid_col = strtolower($w->person_table).'_id'; - if (isset($w->{$personid_col})) { - $lar[$personid_col] = $w->{$personid_col}; + if (isset($w->person_table)) { + $personid_col = strtolower($w->person_table).'_id'; + if (isset($w->{$personid_col})) { + $lar[$personid_col] = $w->{$personid_col}; + } } @@ -200,17 +217,9 @@ class Pman_Core_NotifySend extends Pman $email = $this->makeEmail($o, $p, $last, $w, $force); if ($email === true) { - - $ev = $this->addEvent('NOTIFY', $w, - "Notification event cleared (not required any more)" );; - $ww = clone($w); - $w->sent = (!$w->sent || $w->sent == '0000-00-00 00:00:00') ? $w->sqlValue('NOW()') : $w->sent; // do not update if sent..... - $w->msgid = ''; - $w->event_id = $ev->id; - $w->update($ww); - $this->errorHandler(date('Y-m-d h:i:s ') . - "Notification event cleared (not required any more)" - ."\n"); + $ev = $this->addEvent('NOTIFY', $w, "Notification event cleared (not required any more) - toEmail=true" );; + $w->flagDone($ev, ''); + $this->errorHandler( $ev->remarks); } if (is_a($email, 'PEAR_Error')) { $email =array( @@ -227,25 +236,18 @@ class Pman_Core_NotifySend extends Pman if ($email === false || isset($email['error']) || empty($p)) { // object returned 'false' - it does not know how to send it.. - $ev = $this->addEvent('NOTIFY', $w, isset($email['error']) ? - $email['error'] : "INTERNAL ERROR - We can not handle " . $w->ontable); - $ww = clone($w); - $w->sent = (!$w->sent || $w->sent == '0000-00-00 00:00:00') ? $w->sqlValue('NOW()') : $w->sent; // do not update if sent..... - $w->msgid = ''; - $w->event_id = $ev->id; - $w->update($ww); - $this->errorHandler(date('Y-m-d h:i:s ') . - (isset($email['error']) ? - $email['error'] : "INTERNAL ERROR - We can not handle " . $w->ontable) - ."\n"); + $ev = $this->addEvent('NOTIFYFAIL', $w, isset($email['error']) ? $email['error'] : "INTERNAL ERROR - We can not handle " . $w->ontable); + $w->flagDone($ev, ''); + $this->errorHandler( $ev->remarks); } + if (isset($email['later'])) { - $old = clone($w); - $w->act_when = $email['later']; - $w->update($old); - $this->errorHandler(date('Y-m-d h:i:s ') . " Delivery postponed by email creator to {$email['later']}"); + + $this->server->updateNotifyToNextServer($w, $email['later'],true); + + $this->errorHandler("Delivery postponed by email creator to {$email['later']}"); } @@ -272,26 +274,33 @@ class Pman_Core_NotifySend extends Pman // since some of them have spaces?!?! $p->email = trim($p->email); + $ww = clone($w); + $ww->to_email = empty($ww->to_email) ? $p->email : $ww->to_email; + $explode_email = explode('@', $ww->to_email); + $dom = array_pop($explode_email); + + $core_domain = DB_DataObject::factory('core_domain')->loadOrCreate($dom); + + + $ww->domain_id = $core_domain->id; + // if to_email has not been set!? + $ww->update($w); // if nothing has changed this will not do anything. + $w = clone($ww); + require_once 'Validate.php'; if (!Validate::email($p->email, true)) { - $ev = $this->addEvent('NOTIFY', $w, "INVALID ADDRESS: " . $p->email); - $ww = clone($w); - $w->sent = (!$w->sent || $w->sent == '0000-00-00 00:00:00') ? $w->sqlValue('NOW()') : $w->sent; // do not update if sent..... - $w->msgid = ''; - $w->event_id = $ev->id; - $w->update($ww); - $this->errorHandler(date('Y-m-d h:i:s ') . "INVALID ADDRESS: " . $p->email. "\n"); + $ev = $this->addEvent('NOTIFYFAIL', $w, "INVALID ADDRESS: " . $p->email); + $w->flagDone($ev, ''); + $this->errorHandler($ev->remarks); } $ff = HTML_FlexyFramework::get(); - $explode_email = explode('@', $p->email); - $dom = array_pop($explode_email); - + $mxs = $this->mxs($dom); $ww = clone($w); @@ -299,37 +308,33 @@ class Pman_Core_NotifySend extends Pman // need to handle temporary failure.. - // we try for 3 days.. - $retry = 5; + // we try for 2 days.. + $retry = 15; if (strtotime($w->act_start) < strtotime('NOW - 1 HOUR')) { // older that 1 hour. - $retry = 15; + $retry = 60; } if (strtotime($w->act_start) < strtotime('NOW - 1 DAY')) { // older that 1 day. - $retry = 60; + $retry = 120; } if (strtotime($w->act_start) < strtotime('NOW - 2 DAY')) { // older that 1 day. - $retry = 120; + $retry = 240; } - if ($mxs === false) { - // only retry for 2 day son the MX issue.. - if ($retry < 120) { + if (empty($mxs)) { + // only retry for 1 day if the MX issue.. + if ($retry < 240) { $this->addEvent('NOTIFY', $w, 'MX LOOKUP FAILED ' . $dom ); - $w->act_when = date('Y-m-d H:i:s', strtotime('NOW + ' . $retry . ' MINUTES')); - $w->update($ww); - $this->errorHandler(date('Y-m-d h:i:s') . " - MX LOOKUP FAILED\n"); + $w->flagLater(date('Y-m-d H:i:s', strtotime('NOW + ' . $retry . ' MINUTES'))); + $this->errorHandler($ev->remarks); } - $ev = $this->addEvent('NOTIFY', $w, "BAD ADDRESS - BAD DOMAIN - ". $p->email ); - $w->sent = (!$w->sent || $w->sent == '0000-00-00 00:00:00') ? $w->sqlValue('NOW()') : $w->sent; // do not update if sent..... - $w->msgid = ''; - $w->event_id = $ev->id; - $w->update($ww); - $this->errorHandler(date('Y-m-d h:i:s') . " - FAILED - BAD EMAIL - {$p->email} \n"); + $ev = $this->addEvent('NOTIFYFAIL', $w, "BAD ADDRESS - BAD DOMAIN - ". $p->email ); + $w->flagDone($ev, ''); + $this->errorHandler($ev->remarks); } @@ -337,21 +342,19 @@ class Pman_Core_NotifySend extends Pman - if (!$force && strtotime($w->act_start) < strtotime('NOW - 14 DAY')) { - $ev = $this->addEvent('NOTIFY', $w, "BAD ADDRESS - GIVE UP - ". $p->email ); - $w->sent = (!$w->sent || $w->sent == '0000-00-00 00:00:00') ? $w->sqlValue('NOW()') :$w->sent; // do not update if sent..... - $w->msgid = ''; - $w->event_id = $ev->id; - $w->update($ww); - $this->errorHandler(date('Y-m-d h:i:s') . " - FAILED - GAVE UP TO OLD - {$p->email} \n"); + if (!$force && strtotime($w->act_start) < strtotime('NOW - 3 DAY')) { + $ev = $this->addEvent('NOTIFYFAIL', $w, "BAD ADDRESS - GIVE UP - ". $p->email ); + $w->flagDone($ev, ''); + $this->errorHandler( $ev->remarks); } + $retry_when = date('Y-m-d H:i:s', strtotime('NOW + ' . $retry . ' MINUTES')); - - $w->to_email = $p->email; //$this->addEvent('NOTIFY', $w, 'GREYLISTED ' . $p->email . ' ' . $res->toString()); // we can only update act_when if it has not been sent already (only happens when running in force mode..) - $w->act_when = (!$w->act_when || $w->act_when == '0000-00-00 00:00:00') ? date('Y-m-d H:i:s', strtotime('NOW + ' . $retry . ' MINUTES')) : $w->act_when; + // set act when if it's empty... + $w->act_when = (!$w->act_when || $w->act_when == '0000-00-00 00:00:00') ? $retry_when : $w->act_when; + $w->update($ww); $ww = clone($w); @@ -359,20 +362,19 @@ class Pman_Core_NotifySend extends Pman $fail = false; require_once 'Mail.php'; - $core_domain = DB_DataObject::factory('core_domain'); - if(!$core_domain->get('domain', $dom)){ - $core_domain = DB_DataObject::factory('core_domain'); - $core_domain->setFrom(array( - 'domain' => $dom - )); - $core_domain->insert(); + + $this->server->initHelo(); + + if (!isset($ff->Mail['helo'])) { + $this->errorHandler("config Mail[helo] is not set"); } + + $email = DB_DataObject::factory('core_notify_sender')->filterEmail($email, $w); + foreach($mxs as $mx) { - if (!isset($ff->Mail['helo'])) { - $this->errorHandler("config Mail[helo] is not set"); - } + $this->debug_str = ''; $this->debug("Trying SMTP: $mx / HELO {$ff->Mail['helo']}"); $mailer = Mail::factory('smtp', array( @@ -383,76 +385,75 @@ class Pman_Core_NotifySend extends Pman //'debug' => isset($opts['debug']) ? 1 : 0, 'debug' => 1, 'debug_handler' => array($this, 'debugHandler') - )); + )); // if the host is the mail host + it's authenticated add auth details // this normally will happen if you sent Pman_Core_NotifySend['host'] + + if (isset($ff->Mail['host']) && $ff->Mail['host'] == $mx && !empty($ff->Mail['auth'] )) { - $username = $ff->Mail['username'] ; - $password = $ff->Mail['password'] ; + $mailer->auth = true; + $mailer->username = $ff->Mail['username']; + $mailer->password = $ff->Mail['password']; + } + + if(!empty($ff->Core_Notify) && !empty($ff->Core_Notify['routes'])){ - if(!empty($ff->Core_Notify) && !empty($ff->Core_Notify['routes'])){ + // we might want to regex 'office365 as a mx host + foreach ($ff->Core_Notify['routes'] as $server => $settings){ + if(!in_array($dom, $settings['domains'])){ + continue; + } - foreach ($ff->Core_Notify['routes'] as $server => $settings){ - if(!in_array($dom, $settings['domains'])){ - continue; - } - - // what's the minimum timespan.. - if we have 60/hour.. that's 1 every minute. - // if it's newer that '1' minute... - // then shunt it.. - - $seconds = floor((60 * 60) / $settings['rate']); - - $core_notify = DB_DataObject::factory($this->table); - $core_notify->domain_id = $core_domain->id; - $core_notify->whereAdd(" - sent >= NOW() - INTERVAL $seconds SECONDS - "); - - if($core_notify->count()){ - $old = clone($w); - $w->act_when = date("Y-m-d H:i:s", time() + $seconds); - $w->update($old); - $this->errorHandler(date('Y-m-d h:i:s ') . " Too many emails sent by {$dom}"); - } - - // that make's this test obsolete... - /* - - $core_notify = DB_DataObject::factory($this->table); - $core_notify->domain_id = $core_domain->id; - $core_notify->whereAdd(" - sent >= NOW() - INTERVAL 1 HOUR - "); - - if($core_notify->count() >= $settings['rate']){ - $old = clone($w); - $w->act_when = date("Y-m-d H:i:s", strtotime('+1 HOUR')); - $w->update($old); - $this->errorHandler(date('Y-m-d h:i:s ') . " Too many emails sent by {$dom}"); - } - */ - - - - $mailer->host = $server; - $username = $settings['username']; - $password = $settings['password']; + // what's the minimum timespan.. - if we have 60/hour.. that's 1 every minute. + // if it's newer that '1' minute... + // then shunt it.. + + $settings['rate'] = isset( $settings['rate']) ? $settings['rate'] : 360; + + $seconds = floor((60 * 60) / $settings['rate']); + + $core_notify = DB_DataObject::factory($this->table); + $core_notify->domain_id = $core_domain->id; + $core_notify->server_id = $this->server->id; + $core_notify->whereAdd(" + sent >= NOW() - INTERVAL $seconds SECOND + "); + + if($core_notify->count()){ + $this->server->updateNotifyToNextServer( $w , date("Y-m-d H:i:s", time() + $seconds), true); + $this->errorHandler( " Too many emails sent by {$dom} - requeing"); + } + + + + $mailer->host = $server; + $mailer->auth = isset($settings['auth']) ? $settings['auth'] : true; + $mailer->username = $settings['username']; + $mailer->password = $settings['password']; + if (isset($settings['port'])) { + $mailer->port = $settings['port']; + } + if (isset($settings['socket_options'])) { + $mailer->socket_options = $settings['socket_options']; - break; } + + break; } - $mailer->auth = true; - $mailer->username = $username; - $mailer->password = $password; } + + + $res = $mailer->send($p->email, $email['headers'], $email['body']); - + if (is_object($res)) { + $res->backtrace = array(); + } + $this->debug("GOT response to send: ". print_r($res,true)); if ($res === true) { // success.... @@ -462,46 +463,36 @@ class Pman_Core_NotifySend extends Pman $ev = $this->addEvent($successEventName, $w, "{$w->to_email} - {$email['headers']['Subject']}"); $ev->writeEventLog($this->debug_str); + + $w->flagDone($ev, $email['headers']['Message-Id']); - if(strtotime($w->act_when) > strtotime("NOW")){ - $w->act_when = date('Y-m-d H:i:s'); - } - - $w->sent = (!$w->sent || $w->sent == '0000-00-00 00:00:00') ? $w->sqlValue('NOW()') : $w->sent; // do not update if sent..... - $w->msgid = $email['headers']['Message-Id']; - $w->event_id = $ev->id; // sent ok.. - no need to record it.. - $w->domain_id = $core_domain->id; - $w->update($ww); - + // enable cc in notify.. if (!empty($email['headers']['Cc'])) { $cmailer = Mail::factory('smtp', isset($ff->Mail) ? $ff->Mail : array() ); $email['headers']['Subject'] = "(CC): " . $email['headers']['Subject']; - $cmailer->send($email['headers']['Cc'], - $email['headers'], $email['body']); + $cmailer->send($email['headers']['Cc'], $email['headers'], $email['body']); } if (!empty($email['bcc'])) { $cmailer = Mail::factory('smtp', isset($ff->Mail) ? $ff->Mail : array() ); $email['headers']['Subject'] = "(CC): " . $email['headers']['Subject']; - $res = $cmailer->send($email['bcc'], - $email['headers'], $email['body']); + $res = $cmailer->send($email['bcc'], $email['headers'], $email['body']); if (!$res || is_a($res, 'PEAR_Error')) { echo "could not send bcc..\n"; } else { echo "Sent BCC to {$email['bcc']}\n"; } } - - - $this->errorHandler(date('Y-m-d h:i:s') . " - SENT {$w->id} - {$w->to_email} \n", true); + + $this->errorHandler( " SENT {$w->id} - {$ev->remarks}", true); } // what type of error.. $code = empty($res->userinfo['smtpcode']) ? -1 : $res->userinfo['smtpcode']; if (!empty($res->code) && $res->code == 10001) { // fake greylist if timed out. - $code = 421; + $code = -1; } if ($code < 0) { @@ -518,51 +509,67 @@ class Pman_Core_NotifySend extends Pman $errmsg= $res->userinfo['smtpcode'] . ':' . $res->userinfo['smtptext']; } //print_r($res); - $this->addEvent('NOTIFY', $w, 'GREYLISTED - ' . $errmsg); - $w->act_when = date('Y-m-d H:i:s', strtotime('NOW + ' . $retry . ' MINUTES')); - $w->domain_id = $core_domain->id; - $w->update($ww); + $ev = $this->addEvent('NOTIFY', $w, 'GREYLISTED - ' . $errmsg); + $this->server->updateNotifyToNextServer($w, $retry_when,true); - $this->errorHandler(date('Y-m-d h:i:s') . " - GREYLISTED - $errmsg \n"); + $this->errorHandler( $ev->remarks); } + $fail = true; break; } - if ($fail || $next_try_min > (2*24*60)) { + + // after trying all mxs - could not connect... + if (!$fail && ($next_try_min > (2*24*60) || strtotime($w->act_start) < strtotime('NOW - 3 DAYS'))) { + + $errmsg= " - UNKNOWN ERROR"; + if (isset($res->userinfo['smtptext'])) { + $errmsg= $res->userinfo['smtpcode'] . ':' . $res->userinfo['smtptext']; + } + + $ev = $this->addEvent('NOTIFYFAIL', $w, "RETRY TIME EXCEEDED - " . $errmsg); + $w->flagDone($ev, ''); + $this->errorHandler( $ev->remarks); + } + + if ($fail) { //// !!!!<<< BLACKLIST DETECT? // fail.. = log and give up.. - $errmsg= $fail ? ($res->userinfo['smtpcode'] . ': ' .$res->toString()) : " - UNKNOWN ERROR"; + $errmsg= $res->userinfo['smtpcode'] . ': ' .$res->toString(); if (isset($res->userinfo['smtptext'])) { $errmsg= $res->userinfo['smtpcode'] . ':' . $res->userinfo['smtptext']; } - $ev = $this->addEvent('NOTIFY', $w, ($fail ? "FAILED - " : "RETRY TIME EXCEEDED - ") . - $errmsg); - $w->sent = (!$w->sent || $w->sent == '0000-00-00 00:00:00') ? $w->sqlValue('NOW()') : $w->sent; // do not update if sent..... - $w->msgid = ''; - $w->event_id = $ev->id; - $w->domain_id = $core_domain->id; - $w->update($ww); - $this->errorHandler(date('Y-m-d h:i:s') . ' - FAILED - '. ($fail ? $res->toString() : "RETRY TIME EXCEEDED\n")); - } - - // handle no host availalbe forever... - if (strtotime($w->act_start) < strtotime('NOW - 3 DAYS')) { - $ev = $this->addEvent('NOTIFY', $w, "RETRY TIME EXCEEDED - ". $p->email); - $w->sent = (!$w->sent || $w->sent == '0000-00-00 00:00:00') ? $w->sqlValue('NOW()') : $w->sent; // do not update if sent..... - $w->msgid = ''; - $w->event_id = $ev->id; - $w->domain_id = $core_domain->id; - $w->update($ww); - $this->errorHandler(date('Y-m-d h:i:s') . " - FAILED - RETRY TIME EXCEEDED\n"); - } - - - $this->addEvent('NOTIFY', $w, 'NO HOST CAN BE CONTACTED:' . $p->email); - $w->act_when = date('Y-m-d H:i:s', strtotime('NOW + 5 MINUTES')); - $w->domain_id = $core_domain->id; - $w->update($ww); - $this->errorHandler(date('Y-m-d h:i:s') ." - NO HOST AVAILABLE\n"); + if ( $res->userinfo['smtpcode']> 500 ) { + + DB_DataObject::factory('core_notify_sender')->checkSmtpResponse($email, $w, $errmsg); + + + if ($this->server->checkSmtpResponse($errmsg, $core_domain)) { + $ev = $this->addEvent('NOTIFY', $w, 'BLACKLISTED - ' . $errmsg); + $this->server->updateNotifyToNextServer($w, $retry_when,true); + $this->errorHandler( $ev->remarks); + } + } + + $ev = $this->addEvent('NOTIFYFAIL', $w, ($fail ? "FAILED - " : "RETRY TIME EXCEEDED - ") . $errmsg); + $w->flagDone($ev, ''); + + $this->errorHandler( $ev->remarks); + } + + // at this point we just could not find any MX records.. + + + // try again. + + $ev = $this->addEvent('NOTIFY', $w, 'GREYLIST - NO HOST CAN BE CONTACTED:' . $p->email); + + $this->server->updateNotifyToNextServer($w, $retry_when ,true); + + + + $this->errorHandler($ev->remarks); } @@ -586,9 +593,11 @@ class Pman_Core_NotifySend extends Pman asort($mx_weight,SORT_NUMERIC); foreach($mx_weight as $k => $weight) { - $mxs[] = $mx_records[$k]; + if (!empty($mx_records[$k])) { + $mxs[] = $mx_records[$k]; + } } - return $mxs; + return empty($mxs) ? false : $mxs; } /** @@ -621,7 +630,8 @@ class Pman_Core_NotifySend extends Pman echo "calling :" . get_class($object) . '::' .$m . "\n"; return $object->$m($rcpt, $last_sent_date, $notify, $force); } - + // fallback if evtype is empty.. + if (method_exists($object, 'toMailerData')) { return $object->toMailerData(array( 'rcpts'=>$rcpt, @@ -630,8 +640,12 @@ class Pman_Core_NotifySend extends Pman //var_Dump($object); //exit; } + if (method_exists($object, 'toEmail')) { + return $object->toEmail($rcpt, $last_sent_date, $notify, $force); + } + // no way to send this.. - this needs to handle core_notify how we have used it for the approval stuff.. - return $object->toEmail($rcpt, $last_sent_date, $notify, $force); + return false; } function debug($str) @@ -652,6 +666,7 @@ class Pman_Core_NotifySend extends Pman { $this->debug_str .= strlen($this->debug_str) ? "\n" : ''; $this->debug_str .= $message; + //echo $message ."\n"; } function errorHandler($msg, $success = false) @@ -664,8 +679,27 @@ class Pman_Core_NotifySend extends Pman throw new Pman_Core_NotifySend_Exception_Fail($msg); } - die($msg); + die(date('Y-m-d h:i:s') . ' ' . $msg ."\n"); } + + function updateServer($w) + { + $ff = HTML_FlexyFramework::get(); + + if (empty($ff->Core_Notify['servers'])) { + return; + } + // some classes dont support server routing + if (!property_exists($w, 'server_id')) { + return; + } + // next server.. + $w->server_id = ($w->server_id + 1) % count(array_keys($ff->Core_Notify['servers'])); + + } + + + } \ No newline at end of file diff --git a/Pman.Delete.js b/Pman.Delete.js index 30aa19b1..11efd6df 100644 --- a/Pman.Delete.js +++ b/Pman.Delete.js @@ -126,12 +126,12 @@ Pman.Delete = { tab.paging.onClick('refresh'); } else if (tab.grid.footer && tab.grid.footer.onClick) { // new xtype built grids - tab.grid.footer.onClick('refresh'); + tab.grid.footer.onClick('refresh'); + } else if (tab.grid) { + tab.grid.getDataSource().load({}); } else if (tab.refresh) { tab.refresh(); // this might cause problems as panels have a refresh method? - } else { - tab.grid.getDataSource().load(); - } + } } } \ No newline at end of file diff --git a/Pman.Dialog.CoreColumnConfig.bjs b/Pman.Dialog.CoreColumnConfig.bjs new file mode 100644 index 00000000..1bff5574 --- /dev/null +++ b/Pman.Dialog.CoreColumnConfig.bjs @@ -0,0 +1,113 @@ +{ + "name" : "Pman.Dialog.CoreColumnConfig", + "parent" : "", + "title" : "", + "path" : "/home/alan/gitlive/Pman.Core/Pman.Dialog.CoreColumnConfig.bjs", + "permname" : "", + "modOrder" : "001", + "strings" : { + "cfcd208495d565ef66e7dff9f98764da" : "0", + "ea4788705e6873b424c65e91c2846b19" : "Cancel", + "c40cab5f875bb6c270d800eff77a4af0" : "Save Column Configuration", + "b5a7adde1af5c87d7fd797b6245c2a39" : "Description", + "c9cc8cce247e49bae79f15173ce97354" : "Save" + }, + "named_strings" : { + "description_fieldLabel" : "b5a7adde1af5c87d7fd797b6245c2a39", + "name_value" : "cfcd208495d565ef66e7dff9f98764da" + }, + "items" : [ + { + "listeners" : { + "show" : "function (_self)\n{\n \n}" + }, + "modal" : true, + "collapsible" : false, + "background" : true, + "title" : "Save Column Configuration", + "xtype" : "LayoutDialog", + "width" : 400, + "$ xns" : "Roo", + "closable" : false, + "resizable" : false, + "height" : 110, + "items" : [ + { + "xtype" : "LayoutRegion", + "$ xns" : "Roo", + "titlebar" : false, + "* prop" : "center" + }, + { + "background" : true, + "region" : "center", + "fitToFrame" : true, + "xtype" : "ContentPanel", + "$ xns" : "Roo", + "items" : [ + { + "listeners" : { + "|actioncomplete" : "function (_self, action)\n{\n if (action.type == 'setdata') {\n\n \n \n if(typeof(_this.data.title) != 'undefined' && _this.data.title.length){\n _this.dialog.setTitle(_this.data.title);\n }\n \n if(_this.data.id){\n _this.dialog.el.mask(\"Loading\");\n this.load({ method: 'GET', params: { '_id' : _this.data.id }}); \n }\n \n return;\n }\n if (action.type == 'load') {\n _this.dialog.el.unmask();\n return;\n }\n if (action.type == 'submit' ) {\n _this.dialog.el.unmask();\n _this.dialog.hide();\n\n if (_this.callback) {\n _this.callback.call(_this, action.result.data);\n }\n _this.form.reset();\n }\n}\n", + "|rendered" : "function (form)\n{\n _this.form = form;\n}" + }, + "$ url" : "baseURL + '/Roo/core_setting'", + "method" : "POST", + "xtype" : "Form", + "style" : "padding:5px", + "$ xns" : "Roo.form", + "items" : [ + { + "fieldLabel" : "Description", + "xtype" : "TextField", + "allowBlank" : false, + "width" : 250, + "$ xns" : "Roo.form", + "name" : "description" + }, + { + "xtype" : "Hidden", + "$ xns" : "Roo.form", + "name" : "module" + }, + { + "xtype" : "Hidden", + "$ xns" : "Roo.form", + "name" : "val" + }, + { + "xtype" : "Hidden", + "value" : 0, + "$ xns" : "Roo.form", + "name" : "name" + }, + { + "xtype" : "Hidden", + "$ xns" : "Roo.form", + "name" : "id" + } + ] + } + ] + }, + { + "listeners" : { + "|click" : "function() {\n _this.form.reset();\n _this.dialog.hide();\n}" + }, + "text" : "Cancel", + "xtype" : "Button", + "$ xns" : "Roo", + "* prop" : "buttons[]" + }, + { + "listeners" : { + "|click" : "function() {\n\n \n _this.form.doAction('submit');\n \n}" + }, + "text" : "Save", + "xtype" : "Button", + "$ xns" : "Roo", + "* prop" : "buttons[]" + } + ] + } + ] +} \ No newline at end of file diff --git a/Pman.Dialog.CoreColumnConfig.js b/Pman.Dialog.CoreColumnConfig.js new file mode 100644 index 00000000..9e9cbed5 --- /dev/null +++ b/Pman.Dialog.CoreColumnConfig.js @@ -0,0 +1,191 @@ +// + + {if:isDev} + - - -{else:} + {else:} + - -{end:} + {end:} + -Ext.form.SecurePass = function (config) { +Roo.form.SecurePass = function (config) { // these go here, so the translation tool can replace them.. this.errors = { PwdEmpty: "Please type a password, and then retype it to confirm.", @@ -16,10 +16,10 @@ Ext.form.SecurePass = function (config) { }, this.meterLabel = "Password strength:"; this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"]; - Ext.form.SecurePass.superclass.constructor.call(this, config); + Roo.form.SecurePass.superclass.constructor.call(this, config); } -Ext.extend(Ext.form.SecurePass, Ext.form.TextField, { +Roo.extend(Roo.form.SecurePass, Roo.form.TextField, { /** * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to * { @@ -65,7 +65,7 @@ Ext.extend(Ext.form.SecurePass, Ext.form.TextField, { insecure: false, // private initEvents: function () { - Ext.form.SecurePass.superclass.initEvents.call(this); + Roo.form.SecurePass.superclass.initEvents.call(this); if (this.el.is('input[type=password]') && Roo.isSafari) { this.el.on('keydown', this.SafariOnKeyDown, this); @@ -75,7 +75,7 @@ Ext.extend(Ext.form.SecurePass, Ext.form.TextField, { }, // private onRender: function (ct, position) { - Ext.form.SecurePass.superclass.onRender.call(this, ct, position); + Roo.form.SecurePass.superclass.onRender.call(this, ct, position); this.wrap = this.el.wrap({cls: 'x-form-field-wrap'}); this.trigger = this.wrap.createChild({tag: 'div', cls: 'roo-strength-meter ' + this.triggerClass}); @@ -122,7 +122,7 @@ Ext.extend(Ext.form.SecurePass, Ext.form.TextField, { if (this.wrap) { this.wrap.remove(); } - Ext.form.TriggerField.superclass.onDestroy.call(this); + Roo.form.TriggerField.superclass.onDestroy.call(this); }, // private checkStrength: function () { @@ -153,7 +153,7 @@ Ext.extend(Ext.form.SecurePass, Ext.form.TextField, { this._lastPwd = pwd; }, reset: function () { - Ext.form.SecurePass.superclass.reset.call(this); + Roo.form.SecurePass.superclass.reset.call(this); this._lastPwd = ''; var pm = this.trigger.child('div/div/div').dom; pm.style.width = 0; @@ -162,7 +162,7 @@ Ext.extend(Ext.form.SecurePass, Ext.form.TextField, { // private validateValue: function (value) { - if (!Ext.form.TextField.superclass.validateValue.call(this, value)) { + if (!Roo.form.TextField.superclass.validateValue.call(this, value)) { return false; } if (value.length == 0) {