From: short <> Date: Mon, 20 Oct 2003 08:07:11 +0000 (+0000) Subject: modperl branch collapsed back to MAIN trunk, man! X-Git-Tag: bp_apache2~10 X-Git-Url: http://git.jankratochvil.net/?p=MyWeb.git;a=commitdiff_plain;h=633dc05b0519d86762bd084d872d34f9183237e0 modperl branch collapsed back to MAIN trunk, man! --- diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..841a425 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,26 @@ +# $Id$ +# automake source for the Makefile of project/ subdir +# Copyright (C) 2003 Jan Kratochvil +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; exactly version 2 of June 1991 is required +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +include $(top_srcdir)/Makefile-head.am + +EXTRA_DIST+= \ + Project.pm \ + Web.pm \ + arrow-left.png \ + arrow-right.png \ + arrow-up.png diff --git a/Project.pm b/Project.pm new file mode 100644 index 0000000..3d836d8 --- /dev/null +++ b/Project.pm @@ -0,0 +1,338 @@ +# $Id$ +# Common functions for HTML/XHTML output generation +# Copyright (C) 2003 Jan Kratochvil +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; exactly version 2 of June 1991 is required +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +package My::Project; +require 5.6.0; # at least 'use warnings;' but we need some 5.6.0+ modules anyway +our $VERSION=do { my @r=(q$Revision$=~/\d+/g); sprintf "%d.".("%03d"x$#r),@r; }; +our $CVS_ID=q$Id$; +use strict; +use warnings; + +use My::Web; + +use Exporter; +our @EXPORT=qw(); +our @ISA=qw(My::Web Exporter); + + +sub ENTRIES { return top_dir_disk()."/project/CVS/Entries"; } +sub ENTRIES_LOG { return top_dir_disk()."/project/CVS/Entries.Log"; } + + +sub print_project +{ +my($class,$ListItem,%args)=@_; + + print "

".$W->{"title"}."

