Skip to content

Commit 01ed107

Browse files
committed
Merge branch 'pb/gitweb'
* pb/gitweb: gitweb: Support for simple project search form gitweb: Make the by_tag filter delve in forks as well gitweb: Support for tag clouds gitweb: Add support for extending the action bar with custom links gitweb: Sort the list of forks on the summary page by age gitweb: Clean-up sorting of project list
2 parents 52a7311 + 3e3d4ee commit 01ed107

File tree

2 files changed

+187
-31
lines changed

2 files changed

+187
-31
lines changed

gitweb/gitweb.css

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -435,6 +435,10 @@ div.search {
435435
right: 12px
436436
}
437437

438+
p.projsearch {
439+
text-align: center;
440+
}
441+
438442
td.linenr {
439443
text-align: right;
440444
}

gitweb/gitweb.perl

Lines changed: 183 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,44 @@ BEGIN
282282
'forks' => {
283283
'override' => 0,
284284
'default' => [0]},
285+
286+
# Insert custom links to the action bar of all project pages.
287+
# This enables you mainly to link to third-party scripts integrating
288+
# into gitweb; e.g. git-browser for graphical history representation
289+
# or custom web-based repository administration interface.
290+
291+
# The 'default' value consists of a list of triplets in the form
292+
# (label, link, position) where position is the label after which
293+
# to inster the link and link is a format string where %n expands
294+
# to the project name, %f to the project path within the filesystem,
295+
# %h to the current hash (h gitweb parameter) and %b to the current
296+
# hash base (hb gitweb parameter).
297+
298+
# To enable system wide have in $GITWEB_CONFIG e.g.
299+
# $feature{'actions'}{'default'} = [('graphiclog',
300+
# '/git-browser/by-commit.html?r=%n', 'summary')];
301+
# Project specific override is not supported.
302+
'actions' => {
303+
'override' => 0,
304+
'default' => []},
305+
306+
# Allow gitweb scan project content tags described in ctags/
307+
# of project repository, and display the popular Web 2.0-ish
308+
# "tag cloud" near the project list. Note that this is something
309+
# COMPLETELY different from the normal Git tags.
310+
311+
# gitweb by itself can show existing tags, but it does not handle
312+
# tagging itself; you need an external application for that.
313+
# For an example script, check Girocco's cgi/tagproj.cgi.
314+
# You may want to install the HTML::TagCloud Perl module to get
315+
# a pretty tag cloud instead of just a list of tags.
316+
317+
# To enable system wide have in $GITWEB_CONFIG
318+
# $feature{'ctags'}{'default'} = ['path_to_tag_script'];
319+
# Project specific override is not supported.
320+
'ctags' => {
321+
'override' => 0,
322+
'default' => [0]},
285323
);
286324

