# Re: [PATCH] gitweb: Identify all summary metadata table rows

6 messages from 2008-10-02 to 2008-10-03. Participants: Shawn O. Pearce, Jakub Narebski, Petr Baudis.
Thread: https://gitlist.dev/t/15762

## Petr Baudis, 2008-10-02 14:25

Subject: [PATCH] gitweb: Identify all summary metadata table rows
Message-ID: <1222957505-5150-1-git-send-email-pasky@suse.cz>
URL: https://gitlist.dev/e/1222957505-5150-1-git-send-email-pasky%40suse.cz

```
In the metadata table of the summary page, all rows have their
id (or class in case of URL) set now. This for example lets sites
easily disable fields they do not want to show in their custom
stylesheet (e.g. they are overly technical or irrelevant for the site).

Many of my other patches depend on this, so I would appreciate to hear
as soon as possible if someone has an issue with this patch.

Signed-off-by: Petr Baudis <petr.baudis@novartis.com>

---
 gitweb/gitweb.perl |    8 ++++----
 1 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index da474d0..bd8124a 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -4070,10 +4070,10 @@ sub git_summary {
 
 	print "<div class=\"title\">&nbsp;</div>\n";
 	print "<table class=\"projects_list\">\n" .
-	      "<tr><td>description</td><td>" . esc_html($descr) . "</td></tr>\n" .
-	      "<tr><td>owner</td><td>" . esc_html($owner) . "</td></tr>\n";
+	      "<tr id=\"metadata_desc\"><td>description</td><td>" . esc_html($descr) . "</td></tr>\n" .
+	      "<tr id=\"metadata_owner\"><td>owner</td><td>" . esc_html($owner) . "</td></tr>\n";
 	if (defined $cd{'rfc2822'}) {
-		print "<tr><td>last change</td><td>$cd{'rfc2822'}</td></tr>\n";
+		print "<tr id=\"metadata_lchange\"><td>last change</td><td>$cd{'rfc2822'}</td></tr>\n";
 	}
 
 	# use per project git URL list in $projectroot/$project/cloneurl
@@ -4083,7 +4083,7 @@ sub git_summary {
 	@url_list = map { "$_/$project" } @git_base_url_list unless @url_list;
 	foreach my $git_url (@url_list) {
 		next unless $git_url;
-		print "<tr><td>$url_tag</td><td>$git_url</td></tr>\n";
+		print "<tr class=\"metadata_url\"><td>$url_tag</td><td>$git_url</td></tr>\n";
 		$url_tag = "";
 	}
 	print "</table>\n";
-- 
tg: (c427559..) t/summary/css-metadata (depends on: vanilla/master)

```

## Petr Baudis, 2008-10-02 15:13

Subject: [PATCH] gitweb: Support for tag clouds
Message-ID: <1222960382-11619-1-git-send-email-pasky@suse.cz>
URL: https://gitlist.dev/e/1222960382-11619-1-git-send-email-pasky%40suse.cz
In-Reply-To: <1222957505-5150-1-git-send-email-pasky@suse.cz>

```
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>

---
 gitweb/gitweb.perl |  109 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 109 insertions(+), 0 deletions(-)

diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index bd8124a..5ba094e 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -275,6 +275,24 @@ our %feature = (
 	'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 {
@@ -1755,6 +1773,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;
 
@@ -3573,6 +3652,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'});
@@ -3599,6 +3679,7 @@ sub fill_project_list_info {
 				$pr->{'forks'} = 0;
 			}
 		}
+		$show_ctags and $pr->{'ctags'} = git_get_project_ctags($pr->{'path'});
 		push @projects, $pr;
 	}
 
@@ -3645,6 +3726,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";
@@ -3663,8 +3756,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 {
@@ -4086,6 +4181,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") {
-- 
tg: (c5a1246..) t/tagcloud/tagcloud (depends on: vanilla/master t/summary/css-metadata)

```

## Petr Baudis, 2008-10-02 15:17

