]> git.ipfire.org Git - thirdparty/public-inbox.git/commitdiff
repobrowse: implement preliminary summary page
authorEric Wong <e@80x24.org>
Fri, 15 Jan 2016 03:00:13 +0000 (03:00 +0000)
committerEric Wong <e@80x24.org>
Tue, 5 Apr 2016 18:58:27 +0000 (18:58 +0000)
This should provide a decent landing page for projects.
Alternative README files may be configured with the per-repo
"readme" directive.

lib/PublicInbox/Repobrowse.pm
lib/PublicInbox/RepobrowseConfig.pm
lib/PublicInbox/RepobrowseGitSummary.pm [new file with mode: 0644]

index 75dee72fafebbbf33eb07dc0eb139cf8fbbde584..9e97593b19c8dad4437a2ece0e8f05ff070132ea 100644 (file)
@@ -63,33 +63,41 @@ sub run {
        };
 
        my $cmd = shift @extra;
+       my $vcs_lc = $repo_info->{vcs};
+       my $vcs = $VCS{$vcs_lc} or return r404();
+       my $mod;
        if (defined $cmd && length $cmd) {
-               my $vcs_lc = $repo_info->{vcs};
-               my $vcs = $VCS{$vcs_lc} or return r404();
-               my $mod = $CMD{$cmd};
+               $mod = $CMD{$cmd};
                unless ($mod) {
                        unshift @extra, $cmd;
                        $mod = 'Fallback';
                }
-               $mod = load_once("PublicInbox::Repobrowse$vcs$mod");
-               $vcs = load_once("PublicInbox::$vcs");
-               $repo_info->{$vcs_lc} ||= $vcs->new($repo_info->{path});
                $req->{relcmd} = '../' x scalar(@extra);
-               while (@extra && $extra[-1] eq '') {
-                       pop @extra;
-                       ++$req->{tslash};
-               }
-               $req->{expath} = join('/', @extra);
-               my $rv = eval { $mod->new->call($cmd, $req) };
-               $rv || r404();
        } else {
-               $req->{relcmd} = defined $cmd ? ''  : './';
-               summary($req);
+               $mod = 'Summary';
+               $cmd = 'summary';
+               if ($path_info =~ m!/\z!) {
+                       $req->{tslash} = $path_info =~ tr!/!!;
+                       $req->{relcmd} = '';
+               } else {
+                       my @repo = split('/', $repo_path);
+                       if (@repo > 1) {
+                               $req->{relcmd} = "./$repo[-1]/";
+                       } else {
+                               $req->{relcmd} = "/$repo[-1]/";
+                       }
+               }
        }
-}
-
-sub summary {
-       r404();
+       $mod = load_once("PublicInbox::Repobrowse$vcs$mod");
+       $vcs = load_once("PublicInbox::$vcs");
+       $repo_info->{$vcs_lc} ||= $vcs->new($repo_info->{path});
+       while (@extra && $extra[-1] eq '') {
+               pop @extra;
+               ++$req->{tslash};
+       }
+       $req->{expath} = join('/', @extra);
+       my $rv = eval { $mod->new->call($cmd, $req) }; # RepobrowseBase::call
+       $rv || r404();
 }
 
 sub r404 { r(404, 'Not Found') }
