| 1 | ################################################################################ |
1 | ################################################################################ |
| 2 | # WeBWorK Online Homework Delivery System |
2 | # WeBWorK Online Homework Delivery System |
| 3 | # Copyright © 2000-2006 The WeBWorK Project, http://openwebwork.sf.net/ |
3 | # Copyright © 2000-2006 The WeBWorK Project, http://openwebwork.sf.net/ |
| 4 | # $CVSHeader: webwork2/lib/WeBWorK/Utils/CourseManagement.pm,v 1.39 2007/03/06 01:33:53 sh002i Exp $ |
4 | # $CVSHeader: webwork2/lib/WeBWorK/Utils/CourseManagement.pm,v 1.40 2007/06/25 12:10:49 gage Exp $ |
| 5 | # |
5 | # |
| 6 | # This program is free software; you can redistribute it and/or modify it under |
6 | # This program is free software; you can redistribute it and/or modify it under |
| 7 | # the terms of either: (a) the GNU General Public License as published by the |
7 | # the terms of either: (a) the GNU General Public License as published by the |
| 8 | # Free Software Foundation; either version 2, or (at your option) any later |
8 | # Free Software Foundation; either version 2, or (at your option) any later |
| 9 | # version, or (b) the "Artistic License" which comes with this package. |
9 | # version, or (b) the "Artistic License" which comes with this package. |
| … | |
… | |
| 282 | courseID => $courseID, |
282 | courseID => $courseID, |
| 283 | ce => $ce, |
283 | ce => $ce, |
| 284 | dbOptions => $dbOptions, |
284 | dbOptions => $dbOptions, |
| 285 | newCourseID => $newCourseID, |
285 | newCourseID => $newCourseID, |
| 286 | |
286 | |
|
|
287 | %options may also contain: |
|
|
288 | |
|
|
289 | skipDBRename => $skipDBRename, |
|
|
290 | |
| 287 | Rename the course named $courseID to $newCourseID. |
291 | Rename the course named $courseID to $newCourseID. |
| 288 | |
292 | |
| 289 | $ce is a WeBWorK::CourseEnvironment object that describes the existing course's |
293 | $ce is a WeBWorK::CourseEnvironment object that describes the existing course's |
| 290 | environment. |
294 | environment. |
| 291 | |
295 | |
| … | |
… | |
| 299 | If the course's database layout is C<sql_single> or C<sql_moodle>, new tables |
303 | If the course's database layout is C<sql_single> or C<sql_moodle>, new tables |
| 300 | are created in the current database, course data is copied from the old tables |
304 | are created in the current database, course data is copied from the old tables |
| 301 | to the new tables, and the old tables are deleted. |
305 | to the new tables, and the old tables are deleted. |
| 302 | |
306 | |
| 303 | If the course's database layout is something else, no database changes are made. |
307 | If the course's database layout is something else, no database changes are made. |
|
|
308 | |
|
|
309 | If $skipDBRename is true, no database changes are made. This is useful if a |
|
|
310 | course is being unarchived and no database was found, or for renaming the |
|
|
311 | modelCourse. |
| 304 | |
312 | |
| 305 | Any errors encountered while renaming the course are returned. |
313 | Any errors encountered while renaming the course are returned. |
| 306 | |
314 | |
| 307 | =cut |
315 | =cut |
| 308 | |
316 | |
| … | |
… | |
| 319 | |
327 | |
| 320 | my $oldCourseID = $options{courseID}; |
328 | my $oldCourseID = $options{courseID}; |
| 321 | my $oldCE = $options{ce}; |
329 | my $oldCE = $options{ce}; |
| 322 | my %dbOptions = defined $options{dbOptions} ? %{ $options{dbOptions} } : (); |
330 | my %dbOptions = defined $options{dbOptions} ? %{ $options{dbOptions} } : (); |
| 323 | my $newCourseID = $options{newCourseID}; |
331 | my $newCourseID = $options{newCourseID}; |
|
|
332 | my $skipDBRename = $options{skipDBRename} || 0; |
| 324 | |
333 | |
| 325 | # get the database layout out of the options hash |
334 | # get the database layout out of the options hash |
| 326 | my $dbLayoutName = $oldCE->{dbLayoutName}; |
335 | my $dbLayoutName = $oldCE->{dbLayoutName}; |
| 327 | |
336 | |
| 328 | # collect some data |
337 | # collect some data |
| … | |
… | |
| 420 | } |
429 | } |
| 421 | } |
430 | } |
| 422 | |
431 | |
| 423 | ##### step 2: rename database ##### |
432 | ##### step 2: rename database ##### |
| 424 | |
433 | |
|
|
434 | unless ($skipDBRename) { |
| 425 | my $oldDB = new WeBWorK::DB($oldCE->{dbLayouts}{$dbLayoutName}); |
435 | my $oldDB = new WeBWorK::DB($oldCE->{dbLayouts}{$dbLayoutName}); |
| 426 | my $rename_db_result = $oldDB->rename_tables($newCE->{dbLayouts}{$dbLayoutName}); |
436 | my $rename_db_result = $oldDB->rename_tables($newCE->{dbLayouts}{$dbLayoutName}); |
| 427 | die "$oldCourseID: course database renaming failed.\n" unless $rename_db_result; |
437 | die "$oldCourseID: course database renaming failed.\n" unless $rename_db_result; |
|
|
438 | } |
| 428 | } |
439 | } |
| 429 | |
440 | |
| 430 | ################################################################################ |
441 | ################################################################################ |
| 431 | |
442 | |
| 432 | =item deleteCourse(%options) |
443 | =item deleteCourse(%options) |
| … | |
… | |
| 529 | |
540 | |
| 530 | =item archiveCourse(%options) |
541 | =item archiveCourse(%options) |
| 531 | |
542 | |
| 532 | %options must contain: |
543 | %options must contain: |
| 533 | |
544 | |
| 534 | courseID => $courseID, |
545 | courseID => $courseID, |
| 535 | ce => $ce, |
546 | ce => $ce, |
| 536 | dbOptions => $dbOptions, |
|
|
| 537 | newCourseID => $newCourseID, |
|
|
| 538 | |
547 | |
| 539 | Archive the course named $courseID in the $webworkDirs{courses} directory |
548 | Creates a gzipped tar archive (.tar.gz) of the course $courseID in the WeBWorK |
| 540 | as $webworkDirs{courses}/$courseID.tar.gz. The data from the database is |
549 | courses directory. Before archiving, the course database is dumped into a |
| 541 | stored in several files at $courseID/DATA/$table_name.txt before the course's directories |
550 | subdirectory of the course's DATA directory. |
| 542 | are tarred and gzipped. The table names are $courseID_user, $courseID_set |
551 | |
| 543 | and so forth. Only files and directories stored directly in the course directory |
552 | Only files and directories stored directly in the course directory are archived. |
| 544 | are archived. The contents of linked files is not archived although the symbolic links |
553 | The contents of linked files is not archived although the symbolic links |
| 545 | themselves are saved. |
554 | themselves are saved. |
| 546 | |
555 | |
|
|
556 | $courseID is the name of the course to archive. |
|
|
557 | |
| 547 | $ce is a WeBWorK::CourseEnvironment object that describes the existing course's |
558 | $ce is a WeBWorK::CourseEnvironment object that describes the course's |
| 548 | environment. |
559 | environment. (This is used to access the course database and get path |
|
|
560 | information.) |
| 549 | |
561 | |
| 550 | $dbOptions is a reference to a hash containing information required to create |
562 | If an error occurs, an exception is thrown. |
| 551 | the course's new database and delete the course's old database. (Currently, |
|
|
| 552 | no information is needed, so this should be an empty hash.) |
|
|
| 553 | |
|
|
| 554 | If the course's database layout is C<sql_single>, the contents of |
|
|
| 555 | the courses database tables are exported to text files using the sql database's |
|
|
| 556 | export facility. Then the tables are deleted from the database. |
|
|
| 557 | |
|
|
| 558 | If the course's database layout is something else, no database changes are made. |
|
|
| 559 | |
|
|
| 560 | Any errors encountered while renaming the course are returned. |
|
|
| 561 | |
563 | |
| 562 | =cut |
564 | =cut |
| 563 | |
565 | |
| 564 | sub archiveCourse { |
566 | sub archiveCourse { |
| 565 | my (%options) = @_; |
567 | my (%options) = @_; |
| 566 | |
|
|
| 567 | # archiveCourseHelper needs: |
|
|
| 568 | # $fromCourseID ($oldCourseID) |
|
|
| 569 | # $fromCE ($ce) |
|
|
| 570 | # $toCourseID ($newCourseID) |
|
|
| 571 | # $toCE (construct from $ce) |
|
|
| 572 | # $dbLayoutName ($ce->{dbLayoutName}) |
|
|
| 573 | # %options ($dbOptions) |
|
|
| 574 | |
|
|
| 575 | my $courseID = $options{courseID}; |
568 | my $courseID = $options{courseID}; |
| 576 | my $ce = $options{ce}; |
569 | my $ce = $options{ce}; |
| 577 | my %dbOptions = defined $options{dbOptions} ? %{ $options{dbOptions} } : (); |
|
|
| 578 | |
|
|
| 579 | |
570 | |
| 580 | # get the database layout out of the options hash |
571 | # make sure the user isn't brain damaged |
| 581 | my $dbLayoutName = $ce->{dbLayoutName}; |
572 | croak "The course environment supplied doesn't appear to describe the course $courseID. Can't proceed" |
|
|
573 | unless $ce->{courseName} eq $courseID; |
| 582 | |
574 | |
| 583 | if (not ref getHelperRef("archiveCourseHelper", $dbLayoutName)) { |
575 | # grab some values we'll need |
| 584 | die "This database layout doesn't support course archiving. Sorry!\n" |
576 | my $course_dir = $ce->{courseDirs}{root}; |
| 585 | } |
577 | my $archive_path = $ce->{webworkDirs}{courses} . "/$courseID.tar.gz"; |
| 586 | |
|
|
| 587 | # collect some data |
|
|
| 588 | my $coursesDir = $ce->{webworkDirs}->{courses}; |
|
|
| 589 | my $courseDir = "$coursesDir/$courseID"; |
|
|
| 590 | my $dataDir = "$courseDir/DATA"; |
578 | my $data_dir = $ce->{courseDirs}{DATA}; |
| 591 | my $archivePath = "$coursesDir/$courseID.tar.gz"; |
579 | my $dump_dir = "$data_dir/mysqldump"; |
| 592 | |
580 | |
| 593 | # create DATA directory if it does not exist. |
|
|
| 594 | unless (-e $dataDir) { |
|
|
| 595 | mkdir "$dataDir" or die "Failed to create course directory $dataDir"; |
|
|
| 596 | } |
|
|
| 597 | # fail if the target file already exists |
|
|
| 598 | if (-e $archivePath) { |
|
|
| 599 | croak "The course $courseID has already been archived at $archivePath"; |
|
|
| 600 | } |
|
|
| 601 | |
581 | |
| 602 | # fail if the source course does not exist |
582 | # fail if the source course does not exist |
| 603 | unless (-e $courseDir) { |
583 | unless (-e $course_dir) { |
| 604 | croak "$courseID: course not found"; |
584 | croak "$courseID: course not found"; |
| 605 | } |
585 | } |
| 606 | |
586 | |
| 607 | $dbOptions{archiveDatabasePath} = "$dataDir/${courseID}_mysql.database"; |
587 | # fail if a course archive already exists |
| 608 | ##### step 1: export database contents ###### |
588 | # FIXME there could be an option to overwrite an existing archive |
| 609 | # munge DB options to move new_database => database |
589 | if (-e $archive_path) { |
| 610 | |
590 | croak "The course '$courseID' has already been archived at '$archive_path'.\n"; |
|
|
591 | } |
| 611 | |
592 | |
| 612 | my $archiveHelperResult = archiveCourseHelper($courseID, $ce, $dbLayoutName, %dbOptions); |
593 | #### step 1: dump tables ##### |
| 613 | die "$courseID: course database dump failed.\n" unless $archiveHelperResult; |
594 | |
| 614 | |
595 | unless (-e $dump_dir) { |
|
|
596 | mkdir $dump_dir or croak "Failed to create course database dump directory '$data_dir': $!"; |
|
|
597 | } |
|
|
598 | |
|
|
599 | my $db = new WeBWorK::DB($ce->{dbLayout}); |
|
|
600 | my $dump_db_result = $db->dump_tables($dump_dir); |
|
|
601 | unless ($dump_db_result) { |
|
|
602 | _archiveCourse_remove_dump_dir($ce, $dump_dir); |
|
|
603 | croak "$courseID: course database dump failed.\n"; |
|
|
604 | } |
|
|
605 | |
| 615 | ##### step 2: tar and gzip course directory ##### |
606 | ##### step 2: tar and gzip course directory (including dumped database) ##### |
| 616 | |
607 | |
| 617 | # archive top-level course directory |
608 | # we want tar to run from the parent directory of the course directory |
|
|
609 | my $chdir_to = "$course_dir/.."; |
|
|
610 | |
| 618 | my $tar_cmd = "2>&1"." ".$ce->{externalPrograms}{tar} |
611 | my $tar_cmd = "2>&1 " . $ce->{externalPrograms}{tar} |
| 619 | . " -C " . shell_quote($coursesDir) |
612 | . " -C " . shell_quote($chdir_to) |
| 620 | . " -czf " . shell_quote($archivePath) |
613 | . " -czf " . shell_quote($archive_path) |
| 621 | . " " . shell_quote($courseID); |
614 | . " " . shell_quote($courseID); |
| 622 | debug("archiving course dir: $tar_cmd\n"); |
|
|
| 623 | my $tar_out = readpipe $tar_cmd; |
615 | my $tar_out = readpipe $tar_cmd; |
| 624 | if ($?) { |
616 | if ($?) { |
| 625 | my $exit = $? >> 8; |
617 | my $exit = $? >> 8; |
| 626 | my $signal = $? & 127; |
618 | my $signal = $? & 127; |
| 627 | my $core = $? & 128; |
619 | my $core = $? & 128; |
|
|
620 | _archiveCourse_remove_dump_dir($ce, $dump_dir); |
| 628 | die "Failed to archive course directory with command '$tar_cmd' (exit=$exit signal=$signal core=$core): $tar_out\n"; |
621 | croak "Failed to archive course directory '$course_dir' with command '$tar_cmd' (exit=$exit signal=$signal core=$core): $tar_out\n"; |
| 629 | } |
622 | } |
|
|
623 | |
|
|
624 | ##### step 3: remove database dump files from course directory ##### |
|
|
625 | |
|
|
626 | _archiveCourse_remove_dump_dir($ce, $dump_dir); |
| 630 | } |
627 | } |
| 631 | |
628 | |
|
|
629 | sub _archiveCourse_remove_dump_dir { |
|
|
630 | my ($ce, $dump_dir) = @_; |
|
|
631 | my $rm_cmd = "2>&1 " . $ce->{externalPrograms}{rm} |
|
|
632 | . " -rf " . shell_quote($dump_dir); |
|
|
633 | my $rm_out = readpipe $rm_cmd; |
|
|
634 | if ($?) { |
|
|
635 | my $exit = $? >> 8; |
|
|
636 | my $signal = $? & 127; |
|
|
637 | my $core = $? & 128; |
|
|
638 | carp "Failed to remove course database dump directory '$dump_dir' with command '$rm_cmd' (exit=$exit signal=$signal core=$core): $rm_out\n"; |
|
|
639 | } |
|
|
640 | } |
|
|
641 | |
| 632 | ################################################################################ |
642 | ################################################################################ |
|
|
643 | |
|
|
644 | =item unarchiveCourse(%options) |
|
|
645 | |
|
|
646 | %options must contain: |
|
|
647 | |
|
|
648 | oldCourseID => $oldCourseID, |
|
|
649 | archivePath => $archivePath, |
|
|
650 | ce => $ce, |
|
|
651 | |
|
|
652 | %options may also contain: |
|
|
653 | |
|
|
654 | newCourseID => $newCourseID, |
|
|
655 | |
|
|
656 | Restores course $oldCourseID from a gzipped tar archive (.tar.gz) located at |
|
|
657 | $archivePath. After unarchiving, the course database is restored from a |
|
|
658 | subdirectory of the course's DATA directory. |
|
|
659 | |
|
|
660 | If $newCourseID is defined and differs from $oldCourseID, the course is renamed |
|
|
661 | after unarchiving. |
|
|
662 | |
|
|
663 | $ce is a WeBWorK::CourseEnvironment object that describes the some course's |
|
|
664 | environment. (Usually this would be the admin course.) This is used to access |
|
|
665 | the course database and get path information. |
|
|
666 | |
|
|
667 | If an error occurs, an exception is thrown. |
|
|
668 | |
|
|
669 | =cut |
| 633 | |
670 | |
| 634 | sub unarchiveCourse { |
671 | sub unarchiveCourse { |
| 635 | my (%options) = @_; |
672 | my (%options) = @_; |
| 636 | |
673 | |
| 637 | # renameCourseHelper needs: |
|
|
| 638 | # $fromCourseID ($oldCourseID) |
|
|
| 639 | # $fromCE ($ce) |
|
|
| 640 | # $toCourseID ($newCourseID) |
|
|
| 641 | # $toCE (construct from $ce) |
|
|
| 642 | # $dbLayoutName ($ce->{dbLayoutName}) |
|
|
| 643 | # %options ($dbOptions) |
|
|
| 644 | |
|
|
| 645 | my $newCourseID = $options{newCourseID}; |
674 | my $newCourseID = $options{newCourseID}; |
| 646 | my $oldCourseID = $options{oldCourseID}; |
675 | my $currCourseID = $options{oldCourseID}; |
| 647 | my $archivePath = $options{archivePath}; |
676 | my $archivePath = $options{archivePath}; |
| 648 | my $ce = $options{ce}; |
677 | my $ce = $options{ce}; |
| 649 | my %dbOptions = defined $options{dbOptions} ? %{ $options{dbOptions} } : (); |
678 | |
| 650 | my $coursesDir = $ce->{webworkDirs}->{courses}; |
679 | my $coursesDir = $ce->{webworkDirs}{courses}; |
| 651 | |
680 | |
| 652 | # Double check that the new course does not exist |
681 | # Double check that the new course does not exist |
| 653 | if (-e "$coursesDir/$newCourseID") { |
682 | if (-e "$coursesDir/$newCourseID") { |
| 654 | die "Cannot overwrite existing course $coursesDir/$newCourseID"; |
683 | die "Cannot overwrite existing course $coursesDir/$newCourseID"; |
| 655 | } |
684 | } |
| 656 | my $restoreCourseData = undef; |
|
|
| 657 | ## |
|
|
| 658 | # Temporarily rename the old course if it exists -- saving data |
|
|
| 659 | ## |
|
|
| 660 | if (-e "$coursesDir/$oldCourseID") { |
|
|
| 661 | my $tmpCourseID = "${oldCourseID}_tmp"; |
|
|
| 662 | |
|
|
| 663 | debug("Moving $oldCourseID to $tmpCourseID"); |
|
|
| 664 | my $tmpce = WeBWorK::CourseEnvironment->new( |
|
|
| 665 | $ce->{webworkDirs}->{root}, |
|
|
| 666 | $ce->{webworkURLs}->{root}, |
|
|
| 667 | $ce->{pg}->{directories}->{root}, |
|
|
| 668 | $tmpCourseID, |
|
|
| 669 | ); |
|
|
| 670 | $restoreCourseData = { |
|
|
| 671 | courseID => $tmpCourseID, # data for restoring from tmpCourse |
|
|
| 672 | ce => $tmpce, |
|
|
| 673 | dbOptions => undef, |
|
|
| 674 | newCourseID => $oldCourseID, |
|
|
| 675 | }; |
|
|
| 676 | renameCourse( |
|
|
| 677 | courseID => $oldCourseID, |
|
|
| 678 | ce => WeBWorK::CourseEnvironment->new( |
|
|
| 679 | $ce->{webworkDirs}->{root}, |
|
|
| 680 | $ce->{webworkURLs}->{root}, |
|
|
| 681 | $ce->{pg}->{directories}->{root},$oldCourseID, |
|
|
| 682 | ), |
|
|
| 683 | dbOptions => undef, |
|
|
| 684 | newCourseID => $tmpCourseID, |
|
|
| 685 | ); |
|
|
| 686 | } |
|
|
| 687 | ## |
|
|
| 688 | # Unarchive the old course |
|
|
| 689 | ## |
|
|
| 690 | |
685 | |
|
|
686 | ##### step 1: move a conflicting course away ##### |
| 691 | |
687 | |
| 692 | my $courseID = $oldCourseID; |
688 | # if this function returns undef, it means there was no course in the way |
| 693 | ############################################################### |
689 | my $restoreCourseData = _unarchiveCourse_move_away($ce, $currCourseID); |
| 694 | # RPC call to tar and gzip the courses directory |
690 | |
| 695 | ############################################################### |
691 | ##### step 2: crack open the tarball ##### |
|
|
692 | |
| 696 | my $tar_cmd = "2>&1"." ".$ce->{externalPrograms}{tar} |
693 | my $tar_cmd = "2>&1 " . $ce->{externalPrograms}{tar} |
| 697 | . " -C " . shell_quote($coursesDir) |
694 | . " -C " . shell_quote($coursesDir) |
| 698 | . " -xzf " . shell_quote($archivePath); |
695 | . " -xzf " . shell_quote($archivePath); |
| 699 | debug("unarchiving course dir: $tar_cmd\n"); |
|
|
| 700 | my $tar_out = readpipe $tar_cmd; |
696 | my $tar_out = readpipe $tar_cmd; |
| 701 | if ($?) { |
697 | if ($?) { |
| 702 | my $exit = $? >> 8; |
698 | my $exit = $? >> 8; |
| 703 | my $signal = $? & 127; |
699 | my $signal = $? & 127; |
| 704 | my $core = $? & 128; |
700 | my $core = $? & 128; |
|
|
701 | _unarchiveCourse_move_back($restoreCourseData); |
| 705 | die "Failed to unarchive course directory with command '$tar_cmd' (exit=$exit signal=$signal core=$core): $tar_out\n"; |
702 | die "Failed to unarchive course directory with command '$tar_cmd' (exit=$exit signal=$signal core=$core): $tar_out\n"; |
| 706 | } |
703 | } |
| 707 | ############################################################### |
|
|
| 708 | # End RPC call to tar and gzip the courses directory |
|
|
| 709 | ############################################################### |
|
|
| 710 | |
704 | |
| 711 | # read the global.conf and course.conf files for the newly created course |
705 | ##### step 3: read the course environment for this course ##### |
| 712 | debug( "Checking that course directory is at $coursesDir/$courseID: = ", -e "$coursesDir/$courseID"); |
706 | |
| 713 | my $ce2 = WeBWorK::CourseEnvironment->new( |
707 | my $ce2 = new WeBWorK::CourseEnvironment( |
|
|
708 | $ce->{webworkDirs}{root}, |
|
|
709 | $ce->{webworkURLs}{root}, |
|
|
710 | $ce->{pg}{directories}{root}, |
|
|
711 | $currCourseID, |
|
|
712 | ); |
|
|
713 | |
|
|
714 | # pull out some useful stuff |
|
|
715 | my $course_dir = $ce2->{courseDirs}{root}; |
|
|
716 | my $data_dir = $ce2->{courseDirs}{DATA}; |
|
|
717 | my $dump_dir = "$data_dir/mysqldump"; |
|
|
718 | my $old_dump_file = "$data_dir/${currCourseID}_mysql.database"; |
|
|
719 | |
|
|
720 | ##### step 4: restore the database tables ##### |
|
|
721 | |
|
|
722 | my $no_database; |
|
|
723 | my $restore_db_result = 1; |
|
|
724 | if (-e $dump_dir) { |
|
|
725 | my $db = new WeBWorK::DB($ce2->{dbLayout}); |
|
|
726 | $restore_db_result = $db->restore_tables($dump_dir); |
|
|
727 | } elsif (-e $old_dump_file) { |
|
|
728 | my $dbLayoutName = $ce2->{dbLayoutName}; |
|
|
729 | if (ref getHelperRef("unarchiveCourseHelper", $dbLayoutName)) { |
|
|
730 | eval { |
|
|
731 | $restore_db_result = unarchiveCourseHelper($currCourseID, $ce2, $dbLayoutName, |
|
|
732 | unarchiveDatabasePath=>$old_dump_file); |
|
|
733 | }; |
|
|
734 | if ($@) { |
|
|
735 | warn "failed to unarchive course database from dump file '$old_dump_file: $@\n"; |
|
|
736 | } |
|
|
737 | } else { |
|
|
738 | warn "course '$currCourseID' uses dbLayout '$dbLayoutName', which doesn't support restoring database tables. database tables will not be restored.\n"; |
|
|
739 | $no_database = 1; |
|
|
740 | } |
|
|
741 | } else { |
|
|
742 | warn "course '$currCourseID' has no database dump in its data directory (checked for $dump_dir and $old_dump_file). database tables will not be restored.\n"; |
|
|
743 | $no_database = 1; |
|
|
744 | } |
|
|
745 | |
|
|
746 | unless ($restore_db_result) { |
|
|
747 | warn "database restore of course '$currCourseID' failed: the course will probably not be usable.\n"; |
|
|
748 | } |
|
|
749 | |
|
|
750 | ##### step 5: delete dump_dir and/or old_dump_file ##### |
|
|
751 | |
|
|
752 | if (-e $dump_dir) { |
|
|
753 | _archiveCourse_remove_dump_dir($ce, $dump_dir); |
|
|
754 | } |
|
|
755 | if (-e $old_dump_file) { |
|
|
756 | unlink $old_dump_file or carp "Failed to unlink course database dump file '$old_dump_file: $_\n"; |
|
|
757 | } |
|
|
758 | |
|
|
759 | ##### step 6: rename course ##### |
|
|
760 | |
|
|
761 | if (defined $newCourseID and $newCourseID ne $currCourseID) { |
|
|
762 | renameCourse( |
|
|
763 | courseID => $currCourseID, |
|
|
764 | ce => $ce2, |
|
|
765 | newCourseID => $newCourseID, |
|
|
766 | skipDBRename => $no_database, |
|
|
767 | ); |
|
|
768 | } |
|
|
769 | |
|
|
770 | ##### step 7: return conflicting course to its rightful place ##### |
|
|
771 | |
|
|
772 | _unarchiveCourse_move_back($restoreCourseData); |
|
|
773 | } |
|
|
774 | |
|
|
775 | sub _unarchiveCourse_move_away { |
|
|
776 | my ($ce, $courseID) = @_; |
|
|
777 | |
|
|
778 | # course environment for before the course is moved |
|
|
779 | my $ce2 = new WeBWorK::CourseEnvironment( |
| 714 | $ce->{webworkDirs}->{root}, |
780 | $ce->{webworkDirs}->{root}, |
| 715 | $ce->{webworkURLs}->{root}, |
781 | $ce->{webworkURLs}->{root}, |
| 716 | $ce->{pg}->{directories}->{root}, |
782 | $ce->{pg}->{directories}->{root}, |
| 717 | $courseID, |
783 | $courseID, |
| 718 | ); |
784 | ); |
| 719 | my $courseDir = "$coursesDir/$courseID"; |
|
|
| 720 | my $dataDir = "$courseDir/DATA"; |
|
|
| 721 | |
|
|
| 722 | #get the database layout out of the options hash |
|
|
| 723 | my $dbLayoutName = $ce2->{dbLayoutName}; |
|
|
| 724 | |
|
|
| 725 | if (not ref getHelperRef("unarchiveCourseHelper", $dbLayoutName)) { |
|
|
| 726 | die "This database layout doesn't support course archiving. Sorry!\n" |
|
|
| 727 | } |
|
|
| 728 | $dbOptions{unarchiveDatabasePath} = "$dataDir/${courseID}_mysql.database"; |
|
|
| 729 | # import database tables |
|
|
| 730 | my $unarchiveHelperResult = unarchiveCourseHelper($courseID, $ce, $dbLayoutName, %dbOptions); |
|
|
| 731 | die "$courseID: unable to import tables into database.\n" unless $unarchiveHelperResult; |
|
|
| 732 | |
785 | |
| 733 | ## |
786 | # if course directory doesn't exist, we don't have to do anything |
| 734 | # Change the unarchived course to the new course name if they are different |
787 | return unless -e $ce2->{courseDirs}{root}; |
| 735 | ## |
788 | |
| 736 | if ($courseID ne $newCourseID) { |
789 | # temporary name for course |
| 737 | |
790 | my $tmpCourseID = "${courseID}_tmp"; |
| 738 | debug("rename $courseID to $newCourseID"); |
791 | |
|
|
792 | debug("Temporarily moving $courseID to $tmpCourseID to make room for course unarchiving"); |
|
|
793 | renameCourse( |
|
|
794 | courseID => $courseID, |
|
|
795 | ce => $ce2, |
|
|
796 | newCourseID => $tmpCourseID, |
|
|
797 | ); |
|
|
798 | |
|
|
799 | # course environment for after the course is moved |
| 739 | my $oldce = WeBWorK::CourseEnvironment->new( |
800 | my $ce3 = new WeBWorK::CourseEnvironment( |
| 740 | $ce->{webworkDirs}->{root}, |
801 | $ce->{webworkDirs}->{root}, |
| 741 | $ce->{webworkURLs}->{root}, |
802 | $ce->{webworkURLs}->{root}, |
| 742 | $ce->{pg}->{directories}->{root}, |
803 | $ce->{pg}->{directories}->{root}, |
| 743 | $courseID, |
804 | $tmpCourseID, |
| 744 | ); |
805 | ); |
| 745 | renameCourse( |
806 | |
|
|
807 | # data to pass to renameCourse when moving the course back to it's original name |
|
|
808 | my $restore_course_data = { |
| 746 | courseID => $courseID, |
809 | courseID => $tmpCourseID, |
| 747 | ce => $oldce, |
810 | ce => $ce3, # course environment for moved course |
| 748 | dbOptions => undef, |
|
|
| 749 | newCourseID => $newCourseID, |
811 | newCourseID => $courseID, |
| 750 | ); |
|
|
| 751 | } |
812 | }; |
| 752 | if (defined($restoreCourseData) ) { |
|
|
| 753 | |
813 | |
| 754 | ## |
814 | return $restore_course_data; |
| 755 | # Rename the temporary old course back to old course |
815 | } |
| 756 | ## |
|
|
| 757 | debug("rename ".$restoreCourseData->{courseID}. " to ". $restoreCourseData->{newCourseID}); |
|
|
| 758 | renameCourse( |
|
|
| 759 | %{$restoreCourseData} |
|
|
| 760 | ); |
|
|
| 761 | |
816 | |
| 762 | } |
817 | sub _unarchiveCourse_move_back { |
|
|
818 | my ($restore_course_data) = @_; |
|
|
819 | |
|
|
820 | return unless $restore_course_data; |
|
|
821 | |
|
|
822 | debug("Moving $$restore_course_data{courseID} back to $$restore_course_data{newCourseID} after course unarchiving"); |
|
|
823 | renameCourse(%$restore_course_data); |
| 763 | } |
824 | } |
| 764 | |
825 | |
| 765 | ################################################################################ |
826 | ################################################################################ |
| 766 | |
827 | |
| 767 | =item dbLayoutSQLSources($dbLayout) |
828 | =item dbLayoutSQLSources($dbLayout) |