Initial mod_perl-2.0 port.
[www.jankratochvil.net.git] / project / Lib.pm
1 # $Id$
2 # Common functions for HTML/XHTML output generation
3 # Copyright (C) 2003 Jan Kratochvil <project-www.jankratochvil.net@jankratochvil.net>
4
5 # This program is free software; you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; exactly version 2 of June 1991 is required
8
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 # GNU General Public License for more details.
13
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17
18
19 package project::Lib;
20 require 5.6.0;  # at least 'use warnings;' but we need some 5.6.0+ modules anyway
21 our $VERSION=do { my @r=(q$Revision$=~/\d+/g); sprintf "%d.".("%03d"x$#r),@r; };
22 our $CVS_ID=q$Id$;
23 use strict;
24 use warnings;
25
26 use My::Web;
27 require CGI;
28 use Carp qw(cluck confess);
29
30 use Exporter;
31 our @EXPORT=qw();
32 our @ISA=qw(My::Web Exporter);
33 Wuse 'My::Hash::Sub';
34 Wuse 'My::Hash::Sub::Readonly';
35
36
37 sub LIST_FILENAME()
38 {
39         # Do not: path_abs_disk("/project/SUBDIRS");
40         # as we would need $W->{"r"} for the possibly relative path resolving.
41         return My::Web::dir_top_abs_disk()."/project/SUBDIRS";
42 }
43
44
45 sub print_project
46 {
47 my($class,$ListItem,%args)=@_;
48
49         print "<h1>".$W->{"title"}."</h1>\n";
50         do { print $_ if $_; } for ($args{"project_text_after_title"});
51         print $ListItem->{"description"};
52         print "<hr />\n";
53         print($args{"before_project_data"}||"");
54         return if $args{"no_project_data"};
55         my @table=(
56                 {"key"=>"summary","text"=>"Summary"},
57                 {"key"=>"license","text"=>"License","format"=>sub ($) {
58                                 my %known=(
59                                                 "PD"=>"Public Domain",
60                                                 "GPL"=>a_href("http://www.gnu.org/licenses/gpl.html","GNU General Public License"),
61                                                 "LGPL"=>a_href("http://www.gnu.org/licenses/lgpl.html","GNU Lesser General Public License"),
62                                                 "com"=>"Commercial"
63                                                 );
64                                 return $known{$_[0]};
65                                 }},
66                 {"key"=>"maintenance","text"=>"State","format"=>sub ($) {
67                                 my %known=(
68                                                 "active"=>"Ready to use. Project is now actively developed.",
69                                                 "ready"=>"Ready to use. Maintained.",
70                                                 "dead"=>"Dead code, no longer supported.",
71                                                 "merge"=>"Functions belong to existing other project.",
72                                                 "obsolete"=>"Obsoleted.",
73                                                 "update"=>"Package needs updating to recent software.",
74                                                 "accepted"=>"This patch got already integrated by the original package maintainer.",
75                                                 "pending"=>"Patch is ready to be applied to the mainstream.",
76                                                 "ignored"=>"Patch was ignored. It is not applied in the mainstream.",
77                                                 ""=>"",
78                                                 );
79                                 my @r;
80                                 for ($known{($_[0]=~/^([^-]*)-?/)[0] || ""}) {
81                                         push @r,$_ if $_;
82                                         push @r," $'" if $';
83                                         }
84                                 return join(" ",@r);
85                                 }},
86                 {"key"=>"aminet","text"=>a_href('http://www.aminet.net/','Aminet'),"format"=>sub ($) {
87                                 return join(" ",
88                                                 a_href('http://www.aminet.net/'.$_[0].".lha",$_[0].".lha"),
89                                                 "(".a_href('http://www.aminet.net/'.$_[0].".readme","readme").")");
90                                 }},
91                 {"key"=>qr(^download\b),"text"=>sub ($) {
92                                                 $_[0]=~s/^download//;
93                                                 $_[0]=~s/^-/ /;
94                                                 return "Download".$_[0];
95                                                 },
96                                 "format"=>sub ($) {
97                                                 return a_href($_[0],CGI::escapeHTML(File::Basename::basename($_[0])),"size"=>2);
98                                                 }},
99                 {"key"=>qr(^link\b),"text"=>sub ($) {
100                                                 $_[0]=~s/^link-//;
101                                                 return $_[0];
102                                                 },
103                                 "format"=>sub ($) {
104                                                 return($_[0]=~/^<a\b/ ? $_[0] : a_href($_[0],CGI::escapeHTML($_[0])));
105                                                 }},
106                 {"key"=>qr(^cvs\b),"text"=>sub ($) {
107                                                 $_[0]=~s/^cvs//;
108                                                 $_[0]=~s/^-/ /;
109                                                 return "CVS".$_[0];
110                                                 },
111                                 "format"=>sub ($$) {
112                                                 my($val,$key)=@_;
113                                                 $key=~s/^cvs//;
114                                                 $key=~s/^-/ /;
115                                                 my $branch="";
116                                                 $branch=$1 if $val=~s/:(.*)//;
117                                                 return join("<br />\n\t\t",
118                                                                 CGI::escapeHTML("cvs -d ".$W->{"pserver"}.":".$W->{"pserver_path"}." -z3"
119                                                                                 ." checkout".(!$branch ? "" : " -r $branch -kk")
120                                                                                 .($val!~m#/# ? "" : " -d ".File::Basename::basename($val))
121                                                                                 ." $val"),
122                                                                 join(" | \n\t\t",
123                                                                                 map({ a_href($_->[1],$_->[0]); }
124                                                                                                 ["ViewCVS CVS repository",$W->{"project_viewcvs"}.$val."/".(!$branch ? "" : '?only_with_tag='.$branch)],
125                                                                                                 ["Download CVS snapshot" ,
126                                                                                                                 $W->{"project_viewcvs"}.$val."/".File::Basename::basename($val).".tar.gz?tarball=1"
127                                                                                                                                 .(!$branch ? "" : '&only_with_tag='.$branch)],
128                                                                                                 ["CVS ChangeLog"         ,"/project/ChangeLog.pm?cvs=$val"])));
129                                                 }},
130                 {"key"=>"ownership","text"=>"Ownership"},
131                 {"key"=>"sponsorship","text"=>"Sponsorship"},
132                 {"key"=>"language","text"=>"Programming language","format"=>sub ($) {
133                                 return a_href("http://java.sun.com/",CGI::escapeHTML($_[0]))
134                                                 if $_[0]=~/^Java\b/;
135                                 return a_href("http://www.php.net/",CGI::escapeHTML($_[0]))
136                                                 if $_[0]=~/^PHP\b/;
137                                 return undef();
138                                 }},
139                 );
140
141 sub tableit_func
142 {
143 my($tableit,$val,$key,$ListItem)=@_;
144
145         my $r="";
146         $r.="<tr>";
147                 if ($tableit->{"text"}) {
148                         $r.="<td>";
149                                 $r.=(!ref $_ ? $_ : &{$_}($key)) for ($tableit->{"text"});
150                         $r.="</td>";
151                         }
152                 if ($tableit->{"format"}) {
153                         do { $val=$_ if defined $_; } for (&{$tableit->{"format"}}($val,$key));
154                         }
155                 return join("",map("<tr><td>".$_->[0]."</td><td>".$_->[1]."</td></tr>\n",@$val))
156                                 if ref $val;
157                 $r.="<td>$val</td>";
158         $r.="</tr>\n";
159 }
160
161         my %used_key;
162         print '<table border="0" class="print_project">'."\n";
163                 for my $tableit (@table) {
164                         if (!ref $tableit->{"key"}) {
165                                 print tableit_func($tableit,$ListItem->{$tableit->{"key"}},$tableit->{"key"},$ListItem)
166                                                 if $ListItem->{$tableit->{"key"}} && !$used_key{$tableit->{"key"}}++;
167                                 }
168                         else {
169                                 for my $key (@{$ListItem->{"keys_array"}}) {
170                                         my $keyregex=$tableit->{"key"};
171                                         next if $key!~/$keyregex/;
172                                         print tableit_func($tableit,$ListItem->{$key},$key,$ListItem);
173                                         }
174                                 }
175                         }
176         print "</table>\n";
177         print vskip;
178 }
179
180 sub hashlikearray_get_keys(@)
181 {
182 my(@hashlikearray)=@_;
183
184         my @r;
185         while (@hashlikearray) {
186                 push @r,shift @hashlikearray;   # key
187                 shift @hashlikearray;   # val
188                 }
189         return @r;
190 }
191
192 sub project_arrayref_to_hashref($$)
193 {
194 my($self,$arrayref)=@_;
195
196         my $r={};
197         tie %$r,"My::Hash::Sub::Readonly",(
198                         @$arrayref,
199                         "keys_array"=>[ hashlikearray_get_keys(@$arrayref) ],
200                         );
201         return $r;
202 }
203
204 sub title ($$)
205 {
206 my($class,$hashref)=@_;
207
208         cluck if !$hashref->{"name"} || !$hashref->{"summary"};
209         return $hashref->{"name"}.": ".$hashref->{"summary"},
210 }
211
212 # Returns: hashref if !wantarray(), list if wantarray().
213 sub list($)
214 {
215 my($self)=@_;
216
217         our %list_cache;
218         our @list_cache;
219         if (!@list_cache) {
220                 My::Web->make_file(LIST_FILENAME());
221                 local *F;
222                 open F,LIST_FILENAME() or do {
223                         cluck "Error opening \"".LIST_FILENAME()."\": $!";
224                         return;
225                         };
226                 my @r=split(" ",do { undef $/; <F>; });
227                 close F or cluck "Error closing \"".LIST_FILENAME()."\": $!";
228                 cluck "No projects found?" if !@r;
229                 @list_cache=@r;
230                 %list_cache=map(($_=>1),@list_cache);
231                 }
232         return \%list_cache if !wantarray();
233         return @list_cache;
234 }
235
236 # Returns: hashlist of hashrefs if !$name.
237 sub name_to_hashref($;$)
238 {
239 my($class,$name)=@_;
240
241         cluck if !wantarray() && !$name;
242         # Do not cache the result to get all the items &Wrequire-mapped.
243         return map(($_=>$class->name_to_hashref($_)),$class->list()) if !$name;
244         cluck join(" ","Project name \"$name\" not listed in 'list_cache':",$class->list())
245                         if !$class->list()->{$name};
246         # Do not cache &Wrequire to gets its $Id$ markers / usage map.
247         Wrequire "project::${name}::Index";
248         our %cache;
249         if (!$cache{$name}) {
250                 my $arrayref=eval('\@project::'.$name.'::Index::ListItem');
251                 do { warn "Broken project/$name/Index.pm"; return undef(); } if !@$arrayref;
252                 $cache{$name}=$class->project_arrayref_to_hashref($arrayref);
253                 }
254         return $cache{$name};
255 }
256
257 # $args{"ListItem"}=\%...;
258 sub init($%)
259 {
260 my($class,%args)=@_;
261
262         $args{"__PACKAGE__"}||=caller();
263         $args{"project_name"}||=($args{"__PACKAGE__"}=~/^project::(\w+)::Index$/)[0]
264                         or cluck "Error finding project name of the package: ".$args{"__PACKAGE__"};
265         my $ListItem=$class->name_to_hashref($args{"project_name"});
266         my $W=$class->SUPER::init(
267                         "title"=>$class->title($ListItem),
268                         map(("rel_$_"=>'/project/Rel.pm?rel='.$_.'&project='.$args{"project_name"}),qw(prev next)),
269                         "rel_up"=>'/project/',
270 #                       "rel_start"=>"/",       # TODO:homepage
271                         %args,
272                         "head"=>($args{"head"}||"").<<"HERE",
273 <link rel="stylesheet" type="text/css" href="@{[ uri_escaped(path_web "/project/Lib.css") ]}" />
274 HERE
275                         "heading_novskip"=>1,
276                         );
277         $class->heading();
278         print $class->platforms($ListItem->{"platform"});
279         $class->print_project($ListItem,%args);
280         return $W;
281 }
282
283 our @platforms=(
284                 "unixuser"=>"UNIX",
285                 "unixdevel"=>"UNIX-devel",
286                 "web"=>"Web",
287                 "amiga"=>"Amiga",
288                 "w32"=>"MS-Windows",
289                 "dos"=>"MS-DOS",
290                 "patch"=>"Patches",
291                 );
292
293 sub views ($$)
294 {
295 my($class,$view_selected)=@_;
296
297         my $view=sub ($$)
298                 {
299                 my($current,$href,$content)=@_;
300
301                         return a_href($href,$content) if $current ne $view_selected;
302                         return "<b>".$content."</b> (current)";
303                 };
304
305         return <<"HERE";
306 <h1>Project List of @{[ a_href 'http://www.jankratochvil.net/','Jan Kratochvil' ]}</h1>
307
308 <ul>
309         <li>@{[ &{$view}('Detailed'     ,'/project/','Detailed project listing per platform') ]}</li>
310         <li>@{[ &{$view}('BriefPlatform','/project/List.pm?platform=platform',
311                         'Brief project listing per platform') ]}</li>
312         <li>@{[ &{$view}('BriefUnified' ,'/project/List.pm',
313                         'Unified brief project listing') ]}</li>
314 </ul>
315 @{[ vskip "1ex" ]}
316 HERE
317 }
318
319 sub platforms ($;$%)
320 {
321 my($class,$platform_selected,%args)=@_;
322
323         my $r="";
324         $r.='<table border="0" width="100%"><tr><td align="center">'."\n";
325                 $r.='<table><tr>'."\n";
326                         $r.='<td>';
327                                 $r.='<table border="1" style="border-collapse: collapse; border-style: solid; border-width: 1px;">'."\n";
328                                         $r.='<tr>'."\n";
329                                                 $r.='<td style="padding: 5px; font-weight: bold;">'."\n";
330                                                         $r.='Projects';
331                                                 $r.='</td>'."\n";
332                                         $r.='</tr>'."\n";
333                                 $r.='</table>';
334                         $r.='</td>';
335                         $r.='<td>';
336                                 $r.='<table border="1" style="border-collapse: collapse; border-style: solid;">'."\n";
337                                         $r.='<tr>'."\n";
338                                                 my @platforms=@platforms;
339                                                 while (@platforms) {
340                                                         my $platform_sym =shift @platforms;
341                                                         my $platform_name=shift @platforms;
342                                                         my $chosen=($platform_selected && $platform_selected eq $platform_sym);
343                                                         $r.='<td style="padding: 5px;">';
344                                                                 $r.=a_href((!$platform_selected ? "" : "/project/").'#'.$platform_sym,$platform_name,
345                                                                                 "attr"=>($chosen
346                                                                                                 ? 'style="text-decoration: underline; font-weight: bold;"'
347                                                                                                 : 'style="text-decoration: inherit; /* revoke underline */"'));
348                                                         $r.="</td>\n";
349                                                         }
350                                         $r.='</tr>'."\n";
351                                 $r.='</table>'."\n";
352                         $r.='</td>'."\n";
353                 $r.='</tr></table>'."\n";
354         $r.='</td></tr></table>'."\n";
355         if (!$args{"novskip"}) {
356                 $r.="<hr />\n";
357                 $r.=My::Web::vskip "6ex";
358                 }
359         return $r;
360 }
361
362 sub section ($$)
363 {
364 my($class,$name)=@_;
365
366         my $item=$class->name_to_hashref($name);
367         my $title=$class->title($item);
368         my $r="";
369
370         print $class->platforms($item->{"platform"},"novskip"=>1);
371
372         $r.='<table border="0" width="100%"><tr><td align="center">'."\n";
373                 $r.='<table border="1" style="border-collapse: collapse; border-style: solid;">'."\n";
374                         $r.='<tr><td style="font-size: larger;">'."\n";
375                                 $r.=a_href "/project/$name/",$title;
376                         $r.='</td></tr>'."\n";
377                 $r.='</table>'."\n";
378         $r.='</td></tr></table>'."\n";
379         $r.=vskip "1ex";
380         return $r;
381 }
382
383 1;