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.. require_once 'MTrackWeb.php'; class MTrackWeb_Browse extends MTrackWeb { 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 = ''; function getAuth() { parent::getAuth(); return true; } function get($pi) { $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) { $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 ; //$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 '
';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); } /* 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??? $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 = ''. htmlspecialchars($event->rev) . ''; $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; if (isset($_GET['jump']) && strlen($_GET['jump'])) { list($this->object, $this->ident) = explode(':', $_GET['jump'], 2); } 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; } }