Added support of subversion.

This commit is contained in:
Nicolas LASSALLE 2008-08-29 19:50:10 +02:00 committed by Loic d'Anterroches
parent 763d7ca7f6
commit ccc41c86b0
14 changed files with 713 additions and 35 deletions

View File

@ -56,6 +56,14 @@ class IDF_Diff
$current_chunk = 0;
continue;
}
if (0 === strpos($line, 'Index: ')) {
$current_file = self::getSvnFile($line);
$files[$current_file] = array();
$files[$current_file]['chunks'] = array();
$files[$current_file]['chunks_def'] = array();
$current_chunk = 0;
continue;
}
if (0 === strpos($line, '@@ ')) {
$files[$current_file]['chunks_def'][] = self::getChunk($line);
$files[$current_file]['chunks'][] = array();
@ -95,6 +103,11 @@ class IDF_Diff
return trim(substr($line, 3, $n-3));
}
public static function getSvnFile($line)
{
return substr(trim($line), 7);
}
/**
* Return the html version of a parsed diff.
*/

View File

@ -40,9 +40,10 @@ class IDF_Git
* Test a given object hash.
*
* @param string Object hash.
* @param null to be svn client compatible
* @return mixed false if not valid or 'blob', 'tree', 'commit'
*/
public function testHash($hash)
public function testHash($hash, $dummy=null)
{
$cmd = sprintf('GIT_DIR=%s git cat-file -t %s',
escapeshellarg($this->repo),
@ -297,6 +298,7 @@ class IDF_Git
}
}
$c['full_message'] = trim($c['full_message']);
$res[] = (object) $c;
return $res;
}

View File

@ -335,6 +335,34 @@ class IDF_Project extends Pluf_Model
return $gitrep.'/'.$this->shortname.'.git';
}
/**
* Get the path to the git repository.
*
* @return string Path to the git repository
*/
public function getSvnDaemonUrl()
{
$conf = new IDF_Conf();
$conf->setProject($this);
return $conf->getVal('svn_daemon_url');
}
/**
* Get the root name of the project scm
*
* @return string SCM root
*/
public function getScmRoot()
{
$roots = array('git' => 'master', 'svn' => 'HEAD');
$conf = new IDF_Conf();
$conf->setProject($this);
$scm = $conf->getVal('scm', 'git');
return $roots[$scm];
}
/**
* Check that the object belongs to the project or rise a 404
* error.

53
src/IDF/ScmFactory.php Normal file
View File

@ -0,0 +1,53 @@
<?php
/* -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
# ***** BEGIN LICENSE BLOCK *****
# This file is part of InDefero, an open source project management application.
# Copyright (C) 2008 Céondo Ltd and contributors.
#
# InDefero is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# InDefero is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#
# ***** END LICENSE BLOCK ***** */
/**
* Manage differents SCM systems
*/
class IDF_ScmFactory
{
/**
* Returns an instance of the correct scm backend object.
*
* @return Object
*/
public static function getScm($request=null)
{
// Get scm type from project conf ; defaults to git
$scm = $request->conf->getVal('scm', 'git');
// CASE: git
if ($scm === 'git') {
return new IDF_Git($request->project->getGitRepository());
}
// CASE: svn
if ($scm === 'svn') {
return new IDF_Svn($request->conf->getVal('svn_repository'),
$request->conf->getVal('svn_username'),
$request->conf->getVal('svn_password'));
}
}
}

384
src/IDF/Svn.php Normal file
View File

