File Coverage

blib/lib/Template/TT2Site.pm
Criterion Covered Total %
statement 49 210 23.3
branch 0 86 0.0
condition 0 21 0.0
subroutine 19 38 50.0
pod 5 9 55.5
total 73 364 20.0


line stmt bran cond sub pod time code
1             package Template::TT2Site;
2              
3 2     2   70007 use strict;
  2         5  
  2         111  
4 2     2   12 use vars qw($VERSION);
  2         4  
  2         6514  
5              
6             $VERSION = sprintf("%d.%02d", q$Revision: 1.46 $ =~ /(\d+)\.(\d+)/);
7              
8 2     2   13 use strict;
  2         9  
  2         129  
9              
10             =head1 NAME
11              
12             Template::TT2Site - Create standard web sites with the Template Toolkit
13              
14             =head1 SYNOPSIS
15              
16             $ mkdir NewSite
17             $ cd NewSite
18             $ tt2site setup
19             ... make your pages ...
20             $ tt2site build
21             ... point your browser at html/index.html ...
22              
23             C is just a wrapper program. C is equivalent
24             to C, and so on.
25              
26             =head1 DESCRIPTION
27              
28             B is a framework to create web sites using the
29             Template Toolkit.
30              
31             The technical structure of the site is patterned after the method
32             described in chapter 11 of I. The structure has been
33             slightly simplified for ease of use, and a couple of neat features are
34             added:
35              
36             =over 4
37              
38             =item *
39              
40             The resultant site is position independent, i.e., it only uses
41             relative URLs to the extent possible. This makes it easy to build
42             partial sites, and to relocate the contents.
43              
44             =item *
45              
46             The necessary means are provided to create multi-language sites, where
47             each page gets a link to its translations.
48              
49             =item *
50              
51             The 'site.map' hash, required for site navigation, is created
52             automatically using minimal, position independent, directions.
53              
54             =back
55              
56             This module, B, provides the necessary methods to
57             setup and maintain a site. It is used by the wrapper program,
58             B.
59              
60             For more information, see the
61             Web site: L.
62              
63             =head1 METHODS
64              
65             The following methods are exported by default.
66              
67             =over 8
68              
69             =item B
70              
71             Initialises a new site directory. This command must be run once before
72             you can do anything else.
73              
74             =item B
75              
76             Run the C application to update the site files.
77              
78             =item B
79              
80             Run the C application to completely rebuild all site files.
81              
82             =item B
83              
84             Cleans the generated HTML files, and editor backup files.
85              
86             =item B
87              
88             Cleans the generated HTML files, editor backup files, and all files
89             originally installed using the B command.
90              
91             You'll be asked for confirmation before your files are removed.
92              
93             =back
94              
95             All other methods are for internal use only.
96              
97             =head1 AUTHOR
98              
99             Johan Vromans
100              
101             =head1 COPYRIGHT
102              
103             This programs is Copyright 2004,2005, Squirrel Consultancy.
104              
105             This program is free software; you can redistribute it and/or modify
106             it under the terms of the Perl Artistic License or the GNU General
107             Public License as published by the Free Software Foundation; either
108             version 2 of the License, or (at your option) any later version.
109              
110             =head1 DEPENDENCIES
111              
112             B requires the following Perl modules, all
113             available on CPAN:
114              
115             =over 4
116              
117             =item *
118              
119             B, version 2.13 (preferrably 2.14) or later.
120              
121             B uses the B tool, which is assumed to be
122             available in your execution path I.
123              
124             =item *
125              
126             B. This is used by the B tool.
127              
128             =back
129              
130             =head1 BUGS AND PROBLEMS
131              
132             This product is better than this documentation.
133              
134             =head1 AUTHOR AND CREDITS
135              
136             Johan Vromans (jvromans@squirrel.nl) wrote this software.
137              
138             Many things were borrowed and adapted from the Template Toolkit
139             sample materials and the Badger book.
140              
141             Web site: L.
142              
143             =head1 COPYRIGHT AND DISCLAIMER
144              
145             This software is Copyright 2004-2005 by Squirrel Consultancy. All
146             rights reserved.
147              
148             This program is free software; you can redistribute it and/or modify
149             it under the terms of either: a) the GNU General Public License as
150             published by the Free Software Foundation; either version 1, or (at
151             your option) any later version, or b) the "Artistic License" which
152             comes with Perl.
153              
154             This program is distributed in the hope that it will be useful, but
155             WITHOUT ANY WARRANTY; without even the implied warranty of
156             MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See either the
157             GNU General Public License or the Artistic License for more details.
158              
159             =cut
160              
161 2     2   19 use base qw(Exporter);
  2         2  
  2         469  
