@@ -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
287325sub 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 / / / g ;
1841+ $title =~ s / ^/ / g ;
1842+ $title =~ s / $/ / 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+
17651864sub 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
36173729sub 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-
36453743sub 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