3 * Table Definition for mtrack_repos
5 class_exists('DB_DataObject') ? '' : require_once 'DB/DataObject.php';
7 class Pman_MTrack_DataObjects_Mtrack_repos extends DB_DataObject
10 /* the code below is auto generated do not remove the above tag */
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
26 /* the code above is auto generated do not remove the tag below */
30 function applyFilters($q, $au, $roo)
33 if (!empty($q['_daychanges'])) {
34 $this->dayChanges($roo, $q);
37 /// only show unposted..
38 $this->autoImport('/home/git/private'); // should be an incomming variable..
41 $cmd = empty($q['_cmd']) ? '' : $q['_cmd'];
42 // non destructive commands can be run via cmd line.
48 $p = DB_DAtaObject::Factory('Projects');
49 $p->get($this->project_id);
56 function autoImport($dir)
58 // this code is specific for my project at present.. - should be more generic..
60 foreach(scandir($dir) as $d) {
61 if (!file_exists("$dir/$d/HEAD") || !file_exists("$dir/$d/config") ) {
62 continue; // not a git dir..
64 $x = DB_DataObject::factory('mtrack_repos');
65 if ($x->get('repopath', "$dir/$d")) {
69 'repopath' => "$dir/$d",
73 'description' => file_exists("$dir/$d/description") ? file_get_contents("$dir/$d/description") : '',
85 * returns the remaining path.
86 * @param string $pi path like /a/b/cccc/dddd
89 function loadFromPath($pi)
91 $crumbs = explode('/', $pi);
92 if (count($crumbs) < 2) {
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();
103 array_shift($crumbs);
104 array_shift($crumbs);
105 return implode('/', $crumbs);
109 * creates the default public wiki..
111 * -- not sure how we will handle project ones yet..
112 * -- might be subdirectories???
114 function createDefaultWiki()
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]");
121 $p = DB_DataObject::factory('Projects');
122 if ($p->get('code', '*PUBLIC')) {
123 $p->code = '*PUBLIC';
124 $p->name = 'Public Projects';
129 'shortname' => 'wiki',
131 'repopath' => $ff->MTrackWeb['working_dir'] .'/wiki',
134 'description' => 'MTrack Wiki',
138 'project_id' => $p->id,
145 //checkPerm('S'/'E'/'A', $authuser) - can
146 function checkPerm($perm, $au)
149 if ($au && $au->company()->comptype == 'OWNER') {
150 // owner can do anything..????
154 if ($perm == 'E' || $perm == 'D' ) { // Edit and delete ..not allowed..
158 if (!$au && $perm != 'S') {
159 // non-authenticated users can only list stuff..
164 //DB_DataObject::debugLevel(1);
165 $obj = $this->project();
169 //if (!method_exists($obj, 'checkPerm')) {
172 // must have same perm on project..
174 return $obj->checkPerm($perm, $au);
180 function displayName()
182 if (!empty($this->parent)) {
183 list($type, $owner) = explode(':', $this->parent);
184 return "$owner/$this->shortname";
186 return "default/$this->shortname";
189 *return the mtrack_repo implemenation
196 static $repo = array();
197 if (isset($repo[$this->id])) {
198 return $repo[$this->id];
200 require_once 'MTrack/Repo.php';
201 $repo[$this->id] = MTrack_Repo::factory(
204 return $repo[$this->id];
208 function save($do=false)
210 throw new Exception("Error invalid call");
213 function save_changes($DO_CHANGES) {
214 if (!$this->impl()) {
215 throw new Exception("unsupported repo type " . $this->scmtype);
219 $old = DB_DataObject::facotry('repos');
220 $old->get($this->id);
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");
234 $repodir = $cfg['new_repo_basedir'];
237 if (!is_dir($repodir)) {
238 mkdir($repodir,0700, true);
240 $page = HTML_FlexyFramework::get()->page;
241 // in our system - users do not have 'canons' - they have names & email - and that's it..
245 if (!$this->parent) {
246 $owner = $page->authUser->id;
247 $this->parent = 'user:' . $owner;
250 list($type, $owner) = explode(':', $this->parent, 2);
254 $p = DB_DAtaObject::factory('Projects');
255 if (!$p->get($owner)) {
256 throw new Exception("invalid project $owner");
259 //MTrackACL::requireAllRights("project:$P->id", 'modify');
264 if ($owner != $page->authUser->id) {
265 throw new Exception("can't make a repo for another user");
269 throw new Exception("invalid parent ($this->parent)");
274 //if (preg_match("/[^a-zA-Z0-9_.-]/", $owner)) {
275 // throw new Exception("$owner must not contain special characters");
278 $this->repopath = $repodir . DIRECTORY_SEPARATOR . $owner;
280 if (!is_dir($this->repopath)) {
281 mkdir($this->repopath,0700,true);
283 $this->repopath .= DIRECTORY_SEPARATOR . $this->shortname;
286 /* default ACL is allow user all rights, block everybody else */
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),
299 } // end - no repo path set...
304 if ($acl !== null) { // only do this if it's a new repo path..
306 //MTrackACL::setACL("repo:$this->id", 0, $acl);
309 $w = DB_DataObject::factory('core_watch');
313 'userid' => $page->authUser->id,
319 $ww->event = 'ticket';
322 $w->event = 'changeset';
329 $this->impl()->reconcileRepoSettings();
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');
337 if (!is_dir($repodir)) {
340 $repodir .= '/default';
341 if (!is_dir($repodir)) {
344 $repodir .= '/' . $this->shortname;
345 if (!file_exists($repodir)) {
346 symlink($this->repopath, $repodir);
347 } else if (is_link($repodir) && readlink($repodir) != $this->repopath) {
349 symlink($this->repopath, $repodir);
354 $DO_CHANGES->add("repo:" . $this->id , $old, $this);
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]);
361 foreach ($this->links_to_remove as $linkid) {
362 MTrackDB::q('delete from project_repo_link where id = ? and linkid = ?', $this->id, $linkid);
370 function historyWithChangelog($path, $limit = null, $object = null, $ident = null)
373 $ents = $this->impl()->history($path, $limit, $object, $ident);
374 $data = new StdClass;
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);
388 //$changelog = $ent->changelog;
390 //$data->changelog = $changelog;
392 //if (is_array($ent->files)) foreach ($ent->files as $file) {
393 // $file->diff = mtrack_diff($repo->diff($file, $ent->rev));
400 function historyWithChangelogAndDiff($path, $limit = null, $object = null, $ident = null)
402 $ret = $this->impl()->historyWithChangelog($path, $limit, $object, $ident);
406 if (!is_array($ret->ent->files)) {
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)
422 function cloneCommand()
425 $pg = HTML_FlexyFramework::get()->page;
426 $dom = $pg->authUser ? 'roojs.com' : 'roojs.org';
428 if (empty($this->cloneCommand)) {
429 return "git clone http://git.{$dom}/{$this->shortname}";
431 return $this->cloneCommand;
436 // rendering.... - still not in the right place...
438 function descriptionToHtml()
440 return htmlspecialchars($this->description);
442 //require_once 'MTrack/Wiki.php';
443 //return MTrack_Wiki::format_to_html($this->description);
447 function branchObject($n)
449 // create or return branch dataobject
450 $b = DB_DataObject::factory('mtrack_repos_branch');
451 $b->repo_id = $this->id;
453 if ($b->find(true)) {
460 // these should really be phased out, and the Controlers use impl()-> directly..
461 function getBranches()
463 return $this->impl()->getBranches();
467 return $this->impl()->getTags();
469 function readdir($pi, $object = null, $ident = null)
471 return $this->impl()->readdir($pi, $object,$ident);
473 function file($path, $object = null, $ident = null)
475 return $this->impl()->file($path, $object,$ident);
477 function history($path, $limit = null, $object = null, $ident = null)
479 return $this->impl()->history($path, $limit, $object,$ident);
481 function diff($path, $from = null, $to = null)
483 return $this->impl()->diff($path, $from, $to);
486 function getRelatedChanges($revision)
488 return $this->impl()->getRelatedChanges($revision);
490 function getWorkingCopy()
492 return $this->impl()->getWorkingCopy();
495 function getSCMMetaData()
497 return $this->impl()->getSCMMetaData();
499 function getBrowseRootName()
501 return $this->impl()->getBrowseRootName();
503 function reconcileRepoSettings(MTrackSCM $r)
505 return $this->impl()->reconcileRepoSettings($r);
507 function resolveRevision($rev, $object, $ident) {
508 return $this->impl()->resolveRevision($rev, $object, $ident);
511 function getCheckoutCommand() {
512 return $this->impl()->getCheckoutCommand();
516 return $this->impl()->canFork();
519 function parseDiff($input)
524 if (is_resource($input)) {
526 while (($line = fgets($input)) !== false) {
527 $lines[] = rtrim($line, "\r\n");
533 if (is_string($input)) {
534 $abase = md5($input);
535 $input = preg_split("/\r?\n/", $input);
537 $abase = md5(join("\n", $input));
545 if (!count($input)) {
549 $line = array_shift($input);
552 if (!strncmp($line, '@@ ', 3)) {
553 /* done with preamble */
556 $ret[] = (object)array(
561 'anchor' => $abase .'.'.$nlines,
567 $lines = array(0, 0);
575 if (preg_match("/^@@\s+-(\pN+)(?:,\pN+)?\s+\+(\pN+)(?:,\pN+)?\s*@@/",
577 $lines[0] = (int)$M[1] - 1;
578 $lines[1] = (int)$M[2] - 1;
582 } else if (preg_match("/^diff /", $line)) {
586 } elseif (strlen($line)) {
587 if ($line[0] == '-') {
590 } elseif ($line[0] == '+') {
611 if ($class != 'meta' && $first) {
616 $anchor = $abase . '.' . $nlines;
617 $add = (object)array(
619 'text' => $class == 'meta' ? $line : substr($line, 1),
620 'meta' => $class == 'meta',
622 'anchor' => $anchor ,
631 if (!count($input)) {
634 $line = array_shift($input);
646 function dayChanges($roo, $q)
648 // find out the repos that have had changes on that day..
650 //DB_DAtaObject::debugLevel(1);
652 // $mc->person_id = $person_id;
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'])) {
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';
663 $mc->selectAdd('distinct(onid) as onid');
664 $ids = $mc->fetchAll('onid');
667 $this->whereAddIn('mtrack_repos.id', $ids, 'int');
673 //print_r($ids);exit;
675 $r =DB_DataObject::factory('mtrack_repos');
682 echo '<PRE>';print_R($r);
686 //echo "RANGE: '{$dt}'..'{$df}' ";
687 //$r->impl()->debug=true;
690 if ($imp->shortname == 'greengarden') {
695 $res = $imp->history('.', array('since'=> '{'.$df.'}' , 'before' => '{'.$dt .'}'), $object, $branch);
696 // echo '<PRE>';print_R($res);
697 $add = $r->historyToSummary($res, $df, 'toArray');
703 //echo '<PRE>';print_R($ret);
709 function historyToSummary($res, $dt, $toarray = false)
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..)
722 $baseday = date('Y-m-d', strtotime($dt));
725 foreach($res as $cg) {
726 $cg->commitday = date('Y-m-d', strtotime($cg->ctime));
727 $cg->committime = date('H:i', strtotime($cg->ctime));
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;
738 //if ($cg->commitday == $baseday) {
744 usort($commits, function($a,$b) {
745 return $a->utime > $b->utime ? 1 : -1;
749 foreach($commits as $cg) {
750 $cg->committime = ($last && $last->committime == $cg->committime) ?
751 '' : $cg->committime;
753 $cg->commitby = ($last && $cg->changeby == $last->changeby) ?
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;
765 //echo '<PRE>';print_R($commits);
766 // then do DIFF on each
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.
779 // we need the last rev..
780 $first = array_shift($res);
781 $last = array_pop($res);
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();
794 $res = $this->impl()->history('.', 3 , 'rev', $last->rev);
797 //print_r($res);exit;
799 //print_r($last);exit;
802 foreach($obj->summary as $file=>$who) {
803 $res = $this->impl()->diff($file, $last ? $last->rev : $first->rev, $last ? $first->rev : null);
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";
812 if (preg_match('/\.map$/', $file)) {
816 if (preg_match('/\.min\.css$/', $file)) {
819 // put this in if you plan to delete quite a few files..
820 //if (strpos($lines, '+++ /dev/null') > 1) {
821 // $lines = "Deleted";
826 $obj->filelinks[$file] = isset($lines[0]->anchor) ? $lines[0]->anchor : false;
829 $obj->files[] = array('filename' => $file, 'lines' => $lines);
831 $obj->files[$file] = $lines;
837 $obj->no_auth = true;
838 $obj->date = date('d M Y', strtotime($dt));
839 $obj->commits = $commits;
844 function notifyENDOFDAYMAIL($rcpt, $last_sent_date, $notify, $force)
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));
854 if ($this->impl()->shortname == 'greengarden') {
859 //$this->impl()->debug=1;
860 //print_r(array('since'=> '{'.$start.'}' , 'before' => '{'.$end .'}'));
863 $res = $this->impl()->history('.', array('since'=> '{'.$start.'}' , 'before' => '{'.$end .'}'), $object, $branch);
865 // find the last rev the day before...
869 $obj = $this->historyToSummary($res, $notify->act_start);
871 // echo "History returned nothing";
874 $obj->from_dt = $start;
876 $obj->branch = $branch ? $branch : 'master';
877 //print_r($obj);exit;
879 $ret = $rcpt->buildMail('repo_daily_changes', $obj);
885 // used by diff template... could be better ....
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])) {
896 return is_array($ar) ? '' : $ar;