Skip to content

Commit

Permalink
gitweb: Support for tag clouds
Browse files Browse the repository at this point in the history
The "Content tags" (nothing to do with usual Git tags!) are free-form
strings that are attached to random projects and displayed in the
well-known Web2.0-ish tag cloud above project list.

The feature will make use of HTML::TagCloud if available, but will
still display (less pretty) list of tags in case the module is not
installed.

The tagging itself is not done by gitweb - user-provided external
helper CGI needs to be provided; one example is the tagproj.cgi
of Girocco. This functionality might get integrated to gitweb
in the future.

The tags are stored one-per-file in ctags/ subdirectory. The reason
they are not stored in the project config file is that you usually
want to give anyone (even CGI scripts) permission to create new tags
and they are non-essential information, and thus you would make
the ctags/ subdirectory world-writable.

Signed-off-by: Petr Baudis <petr.baudis@novartis.com>
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
  • Loading branch information
Petr Baudis authored and Shawn O. Pearce committed Oct 3, 2008
1 parent 52e8370 commit aed93de
Showing 1 changed file with 109 additions and 0 deletions.
109 changes: 109 additions & 0 deletions gitweb/gitweb.perl
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,24 @@ BEGIN
'forks' => {
'override' => 0,
'default' => [0]},

# 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 {
Expand Down Expand Up @@ -1762,6 +1780,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/ /&nbsp;/g;
$title =~ s/^/&nbsp;/g;
$title =~ s/$/&nbsp;/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;

Expand Down Expand Up @@ -3580,6 +3659,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'});
Expand All @@ -3606,6 +3686,7 @@ sub fill_project_list_info {
$pr->{'forks'} = 0;
}
}
$show_ctags and $pr->{'ctags'} = git_get_project_ctags($pr->{'path'});
push @projects, $pr;
}

Expand Down Expand Up @@ -3652,6 +3733,18 @@ sub git_project_list_body {
$from = 0 unless defined $from;
$to = $#projects if (!defined $to || $#projects < $to);

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";
Expand All @@ -3670,8 +3763,10 @@ sub git_project_list_body {
"</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'}};
if ($alternate) {
print "<tr class=\"dark\">\n";
} else {
Expand Down Expand Up @@ -4093,6 +4188,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") {
Expand Down

0 comments on commit aed93de

Please sign in to comment.