diff options
Diffstat (limited to 'lib/PublicInbox/RepoGitAtom.pm')
-rw-r--r-- | lib/PublicInbox/RepoGitAtom.pm | 160 |
1 files changed, 160 insertions, 0 deletions
diff --git a/lib/PublicInbox/RepoGitAtom.pm b/lib/PublicInbox/RepoGitAtom.pm new file mode 100644 index 00000000..4b074fcc --- /dev/null +++ b/lib/PublicInbox/RepoGitAtom.pm @@ -0,0 +1,160 @@ +# Copyright (C) 2016 all contributors <meta@public-inbox.org> +# License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt> + +# show log as an Atom feed +package PublicInbox::RepoGitAtom; +use strict; +use warnings; +use PublicInbox::Hval qw(utf8_html); +use base qw(PublicInbox::RepoBase); +use PublicInbox::Qspawn; + +use constant DATEFMT => '%Y-%m-%dT%H:%M:%SZ'; +use constant STATES => qw(H ct an ae at s b); +use constant STATE_BODY => (scalar(STATES) - 1); +my $ATOM_FMT = '--pretty=tformat:'. + join('%n', map { "%$_" } STATES).'%x00'; +use POSIX qw(strftime); + +sub repo_root_url { + my ($self, $req) = @_; + my $env = $req->{env}; + my $uri = $env->{REQUEST_URI}; + $uri =~ s/\?.+\z//; # no query string + my @uri = split(m!/+!, $uri); + my @extra = @{$req->{extra}}; + while (@uri && @extra && $uri[-1] eq $extra[-1]) { + pop @uri; + pop @extra; + } + pop @uri if $uri[-1] eq 'atom'; # warn if not equal? + PublicInbox::Repobrowse::base_url($env) . join('/', @uri); +} + +sub flush_hdr ($$$) { + my ($dst, $hdr, $url) = @_; + $$dst .= '<entry><title>'; + $$dst .= utf8_html($hdr->{'s'}); # commit subject + $$dst .= '</title><updated>'; + $$dst .= strftime(DATEFMT, gmtime($hdr->{ct})); + $$dst .= '</updated><author><name>'; + $$dst .= utf8_html($hdr->{an}); + $$dst .= '</name><email>'; + $$dst .= utf8_html($hdr->{ae}); + $$dst .= '</email></author><published>'; + $$dst .= strftime(DATEFMT, gmtime($hdr->{at})); + $$dst .= '</published>'; + $$dst .= qq(<link\nrel="alternate"\ntype="text/html"\nhref="); + $$dst .= $url; + $$dst .= '/commit/'; + + my $H = $hdr->{H}; + $$dst .= $H; + $$dst .= qq("\n/><id>); + $$dst .= $H; + $$dst .= qq(</id>); + + $$dst .= qq(<content\ntype="xhtml"><div\nxmlns="); + $$dst .= qq(http://www.w3.org/1999/xhtml">); + $$dst .= qq(<pre\nstyle="white-space:pre-wrap">); + undef +} + +sub git_atom_sed ($$) { + my ($self, $req) = @_; + my $buf = ''; + my $state = 0; + my $rel = $req->{relcmd}; + my $repo = $req->{-repo}; + my $tip = $repo->tip; + my $title = join('/', $repo->{repo}, @{$req->{extra}}); + $title = utf8_html("$title, $tip"); + my $url = repo_root_url($self, $req); + my $hdr = {}; + my $subtitle = $repo->desc_html; + $req->{axml} = qq(<?xml version="1.0"?>\n) . + qq(<feed\nxmlns="http://www.w3.org/2005/Atom">) . + qq(<title>$title</title>) . + qq(<subtitle>$subtitle</subtitle>) . + qq(<link\nrel="alternate"\ntype="text/html"\nhref="$url"\n/>); + my ($plinks, $ai); + my $end = ''; + my $blines; + sub { + my $dst; + # $_[0] == scalar buffer, undef means EOF from "git log" + $dst = delete $req->{axml} || ''; + my @tmp; + if (defined $_[0]) { + $buf .= $_[0]; + @tmp = split(/\n/, $buf, -1); + $buf = @tmp ? pop(@tmp) : ''; + } else { + @tmp = split(/\n/, $buf, -1); + $buf = ''; + $end = '</feed>'; + } + + foreach my $l (@tmp) { + if ($state != STATE_BODY) { + $hdr->{((STATES)[$state])} = $l; + if (++$state == STATE_BODY) { + flush_hdr(\$dst, $hdr, $url); + $hdr = {}; + $blines = 0; + } + next; + } + if ($l eq "\0") { + $dst .= qq(</pre></div></content></entry>); + $state = 0; + } else { + $dst .= "\n" if $blines++; + $dst .= utf8_html($l); + } + } + $dst .= $end; + } +} + +sub git_atom_cb { + my ($self, $req) = @_; + sub { + my ($r) = @_; + my $env = $req->{env}; + if (!defined $r) { + my $git = $req->{-repo}->{git}; + return $self->rt(400, 'plain', $git->err); + } + $env->{'qspawn.filter'} = git_atom_sed($self, $req); + [ 200, [ 'Content-Type', 'application/atom+xml' ] ]; + } +} + +sub call_git_atom { + my ($self, $req) = @_; + my $repo = $req->{-repo}; + my $max = $repo->{max_commit_count} || 10; + $max = int($max); + $max = 50 if $max == 0; + + my $git = $repo->{git}; + my $env = $req->{env}; + my $tip = $req->{tip} || $repo->tip; + my $read_log = sub { + my $cmd = $git->cmd(qw(log --no-notes --no-color --no-abbrev), + $ATOM_FMT, "-$max", $tip, '--'); + my $expath = $req->{expath}; + push @$cmd, $expath if $expath ne ''; + my $rdr = { 2 => $git->err_begin }; + my $qsp = PublicInbox::Qspawn->new($cmd, undef, undef, $rdr); + $qsp->psgi_return($env, undef, git_atom_cb($self, $req)); + }; + + sub { + $env->{'qspawn.response'} = $_[0]; + $read_log->(); + } +} + +1; |