162             our (@EXPORT) = qw(build setup rebuild clean realclean);
163              
164             my $my_name = __PACKAGE__;
165              
166             my $realclean = 0;
167             my $verbose = 0; # more verbosity
168              
169             my $debug = 0; # debugging
170             my $trace = 0; # trace (show process)
171             my $test = 0; # test mode.
172              
173             ################ Presets ################
174              
175             my $setupdone = ".setupdone";
176             my $ttree = "ttree";
177             my $sitelib;
178             my @cmds;
179             my %help;
180              
181             ################ The Process ################
182              
183 2     2   12 use File::Spec;
  2         18  
  2         267  
184 2     2   13 use File::Path;
  2         3  
  2         736  
185 2     2   16 use File::Find;
  2         4  
  2         165  
186 2     2   2309 use File::Copy;
  2         15831  
  2         200  
187 2     2   22 use File::Basename;
  2         4  
  2         236  
188 2     2   26 use Carp;
  2         5  
  2         4756  
189              
190             ################ Subroutines ################
191              
192             sub execute {
193 0     0 0   my ($self, @args) = @_;
194 0           local(@ARGV) = @args;
195 0 0         @ARGV = qw(build) unless @ARGV;
196 0           my $cmdname = lc(shift(@ARGV));
197 0 0         my $cmd = __PACKAGE__->can($cmdname) if $cmdname =~ /^[a-z]/;
198 0 0         _usage(1) unless $cmd;
199 0           $cmd->(@ARGV);
200             }
201              
202             sub _preamble($;$) {
203 0     0     $my_name .= "::" . shift;
204 0           _check_lib();
205 0           _options(@_);
206 0           _find_ttree();
207 0 0 0       _check_setup() unless @_ && $_[0];
208             }
209              
210             INIT {
211 2     2   8 push(@cmds, "setup");
212 2         12 $help{$cmds[-1]} = <
213             Initialises a new site directory. This command must be run
214             once before you can do anything else.
215             EOD
216             }
217             my @samples;
218             INIT {
219 2     2   18 @samples = ( [ qw(lib config site) ],
220             [ qw(lib config images) ],
221             );
222             }
223             sub setup {
224 0     0 1   _preamble("setup", 1);
225              
226 0 0         if ( -f $setupdone ) {
227 0           carp("$my_name: \"setup\" already done\n");
228 0           return 0;
229             }
230              
231 0           my $dir = File::Spec->rel2abs(File::Spec->curdir);
232 0           my $lib = _cf($sitelib, qw(Template TT2Site));
233 0           my $skel = _cf($lib, qw(setup data));
234              
235 0           unshift(@ARGV,
236             '-s', $skel,
237             '-d', $dir,
238             '-f', _cf($lib, qw(setup etc ttree.cfg)),
239             '--define', "dir=$dir",
240             '--define', "sitelib=". $lib,
241             '--define', "tmplsrc=src",
242             '--define', "debug=$debug");
243              
244 0           unshift(@ARGV, "perl", $ttree);
245              
246 0 0         warn("+ @ARGV\n") if $trace;
247 0           system $^X @ARGV;
248 0 0         croak("$my_name: ttree did not complete\n") if $?;
249 0 0         croak("$my_name: ttree did not complete\n")
250             unless -f _cf(qw(etc ttree.cfg));
251              
252 0           chmod(0666, _cf(qw(etc ttree.cfg)));
253 0           chmod(0666, _cf(qw(src css site.css)));
254 0           chmod(0666, _cf(qw(src debug.html)));
255              
256             # Provide some sample data.
257 0           foreach my $ss ( @samples ) {
258 0           my $fn = _cf(@$ss);
259 0           mkpath([dirname($fn)], 1, 0777);
260 0 0         if ( -e $fn ) {
261 0           warn("File $fn exists, not overwritten\n");
262 0           next;
263             }
264 0           warn("Copying sample $fn\n");
265 0           copy(_cf($lib, $fn), $fn);
266 0 0         chmod(0666, $fn)
267             or warn("Error copying $fn: $!\n");
268             }
269              
270 0           open(my $fh, ">$setupdone");
271              
272 0           return 0;
273             }
274              
275             INIT {
276 2     2   24 push(@cmds, "build");
277 2         11 $help{$cmds[-1]} = <
278             Runs the 'ttree' application to update the site files.
279             EOD
280             }
281             sub build {
282 0     0 1   _preamble("build");
283              
284 0           my (@args) = qw(-f etc/ttree.cfg);
285              
286 0           unshift(@args, "perl", "-Mlib=$sitelib", $ttree);
287 0 0         warn("+ @args\n") if $trace;
288 0           system $^X @args;
289 0 0         croak("$my_name: ttree did not complete\n$@") if $?;
290 0           return 0;
291             }
292              
293             INIT {
294 2     2   5 push(@cmds, "rebuild");
295 2         9 $help{$cmds[-1]} = <
296             Runs the 'ttree' application to completely rebuild
297             the site files.
298             EOD
299             }
300             sub rebuild {
301 0     0 1   _preamble("rebuild");
302              
303 0           my (@args) = qw(-a -f etc/ttree.cfg);
304              
305 0           unshift(@args, "perl", "-Mlib=$sitelib", $ttree);
306 0 0         warn("+ @args\n") if $trace;
307 0           system $^X @args;
308 0 0         croak("$my_name: ttree did not complete\n$@") if $?;
309 0           return 0;
310             }
311              
312             INIT {
313 2     2   6 push(@cmds, "fetch");
314 2         11 $help{$cmds[-1]} = <
315             Copies files from the TT2Site library to the local tree.
316             EOD
317             }
318             sub fetch {
319 0     0 0   _preamble("fetch");
320 0           my $lib = _cf($sitelib, qw(Template TT2Site));
321              
322 0           foreach my $file ( @ARGV ) {
323 0           my $f = _cf($lib, $file);
324 0 0         unless ( -f $f ) {
325 0           carp("File not in TT2Site library: $file");
326 0           next;
327             }
328 0           my $dir = dirname($file);
329 0           my $base = basename($file);
330 0 0         mkpath([$dir], 1, 0777) unless -d $dir;
331 0           copy($f, $file);
332 0           chmod(0666, $file);
333             }
334              
335 0           return 0;
336             }
337              
338             sub publish {
339 0     0 0   _preamble("publish");
340              
341 0           croak("$my_name: \"publish\" not yet implemented\n");
342 0           return 0;
343             }
344              
345             INIT {
346 2     2   5 push(@cmds, "clean");
347 2         31 $help{$cmds[-1]} = <
348             Cleans the generated HTML files, and editor backup files.
349             EOD
350             }
351             sub clean {
352 0 0 0 0 1   _preamble("clean") unless @_ && $_[0];
353              
354 0           rmtree(["html"], $verbose, 1);
355             find(sub {
356 0 0   0     if ( /~$/ ) {
357 0 0         warn("+ rm $File::Find::name\n") if $verbose;
358 0 0         unlink($_)
359             or warn("$File::Find::name: $!\n");
360             }
361 0           }, ".");
362 0           return 0;
363             }
364              
365             INIT {
366 2     2   5 push(@cmds, "realclean");
367 2         10 $help{$cmds[-1]} = <
368             Cleans the generated HTML files, editor backup files,
369             and all files originally installed using the 'setup'
370             command.
371              
372             You'll be asked for confirmation before your files are
373             removed.
374             EOD
375             }
376             sub realclean {
377 0     0 1   _preamble("realclean");
378 0           print STDERR ("WARNING: ",
379             "Your customisations to copied files will be lost!\n",
380             "Hit Enter to continue, Control-C to cancel ");
381 0           ;
382              
383 0           clean(1);
384              
385 0           my $lib = _cf($sitelib, qw(Template TT2Site));
386 0           my @files;
387             my @chfiles;
388 2     2   18 use Cwd;
  2         4  
  2         3409  
389 0           my $cur = getcwd;
390 0           chdir(_cf($sitelib, qw(Template TT2Site setup data)));
391             find(sub {
392 0 0   0     return unless -f $_;
393 0 0         return unless -f _cf($cur, $File::Find::name);
394 0 0         push(@{_differ($_, _cf($cur, $File::Find::name))
  0            
395             ? \@chfiles : \@files}, $File::Find::name);
396 0           }, ".");
397 0           chdir($cur);
398              
399 0 0         if ( @chfiles ) {
400 0           print STDERR ("WARNING: ",
401             "The following files were modified:\n",
402             "\t", join("\n\t", @chfiles), "\n",
403             "Your changes will be lost!\n",
404             "Hit Enter to continue, Control-C to cancel ");
405 0           ;
406             }
407              
408 0           foreach my $file ( @files, @chfiles, $setupdone ) {
409 0           warn("+ rm $file\n");
410 0           unlink($file);
411             }
412              
413             # Remove sample data only if not modified.
414 0           foreach my $ss ( @samples ) {
415 0           my $fn = _cf(@$ss);
416 0 0         if ( _differ(_cf($lib, $fn), $fn) ) {
417 0           warn("$fn has been modified -- not removed\n");
418 0           next;
419             }
420 0           warn("+ rm $fn\n");
421 0 0         unlink($fn)
422             or warn("Error removing $fn: $!\n");
423             }
424              
425 0           foreach my $dir ( _cf(qw(src images)),
426             _cf(qw(src css)),
427             _cf(qw(src)),
428             _cf(qw(lib config)),
429             _cf(qw(lib)),
430             _cf(qw(etc)) ) {
431 0 0         rmdir($dir) && warn("+ rmdir $dir\n");
432             }
433              
434 0           return 0;
435             }
436              
437             ################ Helpers ################
438              
439             sub command_help {
440 0     0 0   my $self = shift;
441              
442 0           foreach my $cmd ( @cmds ) {
443 0           my $tag = "$cmd\t";
444 0           foreach ( split(/\n/, $help{$cmd}) ) {
445 0           print STDOUT ($tag, $_, "\n");
446 0           $tag = "\t";
447             }
448 0           print STDOUT "\n";
449             }
450 0           exit(0);
451             }
452              
453             sub _find_ttree {
454 0     0     $ttree = "ttree";
455 0           foreach my $p ( File::Spec->path ) {
456 0 0         if ( -s "$p/$ttree.pl" ) {
457 0           $ttree = "$p/$ttree.pl";
458 0           last;
459             }
460 0 0 0       if ( -s "$p/$ttree" && -x _ ) {
461 0           $ttree = "$p/$ttree";
462 0           last;
463             }
464             }
465 0 0         if ( $ttree eq "ttree" ) {
466 0           croak("$my_name: Could not find ttree or ttree.pl in PATH\n")
467             }
468             else {
469 0 0         open (my $f, "<$ttree") or die("Cannot open $ttree: $!\n");
470 0           my $line = <$f>;
471 0           close($f);
472 0 0         if ( $line !~ m;^#!.*\bperl\b; ) {
473 0           croak("Found ttree in $ttree, but it doesn't seem".
474             " to be a Perl program.\n",
475             "TT2Site needs the Perl program to execute.\n",
476             "Please make it available.\n");
477             }
478             }
479             }
480              
481             sub _check_setup {
482 0 0   0     croak("$my_name: Please execute \"setup\" first\n")
483             unless -f $setupdone;
484             }
485              
486 0     0     sub _cf { File::Spec->catfile(@_) }
487              
488             sub _check_lib {
489              
490 0     0     my $lib = $ENV{TT2SITE_LIB};
491 0 0         if ( $lib ) {
492 0 0         unless ( -f _cf($lib, qw(Template TT2Site.pm)) ) {
493 0           die("$my_name: Installation problem!\n",
494             "Cannot find Template::TT2Site in $lib\n",
495             "Please verify your installation, or set environment variable ",
496             "TT2SITE_LIB to the directory containing Template/TT2Site.pm\n");
497             }
498 0           $sitelib = $lib;
499             }
500             else {
501 0           foreach $lib ( @INC ) {
502             # warn("Trying: " . _cf($lib, qw(Template TT2Site.pm)) . "\n");
503 0 0         $sitelib = $lib, last if -f _cf($lib, qw(Template TT2Site.pm))
504             }
505             }
506              
507 0 0         unless ( -f _cf($sitelib, qw(Template TT2Site.pm)) ) {
508 0           die("$my_name: Installation problem!\n",
509             "Cannot find Template::TT2Site in $sitelib or \@INC\n",
510             "Please verify your installation, or set environment variable ",
511             "TT2SITE_LIB to the directory containing Template/TT2Site.pm\n");
512             }
513             }
514              
515             sub _differ {
516             # Perl version of the 'cmp' program.
517             # Returns 1 if the files differ, 0 if the contents are equal.
518 0     0     my ($old, $new) = @_;
519 0 0         unless ( open (F1, $old) ) {
520 0           print STDERR ("$old: $!\n");
521 0           return 1;
522             }
523 0 0         unless ( open (F2, $new) ) {
524 0           print STDERR ("$new: $!\n");
525 0           return 1;
526             }
527 0           my ($buf1, $buf2);
528 0           my ($len1, $len2);
529 0           binmode(F1);
530 0           binmode(F1);
531 0           while ( 1 ) {
532 0           $len1 = sysread (F1, $buf1, 10240);
533 0           $len2 = sysread (F2, $buf2, 10240);
534 0 0 0       return 0 if $len1 == $len2 && $len1 == 0;
535 0 0 0       return 1 if $len1 != $len2 || ( $len1 && $buf1 ne $buf2 );
      0        
536             }
537             }
538              
539             ################ Command Line Options ################
540              
541 2     2   3295 use Getopt::Long 2.00;
  2         32484  
  2         67  
542              
543             sub _options {
544              
545 0 0   0     GetOptions(verbose => \$verbose,
546              
547             # development options
548             test => \$test,
549             trace => \$trace,
550             debug => \$debug)
551             or _usage(2);
552              
553             # Post-processing.
554 0   0       $trace |= ($debug || $test);
555             }
556              
557             sub _usage {
558 0     0     my ($ret) = (@_);
559 0           print STDERR ("Commands: ", join(", ", @cmds), ".\n\n",
560             "Options:\n\n",
561             " --verbose increase verbosity\n");
562 0 0         exit($ret) if defined $ret;
563             }
564              
565             1;