php8
[web.mtrack] / MTrackWeb / Browse.php
index bd36fed..e8ce02b 100644 (file)
@@ -1,6 +1,15 @@
 <?php # vim:ts=2:sw=2:et:
 /* For licensing and copyright terms, see the file named LICENSE */
 
+/**
+ * Think about how to handle the nav stuff...
+ * -> all urls need to respond to direct requests.
+ * -> the direct request is always an ajax
+ *
+ *
+ *
+ */
+
 // Browse.php - only for rendering the body..
 // Tree.php - the actually tree..
 
@@ -8,10 +17,35 @@ require_once 'MTrackWeb.php';
 
 class MTrackWeb_Browse extends MTrackWeb
 {
-    var $template = 'browse.html'; 
+   
+    
+    
+    
  
-     
+    var $object;
+    var $ident;
+    
+    var $repo;
+    var $elements = array();
+    
+    var $showCreate = false; // show create repo.
+    var $canEditRepo = false;
+    
+    var $canFork = false;
+    var $canDeleteFork = false;
+    
+    var $forkname = '';
+    
+    var $bdata = false;
+  
+    var $up = '';
+    var $jump = '';
+    var $pi;
+    var $basename;
+    var $crumbs;
+    var $dirname;
+    var $repos;
+    
     function getAuth() 
     {
         parent::getAuth();
@@ -19,34 +53,422 @@ class MTrackWeb_Browse extends MTrackWeb
         return true;
   
     }
-    
-     
-    
-    
-    function get($pi)
+    function get($pi='', $args = array())
     {
-        
         $this->pi =  $pi . (strlen($pi) ? $this->bootLoader->ext : '');
-    
+       
+        if (!isset($_REQUEST['ajax_body'])) {
+            $this->title = "Browse: " . $this->pi;
+            return;
+        }
+        $this->masterTemplate = 'tree.html';
+        //DB_DataObject::debugLevel(1);
+       
         //var_dump($pi);
         
          
         $this->repo = DB_DataObject::factory('mtrack_repos');
         $file = $this->repo->loadFromPath($this->pi);
         if (!$this->repo->id) {
-            return;
+            $this->repo = false;
         }
+        // if we have an active project.. enforce it..
+        if ($this->currentProject() && $this->repo &&  $this->repo->project_id != $this->currentProject()) {
+            $this->repo =  false; // no repo..
+        }
+        
+        
+        $this->object = null;
+        $this->ident = null;
+        
+        if (isset($_GET['jump']) && strlen($_GET['jump'])) {
+            list($this->object, $this->ident) = explode(':', $_GET['jump'], 2);
+        }
+        
+        if ($this->repo) {
+            if (!$this->projectPerm($this->repo->project_id, 'MTrack.Repos', 'S')) {
+                return HTML_FlexyFramework::run('Noperm');  // noperm = loggedin -> need more perms / not.. try loggin in..
+            }
+            
+            $this->bdata = $this->getBrowseData($this->repo, $file, $this->object, $this->ident);
+            if (isset($this->bdata->err) ) {
+                throw new Exception($this->bdata->err);
+            }
+            require_once 'MTrack/Wiki/Item.php';
+            //make sure we can render..
+            MTrack_Wiki_Item::$repo = $this->repo->impl();
+        }    
+        //$this->repo  = (count($crumbs) > 2) ?   MTrackSCM::factory($this->pi) : null ;
          
-        if (!$this->projectPerm($this->repo->project_id, 'MTrack.Repos', 'S')) {
-            return HTML_FlexyFramework::run('Noperm');  // noperm = loggedin -> need more perms / not.. try loggin in..
+        
+        
+        
+       
+        
+        //$this->bdata = mtrack_cache(
+        //    array($this,'getBrowseData'), 
+        //    array($this->repo, $this->pi, $this->object, $this->ident)
+        //);
+
+        $this->basename  = basename($file);
+        $location = '';
+        $crumbs = !strlen($file) || dirname($file) == '.' || !strlen(dirname($file)) ? array() :
+            explode('/', dirname($file));
+        
+        
+        $this->crumbs = array();
+        
+        if (!$this->repo) {
+            $crumbs = array();
+        }
+        
+        foreach($crumbs as $path) 
+        {
+             
+            $c = new StdClass;
+            $c->name =   $path ;
+            $location .=  strlen($location) ? '/' : '';
+            $location .=  strlen($path)  ?  urlencode($path)  : '';
+            $c->location = $location;
+            $this->crumbs[] = $c;
+        }
+       // echo '<PRE>';print_R($this->crumbs);
+         
+        if ($this->bdata && count($this->bdata->jumps)) {
+            require_once 'HTML/Template/Flexy/Element.php';
+            $this->elements['jump'] = new HTML_Template_Flexy_Element('select');
+           // print_r($bdata->jumps);
+            $this->elements['jump']->setOptions($this->bdata->jumps);
         }
         
         
         
         
-       // $this->repo  = (count($crumbs) > 2) ?   MTrackSCM::factory($this->pi) : null ;
+         /*
+        if (MTrackACL::hasAllRights('Browser', 'create')) {
+          some users may have rights to create repos that belong to projects.
+            * Determine that list of projects here, because we need it for both
+            * the fork and new repo cases */
+            /*
+            $owners = array("user:{$this->authUser->userid}" => $this->authUser->userid);
+            $q = MTrackDB::q( 'select projid, shortname, name from projects order by ordinal') ;
+            foreach ($q->fetchAll(PDO::FETCH_ASSOC) as $row) {
+                if (MTrackACL::hasAllRights("project:". $row['projid'], 'modify')) {
+                    $owners["project:". $row['shortname']] = $row['shortname'];
+                }
+            }
+            $this->showCreate = count($owners) > 1 ? true : false;
+            require_once 'HTML/Template/Flexy/Element.php';
+            $this->elements['repo:parent'] = new HTML_Template_Flexy_Element('select');
+            $this->elements['repo:parent']->setOptions($owners);
+            
+        }
+          
+        if ($this->repo) {
+            MTrackACL::requireAllRights("repo:{$this->repo->id}", 'read');
+             
+            // this looks buggy..
+           
+            if (    $this->repo->canFork() && 
+                    MTrackACL::hasAllRights('Browser', 'fork') &&
+                    MTrackConfig::get('repos', 'allow_user_repo_creation')
+                ) {
+                    
+                $this->canFork = true;
+                $this->forkname = $this->repo->shortname;
+                
+                if ("{$this->authUser->userid}/{$this->repo->shortname}" == $this->repo->getBrowseRootName()) {
+                    // if this is mine already, make a "more unique" name for my new fork 
+                    $this->forkname = $this->repo->shortname . '2';
+                }
+            }
+            
+            if (    $this->repo->parent &&
+                    MTrackACL::hasAllRights("repo:{$this->repo->id}", "delete")
+                ) {
+                    
+                $this->canDeleteFork = true;
+            }
+            
+            if (MTrackACL::hasAllRights("repo:{$this->repo->id}", "modify")) {
+                $this->canEditRepo = true;
+            }
+            // watch UI is different in this version..
+            // MTrackWatch::renderWatchUI('repo', $repo->id);
+            
+        }
+         */
+        /// non repo options..
+        
+
+        
+        $this->dirname = $this->repo ? $this->repo->displayName() : '';
+        // up...
+        if (strlen($file)) {
+            $this->dirname .= '/'. $file;
+        }
+        $this->up =  strlen($file) ? dirname($this->dirname) : '';
+        $this->jump =  isset($_GET['jump']) ? $_GET['jump'] : '';
+    
+         if (!$this->repo) {
+            return $this->noRepo(explode('/', $pi));
          
+        }
+        //print_r($this); exit;
+        
+          
+    }
+     
+    function noRepo($crumbs)
+    {
+        
+        /*if (  MTrackACL::hasAllRights('Browser', 'fork')
+            && MTrackConfig::get('repos', 'allow_user_repo_creation')
+            ){
+                
+            $repotypes = array();
+            
+            foreach (MTrack_Repo::getAvailableSCMs() as $t => $r) {
+              $d = $r->getSCMMetaData();
+              $repotypes[$t] = $d['name'];
+            }
+            require_once 'HTML/Template/Flexy/Element.php';
+            $this->elements['repo:type'] = new HTML_Template_Flexy_Element('select');
+            $this->elements['repo:type']->setOptions($repotypes);
+        }
+        */
+       // DB_DataObject::debugLevel(1);
+        $mine = $this->authUser ? "user:{$this->authUser->id}" : '';
+        
+        $do = DB_DataObject::factory('mtrack_repos');
+        $params = array();
          
+        $crumbs[0]  = empty($crumbs[0]) ? 'default' : $crumbs[0] ;
+        
+        if (count($crumbs) == 1 && $crumbs[0] != 'default') {
+        
+             $do->whereAdd("parent like('%:". $do->escape($crumbs[0]). "')"); // subset
+             
+         } else if (count($crumbs) == 1 && $crumbs[0] == 'default') {
+         
+            $do->whereAdd(" parent = ''"); // top level..
+         } else {
+             /* looking for top level items */
+             ///$where = "1 = 1";
+         }
+         /* have my own repos bubble up */
+        if ($this->authUser) {
+            $do->orderBy("CASE WHEN  parent = '" . $do->escape('user:' . $this->authUser->id) . "' THEN 0 ELSE 1 END, shortname ASC");
+        } else {
+            $do->orderBy("shortname ASC");
+        }
+        
+        if ($this->currentProject()) {
+            $do->project_id = $this->currentProject();
+        }
+// FIXME -> permissions on repositories goes here.. 
+        //$do->ensurePerm($this->authUser); 
+        //$do->fetchAll();
+        $do->find();
+        $this->repos= array();
+        while ($do->fetch()) {
+             
+            if (!$this->projectPerm($do->project_id, 'MTrack.Repos', 'S')) {
+                 continue;
+            }
+             
+            $this->repos[]= clone($do);
+            
+        }
+      
+    
+     
+    }
+    
+    
+
+
+// static..?
+    function getBrowseData($repo, $pi, $object, $ident, $hashes = false)
+    {
+         $data = new StdClass;
+        $data->dirs = array();
+        $data->files = array();
+        $data->jumps = array();
+
+        if (!$repo) {
+            return $data;
+        }
+        $branches = $repo->getBranches();
+        $tags = $repo->getTags();
+        if (count($branches) + count($tags)) {
+            $jumps = array("" => "- Select Branch / Tag - ");
+            if (is_array($branches)) {
+                foreach ($branches as $name => $notcare) {
+                    $jumps["branch:$name"] = "Branch: $name";
+                }
+            }
+            if (is_array($tags)) {
+                foreach ($tags as $name => $notcare) {
+                    $jumps["tag:$name"] = "Tag: $name";
+                }
+            }
+            $data->jumps = $jumps;
+        }
+        $files = array();
+        $dirs = array();
+        
+        $needLog = array();
+        if ($repo) {
+            //try {
+            $ents = $repo->readdir($pi, $object, $ident);
+            if ($hashes === false) { 
+                $ents = $this->cachedChangeEvent($ents);
+            } else {
+                // we are filling the cache..
+                return $this->cachedChangeEventFill($ents, $hashes);
+                 
+            }
+            
+            foreach ($ents as $file) {
+                if (isset($file->hash)) {
+                    $needLog[] = $file->hash;
+                }
+                
+                $basename = basename($file->name);
+                if ($file->is_dir) {
+                    $dirs[$basename] = $file;
+                } else {
+                    $files[$basename] = $file;
+                }
+            }
+        }
+        uksort($files, 'strnatcmp');
+        uksort($dirs, 'strnatcmp');
+
+         
+        $data->dirs  = array_values($dirs);
+        $data->files = array_values($files);
+        
+        $this->repopath = $this->repo->displayName();
+        $this->needLog = $needLog;
+        // gather list 
+        
+        return $data;
+    }
+    /**
+     * New version of Directory listing
+     * Basically fetching the last change is very slow...
+     *  - 1st run through -> fetch all the change events that we have
+     *  for ones we do not have, fetch them later..
+     *
+     */
+    function cachedChangeEvent($ar)
+    {
+        // fetch a list of hashes...
+        // build a map...
+        $map = array();
+        foreach($ar as $e) {
+            $e->basename = basename($e->name);
+            // remove e->rev as it's not valid..
+            $e->rev = false;
+            $map[$e->hash] = $e;
+            
+        }
+        
+        $q = DB_DataObject::factory('mtrack_clcache');
+        $q->whereAddIn('rev', array_keys($map), 'string');
+        $q->repo_id = $this->repo->id;
+        $revs = $q->fetchAll('rev', 'sobject');
+        $impl = $this->repo->impl();
+        
+        foreach($revs as $hash => $sobject) {
+            $event = $impl->commitLogToEvent($sobject);
+            // add something??? 
+            if (!$event) {
+                continue;
+            }
+            $event->is_dir = $map[$hash]->is_dir;
+            $event->name = $map[$hash]->name;
+            $event->basename = $map[$hash]->basename;
+        
+            $map[$hash] =  $event; // this was previous only done for directories??? why???
+        }
+        return array_values($map);
+    }
+    
+    function cachedChangeEventFill($ar,$hashes)
+    {
+        $map = array();
+        $impl = $this->repo->impl();
+                
+        foreach($ar as $e) {
+            if (!in_array($e->hash,$hashes)) {
+                continue;
+            }
+            
+            // there is a small chance that concurrent users are looking for the same info..
+            $q = DB_DataObject::factory('mtrack_clcache');
+            $q->rev = $e->hash;
+            $q->repo_id = $this->repo->id;
+            
+            if ($q->find(true)) {
+                $event = $impl->commitLogToEvent($q->sobject);
+            } else { 
+                $ent = $this->repo->history($e->name, 1, 'rev', $e->rev);
+                
+                
+                if (!$ent) {
+                    continue;
+                }
+                $event = $ent[0];
+                // cache it..
+                $q = DB_DataObject::factory('mtrack_clcache');
+                $q->rev = $e->hash;
+                $q->repo_id = $this->repo->id;
+                $q->sobject = $event->commit;
+                $q->insert();
+            }
+            
+            // only copy a few essentials from ent, as we will send it back via json.
+            // these all need escaoing..
+            $add = new stdClass;
+            $add->changelog = $event->changelogOneToHtml($this->link);
+            $add->age = $event->ctimeToHtml($this->link);
+            $add->basename = basename($e->name);
+            $add->changeby = htmlspecialchars($event->changeby); 
+            $add->rev = '<a class="changesetlink browse-link"  href="'. 
+                        htmlspecialchars($this->baseURL) .
+                        '/Changeset/' .
+                        htmlspecialchars($this->repo->displayName()). 
+                        '/'.
+                        htmlspecialchars($event->rev) .
+                        '">'.
+                        htmlspecialchars($event->rev) .
+                        '</a>';
+            
+            $map[$e->hash] = $add;
+            
+            
+        }
+        return $map;
+        
+        
+    
+    }
+    function post($pi)
+    {
+        $this->pi =  $pi . (strlen($pi) ? $this->bootLoader->ext : '');
+       
+         
+         
+        $this->repo = DB_DataObject::factory('mtrack_repos');
+        $file = $this->repo->loadFromPath($this->pi);
+        if (!$this->repo->id) {
+            $this->jerr("INVALID URL");
+        }
+        
         $this->object = null;
         $this->ident = null;
         
@@ -54,19 +476,29 @@ class MTrackWeb_Browse extends MTrackWeb
             list($this->object, $this->ident) = explode(':', $_GET['jump'], 2);
         }
         
-        $this->repoName = $this->repo ? $this->repo->displayName() : '';
+        if (!$this->repo) {
+            $this->jerr("INVALID URL");
+        }
+        if (!$this->projectPerm($this->repo->project_id, 'MTrack.Repos', 'S')) {
+            $this->jerr("INVALID URL");
+        }
+        if (empty($_POST['hashes'])) {
+            $this->jerr("INVALID URL");
+        }
+        
+        $ar =  $this->getBrowseData($this->repo, $file, $this->object, $this->ident, explode(',', $_POST['hashes']));
+        $this->jdata($ar);
+        exit;
+        
         
-  
     }
-     
     
-     
-   
     
 }
     
-
+     
+    
+