\n"; + print $ListItem->{"description"}; + print "
\n"; + print $args{"before_project_data"} || ""; + return if $args{"no_project_data"}; + my @table=( + {"key"=>"summary","text"=>"Summary"}, + {"key"=>"license","text"=>"License","format"=>sub ($) { + my %known=( + "PD"=>"Public Domain", + "GPL"=>a_href("http://www.gnu.org/licenses/gpl.html","GNU General Public License"), + "LGPL"=>a_href("http://www.gnu.org/licenses/lgpl.html","GNU Lesser General Public License"), + "com"=>"Commercial" + ); + return $known{$_[0]}; + }}, + {"key"=>"maintenance","text"=>"State","format"=>sub ($) { + my %known=( + "active"=>"Ready to use. Project is now actively developed.", + "ready"=>"Ready to use. Maintained.", + "dead"=>"Dead code, no longer supported.", + "merge"=>"Functions belong to existing other project.", + "obsolete"=>"Obsoleted.", + "update"=>"Package needs updating to recent software.", + "accepted"=>"This patch got already integrated by the original package maintainer.", + "pending"=>"Patch is ready to be applied to the mainstream.", + "ignored"=>"Patch was ignored. It is not applied in the mainstream.", + ""=>"", + ); + my @r; + for ($known{($_[0]=~/^([^-]*)-?/)[0] || ""}) { + push @r,$_ if $_; + push @r," $'" if $'; + } + return join(" ",@r); + }}, + {"key"=>"aminet","text"=>a_href('http://www.aminet.net/','Aminet'),"format"=>sub ($) { + return join(" ", + a_href('http://www.aminet.net/'.$_[0].".lha",$_[0].".lha"), + "(".a_href('http://www.aminet.net/'.$_[0].".readme","readme").")"); + }}, + {"key"=>qr(^download\b),"text"=>sub ($) { + $_[0]=~s/^download//; + $_[0]=~s/^-/ /; + return "Download".$_[0]; + }, + "format"=>sub ($) { + return a_href($_[0],CGI::escapeHTML(File::Basename::basename($_[0]))); + }}, + {"key"=>qr(^link\b),"text"=>sub ($) { + $_[0]=~s/^link-//; + return $_[0]; + }, + "format"=>sub ($) { + return($_[0]=~/^qr(^cvs\b),"text"=>sub ($) { + $_[0]=~s/^cvs//; + $_[0]=~s/^-/ /; + return "CVS".$_[0]; + }, + "format"=>sub ($$) { + my($val,$key)=@_; + $key=~s/^cvs//; + $key=~s/^-/ /; + my $branch=""; + $branch=$1 if $val=~s/:(.*)//; + return join("
\n\t\t", + CGI::escapeHTML("cvs -d ".$W->{"pserver"}.":".$W->{"pserver_path"}." -z3" + ." checkout".(!$branch ? "" : " -r $branch -kk") + .($val!~m#/# ? "" : " -d ".File::Basename::basename($val)) + ." $val"), + join(" | \n\t\t", + map({ a_href($_->[1],$_->[0]); } + ["ViewCVS CVS repository",$W->{"project_viewcvs"}.$val."/".(!$branch ? "" : '?only_with_tag='.$branch)], + ["Download CVS snapshot" , + $W->{"project_viewcvs"}.$val."/".File::Basename::basename($val).".tar.gz?tarball=1" + .(!$branch ? "" : '&only_with_tag='.$branch)], + ["CVS ChangeLog" ,"/project/ChangeLog.txt.pl?cvs=$val"]))); + }}, + {"key"=>"ownership","text"=>"Ownership"}, + {"key"=>"sponsorship","text"=>"Sponsorship"}, + {"key"=>"language","text"=>"Programming language","format"=>sub ($) { + return a_href("http://java.sun.com/",CGI::escapeHTML($_[0])) + if $_[0]=~/^Java\b/; + return a_href("http://www.php.net/",CGI::escapeHTML($_[0])) + if $_[0]=~/^PHP\b/; + return undef(); + }}, + ); + +sub tableit_func +{ +my($tableit,$val,$key,$ListItem)=@_; + + delete $ListItem->{$key}; + my $r=""; + $r.=""; + if ($tableit->{"text"}) { + $r.=""; + $r.=(!ref $_ ? $_ : &{$_}($key)) for ($tableit->{"text"}); + $r.=""; + } + if ($tableit->{"format"}) { + do { $val=$_ if defined $_; } for (&{$tableit->{"format"}}($val,$key)); + } + return join("",map("".$_->[0]."".$_->[1]."\n",@$val)) + if ref $val; + $r.="$val"; + $r.="\n"; +} + + print ''."\n"; + for my $tableit (@table) { + if (!ref $tableit->{"key"}) { + print tableit_func($tableit,$ListItem->{$tableit->{"key"}},$tableit->{"key"},$ListItem) + if $ListItem->{$tableit->{"key"}}; + } + else { + for my $key (@{$ListItem->{"keys_array"}}) { + my $keyregex=$tableit->{"key"}; + next if $key!~/$keyregex/; + print tableit_func($tableit,$ListItem->{$key},$key,$ListItem); + } + } + } + print "\n"; + print vskip; +} + +sub project_arr_to_hash (@) +{ +my(@arr)=@_; + + return ( + @arr, + "keys_array"=>[ My::Web::arr_keys(@arr) ], + ); +} + +sub title ($$) +{ +my($class,$hashref)=@_; + + return $hashref->{"name"}.": ".$hashref->{"summary"}, +} + +# $args{"ListItem"}=\%...; +sub init_project ($%) +{ +my($class,%args)=@_; + + my $ListItem={ project_arr_to_hash(@{$args{"ListItem"}}) }; + my $W=$class->init( + "title"=>$class->title($ListItem), + map(("rel_$_"=>top_dir('/project/Rel.pl?rel='.$_.'&project='.($args{"__PACKAGE__"}=~/^.*::([^:]+)::[^:]+$/)[0])), + qw(prev next)), + "rel_up"=>top_dir('/project/'), + "rel_start"=>top_dir(), + %args, + "head_css"=>($args{"head_css"} || "")." +table.print_project td { vertical-align: top; } +", + "WebConfig::heading_novskip"=>1, + ); + $class->heading(); + print $class->platforms($ListItem->{"platform"}); + $class->print_project({ %$ListItem },%args); + return $W; +} + +sub one_item_list_read($$) +{ +my($class,$name)=@_; + + Wrequire "project::${name}::ListItem"; + my $item=eval('\@project::'.$name.'::ListItem::ListItem'); + do { warn "Broken project/$name/ListItem.pm"; next; } if !defined $item; + return @$item; +} + +sub item_hash_read () +{ +# FIXME: $class + my %dirs; + for my $ENTRIES (ENTRIES(),ENTRIES_LOG()) { + local *E; + next if !open E,$ENTRIES; + while () { + chomp; + do { $dirs{$1}=1; next; } if m#^(?:A )?D/([^/]*)/#; + next if m#^/([^/]*)/# ; + next if /^D$/; + warn "File $ENTRIES contains invalid line \"$_\": $!"; + } + close E; + } + + my %item; + for my $dir (keys(%dirs)) { + Wrequire "project::${dir}::ListItem"; + my $item=eval('\@project::'.$dir.'::ListItem::ListItem'); + do { warn "Broken project/$dir/ListItem.pm"; next; } if !defined $item; + $item{$dir}={ My::Project::project_arr_to_hash(My::Project->one_item_list_read($dir)) }; + } + return %item; +} + +our @platforms=( + "unixuser"=>"UNIX", + "unixdevel"=>"UNIX-devel", + "web"=>"Web", + "amiga"=>"Amiga", + "w32"=>"MS-Windows", + "dos"=>"MS-DOS", + "patch"=>"Patches", + ); + +sub views ($$) +{ +my($class,$view_selected)=@_; + + my $view=sub ($$) + { + my($current,$href,$content)=@_; + + return a_href($href,$content) if $current ne $view_selected; + return "".$content." (current)"; + }; + + return <<"HERE"; +

