daa5a8f3b4daf3f5d6d4920ba6af56cc3c0f1c43
[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         $b->insert();
457         return $b;
458     }
459     
460     // these should really be phased out, and the Controlers use impl()-> directly..
461     function getBranches()
462     {
463         return $this->impl()->getBranches();
464     }
465     function getTags()
466     {
467         return $this->impl()->getTags();
468     }
469     function readdir($pi, $object = null, $ident = null)
470     {
471         return $this->impl()->readdir($pi, $object,$ident);
472     }
473     function file($path, $object = null, $ident = null)
474     {
475         return $this->impl()->file($path, $object,$ident);
476     }
477     function history($path, $limit = null, $object = null, $ident = null)
478     {
479         return $this->impl()->history($path, $limit, $object,$ident);
480     }
481     function diff($path, $from = null, $to = null)
482     {
483         return $this->impl()->diff($path, $from, $to);
484     }
485  
486     function getRelatedChanges($revision)
487     {
488         return $this->impl()->getRelatedChanges($revision);
489     }
490     function getWorkingCopy()
491     {
492         return $this->impl()->getWorkingCopy();
493     }
494  
495     function getSCMMetaData()
496     {
497         return $this->impl()->getSCMMetaData();   
498     }
499     function getBrowseRootName()
500     {
501         return  $this->impl()->getBrowseRootName();
502     }
503     function reconcileRepoSettings(MTrackSCM $r)
504     {
505         return  $this->impl()->reconcileRepoSettings($r);
506     }
507     function resolveRevision($rev, $object, $ident) {
508         return  $this->impl()->resolveRevision($rev, $object, $ident);
509     }
510     
511     function getCheckoutCommand() {
512         return $this->impl()->getCheckoutCommand();
513     }
514     function canFork()
515     {
516         return $this->impl()->canFork();
517     }
518     
519     function parseDiff($input)
520     {
521          
522         $nlines = 0;
523
524         if (is_resource($input)) {
525             $lines = array();
526             while (($line = fgets($input)) !== false) {
527                 $lines[] = rtrim($line, "\r\n");
528             }
529             $input = $lines;
530         }
531         
532         // abase???
533         if (is_string($input)) {
534             $abase = md5($input);
535             $input = preg_split("/\r?\n/", $input);
536         } else {
537             $abase = md5(join("\n", $input));
538         }
539         
540         $ret = array();
541            
542         while (true) {
543              
544             
545             if (!count($input)) {
546                 break;
547             }
548             
549             $line = array_shift($input);
550             $nlines++;
551             
552             if (!strncmp($line, '@@ ', 3)) {
553                 /* done with preamble */
554                 break;
555             }
556             $ret[] = (object)array(
557                 
558                 'text' => $line,
559                 'meta' => true,
560                 'cls' =>'meta',
561                 'anchor' => $abase .'.'.$nlines,
562             );
563              
564         }
565         $class = 'meta';
566           
567         $lines = array(0, 0);
568         $first = false;
569         
570         $in_meta = false;
571         
572         while (true) {
573             $class = 'unmod';
574             
575             if (preg_match("/^@@\s+-(\pN+)(?:,\pN+)?\s+\+(\pN+)(?:,\pN+)?\s*@@/",
576                   $line, $M)) {
577                 $lines[0] = (int)$M[1] - 1;
578                 $lines[1] = (int)$M[2] - 1;
579                 $class = 'meta';
580                 $first = true;
581                 $in_meta = false;
582             } else if (preg_match("/^diff /", $line)) {
583                 $class = 'meta';
584                 $in_meta=true;
585             
586             } elseif (strlen($line)) {
587                 if ($line[0] == '-') {
588                     $lines[0]++;
589                     $class = 'removed';
590                 } elseif ($line[0] == '+') {
591                     $lines[1]++;
592                     $class = 'added';
593                 } else {
594                     $lines[0]++;
595                     $lines[1]++;
596                 }
597             } else {
598                 $lines[0]++;
599                 $lines[1]++;
600             }
601             
602             
603             if ($in_meta) {
604                 $class = 'meta';
605             }
606             $cls = $class;
607             
608             if ($first) {
609                 $cls .= ' first';
610             } 
611             if ($class != 'meta' && $first) {
612                 $first = false;
613             }
614         
615
616             $anchor = $abase . '.' . $nlines;
617             $add = (object)array(
618                 
619                 'text' => $class == 'meta' ? $line : substr($line, 1),
620                 'meta' => $class == 'meta',
621                 'cls' => $cls,
622                 'anchor' =>  $anchor ,
623                 'no' => $lines,
624             );
625             
626             $add->$class = true;
627             
628             $ret[] = $add;
629            
630         
631             if (!count($input)) {
632                 break;
633             }
634             $line = array_shift($input);
635             $nlines++;
636         }
637           
638         if ($nlines == 0) {
639             return null;
640         }
641       
642         return $ret; 
643     }
644     
645     
646     function dayChanges($roo, $q)
647     {
648         // find out the repos that have had changes on that day..
649       
650         //DB_DAtaObject::debugLevel(1);
651          //if ($person_id) { 
652         //    $mc->person_id = $person_id;
653         //}
654         $df = date('Y-m-d 00:00:00', strtotime($q['_daychanges']));
655         $dt = date('Y-m-d 23:59:59', strtotime($q['_daychanges']));
656         if (empty($q['id'])) { 
657         
658             $mc = DB_DataObject::Factory('mtrack_change');
659             $mc->whereAdd("mtrack_change.changedate >= '$df' ");
660             $mc->whereAdd("mtrack_change.changedate < '$dt' ");
661             $mc->ontable = 'mtrack_repos';
662             $mc->selectAdd();
663             $mc->selectAdd('distinct(onid) as onid');
664             $ids = $mc->fetchAll('onid');
665             
666        
667             $this->whereAddIn('mtrack_repos.id', $ids, 'int');
668             return; 
669              
670         }
671         
672         
673         //print_r($ids);exit;
674         
675         $r =DB_DataObject::factory('mtrack_repos');
676         $r->get($q['id']);
677         
678         $ret = array();
679         $imp = $r->impl();
680         if (!$imp) {
681             echo "NO IMP?";
682             echo '<PRE>';print_R($r);
683             return;
684         }
685         //print_r($imp);
686         //echo "RANGE: '{$dt}'..'{$df}'    ";
687         //$r->impl()->debug=true;
688         $object = null;
689         $branch = null;
690         if ($imp->shortname == 'greengarden') {
691             $object = 'branch';
692             $branch = 'roojs';
693         }
694                 
695         $res = $imp->history('.', array('since'=> '{'.$df.'}' , 'before' => '{'.$dt .'}'), $object, $branch);
696         // echo '<PRE>';print_R($res);
697         $add = $r->historyToSummary($res, $df, 'toArray');
698          
699         if ($add) {
700             $ret[] = $add;
701         }
702         
703         //echo '<PRE>';print_R($ret);
704         
705         $roo->jdata($ret);
706         
707     }
708     
709     function historyToSummary($res, $dt, $toarray = false)
710     { 
711         
712         // gather all the files that have changed..
713         // display a summary of who changed what first..
714         //echo '<PRE>';print_R($res);exit;
715         // if no changes happen (which should not really occur as we are triggering
716         // the send out - then a dummy message will get sent..
717         // we can live with this as it indicates something has gone wrong..)
718         
719         $sum = array();
720         $chg = array();
721         
722         $baseday = date('Y-m-d', strtotime($dt));
723         
724         $commits = array();
725         foreach($res as $cg) {
726             $cg->commitday = date('Y-m-d', strtotime($cg->ctime));
727             $cg->committime = date('H:i', strtotime($cg->ctime));
728              
729             
730             $cg->utime =  strtotime($cg->ctime);
731             foreach($cg->files as $k=>$v) {
732                 $sum[$k] = isset($sum[$k]) ? $sum[$k] : array();
733                 $chg[$cg->changeby] = 1;
734                 $sum[$k][$cg->changeby] = isset($sum[$k][$cg->changeby]) ?
735                     ($sum[$k][$cg->changeby] +1) : 1;
736                 
737             }
738             //if ($cg->commitday == $baseday) {
739                 $commits[] = $cg;
740             //}
741             
742         }
743         
744         usort($commits, function($a,$b) {
745             return  $a->utime >   $b->utime ?  1 : -1;
746         });
747         $last = false;
748         
749         foreach($commits as $cg) {
750             $cg->committime = ($last && $last->committime  == $cg->committime) ?
751                 '' : $cg->committime;
752             
753             $cg->commitby = ($last && $cg->changeby == $last->changeby) ?
754                 '' : $cg->changeby;
755             
756             // files array is an array of SVMFileEvents + an ocasionaly stdClass?
757             $cg->files_array = ($last &&
758                         serialize($cg->files_array) == serialize($last->files_array) 
759                         ) ? array() : $cg->files_array;
760             
761             $last = $cg;
762         }
763         
764         
765         //echo '<PRE>';print_R($commits);
766          // then do DIFF on each
767         ksort($sum);
768         $obj = new stdClass;
769         $obj->id = $this->id;
770         $obj->repo = $toarray ? $this->$toarray() : $this;
771         $obj->summary = $sum;
772         $obj->changers = $chg;
773         $obj->filelinks = array();
774         $obj->files = array();
775         // it does not look like we can easily find out what specific changes
776         // where done by each person.. - although it is available via blame.
777         
778         //print_r($res);
779         // we need the last rev..
780         $first = array_shift($res);
781         $last = array_pop($res);
782         //if (!$last) {
783         //    return false;
784         //}
785         
786         //$this->impl()->debug=1;
787         // git log {lastrev} -n 2 << gets us the rev before...
788         // not sure why 2 did not work...
789         $imp = $this->impl();
790         if (!$imp) {
791             print_R($this);exit;
792         }
793         if ($last) {
794             $res = $this->impl()->history('.', 3  , 'rev', $last->rev);
795             $last = $res[1];
796         }
797         //print_r($res);exit;
798         
799         //print_r($last);exit;
800         $body = '';
801         $len = 0;
802         foreach($obj->summary as $file=>$who) { 
803             $res = $this->impl()->diff($file, $last ? $last->rev : $first->rev, $last ? $first->rev : null);
804             //print_r($res);
805             //$lines = $this->parseDiff($res);
806             $lines = stream_get_contents($res);
807             $len += strlen($lines);
808             if ($len > 2000000) {
809                 $lines = "Too large see repo";
810             }
811             
812             if (preg_match('/\.map$/', $file)) {
813                 $lines = "Changed";
814             }
815             
816             if (preg_match('/\.min\.css$/', $file)) {
817                 $lines = "Changed";
818             }
819             // put this in if you plan to delete quite a few files..
820             //if (strpos($lines, '+++ /dev/null') > 1) {
821             //    $lines = "Deleted";
822             //}
823             
824                            
825             
826             $obj->filelinks[$file] = isset($lines[0]->anchor) ? $lines[0]->anchor : false;
827             
828             if ($toarray) {
829                 $obj->files[] = array('filename' => $file, 'lines' => $lines);
830             } else {
831                 $obj->files[$file] = $lines;
832             }
833              
834         }
835        
836         
837         $obj->no_auth = true;
838         $obj->date = date('d M Y', strtotime($dt));
839         $obj->commits = $commits;
840         return $obj;
841     }
842      
843
844     function notifyENDOFDAYMAIL($rcpt, $last_sent_date, $notify, $force)
845     {
846         // fisrst use history to show a list of changes between the dates.
847         //die("building end of day mail");
848         // var_dump($notify->act_start );
849         $start = date('Y-m-d H:i:s', strtotime($notify->act_start . ' - 1 DAY'));
850         $end = date('Y-m-d H:i:s', strtotime($notify->act_start));
851         
852         $object = null;
853         $branch = null;
854         if ($this->impl()->shortname == 'greengarden') {
855             $object = 'branch';
856             $branch = 'roojs';
857         }
858         
859         //$this->impl()->debug=1;
860         //print_r(array('since'=> '{'.$start.'}' , 'before' => '{'.$end .'}'));
861     
862          
863          $res = $this->impl()->history('.', array('since'=> '{'.$start.'}' , 'before' => '{'.$end .'}'), $object, $branch);
864           
865          // find the last rev the day before...
866          
867          
868           
869          $obj = $this->historyToSummary($res, $notify->act_start);
870          if (!$obj) {
871             // echo "History returned nothing";
872              return true;
873          }
874          $obj->from_dt = $start;
875          $obj->to_dt = $end;
876          $obj->branch = $branch ? $branch : 'master';
877             //print_r($obj);exit; 
878           
879         $ret =   $rcpt->buildMail('repo_daily_changes', $obj);
880         
881         return $ret;
882         
883         
884     }
885     // used by diff template... could be better ....
886      
887     function array_value() {
888         $args = func_get_args();
889         $ar = array_shift($args);
890         foreach($args as $a) {
891             if (!is_array($ar) || !isset($ar[$a])) {
892                 return '';
893             }
894             $ar = $ar[$a];
895         }
896         return is_array($ar) ? '' : $ar; 
897     }
898     
899     
900 }
901