Subject: [PATCH] gitweb: Make the by_tag filter delve in forks as well
Message-ID: <1222960621-12044-1-git-send-email-pasky@suse.cz>
URL: https://gitlist.dev/e/1222960621-12044-1-git-send-email-pasky%40suse.cz
In-Reply-To: <1222960382-11619-1-git-send-email-pasky@suse.cz>

```
This requires us to build a full index including forks and then weed
them out only when printing.

Signed-off-by: Petr Baudis <petr.baudis@novartis.com>

---
 gitweb/gitweb.perl |   13 ++++++++++---
 1 files changed, 10 insertions(+), 3 deletions(-)

diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index 02083d5..07fa1e6 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -1880,9 +1880,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;
 				}
@@ -3715,6 +3713,7 @@ sub print_sort_th_num {
 }
 
 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');
@@ -3757,7 +3756,15 @@ sub git_project_list_body {
 	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'}};
+		# Weed out forks
+		if ($check_forks) {
+			my $forkbase = $project; $forkbase ||= ''; $forkbase =~ s#\.git$#/#;
+			$forkbase="^$forkbase" if $forkbase;
+			next if not $tagfilter and $pr->{'path'} =~ m#$forkbase.*/.*#; # regexp-safe
+		}
+
 		if ($alternate) {
 			print "<tr class=\"dark\">\n";
 		} else {
-- 
tg: (f429121..) t/tagcloud/forks (depends on: t/tagcloud/tagcloud)

```

## Shawn O. Pearce, 2008-10-03 00:30

Subject: Re: [PATCH] gitweb: Identify all summary metadata table rows
Message-ID: <20081003003053.GT21310@spearce.org>
URL: https://gitlist.dev/e/20081003003053.GT21310%40spearce.org
In-Reply-To: <1222957505-5150-1-git-send-email-pasky@suse.cz>

```
Petr Baudis <pasky@suse.cz> wrote:
> In the metadata table of the summary page, all rows have their
> id (or class in case of URL) set now. This for example lets sites
> easily disable fields they do not want to show in their custom
> stylesheet (e.g. they are overly technical or irrelevant for the site).
> 
> Many of my other patches depend on this, so I would appreciate to hear
> as soon as possible if someone has an issue with this patch.

Its pretty trivial and painless.  So its applied.  Marking up unique
elements so you can control them via CSS isn't rocket science.  ;-)
 

> diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
> index da474d0..bd8124a 100755
> --- a/gitweb/gitweb.perl
> +++ b/gitweb/gitweb.perl
> @@ -4070,10 +4070,10 @@ sub git_summary {
>  
>  	print "<div class=\"title\">&nbsp;</div>\n";
>  	print "<table class=\"projects_list\">\n" .
> -	      "<tr><td>description</td><td>" . esc_html($descr) . "</td></tr>\n" .
> -	      "<tr><td>owner</td><td>" . esc_html($owner) . "</td></tr>\n";
> +	      "<tr id=\"metadata_desc\"><td>description</td><td>" . esc_html($descr) . "</td></tr>\n" .
> +	      "<tr id=\"metadata_owner\"><td>owner</td><td>" . esc_html($owner) . "</td></tr>\n";
>  	if (defined $cd{'rfc2822'}) {
> -		print "<tr><td>last change</td><td>$cd{'rfc2822'}</td></tr>\n";
> +		print "<tr id=\"metadata_lchange\"><td>last change</td><td>$cd{'rfc2822'}</td></tr>\n";
>  	}
>  
>  	# use per project git URL list in $projectroot/$project/cloneurl
> @@ -4083,7 +4083,7 @@ sub git_summary {
>  	@url_list = map { "$_/$project" } @git_base_url_list unless @url_list;
>  	foreach my $git_url (@url_list) {
>  		next unless $git_url;
> -		print "<tr><td>$url_tag</td><td>$git_url</td></tr>\n";
> +		print "<tr class=\"metadata_url\"><td>$url_tag</td><td>$git_url</td></tr>\n";
>  		$url_tag = "";
>  	}
>  	print "</table>\n";

-- 
Shawn.

```

