Bug 17855: Redirect to the installer if installation process is not done yet
[koha.git] / installer / onboarding.pl
1 #!/usr/bin/perl
2
3 # This file is part of Koha.
4 #
5 # Copyright (C) 2017 Catalyst IT
6 #
7 # Koha is free software; you can redistribute it and/or modify it
8 # under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 3 of the License, or
10 # (at your option) any later version.
11 #
12 # Koha is distributed in the hope that it will be useful, but
13 # WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
16 #
17 # You should have received a copy of the GNU General Public License
18 # along with Koha; if not, see <http://www.gnu.org/licenses>.
19
20 use Modern::Perl;
21 use C4::Context;
22 use C4::InstallAuth;
23 use CGI qw ( -utf8 );
24 use C4::Output;
25 use C4::Members;
26 use Koha::Patrons;
27 use Koha::Libraries;
28 use Koha::Database;
29 use Koha::DateUtils;
30 use Koha::Patron::Categories;
31 use Koha::Patron::Category;
32 use Koha::ItemTypes;
33 use Koha::IssuingRule;
34 use Koha::IssuingRules;
35
36 #Setting variables
37 my $input = new CGI;
38 my $step  = $input->param('step');
39
40 unless ( C4::Context->preference('Version') ) {
41     print $input->redirect("/cgi-bin/koha/installer/install.pl");
42     exit;
43 }
44
45 #Getting the appropriate template to display to the user
46 my ( $template, $loggedinuser, $cookie ) =
47   C4::InstallAuth::get_template_and_user(
48     {
49         template_name => "/onboarding/onboardingstep"
50           . ( $step ? $step : 1 ) . ".tt",
51         query           => $input,
52         type            => "intranet",
53         authnotrequired => 0,
54         debug           => 1,
55     }
56   );
57
58 #Store the value of the template input name='op' in the variable $op so we can check if the user has pressed the button with the name="op" and value="finish" meaning the user has finished the onboarding tool.
59 my $op = $input->param('op') || '';
60 $template->param( 'op' => $op );
61
62 my $schema = Koha::Database->new()->schema();
63
64 if ( $op && $op eq 'finish' )
65 { #If the value of $op equals 'finish' then redirect user to /cgi-bin/koha/mainpage.pl
66     print $input->redirect("/cgi-bin/koha/mainpage.pl");
67     exit;
68 }
69
70 my $libraries = Koha::Libraries->search( {}, { order_by => ['branchcode'] }, );
71 $template->param(
72     libraries   => $libraries,
73     group_types => [
74         {
75             categorytype => 'searchdomain',
76             categories   => [
77                 Koha::LibraryCategories->search(
78                     { categorytype => 'searchdomain' }
79                 )
80             ],
81         },
82         {
83             categorytype => 'properties',
84             categories   => [
85                 Koha::LibraryCategories->search(
86                     { categorytype => 'properties' }
87                 )
88             ],
89         },
90     ]
91 );
92
93 #Select all the patron category records in the categories database table and give them to the template
94 my $categories = Koha::Patron::Categories->search();
95 $template->param( 'categories' => $categories, );
96
97 #Check if the $step variable equals 1 i.e. the user has clicked to create a library in the create library screen 1
98 my $itemtypes = Koha::ItemTypes->search();
99 $template->param( 'itemtypes' => $itemtypes, );
100
101 if ( $step && $step == 1 ) {
102
103     #store inputted parameters in variables
104     my $branchcode = $input->param('branchcode');
105     $branchcode = uc($branchcode);
106     my $categorycode = $input->param('categorycode');
107     my $op = $input->param('op') || 'list';
108     my $message;
109     my $library;
110
111     #Take the text 'branchname' and store it in the @fields array
112     my @fields = qw(
113       branchname
114     );
115
116     $template->param( 'branchcode' => $branchcode );
117     $branchcode =~ s|\s||g
118       ; # Use a regular expression to check the value of the inputted branchcode
119
120 #Create a new library object and store the branchcode and @fields array values in this new library object
121     $library = Koha::Library->new(
122         {
123             branchcode => $branchcode,
124             ( map { $_ => scalar $input->param($_) || undef } @fields )
125         }
126     );
127
128     eval { $library->store; }; #Use the eval{} function to store the library object
129     if ($library) {
130         $message = 'success_on_insert';
131     }
132     else {
133         $message = 'error_on_insert';
134     }
135     $template->param( 'message' => $message );
136
137 #Check if the $step variable equals 2 i.e. the user has clicked to create a patron category in the create patron category screen 1
138 }
139 elsif ( $step && $step == 2 ) {
140     if ( $op eq "add_validate_category" ) {
141
142         #Initialising values
143         my $searchfield  = $input->param('description') // q||;
144         my $categorycode = $input->param('categorycode');
145         my $op           = $input->param('op') // 'list';
146         my $message;
147         my $category;
148         $template->param( 'categorycode' => $categorycode );
149
150         my ( $template, $loggedinuser, $cookie ) =
151           C4::InstallAuth::get_template_and_user(
152             {
153                 template_name   => "/onboarding/onboardingstep2.tt",
154                 query           => $input,
155                 type            => "intranet",
156                 authnotrequired => 0,
157                 flagsrequired =>
158                   { parameters => 'parameters_remaining_permissions' },
159                 debug => 1,
160             }
161           );
162
163       #Once the user submits the page, this code validates the input and adds it
164       #to the database as a new patron category
165         $categorycode = $input->param('categorycode');
166         my $description           = $input->param('description');
167         my $overduenoticerequired = $input->param('overduenoticerequired');
168         my $category_type         = $input->param('category_type');
169         my $default_privacy       = $input->param('default_privacy');
170         my $enrolmentperiod       = $input->param('enrolmentperiod');
171         my $enrolmentperioddate = $input->param('enrolmentperioddate') || undef;
172
173         #Converts the string into a date format
174         if ($enrolmentperioddate) {
175             $enrolmentperioddate = output_pref(
176                 {
177                     dt         => dt_from_string($enrolmentperioddate),
178                     dateformat => 'DateTime',
179                     dateonly   => 1,
180                 }
181             );
182         }
183
184         #Adds a new patron category to the database
185         $category = Koha::Patron::Category->new(
186             {
187                 categorycode          => $categorycode,
188                 description           => $description,
189                 overduenoticerequired => $overduenoticerequired,
190                 category_type         => $category_type,
191                 default_privacy       => $default_privacy,
192                 enrolmentperiod       => $enrolmentperiod,
193                 enrolmentperioddate   => $enrolmentperioddate
194             }
195         );
196
197         eval { $category->store; };
198
199         #Error messages
200         if ($category) {
201             $message = 'success_on_insert';
202         }
203         else {
204             $message = 'error_on_insert';
205         }
206
207         $template->param( 'message' => $message );
208     }
209
210     #Create a patron
211 }
212 elsif ( $step && $step == 3 ) {
213     my $firstpassword  = $input->param('password')  || '';
214     my $secondpassword = $input->param('password2') || '';
215
216     #Find all patron records in the database and hand them to the template
217     my %currentpatrons = Koha::Patrons->search();
218     my $currentpatrons = values %currentpatrons;
219     $template->param( 'patrons' => $currentpatrons );
220
221 #Find all library records in the database and hand them to the template to display in the library dropdown box
222     my $libraries =
223       Koha::Libraries->search( {}, { order_by => ['branchcode'] }, );
224     $template->param(
225         libraries   => $libraries,
226         group_types => [
227             {
228                 categorytype => 'searchdomain',
229                 categories   => [
230                     Koha::LibraryCategories->search(
231                         { categorytype => 'searchdomain' }
232                     )
233                 ],
234             },
235             {
236                 categorytype => 'properties',
237                 categories   => [
238                     Koha::LibraryCategories->search(
239                         { categorytype => 'properties' }
240                     )
241                 ],
242             },
243         ]
244     );
245
246 #Find all patron categories in the database and hand them to the template to display in the patron category dropdown box
247     my $categories = Koha::Patron::Categories->search();
248     $template->param( 'categories' => $categories, );
249
250 #Incrementing the highest existing patron cardnumber to prevent duplicate cardnumber entry
251
252     my $existing_cardnumber =
253       $schema->resultset('Borrower')->get_column('cardnumber')->max() // 0;
254
255     my $new_cardnumber = $existing_cardnumber + 1;
256     $template->param( "newcardnumber" => $new_cardnumber );
257
258     my $op = $input->param('op') // 'list';
259     my $minpw = C4::Context->preference("minPasswordLength");
260     $template->param( "minPasswordLength" => $minpw );
261     my @messages;
262     my @errors;
263     my $nok            = $input->param('nok');
264     my $cardnumber     = $input->param('cardnumber');
265     my $borrowernumber = $input->param('borrowernumber');
266     my $userid         = $input->param('userid');
267
268     # function to designate mandatory fields (visually with css)
269     my $check_BorrowerMandatoryField =
270       C4::Context->preference("BorrowerMandatoryField");
271     my @field_check = split( /\|/, $check_BorrowerMandatoryField );
272     foreach (@field_check) {
273         $template->param( "mandatory$_" => 1 );
274         $template->param(
275             BorrowerMandatoryField =>
276               C4::Context->preference("BorrowerMandatoryField")
277             ,    #field to test with javascript
278         );
279     }
280
281  #If the entered cardnumber causes an error hand this error to the @errors array
282     if ( my $error_code = checkcardnumber( $cardnumber, $borrowernumber ) ) {
283         push @errors,
284             $error_code == 1 ? 'ERROR_cardnumber_already_exists'
285           : $error_code == 2 ? 'ERROR_cardnumber_length'
286           :                    ();
287     }
288
289    #If the entered password causes an error hand this error to the @errors array
290     push @errors, "ERROR_password_mismatch"
291       if $firstpassword ne $secondpassword;
292     push @errors, "ERROR_short_password"
293       if ( $firstpassword
294         && $minpw
295         && $firstpassword ne '****'
296         && ( length($firstpassword) < $minpw ) );
297
298     #Passing errors to template
299     $nok = $nok || scalar(@errors);
300
301 #If errors have been generated from the users inputted cardnumber or password then display the error and do not insert the patron into the borrowers table
302     if ($nok) {
303         foreach my $error (@errors) {
304             if ( $error eq 'ERROR_password_mismatch' ) {
305                 $template->param( errorpasswordmismatch => 1 );
306             }
307             if ( $error eq 'ERROR_login_exist' ) {
308                 $template->param( errorloginexists => 1 );
309             }
310             if ( $error eq 'ERROR_cardnumber_already_exists' ) {
311                 $template->param( errorcardnumberexists => 1 );
312             }
313             if ( $error eq 'ERROR_cardnumber_length' ) {
314                 $template->param( errorcardnumberlength => 1 );
315             }
316             if ( $error eq 'ERROR_short_password' ) {
317                 $template->param( errorshortpassword => 1 );
318             }
319         }
320         $template->param( 'nok' => 1 );
321
322 #Else if no errors have been caused by the users inputted card number or password then insert the patron into the borrowers table
323     }
324     else {
325         my ( $template, $loggedinuser, $cookie ) =
326           C4::InstallAuth::get_template_and_user(
327             {
328                 template_name   => "/onboarding/onboardingstep3.tt",
329                 query           => $input,
330                 type            => "intranet",
331                 authnotrequired => 0,
332                 flagsrequired   => { borrowers => 1 },
333                 debug           => 1,
334             }
335           );
336
337         if ( $op eq 'add_validate' ) {
338             my %newdata;
339
340             #Store the template form values in the newdata hash
341             $newdata{borrowernumber} = $input->param('borrowernumber');
342             $newdata{surname}        = $input->param('surname');
343             $newdata{firstname}      = $input->param('firstname');
344             $newdata{cardnumber}     = $input->param('cardnumber');
345             $newdata{branchcode}     = $input->param('libraries');
346             $newdata{categorycode}   = $input->param('categorycode_entry');
347             $newdata{userid}         = $input->param('userid');
348             $newdata{password}       = $input->param('password');
349             $newdata{password2}      = $input->param('password2');
350             $newdata{privacy}        = "default";
351             $newdata{address}        = "";
352             $newdata{city}           = "";
353
354 #Hand tne the dateexpiry of the patron based on the patron category it is created from
355             my $patron_category =
356               Koha::Patron::Categories->find( $newdata{categorycode} );
357             $newdata{dateexpiry} =
358               $patron_category->get_expiry_date( $newdata{dateenrolled} );
359
360 #Hand the newdata hash to the AddMember subroutine in the C4::Members module and it creates a patron and hands back a borrowernumber which is being stored
361             my $borrowernumber = &AddMember(%newdata);
362
363 #Create a hash named member2 and fill it with the borrowernumber of the borrower that has just been created
364             my %member2;
365             $member2{'borrowernumber'} = $borrowernumber;
366
367 #Perform data validation on the flag that has been handed to onboarding.pl by the template
368             my $flag = $input->param('flag');
369             if ( $input->param('newflags') ) {
370                 my @perms            = $input->multi_param('flag');
371                 my %all_module_perms = ();
372                 my %sub_perms        = ();
373                 foreach my $perm (@perms) {
374                     if ( $perm !~ /:/ ) {
375                         $all_module_perms{$perm} = 1;
376                     }
377                     else {
378                         my ( $module, $sub_perm ) = split /:/, $perm, 2;
379                         push @{ $sub_perms{$module} }, $sub_perm;
380                     }
381                 }
382
383                 # construct flags
384                 my @userflags = $schema->resultset('Userflag')->search(
385                     {},
386                     {
387                         order_by => { -asc => 'bit' },
388                     }
389                 );
390
391                 #Setting superlibrarian permissions for new patron
392                 my $flags =
393                   Koha::Patrons->find($borrowernumber)->set( { flags => 1 } )
394                   ->store;
395
396                 #Error handling checking if the patron was created successfully
397                 if ( !$borrowernumber ) {
398                     push @messages,
399                       { type => 'error', code => 'error_on_insert' };
400                 }
401                 else {
402                     push @messages,
403                       { type => 'message', code => 'success_on_insert' };
404                 }
405             }
406         }
407     }
408 }
409 elsif ( $step && $step == 4 ) {
410     my ( $template, $borrowernumber, $cookie ) =
411       C4::InstallAuth::get_template_and_user(
412         {
413             template_name   => "/onboarding/onboardingstep4.tt",
414             query           => $input,
415             type            => "intranet",
416             authnotrequired => 0,
417             flagsrequired =>
418               { parameters => 'parameters_remaining_permissions' },
419             debug => 1,
420         }
421       );
422     if ( $op eq "add_validate" ) {
423         my $description   = $input->param('description');
424         my $itemtype_code = $input->param('itemtype');
425         $itemtype_code = uc($itemtype_code);
426
427   #Create a new itemtype object using the user inputted itemtype and description
428         my $itemtype = Koha::ItemType->new(
429             {
430                 itemtype    => $itemtype_code,
431                 description => $description,
432             }
433         );
434         eval { $itemtype->store; };
435         my $message;
436
437 #Fill the $message variable with an error if the item type object was not successfully created and inserted into the itemtypes table
438         if ($itemtype) {
439             $message = 'success_on_insert';
440         }
441         else {
442             $message = 'error_on_insert';
443         }
444         $template->param( 'message' => $message );
445     }
446 }
447 elsif ( $step && $step == 5 ) {
448
449   #Find all the existing categories to display in a dropdown box in the template
450     my $categories;
451     $categories = Koha::Patron::Categories->search();
452     $template->param( categories => $categories, );
453
454  #Find all the exisiting item types to display in a dropdown box in the template
455     my $itemtypes;
456     $itemtypes = Koha::ItemTypes->search();
457     $template->param( itemtypes => $itemtypes, );
458
459   #Find all the exisiting libraries to display in a dropdown box in the template
460     my $libraries =
461       Koha::Libraries->search( {}, { order_by => ['branchcode'] }, );
462     $template->param(
463         libraries   => $libraries,
464         group_types => [
465             {
466                 categorytype => 'searchdomain',
467                 categories   => [
468                     Koha::LibraryCategories->search(
469                         { categorytype => 'searchdomain' }
470                     )
471                 ],
472             },
473             {
474                 categorytype => 'properties',
475                 categories   => [
476                     Koha::LibraryCategories->search(
477                         { categorytype => 'properties' }
478                     )
479                 ],
480             },
481         ]
482     );
483
484     my $input = CGI->new;
485
486     my ( $template, $loggedinuser, $cookie ) =
487       C4::InstallAuth::get_template_and_user(
488         {
489             template_name   => "/onboarding/onboardingstep5.tt",
490             query           => $input,
491             type            => "intranet",
492             authnotrequired => 0,
493             flagsrequired   => { parameters => 'manage_circ_rules' },
494             debug           => 1,
495         }
496       );
497
498     #If no libraries exist then set the $branch value to *
499     my $branch = $input->param('branch');
500     unless ($branch) {
501         if ( C4::Context->preference('DefaultToLoggedInLibraryCircRules') ) {
502             $branch =
503               Koha::Libraries->search->count() == 1
504               ? undef
505               : C4::Context::mybranch();
506         }
507         else {
508             $branch =
509               C4::Context::only_my_library()
510               ? ( C4::Context::mybranch() || '*' )
511               : '*';
512         }
513     }
514     $branch = '*' if $branch eq 'NO_LIBRARY_SET';
515     my $op = $input->param('op') || q{};
516
517     if ( $op eq 'add_validate' ) {
518         my $type            = $input->param('type');
519         my $br              = $input->param('branch');
520         my $bor             = $input->param('categorycode');
521         my $itemtype        = $input->param('itemtype');
522         my $maxissueqty     = $input->param('maxissueqty');
523         my $issuelength     = $input->param('issuelength');
524         my $lengthunit      = $input->param('lengthunit');
525         my $renewalsallowed = $input->param('renewalsallowed');
526         my $renewalperiod   = $input->param('renewalperiod');
527         my $onshelfholds    = $input->param('onshelfholds') || 0;
528         $maxissueqty =~ s/\s//g;
529         $maxissueqty = undef if $maxissueqty !~ /^\d+/;
530         $issuelength = $issuelength eq q{} ? undef : $issuelength;
531
532         my $params = {
533             branchcode      => $br,
534             categorycode    => $bor,
535             itemtype        => $itemtype,
536             maxissueqty     => $maxissueqty,
537             renewalsallowed => $renewalsallowed,
538             renewalperiod   => $renewalperiod,
539             issuelength     => $issuelength,
540             lengthunit      => $lengthunit,
541             onshelfholds    => $onshelfholds,
542         };
543
544         my @messages;
545
546 #Allows for the 'All' option to work when selecting all libraries for a circulation rule to apply to.
547         if ( $branch eq "*" ) {
548             my $search_default_rules =
549               $schema->resultset('DefaultCircRule')->count();
550             my $insert_default_rules =
551               $schema->resultset('Issuingrule')
552               ->new(
553                 { maxissueqty => $maxissueqty, onshelfholds => $onshelfholds }
554               );
555         }
556
557 #Allows for the 'All' option to work when selecting all patron categories for a circulation rule to apply to.
558         elsif ( $bor eq "*" ) {
559
560             my $search_default_rules =
561               $schema->resultset('DefaultCircRule')->count();
562             my $insert_default_rules = $schema->resultset('Issuingrule')
563               ->new( { maxissueqty => $maxissueqty } );
564         }
565
566 #Allows for the 'All' option to work when selecting all itemtypes for a circulation rule to apply to
567         elsif ( $itemtype eq "*" ) {
568             my $search_default_rules =
569               $schema->resultset('DefaultCircRule')->search(
570                 {},
571                 {
572                     branchcode => $branch
573                 }
574
575               );
576
577             my $insert_default_rules = $schema->resultset('Issuingrule')
578               ->new( { branchcode => $branch, onshelfholds => $onshelfholds } );
579         }
580
581         my $issuingrule = Koha::IssuingRules->find(
582             { categorycode => $bor, itemtype => $itemtype, branchcode => $br }
583         );
584         if ($issuingrule) {
585             $issuingrule->set($params)->store();
586             push @messages,
587               {
588                 type => 'error',
589                 code => 'error_on_insert'
590               }; #Stops crash of the onboarding tool if someone makes a circulation rule with the same item type, library and patron categroy as an exisiting circulation rule.
591
592         }
593         else {
594             Koha::IssuingRule->new()->set($params)->store();
595         }
596     }
597 }
598
599 output_html_with_http_headers $input, $cookie, $template->output;