287325
sub gitweb_check_feature {
@@ -1762,6 +1800,67 @@ sub git_get_project_description {
17621800
return $descr;
17631801
}
17641802

1803+
sub git_get_project_ctags {
1804+
my $path = shift;
1805+
my $ctags = {};
1806+
1807+
$git_dir = "$projectroot/$path";
1808+
foreach (<$git_dir/ctags/*>) {
1809+
open CT, $_ or next;
1810+
my $val = <CT>;
1811+
chomp $val;
1812+
close CT;
1813+
my $ctag = $_; $ctag =~ s#.*/##;
1814+
$ctags->{$ctag} = $val;
1815+
}
1816+
$ctags;
1817+
}
1818+
1819+
sub git_populate_project_tagcloud {
1820+
my $ctags = shift;
1821+
1822+
# First, merge different-cased tags; tags vote on casing
1823+
my %ctags_lc;
1824+
foreach (keys %$ctags) {
1825+
$ctags_lc{lc $_}->{count} += $ctags->{$_};
1826+
if (not $ctags_lc{lc $_}->{topcount}
1827+
or $ctags_lc{lc $_}->{topcount} < $ctags->{$_}) {
1828+
$ctags_lc{lc $_}->{topcount} = $ctags->{$_};
1829+
$ctags_lc{lc $_}->{topname} = $_;
1830+
}
1831+
}
1832+
1833+
my $cloud;
1834+
if (eval { require HTML::TagCloud; 1; }) {
1835+
$cloud = HTML::TagCloud->new;
1836+
foreach (sort keys %ctags_lc) {
1837+
# Pad the title with spaces so that the cloud looks
1838+
# less crammed.
1839+
my $title = $ctags_lc{$_}->{topname};
1840+
$title =~ s/ /&nbsp;/g;
1841+
$title =~ s/^/&nbsp;/g;
1842+
$title =~ s/$/&nbsp;/g;
1843+
$cloud->add($title, $home_link."?by_tag=".$_, $ctags_lc{$_}->{count});
1844+
}
1845+
} else {
1846+
$cloud = \%ctags_lc;
1847+
}
1848+
$cloud;
1849+
}
1850+
1851+
sub git_show_project_tagcloud {
1852+
my ($cloud, $count) = @_;
1853+
print STDERR ref($cloud)."..\n";
1854+
if (ref $cloud eq 'HTML::TagCloud') {
1855+
return $cloud->html_and_css($count);
1856+
} else {
1857+
my @tags = sort { $cloud->{$a}->{count} <=> $cloud->{$b}->{count} } keys %$cloud;
1858+
return '<p align="center">' . join (', ', map {
1859+
"<a href=\"$home_link?by_tag=$_\">$cloud->{$_}->{topname}</a>"
1860+
} splice(@tags, 0, $count)) . '</p>';
1861+
}
1862+
}
1863+
17651864
sub git_get_project_url_list {
17661865
my $path = shift;
17671866

@@ -1810,9 +1909,7 @@ sub git_get_projects_list {
18101909

18111910
my $subdir = substr($File::Find::name, $pfxlen + 1);
18121911
# we check related file in $projectroot
1813-
if ($check_forks and $subdir =~ m#/.#) {
1814-
$File::Find::prune = 1;
1815-
} elsif (check_export_ok("$projectroot/$filter/$subdir")) {
1912+
if (check_export_ok("$projectroot/$filter/$subdir")) {
18161913
push @list, { path => ($filter ? "$filter/" : '') . $subdir };
18171914
$File::Find::prune = 1;
18181915
}
@@ -2764,13 +2861,26 @@ sub git_print_page_nav {
27642861
}
27652862
}
27662863
}
2864+
27672865
$arg{'tree'}{'hash'} = $treehead if defined $treehead;
27682866
$arg{'tree'}{'hash_base'} = $treebase if defined $treebase;
27692867

2868+
my @actions = gitweb_check_feature('actions');
2869+
while (@actions) {
2870+
my ($label, $link, $pos) = (shift(@actions), shift(@actions), shift(@actions));
2871+
@navs = map { $_ eq $pos ? ($_, $label) : $_ } @navs;
2872+
# munch munch
2873+
$link =~ s#%n#$project#g;
2874+
$link =~ s#%f#$git_dir#g;
2875+
$treehead ? $link =~ s#%h#$treehead#g : $link =~ s#%h##g;
2876+
$treebase ? $link =~ s#%b#$treebase#g : $link =~ s#%b##g;
2877+
$arg{$label}{'_href'} = $link;
2878+
}
2879+
27702880
print "<div class=\"page_nav\">\n" .
27712881
(join " | ",
27722882
map { $_ eq $current ?
2773-
$_ : $cgi->a({-href => href(%{$arg{$_}})}, "$_")
2883+
$_ : $cgi->a({-href => ($arg{$_}{_href} ? $arg{$_}{_href} : href(%{$arg{$_}}))}, "$_")
27742884
} @navs);
27752885
print "<br/>\n$extra<br/>\n" .
27762886
"</div>\n";
@@ -3580,6 +3690,7 @@ sub fill_project_list_info {
35803690
my ($projlist, $check_forks) = @_;
35813691
my @projects;
35823692

3693+
my $show_ctags = gitweb_check_feature('ctags');
35833694
PROJECT:
35843695
foreach my $pr (@$projlist) {
35853696
my (@activity) = git_get_last_activity($pr->{'path'});
@@ -3606,25 +3717,20 @@ sub fill_project_list_info {
36063717
$pr->{'forks'} = 0;
36073718
}
36083719
}
3720+
$show_ctags and $pr->{'ctags'} = git_get_project_ctags($pr->{'path'});
36093721
push @projects, $pr;
36103722
}
36113723

36123724
return @projects;
36133725
}
36143726

3615-
# print 'sort by' <th> element, either sorting by $key if $name eq $order
3616-
# (changing $list), or generating 'sort by $name' replay link otherwise
3727+
# print 'sort by' <th> element, generating 'sort by $name' replay link
3728+
# if that order is not selected
36173729
sub print_sort_th {
3618-
my ($str_sort, $name, $order, $key, $header, $list) = @_;
3619-
$key ||= $name;
3730+
my ($name, $order, $header) = @_;
36203731
$header ||= ucfirst($name);
36213732

36223733
if ($order eq $name) {
3623-
if ($str_sort) {
3624-
@$list = sort {$a->{$key} cmp $b->{$key}} @$list;
3625-
} else {
3626-
@$list = sort {$a->{$key} <=> $b->{$key}} @$list;
3627-
}
36283734
print "<th>$header</th>\n";
36293735
} else {
36303736
print "<th>" .
@@ -3634,15 +3740,8 @@ sub print_sort_th {
36343740
}
36353741
}
36363742

3637-
sub print_sort_th_str {
3638-
print_sort_th(1, @_);
3639-
}
3640-
3641-
sub print_sort_th_num {
3642-
print_sort_th(0, @_);
3643-
}
3644-
36453743
sub git_project_list_body {
3744+
# actually uses global variable $project
36463745
my ($projlist, $order, $from, $to, $extra, $no_header) = @_;
36473746

36483747
my ($check_forks) = gitweb_check_feature('forks');
@@ -3652,26 +3751,60 @@ sub git_project_list_body {
36523751
$from = 0 unless defined $from;
36533752
$to = $#projects if (!defined $to || $#projects < $to);
36543753

3754+
my %order_info = (
3755+
project => { key => 'path', type => 'str' },
3756+
descr => { key => 'descr_long', type => 'str' },
3757+
owner => { key => 'owner', type => 'str' },
3758+
age => { key => 'age', type => 'num' }
3759+
);
3760+
my $oi = $order_info{$order};
3761+
if ($oi->{'type'} eq 'str') {
3762+
@projects = sort {$a->{$oi->{'key'}} cmp $b->{$oi->{'key'}}} @projects;
3763+
} else {
3764+
@projects = sort {$a->{$oi->{'key'}} <=> $b->{$oi->{'key'}}} @projects;
3765+
}
3766+
3767+
my $show_ctags = gitweb_check_feature('ctags');
3768+
if ($show_ctags) {
3769+
my %ctags;
3770+
foreach my $p (@projects) {
3771+
foreach my $ct (keys %{$p->{'ctags'}}) {
3772+
$ctags{$ct} += $p->{'ctags'}->{$ct};
3773+
}
3774+
}
3775+
my $cloud = git_populate_project_tagcloud(\%ctags);
3776+
print git_show_project_tagcloud($cloud, 64);
3777+
}
3778+
36553779
print "<table class=\"project_list\">\n";
36563780
unless ($no_header) {
36573781
print "<tr>\n";
36583782
if ($check_forks) {
36593783
print "<th></th>\n";
36603784
}
3661-
print_sort_th_str('project', $order, 'path',
3662-
'Project', \@projects);
3663-
print_sort_th_str('descr', $order, 'descr_long',
3664-
'Description', \@projects);
3665-
print_sort_th_str('owner', $order, 'owner',
3666-
'Owner', \@projects);
3667-
print_sort_th_num('age', $order, 'age',
3668-
'Last Change', \@projects);
3785+
print_sort_th('project', $order, 'Project');
3786+
print_sort_th('descr', $order, 'Description');
3787+
print_sort_th('owner', $order, 'Owner');
3788+
print_sort_th('age', $order, 'Last Change');
36693789
print "<th></th>\n" . # for links
36703790
"</tr>\n";
36713791
}
36723792
my $alternate = 1;
3793+
my $tagfilter = $cgi->param('by_tag');
36733794
for (my $i = $from; $i <= $to; $i++) {
36743795
my $pr = $projects[$i];
3796+
3797+
next if $tagfilter and $show_ctags and not grep { lc $_ eq lc $tagfilter } keys %{$pr->{'ctags'}};
3798+
next if $searchtext and not $pr->{'path'} =~ /$searchtext/
3799+
and not $pr->{'descr_long'} =~ /$searchtext/;
3800+
# Weed out forks or non-matching entries of search
3801+
if ($check_forks) {
3802+
my $forkbase = $project; $forkbase ||= ''; $forkbase =~ s#\.git$#/#;
3803+
$forkbase="^$forkbase" if $forkbase;
3804+
next if not $searchtext and not $tagfilter and $show_ctags
3805+
and $pr->{'path'} =~ m#$forkbase.*/.*#; # regexp-safe
3806+
}
3807+
36753808
if ($alternate) {
36763809
print "<tr class=\"dark\">\n";
36773810
} else {
@@ -4006,6 +4139,11 @@ sub git_project_list {
40064139
close $fd;
40074140
print "</div>\n";
40084141
}
4142+
print $cgi->startform(-method => "get") .
4143+
"<p class=\"projsearch\">Search:\n" .
4144+
$cgi->textfield(-name => "s", -value => $searchtext) . "\n" .
4145+
"</p>" .
4146+
$cgi->end_form() . "\n";
40094147
git_project_list_body(\@list, $order);
40104148
git_footer_html();
40114149
}
@@ -4093,6 +4231,20 @@ sub git_summary {
40934231
print "<tr class=\"metadata_url\"><td>$url_tag</td><td>$git_url</td></tr>\n";
40944232
$url_tag = "";
40954233
}
4234+
4235+
# Tag cloud
4236+
my $show_ctags = (gitweb_check_feature('ctags'))[0];
4237+
if ($show_ctags) {
4238+
my $ctags = git_get_project_ctags($project);
4239+
my $cloud = git_populate_project_tagcloud($ctags);
4240+
print "<tr id=\"metadata_ctags\"><td>Content tags:<br />";
4241+
print "</td>\n<td>" unless %$ctags;
4242+
print "<form action=\"$show_ctags\" method=\"post\"><input type=\"hidden\" name=\"p\" value=\"$project\" />Add: <input type=\"text\" name=\"t\" size=\"8\" /></form>";
4243+
print "</td>\n<td>" if %$ctags;
4244+
print git_show_project_tagcloud($cloud, 48);
4245+
print "</td></tr>";
4246+
}
4247+
40964248
print "</table>\n";
40974249

40984250
if (-s "$projectroot/$project/README.html") {
@@ -4131,10 +4283,10 @@ sub git_summary {
41314283

41324284
if (@forklist) {
41334285
git_print_header_div('forks');
4134-
git_project_list_body(\@forklist, undef, 0, 15,
4286+
git_project_list_body(\@forklist, 'age', 0, 15,
41354287
$#forklist <= 15 ? undef :
41364288
$cgi->a({-href => href(action=>"forks")}, "..."),
4137-
'noheader');
4289+
'no_header');
41384290
}
41394291

41404292
git_footer_html();

0 commit comments

Comments
 (0)