@ -0,0 +1,384 @@
<?php
/* -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
# ***** BEGIN LICENSE BLOCK *****
# This file is part of InDefero, an open source project management application.
# Copyright (C) 2008 Céondo Ltd and contributors.
#
# InDefero is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# InDefero is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#
# ***** END LICENSE BLOCK ***** */
/**
* SVN utils.
*
*/
class IDF_Svn
{
public $repo = '';
public $username = '';
public $password = '';
private $assoc = array('dir' => 'tree',
'file' => 'blob');
public function __construct($repo, $username='', $password='')
{
$this->repo = $repo;
$this->username = $username;
$this->password = $password;
}
/**
* Test a given object hash.
*
* @param string Object hash.
* @return mixed false if not valid or 'blob', 'tree', 'commit'
*/
public function testHash($rev, $path='')
{
// OK if HEAD on /
if ($rev === 'HEAD' && $path === '') {
return 'commit';
}
// Else, test the path on revision
$cmd = sprintf('svn info --xml --username=%s --password=%s %s@%s',
escapeshellarg($this->username),
escapeshellarg($this->password),
escapeshellarg($this->repo.'/'.$path),
escapeshellarg($rev));
$xmlInfo = shell_exec($cmd);
// If exception is thrown, return false
try {
$xml = simplexml_load_string($xmlInfo);
}
catch (Exception $e) {
return false;
}
// If the entry node does exists, params are wrong
if (!isset($xml->entry)) {
return false;
}
// Else, enjoy it :)
return 'commit';
}
/**
* Given a commit hash returns an array of files in it.
*
* A file is a class with the following properties:
*
* 'perm', 'type', 'size', 'hash', 'file'
*
* @param string Commit ('HEAD')
* @param string Base folder ('')
* @return array
*/
public function filesAtCommit($rev='HEAD', $folder='')
{
$cmd = sprintf('svn ls --xml --username=%s --password=%s %s@%s',
escapeshellarg($this->username),
escapeshellarg($this->password),
escapeshellarg($this->repo.'/'.$folder),
escapeshellarg($rev));
$xmlLs = shell_exec($cmd);
$xml = simplexml_load_string($xmlLs);
$res = array();
foreach ($xml->list->entry as $entry) {
$file = array();
$file['type'] = $this->assoc[(String) $entry['kind']];
$file['file'] = (String) $entry->name;
$file['fullpath'] = $folder.'/'.((String) $entry->name);
$file['date'] = gmdate('Y-m-d H:i:s',
strtotime((String) $entry->commit->date));
$file['rev'] = (String) $entry->commit['revision'];
// Get commit message
$currentReposFile = $this->repo.'/'.$folder.'/'.$file['file'];
$file['log'] = $this->getCommitMessage($currentReposFile, $rev);
// Get the size if the type is blob
if ($file['type'] == 'blob') {
$file['size'] = (String) $entry->size;
}
$file['perm'] = '';
$res[] = (Object) $file;
}
return $res;
}
/**
* Get a commit message for given file and revision.
*
* @param string File
* @param string Commit ('HEAD')
*
* @return String commit message
*/
private function getCommitMessage($file, $rev='HEAD')
{
$cmd = sprintf('svn log --xml --limit 1 --username=%s --password=%s %s@%s',
escapeshellarg($this->username),
escapeshellarg($this->password),
escapeshellarg($file),
escapeshellarg($rev));
$xmlLog = shell_exec($cmd);
$xml = simplexml_load_string($xmlLog);
return (String) $xml->logentry->msg;
}
/**
* Get the file info.
*
* @param string File
* @param string Commit ('HEAD')
* @return false Information
*/
public function getFileInfo($totest, $rev='HEAD')
{
$cmd = sprintf('svn info --xml --username=%s --password=%s %s@%s',
escapeshellarg($this->username),
escapeshellarg($this->password),
escapeshellarg($this->repo.'/'.$totest),
escapeshellarg($rev));
$xmlInfo = shell_exec($cmd);
$xml = simplexml_load_string($xmlInfo);
$entry = $xml->entry;
$file = array();
$file['fullpath'] = $totest;
$file['hash'] = (String) $entry->repository->uuid;
$file['type'] = $this->assoc[(String) $entry['kind']];
$file['file'] = $totest;
$file['rev'] = (String) $entry->commit['revision'];
$file['author'] = (String) $entry->author;
$file['date'] = gmdate('Y-m-d H:i:s', strtotime((String) $entry->commit->date));
$file['size'] = (String) $entry->size;
$file['log'] = '';
return (Object) $file;
}
/**
* Get a blob.
*
* @param string Blob hash
* @return string Raw blob
*/
public function getBlob($path, $rev)
{
$cmd = sprintf('svn cat --username=%s --password=%s %s@%s',
escapeshellarg($this->username),
escapeshellarg($this->password),
escapeshellarg($this->repo.'/'.$path),
escapeshellarg($rev));
return shell_exec($cmd);
}
/**
* Get the branches.
*
* @return array Branches.
*/
public function getBranches()
{
$res = array('HEAD');
return $res;
}
/**
* Get commit details.
*
* @param string Commit ('HEAD').
* @return array Changes.
*/
public function getCommit($rev='HEAD')
{
$res = array();
$cmd = sprintf('svn log --xml -v --username=%s --password=%s %s@%s',
escapeshellarg($this->username),
escapeshellarg($this->password),
escapeshellarg($this->repo),
escapeshellarg($rev));
$xmlRes = shell_exec($cmd);
$xml = simplexml_load_string($xmlRes);
$res['author'] = (String) $xml->logentry->author;
$res['date'] = gmdate('Y-m-d H:i:s', strtotime((String) $xml->logentry->date));
$res['title'] = (String) $xml->logentry->msg;
$res['commit'] = (String) $xml->logentry['revision'];
$res['changes'] = $this->getDiff($rev);
$res['tree'] = '';
return (Object) $res;
}
private function getDiff($rev='HEAD')
{
$res = array();
$cmd = sprintf('svn diff -c %s --username=%s --password=%s %s',
escapeshellarg($rev),
escapeshellarg($this->username),
escapeshellarg($this->password),
escapeshellarg($this->repo));
return shell_exec($cmd);
}
/**
* Get latest changes.
*
* @param string Commit ('HEAD').
* @param int Number of changes (10).
*
* @return array Changes.
*/
public function getChangeLog($rev='HEAD', $n=10)
{
$res = array();
$cmd = sprintf('svn log --xml -v --limit %s --username=%s --password=%s %s@%s',
escapeshellarg($n),
escapeshellarg($this->username),
escapeshellarg($this->password),
escapeshellarg($this->repo),
escapeshellarg($rev));
$xmlRes = shell_exec($cmd);
$xml = simplexml_load_string($xmlRes);
$res = array();
foreach ($xml->logentry as $entry) {
$log = array();
$log['author'] = (String) $entry->author;
$log['date'] = gmdate('Y-m-d H:i:s', strtotime((String) $entry->date));
$log['title'] = (String) $entry->msg;
$log['commit'] = (String) $entry['revision'];
$log['full_message'] = '';
$res[] = (Object) $log;
}
return $res;
}
/**
* Generate the command to create a zip archive at a given commit.
* Unsupported feature in subversion
*
* @param string dummy
* @param string dummy
* @return Exception
*/
public function getArchiveCommand($commit, $prefix='git-repo-dump/')
{
throw new Exception(('Unsupported feature.'));
}
/**
* Get additionnals properties on path and revision
*
* @param string File
* @param string Commit ('HEAD')
* @return array
*/
public function getProperties($rev, $path='')
{
$res = array();
$cmd = sprintf('svn proplist --xml --username=%s --password=%s %s@%s',
escapeshellarg($this->username),
escapeshellarg($this->password),
escapeshellarg($this->repo.'/'.$path),
escapeshellarg($rev));
$xmlProps = shell_exec($cmd);
$props = simplexml_load_string($xmlProps);
// No properties, returns an empty array
if (!isset($props->target)) {
return $res;
}
// Get the value of each property
foreach ($props->target->property as $prop) {
$key = (String) $prop['name'];
$res[$key] = $this->getProperty($key, $rev, $path);
}
return $res;
}
/**
* Get a specific additionnal property on path and revision
*
* @param string Property
* @param string File
* @param string Commit ('HEAD')
* @return string the property value
*/
private function getProperty($property, $rev, $path='')
{
$res = array();
$cmd = sprintf('svn propget --xml %s --username=%s --password=%s %s@%s',
escapeshellarg($property),
escapeshellarg($this->username),
escapeshellarg($this->password),
escapeshellarg($this->repo.'/'.$path),
escapeshellarg($rev));
$xmlProp = shell_exec($cmd);
$prop = simplexml_load_string($xmlProp);
return (String) $prop->target->property;
}
/**
* Get the number of the last commit in the repository.
*
* @param string Commit ('HEAD').
*
* @return String last number commit
*/
public function getLastCommit($rev='HEAD')
{
$xmlInfo = '';
$cmd = sprintf('svn info --xml --username=%s --password=%s %s@%s',
escapeshellarg($this->username),
escapeshellarg($this->password),
escapeshellarg($this->repo),
escapeshellarg($rev));
$xmlInfo = shell_exec($cmd);
$xml = simplexml_load_string($xmlInfo);
return (String) $xml->entry->commit['revision'];
}
}

