<?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..
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 = '';
+
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) {
- 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 (isset($map[$hash])) {
+ $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;
list($this->object, $this->ident) = explode(':', $_GET['jump'], 2);
}
- $this->repoName = $this->repo ? $this->repo->displayName() : '';
-
- if (!empty($_REQUEST['body_only'])) {
- $this->masterTemplate = $this->template;
- $this->getBody();
+ 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;
+
+
}
-
-
-
}
-
-
+
+
+