Fix #7734 - add ticket to branches
[Pman.MTrack] / DataObjects / Mtrack_repos.php
1 <?php
2 /**
3  * Table Definition for mtrack_repos
4  */
5 class_exists('DB_DataObject') ? '' : require_once 'DB/DataObject.php';
6
7 class Pman_MTrack_DataObjects_Mtrack_repos extends DB_DataObject 
8 {
9     ###START_AUTOCODE
10     /* the code below is auto generated do not remove the above tag */
11
12     public $__table = 'mtrack_repos';                    // table name
13     public $id;                              // int(11)  not_null primary_key auto_increment
14     public $shortname;                       // string(64)  not_null
15     public $scmtype;                         // string(16)  not_null
16     public $repopath;                        // string(255)  not_null
17     public $browserurl;                      // string(255)  
18     public $browsertype;                     // string(255)  
19     public $description;                     // blob(65535)  blob
20     public $serverurl;                       // string(255)  
21     public $parent;                          // string(255)  not_null
22     public $clonedfrom;                      // int(11)  
23     public $project_id;                      // int(11)  multiple_key
24
25     
26     /* the code above is auto generated do not remove the tag below */
27     ###END_AUTOCODE
28     
29        
30     function applyFilters($q, $au, $roo)
31     {
32        
33         if (!empty($q['_daychanges'])) {
34             $this->dayChanges($roo, $q);
35         }
36        
37         /// only show unposted..
38         $this->autoImport('/home/git/private'); // should be an incomming variable..
39         
40         
41         $cmd = empty($q['_cmd']) ? '' : $q['_cmd'];
42         // non destructive commands can be run via cmd line.
43   
44         
45     }
46     function project()
47     {
48         $p = DB_DAtaObject::Factory('Projects');
49         $p->get($this->project_id);
50         return $p;
51         
52     }
53     
54  
55     
56     function autoImport($dir)
57     {
58         // this code is specific for my project at present.. - should be more generic..
59         $ar = scandir($dir);
60         foreach(scandir($dir) as $d) {
61             if (!file_exists("$dir/$d/HEAD") || !file_exists("$dir/$d/config") ) {
62                 continue; // not a git dir..
63             }
64             $x = DB_DataObject::factory('mtrack_repos');
65             if ($x->get('repopath', "$dir/$d")) {
66                 continue;
67             }
68             $x->setFrom( array(
69                 'repopath' => "$dir/$d",
70                 'project_id' => 0,
71                 'shortname' => $d,
72                 'scmtype' => 'git',
73                 'description' => file_exists("$dir/$d/description") ? file_get_contents("$dir/$d/description") : '',
74                 
75             ));
76             $x->insert(); 
77             
78             
79         }
80         
81     }
82      
83     /**
84      * loads the repo
85      * returns the remaining path.
86      * @param string $pi path like /a/b/cccc/dddd
87      *
88      */
89     function loadFromPath($pi)
90     {
91         $crumbs = explode('/', $pi);
92         if (count($crumbs) < 2) {
93             return '';
94         }
95         $this->parent = $crumbs[0] == 'default' ? '' : 'user:' . $crumbs[0]; 
96         $res = $this->get('shortname', $crumbs[1]);
97         // we have a special wiki...
98         if (!$res && $crumbs[1] == 'wiki') {
99             $res = $this->createDefaultWiki();
100         }
101         
102         
103         array_shift($crumbs);
104         array_shift($crumbs);
105         return implode('/', $crumbs);
106     }
107     
108     /**
109      * creates the default public wiki..
110      *
111      * -- not sure how we will handle project ones yet..
112      * -- might be subdirectories???
113      */
114     function createDefaultWiki()
115     {
116         $ret = DB_DataObject::factory('mtrack_repos');
117         $ff= HTML_FlexyFramework::get();;
118         if (empty($ff->MTrackWeb['working_dir'])) {
119             die("configuration must contains MTrackWeb[working_dir]");
120         }
121         $p = DB_DataObject::factory('Projects');
122         if ($p->get('code', '*PUBLIC')) {
123             $p->code = '*PUBLIC';
124             $p->name = 'Public Projects';
125             $p->insert();
126         }
127         
128         $ret->setFrom(array(
129             'shortname' => 'wiki',                  
130             'scmtype' => 'git',                   
131             'repopath' => $ff->MTrackWeb['working_dir'] .'/wiki',             
132             'browserurl' => '',            
133             'browsertype' => '',          
134             'description' => 'MTrack Wiki',         
135             'serverurl' => '',         
136             'parent' => '',
137             'clonedfrom' => '',              
138             'project_id' => $p->id,          
139          
140         ));
141         $ret->insert();
142         
143     }
144     
145        //checkPerm('S'/'E'/'A', $authuser) - can
146     function checkPerm($perm, $au)
147     {
148         
149         if ($au && $au->company()->comptype == 'OWNER') {
150             // owner can do anything..????
151             return true;
152         }
153         
154         if ($perm == 'E' || $perm == 'D' ) { // Edit and delete ..not allowed..
155             return false;
156         }
157         
158         if (!$au && $perm != 'S') {
159             // non-authenticated users can only list stuff..
160             return false;
161             
162         }
163         
164         //DB_DataObject::debugLevel(1);
165         $obj = $this->project();
166         if (!$obj) {
167             return false;
168         }
169         //if (!method_exists($obj, 'checkPerm')) {
170         //    return false;
171         //}
172         // must have same perm on project..
173         
174         return $obj->checkPerm($perm, $au);
175         
176          
177     }
178     
179     
180     function displayName()
181     {
182         if (!empty($this->parent)) {
183           list($type, $owner) = explode(':', $this->parent);
184           return "$owner/$this->shortname";
185         }
186         return "default/$this->shortname";
187     }
188     /**
189      *return the mtrack_repo implemenation
190      */
191     function impl() {
192         
193         if (!$this->id) {
194             return false;
195         }
196         static $repo = array();
197         if (isset($repo[$this->id])) {
198             return $repo[$this->id];
199         }
200         require_once 'MTrack/Repo.php';
201         $repo[$this->id] = MTrack_Repo::factory(
202             $this->toArray()
203         );
204         return $repo[$this->id];
205     }
206     
207     
208     function save($do=false)
209     {
210         throw new Exception("Error invalid call");
211     }
212     
213     function save_changes($DO_CHANGES) {
214         if (!$this->impl()) {
215           throw new Exception("unsupported repo type " . $this->scmtype);
216         }
217         $old = false;
218         if ($this->id) {
219             $old = DB_DataObject::facotry('repos');
220             $old->get($this->id);
221             $this->update();
222              
223         }
224         // NEW...
225             
226         $acl = null;
227     
228         if (!strlen($this->repopath)) {
229             $cfg = HTML_FlexyFramework::get()->MTrack;
230             if (empty($cfg['new_repo_basedir'])) { 
231                 throw new Exception("configuration does not allow repo creation - set new_repo_basedir");
232             }
233             
234             $repodir = $cfg['new_repo_basedir'];
235             
236             // bit hopefull.
237             if (!is_dir($repodir)) {
238                 mkdir($repodir,0700, true);
239             }
240             $page = HTML_FlexyFramework::get()->page;
241             // in our system - users do not have 'canons' - they have names & email - and that's it..
242             
243             
244             // validate parent..
245             if (!$this->parent) {
246                 $owner = $page->authUser->id;
247                 $this->parent = 'user:' . $owner;
248             } else {
249                 
250                 list($type, $owner) = explode(':', $this->parent, 2);
251                 
252                 switch ($type) {
253                     case 'project':
254                         $p = DB_DAtaObject::factory('Projects');
255                         if (!$p->get($owner)) {
256                             throw new Exception("invalid project $owner");
257                         }
258                     
259                         //MTrackACL::requireAllRights("project:$P->id", 'modify');
260                         break;
261                 
262                     case 'user':
263                         
264                         if ($owner != $page->authUser->id) {
265                             throw new Exception("can't make a repo for another user");
266                         }
267                         break;
268                     default:
269                         throw new Exception("invalid parent ($this->parent)");
270                 }
271             }
272             
273             
274             //if (preg_match("/[^a-zA-Z0-9_.-]/", $owner)) {
275             //  throw new Exception("$owner must not contain special characters");
276             //}
277             
278             $this->repopath = $repodir . DIRECTORY_SEPARATOR . $owner;
279             
280             if (!is_dir($this->repopath)) {
281                 mkdir($this->repopath,0700,true);
282             }
283             $this->repopath .= DIRECTORY_SEPARATOR . $this->shortname;
284             
285     
286             /* default ACL is allow user all rights, block everybody else */
287             $acl = array(
288                 array($owner, 'read', 1),
289                 array($owner, 'modify', 1),
290                 array($owner, 'delete', 1),
291                 array($owner, 'checkout', 1),
292                 array($owner, 'commit', 1),
293                 array('*', 'read', 0),
294                 array('*', 'modify', 0),
295                 array('*', 'delete', 0),
296                 array('*', 'checkout', 0),
297                 array('*', 'commit', 0),
298             );
299         } // end - no repo path set... 
300         $this->insert();
301     
302         $old = null;
303         
304         if ($acl !== null) { // only do this if it's a new repo path..
305             
306             //MTrackACL::setACL("repo:$this->id", 0, $acl);
307             
308             
309             $w = DB_DataObject::factory('core_watch');
310             $w->setFrom(array(
311                 'otype'  => 'repo',
312                 'oid'    => $this->id,
313                 'userid' => $page->authUser->id,
314                 'medium' => 'email',
315                 'active' => 1
316             ));
317             $ww = clone($w);
318             
319             $ww->event = 'ticket';
320             $ww->insert();
321             
322             $w->event = 'changeset';
323             $w->insert();
324             
325           
326         }
327         
328         
329         $this->impl()->reconcileRepoSettings();
330         
331         /*
332         if (!$this->parent) {
333            // for SSH access, populate a symlink from the repos basedir to the
334            // actual path for this repo 
335             $repodir = MTrackConfig::get('repos', 'basedir');
336             
337             if (!is_dir($repodir)) {
338                   mkdir($repodir);
339             }
340             $repodir .= '/default';
341             if (!is_dir($repodir)) {
342               mkdir($repodir);
343             }
344             $repodir .= '/' . $this->shortname;
345             if (!file_exists($repodir)) {
346               symlink($this->repopath, $repodir);
347             } else if (is_link($repodir) && readlink($repodir) != $this->repopath) {
348               unlink($repodir);
349               symlink($this->repopath, $repodir);
350             }
351         }
352         */
353         //
354         $DO_CHANGES->add("repo:" . $this->id , $old, $this);
355          
356          
357          /*
358         foreach ($this->links_to_add as $link) {
359             MTrackDB::q('insert into project_repo_link (projid, id, repopathregex) values (?, ?, ?)', $link[0], $this->id, $link[1]);
360         }
361         foreach ($this->links_to_remove as $linkid) {
362             MTrackDB::q('delete from project_repo_link where id = ? and linkid = ?', $this->id, $linkid);
363         }
364          */
365     }
366     
367     
368     
369
370     function historyWithChangelog($path, $limit = null, $object = null,   $ident = null) 
371     {
372       
373         $ents = $this->impl()->history($path, $limit, $object, $ident);
374         $data = new StdClass;
375         if (!count($ents)) {
376             $data->ent = null;
377             return $data;
378         }
379         $ent = $ents[0];
380         $data->ent = $ent;
381
382         // Determine project from the file list
383         //$the_proj = $this->projectFromPath($ent->files);
384         //if ($the_proj > 1) {
385         //  $proj = MTrackProject::loadById($the_proj);
386         //  $changelog = $proj->adjust_links($ent->changelog, true);
387         //} else {
388           //$changelog = $ent->changelog;
389         //}
390         //$data->changelog = $changelog;
391
392         //if (is_array($ent->files)) foreach ($ent->files as $file) {
393         //  $file->diff = mtrack_diff($repo->diff($file, $ent->rev));
394         //}
395       
396
397         return $data;
398     }
399     
400     function historyWithChangelogAndDiff($path, $limit = null, $object = null,   $ident = null)
401     {
402         $ret = $this->impl()->historyWithChangelog($path, $limit, $object, $ident);
403         if (!$ret->ent) {
404             return $ret;
405         }
406         if (!is_array($ret->ent->files)) {
407             return $ret;
408         }
409         foreach ($ret->ent->files as $file) {
410             // where is mtrack_diff...
411             $page = HTML_Flexyframework::get()->page;
412             $file->diff = $page->link->diff(
413                                 $this->impl()->diff($file, $ret->ent->rev)
414                         );
415         }
416       
417
418         return $data;
419     }
420     
421     
422     function cloneCommand()
423     {
424         //hacky..
425         $pg  = HTML_FlexyFramework::get()->page;
426         $dom = $pg->authUser ? 'roojs.com' : 'roojs.org';
427         
428         if (empty($this->cloneCommand)) {
429             return "git clone http://git.{$dom}/{$this->shortname}";
430         }
431         return $this->cloneCommand;
432         
433         
434     }
435     
436     // rendering.... - still not in the right place...
437     
438     function descriptionToHtml()
439     {
440         return htmlspecialchars($this->description);
441         
442         //require_once 'MTrack/Wiki.php';
443         //return  MTrack_Wiki::format_to_html($this->description);
444     }
445     
446     
447     function branchObject($n)
448     {
449         // create or return branch dataobject
450         $b = DB_DataObject::factory('mtrack_repos_branch');
451         $b->repo_id = $this->id;
452         $b->name = $n;
453         if ($b->find(true)) {
454             return $b;
455         }
456         if (preg_match("/_T([0-9]+)_/",$n, $matches)) {
457             $b->ticket_id = $matches[1];
458         }
459         
460         $b->insert();
461         return $b;
462     }
463     
464     // these should really be phased out, and the Controlers use impl()-> directly..
465     function getBranches()
466     {
467         return $this->impl()->getBranches();
468     }
469     function getTags()
470     {
471         return $this->impl()->getTags();
472     }
473     function readdir($pi, $object = null, $ident = null)
474     {
475         return $this->impl()->readdir($pi, $object,$ident);
476     }
477     function file($path, $object = null, $ident = null)
478     {
479         return $this->impl()->file($path, $object,$ident);
480     }
481     function history($path, $limit = null, $object = null, $ident = null)
482     {
483         return $this->impl()->history($path, $limit, $object,$ident);
484     }
485     function diff($path, $from = null, $to = null)
486     {
487         return $this->impl()->diff($path, $from, $to);
488     }
489  
490     function getRelatedChanges($revision)
491     {
492         return $this->impl()->getRelatedChanges($revision);
493     }
494     function getWorkingCopy()
495     {
496         return $this->impl()->getWorkingCopy();
497     }
498  
499     function getSCMMetaData()
500     {
501         return $this->impl()->getSCMMetaData();   
502     }
503     function getBrowseRootName()
504     {
505         return  $this->impl()->getBrowseRootName();
506     }
507     function reconcileRepoSettings(MTrackSCM $r)
508     {
509         return  $this->impl()->reconcileRepoSettings($r);
510     }
511     function resolveRevision($rev, $object, $ident) {
512         return  $this->impl()->resolveRevision($rev, $object, $ident);
513     }
514     
515     function getCheckoutCommand() {
516         return $this->impl()->getCheckoutCommand();
517     }
518     function canFork()
519     {
520         return $this->impl()->canFork();
521     }
522     
523     function parseDiff($input)
524     {
525          
526         $nlines = 0;
527
528         if (is_resource($input)) {
529             $lines = array();
530             while (($line = fgets($input)) !== false) {
531                 $lines[] = rtrim($line, "\r\n");
532             }
533             $input = $lines;
534         }
535         
536         // abase???
537         if (is_string($input)) {
538             $abase = md5($input);
539             $input = preg_split("/\r?\n/", $input);
540         } else {
541             $abase = md5(join("\n", $input));
542         }
543         
544         $ret = array();
545            
546         while (true) {
547              
548             
549             if (!count($input)) {
550                 break;
551             }
552             
553             $line = array_shift($input);
554             $nlines++;
555             
556             if (!strncmp($line, '@@ ', 3)) {
557                 /* done with preamble */
558                 break;
559             }
560             $ret[] = (object)array(
561                 
562                 'text' => $line,
563                 'meta' => true,
564                 'cls' =>'meta',
565                 'anchor' => $abase .'.'.$nlines,
566             );
567              
568         }
569         $class = 'meta';
570           
571         $lines = array(0, 0);
572         $first = false;
573         
574         $in_meta = false;
575         
576         while (true) {
577             $class = 'unmod';
578             
579             if (preg_match("/^@@\s+-(\pN+)(?:,\pN+)?\s+\+(\pN+)(?:,\pN+)?\s*@@/",
580                   $line, $M)) {
581                 $lines[0] = (int)$M[1] - 1;
582                 $lines[1] = (int)$M[2] - 1;
583                 $class = 'meta';
584                 $first = true;
585                 $in_meta = false;
586             } else if (preg_match("/^diff /", $line)) {
587                 $class = 'meta';
588                 $in_meta=true;
589             
590             } elseif (strlen($line)) {
591                 if ($line[0] == '-') {
592                     $lines[0]++;
593                     $class = 'removed';
594                 } elseif ($line[0] == '+') {
595                     $lines[1]++;
596                     $class = 'added';
597                 } else {
598                     $lines[0]++;
599                     $lines[1]++;
600                 }
601             } else {
602                 $lines[0]++;
603                 $lines[1]++;
604             }
605             
606             
607             if ($in_meta) {
608                 $class = 'meta';
609             }
610             $cls = $class;
611             
612             if ($first) {
613                 $cls .= ' first';
614             } 
615             if ($class != 'meta' && $first) {
616                 $first = false;
617             }
618         
619
620             $anchor = $abase . '.' . $nlines;
621             $add = (object)array(
622                 
623                 'text' => $class == 'meta' ? $line : substr($line, 1),
624                 'meta' => $class == 'meta',
625                 'cls' => $cls,
626                 'anchor' =>  $anchor ,
627                 'no' => $lines,
628             );
629             
630             $add->$class = true;
631             
632             $ret[] = $add;
633            
634         
635             if (!count($input)) {
636                 break;
637             }
638             $line = array_shift($input);
639             $nlines++;
640         }
641           
642         if ($nlines == 0) {
643             return null;
644         }
645       
646         return $ret; 
647     }
648     
649     
650     function dayChanges($roo, $q)
651     {
652         // find out the repos that have had changes on that day..
653       
654         //DB_DAtaObject::debugLevel(1);
655          //if ($person_id) { 
656         //    $mc->person_id = $person_id;
657         //}
658         $df = date('Y-m-d 00:00:00', strtotime($q['_daychanges']));
659         $dt = date('Y-m-d 23:59:59', strtotime($q['_daychanges']));
660         if (empty($q['id'])) { 
661         
662             $mc = DB_DataObject::Factory('mtrack_change');
663             $mc->whereAdd("mtrack_change.changedate >= '$df' ");
664             $mc->whereAdd("mtrack_change.changedate < '$dt' ");
665             $mc->ontable = 'mtrack_repos';
666             $mc->selectAdd();
667             $mc->selectAdd('distinct(onid) as onid');
668             $ids = $mc->fetchAll('onid');
669             
670        
671             $this->whereAddIn('mtrack_repos.id', $ids, 'int');
672             return; 
673              
674         }
675         
676         
677         //print_r($ids);exit;
678         
679         $r =DB_DataObject::factory('mtrack_repos');
680         $r->get($q['id']);
681         
682         $ret = array();
683         $imp = $r->impl();
684         if (!$imp) {
685             echo "NO IMP?";
686             echo '<PRE>';print_R($r);
687             return;
688         }
689         //print_r($imp);
690         //echo "RANGE: '{$dt}'..'{$df}'    ";
691         //$r->impl()->debug=true;
692         $object = null;
693         $branch = null;
694         if ($imp->shortname == 'greengarden') {
695             $object = 'branch';
696             $branch = 'roojs';
697         }
698                 
699         $res = $imp->history('.', array('since'=> '{'.$df.'}' , 'before' => '{'.$dt .'}'), $object, $branch);
700         // echo '<PRE>';print_R($res);
701         $add = $r->historyToSummary($res, $df, 'toArray');
702          
703         if ($add) {
704             $ret[] = $add;
705         }
706         
707         //echo '<PRE>';print_R($ret);
708         
709         $roo->jdata($ret);
710         
711     }
712     
713     function historyToSummary($res, $dt, $toarray = false)
714     { 
715         
716         // gather all the files that have changed..
717         // display a summary of who changed what first..
718         //echo '<PRE>';print_R($res);exit;
719         // if no changes happen (which should not really occur as we are triggering
720         // the send out - then a dummy message will get sent..
721         // we can live with this as it indicates something has gone wrong..)
722         
723         $sum = array();
724         $chg = array();
725         
726         $baseday = date('Y-m-d', strtotime($dt));
727         
728         $commits = array();
729         foreach($res as $cg) {
730             $cg->commitday = date('Y-m-d', strtotime($cg->ctime));
731             $cg->committime = date('H:i', strtotime($cg->ctime));
732              
733             
734             $cg->utime =  strtotime($cg->ctime);
735             foreach($cg->files as $k=>$v) {
736                 $sum[$k] = isset($sum[$k]) ? $sum[$k] : array();
737                 $chg[$cg->changeby] = 1;
738                 $sum[$k][$cg->changeby] = isset($sum[$k][$cg->changeby]) ?
739                     ($sum[$k][$cg->changeby] +1) : 1;
740                 
741             }
742             //if ($cg->commitday == $baseday) {
743                 $commits[] = $cg;
744             //}
745             
746         }
747         
748         usort($commits, function($a,$b) {
749             return  $a->utime >   $b->utime ?  1 : -1;
750         });
751         $last = false;
752         
753         foreach($commits as $cg) {
754             $cg->committime = ($last && $last->committime  == $cg->committime) ?
755                 '' : $cg->committime;
756             
757             $cg->commitby = ($last && $cg->changeby == $last->changeby) ?
758                 '' : $cg->changeby;
759             
760             // files array is an array of SVMFileEvents + an ocasionaly stdClass?
761             $cg->files_array = ($last &&
762                         serialize($cg->files_array) == serialize($last->files_array) 
763                         ) ? array() : $cg->files_array;
764             
765             $last = $cg;
766         }
767         
768         
769         //echo '<PRE>';print_R($commits);
770          // then do DIFF on each
771         ksort($sum);
772         $obj = new stdClass;
773         $obj->id = $this->id;
774         $obj->repo = $toarray ? $this->$toarray() : $this;
775         $obj->summary = $sum;
776         $obj->changers = $chg;
777         $obj->filelinks = array();
778         $obj->files = array();
779         // it does not look like we can easily find out what specific changes
780         // where done by each person.. - although it is available via blame.
781         
782         //print_r($res);
783         // we need the last rev..
784         $first = array_shift($res);
785         $last = array_pop($res);
786         //if (!$last) {
787         //    return false;
788         //}
789         
790         //$this->impl()->debug=1;
791         // git log {lastrev} -n 2 << gets us the rev before...
792         // not sure why 2 did not work...
793         $imp = $this->impl();
794         if (!$imp) {
795             print_R($this);exit;
796         }
797         if ($last) {
798             $res = $this->impl()->history('.', 3  , 'rev', $last->rev);
799             $last = $res[1];
800         }
801         //print_r($res);exit;
802         
803         //print_r($last);exit;
804         $body = '';
805         $len = 0;
806         foreach($obj->summary as $file=>$who) { 
807             $res = $this->impl()->diff($file, $last ? $last->rev : $first->rev, $last ? $first->rev : null);
808             //print_r($res);
809             //$lines = $this->parseDiff($res);
810             $lines = stream_get_contents($res);
811             $len += strlen($lines);
812             if ($len > 2000000) {
813                 $lines = "Too large see repo";
814             }
815             
816             if (preg_match('/\.map$/', $file)) {
817                 $lines = "Changed";
818             }
819             
820             if (preg_match('/\.min\.css$/', $file)) {
821                 $lines = "Changed";
822             }
823             // put this in if you plan to delete quite a few files..
824             //if (strpos($lines, '+++ /dev/null') > 1) {
825             //    $lines = "Deleted";
826             //}
827             
828                            
829             
830             $obj->filelinks[$file] = isset($lines[0]->anchor) ? $lines[0]->anchor : false;
831             
832             if ($toarray) {
833                 $obj->files[] = array('filename' => $file, 'lines' => $lines);
834             } else {
835                 $obj->files[$file] = $lines;
836             }
837              
838         }
839        
840         
841         $obj->no_auth = true;
842         $obj->date = date('d M Y', strtotime($dt));
843         $obj->commits = $commits;
844         return $obj;
845     }
846      
847
848     function notifyENDOFDAYMAIL($rcpt, $last_sent_date, $notify, $force)
849     {
850         // fisrst use history to show a list of changes between the dates.
851         //die("building end of day mail");
852         // var_dump($notify->act_start );
853         $start = date('Y-m-d H:i:s', strtotime($notify->act_start . ' - 1 DAY'));
854         $end = date('Y-m-d H:i:s', strtotime($notify->act_start));
855         
856         $object = null;
857         $branch = null;
858         if ($this->impl()->shortname == 'greengarden') {
859             $object = 'branch';
860             $branch = 'roojs';
861         }
862         
863         //$this->impl()->debug=1;
864         //print_r(array('since'=> '{'.$start.'}' , 'before' => '{'.$end .'}'));
865     
866          
867          $res = $this->impl()->history('.', array('since'=> '{'.$start.'}' , 'before' => '{'.$end .'}'), $object, $branch);
868           
869          // find the last rev the day before...
870          
871          
872           
873          $obj = $this->historyToSummary($res, $notify->act_start);
874          if (!$obj) {
875             // echo "History returned nothing";
876              return true;
877          }
878          $obj->from_dt = $start;
879          $obj->to_dt = $end;
880          $obj->branch = $branch ? $branch : 'master';
881             //print_r($obj);exit; 
882           
883         $ret =   $rcpt->buildMail('repo_daily_changes', $obj);
884         
885         return $ret;
886         
887         
888     }
889     // used by diff template... could be better ....
890      
891     function array_value() {
892         $args = func_get_args();
893         $ar = array_shift($args);
894         foreach($args as $a) {
895             if (!is_array($ar) || !isset($ar[$a])) {
896                 return '';
897             }
898             $ar = $ar[$a];
899         }
900         return is_array($ar) ? '' : $ar; 
901     }
902     
903     
904 }
905