MTrack/Repo.php
[web.mtrack] / MTrack / Repo.php
1 <?php
2 require_once 'MTrack/SCM.php';
3 require_once 'MTrack/DB.php';
4 require_once 'MTrack/Config.php';
5 require_once 'MTrack/Project.php';
6 require_once 'MTrack/SCMFileEvent.php';
7 require_once 'MTrack/ACL.php';
8 require_once 'MTrack/Changeset.php';
9 require_once 'MTrack/Wiki.php';
10
11
12 class MTrack_Repo extends MTrackSCM 
13 {
14     public $repoid = null;
15     public $shortname = null;
16     public $scmtype = null;
17     public $repopath = null;
18     public $browserurl = null;
19     public $browsertype = null;
20     public $description = null;
21     public $parent = '';
22     public $clonedfrom = null;
23     public $serverurl = null;
24     
25     
26     private $links_to_add = array();
27     private $links_to_remove = array();
28     private $links = null;
29     static $scms = array();
30     /**
31      *load class and create instance using array as properties
32      */
33     
34     static function factory($ar)
35     {
36         print_r($ar);
37         $type = ucfirst($ar['scmtype']);
38         $fn = 'MTrack/SCM/'.$type .'/Repo.php';
39         $cls = 'MTrack_SCM_'.$type .'_Repo';
40         require_once $fn;
41         
42         $ret = new $cls($ar);
43         
44         return $ret;
45         
46     }
47     
48  
49     static function getAvailableSCMs()
50     {
51         $ret = array();
52         $ar = scandir(dirname(__FILE__).'/SCM');
53         
54         foreach($ar as $a) {
55             if (empty($a) || $a[0] == '.') {
56                 continue;
57             }
58             $fn = dirname(__FILE__).'/SCM/'.$a.'/Repo.php';
59             if (!file_exists($fn)) {
60                 continue;
61             } 
62             $ret[$a] = MTrack_Repo::factory(array('scmtype'=> $a));
63             
64         }
65         return $ret;
66     }  
67     
68     
69     /*static function loadById($id) {
70         list($row) = MTrackDB::q(
71           'select repoid, scmtype from repos where repoid = ?',
72           $id)->fetchAll();
73         if (isset($row[0])) {
74           $type = $row[1];
75           if (isset(self::$scms[$type])) {
76             $class = self::$scms[$type];
77             return new $class($row[0]);
78           }
79           throw new Exception("unsupported repo type $type");
80         }
81         return null;
82     }
83     static function loadByName($name) {
84         $bits = explode('/', $name);
85         if (count($bits) > 1 && $bits[0] == 'default') {
86           array_shift($bits);
87           $name = $bits[0];
88         }
89         if (count($bits) > 1) {
90           // wez/reponame -> per user repo 
91           $u = "user:$bits[0]";
92           $p = "project:$bits[0]";
93           $rows = MTrackDB::q(
94             'select repoid, scmtype from repos where shortname = ? and (parent = ? OR parent = ?)',
95             $bits[1], $u, $p)->fetchAll();
96         } else {
97           $rows = MTrackDB::q(
98             "select repoid, scmtype from repos where shortname = ? and parent =''",
99             $name)->fetchAll();
100         }
101         if (is_array($rows) && isset($rows[0])) {
102           $row = $rows[0];
103           if (isset($row[0])) {
104             $type = $row[1];
105             if (isset(self::$scms[$type])) {
106               $class = self::$scms[$type];
107               return new $class($row[0]);
108             }
109             throw new Exception("unsupported repo type $type");
110           }
111         }
112         return null;
113     }
114     static function loadByLocation($path) 
115     {
116           
117         // we have magic configuration - end users commit into SVN
118         // backend is really git... - so pre-commit hooks have to be from svn
119         list($row) = MTrackDB::q('select repoid, scmtype from repos where repopath = ?', $path)->fetchAll();
120         if (isset($row[0])) {
121           $type = $row[1];
122           if (isset(self::$scms[$type])) {
123             $class = self::$scms[$type];
124             return new $class($row[0]);
125           }
126           throw new Exception("unsupported repo type $type");
127         }
128         return null;
129       }
130   
131     static function loadByChangeSet($cs)
132     {
133         
134         static $re = array();
135         if (isset($re[$cs])) {
136             return $re[$cs];
137         }
138         //using (repoid)  ??
139         $q = MTrackDB::q("
140             select 
141                 r.shortname as repo, 
142                 p.shortname as proj 
143             from 
144                 repos r 
145             left join 
146                 project_repo_link l on r.repoid = l.repoid
147             left join 
148                 projects p on p.projid = r.projectid
149             where 
150                 (parent is null  or length(parent) = 0)
151             AND
152                 (
153                     ( ? like CONCAT(proj), '%') 
154                 OR
155                     ( ? like CONCAT(repo), '%')
156                 )                    
157                 ");
158         $ar = $q->fetchAll(PDO::FETCH_ASSOC);
159         if ($ar) {
160             $re[$cs] = self::loadByName($ar['repo']);
161             return $re[$cs];
162         } 
163         $re[$cs] = false;
164         return $re[$cs];
165     }
166     */
167     // methods 
168   
169     function __construct($ar = null) {
170         
171         if (!is_array($ar)) {
172             return; // can accept empty ctrs
173         }
174         foreach($ar as $k=>$v) {
175             $this->$k = $v;
176         }
177         
178     }
179     
180     function reconcileRepoSettings()
181     {
182         $c = self::Factory(array('scmtype'=>$this->scmtype));
183         $s->reconcileRepoSettings($this);
184     }
185     // these are needed just to implement the abstract interface..
186     function getSCMMetaData() {
187         return null;
188     }
189     
190     function getServerURL()
191     {
192         if ($this->serverurl) {
193             return $this->serverurl;
194         }
195         /*
196         $url = MTrackConfig::get('repos', "$this->scmtype.serverurl");
197         if ($url) {
198           return $url . $this->getBrowseRootName();
199         }
200         */
201         return null;
202     }
203
204     function getCheckoutCommand() {
205         $url = $this->getServerURL();
206         if (strlen($url)) {
207           return $this->scmtype . ' clone ' . $this->getServerURL();
208         }
209         return null;
210     }
211
212     function canFork() {
213         return false;
214     }
215
216     function getWorkingCopy() {
217          throw new Exception("cannot getWorkingCopy from a generic repo object");
218     }
219     /*
220     function deleteRepo(MTrackChangeset $CS) {
221         MTrackDB::q('delete from repos where repoid = ?', $this->repoid);
222         mtrack_rmdir($this->repopath);
223     }
224     */
225  
226
227     function getLinks()
228   {
229     if ($this->links === null) {
230       $this->links = array();
231       foreach (MTrackDB::q('select linkid, projid, repopathregex
232           from project_repo_link where repoid = ? order by repopathregex',
233           $this->repoid)->fetchAll() as $row) {
234         $this->links[$row[0]] = array($row[1], $row[2]);
235       }
236     }
237     return $this->links;
238   }
239
240     function addLink($proj, $regex)
241   {
242     if ($proj instanceof MTrackProject) {
243       $this->links_to_add[] = array($proj->projid, $regex);
244     } else {
245       $this->links_to_add[] = array($proj, $regex);
246     }
247   }
248
249     function removeLink($linkid)
250   {
251     $this->links_to_remove[$linkid] = $linkid;
252   }
253
254     function getBranches() {}
255     function getTags() {}
256     function readdir($path, $object = null, $ident = null) {}
257     function file($path, $object = null, $ident = null) {}
258     function history($path, $limit = null, $object = null, $ident = null){}
259     function diff($path, $from = null, $to = null) {}
260     function getRelatedChanges($revision) {}
261
262     function projectFromPath($filename)
263     {
264     static $links = array();
265     if (!isset($links[$this->repoid]) || $links[$this->repoid] === null) {
266       $links[$this->repoid] = array();
267       foreach (MTrackDB::q(
268         'select projid, repopathregex from project_repo_link where repoid = ?',
269             $this->repoid) as $row) {
270         $re = str_replace('/', '\\/', $row[1]);
271         $links[$this->repoid][] = array($row[0], "/$re/");
272       }
273     }
274     if (is_array($filename)) {
275       $proj_incidence = array();
276       foreach ($filename as $file) {
277         $proj = $this->projectFromPath($file);
278         if ($proj === null) continue;
279         if (isset($proj_incidence[$proj])) {
280           $proj_incidence[$proj]++;
281         } else {
282           $proj_incidence[$proj] = 1;
283         }
284       }
285       $the_proj = null;
286       $the_proj_count = 0;
287       foreach ($proj_incidence as $proj => $count) {
288         if ($count > $the_proj_count) {
289           $the_proj_count = $count;
290           $the_proj = $proj;
291         }
292       }
293       return $the_proj;
294     }
295
296     if ($filename instanceof MTrackSCMFileEvent) {
297       $filename = $filename->name;
298     }
299
300     // walk through the regexes; take the longest match as definitive
301     $longest = null;
302     $longest_id = null;
303     if ($filename[0] != '/') {
304       $filename = '/' . $filename;
305     }
306     foreach ($links[$this->repoid] as $link) {
307       if (preg_match($link[1], $filename, $M)) {
308         if (strlen($M[0]) > strlen($longest)) {
309           $longest = $M[0];
310           $longest_id = $link[0];
311         }
312       }
313     }
314     return $longest_id;
315   }
316   
317     function historyWithChangelog($path, $limit = null, $object = null,   $ident = null) 
318     {
319       
320         $ents = $this->history($path, $limit, $object, $ident);
321         $data = new StdClass;
322         if (!count($ents)) {
323             $data->ent = null;
324             return $data;
325         }
326         $ent = $ents[0];
327         $data->ent = $ent;
328
329         // Determine project from the file list
330         $the_proj = $this->projectFromPath($ent->files);
331         if ($the_proj > 1) {
332           $proj = MTrackProject::loadById($the_proj);
333           $changelog = $proj->adjust_links($ent->changelog, true);
334         } else {
335           $changelog = $ent->changelog;
336         }
337         $data->changelog = $changelog;
338
339         //if (is_array($ent->files)) foreach ($ent->files as $file) {
340         //  $file->diff = mtrack_diff($repo->diff($file, $ent->rev));
341         //}
342       
343
344         return $data;
345     }
346     function historyWithChangelogAndDiff($path, $limit = null, $object = null,   $ident = null)
347     {
348         $ret = $this->historyWithChangelog($path, $limit, $object, $ident);
349         if (!$ret->ent) {
350             return $ret;
351         }
352         if (!is_array($ret->ent->files)) {
353             return $ret;
354         }
355         foreach ($ret->ent->files as $file) {
356             // where is mtrack_diff...
357             $file->diff = mtrack_diff($this->diff($file, $ret->ent->rev));
358         }
359       
360
361         return $data;
362     }
363     // rendering..
364     
365     function displayName()
366     {
367         // fixme - this code needs to be in here.. rather than in SCM?
368         return MTrackSCM::makeDisplayName($this);
369     }
370     function descriptionToHtml()
371     {
372         return  MTrack_Wiki::format_to_html($this->description);
373     }
374     
375     static function defaultRepo($cfg = null)
376     {
377         static $defrepo = null;
378         if ($defrepo !== null) {
379             return $defrepo; // already to it..
380         }
381         
382         $defrepo = $cfg;
383         if ($defrepo !== null) {
384             $defrepo = strpos($defrepo, '/') === false ?  'default/' . $defrepo :  $defrepo;
385             return $defrepo;
386         }
387         
388         $defrepo = '';
389         $q = MTrackDB::q( 'select parent, shortname from repos order by shortname');
390         foreach($q->fetchAll() as $row) {
391             $defrepo = MTrackSCM::makeDisplayName($row);
392             return $defrepo;
393         }
394         return '';
395         
396     }
397     
398 }