diff --git a/src/IDF/Diff.php b/src/IDF/Diff.php
index 58bab6b..b1073b5 100644
--- a/src/IDF/Diff.php
+++ b/src/IDF/Diff.php
@@ -127,7 +127,7 @@ class IDF_Diff
} else {
$class = 'diff-a';
}
- $line_content = $this->padLine(Pluf_esc($line[2]));
+ $line_content = self::padLine(Pluf_esc($line[2]));
$out .= sprintf('
%s | %s | %s |
'."\n", $line[0], $line[1], $class, $line_content);
}
if (count($file['chunks']) > $cc)
@@ -136,11 +136,11 @@ class IDF_Diff
}
$out .= '';
}
- return $out;
+ return Pluf_Template::markSafe($out);
}
- public function padLine($line)
+ public static function padLine($line)
{
$n = strlen($line);
for ($i=0;$i<$n;$i++) {
diff --git a/src/IDF/Views/Source.php b/src/IDF/Views/Source.php
index a1917ef..172277d 100644
--- a/src/IDF/Views/Source.php
+++ b/src/IDF/Views/Source.php
@@ -128,12 +128,17 @@ class IDF_Views_Source
$rep = new Pluf_HTTP_Response($scm->getBlob($request_file_info, $commit),
$info[0]);
$rep->headers['Content-Disposition'] = 'attachment; filename="'.$info[1].'"';
+ return $rep;
} else {
// We want to display the content of the file as text
- $rep = new Pluf_HTTP_Response($scm->getBlob($request_file_info, $commit),
- 'text/plain');
+ $extra = array('branches' => $branches,
+ 'commit' => $commit,
+ 'request_file' => $request_file,
+ 'request_file_info' => $request_file_info,
+ 'mime' => $info,
+ );
+ return $this->viewFile($request, $match, $extra);
}
- return $rep;
}
$bc = self::makeBreadCrumb($request->project, $commit, $request_file_info->file);
$page_title = $bc.' - '.$title;
@@ -215,6 +220,83 @@ class IDF_Views_Source
$request);
}
+ /**
+ * Should only be called through self::tree
+ */
+ public function viewFile($request, $match, $extra)
+ {
+ $title = sprintf(__('%1$s %2$s Source Tree'), (string) $request->project,
+ $this->getScmType($request));
+ $scm = IDF_Scm::get($request);
+ $branches = $extra['branches'];
+ $commit = $extra['commit'];
+ $request_file = $extra['request_file'];
+ $request_file_info = $extra['request_file_info'];
+ $bc = self::makeBreadCrumb($request->project, $commit, $request_file_info->file);
+ $page_title = $bc.' - '.$title;
+ $cobject = $scm->getCommit($commit);
+ $tree_in = in_array($commit, $branches);
+ // try to find the previous level if it exists.
+ $prev = split('/', $request_file);
+ $l = array_pop($prev);
+ $previous = substr($request_file, 0, -strlen($l.' '));
+ $scmConf = $request->conf->getVal('scm', 'git');
+ $props = null;
+ if ($scmConf === 'svn') {
+ $props = $scm->getProperties($commit, $request_file);
+ }
+ $content = self::highLight($extra['mime'], $scm->getBlob($request_file_info, $commit));
+ return Pluf_Shortcuts_RenderToResponse('source/'.$scmConf.'/file.html',
+ array(
+ 'page_title' => $page_title,
+ 'title' => $title,
+ 'breadcrumb' => $bc,
+ 'file' => $content,
+ 'commit' => $commit,
+ 'cobject' => $cobject,
+ 'fullpath' => $request_file,
+ 'base' => $request_file_info->file,
+ 'prev' => $previous,
+ 'tree_in' => $tree_in,
+ 'branches' => $branches,
+ 'props' => $props,
+ ),
+ $request);
+ }
+
+ /**
+ * Get a given file at a given commit.
+ *
+ */
+ public $getFile_precond = array('IDF_Precondition::accessSource');
+ public function getFile($request, $match)
+ {
+ $scm = IDF_Scm::get($request);
+ $branches = $scm->getBranches();
+ $commit = $match[2];
+ $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_info = $scm->getFileInfo($request_file, $commit);
+ if (!$request_file_info or $request_file_info->type == 'tree') {
+ // 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);
+ }
+ $info = self::getMimeType($request_file_info->file);
+ $rep = new Pluf_HTTP_Response($scm->getBlob($request_file_info, $commit),
+ $info[0]);
+ $rep->headers['Content-Disposition'] = 'attachment; filename="'.$info[1].'"';
+ return $rep;
+ }
+
/**
* Get a zip archive of the current commit.
*
@@ -289,6 +371,18 @@ class IDF_Views_Source
return (in_array($fileinfo[2], explode(' ', $ext)));
}
+ public static function highLight($fileinfo, $content)
+ {
+ $table = array();
+ $i = 1;
+ foreach (preg_split("/\015\012|\015|\012/", $content) as $line) {
+ $table[] = ''.$i.' | '
+ .''.IDF_Diff::padLine(Pluf_esc($line)).' |
';
+ $i++;
+ }
+ return Pluf_Template::markSafe(implode("\n", $table));
+ }
+
/**
* Get the scm type for page title
*
diff --git a/src/IDF/conf/views.php b/src/IDF/conf/views.php
index 4a65b97..c14c6a2 100644
--- a/src/IDF/conf/views.php
+++ b/src/IDF/conf/views.php
@@ -165,6 +165,12 @@ $ctl[] = array('regex' => '#^/p/([\-\w]+)/source/download/(\w+)/$#',
'model' => 'IDF_Views_Source',
'method' => 'download');
+$ctl[] = array('regex' => '#^/p/([\-\w]+)/source/file/(\w+)/(.*)$#',
+ 'base' => $base,
+ 'priority' => 4,
+ 'model' => 'IDF_Views_Source',
+ 'method' => 'getFile');
+
$ctl[] = array('regex' => '#^/p/([\-\w]+)/source/treerev/$#',
'base' => $base,
'priority' => 4,
diff --git a/src/IDF/templates/source/commit.html b/src/IDF/templates/source/commit.html
index 4231c30..d912890 100644
--- a/src/IDF/templates/source/commit.html
+++ b/src/IDF/templates/source/commit.html
@@ -32,7 +32,7 @@
{if count($diff.files)}
{trans 'Change Details'}
-{$diff.as_html()|safe}
+{$diff.as_html()}
{/if}
{/block}
{block context}
diff --git a/src/IDF/templates/source/git/file.html b/src/IDF/templates/source/git/file.html
new file mode 100644
index 0000000..5607344
--- /dev/null
+++ b/src/IDF/templates/source/git/file.html
@@ -0,0 +1,21 @@
+{extends "source/base.html"}
+{block docclass}yui-t1{assign $inSourceTree=true}{/block}
+{block body}
+{trans 'Root'}/{if $breadcrumb}{$breadcrumb|safe}{/if}
+
+
+{aurl 'url', 'IDF_Views_Source::getFile', array($project.shortname, $commit, $fullpath)}
+ {trans 'Download this file'}
+
+
+{/block}
+{block context}
+{trans 'Branches:'}
+{foreach $branches as $branch}
+{aurl 'url', 'IDF_Views_Source::treeBase', array($project.shortname, $branch)}
+{$branch}
+{/foreach}
+
+{/block}
diff --git a/src/IDF/templates/source/git/tree.html b/src/IDF/templates/source/git/tree.html
index 9db8cae..6bf4549 100644
--- a/src/IDF/templates/source/git/tree.html
+++ b/src/IDF/templates/source/git/tree.html
@@ -1,7 +1,7 @@
{extends "source/base.html"}
{block docclass}yui-t1{assign $inSourceTree=true}{/block}
{block body}
-{trans 'Root'}/{if $breadcrumb}{$breadcrumb|safe}{/if}
+{trans 'Root'}/{if $breadcrumb}{$breadcrumb|safe}{/if}
diff --git a/src/IDF/templates/source/svn/file.html b/src/IDF/templates/source/svn/file.html
new file mode 100644
index 0000000..fcd7213
--- /dev/null
+++ b/src/IDF/templates/source/svn/file.html
@@ -0,0 +1,23 @@
+{extends "source/base.html"}
+{block docclass}yui-t1{assign $inSourceTree=true}{/block}
+{block body}
+{trans 'Root'}/{if $breadcrumb}{$breadcrumb|safe}{/if}
+
+
+{aurl 'url', 'IDF_Views_Source::getFile', array($project.shortname, $commit, $fullpath)}
+ {trans 'Download this file'}
+
+
+{/block}
+{block context}
+
+
+{/block}
diff --git a/src/IDF/templates/source/svn/tree.html b/src/IDF/templates/source/svn/tree.html
index e0016d1..7e8c0c7 100644
--- a/src/IDF/templates/source/svn/tree.html
+++ b/src/IDF/templates/source/svn/tree.html
@@ -1,7 +1,7 @@
{extends "source/base.html"}
{block docclass}yui-t1{assign $inSourceTree=true}{/block}
{block body}
-{trans 'Root'}/{if $breadcrumb}{$breadcrumb|safe}{/if}
+{trans 'Root'}/{if $breadcrumb}{$breadcrumb|safe}{/if}
diff --git a/www/media/idf/css/style.css b/www/media/idf/css/style.css
index f83f64f..b350285 100644
--- a/www/media/idf/css/style.css
+++ b/www/media/idf/css/style.css
@@ -19,6 +19,10 @@
#
# ***** END LICENSE BLOCK ***** */
+h2.top {
+ margin-top: 0;
+}
+
.yui-g {
padding: 0 1em;
}
@@ -480,6 +484,57 @@ table.diff tr.diff-next td {
padding: 1px 5px;
}
+/**
+ * view file content
+ */
+table.code {
+ border-bottom: 1px solid #d3d7cf;
+ border-top: 1px solid #d3d7cf;
+ width: 100%;
+}
+
+table.code tr {
+ border-left: 1px solid #d3d7cf;
+ border-right: 1px solid #d3d7cf;
+ border-bottom: none;
+ border-top: none;
+}
+
+table.code td {
+ font-size: 90%;
+ vertical-align: top;
+ padding: 1px;
+ border-color: inherit;
+}
+
+table.code td.code {
+ border: none;
+ /* Whitespace hacking from: http://ln.hixie.ch/ */
+ white-space: pre; /* CSS2 */
+ white-space: -moz-pre-wrap; /* Mozilla */
+ white-space: -hp-pre-wrap; /* HP printers */
+ white-space: -o-pre-wrap; /* Opera 7 */
+ white-space: -pre-wrap; /* Opera 4-6 */
+ white-space: pre-wrap; /* CSS 2.1 */
+ white-space: pre-line; /* CSS 3 (and 2.1 as well, actually) */
+ word-wrap: break-word; /* IE */
+ padding-left: 5px;
+}
+
+table.code td.code-lc {
+ text-align: right;
+ padding: 1px 5px;
+ border-color: inherit;
+ border-top: 1px solid #d3d7cf;
+ border-bottom: 1px solid #d3d7cf;
+ width: 3em;
+}
+
+table.code td.code-lc a {
+ color: #555753;
+ text-decoration: none;
+}
+
/**
* Download
*/