1 #!/usr/bin/perl -w # please develop with -w
8 use C4::Interface::CGI::Output;
11 use strict; # please develop with the strict pragma
16 my $step=$query->param('step');
18 my $language=$query->param('language');
19 my ($template, $loggedinuser, $cookie);
22 my $all_languages=getAllLanguages();
24 if (defined($language) ){
25 setlanguagecookie($query,$language,"install.pl?step=1");
27 ($template, $loggedinuser, $cookie)
28 = get_template_and_user({template_name => "installer/step".($step?$step:1).".tmpl",
35 if ($step && $step==1){
37 #Checking ALL perl Modules and services needed are installed.
38 #Whenever there is an error, adding a report to the page
39 # I suppose here that Apache user can access /usr/bin/
40 # If mysql or zebra are in some fancy directory not in PATH
41 # Performing a disk search.
42 $template->param(language=>1);
45 unless ($] >= 5.006001) { # Bug 179
46 $template->param("problems"=>1,"perlversion"=>1);
49 unless (-x "/usr/bin/perl") {
50 my $realperl=`which perl`;
51 $realperl=`find / -name perl` unless ($realperl);
53 $template->param("problems"=>1,'perllocation'=>1) unless ($realperl);
54 $problem=1 unless($realperl);
56 unless (-x "/usr/local/bin/mysql") {
57 my $mysql=`which mysql`;
58 $mysql=`find / -name mysql` unless ($mysql);
60 $template->param("problems"=>1,'mysql'=>1) unless ($mysql);
61 $problem=1 unless($mysql);
63 unless (-x "/usr/local/bin/zebraidx" ||-x "/usr/local/bin/zebraidx-2.0") {
64 my $zebra=`which zebraidx`;
65 $zebra=`which zebraidx-2.0` unless ($zebra);
66 $zebra=`find / -name "zebraidx*"` unless ($zebra);
68 $template->param("problems"=>1,'zebra'=>1) unless ($zebra);
69 $problem=1 unless ($zebra);
71 unless (-x "/usr/local/bin/zebrasrv" ||-x "/usr/local/bin/zebrasrv-2.0") {
72 my $zebra=`which zebrasrv`;
73 $zebra=`which zebrasrv-2.0` unless ($zebra);
74 $zebra=`find / -name "zebrasrv*"` unless ($zebra);
76 $template->param("problems"=>1,'zebra'=>1) unless ($zebra);
77 $problem=1 unless ($zebra);
79 unless (-x "/usr/local/bin/yaz-client") {
80 my $yaz=`which yaz-client`;
81 $yaz=`find / -name "yaz-client*"` unless ($yaz);
83 $template->param("problems"=>1,'yaz'=>1) unless ($yaz);
84 $problem=1 unless ($yaz);
86 # We could here use a special find
88 unless (eval {require ZOOM}) {
89 push @missing, {name=>"ZOOM"};
91 unless (eval {require LWP::Simple}) {
92 push @missing, {name=>"LWP::Simple"};
94 unless (eval {require XML::Simple}) {
95 push @missing, {name=>"XML::Simple"};
97 unless (eval {require MARC::File::XML}) {
98 push @missing, {name=>"MARC::File::XML"};
100 unless (eval {require MARC::File::USMARC}) {
101 push @missing, {name=>"MARC::File::USMARC"};
103 unless (eval {require DBI}) { push @missing,{name=>"DBI"} };
104 unless (eval {require Date::Manip}) { push @missing,{name=>"Date::Manip"} };
105 unless (eval {require DBD::mysql}) { push @missing,{name=>"DBD::mysql"} };
106 unless (eval {require HTML::Template}) { push @missing,{name=>"HTML::Template::Pro"} };
107 unless (eval {require HTML::Template}) { push @missing,{name=>"Date::Calc"} };
108 unless (eval {require Digest::MD5}) { push @missing,{name=>"Digest::MD5"} };
109 unless (eval {require MARC::Record}) { push @missing,{name=>"MARC::Record"} };
110 unless (eval {require Mail::Sendmail}) { push @missing,{name=>"Mail::Sendmail",usagemail=>1} };
111 # The following modules are not mandatory, depends on how the library want to use Koha
112 unless (eval {require PDF::API2}) {
113 if ($#missing>=0) { # only when $#missing >= 0 so this isn't fatal
114 push @missing,{name=>"PDF::API2",usagebarcode=>1};
117 unless (eval {require GD::Barcorde}) {
118 if ($#missing>=0) { # only when $#missing >= 0 so this isn't fatal
119 push @missing,{name=>"GD::Barcode",usagebarcode=>1,usagespine=>1};
122 unless (eval {require Data::Random}) {
123 if ($#missing>=0) { # only when $#missing >= 0 so this isn't fatal
124 push @missing,{name=>"Data::Random",usagebarcode=>1};
127 unless (eval {require PDF::Reuse::Barcode}) {
128 if ($#missing>=0) { # only when $#missing >= 0 so this isn't fatal
129 push @missing,{name=>"PDF::Reuse::Barcode",usagebarcode=>1};
132 unless (eval {require PDF::Report}) {
133 if ($#missing>=0) { # only when $#missing >= 0 so this isn't fatal
134 push @missing,{name=>"PDF::Report",usagebarcode=>1};
137 unless (eval {require GD::Barcode::UPCE}) {
138 if ($#missing>=0) { # only when $#missing >= 0 so this isn't fatal
139 push @missing,{name=>"GD::Barcode::UPCE",usagepine=>1};
142 unless (eval {require Net::LDAP}) {
143 if ($#missing>=0) { # only when $#missing >= 0 so this isn't fatal
144 push @missing,{name=>"Net::LDAP",usageLDAP=>1};
148 $template->param(missings=>\@missing) if (scalar(@missing)>0);
149 $template->param('checkmodule'=>1) unless (scalar(@missing) && $problem);
151 } elsif ($step && $step==2){
152 # Check Database connection and access
154 $info{'dbname'}=C4::Context->config("database");
155 $info{'dbms'}=(C4::Context->config("db_scheme")?C4::Context->config("db_scheme"):"mysql");
156 $info{'hostname'}=C4::Context->config("hostname");
157 ($info{'hostname'},$info{'port'})=($1,$2) if $info{'hostname'}=~/([^:]*):([0-9]+)/;
158 $info{'user'}=C4::Context->config("user");
159 $info{'password'}=C4::Context->config("pass");
160 $template->param(%info);
161 my $checkmysql=$query->param("checkmysql");
162 $template->param('mysqlconnection'=>$checkmysql);
164 my $dbh= DBI->connect("DBI:$info{dbms}:$info{dbname}:$info{hostname}".($info{port}?":$info{port}":""),$info{'user'}, $info{'password'});
166 # Can connect to the mysql
167 $template->param("checkdatabaseaccess"=>1);
168 if ($info{dbms} eq "mysql"){
169 #Check if database created
170 my $rv=$dbh->do("SHOW DATABASES LIKE \'$info{dbname}\'");
171 if ($rv==1){$template->param('checkdatabasecreated'=>1);}
172 #Check if user have all necessary grants on this database.
173 my $rq=$dbh->prepare("SHOW GRANTS FOR \'$info{user}\'\@'$info{hostname}'");
176 while (my ($line)=$rq->fetchrow){
177 my $dbname=$info{dbname};
178 if ($line=~m/$dbname/ || index($line,'*.*')>0){
179 $grantaccess=1 if (index($line,'ALL PRIVILEGES')>0 ||
180 ((index($line,'SELECT')>0)&&(index($line,'INSERT')>0)&&(index($line,'UPDATE')>0)&&(index($line,'DELETE')>0)&&(index($line,'CREATE')>0)&&(index($line,'DROP')>0)));
183 unless ($grantaccess){
184 $rq=$dbh->prepare("SHOW GRANTS FOR \'$info{user}\'\@'\%'");
186 while (my ($line)=$rq->fetchrow){
187 my $dbname=$info{dbname};
188 if ($line=~m/$dbname/ || index($line,'*.*')>0){
189 $grantaccess=1 if (index($line,'ALL PRIVILEGES')>0 ||
190 ((index($line,'SELECT')>0)&&(index($line,'INSERT')>0)&&(index($line,'UPDATE')>0)&&(index($line,'DELETE')>0)&&(index($line,'CREATE')>0)&&(index($line,'DROP')>0)));
194 $template->param("checkgrantaccess"=>$grantaccess);
197 $template->param("error"=>DBI::err,"message"=>DBI::errstr);
200 } elsif ($step && $step==3){
202 $info{'dbname'}=C4::Context->config("database");
203 $info{'dbms'}=(C4::Context->config("db_scheme")?C4::Context->config("db_scheme"):"mysql");
204 $info{'hostname'}=C4::Context->config("hostname");
205 ($info{'hostname'},$info{'port'})=($1,$2) if $info{'hostname'}=~/([^:]*):([0-9]+)/;
206 $info{port} = 3306 unless ($info{port});
207 $info{'user'}=C4::Context->config("user");
208 $info{'password'}=C4::Context->config("pass");
209 my $op=$query->param('op');
210 if ($op && $op eq 'finish'){
211 # Installation is finished.
212 # We just deny anybody acess to install
213 # And we redirect people to mainpage.
214 # The installer wil have to relogin since we donot pass cookie to redirection.
215 my $dir=C4::Context->config('intranetdir');
216 qx(chmod -R uog-xw $dir/installer);
217 print $query->redirect("/cgi-bin/koha/mainpage.pl");
219 } elsif ($op && $op eq 'addframeworks'){
220 #Framework importing and reports
224 foreach my $file ($query->param('framework')){
226 my $strcmd="mysql ".($info{hostname}?"-h $info{hostname} ":"").($info{port}?"-P $info{port} ":"").($info{user}?"-u $info{user} ":"").($info{password}?"-p$info{password}":"")." $info{dbname} ";
227 my $str = qx($strcmd < $file 2>&1);
228 my @file = split qr(\/|\\),$file;
229 $lang=$file[scalar(@file)-3] unless ($lang);
230 my $level=$file[scalar(@file)-2];
231 #Bulding here a hierarchy to display files by level.
232 push @{$hashlevel{$level}},{"fwkname"=>$file[scalar(@file)-1],"error"=>$str};
235 map {push @list,{"level"=>$_,"fwklist"=>$hashlevel{$_}}} keys %hashlevel;
237 for my $each_language(@$all_languages) {
238 warn "CODE".$each_language->{'language_code'};
240 if ($lang eq $each_language->{'language_code'}) {
241 $fwk_language = $each_language->{language_locale_name};
244 $template->param("fwklanguage"=>$fwk_language,
246 $template->param("$op"=>1);
247 } elsif ( $op && $op eq 'selectframeworks'){
249 #sql data for import are supposed to be located in misc/sql-datas/<language>/<level>
250 # Where <language> is en|fr or any international abbreviation (provided language hash is updated... This will be a problem with internationlisation.)
251 # Where <level> is a category of requirement : required, recommended optional
252 # level should contain :
253 # SQL File for import With a readable name.
254 # txt File taht explains what this SQL File is meant for.
255 # Could be VERY useful to have A Big file for a kind of library.
256 # But could also be useful to have some Authorised values data set prepared here.
257 # Framework Selection is achieved through checking boxes.
258 my $langchoice=$query->param('fwklanguage') ;
259 $langchoice=$query->cookie('KohaOpacLanguage') unless ($langchoice);
260 my $dir=C4::Context->config('intranetdir')."/misc/sql-datas/";
261 opendir (MYDIR,$dir);
262 my @listdir= grep { !/^\.|CVS/ && -d "$dir/$_"} readdir(MYDIR);
264 my $frmwklangs = getFrameworkLanguages();
267 push @languages,{'dirname'=>$_->{'language_code'}, 'languagedescription'=>$_->{'language_name'},'checked'=>($_->{'language_code'} eq $langchoice) } if ($_->{'language_code'});
269 $template->param("languagelist"=>\@languages);
271 $dir=C4::Context->config('intranetdir')."/misc/sql-datas/$langchoice";
272 opendir (MYDIR,$dir) || warn "no open $dir";
273 @listdir= grep { !/^\.|CVS/ && -d "$dir/$_"} readdir(MYDIR);
276 foreach my $requirelevel (@listdir){
277 $dir =C4::Context->config('intranetdir')."/misc/sql-datas/$langchoice/$requirelevel";
278 opendir (MYDIR,$dir);
279 my @listname = grep { !/^\.|CVS/ && -f "$dir/$_" && $_=~m/\.sql$/} readdir(MYDIR);
284 my $name=substr($_,0,-4);
285 open FILE, "< $dir/$name.txt";
287 $lines=~s/\n|\r/<br \/>/g;
289 utf8::encode($lines) unless (utf8::is_utf8($lines));
290 push @frameworklist,{'fwkname'=>$name, 'fwkfile'=>"$dir/$_",'fwkdescription'=>$lines};
292 $cell{"mandatory"}=($requirelevel=~/(mandatory|requi|oblig|necess)/i);
293 $cell{"frameworks"}=\@frameworklist;
294 $cell{"label"}=ucfirst($requirelevel);
295 $cell{"code"}=lc($requirelevel);
296 push @levellist,\%cell;
298 $template->param("levelloop"=>\@levellist);
299 $template->param("$op"=>1);
300 } elsif ($op && $op eq 'updatestructure'){
301 #Do updatedatabase And report
302 my $execstring=C4::Context->config("intranetdir")."/updater/updatedatabase";
304 my $string= qx|$execstring 2>&1|;
306 $string=~s/\n|\r/<br \/>/g;
307 $string=~s/(DBD::mysql.*? failed: .*? line [0-9]*.|=================.*?====================)/<font color=red>$1<\/font>/g;
308 $template->param("updatereport"=>$string) ;
310 $template->param($op=>1)
311 }elsif ($op && $op eq 'importdatastructure'){
312 #Import data structure and show errors if any
313 my $filename="kohastructure.sql";
315 my $str = qx(mysql -h $info{hostname} -P $info{port} -u $info{user} -p$info{password} $info{dbname} <$filename 2>&1);
316 $str=~s/\n|\r/<br \/>/g;
317 $template->param("error"=>$str ,
318 importdatastructure => 1, );
320 #Check if there are enough tables.
321 # Version 2_2 was 74 tables, so we check if there is more than 75
322 # Maybe could be in step 2
323 #I put it there because it implied a data import if condition was not satisfied.
324 my $dbh= DBI->connect("DBI:$info{dbms}:$info{dbname}:$info{hostname}".($info{port}?":$info{port}":""),$info{'user'}, $info{'password'});
325 my $rq=$dbh->prepare("SHOW TABLES FROM ".$info{'dbname'});
327 my $data=$rq->fetchall_arrayref({});
328 my $count=scalar(@$data);
330 $template->param("count"=>$count,"proposeimport"=>1);
332 $template->param("count"=>$count,"default"=>1);
337 # LANGUAGE SELECTION page by default
338 # using opendir + language Hash
340 my $langavail = getTranslatedLanguages();
342 foreach (@$langavail){
343 push @languages,{'value'=>$_->{'language_code'}, 'description'=>$_->{'language_name'} } if ($_->{'language_code'});
345 $template->param(languages=>\@languages);
347 output_html_with_http_headers $query, $cookie, $template->output;