1 <?php # vim:ts=2:sw=2:et:
2 /* For licensing and copyright terms, see the file named LICENSE */
5 * Think about how to handle the nav stuff...
6 * -> all urls need to respond to direct requests.
7 * -> the direct request is always an ajax
13 // Browse.php - only for rendering the body..
14 // Tree.php - the actually tree..
16 require_once 'MTrackWeb.php';
18 class MTrackWeb_Browse extends MTrackWeb
29 var $elements = array();
31 var $showCreate = false; // show create repo.
32 var $canEditRepo = false;
35 var $canDeleteFork = false;
54 $this->pi = $pi . (strlen($pi) ? $this->bootLoader->ext : '');
56 if (!isset($_REQUEST['ajax_body'])) {
57 $this->title = "Browse: " . $this->pi;
60 $this->masterTemplate = 'tree.html';
61 //DB_DataObject::debugLevel(1);
66 $this->repo = DB_DataObject::factory('mtrack_repos');
67 $file = $this->repo->loadFromPath($this->pi);
68 if (!$this->repo->id) {
75 if (isset($_GET['jump']) && strlen($_GET['jump'])) {
76 list($this->object, $this->ident) = explode(':', $_GET['jump'], 2);
80 if (!$this->projectPerm($this->repo->project_id, 'MTrack.Repos', 'S')) {
81 return HTML_FlexyFramework::run('Noperm'); // noperm = loggedin -> need more perms / not.. try loggin in..
84 $this->bdata = $this->getBrowseData($this->repo, $file, $this->object, $this->ident);
85 if (isset($this->bdata->err) ) {
86 throw new Exception($this->bdata->err);
88 require_once 'MTrack/Wiki/Item.php';
89 //make sure we can render..
90 MTrack_Wiki_Item::$repo = $this->repo->impl();
92 //$this->repo = (count($crumbs) > 2) ? MTrackSCM::factory($this->pi) : null ;
99 //$this->bdata = mtrack_cache(
100 // array($this,'getBrowseData'),
101 // array($this->repo, $this->pi, $this->object, $this->ident)
104 $this->basename = basename($file);
106 $crumbs = !strlen($file) || dirname($file) == '.' || !strlen(dirname($file)) ? array() :
107 explode('/', dirname($file));
110 $this->crumbs = array();
116 foreach($crumbs as $path)
121 $location .= strlen($location) ? '/' : '';
122 $location .= strlen($path) ? urlencode($path) : '';
123 $c->location = $location;
124 $this->crumbs[] = $c;
126 // echo '<PRE>';print_R($this->crumbs);
128 if ($this->bdata && count($this->bdata->jumps)) {
129 require_once 'HTML/Template/Flexy/Element.php';
130 $this->elements['jump'] = new HTML_Template_Flexy_Element('select');
131 // print_r($bdata->jumps);
132 $this->elements['jump']->setOptions($this->bdata->jumps);
139 if (MTrackACL::hasAllRights('Browser', 'create')) {
140 some users may have rights to create repos that belong to projects.
141 * Determine that list of projects here, because we need it for both
142 * the fork and new repo cases */
144 $owners = array("user:{$this->authUser->userid}" => $this->authUser->userid);
145 $q = MTrackDB::q( 'select projid, shortname, name from projects order by ordinal') ;
146 foreach ($q->fetchAll(PDO::FETCH_ASSOC) as $row) {
147 if (MTrackACL::hasAllRights("project:". $row['projid'], 'modify')) {
148 $owners["project:". $row['shortname']] = $row['shortname'];
151 $this->showCreate = count($owners) > 1 ? true : false;
152 require_once 'HTML/Template/Flexy/Element.php';
153 $this->elements['repo:parent'] = new HTML_Template_Flexy_Element('select');
154 $this->elements['repo:parent']->setOptions($owners);
159 MTrackACL::requireAllRights("repo:{$this->repo->id}", 'read');
161 // this looks buggy..
163 if ( $this->repo->canFork() &&
164 MTrackACL::hasAllRights('Browser', 'fork') &&
165 MTrackConfig::get('repos', 'allow_user_repo_creation')
168 $this->canFork = true;
169 $this->forkname = $this->repo->shortname;
171 if ("{$this->authUser->userid}/{$this->repo->shortname}" == $this->repo->getBrowseRootName()) {
172 // if this is mine already, make a "more unique" name for my new fork
173 $this->forkname = $this->repo->shortname . '2';
177 if ( $this->repo->parent &&
178 MTrackACL::hasAllRights("repo:{$this->repo->id}", "delete")
181 $this->canDeleteFork = true;
184 if (MTrackACL::hasAllRights("repo:{$this->repo->id}", "modify")) {
185 $this->canEditRepo = true;
187 // watch UI is different in this version..
188 // MTrackWatch::renderWatchUI('repo', $repo->id);
192 /// non repo options..
196 $this->dirname = $this->repo ? $this->repo->displayName() : '';
199 $this->dirname .= '/'. $file;
201 $this->up = strlen($file) ? dirname($this->dirname) : '';
202 $this->jump = isset($_GET['jump']) ? $_GET['jump'] : '';
205 return $this->noRepo(explode('/', $pi));
208 //print_r($this); exit;
213 function noRepo($crumbs)
216 /*if ( MTrackACL::hasAllRights('Browser', 'fork')
217 && MTrackConfig::get('repos', 'allow_user_repo_creation')
220 $repotypes = array();
222 foreach (MTrack_Repo::getAvailableSCMs() as $t => $r) {
223 $d = $r->getSCMMetaData();
224 $repotypes[$t] = $d['name'];
226 require_once 'HTML/Template/Flexy/Element.php';
227 $this->elements['repo:type'] = new HTML_Template_Flexy_Element('select');
228 $this->elements['repo:type']->setOptions($repotypes);
231 // DB_DataObject::debugLevel(1);
232 $mine = $this->authUser ? "user:{$this->authUser->id}" : '';
234 $do = DB_DataObject::factory('mtrack_repos');
237 $crumbs[0] = empty($crumbs[0]) ? 'default' : $crumbs[0] ;
239 if (count($crumbs) == 1 && $crumbs[0] != 'default') {
241 $do->whereAdd("parent like('%:". $do->escape($crumbs[0]). "')"); // subset
243 } else if (count($crumbs) == 1 && $crumbs[0] == 'default') {
245 $do->whereAdd(" parent = ''"); // top level..
247 /* looking for top level items */
250 /* have my own repos bubble up */
251 if ($this->authUser) {
252 $do->orderBy("CASE WHEN parent = '" . $do->escape('user:' . $this->authUser->id) . "' THEN 0 ELSE 1 END, shortname ASC");
254 $do->orderBy("shortname ASC");
256 // FIXME -> permissions on repositories goes here..
257 //$do->ensurePerm($this->authUser);
260 $this->repos= array();
261 while ($do->fetch()) {
263 if (!$this->projectPerm($do->project_id, 'MTrack.Repos', 'S')) {
267 $this->repos[]= clone($do);
279 function getBrowseData($repo, $pi, $object, $ident, $hashes = false)
281 $data = new StdClass;
282 $data->dirs = array();
283 $data->files = array();
284 $data->jumps = array();
289 $branches = $repo->getBranches();
290 $tags = $repo->getTags();
291 if (count($branches) + count($tags)) {
292 $jumps = array("" => "- Select Branch / Tag - ");
293 if (is_array($branches)) {
294 foreach ($branches as $name => $notcare) {
295 $jumps["branch:$name"] = "Branch: $name";
298 if (is_array($tags)) {
299 foreach ($tags as $name => $notcare) {
300 $jumps["tag:$name"] = "Tag: $name";
303 $data->jumps = $jumps;
311 $ents = $repo->readdir($pi, $object, $ident);
312 if ($hashes === false) {
313 $ents = $this->cachedChangeEvent($ents);
315 // we are filling the cache..
316 return $this->cachedChangeEventFill($ents, $hashes);
320 foreach ($ents as $file) {
321 if (isset($file->hash)) {
322 $needLog[] = $file->hash;
325 $basename = basename($file->name);
327 $dirs[$basename] = $file;
329 $files[$basename] = $file;
333 uksort($files, 'strnatcmp');
334 uksort($dirs, 'strnatcmp');
337 $data->dirs = array_values($dirs);
338 $data->files = array_values($files);
340 $this->repopath = $this->repo->displayName();
341 $this->needLog = $needLog;
347 * New version of Directory listing
348 * Basically fetching the last change is very slow...
349 * - 1st run through -> fetch all the change events that we have
350 * for ones we do not have, fetch them later..
353 function cachedChangeEvent($ar)
355 // fetch a list of hashes...
359 $e->basename = basename($e->name);
360 // remove e->rev as it's not valid..
366 $q = DB_DataObject::factory('mtrack_clcache');
367 $q->whereAddIn('rev', array_keys($map), 'string');
368 $q->repo_id = $this->repo->id;
369 $revs = $q->fetchAll('rev', 'sobject');
370 $impl = $this->repo->impl();
372 foreach($revs as $hash => $sobject) {
373 $event = $impl->commitLogToEvent($sobject);
375 $event->is_dir = $map[$hash]->is_dir;
376 $event->name = $map[$hash]->name;
377 $event->basename = $map[$hash]->basename;
378 $map[$hash] = $event; // this was previous only done for directories??? why???
380 return array_values($map);
383 function cachedChangeEventFill($ar,$hashes)
386 $impl = $this->repo->impl();
389 if (!in_array($e->hash,$hashes)) {
393 // there is a small chance that concurrent users are looking for the same info..
394 $q = DB_DataObject::factory('mtrack_clcache');
396 $q->repo_id = $this->repo->id;
398 if ($q->find(true)) {
399 $event = $impl->commitLogToEvent($q->sobject);
401 $ent = $this->repo->history($e->name, 1, 'rev', $e->rev);
409 $q = DB_DataObject::factory('mtrack_clcache');
411 $q->repo_id = $this->repo->id;
412 $q->sobject = $event->commit;
416 // only copy a few essentials from ent, as we will send it back via json.
417 // these all need escaoing..
419 $add->changelog = $event->changelogOneToHtml($this->link);
420 $add->age = $event->ctimeToHtml($this->link);
421 $add->basename = basename($e->name);
422 $add->changeby = htmlspecialchars($event->changeby);
423 $add->rev = $event->rev;
425 $map[$e->hash] = $add;
436 $this->pi = $pi . (strlen($pi) ? $this->bootLoader->ext : '');
440 $this->repo = DB_DataObject::factory('mtrack_repos');
441 $file = $this->repo->loadFromPath($this->pi);
442 if (!$this->repo->id) {
443 $this->jerr("INVALID URL");
446 $this->object = null;
449 if (isset($_GET['jump']) && strlen($_GET['jump'])) {
450 list($this->object, $this->ident) = explode(':', $_GET['jump'], 2);
454 $this->jerr("INVALID URL");
456 if (!$this->projectPerm($this->repo->project_id, 'MTrack.Repos', 'S')) {
457 $this->jerr("INVALID URL");
459 if (empty($_POST['hashes'])) {
460 $this->jerr("INVALID URL");
463 $ar = $this->getBrowseData($this->repo, $file, $this->object, $this->ident, explode(',', $_POST['hashes']));