View File

@ -34,11 +34,13 @@ class IDF_Views_Source
public $changeLog_precond = array('IDF_Precondition::accessSource');
public function changeLog($request, $match)
{
$title = sprintf(__('%s Git Change Log'), (string) $request->project);
$git = new IDF_Git($request->project->getGitRepository());
$branches = $git->getBranches();
$title = sprintf(__('%s %s Change Log'), (string) $request->project,
$this->getScmType($request));
$scm = IDF_ScmFactory::getScm($request);
$branches = $scm->getBranches();
$commit = $match[2];
$res = $git->getChangeLog($commit, 25);
$res = $scm->getChangeLog($commit, 25);
$scmConf = $request->conf->getVal('scm', 'git');
return Pluf_Shortcuts_RenderToResponse('source/changelog.html',
array(
'page_title' => $title,
@ -46,6 +48,7 @@ class IDF_Views_Source
'changes' => $res,
'commit' => $commit,
'branches' => $branches,
'scm' => $scmConf,
),
$request);
}
@ -53,21 +56,27 @@ class IDF_Views_Source
public $treeBase_precond = array('IDF_Precondition::accessSource');
public function treeBase($request, $match)
{
$title = sprintf(__('%s Git Source Tree'), (string) $request->project);
$git = new IDF_Git($request->project->getGitRepository());
$title = sprintf(__('%s %s Source Tree'), (string) $request->project,
$this->getScmType($request));
$scm = IDF_ScmFactory::getScm($request);
$commit = $match[2];
$branches = $git->getBranches();
if ('commit' != $git->testHash($commit)) {
$branches = $scm->getBranches();
if ('commit' != $scm->testHash($commit)) {
// Redirect to the first branch
$url = Pluf_HTTP_URL_urlForView('IDF_Views_Source::treeBase',
array($request->project->shortname,
$branches[0]));
return new Pluf_HTTP_Response_Redirect($url);
}
$res = $git->filesAtCommit($commit);
$cobject = $git->getCommit($commit);
$res = $scm->filesAtCommit($commit);
$cobject = $scm->getCommit($commit);
$tree_in = in_array($commit, $branches);
return Pluf_Shortcuts_RenderToResponse('source/tree.html',
$scmConf = $request->conf->getVal('scm', 'git');
$props = null;
if ($scmConf === 'svn') {
$props = $scm->getProperties($commit);
}
return Pluf_Shortcuts_RenderToResponse('source/'.$scmConf.'/tree.html',
array(
'page_title' => $title,
'title' => $title,
@ -76,6 +85,7 @@ class IDF_Views_Source
'commit' => $commit,
'tree_in' => $tree_in,
'branches' => $branches,
'props' => $props,
),
$request);
}
@ -83,19 +93,20 @@ class IDF_Views_Source
public $tree_precond = array('IDF_Precondition::accessSource');
public function tree($request, $match)
{
$title = sprintf(__('%s Git Source Tree'), (string) $request->project);
$git = new IDF_Git($request->project->getGitRepository());
$branches = $git->getBranches();
$title = sprintf(__('%s %s Source Tree'), (string) $request->project,
$this->getScmType($request));
$scm = IDF_ScmFactory::getScm($request);
$branches = $scm->getBranches();
$commit = $match[2];
if ('commit' != $git->testHash($commit)) {
$request_file = $match[3];
if ('commit' != $scm->testHash($commit, $request_file)) {
// Redirect to the first branch
$url = Pluf_HTTP_URL_urlForView('IDF_Views_Source::treeBase',
array($request->project->shortname,
$branches[0]));
return new Pluf_HTTP_Response_Redirect($url);
}
$request_file = $match[3];
$request_file_info = $git->getFileInfo($request_file, $commit);
$request_file_info = $scm->getFileInfo($request_file, $commit);
if (!$request_file_info) {
// Redirect to the first branch
$url = Pluf_HTTP_URL_urlForView('IDF_Views_Source::treeBase',
@ -105,21 +116,32 @@ class IDF_Views_Source
}
if ($request_file_info->type != 'tree') {
$info = self::getMimeType($request_file_info->file);
$rep = new Pluf_HTTP_Response($git->getBlob($request_file_info->hash),
if (Pluf::f('src') == 'git') {
$rep = new Pluf_HTTP_Response($scm->getBlob($request_file_info->hash),
$info[0]);
}
else {
$rep = new Pluf_HTTP_Response($scm->getBlob($request_file_info->fullpath, $commit),
$info[0]);
}
$rep->headers['Content-Disposition'] = 'attachment; filename="'.$info[1].'"';
return $rep;
}
$bc = self::makeBreadCrumb($request->project, $commit, $request_file_info->file);
$page_title = $bc.' - '.$title;
$cobject = $git->getCommit($commit);
$cobject = $scm->getCommit($commit);
$tree_in = in_array($commit, $branches);
$res = $git->filesAtCommit($commit, $request_file);
$res = $scm->filesAtCommit($commit, $request_file);
// try to find the previous level if it exists.
$prev = split('/', $request_file);
$l = array_pop($prev);
$previous = substr($request_file, 0, -strlen($l.' '));
return Pluf_Shortcuts_RenderToResponse('source/tree.html',
$scmConf = $request->conf->getVal('scm', 'git');
$props = null;
if ($scmConf === 'svn') {
$props = $scm->getProperties($commit, $request_file);
}
return Pluf_Shortcuts_RenderToResponse('source/'.$scmConf.'/tree.html',
array(
'page_title' => $page_title,
'title' => $title,
@ -131,6 +153,7 @@ class IDF_Views_Source
'prev' => $previous,
'tree_in' => $tree_in,
'branches' => $branches,
'props' => $props,
),
$request);
}
@ -155,10 +178,10 @@ class IDF_Views_Source
public $commit_precond = array('IDF_Precondition::accessSource');
public function commit($request, $match)
{
$git = new IDF_Git($request->project->getGitRepository());
$scm = IDF_ScmFactory::getScm($request);
$commit = $match[2];
$branches = $git->getBranches();
if ('commit' != $git->testHash($commit)) {
$branches = $scm->getBranches();
if ('commit' != $scm->testHash($commit)) {
// Redirect to the first branch
$url = Pluf_HTTP_URL_urlForView('IDF_Views_Source::treeBase',
array($request->project->shortname,
@ -167,9 +190,10 @@ class IDF_Views_Source
}
$title = sprintf(__('%s Commit Details'), (string) $request->project);
$page_title = sprintf(__('%s Commit Details - %s'), (string) $request->project, $commit);
$cobject = $git->getCommit($commit);
$cobject = $scm->getCommit($commit);
$diff = new IDF_Diff($cobject->changes);
$diff->parse();
$scmConf = $request->conf->getVal('scm', 'git');
return Pluf_Shortcuts_RenderToResponse('source/commit.html',
array(
'page_title' => $page_title,
@ -178,6 +202,7 @@ class IDF_Views_Source
'cobject' => $cobject,
'commit' => $commit,
'branches' => $branches,
'scm' => $scmConf,
),
$request);
}
@ -190,9 +215,9 @@ class IDF_Views_Source
public function download($request, $match)
{
$commit = trim($match[2]);
$git = new IDF_Git($request->project->getGitRepository());
$branches = $git->getBranches();
if ('commit' != $git->testHash($commit)) {
$scm = IDF_ScmFactory::getScm($request);
$branches = $scm->getBranches();
if ('commit' != $scm->testHash($commit)) {
// Redirect to the first branch
$url = Pluf_HTTP_URL_urlForView('IDF_Views_Source::treeBase',
array($request->project->shortname,
@ -200,13 +225,76 @@ class IDF_Views_Source
return new Pluf_HTTP_Response_Redirect($url);
}
$base = $request->project->shortname.'-'.$commit;
$cmd = $git->getArchiveCommand($commit, $base.'/');
$cmd = $scm->getArchiveCommand($commit, $base.'/');
$rep = new Pluf_HTTP_Response_CommandPassThru($cmd, 'application/x-zip');
$rep->headers['Content-Transfer-Encoding'] = 'binary';
$rep->headers['Content-Disposition'] = 'attachment; filename="'.$base.'.zip"';
return $rep;
}
/**
* Display tree of a specific SVN revision
*
*/
public function treeRev($request, $match)
{
$prj = $request->project;
// Redirect to tree base if not svn
if ($request->conf->getVal('scm', 'git') != 'svn') {
$url = Pluf_HTTP_URL_urlForView('IDF_Views_Source::treeBase',
array($prj->shortname, $prj->getScmRoot()));
return new Pluf_HTTP_Response_Redirect($url);
}
// Get revision value
if (!isset($request->REQUEST['rev']) or trim($request->REQUEST['rev']) == '') {
$scmRoot = $prj->getScmRoot();
}
else {
$scmRoot = $request->REQUEST['rev'];
}
// Get source if not /
if (isset($request->REQUEST['sourcefile']) and trim($request->REQUEST['sourcefile']) != '') {
$scmRoot .= '/'.$request->REQUEST['sourcefile'];
}
// Redirect
$url = Pluf_HTTP_URL_urlForView('IDF_Views_Source::treeBase',
array($prj->shortname, $scmRoot));
return new Pluf_HTTP_Response_Redirect($url);
}
/**
* Display SVN changelog from specific revision
*
*/
public function changelogRev($request, $match)
{
$prj = $request->project;
// Redirect to tree base if not svn
if ($request->conf->getVal('scm', 'git') != 'svn') {
$scmRoot = $prj->getScmRoot();
}
// Get revision value if svn
else {
if (!isset($request->REQUEST['rev']) or trim($request->REQUEST['rev']) == '') {
$scmRoot = $prj->getScmRoot();
}
else {
$scmRoot = $request->REQUEST['rev'];
}
}
// Redirect
$url = Pluf_HTTP_URL_urlForView('IDF_Views_Source::changeLog',
array($prj->shortname, $scmRoot));
return new Pluf_HTTP_Response_Redirect($url);
}
/**
* Find the mime type of a file.
*
@ -238,6 +326,16 @@ class IDF_Views_Source
}
return array('application/octet-stream', $info['basename']);
}
/**
* Get the scm type for page title
*
* @return String
*/
private function getScmType($request)
{
return ucfirst($scm = $request->conf->getVal('scm', 'git'));
}
}
function IDF_Views_Source_PrettySize($size)

View File

@ -133,7 +133,7 @@ $ctl[] = array('regex' => '#^/p/(\w+)/issues/my/(\w+)/$#',
'model' => 'IDF_Views_Issue',
'method' => 'myIssues');
// ---------- GIT ----------------------------------------
// ---------- SCM ----------------------------------------
$ctl[] = array('regex' => '#^/p/(\w+)/source/$#',
'base' => $base,
@ -171,6 +171,18 @@ $ctl[] = array('regex' => '#^/p/(\w+)/source/download/(\w+)/$#',
'model' => 'IDF_Views_Source',
'method' => 'download');
$ctl[] = array('regex' => '#^/p/(\w+)/source/treerev/$#',
'base' => $base,
'priority' => 4,
'model' => 'IDF_Views_Source',
'method' => 'treeRev');
$ctl[] = array('regex' => '#^/p/(\w+)/source/changesrev/$#',
'base' => $base,
'priority' => 4,
'model' => 'IDF_Views_Source',
'method' => 'changelogRev');
// ---------- Downloads ------------------------------------
$ctl[] = array('regex' => '#^/p/(\w+)/downloads/$#',

View File

@ -44,7 +44,7 @@
<a accesskey="1" href="{url 'IDF_Views_Project::home', array($project.shortname)}"{block tabhome}{/block}>{trans 'Project Home'}</a>
{if $hasIssuesAccess} <a href="{url 'IDF_Views_Issue::index', array($project.shortname)}"{block tabissues}{/block}>{trans 'Issues'}</a>{/if}
{if $hasDownloadsAccess} <a href="{url 'IDF_Views_Download::index', array($project.shortname)}"{block tabdownloads}{/block}>{trans 'Downloads'}</a>{/if}
{if $hasSourceAccess} <a href="{url 'IDF_Views_Source::treeBase', array($project.shortname, 'master')}"{block tabsource}{/block}>{trans 'Source'}</a>{/if}
{if $hasSourceAccess} <a href="{url 'IDF_Views_Source::treeBase', array($project.shortname, $project.getScmRoot())}"{block tabsource}{/block}>{trans 'Source'}</a>{/if}
{if $isOwner}
<a href="{url 'IDF_Views_Project::admin', array($project.shortname)}"{block tabadmin}{/block}>{trans 'Administer'}</a>{/if}{/if}
</div>

View File

@ -2,8 +2,8 @@
{block tabsource} class="active"{/block}
{block subtabs}
<div id="sub-tabs">
<a {if $inSourceTree}class="active" {/if}href="{url 'IDF_Views_Source::treeBase', array($project.shortname, 'master')}">{trans 'Source Tree'}</a> |
<a {if $inChangeLog}class="active" {/if}href="{url 'IDF_Views_Source::changeLog', array($project.shortname, 'master')}">{trans 'Change Log'}</a>
<a {if $inSourceTree}class="active" {/if}href="{url 'IDF_Views_Source::treeBase', array($project.shortname, $project.getScmRoot())}">{trans 'Source Tree'}</a> |
<a {if $inChangeLog}class="active" {/if}href="{url 'IDF_Views_Source::changeLog', array($project.shortname, $project.getScmRoot())}">{trans 'Change Log'}</a>
{if $inCommit}| {trans 'Commit'}{/if}
</div>
{/block}

View File

@ -29,11 +29,21 @@
</table>
{/block}
{block context}
{if $scm == 'git'}
<p><strong>{trans 'Branches:'}</strong><br />
{foreach $branches as $branch}
{aurl 'url', 'IDF_Views_Source::changeLog', array($project.shortname, $branch)}
<span class="label{if $commit == $branch} active{/if}"><a href="{$url}" class="label">{$branch}</a></span><br />
{/foreach}
</p>
{/if}
{if $scm == 'svn'}
<p><strong>{trans 'Revison:'} {$commit}</strong><br />
<form class="star" action="{url 'IDF_Views_Source::changelogRev', array($project.shortname)}" method="get">
<input accesskey="4" type="text" value="{$commit}" name="rev" size="20" />
<input type="submit" name="s" value="{trans 'Go to revision'}" />
</form>
</p>
{/if}
{/block}

View File

@ -36,11 +36,13 @@
{/if}
{/block}
{block context}
{if $scm == 'git'}
<p><strong>{trans 'Branches:'}</strong><br />
{foreach $branches as $branch}
{aurl 'url', 'IDF_Views_Source::commit', array($project.shortname, $branch)}
<span class="label{if $commit == $branch} active{/if}"><a href="{$url}" class="label">{$branch}</a></span><br />
{/foreach}
</p>
{/if}
{/block}

View File

@ -0,0 +1,71 @@
{extends "source/base.html"}
{block docclass}yui-t1{assign $inSourceTree=true}{/block}
{block body}
<h2><a href="{url 'IDF_Views_Source::treeBase', array($project.shortname, $commit)}">{trans 'Root'}</a><span class="sep">/</span>{if $breadcrumb}{$breadcrumb|safe}{/if}</h2>
<table summary="" class="tree-list">
<thead>
<tr>
<th colspan="2">{trans 'File'}</th>
<th>{trans 'Age'}</th>
<th>{trans 'Rev'}</th>
<th>{trans 'Message'}</th>
<th>{trans 'Size'}</th>
</tr>
</thead>{if !$tree_in || $props}
{aurl 'url', 'IDF_Views_Source::commit', array($project.shortname, $commit)}
<tfoot>
{if $props}
<tr><th colspan="6">
<ul>
{foreach $props as $prop => $val}
<li>{trans 'Property'} <strong>{$prop}</strong> {trans 'set to:'} <em>{$val}</em></li>
{/foreach}
</ul>
</th></tr>
{/if}
{if !$tree_in}
<tr><th colspan="6">{blocktrans}Source at commit <a class="mono" href="{$url}">{$commit}</a> created {$cobject.date|dateago}.{/blocktrans}<br />
<span class="smaller">{blocktrans}By {$cobject.author|strip_tags|trim}, {$cobject.title}{/blocktrans}</span>
</th></tr>
{/if}
</tfoot>
{/if}<tbody>
{if $base}
<tr>
<td>&nbsp;</td>
<td>
<a href="{url 'IDF_Views_Source::tree', array($project.shortname, $commit, $prev)}">..</a></td>
<td colspan="3"></td>
</tr>
{/if}
{foreach $files as $file}
{aurl 'url', 'IDF_Views_Source::tree', array($project.shortname, $commit, $file.fullpath)}
<tr>
<td class="fileicon"><img src="{media '/idf/img/'~$file.type~'.png'}" alt="{$file.type}" /></td>
<td><a href="{$url}">{$file.file}</a></td>
<td><span class="smaller">{$file.date|dateago:"wihtout"}</span></td>
<td>{$file.rev}</td>
<td{if $file.type != 'blob'} colspan="2"{/if}><span class="smaller">{$file.log|nl2br}</span></td>
{if $file.type == 'blob'}
<td>{$file.size|size}</td>
{/if}
</tr>
{/foreach}
</tbody>
</table>
<p class="right soft"><img style="vertical-align: text-bottom;" src="{media '/idf/img/package-grey.png'}" alt="{trans 'Archive'}" align="bottom" /></a> <kbd>svn checkout {$project.getSvnDaemonUrl()}@{$commit}</kbd></p>
{/block}
{block context}
<p><strong>{trans 'Revison:'} {$commit}</strong><br />
<form class="star" action="{url 'IDF_Views_Source::treeRev', array($project.shortname)}" method="get">
<input accesskey="4" type="text" value="{$commit}" name="rev" size="20" />
<input type="hidden" name="sourcefile" value="{$base}"/>
<input type="submit" name="s" value="{trans 'Go to revision'}" />
</form>
</p>
{/block}

View File

@ -330,6 +330,11 @@ table.tree-list tfoot th a {
font-weight: normal;
}
table.tree-list tfoot th ul {
text-align: left;
font-size: 85%;
}
table.tree-list tr.log {
border-bottom: 1px solid #e7ebe3;
/* background-color: #eef2ea !important; */