Merge branch 'master' of http://private.akbkhome.com/Pman.Core
authorAlan Knowles <alan@akbkhome.com>
Fri, 15 Apr 2011 04:58:41 +0000 (12:58 +0800)
committerAlan Knowles <alan@akbkhome.com>
Fri, 15 Apr 2011 04:58:41 +0000 (12:58 +0800)
Cli.php
DataObjects/Core_notify.php
DataObjects/Core_watch.php
DataObjects/Group_Members.php
DataObjects/Images.php
DataObjects/Person.php
DataObjects/ProjectDirectory.php
DataObjects/core.sql
Notify.php [new file with mode: 0644]
NotifySend.php [new file with mode: 0644]

diff --git a/Cli.php b/Cli.php
index 7cc7f7b..3c40951 100644 (file)
--- a/Cli.php
+++ b/Cli.php
@@ -40,7 +40,18 @@ class Pman_Core_Cli
      
     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
+    
+        
 ";
 
 
index 9f2f294..12f0e8d 100644 (file)
@@ -27,7 +27,7 @@ class Pman_Core_DataObjects_Core_notify extends DB_DataObject
     /* 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;                         
@@ -47,5 +47,15 @@ class Pman_Core_DataObjects_Core_notify extends DB_DataObject
         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
index 5b9615e..f3fb538 100644 (file)
@@ -34,10 +34,13 @@ class Pman_Core_DataObjects_Core_watch extends DB_DataObject
     
     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);
         
@@ -45,6 +48,9 @@ class Pman_Core_DataObjects_Core_watch extends DB_DataObject
         if ($w->count()) {
             return;
         }
+        $nw->ontable = $ontable;
+        $nw->onid = $onid;
+        
         $nw->medium = 'email';
         $nw->active = 1;
         $nw->insert();
@@ -63,6 +69,9 @@ class Pman_Core_DataObjects_Core_watch extends DB_DataObject
         $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);
@@ -80,53 +89,5 @@ class Pman_Core_DataObjects_Core_watch extends DB_DataObject
         
         
     }
-    /***
-     * 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;
-    }
+     
 }
index 73a82b8..76f0841 100755 (executable)
@@ -19,6 +19,14 @@ class Pman_Core_DataObjects_Group_Members extends DB_DataObject
     ###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;
@@ -36,7 +44,8 @@ class Pman_Core_DataObjects_Group_Members extends DB_DataObject
         
         $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;
@@ -51,18 +60,5 @@ class Pman_Core_DataObjects_Group_Members extends DB_DataObject
     {
         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;
-         
-    }
+   
 }
index 1c790e0..dfc22bb 100644 (file)
@@ -15,18 +15,22 @@ class Pman_Core_DataObjects_Images extends DB_DataObject
     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
@@ -36,34 +40,40 @@ class Pman_Core_DataObjects_Images extends DB_DataObject
      * 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);
@@ -103,7 +113,7 @@ class Pman_Core_DataObjects_Images extends DB_DataObject
      */
     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
@@ -137,7 +147,18 @@ class Pman_Core_DataObjects_Images extends DB_DataObject
         }
         
     }
-    
+    /**
+     * 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)
@@ -246,15 +267,36 @@ class Pman_Core_DataObjects_Images extends DB_DataObject
         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()) {
@@ -318,14 +360,14 @@ class Pman_Core_DataObjects_Images extends DB_DataObject
         $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];
         }
index 20bf36e..a757f89 100644 (file)
@@ -353,6 +353,21 @@ class Pman_Core_DataObjects_Person extends DB_DataObject
          
         
     }
+    /**
+     *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();
@@ -512,7 +527,17 @@ class Pman_Core_DataObjects_Person extends DB_DataObject
             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..
     {
          
index b68b5b7..30f592f 100644 (file)
@@ -23,9 +23,17 @@ class Pman_Core_DataObjects_ProjectDirectory extends DB_DataObject
     ###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');
index d289195..47d8fe7 100644 (file)
@@ -324,6 +324,8 @@ CREATE TABLE  core_notify  (
   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;
  
diff --git a/Notify.php b/Notify.php
new file mode 100644 (file)
index 0000000..7b8dade
--- /dev/null
@@ -0,0 +1,111 @@
+<?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
diff --git a/NotifySend.php b/NotifySend.php
new file mode 100644 (file)
index 0000000..efffbc3
--- /dev/null
@@ -0,0 +1,164 @@
+<?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