index 2f780b65ba6164e5c6db5400a737866b3bef6e0c..b643f7dcef58b9327ab3e0b6361fa39ba2e79a71 100644 (file)
@@ -45,7 +45,7 @@ sub lookup {
        $rv->{desc_html} =
                PublicInbox::Hval->new_oneline($rv->{description})->as_html;
 
-       foreach my $key (qw(publicinbox vcs)) {
+       foreach my $key (qw(publicinbox vcs readme)) {
                $rv->{$key} = $self->{"repo.$repo_path.$key"};
        }
 
diff --git a/lib/PublicInbox/RepobrowseGitSummary.pm b/lib/PublicInbox/RepobrowseGitSummary.pm
new file mode 100644 (file)
index 0000000..65e32b6
--- /dev/null
@@ -0,0 +1,102 @@
+# Copyright (C) 2016 all contributors <meta@public-inbox.org>
+# License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt>
+
+# The main summary/landing page of a git repository viewer
+package PublicInbox::RepobrowseGitSummary;
+use strict;
+use warnings;
+use PublicInbox::Hval qw(utf8_html);
+use base qw(PublicInbox::RepobrowseBase);
+
+sub call_git_summary {
+       my ($self, $req) = @_;
+       sub {
+               my ($res) = @_; # Plack streaming callback
+               emit_summary($self, $req, $res);
+       }
+}
+
+use constant EACH_REF_FMT => '--format=' .
+               join(' ', map { "%($_)" }
+               qw(refname objecttype objectname creatordate:short subject));
+
+sub emit_summary {
+       my ($self, $req, $res) = @_;
+       my $repo_info = $req->{repo_info};
+       my $git = $repo_info->{git};
+       my $count = 10; # TODO: configurable
+       my $fh;
+
+       # n.b. we would use %(HEAD) in for-each-ref --format if we could
+       # rely on git 1.9.0+, but it's too soon for that in early 2016...
+       chomp(my $head_ref = $git->qx(qw(rev-parse HEAD~0)));
+
+       my $refs = $git->popen(qw(for-each-ref --sort=-creatordate),
+                               EACH_REF_FMT, "--count=$count",
+                               qw(refs/heads/ refs/tags/));
+       $fh = $res->([200, ['Content-Type'=>'text/html; charset=UTF-8']]);
+       # ref names are unpredictable in length and requires tables :<
+       $fh->write($self->html_start($req,
+                               "$repo_info->{path_info}: overview") .
+                       '</pre><table>');
+
+       my $rel = $req->{relcmd};
+       foreach (<$refs>) {
+               my ($ref, $type, $hex, $date, $s) = split(' ', $_, 5);
+               $ref =~ s!\Arefs/(?:heads|tags)/!!;
+               $ref = PublicInbox::Hval->utf8($ref);
+               my $h = $ref->as_html;
+               $ref = $ref->as_href;
+               my $sref;
+               if ($type eq 'tag') {
+                       $h = "<b>$h</b>";
+                       $sref = $ref = $rel . 'tag?h=' . $ref;
+               } elsif ($type eq 'commit') {
+                       $sref = $rel . 'commit?h=' . $ref;
+                       $ref = $rel . 'log?h=' . $ref;
+               } else {
+                       # no point in wasting code to support tagged
+                       # trees/blobs...
+                       next;
+               }
+               chomp $s;
+               my $x = $hex eq $head_ref ? ' (HEAD)' : '';
+               $fh->write(qq(<tr><td><tt><a\nhref="$ref">$h</a>$x</tt></td>) .
+                       qq(<td><tt>$date <a\nhref="$sref">) . utf8_html($s) .
+                       '</a></tt></td></tr>');
+
+       }
+       $fh->write('</table>');
+
+       # some people will use README.md or even README.sh here...
+       my $readme = $repo_info->{readme};
+       defined $readme or $readme = 'README';
+       my $doc = $git->cat_file('HEAD:'.$readme);
+       if (defined $doc) {
+               $fh->write('<pre>' .
+                       readme_path_links($rel, $readme) . " (HEAD)\n\n");
+               $fh->write(utf8_html($$doc));
+               $fh->write('</pre>');
+       }
+       $fh->write('</body></html>');
+       $fh->close;
+}
+
+sub readme_path_links {
+       my ($rel, $readme) = @_;
+       my @path = split(m!/+!, $readme);
+
+       my $s = "tree <a\nhref=\"${rel}tree\">root</a>/";
+       my @t;
+       $s .= join('/', (map {
+               push @t, $_;
+               my $e = PublicInbox::Hval->utf8($_, join('/', @t));
+               my $ep = $e->as_path;
+               my $eh = $e->as_html;
+               $e = "<a\nhref=\"${rel}tree/$ep\">$eh</a>";
+               # bold the last one
+               scalar(@t) == scalar(@path) ? "<b>$e</b>" : $e;
+       } @path));
+}
+
+1;