installer (part 2): more work
[koha-equinox.git] / Makefile.PL
1 # Copyright 2007 MJ Ray
2 #
3 # This file is part of Koha.
4 #
5 # Koha is free software; you can redistribute it and/or modify it under the
6 # terms of the GNU General Public License as published by the Free Software
7 # Foundation; either version 2 of the License, or (at your option) any later
8 # version.
9 #
10 # Koha is distributed in the hope that it will be useful, but WITHOUT ANY
11 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
12 # A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU General Public License along with
15 # Koha; if not, write to the Free Software Foundation, Inc., 59 Temple Place,
16 # Suite 330, Boston, MA  02111-1307 USA
17 #
18 # Current maintainer MJR http://mjr.towers.org.uk/
19 # See http://www.koha.org/wiki/?page=KohaInstaller
20 #
21
22 use strict;
23 use warnings;
24 use ExtUtils::MakeMaker;
25 use POSIX;
26 use File::Spec;
27
28 my $DEBUG = 0;
29 die "perl 5.6.1 or later required" unless ($] >= 5.006001);
30
31 # Hash up directory structure & files beginning with the directory we were called from (should be the base of koha)...
32
33 my $dirtree = hashdir('.');
34 my %result = ();
35
36 =head1 NAME
37
38 Makefile.PL - Koha packager and installer
39
40 =head1 SYNOPSIS
41
42 =head2 BASIC INSTALLATION
43
44         perl Makefile.PL
45         make
46         sudo make install
47
48 =head2 PACKAGING RELEASE TARBALLS
49
50         make manifest tardist
51         make manifest zipdist
52
53 =head2 CLEANING UP
54
55         make clean
56
57 =head1 DESCRIPTION
58
59 This is a packager and installer that uses
60 ExtUtils::MakeMaker, which is fairly common
61 on perl systems.
62 As well as building tar or zip files
63 and installing with the above commands,
64 it allows us to check pre-requisites
65 and generate configuration files.
66
67 =head1 VARIABLES
68
69 =head2 NAME, VERSION_FROM, ABSTRACT, AUTHOR
70
71 Basic metadata about this software.
72
73 =head2 NO_META
74
75 Suppress generation of META.yml file.
76
77 =head2 PREREQ_PM
78
79 Hash of perl modules and versions required.
80
81 =head2 PM
82
83 Hash of file mappings
84
85 =head2 CONFIGURE
86
87 Maybe use prompt() here in future to get configuration values 
88 interactively at installation time.
89
90 =head2 PL_FILES
91
92 This is a hash of PL scripts to run after installation and
93 the files to ask them to generate.
94 Maybe use the values from CONFIGURE
95 to generate initial configuration files in future.
96
97 =cut
98
99 =head2 target_map
100
101 This is a hash mapping directories and files in the
102 source tree to installation target directories.  The rules
103 for this mapping are:
104
105 =over 4
106
107 =item If a directory or file is specified, it and its
108 contents will be copied to the installation target directory.
109
110 =item If a subdirectory of a mapped directory is specified,
111 its target overrides the parent's target for that subdirectory.
112
113 =item The value of each map entry may either be a scalar containing 
114 one target or a reference to an array of targets, in which case
115 the directory or file is copied to each target.
116
117 =item Any files at the top level of the source tree that are
118 not included in the map will not be installed.
119
120 =item Any directories at the top level of the source tree
121 that are not included in the map will be installed in
122 INTRANET_CGI_DIR.  This is a sensible default given the
123 current organization of the source tree, but (FIXME) it
124 would be better to reorganize the source tree to better
125 match the installation system, to allow adding new directories
126 without having to adjust Makefile.PL each time.  The idea
127 is to make the C<$target_map> hash as minimal as possible.
128
129 =back
130
131 The permitted installation targets are:
132
133 =over 4
134
135 =item INTRANET_CGI_DIR 
136
137 CGI scripts for intranet (staff) interface.
138
139 =item INTRANET_TMPL_DIR
140
141 HTML templates for the intranet interface.
142
143 =item INTRANET_WWW_DIR
144
145 HTML files, images, etc. for DocumentRoot for the intranet interface.
146
147 =item OPAC_CGI_DIR
148
149 CGI scripts for OPAC (public) interface.
150
151 =item OPAC_TMPL_DIR
152
153 HTML templates for the OPAC interface.
154
155 =item OPAC_WWW_DIR
156
157 HTML files, images, etc. for DocumentRoot for the OPAC interface.
158
159 =item PERL_MODULE_DIR
160
161 Perl modules (at present just the C4 modules) that are intimately
162 tied to Koha.  Depending on the installation options, these
163 may or may not be installed one of the standard directories
164 in Perl's default @LIB.
165
166 =item KOHA_CONF_DIR
167
168 Directory for Koha configuration files.
169
170 =item ZEBRA_CONF_DIR
171
172 Directory for Zebra configuration files.
173
174 =item ZEBRA_LOCK_DIR
175
176 Directory for Zebra's lock files.
177
178 =item ZEBRA_DATA_DIR
179
180 Directory for Zebra's data files.
181
182 =item ZEBRA_RUN_DIR
183
184 Directory for Zebra's UNIX-domain sockets.
185
186 =item EXAMPLE_DIR
187
188 Directory for example configuration files.  This directory
189 exists primarily to make it easier to change the
190 MARC format or language of the active Zebra
191 indexes.
192
193 =item SCRIPT_DIR
194
195 Directory for command-line scripts and daemons.
196
197 =item MAN_DIR
198
199 Directory for man pages created from POD -- will mostly
200 contain information of interest to Koha developers.
201
202 =item DOC_DIR
203
204 Directory for Koha documentation accessed from the 
205 command-line, e.g., READMEs.
206
207 =item LOG_DIR
208
209 Directory for Apache and Zebra logs produced by Koha.
210
211 =item NONE
212
213 This is a dummy target used to explicitly state
214 that a given file or directory is not to be installed.
215 This is used either for parts of the installer itself
216 or for development tools that are not applicable to a
217 production installation.
218
219 =back
220
221 =cut
222
223 my $target_map = {
224   './about.pl'                  => 'INTRANET_CGI_DIR',
225   './acqui'                     => 'INTRANET_CGI_DIR',
226   './admin'                     => 'INTRANET_CGI_DIR',
227   './authorities'               => 'INTRANET_CGI_DIR',
228   './C4'                        => 'PERL_MODULE_DIR',
229   './C4/SIP/t'                  => 'NONE',
230   './C4/SIP/koha_test'          => 'NONE',
231   './C4/tests'                  => 'NONE',
232   './catalogue'                 => 'INTRANET_CGI_DIR',
233   './cataloguing'               => 'INTRANET_CGI_DIR',
234   './changelanguage.pl'         => [ 'INTRANET_CGI_DIR', 'OPAC_CGI_DIR' ],
235   './check_sysprefs.pl'         => 'NONE',
236   './circ'                      => 'INTRANET_CGI_DIR',
237   './edithelp.pl'               => 'INTRANET_CGI_DIR',
238   './etc'                       => { target => 'KOHA_CONF_DIR', trimdir => -1 },
239   './etc/zebradb'               => { target => 'ZEBRA_CONF_DIR', trimdir => -1 },
240   './installer-CPAN.pl'         => 'NONE',
241   './installer'                 => 'INTRANET_CGI_DIR',
242   './koha-tmpl'                 => 'NONE',
243   './koha-tmpl/intranet-tmpl'   => {target => 'INTRANET_TMPL_DIR', trimdir => -1},
244   './koha-tmpl/opac-tmpl'       => {target => 'OPAC_TMPL_DIR', trimdir => -11},
245   './koha-version.pl'           => 'INTRANET_CGI_DIR', # FIXME this may need to be in OPAC_CGI_DIR as well, with an update to C4::Context
246   './labels'                    => 'INTRANET_CGI_DIR',
247   './mainpage.pl'               => 'INTRANET_CGI_DIR',
248   './Makefile.PL'               => 'NONE',
249   './MANIFEST.SKIP'             => 'NONE',
250   './members'                   => 'INTRANET_CGI_DIR',
251   './misc'                      => { target => 'SCRIPT_DIR', trimdir => -1 }, 
252   './misc/info'                 => { target => 'DOC_DIR', trimdir => 2 },
253   './misc/release notes'        => { target => 'DOC_DIR', trimdir => 2 },
254   './misc/translator'           => { target => 'EXAMPLE_DIR', trimdir => 2 }, 
255   './misc/installer_devel_notes' => 'NONE',
256   './opac'                      => 'OPAC_CGI_DIR',
257   './README.txt'                => 'NONE',
258   './reports'                   => 'INTRANET_CGI_DIR',
259   './reserve'                   => 'INTRANET_CGI_DIR',
260   './reviews'                   => 'INTRANET_CGI_DIR',
261   './rewrite-config.PL'         => 'NONE',
262   './reviews'                   => 'INTRANET_CGI_DIR',
263   './rss'                       => 'NONE', # FIXME deal with a little later
264   './serials'                   => 'INTRANET_CGI_DIR',
265   './sms'                       => 'INTRANET_CGI_DIR',
266   './suggestion'                => 'INTRANET_CGI_DIR',
267   './svc'                       => 'INTRANET_CGI_DIR',
268   './t'                         => 'NONE',
269   './tmp'                       => 'NONE', # FIXME deal with later
270   './tools'                     => 'INTRANET_CGI_DIR',
271   './virtualshelves'            => 'INTRANET_CGI_DIR',
272   # ignore files and directories created by the install itself
273   './pm_to_blib'                => 'NONE',
274   './blib'                      => 'NONE',
275 };
276
277 =head1 CONFIGURATION OPTIONS
278
279 The following configuration options are used by the installer.
280
281 =over 4
282
283 =item INSTALL_MODE
284
285 Specifies whether installation will be FHS-compliant (default,
286 assumes user has root), put everything under
287 a single directory (for users installing on a web host
288 that allows CGI scripts and a MySQL database but not root 
289 access), or development (for a developer who wants to run
290 Koha from a git clone with no fuss).
291
292 =item INSTALL_BASE
293
294 Directory under which most components will go.  Default
295 value will vary depending on INSTALL_MODE.
296
297 =item INSTALL_ZEBRA
298
299 Whether to install Zebra configuration files and data
300 directories.
301
302 =item ZEBRA_MARC_FORMAT
303
304 Specifies format of MARC records to be indexed by Zebra.
305
306 =item ZEBRA_LANGUAGE
307
308 Specifies primary language of records that will be 
309 indexed by Zebra.
310
311 =back
312
313 =cut
314
315 # default configuration options
316 my %config_defaults = (
317   'INSTALL_MODE'      => 'standard',
318   'INSTALL_BASE'      => '/usr/share/koha',
319   'INSTALL_ZEBRA'     => 'yes',
320   'ZEBRA_MARC_FORMAT' => 'marc21',
321   'ZEBRA_LANGUAGE'    => 'en',
322 );
323
324 # valid values for certain configuration options
325 my %valid_config_values = (
326   'INSTALL_MODE'  => { 'standard' => 1, 'single' => 1, 'dev' => 1 },
327   'INSTALL_ZEBRA' => { 'yes' => 1, 'no' => 1 },
328   'ZEBRA_MARC_FORMAT' => { 'marc21' => 1, 'unimarc' => 1 }, # FIXME should generate from contents of distributation
329   'ZEBRA_LANGUAGE'    => { 'en' => 1, 'fr' => 1 }, # FIXME should generate from contents of distribution
330 );
331
332 my %config = get_configuration(\%config_defaults, \%valid_config_values);
333 my %target_directories = get_target_directories(\%config);
334 my $file_map = {};
335 get_file_map($target_map, $dirtree, $file_map);
336
337 WriteMakefile(
338     NAME => 'koha',
339     #VERSION => strftime('2.9.%Y%m%d%H',gmtime),
340     VERSION_FROM => 'C4/Context.pm',
341     ABSTRACT => 'Award-winning integrated library system (ILS) and Web OPAC',
342     AUTHOR => 'Koha Developers <koha-devel@nongnu.org>',
343     NO_META => 1,
344     PREREQ_PM => {
345 'CGI' => 3.15,
346 'CGI::Carp' => 1.29,
347 'CGI::Session' => '4.10',
348 'Class::Factory::Util' => 1.7,
349 'Class::Accessor' => 0.30,
350 'DBD::mysql' => 3.0008,
351 'DBI' => 1.53,
352 'Data::Dumper' => 2.121_08,
353 'Date::Calc' => 5.4,
354 'Date::Manip' => 5.44,
355 'Digest::MD5' => 2.36,
356 'File::Temp' => 0.16,
357 'GD::Barcode::UPCE' => 1.1,
358 'Getopt::Long' => 2.35,
359 'Getopt::Std' => 1.05,
360 'HTML::Template::Pro' => 0.65,
361 'HTTP::Cookies' => 1.39,
362 'HTTP::Request::Common' => 1.26,
363 'LWP::Simple' => 1.41,
364 'LWP::UserAgent' => 2.033,
365 'Lingua::Stem' => 0.82,
366 'List::Util' => 1.18,
367 'Locale::Language' => 2.07,
368 'MARC::Charset' => 0.98,
369 'MARC::Crosswalk::DublinCore' => 0.03,
370 'MARC::File::XML' => 0.88,
371 'MARC::Record' => 2.00,
372 'MIME::Base64' => 3.07,
373 'MIME::QuotedPrint' => 3.07,
374 'Mail::Sendmail' => 0.79,
375 'PDF::API2' => 2.000,
376 'PDF::API2::Page' => 2.000,
377 'PDF::API2::Util' => 2.000,
378 'PDF::Reuse' => 0.33,
379 'PDF::Reuse::Barcode' => 0.05,
380 'POSIX' => 1.09,
381 'Schedule::At' => 1.06,
382 'Term::ANSIColor' => 1.10,
383 'Test' => 1.25,
384 'Test::Harness' => 2.56,
385 'Test::More' => 0.62,
386 'Text::CSV' => 0.01,
387 'Text::CSV_XS' => 0.32,
388 'Text::Wrap' => 2005.082401,
389 'Time::HiRes' => 1.86,
390 'Time::localtime' => 1.02,
391 'Unicode::Normalize' => 0.32,
392 'XML::Dumper' => 0.81,
393 'XML::LibXML' => 1.59,
394 'XML::SAX::ParserFactory' => 1.01,
395 'XML::Simple' => 2.14,
396 'XML::RSS' => 1.31,
397 'ZOOM' => 1.16,
398         },
399
400         # File tree mapping
401         PM => $file_map,
402
403     # Man pages generated from POD
404     INSTALLMAN1DIR => File::Spec->catdir($target_directories{'MAN_DIR'}, 'man1'),
405     INSTALLMAN3DIR => File::Spec->catdir($target_directories{'MAN_DIR'}, 'man3'),
406
407         # disable tests
408         'test' => {TESTS => 't/dummy.t'},
409
410 #   CONFIGURE => sub {
411 #     # Ask for options with prompt($question,$default) calls here?
412 #     return { macro => { 'export TEST' => '755' } }
413 #     },
414
415    PL_FILES => { # generator => target(s)
416       'rewrite-config.PL' => [
417          'blib/KOHA_CONF_DIR/koha-conf.xml',
418          'blib/KOHA_CONF_DIR/koha-httpd.conf',
419          'blib/ZEBRA_CONF_DIR/etc/passwd',
420          'blib/ZEBRA_CONF_DIR/zebra-biblios.cfg',
421          'blib/ZEBRA_CONF_DIR/zebra-authorities.cfg'
422          ]
423    }
424 #     'opac/getfromintranet.PL' => ['$(INST_LIBDIR)/opac/cgi-bin/detail.pl','$(INST_LIBDIR)/opac/cgi-bin/moredetail.pl','$(INST_LIBDIR)/opac/cgi-bin/search.pl','$(INST_LIBDIR)/opac/cgi-bin/subjectsearch.pl','$(INST_LIBDIR)/opac/cgi-bin/logout.pl'],
425 #     'misc/koha.conf.PL' => '$(INST_LIBDIR)/../etc/koha.conf',
426 #     'misc/apache-koha.conf.PL' => '$(INST_LIBDIR)/../etc/apache-koha.conf',
427 #     'misc/koha.sql.PL' => '$(INST_LIBDIR)/intranet/scripts/koha.sql',
428 #     'z3950/z3950-daemon-options.PL' => '$(INST_LIBDIR)/intranet/scripts/z3950daemon/z3950-daemon-options',
429 #     # fake target to check permissions
430 #     'misc/chmod.PL' => '$(INST_LIBDIR)/fake-target'
431 #     }
432    # need to set ownerships
433    # need to load koha.sql
434    # need to link koha-httpd.conf
435    # need to start z3950-daemon
436 );
437
438 =head1 FUNCTIONS
439
440 =head2 hashdir
441
442 This function recurses through the directory structure and builds
443 a hash of hashes containing the structure with arrays holding filenames.
444 This directory hashing routine was taken from BrowserUK @ http://www.perlmonks.org/?node_id=219919
445
446 =cut
447
448 sub hashdir{
449     my $dir = shift;
450     opendir my $dh, $dir or die $!;
451     my $tree = {}->{$dir} = {};
452     while( my $file = readdir($dh) ) {
453         next if $file =~ m/^\.{1,2}/;
454         my $path = $dir .'/' . $file;
455         $tree->{$file} = hashdir($path), next if -d $path;
456         push @{$tree->{'.'}}, $file;
457     }
458     return $tree;
459 }
460
461 =head2 get_file_map 
462
463 This function combines the target_map and file hash to
464 map each source file to its destination relative to
465 the set of installation targets.
466
467 Output will be a hash mapping from each source file
468 to its destination value, like this:
469
470 'mainpage.pl' => '$(INTRANET_CGI_DIR)/mainpage.pl'
471
472 =cut
473
474 sub get_file_map {
475     my $target_map = shift;
476     my $dirtree = shift;
477     my $file_map = shift;
478     my $curr_path = @_ ? shift : ['.'];
479
480     # Traverse the directory tree.
481     # For each file or directory, identify the
482     # most specific match in the target_map
483     foreach my $dir (sort keys %{ $dirtree }) {
484         if ($dir eq '.') {
485             # deal with files in directory
486             foreach my $file (sort @{ $dirtree->{$dir} }) {
487                 my $targetdir = undef;
488                 my $matchlevel = undef;
489                 # first, see if there is a match on this specific
490                 # file in the target map
491                 my $filepath = join("/", @$curr_path, $file);
492                 if (exists $target_map->{$filepath}) {
493                     $targetdir = $target_map->{$filepath};
494                     $matchlevel = scalar(@$curr_path) + 1;
495                 } else {
496                     # no match on the specific file; look for
497                     # a directory match
498                     for (my $i = scalar(@$curr_path) - 1; $i >= 0; $i--)  {
499                         my $dirpath = join("/", @$curr_path[0..$i]);
500                         if (exists $target_map->{$dirpath}) {
501                             $targetdir = $target_map->{$dirpath};
502                             $matchlevel = $i + 1;
503                             last;
504                         }
505                     }
506                 }
507                 if (defined $targetdir) {
508                      _add_to_file_map($file_map, $targetdir, $curr_path, $file, $matchlevel);
509                 } else {
510                     my $path = join("/", @$curr_path);
511                     print "failed to map: $path/$file\n" if $DEBUG;
512                 }
513             }
514         } else {
515             # dealing with subdirectory
516             push @$curr_path, $dir;
517             get_file_map($target_map, $dirtree->{$dir}, $file_map, $curr_path);
518             pop @$curr_path;
519         }
520     }
521 }
522
523 sub _add_to_file_map {
524     my $file_map = shift;
525     my $targetdir = shift;
526     my $curr_path = shift;
527     my $file = shift;
528     my $matchlevel = shift;
529     my $dest_path = @_ ? shift : $curr_path;
530
531     # The target can be one of the following:
532     # 1. scalar representing target symbol
533     # 2. hash ref containing target and trimdir keys
534     # 3. array ref containing list of #1 and #2
535     #
536     # Consequently, this routine traverses this structure,
537     # calling itself recursively, until it deals with
538     # all of the scalar target symbols.
539     if (ref $targetdir eq 'ARRAY') {
540         foreach my $subtarget (sort @$targetdir) {
541             _add_to_file_map($file_map, $subtarget, $curr_path, $file, $matchlevel);
542         }
543     } elsif (ref $targetdir eq 'HASH') {
544         my $subtarget = $targetdir->{target};
545         if (exists $targetdir->{trimdir}) {
546             # if we get here, we've specified that
547             # rather than installing the file to
548             # $(TARGET)/matching/dirs/subdirs/file,
549             # we want to install it to
550             # $(TARGET)/subdirs/file
551             #
552             # Note that this the only place where
553             # $matchlevel is used.
554             my @new_dest_path = @$dest_path;
555             if ($targetdir->{trimdir} == -1)  {
556                 splice @new_dest_path, 0, $matchlevel;
557             } else {
558                 splice @new_dest_path, 0, $targetdir->{trimdir};
559             }
560             _add_to_file_map($file_map, $subtarget, $curr_path, $file, $matchlevel, \@new_dest_path);
561         } else {
562             # actually getting here means that the
563             # target was unnecessarily listed
564             # as a hash, but we'll forgive that
565             _add_to_file_map($file_map, $subtarget, $curr_path, $file, $matchlevel);
566         }
567     } elsif ($targetdir ne 'NONE' and $targetdir ne '') {
568         my $source = File::Spec->catfile(@$curr_path, $file);
569         return if $source =~ / /; #FIXME
570         #my $destination = File::Spec->catfile("\$($targetdir)", @$dest_path, $file);
571         my $destination = File::Spec->catfile('blib', $targetdir, @$dest_path, $file);
572         #print "$source => $destination\n"; # DEBUG
573         $file_map->{$source} = $destination;
574     }
575 }
576
577 =head2 unhashdir
578
579 This function unhashes the hash of hashes generated by hashdir().
580 This directory unhashing routine is the personal work of Chris Nighswonger (fbcit).
581 Modified here to build koha makefile. It lists all files and where to install each one.
582 It then returns a hash reference suitable for the PM variable above.
583
584 =cut
585
586 sub unhashdir{
587         my $dirhash = shift;
588         my $dirlevel = shift;
589         my $toplevel = $dirlevel;
590         for my $k1 ( sort keys %$dirhash ) {
591                 if ($k1 ne '.' && $k1 ne '') {
592                         $dirlevel = ( $dirlevel ? $dirlevel . '/' . $k1 : $k1 );
593                         &unhashdir($dirhash->{ $k1 }, $dirlevel);
594                         $dirlevel = $toplevel;
595                 }
596                 elsif ( $k1 eq '.' ) {
597                         foreach my $file ( @{$dirhash->{ $k1 }} ) {
598 #                               TODO: There are some hacks here that may be able to be improved... -fbcit
599                                 if ( $file =~ /^./ ) { next; } # skip hidden files and directories.
600
601                                 elsif ( $file =~ /\.pm/ && $dirlevel =~ /C4/ ) { $result{ ($dirlevel ? $dirlevel . '/' . $file : $file) } = '$(INST_LIBDIR)/' . ($dirlevel ? $dirlevel . '/' . $file : $file); } # C4/*.pm is copied to perl's lib namespace.
602
603                                 elsif ( $dirlevel !~ /koha-tmpl/ && $dirlevel =~ /(installer|errors)/ ) { $result{ ($dirlevel ? $dirlevel . '/' . $file : $file) } = '$(PREFIX)/lib/cgi-bin/koha/' . ($dirlevel ? $dirlevel . '/' . $file : $file); } # error templates are copied to $(PREFIX)/lib/cgi-bin/koha/
604
605                                 elsif ( $dirlevel =~ /koha-tmpl/ && $dirlevel !~ /errors/ ) { $result{ ($dirlevel ? $dirlevel . '/' . $file : $file) } = '$(INST_LIBDIR)/koha/templates/' . ($dirlevel ? $dirlevel . '/' . $file : $file); } # error templates are copied to $(INST_LIBDIR)/koha/templates/
606
607                                 elsif ( $dirlevel =~ /(misc|rss)/ ) { $result{ ($dirlevel ? $dirlevel . '/' . $file : $file) } = '$(INST_LIBDIR)/koha/' . ($dirlevel ? $dirlevel . '/' . $file : $file); } # misc & rss are copied to koha,
608
609 #                               elsif ( $dirlevel =~ /(intranet-tmpl|opac-tmpl)/ ) { $result{ ($dirlevel ? $dirlevel . '/' . $file : $file) } = '$(INST_LIBDIR)/koha/templates/' . ($dirlevel ? $dirlevel . '/' . $file : $file); } # Templates are copied to koha/templates,
610
611                                 elsif ( $file !~ /\.pl/ && $dirlevel =~ /etc/ ) { $result{ ($dirlevel ? $dirlevel . '/' . $file : $file) } = '$(PREFIX)/share/koha/' . ($dirlevel ? $dirlevel . '/' . $file : $file); } # Misc etc to koha/etc
612
613                                 elsif ( $file =~ /\.pl/ ) { $result{ ($dirlevel ? $dirlevel . '/' . $file : $file) } = '$(PREFIX)/lib/cgi-bin/koha/' . ($dirlevel ? $dirlevel . '/' . $file : $file); } # CGIs are copied to $(PREFIX)/lib/cgi-bin/koha/ print $result{ ($dirlevel ? $dirlevel . '/' . $file : $file)},"\n\n"; 
614
615                         }
616                         next;
617                 }
618         }
619         return \%result;
620 }
621
622 =head2 get_configuration_options
623
624 This prompts the user for various configuration options.
625
626 =cut
627
628 sub get_configuration {
629   my $defaults = shift;
630   my $valid_values = shift;
631   my %config = ();
632
633   my $msg = q(
634 By default, Koha can be installed in one of three ways:
635
636 standard: Install files in conformance with the Filesystem
637           Hierarchy Standard (FHS).  This is the default mode
638           and should be used when installing a production
639           Koha system.  On Unix systems, root access is 
640           needed to complete a standard installation.
641
642 single:   Install files under a single directory.  This option
643           is useful for installing Koha without root access, e.g.,
644           on a web host that allows CGI scripts and MySQL databases
645           but requires the user to keep all files under the user's
646           HOME directory.
647
648 dev:      Create a set of symbolic links and configuration files to
649           allow Koha to run directly from the source distribution.
650           This mode is useful for developers who want to run
651           Koha from a git clone.
652
653 Please choose the installation mode);
654     $msg .= _add_valid_values_disp('INSTALL_MODE', $valid_values);
655     $config{'INSTALL_MODE'} = _get_value('INSTALL_MODE', $msg, $defaults->{'INSTALL_MODE'}, $valid_values);
656
657     # set message and default value for INSTALL_BASE
658     # depending on value of INSTALL_MODE
659     my $install_base_default = $defaults->{'INSTALL_BASE'};
660     if ($config{'INSTALL_MODE'} eq 'dev') {
661         $msg = q(
662 Please specify the directory in which to install Koha's
663 active configuration files and (if applicable) the
664 Zebra database.  Koha's CGI scripts and templates will
665 be run from the current directory.);
666         # FIXME - home directory portability consideration apply
667         $install_base_default = (exists $ENV{'HOME'}) ? "$ENV{'HOME'}/koha-dev" : "/usr/share/koha-dev";
668     } elsif ($config{'INSTALL_MODE'} eq 'single') {
669         $msg = "\nPlease specify the directory in which to install Koha";
670         # FIXME -- we're assuming under a 'single' mode install
671         # that user will likely want to install under the home
672         # directory.  This is OK in and of itself, but we should
673         # use File::HomeDir to locate the home directory portably.  
674         # This is deferred for now because File::HomeDir is not yet
675         # core.
676         $install_base_default = (exists $ENV{'HOME'}) ? "$ENV{'HOME'}/koha" : "/usr/share/koha";
677     } else {
678         # must be standard
679         $msg = q(
680 Please specify the directory under which most Koha files 
681 will be installed.
682
683 Note that if you are planning in installing more than 
684 one instance of Koha, you may want to modify the last
685 component of the directory path, which will be used
686 as the package name in the FHS layout.);
687     }
688     $config{'INSTALL_BASE'} = _get_value('INSTALL_BASE', $msg, $install_base_default, $valid_values);
689     $config{'INSTALL_BASE'} = File::Spec->rel2abs($config{'INSTALL_BASE'});
690
691     $msg = q(
692 Koha can use the Zebra search engine for high-performance
693 searching of bibliographic and authority records.  If you
694 have installed the Zebra software and would like to use it,
695 please answer 'yes' to the following question.  Otherwise, 
696 Koha will default to using its internal search engine.
697
698 Please specify whether to install the Zebra configuration files);
699     $msg .= _add_valid_values_disp('INSTALL_ZEBRA', $valid_values);
700     $config{'INSTALL_ZEBRA'} = _get_value('INSTALL_ZEBRA', $msg, $defaults->{'INSTALL_ZEBRA'}, $valid_values);
701
702     if ($config{'INSTALL_ZEBRA'} eq 'yes') {
703         $msg = q(
704 Since you've chosen to use Zebra with Koha,
705 you must specify the primary MARC format of the
706 records to be indexed by Zebra.
707
708 Koha provides Zebra configuration files for MARC 21
709 and UNIMARC.
710
711 Please specify the MARC format);
712         $msg .= _add_valid_values_disp('ZEBRA_MARC_FORMAT', $valid_values);
713         $config{'ZEBRA_MARC_FORMAT'} = _get_value('ZEBRA_MARC_FORMAT', $msg, $defaults->{'ZEBRA_MARC_FORMAT'}, $valid_values);
714         $msg = q(
715 Koha supplies Zebra configuration files tuned for
716 searching either English (en) or French (fr) MARC
717 records.
718
719 Please specify the primary language of the MARC records);
720         $msg .= _add_valid_values_disp('ZEBRA_LANGUAGE', $valid_values);
721         $config{'ZEBRA_LANGUAGE'} = _get_value('ZEBRA_LANGUAGE', $msg, $defaults->{'ZEBRA_LANGUAGE'}, $valid_values);
722     }
723     return %config;
724 }
725
726 sub _add_valid_values_disp {
727     my $key = shift;
728     my $valid_values = shift;
729    
730     my $disp = "";
731     if (exists $valid_values->{$key}) {
732         $disp = " (" . join(", ", sort keys %{ $valid_values->{$key} }) . ")";
733     }
734     return $disp;
735 }
736
737 sub _get_value {
738     my $key = shift;
739     my $msg = shift;
740     my $default = shift;
741     my $valid_values = shift;
742
743     my $val = prompt($msg, $default);
744     while (exists $valid_values->{$key} and not exists $valid_values->{$key}->{$val}) {
745         my $retry_msg = "Value '$val' is not a valid option.\n";
746         $retry_msg .= "Please enter a value";
747         $retry_msg .= _add_valid_values_disp($key, $valid_values);
748         $val = prompt($retry_msg, $default);
749     }
750     return $val;
751 }
752
753 =head2 get_target_directories 
754
755 Creates a hash mapping from symbols for installation target
756 directories to actual directory paths.
757
758 =cut
759
760 sub get_target_directories {
761     my $config = shift;
762
763     my $base = $config->{'INSTALL_BASE'};
764     my $mode = $config->{'INSTALL_MODE'};
765
766     # get last component of install base directory
767     # to treat as package name
768     my ($volume, $directories, $file) = File::Spec->splitpath($base, 1);
769     my @basedir = File::Spec->splitdir($directories);
770     my $package = pop @basedir;
771
772     my %dirmap = ();
773     if ($mode eq 'single') {
774         $dirmap{'INTRANET_CGI_DIR'} = File::Spec->catdir(@basedir, $package, 'intranet', 'cgi-bin');
775         $dirmap{'INTRANET_TMPL_DIR'} = File::Spec->catdir(@basedir, $package, 'intranet', 'templates');
776         $dirmap{'INTRANET_WWW_DIR'} = File::Spec->catdir(@basedir, $package, 'intranet', 'www');
777         $dirmap{'OPAC_CGI_DIR'} = File::Spec->catdir(@basedir, $package, 'opac', 'cgi-bin');
778         $dirmap{'OPAC_TMPL_DIR'} = File::Spec->catdir(@basedir, $package, 'opac', 'templates');
779         $dirmap{'OPAC_WWW_DIR'} = File::Spec->catdir(@basedir, $package, 'opac', 'www');
780         $dirmap{'PERL_MODULE_DIR'} = File::Spec->catdir(@basedir, $package, 'lib');
781         $dirmap{'KOHA_CONF_DIR'} = File::Spec->catdir(@basedir, $package, 'etc');
782         $dirmap{'ZEBRA_CONF_DIR'} = File::Spec->catdir(@basedir, $package, 'etc', 'zebradb');
783         $dirmap{'EXAMPLE_DIR'} = File::Spec->catdir(@basedir, $package, 'example');
784         $dirmap{'SCRIPT_DIR'} = File::Spec->catdir(@basedir, $package, 'bin');
785         $dirmap{'MAN_DIR'} = File::Spec->catdir(@basedir, $package, 'man');
786         $dirmap{'DOC_DIR'} = File::Spec->catdir(@basedir, $package, 'doc');
787         $dirmap{'ZEBRA_LOCK_DIR'} = File::Spec->catdir(@basedir, $package, 'var', 'lock', 'zebradb');
788         $dirmap{'LOG_DIR'} =  File::Spec->catdir(@basedir, $package, 'var', 'log');
789         $dirmap{'ZEBRA_DATA_DIR'} =  File::Spec->catdir(@basedir, $package, 'var', 'lib', 'zebradb');
790         $dirmap{'ZEBRA_RUN_DIR'} =  File::Spec->catdir(@basedir, $package, 'var', 'run', 'zebradb');
791     } elsif ($mode eq 'dev') {
792         my $curdir = File::Spec->rel2abs(File::Spec->curdir());
793         $dirmap{'INTRANET_CGI_DIR'} = File::Spec->catdir($curdir);
794         $dirmap{'INTRANET_TMPL_DIR'} = File::Spec->catdir($curdir, 'koha-tmpl', 'intranet-tmpl');
795         $dirmap{'INTRANET_WWW_DIR'} = File::Spec->catdir($curdir, 'koha-tmpl', 'intranet-tmpl');
796         $dirmap{'OPAC_CGI_DIR'} = File::Spec->catdir($curdir, 'opac');
797         $dirmap{'OPAC_TMPL_DIR'} = File::Spec->catdir($curdir, 'koha-tmpl', 'opac-tmpl');
798         $dirmap{'OPAC_WWW_DIR'} = File::Spec->catdir($curdir, 'koha-tmpl', 'opac-tmpl');
799         $dirmap{'PERL_MODULE_DIR'} = File::Spec->catdir($curdir);
800         $dirmap{'KOHA_CONF_DIR'} = File::Spec->catdir(@basedir, $package, 'etc');
801         $dirmap{'ZEBRA_CONF_DIR'} = File::Spec->catdir(@basedir, $package, 'etc', 'zebradb');
802         $dirmap{'EXAMPLE_DIR'} = File::Spec->catdir(@basedir, $package, 'example');
803         $dirmap{'SCRIPT_DIR'} = File::Spec->catdir(@basedir, $package, 'bin');
804         $dirmap{'MAN_DIR'} = File::Spec->catdir(@basedir, $package, 'man');
805         $dirmap{'DOC_DIR'} = File::Spec->catdir(@basedir, $package, 'doc');
806         $dirmap{'ZEBRA_LOCK_DIR'} = File::Spec->catdir(@basedir, $package, 'var', 'lock', 'zebradb');
807         $dirmap{'LOG_DIR'} =  File::Spec->catdir(@basedir, $package, 'var', 'log');
808         $dirmap{'ZEBRA_DATA_DIR'} =  File::Spec->catdir(@basedir, $package, 'var', 'lib', 'zebradb');
809         $dirmap{'ZEBRA_RUN_DIR'} =  File::Spec->catdir(@basedir, $package, 'var', 'run', 'zebradb');
810     } else {
811         # mode is standard, i.e., 'fhs'
812         $dirmap{'INTRANET_CGI_DIR'} = File::Spec->catdir(@basedir, $package, 'intranet', 'cgi-bin');
813         $dirmap{'INTRANET_TMPL_DIR'} = File::Spec->catdir(@basedir, $package, 'intranet', 'templates');
814         $dirmap{'INTRANET_WWW_DIR'} = File::Spec->catdir(@basedir, $package, 'intranet', 'www');
815         $dirmap{'OPAC_CGI_DIR'} = File::Spec->catdir(@basedir, $package, 'opac', 'cgi-bin');
816         $dirmap{'OPAC_TMPL_DIR'} = File::Spec->catdir(@basedir, $package, 'opac', 'templates');
817         $dirmap{'OPAC_WWW_DIR'} = File::Spec->catdir(@basedir, $package, 'opac', 'www');
818         $dirmap{'PERL_MODULE_DIR'} = File::Spec->catdir(@basedir, $package, 'lib');
819         $dirmap{'KOHA_CONF_DIR'} = File::Spec->catdir(File::Spec->rootdir(), 'etc', $package);
820         $dirmap{'ZEBRA_CONF_DIR'} = File::Spec->catdir(File::Spec->rootdir(), 'etc', $package, 'zebradb');
821         $dirmap{'EXAMPLE_DIR'} = File::Spec->catdir(@basedir, $package, 'example');
822         $dirmap{'SCRIPT_DIR'} = File::Spec->catdir(@basedir, $package, 'bin');
823         $dirmap{'MAN_DIR'} = File::Spec->catdir(@basedir, $package, 'man');
824         $dirmap{'DOC_DIR'} = File::Spec->catdir(@basedir, $package, 'doc');
825         $dirmap{'ZEBRA_LOCK_DIR'} = File::Spec->catdir(File::Spec->rootdir(), 'var', 'lock', $package, 'zebradb');
826         $dirmap{'LOG_DIR'} =  File::Spec->catdir(File::Spec->rootdir(), 'var', 'log', $package);
827         $dirmap{'ZEBRA_DATA_DIR'} =  File::Spec->catdir(File::Spec->rootdir(), 'var', 'lib', $package, 'zebradb');
828         $dirmap{'ZEBRA_RUN_DIR'} =  File::Spec->catdir(File::Spec->rootdir(), 'var', 'run', $package, 'zebradb');
829     }
830
831     foreach my $key (sort keys %dirmap) {
832         print sprintf("%-25.25s%s\n", $key, $dirmap{$key});
833     }
834     return %dirmap;
835 }
836
837 package MY;
838 sub install {
839     my $self = shift;
840     my $install = ""; 
841     # NOTE: we're *not* doing this: my $install = $self->SUPER::install(@_);
842     # This means that we're completely overriding EU::MM's default
843     # installation and uninstallation targets.
844     foreach my $key (sort keys %target_directories) {
845         $install .= qq(
846 KOHA_INST_$key = blib/$key
847 KOHA_DEST_$key = $target_directories{$key}
848 );
849     }
850
851     $install .= qq(
852 install :: all install_koha
853 \t\$(NOECHO) \$(NOOP)
854 );
855     $install .= "install_koha ::\n";      
856     $install .= "\t\$(NOECHO) umask 022; \$(MOD_INSTALL) \\\n";
857     foreach my $key (sort keys %target_directories) {
858         $install .= "\t\t\$(KOHA_INST_$key) \$(KOHA_DEST_$key) \\\n";
859     }
860     $install .= "\t\t\$(INST_MAN1DIR) \$(DESTINSTALLMAN1DIR) \\\n";
861     $install .= "\t\t\$(INST_MAN3DIR) \$(DESTINSTALLMAN3DIR)\n";
862     return $install;
863 }
864
865 sub postamble {
866     # put directory mappings into Makefile
867     # so that Make will export as environment
868     # variables -- this is for the use of
869     # rewrite-confg.PL
870     my $env = join("\n", map { "export __${_}__ = $target_directories{$_}" } keys %target_directories); 
871     return "$env\n";
872 }
873
874 __END__
875
876
877 =head1 SEE ALSO
878
879 ExtUtils::MakeMaker(3)
880
881 =head1 AUTHORS
882
883 MJ Ray mjr at phonecoop.coop
884
885 =cut
886 FIXME: deal with files that have spaces in names
887 FIXME: Zebra lang/MARC mapping
888 FIXME: deal with .htaccess