@@ -425,6 +425,7 @@ sub evaluate_path_info {
425425 " history" => \&git_history,
426426 " log" => \&git_log,
427427 " rss" => \&git_rss,
428+ " atom" => \&git_atom,
428429 " search" => \&git_search,
429430 " search_help" => \&git_search_help,
430431 " shortlog" => \&git_shortlog,
@@ -1198,20 +1199,22 @@ sub parse_date {
11981199 $date {' mday' } = $mday ;
11991200 $date {' day' } = $days [$wday ];
12001201 $date {' month' } = $months [$mon ];
1201- $date {' rfc2822' } = sprintf " %s , %d %s %4d %02d:%02d:%02d +0000" ,
1202- $days [$wday ], $mday , $months [$mon ], 1900+$year , $hour ,$min , $sec ;
1202+ $date {' rfc2822' } = sprintf " %s , %d %s %4d %02d:%02d:%02d +0000" ,
1203+ $days [$wday ], $mday , $months [$mon ], 1900+$year , $hour ,$min , $sec ;
12031204 $date {' mday-time' } = sprintf " %d %s %02d:%02d" ,
12041205 $mday , $months [$mon ], $hour ,$min ;
1206+ $date {' iso-8601' } = sprintf " %04d-%02d-%02dT%02d:%02d:%02dZ" ,
1207+ 1900+$year , $mon , $mday , $hour ,$min , $sec ;
12051208
12061209 $tz =~ m / ^([+\- ][0-9][0-9])([0-9][0-9])$ / ;
12071210 my $local = $epoch + ((int $1 + ($2 /60)) * 3600);
12081211 ($sec , $min , $hour , $mday , $mon , $year , $wday , $yday ) = gmtime ($local );
12091212 $date {' hour_local' } = $hour ;
12101213 $date {' minute_local' } = $min ;
12111214 $date {' tz_local' } = $tz ;
1212- $date {' iso-tz' } = sprintf (" %04d-%02d-%02d %02d:%02d:%02d %s " ,
1213- 1900+$year , $mon +1, $mday ,
1214- $hour , $min , $sec , $tz );
1215+ $date {' iso-tz' } = sprintf (" %04d-%02d-%02d %02d:%02d:%02d %s " ,
1216+ 1900+$year , $mon +1, $mday ,
1217+ $hour , $min , $sec , $tz );
12151218 return %date ;
12161219}
12171220
@@ -1672,14 +1675,17 @@ sub git_header_html {
16721675 }
16731676 }
16741677 if (defined $project ) {
1675- printf (' <link rel="alternate" title="%s log" ' .
1676- ' href="%s" type="application/rss+xml"/>' ." \n " ,
1678+ printf (' <link rel="alternate" title="%s log RSS feed " ' .
1679+ ' href="%s" type="application/rss+xml" />' ." \n " ,
16771680 esc_param($project ), href(action => " rss" ));
1681+ printf (' <link rel="alternate" title="%s log Atom feed" ' .
1682+ ' href="%s" type="application/atom+xml" />' ." \n " ,
1683+ esc_param($project ), href(action => " atom" ));
16781684 } else {
16791685 printf (' <link rel="alternate" title="%s projects list" ' .
16801686 ' href="%s" type="text/plain; charset=utf-8"/>' ." \n " ,
16811687 $site_name , href(project => undef , action => " project_index" ));
1682- printf (' <link rel="alternate" title="%s projects logs " ' .
1688+ printf (' <link rel="alternate" title="%s projects feeds " ' .
16831689 ' href="%s" type="text/x-opml"/>' ." \n " ,
16841690 $site_name , href(project => undef , action => " opml" ));
16851691 }
@@ -1745,7 +1751,9 @@ sub git_footer_html {
17451751 print " <div class=\" page_footer_text\" >" . esc_html($descr ) . " </div>\n " ;
17461752 }
17471753 print $cgi -> a({-href => href(action => " rss" ),
1748- -class => " rss_logo" }, " RSS" ) . " \n " ;
1754+ -class => " rss_logo" }, " RSS" ) . " " ;
1755+ print $cgi -> a({-href => href(action => " atom" ),
1756+ -class => " rss_logo" }, " Atom" ) . " \n " ;
17491757 } else {
17501758 print $cgi -> a({-href => href(project => undef , action => " opml" ),
17511759 -class => " rss_logo" }, " OPML" ) . " " ;
@@ -4150,26 +4158,125 @@ sub git_shortlog {
41504158}
41514159
41524160# # ......................................................................
4153- # # feeds (RSS, OPML)
4161+ # # feeds (RSS, Atom; OPML)
41544162
4155- sub git_rss {
4156- # http://www.notestips.com/80256B3A007F2692/1/NAMO5P9UPQ
4163+ sub git_feed {
4164+ my $format = shift || ' atom' ;
4165+ my ($have_blame ) = gitweb_check_feature(' blame' );
4166+
4167+ # Atom: http://www.atomenabled.org/developers/syndication/
4168+ # RSS: http://www.notestips.com/80256B3A007F2692/1/NAMO5P9UPQ
4169+ if ($format ne ' rss' && $format ne ' atom' ) {
4170+ die_error(undef , " Unknown web feed format" );
4171+ }
4172+
4173+ # log/feed of current (HEAD) branch, log of given branch, history of file/directory
4174+ my $head = $hash || ' HEAD' ;
41574175 open my $fd , " -|" , git_cmd(), " rev-list" , " --max-count=150" ,
4158- git_get_head_hash( $project ) , " --"
4176+ $head , " --" , ( defined $file_name ? $file_name : ())
41594177 or die_error(undef , " Open git-rev-list failed" );
41604178 my @revlist = map { chomp ; $_ } <$fd >;
41614179 close $fd or die_error(undef , " Reading git-rev-list failed" );
4162- print $cgi -> header(-type => ' text/xml' , -charset => ' utf-8' );
4163- print <<XML ;
4164- <?xml version =" 1.0" encoding =" utf-8" ?>
4180+
4181+ my %latest_commit ;
4182+ my %latest_date ;
4183+ my $content_type = " application/$format +xml" ;
4184+ if (defined $cgi -> http(' HTTP_ACCEPT' ) &&
4185+ $cgi -> Accept(' text/xml' ) > $cgi -> Accept($content_type )) {
4186+ # browser (feed reader) prefers text/xml
4187+ $content_type = ' text/xml' ;
4188+ }
4189+ if (defined ($revlist [0])) {
4190+ %latest_commit = parse_commit($revlist [0]);
4191+ %latest_date = parse_date($latest_commit {' committer_epoch' });
4192+ print $cgi -> header(
4193+ -type => $content_type ,
4194+ -charset => ' utf-8' ,
4195+ -last_modified => $latest_date {' rfc2822' });
4196+ } else {
4197+ print $cgi -> header(
4198+ -type => $content_type ,
4199+ -charset => ' utf-8' );
4200+ }
4201+
4202+ # Optimization: skip generating the body if client asks only
4203+ # for Last-Modified date.
4204+ return if ($cgi -> request_method() eq ' HEAD' );
4205+
4206+ # header variables
4207+ my $title = " $site_name - $project /$action " ;
4208+ my $feed_type = ' log' ;
4209+ if (defined $hash ) {
4210+ $title .= " - '$hash '" ;
4211+ $feed_type = ' branch log' ;
4212+ if (defined $file_name ) {
4213+ $title .= " :: $file_name " ;
4214+ $feed_type = ' history' ;
4215+ }
4216+ } elsif (defined $file_name ) {
4217+ $title .= " - $file_name " ;
4218+ $feed_type = ' history' ;
4219+ }
4220+ $title .= " $feed_type " ;
4221+ my $descr = git_get_project_description($project );
4222+ if (defined $descr ) {
4223+ $descr = esc_html($descr );
4224+ } else {
4225+ $descr = " $project " .
4226+ ($format eq ' rss' ? ' RSS' : ' Atom' ) .
4227+ " feed" ;
4228+ }
4229+ my $owner = git_get_project_owner($project );
4230+ $owner = esc_html($owner );
4231+
4232+ # header
4233+ my $alt_url ;
4234+ if (defined $file_name ) {
4235+ $alt_url = href(-full => 1, action => " history" , hash => $hash , file_name => $file_name );
4236+ } elsif (defined $hash ) {
4237+ $alt_url = href(-full => 1, action => " log" , hash => $hash );
4238+ } else {
4239+ $alt_url = href(-full => 1, action => " summary" );
4240+ }
4241+ print qq! <?xml version="1.0" encoding="utf-8"?>\n ! ;
4242+ if ($format eq ' rss' ) {
4243+ print <<XML ;
41654244<rss version =" 2.0" xmlns : content =" http://purl.org/rss/1.0/modules/content/" >
41664245<channel >
4167- <title >$project $my_uri $my_url </title >
4168- <link >${\e sc_html("$my_url ?p=$project ;a=summary")}</link >
4169- <description >$project log</description >
4170- <language >en</language >
41714246XML
4247+ print " <title>$title </title>\n " .
4248+ " <link>$alt_url </link>\n " .
4249+ " <description>$descr </description>\n " .
4250+ " <language>en</language>\n " ;
4251+ } elsif ($format eq ' atom' ) {
4252+ print <<XML ;
4253+ <feed xmlns =" http://www.w3.org/2005/Atom" >
4254+ XML
4255+ print " <title>$title </title>\n " .
4256+ " <subtitle>$descr </subtitle>\n " .
4257+ ' <link rel="alternate" type="text/html" href="' .
4258+ $alt_url . ' " />' . " \n " .
4259+ ' <link rel="self" type="' . $content_type . ' " href="' .
4260+ $cgi -> self_url() . ' " />' . " \n " .
4261+ " <id>" . href(-full => 1) . " </id>\n " .
4262+ # use project owner for feed author
4263+ " <author><name>$owner </name></author>\n " ;
4264+ if (defined $favicon ) {
4265+ print " <icon>" . esc_url($favicon ) . " </icon>\n " ;
4266+ }
4267+ if (defined $logo_url ) {
4268+ # not twice as wide as tall: 72 x 27 pixels
4269+ print " <logo>" . esc_url($logo_url ) . " </logo>\n " ;
4270+ }
4271+ if (! %latest_date ) {
4272+ # dummy date to keep the feed valid until commits trickle in:
4273+ print " <updated>1970-01-01T00:00:00Z</updated>\n " ;
4274+ } else {
4275+ print " <updated>$latest_date {'iso-8601'}</updated>\n " ;
4276+ }
4277+ }
41724278
4279+ # contents
41734280 for (my $i = 0; $i <= $#revlist ; $i ++) {
41744281 my $commit = $revlist [$i ];
41754282 my %co = parse_commit($commit );
@@ -4178,42 +4285,100 @@ sub git_rss {
41784285 last ;
41794286 }
41804287 my %cd = parse_date($co {' committer_epoch' });
4288+
4289+ # get list of changed files
41814290 open $fd , " -|" , git_cmd(), " diff-tree" , ' -r' , @diff_opts ,
4182- $co {' parent' }, $co {' id' }, " --"
4291+ $co {' parent' }, $co {' id' }, " --" , ( defined $file_name ? $file_name : ())
41834292 or next ;
41844293 my @difftree = map { chomp ; $_ } <$fd >;
41854294 close $fd
41864295 or next ;
4187- print " <item>\n " .
4188- " <title>" .
4189- sprintf (" %d %s %02d:%02d" , $cd {' mday' }, $cd {' month' }, $cd {' hour' }, $cd {' minute' }) . " - " . esc_html($co {' title' }) .
4190- " </title>\n " .
4191- " <author>" . esc_html($co {' author' }) . " </author>\n " .
4192- " <pubDate>$cd {'rfc2822'}</pubDate>\n " .
4193- " <guid isPermaLink=\" true\" >" . esc_html(" $my_url ?p=$project ;a=commit;h=$commit " ) . " </guid>\n " .
4194- " <link>" . esc_html(" $my_url ?p=$project ;a=commit;h=$commit " ) . " </link>\n " .
4195- " <description>" . esc_html($co {' title' }) . " </description>\n " .
4196- " <content:encoded>" .
4197- " <![CDATA[\n " ;
4296+
4297+ # print element (entry, item)
4298+ my $co_url = href(-full => 1, action => " commit" , hash => $commit );
4299+ if ($format eq ' rss' ) {
4300+ print " <item>\n " .
4301+ " <title>" . esc_html($co {' title' }) . " </title>\n " .
4302+ " <author>" . esc_html($co {' author' }) . " </author>\n " .
4303+ " <pubDate>$cd {'rfc2822'}</pubDate>\n " .
4304+ " <guid isPermaLink=\" true\" >$co_url </guid>\n " .
4305+ " <link>$co_url </link>\n " .
4306+ " <description>" . esc_html($co {' title' }) . " </description>\n " .
4307+ " <content:encoded>" .
4308+ " <![CDATA[\n " ;
4309+ } elsif ($format eq ' atom' ) {
4310+ print " <entry>\n " .
4311+ " <title type=\" html\" >" . esc_html($co {' title' }) . " </title>\n " .
4312+ " <updated>$cd {'iso-8601'}</updated>\n " .
4313+ " <author><name>" . esc_html($co {' author_name' }) . " </name></author>\n " .
4314+ # use committer for contributor
4315+ " <contributor><name>" . esc_html($co {' committer_name' }) . " </name></contributor>\n " .
4316+ " <published>$cd {'iso-8601'}</published>\n " .
4317+ " <link rel=\" alternate\" type=\" text/html\" href=\" $co_url \" />\n " .
4318+ " <id>$co_url </id>\n " .
4319+ " <content type=\" xhtml\" xml:base=\" " . esc_url($my_url ) . " \" >\n " .
4320+ " <div xmlns=\" http://www.w3.org/1999/xhtml\" >\n " ;
4321+ }
41984322 my $comment = $co {' comment' };
4323+ print " <pre>\n " ;
41994324 foreach my $line (@$comment ) {
4200- $line = to_utf8 ($line );
4201- print " $line <br/> \n " ;
4325+ $line = esc_html ($line );
4326+ print " $line \n " ;
42024327 }
4203- print " <br/>\n " ;
4204- foreach my $line (@difftree ) {
4205- if (!($line =~ m / ^:([0-7]{6}) ([0-7]{6}) ([0-9a-fA-F]{40}) ([0-9a-fA-F]{40}) (.)([0-9]{0,3})\t (.*)$ / )) {
4206- next ;
4328+ print " </pre><ul>\n " ;
4329+ foreach my $difftree_line (@difftree ) {
4330+ my %difftree = parse_difftree_raw_line($difftree_line );
4331+ next if !$difftree {' from_id' };
4332+
4333+ my $file = $difftree {' file' } || $difftree {' to_file' };
4334+
4335+ print " <li>" .
4336+ " [" .
4337+ $cgi -> a({-href => href(-full => 1, action => " blobdiff" ,
4338+ hash => $difftree {' to_id' }, hash_parent => $difftree {' from_id' },
4339+ hash_base => $co {' id' }, hash_parent_base => $co {' parent' },
4340+ file_name => $file , file_parent => $difftree {' from_file' }),
4341+ -title => " diff" }, ' D' );
4342+ if ($have_blame ) {
4343+ print $cgi -> a({-href => href(-full => 1, action => " blame" ,
4344+ file_name => $file , hash_base => $commit ),
4345+ -title => " blame" }, ' B' );
42074346 }
4208- my $file = esc_path(unquote($7 ));
4209- $file = to_utf8($file );
4210- print " $file <br/>\n " ;
4347+ # if this is not a feed of a file history
4348+ if (!defined $file_name || $file_name ne $file ) {
4349+ print $cgi -> a({-href => href(-full => 1, action => " history" ,
4350+ file_name => $file , hash => $commit ),
4351+ -title => " history" }, ' H' );
4352+ }
4353+ $file = esc_path($file );
4354+ print " ] " .
4355+ " $file </li>\n " ;
4356+ }
4357+ if ($format eq ' rss' ) {
4358+ print " </ul>]]>\n " .
4359+ " </content:encoded>\n " .
4360+ " </item>\n " ;
4361+ } elsif ($format eq ' atom' ) {
4362+ print " </ul>\n </div>\n " .
4363+ " </content>\n " .
4364+ " </entry>\n " ;
42114365 }
4212- print " ]]>\n " .
4213- " </content:encoded>\n " .
4214- " </item>\n " ;
42154366 }
4216- print " </channel></rss>" ;
4367+
4368+ # end of feed
4369+ if ($format eq ' rss' ) {
4370+ print " </channel>\n </rss>\n " ;
4371+ } elsif ($format eq ' atom' ) {
4372+ print " </feed>\n " ;
4373+ }
4374+ }
4375+
4376+ sub git_rss {
4377+ git_feed(' rss' );
4378+ }
4379+
4380+ sub git_atom {
4381+ git_feed(' atom' );
42174382}
42184383
42194384sub git_opml {
0 commit comments