Runs the javascript compiler - merging all the JS files so the load faster.
Note: cfg option Pman_Builder['jspacker'] must be set to location of jstoolkit code
+
+================================
+
+ $cli Core/Notify
+ Runs the notification tool - should be run every minute ideally.
+ Sends out emails to anyone in the notification list.
+
+ /etc/cron.d/pman-core-notify
+ * * * * * www-data /usr/bin/php /home/gitlive/web.mtrack/admin.php Core/Notify > /dev/null
+
+
";
/* the code below is auto generated do not remove the above tag */
- public $__table = 'core_nofity';
+ public $__table = 'core_notify';
public $act_when;
public $ontable;
return $c;
}
+ function object()
+ {
+ $c = DB_DataObject::factory($this->ontable);
+ $c->autoJoin();
+ if ($c->get($this->onid)) {
+ return $c;
+ }
+ return false;
+
+ }
}
\ No newline at end of file
function ensureNotify( $ontable, $onid, $person_id, $whereAdd)
{
+ //DB_DAtaObject::debugLevel(1);
$w = DB_DataObject::factory('core_watch');
- $w->ontable = $ontable;
- $w->onid = $onid;
- $w->person_id = $personid;
+ $w->person_id = $person_id;
+ if (empty($w->person_id)) {
+ return;
+ }
+
$nw = clone($w);
$w->whereAdd($whereAdd);
if ($w->count()) {
return;
}
+ $nw->ontable = $ontable;
+ $nw->onid = $onid;
+
$nw->medium = 'email';
$nw->active = 1;
$nw->insert();
$nn->ontable = $ontable;
$nn->onid = $onid;
foreach($people as $p) {
+ if (!$p) { // no people??? bugs in watch table
+ continue;
+ }
$n = clone($nn);
$n->person_id = $p;
$nf = clone($n);
}
- /***
- * The purpose of this is to gather all the events that have
- * occured in the system (where watches exist)
- * Do we want to send the user 1 email ?? or multiple...
- * --> I guess multiple emails..
- *
- * so we need to return
- *
- * array(
- $USER_ID => array(
- $OBJECT:$ID, $OBJECT:$ID, $OBJECT:$ID, .....
- )
- * )
- *
- * The mailer can then go through and call each object ??
- *
- *
- * -- Things we can watch..
- *
- * mtrack_change <- this is a neat log of all events.
- * which logs these things
- * Individual Ticket changes (already)
- * a Project -> which means ticket changes... which again can be discovered via mtrack_changes..
- * a Repo for Commits (-- which will be handled by mtrack_changes)
- * Wiki changes.. later...
- *
- *
- *
- */
-
- function watched($medium, $watcher = null)
- {
- $w = DB_DataObject::factory('core_watch');
- if ($watcher) {
- $w->person_id = $watcher;
- }
- $w->active = 1;
- $w->medium = $medium;
- $ar = $w->fetchAll();
- $ret = array();
- foreach($ar as $o) {
- if (!isset($ret[$o->person_id])) {
- $ret[$o->person_id] = array();
- }
- $ret[$o->person_id][] = $o->ontable .':'. $o->onid;
- }
-
- return $ret;
- }
+
}
###END_AUTOCODE
var $inAdmin = false;
+
+ /**
+ * Get a list of memberships for a person
+ * @param Pman_Core_DataObjects_Person $person who
+ * @param String column to fetch.. eg. group_id or 'name'
+ *
+ */
+
function listGroupMembership($person, $arrayof = 'group_id')
{
$this->inAdmin = false;
$t->find();
- $ret = $arrayof == 'group_id' ? array(0) : array(); // default member of 'All groups'!!
+ $ret = $arrayof == 'group_id' ? array(0) : array();
+ // default member of 'All groups'!!
while ($t->fetch()) {
$ret[] = $t->$arrayof;
{
return false;
}
- function fetchAll($k= false) {
- if ($k !== false) {
- $this->selectAdd();
- $this->selectAdd($k);
- }
-
- $this->find();
- $ret = array();
- while ($this->fetch()) {
- $ret[] = $k === false ? clone($this) : $this->$k;
- }
- return $ret;
-
- }
+
}
public $ontable; // string(32) not_null multiple_key
public $onid; // int(11) not_null
public $mimetype; // string(64) not_null
- public $width; // int(11) not_null
- public $height; // int(11) not_null
public $filesize; // int(11) not_null
- public $displayorder; // int(11) not_null
- public $language; // string(6) not_null
- public $parent_image_id; // int(11) not_null
public $created; // datetime(19) not_null binary
+ public $created_by; // int(11) not_null
+
+ public $width; // int(11) not_null
+ public $height; // int(11) not_null
+
+
public $imgtype; // string(32) not_null
+ public $parent_image_id; // int(11) not_null
public $linkurl; // string(254) not_null
public $descript; // blob(65535) not_null blob
public $title; // string(128) not_null
-
+ public $displayorder; // int(11) not_null
+ public $language; // string(6) not_null
+
/* the code above is auto generated do not remove the tag below */
###END_AUTOCODE
* ontable / onid.
*
*/
- function createFrom($file)
+ function createFrom($file, $filename=false)
{
// copy the file into the storage area..
if (!file_exists($file) || !filesize($file)) {
return false;
}
-
- $imgs = @getimagesize($file);
-
- if (empty($imgs) || empty($imgs[0]) || empty($imgs[1])) {
- // it's a file!!!!
- } else {
- list($this->width , $this->height) = $imgs;
- }
-
- $this->filesize = filesize($file);
- $this->created = date('Y-m-d H:i:s');
+ $filename = empty($filename) ? $file : $filename;
if (empty($this->mimetype)) {
require_once 'File/MimeType.php';
$y = new File_MimeType();
- $this->mimetype = $y->fromFilename($file);
+ $this->mimetype = $y->fromFilename($filename);
+ }
+
+ $this->mimetype= strtolower($this->mimetype);
+
+ if (array_shift(explode('/', $this->mimetype)) == 'image') {
+
+ $imgs = @getimagesize($file);
+
+ if (empty($imgs) || empty($imgs[0]) || empty($imgs[1])) {
+ // it's a file!!!!
+ } else {
+ list($this->width , $this->height) = $imgs;
+ }
}
+ $this->filesize = filesize($file);
+ $this->created = date('Y-m-d H:i:s');
+
if (empty($this->filename)) {
- $this->filename = basename($file);
+ $this->filename = basename($filename);
}
//DB_DataObject::debugLevel(1);
*/
function getStoreName()
{
- $opts = PEAR::getStaticProperty('Pman', 'options');
+ $opts = HTML_FlexyFramework::get()->Pman;
$fn = preg_replace('/[^a-z0-9\.]+/i', '_', $this->filename);
return implode( '/', array(
$opts['storedir'], '_images_', date('Y/m', strtotime($this->created)), $this->id . '-'. $fn
}
}
-
+ /**
+ * check mimetype against type
+ * - eg. img.is(#image#)
+ *
+ */
+ function is($type)
+ {
+ if (empty($this->mimetype)) {
+ return false;
+ }
+ return 0 === strcasecmp($type, array_shift(explode('/',$this->mimetype)));
+ }
/**
* onUpload (singlely attached image to a table)
if (empty($obj->id)) {
return array();
}
+
$c = clone($this);
$c->ontable = $obj->tableName();
$c->onid = $obj->id;
+ $c->autoJoin();
if (!empty($mime_like)) {
$c->whereAdd("Images.mimetype LIKE '". $c->escape($mime_like) ."'");
}
return $c->fetchAll();
}
+
+
+ /**
+ * set or get the dataobject this image is associated with
+ * @param DB_DataObject $obj An object to associate this image with
+ * (does not store it - you need to call update() to do that)
+ * @return DB_DataObject the dataobject this image is attached to.
+ */
+ function object($obj=false)
+ {
+ if ($obj === false) {
+ $ret = DB_DataObject::factory($this->ontable);
+ $ret->get($this->onid);
+ return $ret;
+ }
+ $this->ontable = $obj->tableName();
+ $this->onid = $obj->id; /// assumes our nice standard of using ids..
+ return $obj;
+ }
function toRooArray($req = array()) {
$sz = explode('x', $size);
$sx = $sz[0];
//var_dump($sz);
- if (!$this->id) {
+ if (!$this->id || empty($this->width)) {
$this->height = $sx;
$this->width = empty($sz[1]) ? $sx : $sz[1];
$sy = $this->width ;
}
if (empty($sz[1])) {
- $ratio = $this->height/ ($this->width *1.0);
- $sy = $ration * $sx;
+ $ratio = empty($this->width) ? 1 : $this->height/ ($this->width *1.0);
+ $sy = $ratio * $sx;
} else {
$sy = $sz[1];
}
}
+ /**
+ *Basic group fetching - probably needs to filter by type eventually.
+ *
+ */
+
+ function groups()
+ {
+ $g = DB_DataObject::Factory('Group_Members');
+ $grps = $g->listGroupMembership($this);
+ $g = DB_DataObject::Factory('Groups');
+ $g->whereAddIn('id', $grps, 'int');
+ return $g->fetchAll();
+
+ }
+
function hasPerm($name, $lvl)
{
static $pcache = array();
return "Duplicate Email found";
}
return true;
- }
+ }
+
+
+ /***
+ * Check if the a user has access to modify this item.
+ * @param String $lvl Level (eg. Core.Projects)
+ * @param Pman_Core_DataObjects_Person $au The authenticated user.
+ * @param boolean $changes alllow changes???
+ *
+ * @return false if no access..
+ */
function checkPerm($lvl, $au, $changes=false) //heck who is trying to access this. false == access denied..
{
###END_AUTOCODE
- function toEventString() {
+ function person()
+ {
$p = DB_DataObject::factory('Person');
$p->get($this->person_id);
+ return $p;
+ }
+
+ function toEventString() {
+ $p = $this->person();
+ // this is weird... company is in the person.. - effieciency??
+ // for seaching??
$c = DB_DataObject::factory('Companies');
$c->get($this->company_id);
$pr = DB_DataObject::factory('Projects');
PRIMARY KEY (`id`),
INDEX `lookup`(`act_when`, `msgid`)
);
+ALTER TABLE core_notify CHANGE COLUMN bounced event_id INT(11) DEFAULT 0;
+
ALTER TABLE core_notify CHANGE COLUMN bounced event_id INT(11) NOT NULL DEFAULT 0;
--- /dev/null
+<?php
+require_once 'Pman.php';
+
+/**
+ * notification script runner
+ *
+ * This does not actualy send stuf out, it only starts the NotifySend/{id}
+ * which does the actuall notifcations.
+ *
+ * It manages a pool of notifiers.
+ *
+ *
+ */
+
+
+class Pman_Core_Notify extends Pman
+{
+
+ var $table = 'core_notify';
+ var $target = 'Core/NotifySend';
+
+ function getAuth()
+ {
+ $ff = HTML_FlexyFramework::get();
+ if (!$ff->cli) {
+ die("access denied");
+ }
+ HTML_FlexyFramework::ensureSingle(__FILE__, $this);
+ return true;
+
+ }
+
+ var $pool = array();
+
+ function get()
+ {
+ //DB_DataObject::debugLevel(1);
+ //date_default_timezone_set('UTC');
+ // phpinfo();exit;
+
+ $w = DB_DataObject::factory($this->table);
+ $w->whereAdd('act_when > sent'); // eg.. sent is not valid..
+ $w->whereAdd('act_when < NOW()'); // eg.. not if future..
+
+ $w->orderBy('act_when ASC'); // oldest first.
+ $w->limit(1000); // we can run 1000 ...
+ $ar = $w->fetchAll('id');
+
+ while (true) {
+ if (empty($ar)) {
+ break;
+ }
+ if (!$this->poolfree()) {
+ sleep(3);
+ continue;
+ }
+ $p = array_shift($ar);
+ $this->run($p);
+ }
+ while(count($this->pool)) {
+ $this->poolfree();
+ sleep(3);
+ }
+
+ die("DONE\n");
+ }
+
+ function run($id)
+ {
+ // phpinfo();exit;
+ $tn = tempnam(ini_get('session.save_path'),'stdout') . '.stdout';
+ $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
+ );
+ $php = $_SERVER["_"];
+ $sn = $_SERVER["SCRIPT_NAME"];
+
+ $cwd = $sn[0] == '/' ? dirname($sn) : dirname(realpath(getcwd() . $sn)); // same as run on.. (so script should end up being same relatively..)
+ $app = $cwd . '/' . basename($_SERVER["SCRIPT_NAME"]) . ' ' . $this->target . '/'. $id;
+ $cmd = $php . ' ' . $app. ' &';
+ //echo $cmd . "\n";
+ $pipe = array();
+ $p = proc_open($cmd, $descriptorspec, $pipes, $cwd );
+ $this->pool[] = array('proc' => $p, 'out' => $tn, 'cmd' => $cmd);
+ }
+
+ function poolfree() {
+ $pool = array();
+ foreach($this->pool as $p) {
+ $ar = proc_get_status($p['proc']);
+ // print_r($p);
+ //print_r($ar);
+ if ($ar['running']) {
+ $pool[] = $p;
+ continue;
+ }
+ echo $p['cmd'] . " : " . file_get_contents($p['out']);
+ //unlink($p['out']);
+ }
+ $this->pool = $pool;
+ if (count($pool) < 10) {
+ return true;
+ }
+ return false;
+
+ }
+
+
+}
\ No newline at end of file
--- /dev/null
+<?php
+require_once 'Pman.php';
+
+/**
+ * notification script sender - designed to be run by the Notify script - with many children running
+ * in parallel.
+ *
+ * called with an id of a core_notify element
+ *
+ * uses core_notify - to find an event to object and person.
+ *
+ * uses Events table to log failures
+ *
+ *
+ * calls $object->toEmail($person,$last_send) to generate an email struct with
+ * array (
+ * headers =>
+ * recipients =>
+ * body =>
+ * )
+ *
+ *
+ */
+
+
+class Pman_Core_NotifySend extends Pman
+{
+
+ var $table = 'core_notify';
+ function getAuth()
+ {
+ $ff = HTML_FlexyFramework::get();
+ if (!$ff->cli) {
+ die("access denied");
+ }
+ //HTML_FlexyFramework::ensureSingle(__FILE__, $this);
+ return true;
+
+ }
+
+ var $pool = array();
+
+ function get($id)
+ {
+ //DB_DataObject::debugLevel(1);
+ //date_default_timezone_set('UTC');
+ // phpinfo();exit;
+
+ $w = DB_DataObject::factory($this->table);
+
+ if (!$w->get($id) || strtotime($w->act_when) < strtotime($w->sent)) {
+ die("invalid id or time\n");
+ }
+
+ $o = $w->object();
+ $p = $w->person();
+
+ // let's work out the last notification sent to this user..
+ $l = DB_DataObject::factory($this->table);
+ $l->setFrom( array(
+ 'ontable' => $w->ontable,
+ 'onid' => $w->onid,
+ 'person_id' => $w->person_id,
+ ));
+ $l->whereAdd('id != '. $w->id);
+ $l->orderBy('sent DESC');
+ $l->limit(1);
+ $ar = $l->fetchAll('sent');
+ $last = empty($ar) ? date('Y-m-d H:i:s', 0) : $ar[0];
+
+ // find last event..
+ $ev = DB_DataObject::factory('Events');
+ $ev->on_id = $w->id; // int(11)
+ $ev->od_table = $this->table;
+ $ev->limit(1);
+ $ev->orderBy('event_when DESC');
+ $ar = $ev->fetchAll('event_when');
+ $last_event = empty($ar) ? 0 : $ar[0];
+ $next_try_min = 5;
+ if ($last_event) {
+ $next_try_min = floor((time() - strtotime($last_event)) / 60) * 2;
+ }
+ $next_try = $next_try_min . ' MINUTES';
+
+
+ $email = $o->toEmail($p,$last);
+
+ //$p->email = 'alan@akbkhome.com'; //for testing..
+ //print_r($email);exit;
+ // should we fetch the watch that caused it.. - which should contain the method to call..
+ $dom = array_pop(explode('@', $p->email));
+
+ $mxs = $this->mxs($dom);
+ $ww = clone($w);
+
+ $fail = false;
+ require_once 'Mail.php';
+
+ foreach($mxs as $dom) {
+
+ $mailer = Mail::factory('smtp', array( 'host' => $dom ));
+ $res = $mailer->send($p->email, $email['headers'], $email['body']);
+ if ($res === true) {
+ // success....
+
+ $w->sent = date('Y-m-d H:i:s');
+ $w->msgid = $email['headers']['Message-Id'];
+ $w->event_id = -1; // sent ok.. - no need to record it..
+ $w->update($ww);
+ die("SENT");
+ }
+ // what type of error..
+ list($code, $response) = $mailer->_smtp->getResponse();
+ if ($code < 0) {
+ continue; // try next mx... ??? should we wait??? - nope we did not even connect..
+ }
+ // give up after 2 days..
+ if ($code == 451 && $next_try_min < (2*24*60)) {
+ // try again later..
+ // check last event for this item..
+ $this->addEvent('NOTIFY', $w, 'GREYLISTED');
+ $w->act_when = date('Y-m-d H:i:s', strtotime('NOW + 5 MINUTES'));
+ $w->update($ww);
+ die("GREYLISTED");
+ }
+ $fail = true;
+ break;
+ }
+ if ($fail || $next_try_min > (2*24*60)) {
+ // fail.. = log and give up..
+ $id = $this->addEvent('NOTIFY', $w, 'FAILED - '. $fail ? $res->toString() : "RETRY TIME EXCEEDED");
+ $w->sent = date('Y-m-d H:i:s');
+ $w->msgid = '';
+ $w->event_id = $id;
+ $w->update($ww);
+ die("DONE");
+ }
+
+ $this->addEvent('NOTIFY', $w, 'NO HOST CAN BE CONTACTED');
+ $w->act_when = date('Y-m-d H:i:s', strtotime('NOW + 5 MINUTES'));
+ $w->update($ww);
+ die("NO HOST AVAILABLE");
+
+
+ }
+ function mxs($fqdn)
+ {
+ $mx_records = array();
+ $mx_weight = array();
+ $mxs = array();
+ if (!getmxrr($fqdn, $mx_records, $mx_weight)) {
+ return array($fqdn);
+ }
+
+ asort($mx_weight,SORT_NUMERIC);
+
+ foreach($mx_weight as $k => $weight) {
+ $mxs[] = $mx_records[$k];
+ }
+ return $mxs;
+ }
+
+
+}
\ No newline at end of file