From 9ac6e38e819d181191d1b9e09b4c5e476e0aa289 Mon Sep 17 00:00:00 2001 From: Loic d'Anterroches Date: Tue, 11 Nov 2008 22:32:01 +0100 Subject: [PATCH] Added inline visualization of text files from the repository. --- src/IDF/Diff.php | 6 +- src/IDF/Views/Source.php | 100 ++++++++++++++++++++++++- src/IDF/conf/views.php | 6 ++ src/IDF/templates/source/commit.html | 2 +- src/IDF/templates/source/git/file.html | 21 ++++++ src/IDF/templates/source/git/tree.html | 2 +- src/IDF/templates/source/svn/file.html | 23 ++++++ src/IDF/templates/source/svn/tree.html | 2 +- www/media/idf/css/style.css | 55 ++++++++++++++ 9 files changed, 208 insertions(+), 9 deletions(-) create mode 100644 src/IDF/templates/source/git/file.html create mode 100644 src/IDF/templates/source/svn/file.html 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}

+ + +{$file} +
+{aurl 'url', 'IDF_Views_Source::getFile', array($project.shortname, $commit, $fullpath)} +

{trans 'Archive'} {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}

+ +
+{$file} +
+{aurl 'url', 'IDF_Views_Source::getFile', array($project.shortname, $commit, $fullpath)} +

{trans 'Archive'} {trans 'Download this file'}

+ + +{/block} +{block context} +
+

{trans 'Revision:'} {$commit}

+

+ + +

+
+ +{/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 */