]> Sergey Matveev's repositories - public-inbox.git/blob - lib/PublicInbox/RepoAtom.pm
www_coderepo: implement /$CODE_REPO/atom/ endpoint
[public-inbox.git] / lib / PublicInbox / RepoAtom.pm
1 # Copyright (C) all contributors <meta@public-inbox.org>
2 # License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt>
3 #
4 # git log => Atom feed (cgit-compatible: $REPO/atom/[PATH]?h=$tip
5 package PublicInbox::RepoAtom;
6 use v5.12;
7 use parent qw(PublicInbox::GzipFilter);
8 use POSIX qw(strftime);
9 use URI::Escape qw(uri_escape);
10 use Scalar::Util ();
11 use PublicInbox::Hval qw(ascii_html);
12
13 my $ATOM_FMT = '--pretty=tformat:'.join('%n',
14                                 map { "%$_" } qw(H ct an ae at s b)).'%x00';
15
16 sub log2atom_ok { # parse_hdr for qspawn
17         my ($r, $bref, $ctx) = @_;
18         return [ 404, [], [ "Not Found\n"] ] if $r == 0;
19         bless $ctx, __PACKAGE__;
20         my $h = [ 'Content-Type' => 'application/atom+xml; charset=UTF-8' ];
21         $ctx->{gz} = $ctx->can('gz_or_noop')->($h, $ctx->{env});
22         my $title = ascii_html(delete $ctx->{-feed_title});
23         my $desc = ascii_html($ctx->{git}->description);
24         my $url = ascii_html($ctx->{git}->base_url($ctx->{env}));
25         $ctx->{-base_url} = $url;
26         $ctx->zmore(<<EOM);
27 <?xml version="1.0"?>
28 <feed xmlns="http://www.w3.org/2005/Atom">
29 <title>$title</title><subtitle>$desc</subtitle><link
30 rel="alternate" type="text/html" href="$url"/>
31 EOM
32         [ 200, $h, $ctx ]; # [2] is qspawn.filter
33 }
34
35 # called by GzipFilter->close
36 sub zflush { $_[0]->SUPER::zflush('</feed>') }
37
38 # called by GzipFilter->write or GetlineBody->getline
39 sub translate {
40         my $self = shift;
41         my $rec = $_[0] // return $self->zflush; # getline
42         my @out;
43         my $lbuf = delete($self->{lbuf}) // shift;
44         $lbuf .= shift if @_;
45         while ($lbuf =~ s/\A([^\0]+)\0\n//s) {
46                 my $ent = $1;
47                 utf8::decode($ent);
48                 $ent = ascii_html($ent);
49                 my ($H, $ct, $an, $ae, $at, $s, $bdy) = split(/\n/, $ent, 7);
50                 undef $ent;
51                 $bdy //= '';
52                 $_ = strftime('%Y-%m-%dT%H:%M:%SZ', gmtime($_)) for ($ct, $at);
53
54                 push @out, <<"", $bdy, '</pre></div></content></entry>'
55 <entry><title>$s</title><updated>$ct</updated><author><name>$an</name>
56 <email>$ae</email></author><published>$at</published><link
57 rel="alternate" type="text/html" href="$self->{-base_url}$H/s/"
58 /><id>$H</id><content type="xhtml"><div
59 xmlns="http://www.w3.org/1999/xhtml"><pre style="white-space:pre-wrap">
60
61         }
62         $self->{lbuf} = $lbuf;
63         chomp @out;
64         $self->SUPER::translate(@out);
65 }
66
67 sub srv_atom {
68         my ($ctx, $path) = @_;
69         return if index($path, '//') >= 0 || index($path, '/') == 0;
70         my $max = 50; # TODO configurable
71         my @cmd = ('git', "--git-dir=$ctx->{git}->{git_dir}",
72                         qw(log --no-notes --no-color --no-abbrev),
73                         $ATOM_FMT, "-$max");
74         my $tip = $ctx->{qp}->{h}; # same as cgit
75         $ctx->{-feed_title} = $ctx->{git}->{nick};
76         if (defined($tip)) {
77                 push @cmd, $tip;
78                 $ctx->{-feed_title} .= ", $tip";
79         }
80         # else: let git decide based on HEAD if $tip isn't defined
81         push @cmd, '--';
82         push @cmd, $path if $path ne '';
83         my $qsp = PublicInbox::Qspawn->new(\@cmd);
84         $qsp->psgi_return($ctx->{env}, undef, \&log2atom_ok, $ctx);
85 }
86
87 1;