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
This commit is contained in:
commit
01ed1079f3
@ -435,6 +435,10 @@ div.search {
|
||||
right: 12px
|
||||
}
|
||||
|
||||
p.projsearch {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
td.linenr {
|
||||
text-align: right;
|
||||
}
|
||||
|
@ -282,6 +282,44 @@ our %feature = (
|
||||
'forks' => {
|
||||
'override' => 0,
|
||||
'default' => [0]},
|
||||
|
||||
# Insert custom links to the action bar of all project pages.
|
||||
# This enables you mainly to link to third-party scripts integrating
|
||||
# into gitweb; e.g. git-browser for graphical history representation
|
||||
# or custom web-based repository administration interface.
|
||||
|
||||
# The 'default' value consists of a list of triplets in the form
|
||||
# (label, link, position) where position is the label after which
|
||||
# to inster the link and link is a format string where %n expands
|
||||
# to the project name, %f to the project path within the filesystem,
|
||||
# %h to the current hash (h gitweb parameter) and %b to the current
|
||||
# hash base (hb gitweb parameter).
|
||||
|
||||
# To enable system wide have in $GITWEB_CONFIG e.g.
|
||||
# $feature{'actions'}{'default'} = [('graphiclog',
|
||||
# '/git-browser/by-commit.html?r=%n', 'summary')];
|
||||
# Project specific override is not supported.
|
||||
'actions' => {
|
||||
'override' => 0,
|
||||
'default' => []},
|
||||
|
||||
# Allow gitweb scan project content tags described in ctags/
|
||||
# of project repository, and display the popular Web 2.0-ish
|
||||
# "tag cloud" near the project list. Note that this is something
|
||||
# COMPLETELY different from the normal Git tags.
|
||||
|
||||
# gitweb by itself can show existing tags, but it does not handle
|
||||
# tagging itself; you need an external application for that.
|
||||
# For an example script, check Girocco's cgi/tagproj.cgi.
|
||||
# You may want to install the HTML::TagCloud Perl module to get
|
||||
# a pretty tag cloud instead of just a list of tags.
|
||||
|
||||
# To enable system wide have in $GITWEB_CONFIG
|
||||
# $feature{'ctags'}{'default'} = ['path_to_tag_script'];
|
||||
# Project specific override is not supported.
|
||||
'ctags' => {
|
||||
'override' => 0,
|
||||
'default' => [0]},
|
||||
);
|
||||
|
||||
sub gitweb_check_feature {
|
||||
@ -1762,6 +1800,67 @@ sub git_get_project_description {
|
||||
return $descr;
|
||||
}
|
||||
|
||||
sub git_get_project_ctags {
|
||||
my $path = shift;
|
||||
my $ctags = {};
|
||||
|
||||
$git_dir = "$projectroot/$path";
|
||||
foreach (<$git_dir/ctags/*>) {
|
||||
open CT, $_ or next;
|
||||
my $val = <CT>;
|
||||
chomp $val;
|
||||
close CT;
|
||||
my $ctag = $_; $ctag =~ s#.*/##;
|
||||
$ctags->{$ctag} = $val;
|
||||
}
|
||||
$ctags;
|
||||
}
|
||||
|
||||
sub git_populate_project_tagcloud {
|
||||
my $ctags = shift;
|
||||
|
||||
# First, merge different-cased tags; tags vote on casing
|
||||
my %ctags_lc;
|
||||
foreach (keys %$ctags) {
|
||||
$ctags_lc{lc $_}->{count} += $ctags->{$_};
|
||||
if (not $ctags_lc{lc $_}->{topcount}
|
||||
or $ctags_lc{lc $_}->{topcount} < $ctags->{$_}) {
|
||||
$ctags_lc{lc $_}->{topcount} = $ctags->{$_};
|
||||
$ctags_lc{lc $_}->{topname} = $_;
|
||||
}
|
||||
}
|
||||
|
||||
my $cloud;
|
||||
if (eval { require HTML::TagCloud; 1; }) {
|
||||
$cloud = HTML::TagCloud->new;
|
||||
foreach (sort keys %ctags_lc) {
|
||||
# Pad the title with spaces so that the cloud looks
|
||||
# less crammed.
|
||||
my $title = $ctags_lc{$_}->{topname};
|
||||
$title =~ s/ / /g;
|
||||
$title =~ s/^/ /g;
|
||||
$title =~ s/$/ /g;
|
||||
$cloud->add($title, $home_link."?by_tag=".$_, $ctags_lc{$_}->{count});
|
||||
}
|
||||
} else {
|
||||
$cloud = \%ctags_lc;
|
||||
}
|
||||
$cloud;
|
||||
}
|
||||
|
||||
sub git_show_project_tagcloud {
|
||||
my ($cloud, $count) = @_;
|
||||
print STDERR ref($cloud)."..\n";
|
||||
if (ref $cloud eq 'HTML::TagCloud') {
|
||||
return $cloud->html_and_css($count);
|
||||
} else {
|
||||
my @tags = sort { $cloud->{$a}->{count} <=> $cloud->{$b}->{count} } keys %$cloud;
|
||||
return '<p align="center">' . join (', ', map {
|
||||
"<a href=\"$home_link?by_tag=$_\">$cloud->{$_}->{topname}</a>"
|
||||
} splice(@tags, 0, $count)) . '</p>';
|
||||
}
|
||||
}
|
||||
|
||||
sub git_get_project_url_list {
|
||||
my $path = shift;
|
||||
|
||||
@ -1810,9 +1909,7 @@ sub git_get_projects_list {
|
||||
|
||||
my $subdir = substr($File::Find::name, $pfxlen + 1);
|
||||
# we check related file in $projectroot
|
||||
if ($check_forks and $subdir =~ m#/.#) {
|
||||
$File::Find::prune = 1;
|
||||
} elsif (check_export_ok("$projectroot/$filter/$subdir")) {
|
||||
if (check_export_ok("$projectroot/$filter/$subdir")) {
|
||||
push @list, { path => ($filter ? "$filter/" : '') . $subdir };
|
||||
$File::Find::prune = 1;
|
||||
}
|
||||
@ -2764,13 +2861,26 @@ sub git_print_page_nav {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$arg{'tree'}{'hash'} = $treehead if defined $treehead;
|
||||
$arg{'tree'}{'hash_base'} = $treebase if defined $treebase;
|
||||
|
||||
my @actions = gitweb_check_feature('actions');
|
||||
while (@actions) {
|
||||
my ($label, $link, $pos) = (shift(@actions), shift(@actions), shift(@actions));
|
||||
@navs = map { $_ eq $pos ? ($_, $label) : $_ } @navs;
|
||||
# munch munch
|
||||
$link =~ s#%n#$project#g;
|
||||
$link =~ s#%f#$git_dir#g;
|
||||
$treehead ? $link =~ s#%h#$treehead#g : $link =~ s#%h##g;
|
||||
$treebase ? $link =~ s#%b#$treebase#g : $link =~ s#%b##g;
|
||||
$arg{$label}{'_href'} = $link;
|
||||
}
|
||||
|
||||
print "<div class=\"page_nav\">\n" .
|
||||
(join " | ",
|
||||
map { $_ eq $current ?
|
||||
$_ : $cgi->a({-href => href(%{$arg{$_}})}, "$_")
|
||||
$_ : $cgi->a({-href => ($arg{$_}{_href} ? $arg{$_}{_href} : href(%{$arg{$_}}))}, "$_")
|
||||
} @navs);
|
||||
print "<br/>\n$extra<br/>\n" .
|
||||
"</div>\n";
|
||||
@ -3580,6 +3690,7 @@ sub fill_project_list_info {
|
||||
my ($projlist, $check_forks) = @_;
|
||||
my @projects;
|
||||
|
||||
my $show_ctags = gitweb_check_feature('ctags');
|
||||
PROJECT:
|
||||
foreach my $pr (@$projlist) {
|
||||
my (@activity) = git_get_last_activity($pr->{'path'});
|
||||
@ -3606,25 +3717,20 @@ sub fill_project_list_info {
|
||||
$pr->{'forks'} = 0;
|
||||
}
|
||||
}
|
||||
$show_ctags and $pr->{'ctags'} = git_get_project_ctags($pr->{'path'});
|
||||
push @projects, $pr;
|
||||
}
|
||||
|
||||
return @projects;
|
||||
}
|
||||
|
||||
# print 'sort by' <th> element, either sorting by $key if $name eq $order
|
||||
# (changing $list), or generating 'sort by $name' replay link otherwise
|
||||
# print 'sort by' <th> element, generating 'sort by $name' replay link
|
||||
# if that order is not selected
|
||||
sub print_sort_th {
|
||||
my ($str_sort, $name, $order, $key, $header, $list) = @_;
|
||||
$key ||= $name;
|
||||
my ($name, $order, $header) = @_;
|
||||
$header ||= ucfirst($name);
|
||||
|
||||
if ($order eq $name) {
|
||||
if ($str_sort) {
|
||||
@$list = sort {$a->{$key} cmp $b->{$key}} @$list;
|
||||
} else {
|
||||
@$list = sort {$a->{$key} <=> $b->{$key}} @$list;
|
||||
}
|
||||
print "<th>$header</th>\n";
|
||||
} else {
|
||||
print "<th>" .
|
||||
@ -3634,15 +3740,8 @@ sub print_sort_th {
|
||||
}
|
||||
}
|
||||
|
||||
sub print_sort_th_str {
|
||||
print_sort_th(1, @_);
|
||||
}
|
||||
|
||||
sub print_sort_th_num {
|
||||
print_sort_th(0, @_);
|
||||
}
|
||||
|
||||
sub git_project_list_body {
|
||||
# actually uses global variable $project
|
||||
my ($projlist, $order, $from, $to, $extra, $no_header) = @_;
|
||||
|
||||
my ($check_forks) = gitweb_check_feature('forks');
|
||||
@ -3652,26 +3751,60 @@ sub git_project_list_body {
|
||||
$from = 0 unless defined $from;
|
||||
$to = $#projects if (!defined $to || $#projects < $to);
|
||||
|
||||
my %order_info = (
|
||||
project => { key => 'path', type => 'str' },
|
||||
descr => { key => 'descr_long', type => 'str' },
|
||||
owner => { key => 'owner', type => 'str' },
|
||||
age => { key => 'age', type => 'num' }
|
||||
);
|
||||
my $oi = $order_info{$order};
|
||||
if ($oi->{'type'} eq 'str') {
|
||||
@projects = sort {$a->{$oi->{'key'}} cmp $b->{$oi->{'key'}}} @projects;
|
||||
} else {
|
||||
@projects = sort {$a->{$oi->{'key'}} <=> $b->{$oi->{'key'}}} @projects;
|
||||
}
|
||||
|
||||
my $show_ctags = gitweb_check_feature('ctags');
|
||||
if ($show_ctags) {
|
||||
my %ctags;
|
||||
foreach my $p (@projects) {
|
||||
foreach my $ct (keys %{$p->{'ctags'}}) {
|
||||
$ctags{$ct} += $p->{'ctags'}->{$ct};
|
||||
}
|
||||
}
|
||||
my $cloud = git_populate_project_tagcloud(\%ctags);
|
||||
print git_show_project_tagcloud($cloud, 64);
|
||||
}
|
||||
|
||||
print "<table class=\"project_list\">\n";
|
||||
unless ($no_header) {
|
||||
print "<tr>\n";
|
||||
if ($check_forks) {
|
||||
print "<th></th>\n";
|
||||
}
|
||||
print_sort_th_str('project', $order, 'path',
|
||||
'Project', \@projects);
|
||||
print_sort_th_str('descr', $order, 'descr_long',
|
||||
'Description', \@projects);
|
||||
print_sort_th_str('owner', $order, 'owner',
|
||||
'Owner', \@projects);
|
||||
print_sort_th_num('age', $order, 'age',
|
||||
'Last Change', \@projects);
|
||||
print_sort_th('project', $order, 'Project');
|
||||
print_sort_th('descr', $order, 'Description');
|
||||
print_sort_th('owner', $order, 'Owner');
|
||||
print_sort_th('age', $order, 'Last Change');
|
||||
print "<th></th>\n" . # for links
|
||||
"</tr>\n";
|
||||
}
|
||||
my $alternate = 1;
|
||||
my $tagfilter = $cgi->param('by_tag');
|
||||
for (my $i = $from; $i <= $to; $i++) {
|
||||
my $pr = $projects[$i];
|
||||
|
||||
next if $tagfilter and $show_ctags and not grep { lc $_ eq lc $tagfilter } keys %{$pr->{'ctags'}};
|
||||
next if $searchtext and not $pr->{'path'} =~ /$searchtext/
|
||||
and not $pr->{'descr_long'} =~ /$searchtext/;
|
||||
# Weed out forks or non-matching entries of search
|
||||
if ($check_forks) {
|
||||
my $forkbase = $project; $forkbase ||= ''; $forkbase =~ s#\.git$#/#;
|
||||
$forkbase="^$forkbase" if $forkbase;
|
||||
next if not $searchtext and not $tagfilter and $show_ctags
|
||||
and $pr->{'path'} =~ m#$forkbase.*/.*#; # regexp-safe
|
||||
}
|
||||
|
||||
if ($alternate) {
|
||||
print "<tr class=\"dark\">\n";
|
||||
} else {
|
||||
@ -4006,6 +4139,11 @@ sub git_project_list {
|
||||
close $fd;
|
||||
print "</div>\n";
|
||||
}
|
||||
print $cgi->startform(-method => "get") .
|
||||
"<p class=\"projsearch\">Search:\n" .
|
||||
$cgi->textfield(-name => "s", -value => $searchtext) . "\n" .
|
||||
"</p>" .
|
||||
$cgi->end_form() . "\n";
|
||||
git_project_list_body(\@list, $order);
|
||||
git_footer_html();
|
||||
}
|
||||
@ -4093,6 +4231,20 @@ sub git_summary {
|
||||
print "<tr class=\"metadata_url\"><td>$url_tag</td><td>$git_url</td></tr>\n";
|
||||
$url_tag = "";
|
||||
}
|
||||
|
||||
# Tag cloud
|
||||
my $show_ctags = (gitweb_check_feature('ctags'))[0];
|
||||
if ($show_ctags) {
|
||||
my $ctags = git_get_project_ctags($project);
|
||||
my $cloud = git_populate_project_tagcloud($ctags);
|
||||
print "<tr id=\"metadata_ctags\"><td>Content tags:<br />";
|
||||
print "</td>\n<td>" unless %$ctags;
|
||||
print "<form action=\"$show_ctags\" method=\"post\"><input type=\"hidden\" name=\"p\" value=\"$project\" />Add: <input type=\"text\" name=\"t\" size=\"8\" /></form>";
|
||||
print "</td>\n<td>" if %$ctags;
|
||||
print git_show_project_tagcloud($cloud, 48);
|
||||
print "</td></tr>";
|
||||
}
|
||||
|
||||
print "</table>\n";
|
||||
|
||||
if (-s "$projectroot/$project/README.html") {
|
||||
@ -4131,10 +4283,10 @@ sub git_summary {
|
||||
|
||||
if (@forklist) {
|
||||
git_print_header_div('forks');
|
||||
git_project_list_body(\@forklist, undef, 0, 15,
|
||||
git_project_list_body(\@forklist, 'age', 0, 15,
|
||||
$#forklist <= 15 ? undef :
|
||||
$cgi->a({-href => href(action=>"forks")}, "..."),
|
||||
'noheader');
|
||||
'no_header');
|
||||
}
|
||||
|
||||
git_footer_html();
|
||||
|
Loading…
Reference in New Issue
Block a user