Migrated tree action to new model.
[catagits/Gitalist.git] / lib / gitweb.pm
index 488d18f..d110b76 100755 (executable)
@@ -1,4 +1,4 @@
-co#!/usr/bin/perl
+#!/usr/bin/perl
 
 # gitweb - simple web interface to track changes in git repositories
 #
@@ -20,14 +20,16 @@ use File::Basename qw(basename);
 use FindBin;
 binmode STDOUT, ':utf8';
 
+use Gitalist::Util qw(to_utf8);
+
 BEGIN {
        CGI->compile();
 }
 
 use vars qw(
        $cgi $version $my_url $my_uri $base_url $path_info $GIT $projectroot
-       $project_maxdepth $home_link $home_link_str $site_name $site_header
-       $home_text $site_footer @stylesheets $stylesheet $logo $favicon
+       $project_maxdepth $home_link $home_link_str $site_header
+       $home_text $site_footer @stylesheets
        $logo_url $logo_label $logo_url $logo_label $projects_list
        $projects_list_description_width $default_projects_order
        $export_ok $export_auth_hook $strict_export @git_base_url_list
@@ -79,14 +81,11 @@ sub main {
 
        # core git executable to use
        # this can just be "git" if your webserver has a sensible PATH
-       our $GIT = "/usr/bin/git";
+       our $GIT = `which git`;
+       chomp($GIT);
 
        # absolute fs-path which will be prepended to the project path
-       our $projectroot = "/pub/scm";
-
-       # fs traversing limit for getting project list
-       # the number is relative to the projectroot
-       our $project_maxdepth = 2007;
+       #our $projectroot = "/pub/scm";
 
        # target of the home link on top of all pages
        our $home_link = $my_uri || "/";
@@ -94,11 +93,6 @@ sub main {
        # string of the home link on top of all pages
        our $home_link_str = "Project Gitalist";
 
-       # name of your site or organization to appear in page titles
-       # replace this with something more descriptive for clearer bookmarks
-       our $site_name = ""
-                        || ($ENV{'SERVER_NAME'} || "Untitled") . " Git";
-
        # filename of html text to include at top of each page
        our $site_header = "";
        # html text to include at home page
@@ -108,19 +102,13 @@ sub main {
 
        # URI of stylesheets
        our @stylesheets = ("gitweb.css");
-       # URI of a single stylesheet, which can be overridden in GITWEB_CONFIG.
-       our $stylesheet = undef;
-       # URI of GIT logo (72x27 size)
-       our $logo = "git-logo.png";
-       # URI of GIT favicon, assumed to be image/png type
-       our $favicon = "git-favicon.png";
 
        # URI and label (title) of GIT logo link
        our $logo_url = "http://www.kernel.org/pub/software/scm/git/docs/";
        our $logo_label = "git documentation";
 
        # source of projects list
-       our $projects_list = "";
+       our $projectroot = our $projects_list = $c->config->{projectroot};
 
        # the width (in characters) of the projects list "Description" column
        our $projects_list_description_width = 25;
@@ -436,8 +424,6 @@ sub main {
        # version of the core git binary
        our $git_version = qx("$GIT" --version) =~ m/git version (.*)$/ ? $1 : "unknown";
 
-       $projects_list ||= $projectroot;
-
        # ======================================================================
        # input validation and dispatch
 
@@ -1031,19 +1017,6 @@ sub validate_refname {
        return $input;
 }
 
-# decode sequences of octets in utf8 into Perl's internal form,
-# which is utf-8 with utf8 flag set if needed.  gitweb writes out
-# in utf-8 thanks to "binmode STDOUT, ':utf8'" at beginning
-sub to_utf8 {
-       my $str = shift;
-       if (utf8::valid($str)) {
-               utf8::decode($str);
-               return $str;
-       } else {
-               return decode($fallback_encoding, $str, Encode::FB_DEFAULT);
-       }
-}
-
 # quote unsafe chars, but keep the slash, even when it's not
 # correct, but quoted slashes look too horrible in bookmarks
 sub esc_param {
@@ -2167,106 +2140,6 @@ sub git_get_project_url_list {
        return wantarray ? @git_project_url_list : \@git_project_url_list;
 }
 
-sub git_get_projects_list {
-       my ($filter) = @_;
-       my @list;
-
-       $filter ||= '';
-       $filter =~ s/\.git$//;
-
-       my $check_forks = gitweb_check_feature('forks');
-
-       if (-d $projects_list) {
-               # search in directory
-               my $dir = $projects_list . ($filter ? "/$filter" : '');
-               # remove the trailing "/"
-               $dir =~ s!/+$!!;
-               my $pfxlen = length("$dir");
-               my $pfxdepth = ($dir =~ tr!/!!);
-
-               File::Find::find({
-                       follow_fast => 1, # follow symbolic links
-                       follow_skip => 2, # ignore duplicates
-                       dangling_symlinks => 0, # ignore dangling symlinks, silently
-                       wanted => sub {
-                               # skip project-list toplevel, if we get it.
-                               return if (m!^[/.]$!);
-                               # only directories can be git repositories
-                               return unless (-d $_);
-                               # don't traverse too deep (Find is super slow on os x)
-                               if (($File::Find::name =~ tr!/!!) - $pfxdepth > $project_maxdepth) {
-                                       $File::Find::prune = 1;
-                                       return;
-                               }
-
-                               my $subdir = substr($File::Find::name, $pfxlen + 1);
-                               # we check related file in $projectroot
-                               my $path = ($filter ? "$filter/" : '') . $subdir;
-                               if (check_export_ok("$projectroot/$path")) {
-                                       push @list, { path => $path };
-                                       $File::Find::prune = 1;
-                               }
-                       },
-               }, "$dir");
-
-       } elsif (-f $projects_list) {
-               # read from file(url-encoded):
-               # 'git%2Fgit.git Linus+Torvalds'
-               # 'libs%2Fklibc%2Fklibc.git H.+Peter+Anvin'
-               # 'linux%2Fhotplug%2Fudev.git Greg+Kroah-Hartman'
-               my %paths;
-               open my ($fd), $projects_list or return;
-       PROJECT:
-               while (my $line = <$fd>) {
-                       chomp $line;
-                       my ($path, $owner) = split ' ', $line;
-                       $path = unescape($path);
-                       $owner = unescape($owner);
-                       if (!defined $path) {
-                               next;
-                       }
-                       if ($filter ne '') {
-                               # looking for forks;
-                               my $pfx = substr($path, 0, length($filter));
-                               if ($pfx ne $filter) {
-                                       next PROJECT;
-                               }
-                               my $sfx = substr($path, length($filter));
-                               if ($sfx !~ /^\/.*\.git$/) {
-                                       next PROJECT;
-                               }
-                       } elsif ($check_forks) {
-                       PATH:
-                               foreach my $filter (keys %paths) {
-                                       # looking for forks;
-                                       my $pfx = substr($path, 0, length($filter));
-                                       if ($pfx ne $filter) {
-                                               next PATH;
-                                       }
-                                       my $sfx = substr($path, length($filter));
-                                       if ($sfx !~ /^\/.*\.git$/) {
-                                               next PATH;
-                                       }
-                                       # is a fork, don't include it in
-                                       # the list
-                                       next PROJECT;
-                               }
-                       }
-                       if (check_export_ok("$projectroot/$path")) {
-                               my $pr = {
-                                       path => $path,
-                                       owner => to_utf8($owner),
-                               };
-                               push @list, $pr;
-                               (my $forks_path = $path) =~ s/\.git$//;
-                               $paths{$forks_path}++;
-                       }
-               }
-               close $fd;
-       }
-       return @list;
-}
-
 our $gitweb_project_owner = undef;
 sub git_get_project_list_from_file {
 
@@ -2903,182 +2776,6 @@ sub blob_contenttype {
        return $type;
 }
 
-## ======================================================================
-## functions printing HTML: header, footer, error page
-
-sub git_header_html {
-       # XXX These aren't used, how odd.
-       my $status = shift || "200 OK";
-       my $expires = shift;
-
-       my $title = "$site_name";
-       if (defined $project) {
-               $title .= " - " . to_utf8($project);
-               if (defined $action) {
-                       $title .= "/$action";
-                       if (defined $file_name) {
-                               $title .= " - " . esc_path($file_name);
-                               if ($action eq "tree" && $file_name !~ m|/$|) {
-                                       $title .= "/";
-                               }
-                       }
-               }
-       }
-
-       $c->stash->{version} = $version;
-       $c->stash->{git_version} = $git_version;
-       $c->stash->{title} = $title;
-
-       # the stylesheet, favicon etc urls won't work correctly with path_info
-       # unless we set the appropriate base URL
-       $c->stash->{baseurl} = $ENV{PATH_INFO}
-                                                ? q[<base href="].esc_url($base_url).q[" />]
-                                                : '';
-
-       # print out each stylesheet that exist, providing backwards capability
-       # for those people who defined $stylesheet in a config file
-       my $ssfmt = q[<link rel="stylesheet" type="text/css" href="%s"/>];
-       $c->stash->{stylesheets} = [defined $stylesheet
-               ? sprintf($ssfmt, $stylesheet)
-               : map(sprintf($ssfmt, $_), grep $_, @stylesheets)
-       ];
-
-       $c->stash->{project} = defined $project;
-       if (defined $project) {
-               my %href_params = get_feed_info();
-               if (!exists $href_params{'-title'}) {
-                       $href_params{'-title'} = 'log';
-               }
-
-               foreach my $format qw(RSS Atom) {
-                       my $type = lc($format);
-                       my %link_attr = (
-                               '-rel' => 'alternate',
-                               '-title' => "$project - $href_params{'-title'} - $format feed",
-                               '-type' => "application/$type+xml"
-                       );
-
-                       $href_params{'action'} = $type;
-                       $link_attr{'-href'} = href(%href_params);
-                       $c->stash->{lc $format.'_link'} = "<link ".
-                             "rel=\"$link_attr{'-rel'}\" ".
-                             "title=\"$link_attr{'-title'}\" ".
-                             "href=\"$link_attr{'-href'}\" ".
-                             "type=\"$link_attr{'-type'}\" ".
-                             "/>\n";
-
-                       $href_params{'extra_options'} = '--no-merges';
-                       $link_attr{'-href'} = href(%href_params);
-                       $link_attr{'-title'} .= ' (no merges)';
-                       $c->stash->{lc $format.'_link_no_merges'} = "<link ".
-                             "rel=\"$link_attr{'-rel'}\" ".
-                             "title=\"$link_attr{'-title'}\" ".
-                             "href=\"$link_attr{'-href'}\" ".
-                             "type=\"$link_attr{'-type'}\" ".
-                             "/>\n";
-               }
-
-       } else {
-               $c->stash->{projects_list} = sprintf('<link rel="alternate" title="%s projects list" '.
-                      'href="%s" type="text/plain; charset=utf-8" />'."\n",
-                      $site_name, href(project=>undef, action=>"project_index"));
-               $c->stash->{projects_feed} = sprintf('<link rel="alternate" title="%s projects feeds" '.
-                      'href="%s" type="text/x-opml" />'."\n",
-                      $site_name, href(project=>undef, action=>"opml"));
-       }
-
-       $c->stash->{favicon} = defined $favicon
-               ? qq(<link rel="shortcut icon" href="$favicon" type="image/png" />)
-               : '';
-
-       # </head><body>
-
-       $c->stash->{site_header} = -f $site_header
-               ? insert_file($site_header)
-               : '';
-
-       $c->stash->{logo}
-               = $cgi->a({-href => esc_url($logo_url),
-                                  -title => $logo_label},
-                                  qq(<img src="$logo" width="72" height="27" alt="git" class="logo"/>));
-       $c->stash->{home_link} =  $cgi->a({-href => esc_url($home_link)}, $home_link_str);
-
-       if(defined $project) {
-               $c->stash->{summary} = $cgi->a({-href => href(action=>"summary")}, esc_html($project));
-               $c->stash->{action}  = $action;
-       }
-
-       my $have_search = $c->stash->{have_search} = gitweb_check_feature('search');
-       if (defined $project && $have_search) {
-               if (!defined $searchtext) {
-                       $searchtext = "";
-               }
-               my $search_hash;
-               if (defined $hash_base) {
-                       $search_hash = $hash_base;
-               } elsif (defined $hash) {
-                       $search_hash = $hash;
-               } else {
-                       $search_hash = "HEAD";
-               }
-               my $action = $my_uri;
-               my $use_pathinfo = gitweb_check_feature('pathinfo');
-               if ($use_pathinfo) {
-                       $action .= "/".esc_url($project);
-               }
-               # This could be done better, but meh.
-               $c->stash->{search_form} = $cgi->startform(-method => "get", -action => $action) .
-                     (!$use_pathinfo &&
-                     $cgi->input({-name=>"p", -value=>$project, -type=>"hidden"}) . "\n") .
-                     $cgi->input({-name=>"a", -value=>"search", -type=>"hidden"}) . "\n" .
-                     $cgi->input({-name=>"h", -value=>$search_hash, -type=>"hidden"}) . "\n" .
-                     $cgi->popup_menu(-name => 'st', -default => 'commit',
-                                      -values => ['commit', 'grep', 'author', 'committer', 'pickaxe']) .
-                     $cgi->sup($cgi->a({-href => href(action=>"search_help")}, "?")) .
-                     " search:\n".
-                     $cgi->textfield(-name => "s", -value => $searchtext) . "\n" .
-                     "<span title=\"Extended regular expression\">" .
-                     $cgi->checkbox(-name => 'sr', -value => 1, -label => 're',
-                                    -checked => $search_use_regexp) .
-                     "</span>" .
-                     $cgi->end_form() . "\n";
-       }
-}
-
-sub git_footer_html {
-       my $feed_class = 'rss_logo';
-
-       if (defined $project) {
-               my $descr = git_get_project_description($project);
-               $c->stash->{project_description} = defined $descr
-                       ? esc_html($descr)
-                       : '';
-
-               my %href_params = get_feed_info();
-               if (!%href_params) {
-                       $feed_class .= ' generic';
-               }
-               $href_params{'-title'} ||= 'log';
-
-               foreach my $format qw(RSS Atom) {
-                       $href_params{'action'} = lc($format);
-                       $c->stash->{lc $format.'_feed'} = $cgi->a({-href => href(%href_params),
-                                     -title => "$href_params{'-title'} $format feed",
-                                     -class => $feed_class}, $format);
-               }
-
-       } else {
-               $c->stash->{opml_feed} = $cgi->a({-href => href(project=>undef, action=>"opml"),
-                             -class => $feed_class}, "OPML");
-               $c->stash->{index_feed} = $cgi->a({-href => href(project=>undef, action=>"project_index"),
-                             -class => $feed_class}, "TXT");
-       }
-
-       $c->stash->{site_footer} = -f $site_footer
-               ? insert_file($site_footer)
-               : '';
-}
-
 # die_error(<http_status_code>, <error_message>)
 # Example: die_error(404, 'Hash not found')
 # By convention, use the following status codes (as defined in RFC 2616):
@@ -3107,7 +2804,7 @@ sub die_error {
        <br />
        </div>
 EOF
-       die bless {};
+       die bless { $status => $http_responses{$status}, err => $error };
 }
 
 ## ----------------------------------------------------------------------
@@ -6053,7 +5750,7 @@ sub git_feed {
        return if ($cgi->request_method() eq 'HEAD');
 
        # header variables
-       my $title = "$site_name - $project/$action";
+       my $title = $c->config->{sitename} . " - $project/$action";
        my $feed_type = 'log';
        if (defined $hash) {
                $title .= " - '$hash'";
@@ -6099,10 +5796,10 @@ XML
                      "<language>en</language>\n" .
                      # project owner is responsible for 'editorial' content
                      "<managingEditor>$owner</managingEditor>\n";
-               if (defined $logo || defined $favicon) {
+               if ($c->config->{logo} || $c->config->{favicon}) {
                        # prefer the logo to the favicon, since RSS
                        # doesn't allow both
-                       my $img = esc_url($logo || $favicon);
+                       my $img = esc_url($c->config->{logo} || $c->config->{favicon});
                        print "<image>\n" .
                              "<url>$img</url>\n" .
                              "<title>$title</title>\n" .
@@ -6127,12 +5824,12 @@ XML
                      "<id>" . href(-full=>1) . "</id>\n" .
                      # use project owner for feed author
                      "<author><name>$owner</name></author>\n";
-               if (defined $favicon) {
-                       print "<icon>" . esc_url($favicon) . "</icon>\n";
+               if ($c->config->{favicon}) {
+                       print "<icon>" . esc_url($c->config->{favicon}) . "</icon>\n";
                }
                if (defined $logo_url) {
                        # not twice as wide as tall: 72 x 27 pixels
-                       print "<logo>" . esc_url($logo) . "</logo>\n";
+                       print "<logo>" . esc_url($c->config->{logo}) . "</logo>\n";
                }
                if (! %latest_date) {
                        # dummy date to keep the feed valid until commits trickle in:
@@ -6267,11 +5964,12 @@ sub git_opml {
                -charset => 'utf-8',
                -content_disposition => 'inline; filename="opml.xml"');
 
+       my $sitename = $c->config->{sitename};
        print <<XML;
 <?xml version="1.0" encoding="utf-8"?>
 <opml version="1.0">
 <head>
-  <title>$site_name OPML Export</title>
+  <title>$sitename OPML Export</title>
 </head>
 <body>
 <outline text="git RSS feeds">