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