View | Details | Raw Unified | Return to bug 25078
Collapse All | Expand All

(-)a/C4/Installer.pm (-1 / +232 lines)
Lines 19-28 package C4::Installer; Link Here
19
19
20
use Modern::Perl;
20
use Modern::Perl;
21
21
22
use Try::Tiny;
22
use Encode qw( encode is_utf8 );
23
use Encode qw( encode is_utf8 );
23
use DBIx::RunSQL;
24
use DBIx::RunSQL;
24
use YAML::XS;
25
use YAML::XS;
25
use C4::Context;
26
use C4::Context;
27
use Koha::Schema;
26
use DBI;
28
use DBI;
27
use Koha;
29
use Koha;
28
30
Lines 30-36 use vars qw(@ISA @EXPORT); Link Here
30
BEGIN {
32
BEGIN {
31
    require Exporter;
33
    require Exporter;
32
    @ISA = qw( Exporter );
34
    @ISA = qw( Exporter );
33
    push @EXPORT, qw( primary_key_exists foreign_key_exists index_exists column_exists TableExists);
35
    push @EXPORT, qw( primary_key_exists foreign_key_exists index_exists column_exists TableExists TransformToNum CheckVersion sanitize_zero_date update get_db_entries );
34
};
36
};
35
37
36
=head1 NAME
38
=head1 NAME
Lines 683-688 sub TableExists { # Could be renamed table_exists for consistency Link Here
683
    return 0;
685
    return 0;
684
}
686
}
685
687
688
sub version_from_file {
689
    my $file = shift;
690
    return unless $file =~ m|(^\|/)(\d{2})(\d{2})(\d{2})(\d{3}).pl$|;
691
    return sprintf "%s.%s.%s.%s", $2, $3, $4, $5;
692
}
693
694
sub get_db_entries {
695
    my $db_revs_dir = C4::Context->config('intranetdir') . '/installer/data/mysql/db_revs';
696
    opendir my $dh, $db_revs_dir or die "Cannot open $db_revs_dir dir ($!)";
697
    my @files = sort grep { m|\.pl$| && ! m|skeleton\.pl$| } readdir $dh;
698
    my @need_update;
699
    for my $file ( @files ) {
700
        my $version = version_from_file( $file );
701
702
        unless ( $version ) {
703
            warn "Invalid db_rev found: " . $file;
704
            next
705
        }
706
707
        next unless CheckVersion( $version );
708
709
        push @need_update, sprintf( "%s/%s", $db_revs_dir, $file );
710
    }
711
    return \@need_update;
712
}
713
714
sub update {
715
    my ( $files, $params ) = @_;
716
717
    my $force = $params->{force} || 0;
718
719
    my $schema = Koha::Database->new->schema;
720
    my ( @done, @errors );
721
    for my $file ( @$files ) {
722
723
        my $db_rev = do $file;
724
725
        my $error;
726
727
        try {
728
            $schema->txn_do(
729
                sub {
730
                    $db_rev->{up}->();
731
                }
732
            );
733
        } catch {
734
            $error = $_;
735
        };
736
737
        my $db_entry = {
738
            bug_number  => $db_rev->{bug_number},
739
            description => ( ref $db_rev->{description} eq 'CODE' )
740
              ? $db_rev->{description}->()
741
              : $db_rev->{description},
742
            version     => version_from_file($file),
743
            time        => POSIX::strftime( "%H:%M:%S", localtime ),
744
        };
745
        $db_entry->{output} = output_version( { %$db_entry, done => !$error } );
746
747
        if ( $error ) {
748
            push @errors, { %$db_entry, error => $error };
749
            $force ? next : last ;
750
                # We stop the update if an error occurred!
751
        }
752
753
        SetVersion($db_entry->{version});
754
        push @done, $db_entry;
755
    }
756
    return { success => \@done, error => \@errors };
757
}
758
759
sub output_version {
760
    my ( $db_entry ) = @_;
761
762
    my $descriptions = $db_entry->{description};
763
    my $DBversion = $db_entry->{version};
764
    my $bug_number = $db_entry->{bug_number};
765
    my $time = $db_entry->{time};
766
    my $done = $db_entry->{done} ? "done" : "failed";
767
768
    unless ( ref($descriptions) ) {
769
        $descriptions = [ $descriptions ];
770
    }
771
772
    my $first = 1;
773
    my @output;
774
    for my $description ( @$descriptions ) {
775
        if ( @$descriptions > 1 ) {
776
            if ( $first ) {
777
                unless ( $bug_number ) {
778
                    push @output, sprintf "Upgrade to %s %s [%s]:", $DBversion, $done, $time;
779
                } else {
780
                    push @output, sprintf "Upgrade to %s %s [%s]: Bug %5s", $DBversion, $done, $time, $bug_number;
781
                }
782
            }
783
            push @output, sprintf "\t\t\t\t\t\t   - %s", $description;
784
        } else {
785
            unless ( $bug_number ) {
786
                push @output, sprintf "Upgrade to %s %s [%s]: %s", $DBversion, $done, $time, $description;
787
            } else {
788
                push @output, sprintf "Upgrade to %s %s [%s]: Bug %5s - %s", $DBversion, $done, $time, $bug_number, $description;
789
            }
790
        }
791
        $first = 0;
792
    }
793
    return \@output;
794
}
795
796
=head2 DropAllForeignKeys($table)
797
798
Drop all foreign keys of the table $table
799
800
=cut
801
802
sub DropAllForeignKeys {
803
    my ($table) = @_;
804
    # get the table description
805
    my $dbh = C4::Context->dbh;
806
    my $sth = $dbh->prepare("SHOW CREATE TABLE $table");
807
    $sth->execute;
808
    my $vsc_structure = $sth->fetchrow;
809
    # split on CONSTRAINT keyword
810
    my @fks = split /CONSTRAINT /,$vsc_structure;
811
    # parse each entry
812
    foreach (@fks) {
813
        # isolate what is before FOREIGN KEY, if there is something, it's a foreign key to drop
814
        $_ = /(.*) FOREIGN KEY.*/;
815
        my $id = $1;
816
        if ($id) {
817
            # we have found 1 foreign, drop it
818
            $dbh->do("ALTER TABLE $table DROP FOREIGN KEY $id");
819
            $id="";
820
        }
821
    }
822
}
823
824
825
=head2 TransformToNum
826
827
Transform the Koha version from a 4 parts string
828
to a number, with just 1 .
829
830
=cut
831
832
sub TransformToNum {
833
    my $version = shift;
834
    # remove the 3 last . to have a Perl number
835
    $version =~ s/(.*\..*)\.(.*)\.(.*)/$1$2$3/;
836
    # three X's at the end indicate that you are testing patch with dbrev
837
    # change it into 999
838
    # prevents error on a < comparison between strings (should be: lt)
839
    $version =~ s/XXX$/999/;
840
    return $version;
841
}
842
843
=head2 SetVersion
844
845
set the DBversion in the systempreferences
846
847
=cut
848
849
sub SetVersion {
850
    return if $_[0]=~ /XXX$/;
851
      #you are testing a patch with a db revision; do not change version
852
    my $kohaversion = TransformToNum($_[0]);
853
    my $dbh = C4::Context->dbh;
854
    if (C4::Context->preference('Version')) {
855
      my $finish=$dbh->prepare("UPDATE systempreferences SET value=? WHERE variable='Version'");
856
      $finish->execute($kohaversion);
857
    } else {
858
      my $finish=$dbh->prepare("INSERT into systempreferences (variable,value,explanation) values ('Version',?,'The Koha database version. WARNING: Do not change this value manually, it is maintained by the webinstaller')");
859
      $finish->execute($kohaversion);
860
    }
861
    C4::Context::clear_syspref_cache(); # invalidate cached preferences
862
}
863
864
sub NewVersion {
865
    # void FIXME replace me
866
}
867
868
=head2 CheckVersion
869
870
Check whether a given update should be run when passed the proposed version
871
number. The update will always be run if the proposed version is greater
872
than the current database version and less than or equal to the version in
873
kohaversion.pl. The update is also run if the version contains XXX, though
874
this behavior will be changed following the adoption of non-linear updates
875
as implemented in bug 7167.
876
877
=cut
878
879
sub CheckVersion {
880
    my ($proposed_version) = @_;
881
    my $version_number = TransformToNum($proposed_version);
882
883
    # The following line should be deleted when bug 7167 is pushed
884
    return 1 if ( $proposed_version =~ m/XXX/ );
885
886
    if ( C4::Context->preference("Version") < $version_number
887
        && $version_number <= TransformToNum( $Koha::VERSION ) )
888
    {
889
        return 1;
890
    }
891
892
    return 0;
893
}
894
895
sub sanitize_zero_date {
896
    my ( $table_name, $column_name ) = @_;
897
898
    my $dbh = C4::Context->dbh;
899
900
    my (undef, $datatype) = $dbh->selectrow_array(qq|
901
        SHOW COLUMNS FROM $table_name WHERE Field = ?|, undef, $column_name);
902
903
    if ( $datatype eq 'date' ) {
904
        $dbh->do(qq|
905
            UPDATE $table_name
906
            SET $column_name = NULL
907
            WHERE CAST($column_name AS CHAR(10)) = '0000-00-00';
908
        |);
909
    } else {
910
        $dbh->do(qq|
911
            UPDATE $table_name
912
            SET $column_name = NULL
913
            WHERE CAST($column_name AS CHAR(19)) = '0000-00-00 00:00:00';
914
        |);
915
    }
916
}
686
917
687
=head1 AUTHOR
918
=head1 AUTHOR
688
919
(-)a/Koha.pm (-1 / +1 lines)
Lines 29-35 use vars qw{ $VERSION }; Link Here
29
# - #4 : the developer version. The 4th number is the database subversion.
29
# - #4 : the developer version. The 4th number is the database subversion.
30
#        used by developers when the database changes. updatedatabase take care of the changes itself
30
#        used by developers when the database changes. updatedatabase take care of the changes itself
31
#        and is automatically called by Auth.pm when needed.
31
#        and is automatically called by Auth.pm when needed.
32
$VERSION = "21.06.00.004";
32
$VERSION = "21.06.00.005";
33
33
34
sub version {
34
sub version {
35
    return $VERSION;
35
    return $VERSION;
(-)a/installer/data/mysql/db_revs/210600000.pl (+8 lines)
Line 0 Link Here
1
use Modern::Perl;
2
use utf8;
3
4
{
5
    bug_number => undef,
6
    description => ["🎵 Run, rabbit run. 🎶", "Dig that hole, forget the sun,", "And when at last the work is done", "Don't sit down it's time to dig another one."],
7
    up => sub {},
8
}
(-)a/installer/data/mysql/db_revs/skeleton.pl (+13 lines)
Line 0 Link Here
1
use Modern::Perl;
2
3
{
4
    bug_number => "BUG_NUMBER",
5
    description => "A single line description",
6
    # description => ["Multi", "lines", "description"],
7
    # description => sub { return ["Your dynamic description"] },
8
    up => sub {
9
        my $dbh = C4::Context->dbh;
10
        # Do you stuffs here
11
        $dbh->do(q{});
12
    },
13
}
(-)a/installer/data/mysql/updatedatabase.pl (-155 / +16 lines)
Lines 34-39 use feature 'say'; Link Here
34
# CPAN modules
34
# CPAN modules
35
use DBI;
35
use DBI;
36
use Getopt::Long;
36
use Getopt::Long;
37
use Encode qw( encode_utf8 );
37
# Koha modules
38
# Koha modules
38
use C4::Context;
39
use C4::Context;
39
use C4::Installer;
40
use C4::Installer;
Lines 61-70 my ( Link Here
61
62
62
my $schema = Koha::Database->new()->schema();
63
my $schema = Koha::Database->new()->schema();
63
64
64
my $silent;
65
my ( $silent, $force );
65
GetOptions(
66
GetOptions(
66
    's' =>\$silent
67
    's'     => \$silent,
67
    );
68
    'force' => \$force,
69
);
68
my $dbh = C4::Context->dbh;
70
my $dbh = C4::Context->dbh;
69
$|=1; # flushes output
71
$|=1; # flushes output
70
72
Lines 24283-24291 if( CheckVersion( $DBversion ) ) { Link Here
24283
    NewVersion( $DBversion, "", "Koha 21.05.00 release" );
24285
    NewVersion( $DBversion, "", "Koha 21.05.00 release" );
24284
}
24286
}
24285
24287
24286
$DBversion = '21.06.00.000';
24288
unless ( $ENV{HTTP_HOST} ) { # Is that correct?
24287
if( CheckVersion( $DBversion ) ) {
24289
    my $files = C4::Installer::get_db_entries;
24288
    NewVersion( $DBversion, "", ["🎵 Run, rabbit run. 🎶", "Dig that hole, forget the sun,", "And when at last the work is done", "Don't sit down it's time to dig another one."] );
24290
    my $report = update( $files, { force => $force } );
24291
24292
    for my $s ( @{ $report->{success} } ) {
24293
        say Encode::encode_utf8(join "\n", @{$s->{output}});
24294
    }
24295
    for my $e ( @{ $report->{error} } ) {
24296
        say Encode::encode_utf8(join "\n", @{$e->{output}});
24297
        say Encode::encode_utf8("ERROR - " . $e->{error});
24298
    }
24289
}
24299
}
24290
24300
24291
$DBversion = '21.06.00.001';
24301
$DBversion = '21.06.00.001';
Lines 24383-24535 foreach my $file ( sort readdir $dirh ) { Link Here
24383
    }
24393
    }
24384
}
24394
}
24385
24395
24386
=head1 FUNCTIONS
24387
24388
=head2 DropAllForeignKeys($table)
24389
24390
Drop all foreign keys of the table $table
24391
24392
=cut
24393
24394
sub DropAllForeignKeys {
24395
    my ($table) = @_;
24396
    # get the table description
24397
    my $sth = $dbh->prepare("SHOW CREATE TABLE $table");
24398
    $sth->execute;
24399
    my $vsc_structure = $sth->fetchrow;
24400
    # split on CONSTRAINT keyword
24401
    my @fks = split /CONSTRAINT /,$vsc_structure;
24402
    # parse each entry
24403
    foreach (@fks) {
24404
        # isolate what is before FOREIGN KEY, if there is something, it's a foreign key to drop
24405
        $_ = /(.*) FOREIGN KEY.*/;
24406
        my $id = $1;
24407
        if ($id) {
24408
            # we have found 1 foreign, drop it
24409
            $dbh->do("ALTER TABLE $table DROP FOREIGN KEY $id");
24410
            $id="";
24411
        }
24412
    }
24413
}
24414
24415
24416
=head2 TransformToNum
24417
24418
Transform the Koha version from a 4 parts string
24419
to a number, with just 1 .
24420
24421
=cut
24422
24423
sub TransformToNum {
24424
    my $version = shift;
24425
    # remove the 3 last . to have a Perl number
24426
    $version =~ s/(.*\..*)\.(.*)\.(.*)/$1$2$3/;
24427
    # three X's at the end indicate that you are testing patch with dbrev
24428
    # change it into 999
24429
    # prevents error on a < comparison between strings (should be: lt)
24430
    $version =~ s/XXX$/999/;
24431
    return $version;
24432
}
24433
24434
=head2 SetVersion
24435
24436
set the DBversion in the systempreferences
24437
24438
=cut
24439
24440
sub SetVersion {
24441
    return if $_[0]=~ /XXX$/;
24442
      #you are testing a patch with a db revision; do not change version
24443
    my $kohaversion = TransformToNum($_[0]);
24444
    if (C4::Context->preference('Version')) {
24445
      my $finish=$dbh->prepare("UPDATE systempreferences SET value=? WHERE variable='Version'");
24446
      $finish->execute($kohaversion);
24447
    } else {
24448
      my $finish=$dbh->prepare("INSERT into systempreferences (variable,value,explanation) values ('Version',?,'The Koha database version. WARNING: Do not change this value manually, it is maintained by the webinstaller')");
24449
      $finish->execute($kohaversion);
24450
    }
24451
    C4::Context::clear_syspref_cache(); # invalidate cached preferences
24452
}
24453
24454
sub NewVersion {
24455
    my ( $DBversion, $bug_number, $descriptions ) = @_;
24456
24457
    SetVersion($DBversion);
24458
24459
    unless ( ref($descriptions) ) {
24460
        $descriptions = [ $descriptions ];
24461
    }
24462
    my $first = 1;
24463
    my $time = POSIX::strftime("%H:%M:%S",localtime);
24464
    for my $description ( @$descriptions ) {
24465
        if ( @$descriptions > 1 ) {
24466
            if ( $first ) {
24467
                unless ( $bug_number ) {
24468
                    say sprintf "Upgrade to %s done [%s]: %s", $DBversion, $time, $description;
24469
                } else {
24470
                    say sprintf "Upgrade to %s done [%s]: Bug %5s - %s", $DBversion, $time, $bug_number, $description;
24471
                }
24472
            } else {
24473
                say sprintf "\t\t\t\t\t\t   - %s", $description;
24474
            }
24475
        } else {
24476
            unless ( $bug_number ) {
24477
                say sprintf "Upgrade to %s done [%s]: %s", $DBversion, $time, $description;
24478
            } else {
24479
                say sprintf "Upgrade to %s done [%s]: Bug %5s - %s", $DBversion, $time, $bug_number, $description;
24480
            }
24481
        }
24482
        $first = 0;
24483
    }
24484
}
24485
24486
=head2 CheckVersion
24487
24488
Check whether a given update should be run when passed the proposed version
24489
number. The update will always be run if the proposed version is greater
24490
than the current database version and less than or equal to the version in
24491
kohaversion.pl. The update is also run if the version contains XXX, though
24492
this behavior will be changed following the adoption of non-linear updates
24493
as implemented in bug 7167.
24494
24495
=cut
24496
24497
sub CheckVersion {
24498
    my ($proposed_version) = @_;
24499
    my $version_number = TransformToNum($proposed_version);
24500
24501
    # The following line should be deleted when bug 7167 is pushed
24502
    return 1 if ( $proposed_version =~ m/XXX/ );
24503
24504
    if ( C4::Context->preference("Version") < $version_number
24505
        && $version_number <= TransformToNum( $Koha::VERSION ) )
24506
    {
24507
        return 1;
24508
    }
24509
    else {
24510
        return 0;
24511
    }
24512
}
24513
24514
sub sanitize_zero_date {
24515
    my ( $table_name, $column_name ) = @_;
24516
24517
    my (undef, $datatype) = $dbh->selectrow_array(qq|
24518
        SHOW COLUMNS FROM $table_name WHERE Field = ?|, undef, $column_name);
24519
24520
    if ( $datatype eq 'date' ) {
24521
        $dbh->do(qq|
24522
            UPDATE $table_name
24523
            SET $column_name = NULL
24524
            WHERE CAST($column_name AS CHAR(10)) = '0000-00-00';
24525
        |);
24526
    } else {
24527
        $dbh->do(qq|
24528
            UPDATE $table_name
24529
            SET $column_name = NULL
24530
            WHERE CAST($column_name AS CHAR(19)) = '0000-00-00 00:00:00';
24531
        |);
24532
    }
24533
}
24534
24535
exit;
24396
exit;
(-)a/installer/install.pl (-35 / +11 lines)
Lines 393-433 elsif ( $step && $step == 3 ) { Link Here
393
            chk_log( $logdir, "updatedatabase-error_$filename_suffix" )
393
            chk_log( $logdir, "updatedatabase-error_$filename_suffix" )
394
        );
