MTrack/SCM/Git/WorkingCopy.php
[web.mtrack] / MTrack / Repo.php
index 2d1e2ea..34d28a9 100644 (file)
@@ -4,14 +4,13 @@ require_once 'MTrack/DB.php';
 require_once 'MTrack/Config.php';
 require_once 'MTrack/Project.php';
 require_once 'MTrack/SCMFileEvent.php';
-require_once 'MTrack/ACL.php';
-require_once 'MTrack/Changeset.php';
-require_once 'MTrack/Wiki.php';
+ //require_once 'MTrack/Changeset.php';
+//require_once 'MTrack/Wiki.php';
 
 
-class MTrackRepo extends MTrackSCM 
+class MTrack_Repo extends MTrackSCM 
 {
-    public $repoid = null;
+    public $id = null;
     public $shortname = null;
     public $scmtype = null;
     public $repopath = null;
@@ -21,329 +20,105 @@ class MTrackRepo extends MTrackSCM
     public $parent = '';
     public $clonedfrom = null;
     public $serverurl = null;
+    
+    
     private $links_to_add = array();
     private $links_to_remove = array();
     private $links = null;
     static $scms = array();
-
-    static function registerSCM($scmtype, $classname) {
-        self::$scms[$scmtype] = $classname;
+    /**
+     *load class and create instance using array as properties
+     */
+    
+    static function factory($ar)
+    {
+        //print_r($ar);
+        $type = ucfirst($ar['scmtype']);
+        $fn = 'MTrack/SCM/'.$type .'/Repo.php';
+        $cls = 'MTrack_SCM_'.$type .'_Repo';
+        require_once $fn;
+        
+        $ret = new $cls($ar);
+        
+        return $ret;
+        
     }
-    static function getAvailableSCMs() {
+    
+    static function getAvailableSCMs()
+    {
         $ret = array();
-        foreach (self::$scms as $t => $classname) {
-          $o = new $classname;
-          $ret[$t] = $o;
+        $ar = scandir(dirname(__FILE__).'/SCM');
+        
+        foreach($ar as $a) {
+            if (empty($a) || $a[0] == '.') {
+                continue;
+            }
+            $fn = dirname(__FILE__).'/SCM/'.$a.'/Repo.php';
+            if (!file_exists($fn)) {
+                continue;
+            } 
+            $ret[$a] = MTrack_Repo::factory(array('scmtype'=> $a));
+            
         }
         return $ret;
     }  
-    static function loadById($id) {
-        list($row) = MTrackDB::q(
-          'select repoid, scmtype from repos where repoid = ?',
-          $id)->fetchAll();
-        if (isset($row[0])) {
-          $type = $row[1];
-          if (isset(self::$scms[$type])) {
-            $class = self::$scms[$type];
-            return new $class($row[0]);
-          }
-          throw new Exception("unsupported repo type $type");
-        }
-        return null;
-    }
-    static function loadByName($name) {
-        $bits = explode('/', $name);
-        if (count($bits) > 1 && $bits[0] == 'default') {
-          array_shift($bits);
-          $name = $bits[0];
-        }
-        if (count($bits) > 1) {
-          /* wez/reponame -> per user repo */
-          $u = "user:$bits[0]";
-          $p = "project:$bits[0]";
-          $rows = MTrackDB::q(
-            'select repoid, scmtype from repos where shortname = ? and (parent = ? OR parent = ?)',
-            $bits[1], $u, $p)->fetchAll();
-        } else {
-          $rows = MTrackDB::q(
-            "select repoid, scmtype from repos where shortname = ? and parent =''",
-            $name)->fetchAll();
-        }
-        if (is_array($rows) && isset($rows[0])) {
-          $row = $rows[0];
-          if (isset($row[0])) {
-            $type = $row[1];
-            if (isset(self::$scms[$type])) {
-              $class = self::$scms[$type];
-              return new $class($row[0]);
-            }
-            throw new Exception("unsupported repo type $type");
-          }
+     
+    function __construct($ar = null) {
+        
+        if (!is_array($ar)) {
+            return; // can accept empty ctrs
         }
-        return null;
-    }
-    static function loadByLocation($path) 
-    {
-          
-        // we have magic configuration - end users commit into SVN
-        // backend is really git... - so pre-commit hooks have to be from svn
-        list($row) = MTrackDB::q('select repoid, scmtype from repos where repopath = ?', $path)->fetchAll();
-        if (isset($row[0])) {
-          $type = $row[1];
-          if (isset(self::$scms[$type])) {
-            $class = self::$scms[$type];
-            return new $class($row[0]);
-          }
-          throw new Exception("unsupported repo type $type");
+        foreach($ar as $k=>$v) {
+            $this->$k = $v;
         }
-        return null;
-      }
-  
-    static function loadByChangeSet($cs)
-    {
         
-        static $re = array();
-        if (isset($re[$cs])) {
-            return $re[$cs];
-        }
-        //using (repoid)  ??
-        $q = MTrackDB::q("
-            select 
-                r.shortname as repo, 
-                p.shortname as proj 
-            from 
-                repos r 
-            left join 
-                project_repo_link l on r.repoid = l.repoid
-            left join 
-                projects p on p.projid = r.projectid
-            where 
-                (parent is null  or length(parent) = 0)
-            AND
-                (
-                    ( ? like CONCAT(proj), '%') 
-                OR
-                    ( ? like CONCAT(repo), '%')
-                )                    
-                ");
-        $ar = $q->fetchAll(PDO::FETCH_ASSOC);
-        if ($ar) {
-            $re[$cs] = self::loadByName($ar['repo']);
-            return $re[$cs];
-        } 
-        $re[$cs] = false;
-        return $re[$cs];
     }
     
-    // methods 
-  
-    function __construct($id = null) {
-        if ($id !== null) {
-          list($row) = MTrackDB::q(
-                        'select * from repos where repoid = ?',
-                        $id)->fetchAll();
-          if (isset($row[0])) {
-            $this->repoid = $row['repoid'];
-            $this->shortname = $row['shortname'];
-            $this->scmtype = $row['scmtype'];
-            $this->repopath = $row['repopath'];
-            $this->browserurl = $row['browserurl'];
-            $this->browsertype = $row['browsertype'];
-            $this->description = $row['description'];
-            $this->parent = $row['parent'];
-            $this->clonedfrom = $row['clonedfrom'];
-            $this->serverurl = $row['serverurl'];
-            return;
-          }
-          throw new Exception("unable to find repo with id = $id");
-        }
+    function reconcileRepoSettings()
+    {
+        $c = self::Factory(array('scmtype'=>$this->scmtype));
+        $s->reconcileRepoSettings($this);
     }
     
-    function reconcileRepoSettings() {
-        if (!isset(self::$scms[$this->scmtype])) {
-          throw new Exception("invalid scm type $this->scmtype");
+    function getServerURL()
+    {
+        if ($this->serverurl) {
+            return $this->serverurl;
         }
-        $c = self::$scms[$this->scmtype];
-        $s = new $c;
-        $s->reconcileRepoSettings($this);
-    }
-   
-    function getSCMMetaData() {
-    return null;
-  }
-
-    function getServerURL() {
-    if ($this->serverurl) {
-      return $this->serverurl;
-    }
-    $url = MTrackConfig::get('repos', "$this->scmtype.serverurl");
-    if ($url) {
-      return $url . $this->getBrowseRootName();
+        /*
+        $url = MTrackConfig::get('repos', "$this->scmtype.serverurl");
+        if ($url) {
+          return $url . $this->getBrowseRootName();
+        }
+        */
+        return null;
     }
-    return null;
-  }
 
     function getCheckoutCommand() {
-    $url = $this->getServerURL();
-    if (strlen($url)) {
-      return $this->scmtype . ' clone ' . $this->getServerURL();
+        $url = $this->getServerURL();
+        if (strlen($url)) {
+          return $this->scmtype . ' clone ' . $this->getServerURL();
+        }
+        return null;
     }
-    return null;
-  }
 
     function canFork() {
-    return false;
-  }
+        return false;
+    }
 
     function getWorkingCopy() {
-    throw new Exception("cannot getWorkingCopy from a generic repo object");
-  }
-
+         throw new Exception("cannot getWorkingCopy from a generic repo object");
+    }
+    /*
     function deleteRepo(MTrackChangeset $CS) {
-    MTrackDB::q('delete from repos where repoid = ?', $this->repoid);
-    mtrack_rmdir($this->repopath);
-  }
-
-    function save(MTrackChangeset $CS) {
-        if (!isset(self::$scms[$this->scmtype])) {
-          throw new Exception("unsupported repo type " . $this->scmtype);
-        }
-    
-        if ($this->repoid) {
-          list($row) = MTrackDB::q(
-                        'select * from repos where repoid = ?',
-                        $this->repoid)->fetchAll();
-          $old = $row;
-          MTrackDB::q(
-              'update repos set shortname = ?, scmtype = ?, repopath = ?,
-                browserurl = ?, browsertype = ?, description = ?,
-                parent = ?, serverurl = ?, clonedfrom = ? where repoid = ?',
-              $this->shortname, $this->scmtype, $this->repopath,
-              $this->browserurl, $this->browsertype, $this->description,
-              $this->parent, $this->serverurl, $this->clonedfrom, $this->repoid);
-        } else {
-          $acl = null;
-    
-          if (!strlen($this->repopath)) {
-            if (!MTrackConfig::get('repos', 'allow_user_repo_creation')) {
-              throw new Exception("configuration does not allow repo creation");
-            }
-            $repodir = MTrackConfig::get('repos', 'basedir');
-            if ($repodir == null) {
-              $repodir = MTrackConfig::get('core', 'vardir') . '/repos';
-            }
-            if (!is_dir($repodir)) {
-              mkdir($repodir);
-            }
-    
-            if (!$this->parent) {
-              $owner = mtrack_canon_username(MTrackAuth::whoami());
-              $this->parent = 'user:' . $owner;
-            } else {
-              list($type, $owner) = explode(':', $this->parent, 2);
-              switch ($type) {
-                case 'project':
-                  $P = MTrackProject::loadByName($owner);
-                  if (!$P) {
-                    throw new Exception("invalid project $owner");
-                  }
-                  MTrackACL::requireAllRights("project:$P->projid", 'modify');
-                  break;
-                case 'user':
-                  if ($owner != mtrack_canon_username(MTrackAuth::whoami())) {
-                    throw new Exception("can't make a repo for another user");
-                  }
-                  break;
-                default:
-                  throw new Exception("invalid parent ($this->parent)");
-              }
-            }
-            if (preg_match("/[^a-zA-Z0-9_.-]/", $owner)) {
-              throw new Exception("$owner must not contain special characters");
-            }
-            $this->repopath = $repodir . DIRECTORY_SEPARATOR . $owner;
-            if (!is_dir($this->repopath)) {
-              mkdir($this->repopath);
-            }
-            $this->repopath .= DIRECTORY_SEPARATOR . $this->shortname;
-    
-            /* default ACL is allow user all rights, block everybody else */
-            $acl = array(
-              array($owner, 'read', 1),
-              array($owner, 'modify', 1),
-              array($owner, 'delete', 1),
-              array($owner, 'checkout', 1),
-              array($owner, 'commit', 1),
-              array('*', 'read', 0),
-              array('*', 'modify', 0),
-              array('*', 'delete', 0),
-              array('*', 'checkout', 0),
-              array('*', 'commit', 0),
-            );
-          }
-    
-          MTrackDB::q('insert into repos (shortname, scmtype,
-              repopath, browserurl, browsertype, description, parent,
-              serverurl, clonedfrom)
-              values (?, ?, ?, ?, ?, ?, ?, ?, ?)',
-              $this->shortname, $this->scmtype, $this->repopath,
-              $this->browserurl, $this->browsertype, $this->description,
-              $this->parent, $this->serverurl, $this->clonedfrom);
-    
-          $this->repoid = MTrackDB::lastInsertId('repos', 'repoid');
-          $old = null;
-    
-          if ($acl !== null) {
-            MTrackACL::setACL("repo:$this->repoid", 0, $acl);
-            $me = mtrack_canon_username(MTrackAuth::whoami());
-            foreach (array('ticket', 'changeset') as $e) {
-              MTrackDB::q(
-                'insert into watches (otype, oid, userid, medium, event, active) values (?, ?, ?, ?, ?, 1)',
-              'repo', $this->repoid, $me, 'email', $e);
-            }
-          }
-        }
-        $this->reconcileRepoSettings();
-        if (!$this->parent) {
-          /* for SSH access, populate a symlink from the repos basedir to the
-           * actual path for this repo */
-          $repodir = MTrackConfig::get('repos', 'basedir');
-          if ($repodir == null) {
-            $repodir = MTrackConfig::get('core', 'vardir') . '/repos';
-          }
-          if (!is_dir($repodir)) {
-            mkdir($repodir);
-          }
-          $repodir .= '/default';
-          if (!is_dir($repodir)) {
-            mkdir($repodir);
-          }
-          $repodir .= '/' . $this->shortname;
-          if (!file_exists($repodir)) {
-            symlink($this->repopath, $repodir);
-          } else if (is_link($repodir) && readlink($repodir) != $this->repopath) {
-            unlink($repodir);
-            symlink($this->repopath, $repodir);
-          }
-        }
-        $CS->add("repo:" . $this->repoid . ":shortname", $old['shortname'], $this->shortname);
-        $CS->add("repo:" . $this->repoid . ":scmtype", $old['scmtype'], $this->scmtype);
-        $CS->add("repo:" . $this->repoid . ":repopath", $old['repopath'], $this->repopath);
-        $CS->add("repo:" . $this->repoid . ":browserurl", $old['browserurl'], $this->browserurl);
-        $CS->add("repo:" . $this->repoid . ":browsertype", $old['browsertype'], $this->browsertype);
-        $CS->add("repo:" . $this->repoid . ":description", $old['description'], $this->description);
-        $CS->add("repo:" . $this->repoid . ":parent", $old['parent'], $this->parent);
-        $CS->add("repo:" . $this->repoid . ":clonedfrom", $old['clonedfrom'], $this->clonedfrom);
-        $CS->add("repo:" . $this->repoid . ":serverurl", $old['serverurl'], $this->serverurl);
-    
-        foreach ($this->links_to_add as $link) {
-          MTrackDB::q('insert into project_repo_link (projid, repoid, repopathregex) values (?, ?, ?)', $link[0], $this->repoid, $link[1]);
-        }
-        foreach ($this->links_to_remove as $linkid) {
-          MTrackDB::q('delete from project_repo_link where repoid = ? and linkid = ?', $this->repoid, $linkid);
-        }
-  }
-
+        MTrackDB::q('delete from repos where repoid = ?', $this->repoid);
+        mtrack_rmdir($this->repopath);
+    }
+    */
+/**
+ * i could not find where this is used..
     function getLinks()
   {
     if ($this->links === null) {
@@ -370,7 +145,9 @@ class MTrackRepo extends MTrackSCM
   {
     $this->links_to_remove[$linkid] = $linkid;
   }
+ */
 
+// these are needed just to implement the abstract interface..
     function getBranches() {}
     function getTags() {}
     function readdir($path, $object = null, $ident = null) {}
@@ -378,141 +155,12 @@ class MTrackRepo extends MTrackSCM
     function history($path, $limit = null, $object = null, $ident = null){}
     function diff($path, $from = null, $to = null) {}
     function getRelatedChanges($revision) {}
-
-    function projectFromPath($filename)
-    {
-    static $links = array();
-    if (!isset($links[$this->repoid]) || $links[$this->repoid] === null) {
-      $links[$this->repoid] = array();
-      foreach (MTrackDB::q(
-        'select projid, repopathregex from project_repo_link where repoid = ?',
-            $this->repoid) as $row) {
-        $re = str_replace('/', '\\/', $row[1]);
-        $links[$this->repoid][] = array($row[0], "/$re/");
-      }
-    }
-    if (is_array($filename)) {
-      $proj_incidence = array();
-      foreach ($filename as $file) {
-        $proj = $this->projectFromPath($file);
-        if ($proj === null) continue;
-        if (isset($proj_incidence[$proj])) {
-          $proj_incidence[$proj]++;
-        } else {
-          $proj_incidence[$proj] = 1;
-        }
-      }
-      $the_proj = null;
-      $the_proj_count = 0;
-      foreach ($proj_incidence as $proj => $count) {
-        if ($count > $the_proj_count) {
-          $the_proj_count = $count;
-          $the_proj = $proj;
-        }
-      }
-      return $the_proj;
-    }
-
-    if ($filename instanceof MTrackSCMFileEvent) {
-      $filename = $filename->name;
-    }
-
-    // walk through the regexes; take the longest match as definitive
-    $longest = null;
-    $longest_id = null;
-    if ($filename[0] != '/') {
-      $filename = '/' . $filename;
-    }
-    foreach ($links[$this->repoid] as $link) {
-      if (preg_match($link[1], $filename, $M)) {
-        if (strlen($M[0]) > strlen($longest)) {
-          $longest = $M[0];
-          $longest_id = $link[0];
-        }
-      }
+    function getSCMMetaData() { return null; }
+    /**
+     *  converts a commit log message (cached locally into a working object..)
+     *  see Browse.php
+     */
+    function commitLogToEvent($str) {
+        throw new Exception("no implementation for commitLogToEvent");
     }
-    return $longest_id;
-  }
-  
-    function historyWithChangelog($path, $limit = null, $object = null,   $ident = null) 
-    {
-      
-        $ents = $this->history($path, $limit, $object, $ident);
-        $data = new StdClass;
-        if (!count($ents)) {
-            $data->ent = null;
-            return $data;
-        }
-        $ent = $ents[0];
-        $data->ent = $ent;
-
-        // Determine project from the file list
-        $the_proj = $this->projectFromPath($ent->files);
-        if ($the_proj > 1) {
-          $proj = MTrackProject::loadById($the_proj);
-          $changelog = $proj->adjust_links($ent->changelog, true);
-        } else {
-          $changelog = $ent->changelog;
-        }
-        $data->changelog = $changelog;
-
-        //if (is_array($ent->files)) foreach ($ent->files as $file) {
-        //  $file->diff = mtrack_diff($repo->diff($file, $ent->rev));
-        //}
-      
-
-        return $data;
-    }
-    function historyWithChangelogAndDiff($path, $limit = null, $object = null,   $ident = null)
-    {
-        $ret = $this->historyWithChangelog($path, $limit, $object, $ident);
-        if (!$ret->ent) {
-            return $ret;
-        }
-        if (!is_array($ret->ent->files)) {
-            return $ret;
-        }
-        foreach ($ret->ent->files as $file) {
-            // where is mtrack_diff...
-            $file->diff = mtrack_diff($this->diff($file, $ret->ent->rev));
-        }
-      
-
-        return $data;
-    }
-    // rendering..
-    
-    function displayName()
-    {
-        // fixme - this code needs to be in here.. rather than in SCM?
-        return MTrackSCM::makeDisplayName($this);
-    }
-    function descriptionToHtml()
-    {
-        return  MTrack_Wiki::format_to_html($this->description);
-    }
-    
-    static function defaultRepo($cfg = null)
-    {
-        static $defrepo = null;
-        if ($defrepo !== null) {
-            return $defrepo; // already to it..
-        }
-        
-        $defrepo = $cfg;
-        if ($defrepo !== null) {
-            $defrepo = strpos($defrepo, '/') === false ?  'default/' . $defrepo :  $defrepo;
-            return $defrepo;
-        }
-        
-        $defrepo = '';
-        $q = MTrackDB::q( 'select parent, shortname from repos order by shortname');
-        foreach($q->fetchAll() as $row) {
-            $defrepo = MTrackSCM::makeDisplayName($row);
-            return $defrepo;
-        }
-        return '';
-        
-    }
-    
 }