## Jakub Narebski, 2008-10-03 01:04

Subject: Re: [PATCH] gitweb: Identify all summary metadata table rows
Message-ID: <m3hc7uqyix.fsf@localhost.localdomain>
URL: https://gitlist.dev/e/m3hc7uqyix.fsf%40localhost.localdomain
In-Reply-To: <20081003003053.GT21310@spearce.org>

```
"Shawn O. Pearce" <spearce@spearce.org> writes:
> Petr Baudis <pasky@suse.cz> wrote:

> > In the metadata table of the summary page, all rows have their
> > id (or class in case of URL) set now. This for example lets sites
> > easily disable fields they do not want to show in their custom
> > stylesheet (e.g. they are overly technical or irrelevant for the site).
> > 
> > Many of my other patches depend on this, so I would appreciate to hear
> > as soon as possible if someone has an issue with this patch.
> 
> Its pretty trivial and painless.  So its applied.  Marking up unique
> elements so you can control them via CSS isn't rocket science.  ;-)

Somehow original patch didn't appear (yet) on git mailing list;
perhaps it ran afoul vger anti-SPAM filter...
 
> > +	      "<tr id=\"metadata_desc\"><td>description</td><td>" . esc_html($descr) . "</td></tr>\n" .
> > +	      "<tr id=\"metadata_owner\"><td>owner</td><td>" . esc_html($owner) . "</td></tr>\n";
> > +		print "<tr id=\"metadata_lchange\"><td>last change</td><td>$cd{'rfc2822'}</td></tr>\n";
> > +		print "<tr class=\"metadata_url\"><td>$url_tag</td><td>$git_url</td></tr>\n";

I like the idea, and I think this can be seen as beginning to use
either microformats, or RDFa.  You have marked those fragments for CSS
to show or hide; microformats are about marking those fragments for
machine to parse.  Although microformats usually use 'class' (or 'rel'
for links), not 'id' attribute for classifying data...

-- 
Jakub Narebski
Poland
ShadeHawk on #git

```

## Petr Baudis, 2008-10-03 07:29

Subject: [PATCH] gitweb: Support for simple project search form
Message-ID: <1223018985-19169-1-git-send-email-pasky@suse.cz>
URL: https://gitlist.dev/e/1223018985-19169-1-git-send-email-pasky%40suse.cz
In-Reply-To: <1222960621-12044-1-git-send-email-pasky@suse.cz>

```
This is a trivial patch adding support for searching projects by name
and description, making use of the "infrastructure" provided by the
tag cloud generation.

Signed-off-by: Petr Baudis <petr.baudis@novartis.com>

---
 gitweb/gitweb.css  |    4 ++++
 gitweb/gitweb.perl |   12 ++++++++++--
 2 files changed, 14 insertions(+), 2 deletions(-)

diff --git a/gitweb/gitweb.css b/gitweb/gitweb.css
index 07f5b53..a01eac8 100644
--- a/gitweb/gitweb.css
+++ b/gitweb/gitweb.css
@@ -435,6 +435,10 @@ div.search {
 	right: 12px
 }
 
+p.projsearch {
+	text-align: center;
+}
+
 td.linenr {
 	text-align: right;
 }
diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index 07fa1e6..4bc8a12 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -3758,11 +3758,14 @@ sub git_project_list_body {
 		my $pr = $projects[$i];
 
 		next if $tagfilter and $show_ctags and not grep { lc $_ eq lc $tagfilter } keys %{$pr->{'ctags'}};
-		# Weed out forks
+		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 $tagfilter and $pr->{'path'} =~ m#$forkbase.*/.*#; # regexp-safe
+			next if not $searchtext and not $tagfilter and $show_ctags
+				and $pr->{'path'} =~ m#$forkbase.*/.*#; # regexp-safe
 		}
 
 		if ($alternate) {
@@ -4099,6 +4102,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();
 }
-- 
tg: (0d352a0..) t/misc/projsearch (depends on: t/tagcloud/forks)

```