394
        );
395
395
396
        my $cmd = C4::Context->config("intranetdir")
396
        my $updatedatabase_path = C4::Context->config("intranetdir")
397
          . "/installer/data/$info{dbms}/updatedatabase.pl >> $logfilepath 2>> $logfilepath_errors";
397
          . "/installer/data/$info{dbms}/updatedatabase.pl";
398
398
399
        system($cmd );
399
        my $db_entries = get_db_entries();
400
400
        my $report = update( $db_entries );
401
        my $fh;
401
402
        open( $fh, "<:encoding(utf-8)", $logfilepath )
402
        # FIXME restore log to logfilepath and logfilepath_errors
403
          or die "Cannot open log file $logfilepath: $!";
403
404
        my @report = <$fh>;
404
        $template->param( success => $report->{success}, error => $report->{error} );
405
        close $fh;
405
406
        if (@report) {
406
        #warn "The following errors were returned while attempting to run the updatedatabase.pl script:\n"; #FIXME restore this
407
            $template->param( update_report =>
408
                  [ map { { line => $_ =~ s/\t/&emsp;&emsp;/gr } } split( /\n/, join( '', @report ) ) ]
409
            );
410
            $template->param( has_update_succeeds => 1 );
411
        }
412
        else {
413
            eval { `rm $logfilepath` };
414
        }