Project List of @{[ a_href 'http://www.jankratochvil.net/','Jan Kratochvil' ]}

+ +
    +
  • @{[ &{$view}('Detailed' ,'/project/','Detailed project listing per platform') ]}
  • +
  • @{[ &{$view}('BriefPlatform','/project/List.html.pl?platform=platform', + 'Brief project listing per platform') ]}
  • +
  • @{[ &{$view}('BriefUnified' ,'/project/List.html.pl', + 'Unified brief project listing') ]}
  • +
+@{[ vskip "1ex" ]} +HERE +} + +sub platforms ($;$%) +{ +my($class,$platform_selected,%args)=@_; + + my $r=""; + $r.=''."\n"; + $r.=''; + $r.=''."\n"; + $r.='
Projects:  '; + $r.=''."\n"; + $r.=''."\n"; + my @platforms=@platforms; + while (@platforms) { + my $platform_sym =shift @platforms; + my $platform_name=shift @platforms; + my $chosen=($platform_selected && $platform_selected eq $platform_sym); + $r.='\n"; + } + $r.=''."\n"; + $r.='
'; + $r.=a_href((!$platform_selected ? "" : "/project/").'#'.$platform_sym,$platform_name, + "attr"=>($chosen + ? 'style="text-decoration: underline; font-weight: bold;"' + : 'style="text-decoration: inherit; /* revoke underline */"')); + $r.="
'."\n"; + $r.='
'."\n"; + $r.=vskip "1ex" if !$args{"novskip"}; + $r; +} + +sub section ($$) +{ +my($class,$name)=@_; + + my %item=( $class->one_item_list_read($name) ); + my $title=$class->title(\%item); + my $r=""; + + print $class->platforms($item{"platform"},"novskip"=>1); + + $r.=''."\n"; + $r.=''."\n"; + $r.='
'; + $r.=''."\n"; + $r.=''."\n"; + $r.='
'."\n"; + $r.=a_href "/project/$name/",$title; + $r.='
'."\n"; + $r.='
'."\n"; + $r.=vskip "1ex"; + $r; +} + +1; diff --git a/Web.pm b/Web.pm index 013274a..e41a314 100644 --- a/Web.pm +++ b/Web.pm @@ -1,5 +1,3 @@ -#! /usr/bin/perl -# # $Id$ # Common functions for HTML/XHTML output generation # Copyright (C) 2003 Jan Kratochvil @@ -20,161 +18,219 @@ package My::Web; require 5.6.0; # at least 'use warnings;' but we need some 5.6.0+ modules anyway -use vars qw($VERSION); -$VERSION=do { my @r=(q$Revision$=~/\d+/g); sprintf "%d.".("%03d"x$#r),@r; }; +our $VERSION=do { my @r=(q$Revision$=~/\d+/g); sprintf "%d.".("%03d"x$#r),@r; }; +our $CVS_ID=q$Id$; use strict; use warnings; -use WebConfig; # for %WebConfig +use Exporter; +sub Wrequire ($); +sub Wuse ($@); +our $W; +our @EXPORT=qw(&Wrequire &Wuse &a_href &a_href_cz &vskip &img ¢erimg &rightimg $W &top_dir &top_dir_disk); +our @ISA=qw(Exporter); + +BEGIN +{ + sub Wrequire ($) + { + my($file)=@_; + +# print STDERR "Wrequire $file\n"; + $file=~s#/#::#g; + $file=~s/[.]pm$//; + my $class=$file; + $file=~s#::#/#g; + $file.=".pm"; + my $aref=($W->{"packages_used"}{$Apache::Registry::curstash}||=[]); + push @$aref,$class + if !{ map(($_=>1),@$aref) }->{$class}; # Prevent duplicated entries. + CORE::require $file; + 1; # Otherwise 'require' would already file above. + } + + sub Wuse ($@) + { + my($file,@list)=@_; + +# print STDERR "Wuse $file\n"; + Wrequire $file; + local $Exporter::ExportLevel=$Exporter::ExportLevel+1; + $file->import(@list); + 1; + } +} + +BEGIN { Wuse 'WebConfig'; } # for %WebConfig require CGI; # for &escapeHTML require Image::Size; # for &imgsize use File::Basename; # &basename +use Carp qw(cluck confess); +use URI::Escape; +require HTTP::BrowserDetect; +require HTTP::Negotiate; +require Geo::IP; +require CGI; + + +# Undo 'www/engine/httpd-restart' as it may use obsolete Perl for 'mod_perl' +delete $ENV{"PERLLIB"}; +delete $ENV{"LD_LIBRARY_PATH"}; + + +#our $W; + # $W->{"title"} + # $W->{"head"} + # $W->{"head_css"} + # $W->{"force_charset"} + # %{$W->{"packages_used"} + # $W->{"heading_done"} + # $W->{"footer_passed"} + # %{$W->{"headers"}} + # %{$W->{"headers_lc"}} # maps lc($headers_key)=>$headers_key + # @{$W->{"packages_used"}{$Apache::Registry::curstash}}} + # %{$W->{"args"}} - -my %Args; - # $Args{"title"} - # $Args{"force_charset"} - -my $cvs_id_html; sub init ($%) { my($class,%args)=@_; - %WebConfig=(%WebConfig,%args); # override %WebConfig settings + my $packages_used_save=$W->{"packages_used"}; + $W={ %WebConfig,%args }; # override %WebConfig settings + $W->{"packages_used"}=$packages_used_save; + + $W->{"__PACKAGE__"}||="Apache::ROOT".$Apache::Registry::curstash; + + $W->{"top_dir"}||=eval '$'.$W->{"__PACKAGE__"}.'::top_dir'; + + do { $W->{$_}=0 if !defined $W->{$_}; } for ("detect_ent"); + do { $W->{$_}=0 if !defined $W->{$_}; } for ("detect_js"); + do { $W->{$_}=1 if !defined $W->{$_}; } for ("have_css"); # AFAIK it does not hurt anyone. + do { $W->{$_}=1 if !defined $W->{$_}; } for ("heading"); + do { $W->{$_}=1 if !defined $W->{$_}; } for ("footer"); + do { $W->{$_}=1 if !defined $W->{$_}; } for ("footer_delimit"); + do { $W->{$_}=1 if !defined $W->{$_}; } for ("footer_mailme"); + do { $W->{$_}=1 if !defined $W->{$_}; } for ("footer_ids"); + do { $W->{$_}=1 if !defined $W->{$_}; } for ("indexme"); + do { $W->{$_}="" if !defined $W->{$_}; } for ("head"); + do { $W->{$_}="" if !defined $W->{$_}; } for ("head_css"); + + my $footer_any=0; + for (qw(footer_mailme footer_ids)) { + $W->{$_}=0 if !$W->{"footer"}; + $footer_any=1 if $W->{$_}; + } + $W->{"footer"}=0 if !$footer_any; + $W->{"footer_delimit"}=0 if !$W->{"footer"}; + + $W->{"r"}=Apache->request(); + + $W->{"QUERY_STRING"}=$W->{"r"}->args() || ""; + if ($W->{"QUERY_STRING"}=~/[&]amp;have_ent/) + { $W->{"have_ent"}=0; } + elsif ($W->{"QUERY_STRING"}=~ /[&]have_ent/) + { $W->{"have_ent"}=1; } + else + { delete $W->{"have_ent"}; } + if ($W->{"detect_ent"} && !defined $W->{"have_ent"} && $W->{"r"}->method() eq "GET") { + $W->{"head"}.='{"web_hostname_sub"}}()."/".($W->{"r"}->uri()=~m#^/*(.*)$#)[0] + ."?".($W->{"QUERY_STRING"} || "detect_ent_glue=1").'&have_ent=detect') + .'" />'."\n"; + } + $W->{"QUERY_STRING"}=~s/([&])amp;/$1/g; + $W->{"r"}->args($W->{"QUERY_STRING"}); + $ENV{"QUERY_STRING"}=$W->{"QUERY_STRING"}; + # Do not: $W->{"r"}->args() + # as it parses only QUERY_STRING (not POST data). + $W->{"args"}={ CGI->new()->Vars() }; + for (keys(%{$W->{"args"}})) { + my @vals=split /\x00/,$W->{"args"}{$_}; + next if @vals<=1; + $W->{"args"}{$_}=[@vals]; + } + + do { $W->{$_}=$ENV{"HTTP_ACCEPT"} if !defined $W->{$_}; } for ("accept"); + do { $W->{$_}=$ENV{"HTTP_USER_AGENT"} if !defined $W->{$_}; } for ("user_agent"); + + $W->{"browser"}=HTTP::BrowserDetect->new($W->{"user_agent"}); + + if (!defined $W->{"have_style"}) { + $W->{"have_style"}=(!$W->{"browser"}->netscape() || $W->{"browser"}->major>4 ? 1 : 0); + } - undef $WebConfig{"viewcvs"} if $ENV{"SCRIPT_NAME"} && $WebConfig{"viewcvs"} eq $ENV{"SCRIPT_NAME"}; - my @cvs_id_split=split / +/,$::CVS_ID; - if (@cvs_id_split==8) { - $cvs_id_split[2]="" - ."" - .$cvs_id_split[2].""; - $cvs_id_split[1]="".$cvs_id_split[1].""; - $cvs_id_split[5]="".$cvs_id_split[5].""; + $W->{"have_js"}=($W->{"args"}{"have_js"} ? 1 : 0); + if ($W->{"detect_js"} && !$W->{"have_js"}) { + $W->{"head"}.=''."\n"; } - $cvs_id_html=join " ",@cvs_id_split; + + do { args_check(%$_) if $_; } for ($W->{"args_check"}); + + return $W; } -sub print_project ($) +sub top_dir_disk () { -my($class,$ListItem)=@_; - - print "

".$ListItem->{"name"}."

\n"; - print $ListItem->{"description"}; - print "
\n"; - my @table=( - {"key"=>qr(^download\b.*),"text"=>sub ($) { - $_[0]=~s/^download//; - $_[0]=~s/^-/ /; - return "Download".$_[0]; - }, - "format"=>sub ($) { - my $r; - if ($_[0]=~m#^[a-z]+://#) { - $r="".CGI::escapeHTML($_[0]).""; - } - else { - $r="".CGI::escapeHTML(basename($_[0])).""; - my $size=(stat $_[0])[7]; - die "Cannot stat \"".$_[0]."\": $!" if !defined $size; - if ($size>=1024*1024) { $size=int($size/(1024*1024))." MB"; } - elsif ($size>=1024 ) { $size=int($size/(1024 ))." KB"; } - else { $size=int($size )." B"; } - $r.=" ($size)"; - } - return $r; - }}, - {"key"=>qr(^link\b.*),"text"=>sub ($) { - $_[0]=~s/^link-//; - return $_[0]; - }, - "format"=>sub ($) { - return "".CGI::escapeHTML($_[0]).""; - }}, - {"key"=>"summary","text"=>"Summary"}, - {"key"=>"ownership","text"=>"Ownership"}, - {"key"=>"license","text"=>"License","format"=>sub ($) { - my %known=( - "PD"=>"Public Domain", - "GPL"=>"GNU General Public License", - "LGPL"=>"GNU Lesser General Public License", - ); - return $known{$_[0]}; - }}, - {"key"=>"maintenance","text"=>"Currently maintained?","format"=>sub ($) { - my %known=( - "finished"=>"Project is finished. Possible bug reports welcome although project not actively developed.", - "dead"=>"Project became dead code, some updates would be required. It is no longer used, project is not supported.", - "obsolete"=>"Obsolete as some other existing package superseded this one.", - "merge"=>"Functions of this package should be merged to some other one.", - "update"=>"Package needs updating to be fully usable, patches welcome.", - "accepted"=>"This patch was accepted by the original package author. It has no longer any separate meaning.", - ); - return $known{$_[0]}; - }}, - {"key"=>"reason","text"=>"Reason"}, - {"key"=>"sponsorship","text"=>"Sponsoring Company"}, - {"key"=>"language","text"=>"Programming language","format"=>sub ($) { - return "".CGI::escapeHTML($_[0])."" - if $_[0]=~/^Java\b/; - return "".CGI::escapeHTML($_[0])."" - if $_[0]=~/^PHP\b/; - return undef(); - }}, - ); - print ''."\n"; + do { return $_ if $_; } for ($W->{"top_dir"}); + return $INC[0]; # fallback +} -sub tableit_func +sub top_dir (;$) { -my($tableit,$val,$key,$ListItem)=@_; - - print ""; - if ($tableit->{"format"}) { - my $format=$tableit->{"format"}; - my $valn=&$format($val); - $val=$valn if defined $valn; +my($in)=@_; + + if (my $uri=$ENV{"REQUEST_URI"}) { + $uri.="Index" if $uri=~m#/$#; + if (defined $in) { + my($inpath,$inquery)=split /[?]/,$in,2; + $inpath=~tr///cs; + $uri=~tr///cs; + for (;;) { + my($in1 ,$in2 )=($in =~m#^(/[^/]+)(/.*)$#); + my($uri1,$uri2)=($uri=~m#^(/[^/]+)(/.*)$#); + last if !defined $in1 || !defined $uri1 || $in1 ne $uri1; + $in=$in2; + $uri=$uri2; + } + } + $uri=~s#^/*##; + $uri=~s#[^/]+#..#g; + $uri=File::Basename::dirname($uri); + my $r=$uri.(defined $in ? $in : ""); +# 1 while $r=~s#^[.]/##; +# $r="./$r" if $r=~m#^(?:?.*)$#; # empty pathname? + return $r; } - print "\n"; - delete $ListItem->{$key}; + return top_dir_disk().$in; } - for my $tableit (@table) { - if (!ref $tableit->{"key"}) { - tableit_func($tableit,$ListItem->{$tableit->{"key"}},$tableit->{"key"},$ListItem) if $ListItem->{$tableit->{"key"}}; - } - else { - for my $key (keys(%$ListItem)) { - my $keyregex=$tableit->{"key"}; - next if $key!~/$keyregex/; - tableit_func($tableit,$ListItem->{$key},$key,$ListItem); - } +sub fatal (;$); + +sub args_check (%) +{ +my(%tmpl)=@_; + + while (my($name,$regex)=each(%tmpl)) { + my $name_html="Parameter ".CGI::escapeHTML($name).""; + $W->{"args"}{$name}="" if !defined $W->{"args"}{$name}; + $W->{"args"}{$name}=[ $W->{"args"}{$name} ] if !ref $W->{"args"}{$name} && ref $regex; + fatal "$name_html passed as multivar although singlevar expected" + if ref $W->{"args"}{$name} && !ref $regex; + $regex=${$regex}[0] if ref $regex; + for my $val (!ref $W->{"args"}{$name} ? $W->{"args"}{$name} : @{$W->{"args"}{$name}}) { + $val="" if !defined $val; + fatal "$name_html ".CGI::escapeHTML($val)."" + ." does not match the required regex ".CGI::escapeHTML($regex)." " + if $regex ne "" && $val!~/$regex/; } } - print "
"; - if (!ref $tableit->{"text"}) { - print $tableit->{"text"}; - } - else { - my $textfunc=$tableit->{"text"}; - my $key=$key; - print &$textfunc($key); - } - print ":$val
\n"; - print "

 

\n"; } -# $args{"ListItem"}=\%...; -sub init_project ($%) +sub vskip (;$) { -my($class,%args)=@_; +my($height)=@_; - my $ListItem=$args{"ListItem"}; - my $name=$ListItem->{"name"}; - $name=~s#]*>([^<]*)#$1#g; - init($class, - "title"=>$name, - %args); - heading(); - $class->print_project($ListItem); + return ' 

'."\n"; } sub fatal (;$) @@ -183,64 +239,316 @@ my($msg)=@_; $msg="UNKNOWN" if !$msg; -# heading(false/*title*/,false/*indexme*/); // notitle is always safe, don't index the error message - print("\n

 
 


FATAL ERROR: $msg!

\n" + $W->{"indexme"}=0; # For the case no heading was sent yet. + My::Web->heading(); + print "\n".vskip("3ex")."

FATAL ERROR: $msg!

\n" ."

You can report this problem's details to" - ." admin of this website.

\n"); -# footer(); + ." ".a_href("mailto:".$W->{"admin_mail"},"admin of this website").".

\n"; + footer(); } -my $footer_passed; sub footer (;$) { -my($delimit)=@_; + exit 1 if $W->{"footer_passed"}++; # deadlock prevention: + + print vskip if $W->{"footer_delimit"}; - $delimit=1 if !defined $delimit; + if ($W->{"heading"}) { + do { &{$_}() if $_; } for ($W->{"footing_delimit"}); + } + + print "
\n" if $W->{"footer"}; + + if ($W->{"footer_mailme"}) { + print '
'."\n"; + print '

'."\n"; + print ''."\n"; + print ''."\n"; + print ''."\n"; + print '

'."\n"; + print '
'."\n"; + } + + if ($W->{"footer_ids"}) { + print '

'; + print join("
\n",map({ my $package=$_; + my $cvs_id=(eval('$'.$package."::CVS_ID") +# || $package # debug + ); + if (!$cvs_id) { + (); + } + else { + $cvs_id='$'.$cvs_id.'$'; # Eaten by 'q' operator. + my @cvs_id_split=split / +/,$cvs_id; + if (@cvs_id_split==8) { + my $file=$package; + $file=~s#::#/#g; + my $ext; + for (qw(.html.pl .pl .pm),"") { + $ext=$_; + last if -r top_dir_disk()."/$file$ext"; + cluck "Class file $file not found" if !$ext; + } + $file.=$ext; + $cvs_id_split[2]="" + .a_href((map({ my $s=$_; $s=~s#/viewcvs/#$&~checkout~/#; $s; } $W->{"viewcvs"}))[0]."$file?rev=".$cvs_id_split[2], + $cvs_id_split[2]); + $cvs_id_split[1]=a_href($W->{"viewcvs"}.$file, + ($package!~/^Apache::/ ? $package : $cvs_id_split[1])); + $cvs_id_split[5]=&{$W->{"cvs_id_author"}}($cvs_id_split[5]); + } + join " ",@cvs_id_split; + } + } ( + $W->{"__PACKAGE__"}, + __PACKAGE__, + @{$W->{"packages_used"}{$Apache::Registry::curstash}}, + ))); + print "

\n"; + } + + for my $package ( + $W->{"__PACKAGE__"}, + __PACKAGE__, + @{$W->{"packages_used"}{$Apache::Registry::curstash}}, + ) { + my $cvs_id=(eval('$'.$package."::CVS_ID") +# || $package # debug + ); + print ''."\n" if $cvs_id; + } - exit(1) if $footer_passed++; # deadlock prevention: + if ($W->{"heading"}) { + do { &{$_}() if $_; } for ($W->{"footing"}); + } - print "

 

\n" if $delimit; - print "
\n

$cvs_id_html

\n"; print "\n"; exit(0); } -my $heading_done; - -my %headers; -my %headers_lc; # maps lc($headers_key)=>$headers_key sub header (%) { my(%pairs)=@_; while (my($key,$val)=each(%pairs)) { - do { warn "Headers already sent"; next; } if $heading_done; - for ($headers_lc{lc $key} || ()) { - delete $headers{$_}; + do { cluck "Headers already sent"; next; } if $W->{"heading_done"}; + for ($W->{"headers_lc"}{lc $key} || ()) { + delete $W->{"headers"}{$_}; + } + $W->{"headers_lc"}{lc $key}=$key; + $W->{"headers"}{$key}=$val; + } +} + +sub size_display ($) +{ +my($size)=@_; + + if ($size<4096) + {} + elsif ($size<1024*1024) + { $size=sprintf "%.1fK",$size/1024; } + else + { $size=sprintf "%.1fM",$size/1024/1024; } + $size.="B"; + return $size; +} + +sub url_is_local ($) +{ +my($url)=@_; + + return $url!~m#^[a-z]+://#; +} + +sub a_href ($;$%) +{ +my($url,$contents,%args)=@_; + + do { $$_=1 if !defined $$_; } for (\$args{"size"}); + $contents=CGI::escapeHTML($url) if !defined $contents; + $contents=~s#]*>##gi; + $contents=~s###gi; + + $url=top_dir($url) if url_is_local $url && $url=~m#^/#; + + my $r='{"have_ent"}) # non-ent client + { $r.=$url; } + elsif ($W->{"have_ent"}) # ent client + { $r.=$urlent; } + else # unknown client, &CGI::escapeHTML should not be needed here + { $r.=CGI::escapeHTML(top_dir('/Redirect.pl?location='.uri_escape($url))); } + $r.='"'; + do { $r.=" $_" if $_; } for ($args{"attr"}); + $r.='>'.$contents.''; + if ($args{"size"} && url_is_local($url) && $url=~/[.](?:gz|Z|rpm|zip|deb|lha)/) { # Downloadable? + $url=top_dir_disk().$url if $url=~m#^/#; + if (!-r $url) + { cluck "File not readable: $url"; } + else { + $r.=' ('.size_display((stat($url))[7]).')'; } - $headers_lc{lc $key}=$key; - $headers{$key}=$val; } + return $r; +} + +sub remote_ip () +{ + # Do not: PerlModule Apache::ForwardedFor + # PerlPostReadRequestHandler Apache::ForwardedFor + # As 'Apache::ForwardedFor' takes the first of $ENV{"HTTP_X_FORWARDED_FOR"} + # while the contents is '127.0.0.1, 213.220.195.171' if client has its own proxy. + # We must take the last item ourselves. + my $r=$ENV{"HTTP_X_FORWARDED_FOR"} || $W->{"r"}->get_remote_host(); + $r=~s/^.*,\s*//; + return $r; +} + +sub is_cz () +{ + return "CZ" eq Geo::IP->new()->country_code_by_addr(remote_ip()); +} + +sub a_href_cz ($$;%) +{ +my($url,$contents,%args)=@_; + + return a_href $url,$contents,%args if is_cz(); + return $contents; +} + +sub make ($) +{ +my($cmd)=@_; + + system {'flock'} 'flock','-x',top_dir_disk(),$cmd.' >&2'; } sub img_size ($$) { my($width,$height)=@_; - return((1 #$have_style TODO:dyn - ? "style=\"border:0;width:${width}px;height:${height}px\"" : "border=\"0\"") - ." width=\"$width\" height=\"$height\""); + return ($W->{"have_style"} ? "style=\"border:0;width:${width}px;height:${height}px\"" : "border=\"0\"") + ." width=\"$width\" height=\"$height\""; } -sub img ($$;$) +sub negotiate_variant (%) { -my($file,$alt,$attrs)=@_; +my(%args)=@_; + + my @fields=("id","qs","content-type","encoding","charset","lang","size"); + return [ map(($args{$_}),@fields) ]; +} - (my $file_det=$file)=~s/[.]mng$/.gif/; - my($width,$height)=Image::Size::imgsize($file_det); +my @img_variants=( + { "id"=>"png","qs"=>1.0,"content-type"=>"image/png" }, + { "id"=>"gif","qs"=>0.9,"content-type"=>"image/gif" }, + ); +my $img_variants_re='[.](?:'.join('|',"jpeg",map(($_->{"id"}),@img_variants)).')$'; + +sub img_src ($) +{ +my($file_base)=@_; + + if (!url_is_local($file_base)) { + return $file_base if !wantarray(); + return ($file_base,$file_base); + } + # Known image extension? + if ($file_base=~m#$img_variants_re#o) { + return $file_base if !wantarray(); + return ($file_base,$file_base) if $file_base!~m#^/#; + return (top_dir($file_base),top_dir_disk().$file_base); + } + + my $file_base_disk; + my $file_base_uri; + if ($file_base!~m#^/#) { + $file_base_disk=$file_base_uri=$file_base; + } + else { + $file_base_disk=top_dir_disk().$file_base; + $file_base_uri=top_dir($file_base); + } + + my @nego_variants; + for my $var (@img_variants) { + my $file=$file_base_disk.".".$var->{"id"}; + # TODO: Somehow quickly check dependencies? + make('make -s --no-print-directory' + .' -C '."'".File::Basename::dirname($file)."' '".File::Basename::basename($file)."'") + if !-f $file; + push @nego_variants,negotiate_variant( + %$var, + "size"=>(stat $file)[7], + ); + } + # Do not: ,$W->{"r"}); + # but should we provide somehow either 'HTTP::Headers' or 'HTTP::Request' ? + my $ext=HTTP::Negotiate::choose(\@nego_variants); + $ext||=$img_variants[0]->{"id"}; # &HTTP::Negotiate::choose failed? + + return $file_base_uri.".".$ext if !wantarray(); + return ($file_base_uri.".".$ext,$file_base_disk.".".$ext); +} + +sub img ($$;%) +{ +my($file_base,$alt,%attr)=@_; + + my($file_uri,$file_disk)=img_src $file_base; + my($width,$height)=Image::Size::imgsize($file_disk); + $alt=~s/<[^>]*>//g; $alt=CGI::escapeHTML($alt); - return("\"$alt\""); + my $content="\"$alt\""; + return a_href img_src($attr{"a_href_img"}),$content if $attr{"a_href_img"}; + return a_href $attr{"a_href"},$content if $attr{"a_href"}; + return $content; +} + +sub centerimg +{ + my $r.=""; + $r.=''."\n"; + @_=( [@_] ) if !ref $_[0]; + for (@_) { + $r.="\t".''."\n"; + } + $r.='
'.&{\&img}(@$_).'
'."\n"; + return $r; +} + +sub rightimg +{ +my($text,@args_img)=@_; + + # Workaround bug of 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)': + return <<"HERE"; + + ie() ? "1*" : "90%" ) ]}" /> + ie() ? "0*" : "10%" ) ]}" /> + + + + +
+ @{[ $text ]} + + @{[ &{\&img}(@args_img) ]} +
+HERE } sub readfile ($$) @@ -255,37 +563,48 @@ my($class,$filename)=@_; return $data; } -sub heading (;$$) +sub arr_keys (@) { -my($class,$showtitle,$indexme)=@_; +my(@arr)=@_; - $showtitle=1 if !defined $showtitle; - $indexme=1 if !defined $indexme; + my @r=(); + while (@arr) { + push @r,shift @arr; # key + shift @arr; # val + } + return @r; +} + +sub heading () +{ +my($class)=@_; + + return if $W->{"heading_passed"}++; # $ENV{"CLIENT_CHARSET"} ignored (mod_czech support dropped!) - my $client_charset=$Args{"force_charset"} || "us-ascii"; - header("Content-type"=>"text/html; charset=$client_charset"); + my $client_charset=$W->{"force_charset"} || "us-ascii"; header("Content-Style-Type"=>"text/css"); + header("Content-Script-Type"=>"text/javascript"); - if ($ENV{"SERVER_SOFTWARE"}) { - while (my($key,$val)=each(%headers)) { - print "$key: $val\n"; - } - print "\n"; + while (my($key,$val)=each(%{$W->{"headers"}})) { + $W->{"r"}->header_out($key,$val); } + $W->{"r"}->send_http_header("text/html; charset=$client_charset"); # "Content-type"; do not use header() - return if $heading_done++; + return if $W->{"heading_done"}++; + exit if $W->{"r"}->header_only(); if (1) { # || !$msie_major || $msie_major>=4) # TODO:dyn print ''."\n"; } print ''."\n"; print ''."\n"; - print ''.CGI::escapeHTML($WebConfig{"title_prefix"}) - .join("",map({ ': '.CGI::escapeHTML($_); } ($WebConfig{"title"} || ()))) - .''."\n"; + my $title=$W->{"title_prefix"}.join("",map({ ': '.$_; } ($W->{"title"} || ()))); + $title=~s#<[^>]*>##g; + print ""; + print "$title\n"; - if (1) { # || $have_css) # TODO:dyn + if ($W->{"have_css"}) { print <<'HERE'; \n"; } - print ''."\n"; - print $_ for ($WebConfig{"head"} || ()); + print ''."\n"; + print $W->{"head"}; + for my $type (qw(prev next index contents start up)) { + do { print ''."\n" if $_; } for ($W->{"rel_$type"}); + } print "{"browser"}->netscape() && $W->{"browser"}->major<=4; print ">\n"; -# if ($showtitle) -# print("

" -# ."Energie & Peníze") -# ."

\n"); + + if ($W->{"heading"}) { + do { &{$_}() if $_; } for ($W->{"heading"}); + } } 1; diff --git a/arrow-left.png b/arrow-left.png new file mode 100644 index 0000000..074552b Binary files /dev/null and b/arrow-left.png differ diff --git a/arrow-right.png b/arrow-right.png new file mode 100644 index 0000000..971d493 Binary files /dev/null and b/arrow-right.png differ diff --git a/arrow-up.png b/arrow-up.png new file mode 100644 index 0000000..24c9756 Binary files /dev/null and b/arrow-up.png differ