60d01bfc0dbe52ca0d11b709e78213a1de7670b8
[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_CHANGES) {
209         if (!$this->impl()) {
210           throw new Exception("unsupported repo type " . $this->scmtype);
211         }
212         $old = false;
213         if ($this->id) {
214             $old = DB_DataObject::facotry('repos');
215             $old->get($this->id);
216             $this->update();
217              
218         }
219         // NEW...
220             
221         $acl = null;
222     
223         if (!strlen($this->repopath)) {
224             $cfg = HTML_FlexyFramework::get()->MTrack;
225             if (empty($cfg['new_repo_basedir'])) { 
226                 throw new Exception("configuration does not allow repo creation - set new_repo_basedir");
227             }
228             
229             $repodir = $cfg['new_repo_basedir'];
230             
231             // bit hopefull.
232             if (!is_dir($repodir)) {
233                 mkdir($repodir,0700, true);
234             }
235             $page = HTML_FlexyFramework::get()->page;
236             // in our system - users do not have 'canons' - they have names & email - and that's it..
237             
238             
239             // validate parent..
240             if (!$this->parent) {
241                 $owner = $page->authUser->id;
242                 $this->parent = 'user:' . $owner;
243             } else {
244                 
245                 list($type, $owner) = explode(':', $this->parent, 2);
246                 
247                 switch ($type) {
248                     case 'project':
249                         $p = DB_DAtaObject::factory('Projects');
250                         if (!$p->get($owner)) {
251                             throw new Exception("invalid project $owner");
252                         }
253                     
254                         //MTrackACL::requireAllRights("project:$P->id", 'modify');
255                         break;
256                 
257                     case 'user':
258                         
259                         if ($owner != $page->authUser->id) {
260                             throw new Exception("can't make a repo for another user");
261                         }
262                         break;
263                     default:
264                         throw new Exception("invalid parent ($this->parent)");
265                 }
266             }
267             
268             
269             //if (preg_match("/[^a-zA-Z0-9_.-]/", $owner)) {
270             //  throw new Exception("$owner must not contain special characters");
271             //}
272             
273             $this->repopath = $repodir . DIRECTORY_SEPARATOR . $owner;
274             
275             if (!is_dir($this->repopath)) {
276                 mkdir($this->repopath,0700,true);
277             }
278             $this->repopath .= DIRECTORY_SEPARATOR . $this->shortname;
279             
280     
281             /* default ACL is allow user all rights, block everybody else */
282             $acl = array(
283                 array($owner, 'read', 1),
284                 array($owner, 'modify', 1),
285                 array($owner, 'delete', 1),
286                 array($owner, 'checkout', 1),
287                 array($owner, 'commit', 1),
288                 array('*', 'read', 0),
289                 array('*', 'modify', 0),
290                 array('*', 'delete', 0),
291                 array('*', 'checkout', 0),
292                 array('*', 'commit', 0),
293             );
294         } // end - no repo path set... 
295         $this->insert();
296     
297         $old = null;
298         
299         if ($acl !== null) { // only do this if it's a new repo path..
300             
301             //MTrackACL::setACL("repo:$this->id", 0, $acl);
302             
303             
304             $w = DB_DataObject::factory('core_watch');
305             $w->setFrom(array(
306                 'otype'  => 'repo',
307                 'oid'    => $this->id,
308                 'userid' => $page->authUser->id,
309                 'medium' => 'email',
310                 'active' => 1
311             ));
312             $ww = clone($w);
313             
314             $ww->event = 'ticket';
315             $ww->insert();
316             
317             $w->event = 'changeset';
318             $w->insert();
319             
320           
321         }
322         
323         
324         $this->impl()->reconcileRepoSettings();
325         
326         /*
327         if (!$this->parent) {
328            // for SSH access, populate a symlink from the repos basedir to the
329            // actual path for this repo 
330             $repodir = MTrackConfig::get('repos', 'basedir');
331             
332             if (!is_dir($repodir)) {
333                   mkdir($repodir);
334             }
335             $repodir .= '/default';
336             if (!is_dir($repodir)) {
337               mkdir($repodir);
338             }
339             $repodir .= '/' . $this->shortname;
340             if (!file_exists($repodir)) {
341               symlink($this->repopath, $repodir);
342             } else if (is_link($repodir) && readlink($repodir) != $this->repopath) {
343               unlink($repodir);
344               symlink($this->repopath, $repodir);
345             }
346         }
347         */
348         //
349         $DO_CHANGES->add("repo:" . $this->id , $old, $this);
350          
351          
352          /*
353         foreach ($this->links_to_add as $link) {
354             MTrackDB::q('insert into project_repo_link (projid, id, repopathregex) values (?, ?, ?)', $link[0], $this->id, $link[1]);
355         }
356         foreach ($this->links_to_remove as $linkid) {
357             MTrackDB::q('delete from project_repo_link where id = ? and linkid = ?', $this->id, $linkid);
358         }
359          */
360     }
361     
362     
363     
364
365     function historyWithChangelog($path, $limit = null, $object = null,   $ident = null) 
366     {
367       
368         $ents = $this->impl()->history($path, $limit, $object, $ident);
369         $data = new StdClass;
370         if (!count($ents)) {
371             $data->ent = null;
372             return $data;
373         }
374         $ent = $ents[0];
375         $data->ent = $ent;
376
377         // Determine project from the file list
378         //$the_proj = $this->projectFromPath($ent->files);
379         //if ($the_proj > 1) {
380         //  $proj = MTrackProject::loadById($the_proj);
381         //  $changelog = $proj->adjust_links($ent->changelog, true);
382         //} else {
383           //$changelog = $ent->changelog;
384         //}
385         //$data->changelog = $changelog;
386
387         //if (is_array($ent->files)) foreach ($ent->files as $file) {
388         //  $file->diff = mtrack_diff($repo->diff($file, $ent->rev));
389         //}
390       
391
392         return $data;
393     }
394     
395     function historyWithChangelogAndDiff($path, $limit = null, $object = null,   $ident = null)
396     {
397         $ret = $this->impl()->historyWithChangelog($path, $limit, $object, $ident);
398         if (!$ret->ent) {
399             return $ret;
400         }
401         if (!is_array($ret->ent->files)) {
402             return $ret;
403         }
404         foreach ($ret->ent->files as $file) {
405             // where is mtrack_diff...
406             $page = HTML_Flexyframework::get()->page;
407             $file->diff = $page->link->diff(
408                                 $this->impl()->diff($file, $ret->ent->rev)
409                         );
410         }
411       
412
413         return $data;
414     }
415     
416     
417     function cloneCommand()
418     {
419         //hacky..
420         $pg  = HTML_FlexyFramework::get()->page;
421         $dom = $pg->authUser ? 'roojs.com' : 'roojs.org';
422         
423         if (empty($this->cloneCommand)) {
424             return "git clone http://git.{$dom}/{$this->shortname}";
425         }
426         return $this->cloneCommand;
427         
428         
429     }
430     
431     // rendering.... - still not in the right place...
432     
433     function descriptionToHtml()
434     {
435         return htmlspecialchars($this->description);
436         
437         //require_once 'MTrack/Wiki.php';
438         //return  MTrack_Wiki::format_to_html($this->description);
439     }
440     
441     
442     function branchObject($n)
443     {
444         // create or return branch dataobject
445         $b = DB_DataObject::factory('mtrack_repos_branch');
446         $b->repo_id = $this->id;
447         $b->name = $n;
448         if ($b->find(true)) {
449             return $b;
450         }
451         $b->insert();
452         return $b;
453         
454     }
455     
456     // these should really be phased out, and the Controlers use impl()-> directly..
457     function getBranches()
458     {
459         return $this->impl()->getBranches();
460     }
461     function getTags()
462     {
463         return $this->impl()->getTags();
464     }
465     function readdir($pi, $object = null, $ident = null)
466     {
467         return $this->impl()->readdir($pi, $object,$ident);
468     }
469     function file($path, $object = null, $ident = null)
470     {
471         return $this->impl()->file($path, $object,$ident);
472     }
473     function history($path, $limit = null, $object = null, $ident = null)
474     {
475         return $this->impl()->history($path, $limit, $object,$ident);
476     }
477     function diff($path, $from = null, $to = null)
478     {
479         return $this->impl()->diff($path, $from, $to);
480     }
481  
482     function getRelatedChanges($revision)
483     {
484         return $this->impl()->getRelatedChanges($revision);
485     }
486     function getWorkingCopy()
487     {
488         return $this->impl()->getWorkingCopy();
489     }
490  
491     function getSCMMetaData()
492     {
493         return $this->impl()->getSCMMetaData();   
494     }
495     function getBrowseRootName()
496     {
497         return  $this->impl()->getBrowseRootName();
498     }
499     function reconcileRepoSettings(MTrackSCM $r)
500     {
501         return  $this->impl()->reconcileRepoSettings($r);
502     }
503     function resolveRevision($rev, $object, $ident) {
504         return  $this->impl()->resolveRevision($rev, $object, $ident);
505     }
506     
507     function getCheckoutCommand() {
508         return $this->impl()->getCheckoutCommand();
509     }
510     function canFork()
511     {
512         return $this->impl()->canFork();
513     }
514     
515     function parseDiff($input)
516     {
517          
518         $nlines = 0;
519
520         if (is_resource($input)) {
521             $lines = array();
522             while (($line = fgets($input)) !== false) {
523                 $lines[] = rtrim($line, "\r\n");
524             }
525             $input = $lines;
526         }
527         
528         // abase???
529         if (is_string($input)) {
530             $abase = md5($input);
531             $input = preg_split("/\r?\n/", $input);
532         } else {
533             $abase = md5(join("\n", $input));
534         }
535         
536         $ret = array();
537            
538         while (true) {
539              
540             
541             if (!count($input)) {
542                 break;
543             }
544             
545             $line = array_shift($input);
546             $nlines++;
547             
548             if (!strncmp($line, '@@ ', 3)) {
549                 /* done with preamble */
550                 break;
551             }
552             $ret[] = (object)array(
553                 
554                 'text' => $line,
555                 'meta' => true,
556                 'cls' =>'meta',
557                 'anchor' => $abase .'.'.$nlines,
558             );
559              
560         }
561         $class = 'meta';
562           
563         $lines = array(0, 0);
564         $first = false;
565         
566         $in_meta = false;
567         
568         while (true) {
569             $class = 'unmod';
570             
571             if (preg_match("/^@@\s+-(\pN+)(?:,\pN+)?\s+\+(\pN+)(?:,\pN+)?\s*@@/",
572                   $line, $M)) {
573                 $lines[0] = (int)$M[1] - 1;
574                 $lines[1] = (int)$M[2] - 1;
575                 $class = 'meta';
576                 $first = true;
577                 $in_meta = false;
578             } else if (preg_match("/^diff /", $line)) {
579                 $class = 'meta';
580                 $in_meta=true;
581             
582             } elseif (strlen($line)) {
583                 if ($line[0] == '-') {
584                     $lines[0]++;
585                     $class = 'removed';
586                 } elseif ($line[0] == '+') {
587                     $lines[1]++;
588                     $class = 'added';
589                 } else {
590                     $lines[0]++;
591                     $lines[1]++;
592                 }
593             } else {
594                 $lines[0]++;
595                 $lines[1]++;
596             }
597             
598             
599             if ($in_meta) {
600                 $class = 'meta';
601             }
602             $cls = $class;
603             
604             if ($first) {
605                 $cls .= ' first';
606             } 
607             if ($class != 'meta' && $first) {
608                 $first = false;
609             }
610         
611
612             $anchor = $abase . '.' . $nlines;
613             $add = (object)array(
614                 
615                 'text' => $class == 'meta' ? $line : substr($line, 1),
616                 'meta' => $class == 'meta',
617                 'cls' => $cls,
618                 'anchor' =>  $anchor ,
619                 'no' => $lines,
620             );
621             
622             $add->$class = true;
623             
624             $ret[] = $add;
625            
626         
627             if (!count($input)) {
628                 break;
629             }
630             $line = array_shift($input);
631             $nlines++;
632         }
633           
634         if ($nlines == 0) {
635             return null;
636         }
637       
638         return $ret; 
639     }
640     
641     
642     function dayChanges($roo, $q)
643     {
644         // find out the repos that have had changes on that day..
645       
646         //DB_DAtaObject::debugLevel(1);
647          //if ($person_id) { 
648         //    $mc->person_id = $person_id;
649         //}
650         $df = date('Y-m-d 00:00:00', strtotime($q['_daychanges']));
651         $dt = date('Y-m-d 23:59:59', strtotime($q['_daychanges']));
652         if (empty($q['id'])) { 
653         
654             $mc = DB_DataObject::Factory('mtrack_change');
655             $mc->whereAdd("mtrack_change.changedate >= '$df' ");
656             $mc->whereAdd("mtrack_change.changedate < '$dt' ");
657             $mc->ontable = 'mtrack_repos';
658             $mc->selectAdd();
659             $mc->selectAdd('distinct(onid) as onid');
660             $ids = $mc->fetchAll('onid');
661             
662        
663             $this->whereAddIn('mtrack_repos.id', $ids, 'int');
664             return; 
665              
666         }
667         
668         
669         //print_r($ids);exit;
670         
671         $r =DB_DataObject::factory('mtrack_repos');
672         $r->get($q['id']);
673         
674         $ret = array();
675         $imp = $r->impl();
676         if (!$imp) {
677             echo "NO IMP?";
678             echo '<PRE>';print_R($r);
679             return;
680         }
681         //print_r($imp);
682         //echo "RANGE: '{$dt}'..'{$df}'    ";
683         //$r->impl()->debug=true;
684         $object = null;
685         $branch = null;
686         if ($imp->shortname == 'greengarden') {
687             $object = 'branch';
688             $branch = 'roojs';
689         }
690                 
691         $res = $imp->history('.', array('since'=> '{'.$df.'}' , 'before' => '{'.$dt .'}'), $object, $branch);
692         // echo '<PRE>';print_R($res);
693         $add = $r->historyToSummary($res, $df, 'toArray');
694          
695         if ($add) {
696             $ret[] = $add;
697         }
698         
699         //echo '<PRE>';print_R($ret);
700         
701         $roo->jdata($ret);
702         
703     }
704     
705     function historyToSummary($res, $dt, $toarray = false)
706     { 
707         
708         // gather all the files that have changed..
709         // display a summary of who changed what first..
710         //echo '<PRE>';print_R($res);exit;
711         // if no changes happen (which should not really occur as we are triggering
712         // the send out - then a dummy message will get sent..
713         // we can live with this as it indicates something has gone wrong..)
714         
715         $sum = array();
716         $chg = array();
717         
718         $baseday = date('Y-m-d', strtotime($dt));
719         
720         $commits = array();
721         foreach($res as $cg) {
722             $cg->commitday = date('Y-m-d', strtotime($cg->ctime));
723             $cg->committime = date('H:i', strtotime($cg->ctime));
724              
725             
726             $cg->utime =  strtotime($cg->ctime);
727             foreach($cg->files as $k=>$v) {
728                 $sum[$k] = isset($sum[$k]) ? $sum[$k] : array();
729                 $chg[$cg->changeby] = 1;
730                 $sum[$k][$cg->changeby] = isset($sum[$k][$cg->changeby]) ?
731                     ($sum[$k][$cg->changeby] +1) : 1;
732                 
733             }
734             //if ($cg->commitday == $baseday) {
735                 $commits[] = $cg;
736             //}
737             
738         }
739         
740         usort($commits, function($a,$b) {
741             return  $a->utime >   $b->utime ?  1 : -1;
742         });
743         $last = false;
744         
745         foreach($commits as $cg) {
746             $cg->committime = ($last && $last->committime  == $cg->committime) ?
747                 '' : $cg->committime;
748             
749             $cg->commitby = ($last && $cg->changeby == $last->changeby) ?
750                 '' : $cg->changeby;
751                 
752             $cg->files_array = ($last &&
753                         implode('|', $cg->files_array) == implode('|', $last->files_array) 
754                         ) ? array() : $cg->files_array;
755             
756             $last = $cg;
757         }
758         
759         
760         //echo '<PRE>';print_R($commits);
761          // then do DIFF on each
762         ksort($sum);
763         $obj = new stdClass;
764         $obj->id = $this->id;
765         $obj->repo = $toarray ? $this->$toarray() : $this;
766         $obj->summary = $sum;
767         $obj->changers = $chg;
768         $obj->filelinks = array();
769         $obj->files = array();
770         // it does not look like we can easily find out what specific changes
771         // where done by each person.. - although it is available via blame.
772         
773         //print_r($res);
774         // we need the last rev..
775         $first = array_shift($res);
776         $last = array_pop($res);
777         //if (!$last) {
778         //    return false;
779         //}
780         
781         //$this->impl()->debug=1;
782         // git log {lastrev} -n 2 << gets us the rev before...
783         // not sure why 2 did not work...
784         $imp = $this->impl();
785         if (!$imp) {
786             print_R($this);exit;
787         }
788         if ($last) {
789             $res = $this->impl()->history('.', 3  , 'rev', $last->rev);
790             $last = $res[1];
791         }
792         //print_r($res);exit;
793         
794         //print_r($last);exit;
795         $body = '';
796         $len = 0;
797         foreach($obj->summary as $file=>$who) { 
798             $res = $this->impl()->diff($file, $last ? $last->rev : $first->rev, $last ? $first->rev : null);
799             //print_r($res);
800             //$lines = $this->parseDiff($res);
801             $lines = stream_get_contents($res);
802             $len += strlen($lines);
803             if ($len > 2000000) {
804                 $lines = "Too large see repo";
805             }
806             
807             if (preg_match('/\.map$/', $file)) {
808                 $lines = "Changed";
809             }
810             
811             if (preg_match('/\.min\.css$/', $file)) {
812                 $lines = "Changed";
813             }
814             // put this in if you plan to delete quite a few files..
815             //if (strpos($lines, '+++ /dev/null') > 1) {
816             //    $lines = "Deleted";
817             //}
818             
819                            
820             
821             $obj->filelinks[$file] = isset($lines[0]->anchor) ? $lines[0]->anchor : false;
822             
823             if ($toarray) {
824                 $obj->files[] = array('filename' => $file, 'lines' => $lines);
825             } else {
826                 $obj->files[$file] = $lines;
827             }
828              
829         }
830        
831         
832         $obj->no_auth = true;
833         $obj->date = date('d M Y', strtotime($dt));
834         $obj->commits = $commits;
835         return $obj;
836     }
837      
838
839     function notifyENDOFDAYMAIL($rcpt, $last_sent_date, $notify, $force)
840     {
841         // fisrst use history to show a list of changes between the dates.
842         //die("building end of day mail");
843         // var_dump($notify->act_start );
844         $start = date('Y-m-d H:i:s', strtotime($notify->act_start . ' - 1 DAY'));
845         $end = date('Y-m-d H:i:s', strtotime($notify->act_start));
846         
847         $object = null;
848         $branch = null;
849         if ($this->impl()->shortname == 'greengarden') {
850             $object = 'branch';
851             $branch = 'roojs';
852         }
853         
854         //$this->impl()->debug=1;
855         //print_r(array('since'=> '{'.$start.'}' , 'before' => '{'.$end .'}'));
856         $res = $this->impl()->history('.', array('since'=> '{'.$start.'}' , 'before' => '{'.$end .'}'), 'rev', '--all');
857         
858         $branches = array();
859         foreach($res as $r) {
860             if (!preg_match('#^refs/heads/.*$#', $r->branch)) {
861                 continue;
862             }
863             $br = preg_replace('#^refs/heads/.*$#', $r->branch, '');
864             $branches[$br] = 1;
865                 
866         }
867         $objs = array();
868         foreach($branches as $branch=>$one) {
869             
870             
871             $res = $this->impl()->history('.', array('since'=> '{'.$start.'}' , 'before' => '{'.$end .'}'), $object, $branch);
872              
873             // find the last rev the day before...
874             
875             
876              
877             $obj = $this->historyToSummary($res, $notify->act_start);
878             if (!$obj) {
879                // echo "History returned nothing";
880                 return true;
881             }
882             $obj->from_dt = $start;
883             $obj->to_dt = $end;
884             $obj->branch = $br;
885             //print_r($obj);exit; 
886          
887             $objs[] = $obj;
888         }
889         $ret =   $rcpt->buildMail('repo_daily_changes', array('branches' => $obj));
890         
891         return $ret;
892         
893         
894         //$rcpt->sendTemplate('repo_daily_changes', $obj);
895         
896         //echo '<PRE>'.htmlspecialchars(print_r($mr,true));
897         
898         //exit;
899          
900         
901         
902         
903     }
904     // used by diff template... could be better ....
905      
906     function array_value() {
907         $args = func_get_args();
908         $ar = array_shift($args);
909         foreach($args as $a) {
910             if (!is_array($ar) || !isset($ar[$a])) {
911                 return '';
912             }
913             $ar = $ar[$a];
914         }
915         return is_array($ar) ? '' : $ar; 
916     }
917     
918     
919 }
920