415
        open( $fh, "<:encoding(utf-8)", $logfilepath_errors )
416
          or die "Cannot open log file $logfilepath_errors: $!";
417
        @report = <$fh>;
418
        close $fh;
419
        if (@report) {
420
            $template->param( update_errors =>
421
                  [ map { { line => $_ } } split( /\n/, join( '', @report ) ) ]
422
            );
423
            $template->param( has_update_errors => 1 );
424
            warn
425
"The following errors were returned while attempting to run the updatedatabase.pl script:\n";
426
            foreach my $line (@report) { warn "$line\n"; }
427
        }
428
        else {
429
            eval { `rm $logfilepath_errors` };
430
        }
431
        $template->param( $op => 1 );
407
        $template->param( $op => 1 );
432
    }
408
    }
433
    else {
409
    else {
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/installer/step3.tt (-15 / +26 lines)
Lines 244-274 Link Here
244
244
245
                [% IF ( updatestructure ) %]
245
                [% IF ( updatestructure ) %]
246
                    <h2>Updating database structure</h2>
246
                    <h2>Updating database structure</h2>
247
                    [% IF ( has_update_succeeds ) %]
247
                    [% IF success %]
248
                        <p>Update report :</p>
248
                        <p>Update report:</p>
249
                        <ul>
249
                        <ul>
250
                            [% FOREACH l IN update_report %]
250
                            [% FOR s IN success %]
251
                                [% SET line = l.line %]
251
                                [% FOR o IN s.output %]
252
                                [% IF line.match('^Upgrade to') %]
252
                                    <li>[% o | html %]</li>
253
                                    <li>[% line | $raw %]</li>
253
                                    [% IF s.output.size > 1 %]
254
                                [% ELSE %]
254
                                        [% IF loop.first %]<ul>[% ELSIF loop.last %]</ul>[% END %]
255
                                    [% line | $raw %]<br/>
255
                                    [% END %]
256
                                [% END %]
256
                                [% END %]
257
                            [% END %]
257
                            [% END %]
258
                        </ul>
258
                        </ul>
259
                    [% END %]
259
                    [% END %]
260
                    [% IF ( has_update_errors ) %]
260
                    [% IF error %]
261
                        <p>Update errors :</p>
261
                        <p>Update error :</p>
262
                        <ul>
262
                        <ul>
263
                            [% FOREACH update_error IN update_errors %]
263
                            [% FOR e IN error %]
264
                                <li class="update_error">[% update_error.line | html %]</li>
264
                                [% FOR o IN e.output %]
265
                                    <li class="update_error">
266
                                        [% o | html %]
267
                                        <br/>
268
                                        ERROR: [% e.error | html %]
269
270
                                        [% IF e.output.size > 1 %]
271
                                            [% IF loop.first %]<ul>[% ELSIF loop.last %]</ul>[% END %]
272
                                        [% END %]
273
                                    </li>
274
                                [% END %]
265
                            [% END %]
275
                            [% END %]
266
                        </ul>
276
                        </ul>
267
                    [% END %]
277
                    [% END %]
268
                    [% UNLESS ( has_update_errors ) %]
278
                    [% UNLESS error %]
269
                        <p>Everything went okay. Update done.</p>
279
                        <p>Everything went okay. Update done.</p>
280
                        <p><a href="install.pl?step=3&amp;op=finished" class="btn btn-primary">Continue to log in to Koha</a></p>
281
                    [% ELSE %]
282
                        <p><a href="install.pl?step=3&amp;op=updatestructure" class="btn btn-primary">Try again</a></p>
270
                    [% END %]
283
                    [% END %]
271
                    <p><a href="install.pl?step=3&amp;op=finished" class="btn btn-primary">Continue to log in to Koha</a></p>
272
               [% END # / IF updatestructure %]
284
               [% END # / IF updatestructure %]
273
            </div> <!-- / #installer-step3 -->
285
            </div> <!-- / #installer-step3 -->
274
        </div> <!-- / .row -->
286
        </div> <!-- / .row -->
275
- 

Return to bug 25078