MDL-50602 - Automated backup: Remove old backups associated to courses
authorJean-Philippe Gaudreau <jp.gaudreau@umontreal.ca>
Mon, 17 Aug 2015 19:22:08 +0000 (15:22 -0400)
committerJean-Philippe Gaudreau <jp.gaudreau@umontreal.ca>
Thu, 24 Sep 2015 18:08:17 +0000 (14:08 -0400)
Refactoring and renaming of settings.

admin/settings/courses.php
backup/upgrade.txt
backup/util/helper/backup_cron_helper.class.php
backup/util/helper/tests/cronhelper_test.php
lang/en/backup.php
lang/en/moodle.php
lib/db/upgrade.php
version.php

index 88f99c3..bd88630 100644 (file)
@@ -226,7 +226,8 @@ if ($hassiteconfig or has_any_capability($capabilities, $systemcontext)) {
     );
     $temp->add(new admin_setting_configselect('backup/backup_auto_storage', new lang_string('automatedstorage', 'backup'), new lang_string('automatedstoragehelp', 'backup'), 0, $storageoptions));
     $temp->add(new admin_setting_special_backup_auto_destination());
-    $keepoptoins = array(
+
+    $maxkeptoptions = array(
         0 => new lang_string('all'), 1 => '1',
         2 => '2',
         5 => '5',
@@ -240,34 +241,10 @@ if ($hassiteconfig or has_any_capability($capabilities, $systemcontext)) {
         300 => '300',
         400 => '400',
         500 => '500');
-    $temp->add(new admin_setting_configselect('backup/backup_auto_keep', new lang_string('keep'), new lang_string('backupkeephelp'), 1, $keepoptoins));
-    $temp->add(new admin_setting_configcheckbox('backup/backup_shortname', new lang_string('backup_shortname', 'admin'), new lang_string('backup_shortnamehelp', 'admin'), 0));
-    $temp->add(new admin_setting_configcheckbox('backup/backup_auto_skip_hidden', new lang_string('skiphidden', 'backup'), new lang_string('skiphiddenhelp', 'backup'), 1));
-    $temp->add(new admin_setting_configselect('backup/backup_auto_skip_modif_days', new lang_string('skipmodifdays', 'backup'), new lang_string('skipmodifdayshelp', 'backup'), 30, array(
-        0 => new lang_string('never'),
-        1 => new lang_string('numdays', '', 1),
-        2 => new lang_string('numdays', '', 2),
-        3 => new lang_string('numdays', '', 3),
-        5 => new lang_string('numdays', '', 5),
-        7 => new lang_string('numdays', '', 7),
-        10 => new lang_string('numdays', '', 10),
-        14 => new lang_string('numdays', '', 14),
-        20 => new lang_string('numdays', '', 20),
-        30 => new lang_string('numdays', '', 30),
-        60 => new lang_string('numdays', '', 60),
-        90 => new lang_string('numdays', '', 90),
-        120 => new lang_string('numdays', '', 120),
-        180 => new lang_string('numdays', '', 180),
-        365 => new lang_string('numdays', '', 365)
-    )));
-    $temp->add(new admin_setting_configcheckbox('backup/backup_auto_skip_modif_prev', new lang_string('skipmodifprev', 'backup'), new lang_string('skipmodifprevhelp', 'backup'), 0));
+    $temp->add(new admin_setting_configselect('backup/backup_auto_max_kept', new lang_string('automatedmaxkept', 'backup'),
+            new lang_string('automatedmaxkepthelp', 'backup'), 1, $maxkeptoptions));
 
-    // Automated backup removal section.
-    $temp->add(new admin_setting_heading('automatedbackupremovalsettings',
-            new lang_string('automatedbackupremovalsettings', 'backup'), ''));
-
-    $temp->add(new admin_setting_configselect('backup/backup_auto_keep_days', new lang_string('automatedbackupkeepdays', 'backup'),
-            new lang_string('automatedbackupkeepdayshelp', 'backup'), 0, array(
+    $automateddeletedaysoptions = array(
         0 => new lang_string('never'),
         1000 => new lang_string('numdays', '', 1000),
         365  => new lang_string('numdays', '', 365),
@@ -280,11 +257,11 @@ if ($hassiteconfig or has_any_capability($capabilities, $systemcontext)) {
         10   => new lang_string('numdays', '', 10),
         5    => new lang_string('numdays', '', 5),
         2    => new lang_string('numdays', '', 2)
-    )));
+    );
+    $temp->add(new admin_setting_configselect('backup/backup_auto_delete_days', new lang_string('automateddeletedays', 'backup'),
+            '', 0, $automateddeletedaysoptions));
 
-    $temp->add(new admin_setting_configselect('backup/backup_auto_keep_copies',
-            new lang_string('automatedbackupkeepcopies', 'backup'), new lang_string('automatedbackupkeepcopieshelp', 'backup'),
-            0, array(
+    $minkeptoptions = array(
         0 => new lang_string('none'),
         1 => '1',
         2 => '2',
@@ -297,9 +274,31 @@ if ($hassiteconfig or has_any_capability($capabilities, $systemcontext)) {
         100 => '100',
         200 => '200',
         300 => '300',
-        400 => '400',
-        500 => '500'
+        400 => '400'
+    );
+    $temp->add(new admin_setting_configselect('backup/backup_auto_min_kept', new lang_string('automatedminkept', 'backup'),
+            new lang_string('automatedminkepthelp', 'backup'), 0, $minkeptoptions));
+
+    $temp->add(new admin_setting_configcheckbox('backup/backup_shortname', new lang_string('backup_shortname', 'admin'), new lang_string('backup_shortnamehelp', 'admin'), 0));
+    $temp->add(new admin_setting_configcheckbox('backup/backup_auto_skip_hidden', new lang_string('skiphidden', 'backup'), new lang_string('skiphiddenhelp', 'backup'), 1));
+    $temp->add(new admin_setting_configselect('backup/backup_auto_skip_modif_days', new lang_string('skipmodifdays', 'backup'), new lang_string('skipmodifdayshelp', 'backup'), 30, array(
+        0 => new lang_string('never'),
+        1 => new lang_string('numdays', '', 1),
+        2 => new lang_string('numdays', '', 2),
+        3 => new lang_string('numdays', '', 3),
+        5 => new lang_string('numdays', '', 5),
+        7 => new lang_string('numdays', '', 7),
+        10 => new lang_string('numdays', '', 10),
+        14 => new lang_string('numdays', '', 14),
+        20 => new lang_string('numdays', '', 20),
+        30 => new lang_string('numdays', '', 30),
+        60 => new lang_string('numdays', '', 60),
+        90 => new lang_string('numdays', '', 90),
+        120 => new lang_string('numdays', '', 120),
+        180 => new lang_string('numdays', '', 180),
+        365 => new lang_string('numdays', '', 365)
     )));
+    $temp->add(new admin_setting_configcheckbox('backup/backup_auto_skip_modif_prev', new lang_string('skipmodifprev', 'backup'), new lang_string('skipmodifprevhelp', 'backup'), 0));
 
     // Automated defaults section.
     $temp->add(new admin_setting_heading('automatedsettings', new lang_string('automatedsettings','backup'), ''));
index 26e2e00..d9c0667 100644 (file)
@@ -1,6 +1,11 @@
 This files describes API changes in /backup/*,
 information provided here is intended especially for developers.
 
+=== 3.0 ===
+
+* The backup_auto_keep setting, in automated backups configuration, is now
+  renamed to backup_auto_max_kept.
+
 === 2.6 ===
 
 * The backup_controller_dbops::create_temptable_from_real_table()
index 5dff97d..d864076 100644 (file)
@@ -198,17 +198,18 @@ abstract class backup_cron_automated_helper {
                             $DB->update_record('backup_courses', $backupcourse);
 
                             $backupcourse->laststatus = self::launch_automated_backup($course, $backupcourse->laststarttime,
-                                                                                      $admin->id);
+                                    $admin->id);
                             $backupcourse->lastendtime = time();
                             $backupcourse->nextstarttime = $nextstarttime;
 
                             $DB->update_record('backup_courses', $backupcourse);
+
+                            mtrace("complete - next execution: $showtime");
                         }
                     }
 
                     // Remove excess backups.
                     $removedcount = self::remove_excess_backups($course, $now);
-                    mtrace("complete - next execution: $showtime");
                 }
             }
             $rs->close();
@@ -536,183 +537,198 @@ abstract class backup_cron_automated_helper {
     }
 
     /**
-     * Removes excess backups from the external system and the local file system.
-     *
-     * The number of backups keep comes from $config->backup_auto_keep.
+     * Removes excess backups from a specified course.
      *
-     * @param stdClass $course object
-     * @param int $now execution time
-     * @return bool
+     * @param stdClass $course Course object
+     * @param int $now Starting time of the process
+     * @return bool Whether or not backups is being removed
      */
-    public static function remove_excess_backups($course, $now) {
+    public static function remove_excess_backups($course, $now = null) {
         $config = get_config('backup');
-        $keep =     (int)$config->backup_auto_keep;
-        $storage =  $config->backup_auto_storage;
-        $dir =      $config->backup_auto_destination;
-        $histdays = (int)$config->backup_auto_keep_days;
+        $maxkept = (int)$config->backup_auto_max_kept;
+        $storage = $config->backup_auto_storage;
+        $deletedays = (int)$config->backup_auto_delete_days;
 
-        if ($keep == 0 && $histdays == 0) {
-            // Means keep all backup files and never remove backup after x days.
+        if ($maxkept == 0 && $deletedays == 0) {
+            // Means keep all backup files and never delete backup after x days.
             return true;
         }
 
-        if (!file_exists($dir) || !is_dir($dir) || !is_writable($dir)) {
-            $dir = null;
+        if (!isset($now)) {
+            $now = time();
         }
 
         // Clean up excess backups in the course backup filearea.
+        $deletedcoursebackups = false;
         if ($storage == 0 || $storage == 2) {
-            $fs = get_file_storage();
-            $context = context_course::instance($course->id);
-            $component = 'backup';
-            $filearea = 'automated';
-            $itemid = 0;
-            $files = array();
-            // Store all the matching files into timemodified => stored_file array.
-            foreach ($fs->get_area_files($context->id, $component, $filearea, $itemid) as $file) {
-                $files[$file->get_timemodified()] = $file;
-            }
-
-            $backupremoved = self::remove_old_backups($files, true, $now, $course->idnumber);
-            if (!$backupremoved) {
-                return 0;
-            }
+            $deletedcoursebackups = self::remove_excess_backups_from_course($course, $now);
         }
 
         // Clean up excess backups in the specified external directory.
-        if (!empty($dir) && ($storage == 1 || $storage == 2)) {
-            // Calculate backup filename regex, ignoring the date/time/info parts that can be
-            // variable, depending of languages, formats and automated backup settings.
-            $filename = backup::FORMAT_MOODLE . '-' . backup::TYPE_1COURSE . '-' . $course->id . '-';
-            $regex = '#' . preg_quote($filename, '#') . '.*\.mbz$#';
-
-            // Store all the matching files into filename => timemodified array.
-            $files = array();
-            foreach (scandir($dir) as $file) {
-                // Skip files not matching the naming convention.
-                if (!preg_match($regex, $file, $matches)) {
-                    continue;
-                }
-
-                // Read the information contained in the backup itself.
-                try {
-                    $bcinfo = backup_general_helper::get_backup_information_from_mbz($dir . '/' . $file);
-                } catch (backup_helper_exception $e) {
-                    mtrace('Error: ' . $file . ' does not appear to be a valid backup (' . $e->errorcode . ')');
-                    continue;
-                }
-
-                // Make sure this backup concerns the course and site we are looking for.
-                if ($bcinfo->format === backup::FORMAT_MOODLE &&
-                        $bcinfo->type === backup::TYPE_1COURSE &&
-                        $bcinfo->original_course_id == $course->id &&
-                        backup_general_helper::backup_is_samesite($bcinfo)) {
-                    $files[$file] = $bcinfo->backup_date;
-                }
-            }
-
-            $backupremoved = self::remove_old_backups($files, false, $now, $course->idnumber);
-            if (!$backupremoved) {
-                return 0;
-            }
+        $deleteddirectorybackups = false;
+        if ($storage == 1 || $storage == 2) {
+            $deleteddirectorybackups = self::remove_excess_backups_from_directory($course, $now);
         }
 
-        return true;
+        if ($deletedcoursebackups || $deleteddirectorybackups) {
+            return true;
+        } else {
+            return false;
+        }
     }
 
     /**
-     * Check logs to find out if a course was modified since the given time.
-     *
-     * @param int $courseid course id to check
-     * @param int $since timestamp, from which to check
+     * Removes excess backups in the course backup filearea from a specified course.
      *
-     * @return bool true if the course was modified, false otherwise. This also returns false if no readers are enabled. This is
-     * intentional, since we cannot reliably determine if any modification was made or not.
+     * @param stdClass $course Course object
+     * @param int $now Starting time of the process
+     * @return bool Whether or not backups are being removed
      */
-    protected static function is_course_modified($courseid, $since) {
-        $logmang = get_log_manager();
-        $readers = $logmang->get_readers('core\log\sql_reader');
-        $where = "courseid = :courseid and timecreated > :since and crud <> 'r'";
-        $params = array('courseid' => $courseid, 'since' => $since);
-        foreach ($readers as $reader) {
-            if ($reader->get_events_select_count($where, $params)) {
-                return true;
+    protected static function remove_excess_backups_from_course($course, $now) {
+        $fs = get_file_storage();
+        $context = context_course::instance($course->id);
+        $component = 'backup';
+        $filearea = 'automated';
+        $itemid = 0;
+        $backupfiles = array();
+        $backupfilesarea = $fs->get_area_files($context->id, $component, $filearea, $itemid, 'timemodified DESC', false);
+        // Store all the matching files into timemodified => stored_file array.
+        foreach ($backupfilesarea as $backupfile) {
+            $backupfiles[$backupfile->get_timemodified()] = $backupfile;
+        }
+
+        $backupstodelete = self::get_backups_to_delete($backupfiles, $now);
+        if ($backupstodelete) {
+            foreach ($backupstodelete as $backuptodelete) {
+                $backuptodelete->delete();
             }
+            mtrace('Deleted ' . count($backupstodelete) . ' old backup file(s) from the automated filearea');
+            return true;
+        } else {
+            return false;
         }
-        return false;
     }
 
     /**
-     * Removes excess and old backups from the external system or the local file system.
+     * Removes excess backups in the specified external directory from a specified course.
      *
-     * The number of backups to keep comes from $config->backup_auto_keep and
-     * $config->backup_auto_keep_copies.
-     *
-     * @param array $files existing backups
-     * @param bool $timeinkey endicate if the backup's time is in the key of the array
-     * @param int $now starting time of the process
-     * @param string $courseidnumber courseidnumer currently processed
-     * @return bool
+     * @param stdClass $course Course object
+     * @param int $now Starting time of the process
+     * @return bool Whether or not backups are being removed
      */
-    protected static function remove_old_backups($files, $timeinkey, $now, $courseidnumber) {
+    protected static function remove_excess_backups_from_directory($course, $now) {
         $config = get_config('backup');
         $dir = $config->backup_auto_destination;
-        $keep = (int)$config->backup_auto_keep;
-        $histkeep = (int)$config->backup_auto_keep_copies;
-        $histdays = (int)$config->backup_auto_keep_days;
 
-        if ($timeinkey) {
-            // Sort by keys descending (newer to older filemodified).
-            krsort($files);
-        } else {
-            // Sort by values descending (newer to older filemodified).
-            arsort($files);
+        $isnotvaliddir = !file_exists($dir) || !is_dir($dir) || !is_writable($dir);
+        if ($isnotvaliddir) {
+            mtrace('Error: ' . $dir . ' does not appear to be a valid directory');
+            return false;
         }
 
-        $tokeep = $keep;
-        $oldbackup = false;
-        if ($histdays != 0) {
-            $keepcounter = 0;
-            foreach ($files as $key => $value) {
-                if ($timeinkey) {
-                    $timemodified = $key;
-                } else {
-                    $timemodified = $value;
-                }
-                if ($timemodified < ($now - ($histdays * DAYSECS)) || $keepcounter >= $keep) {
-                    $oldbackup = true;
-                    break;
-                }
-                $keepcounter++;
+        // Calculate backup filename regex, ignoring the date/time/info parts that can be
+        // variable, depending of languages, formats and automated backup settings.
+        $filename = backup::FORMAT_MOODLE . '-' . backup::TYPE_1COURSE . '-' . $course->id . '-';
+        $regex = '#' . preg_quote($filename, '#') . '.*\.mbz$#';
+
+        // Store all the matching files into filename => timemodified array.
+        $backupfiles = array();
+        foreach (scandir($dir) as $backupfile) {
+            // Skip files not matching the naming convention.
+            if (!preg_match($regex, $backupfile)) {
+                continue;
             }
 
-            if ($keepcounter < $histkeep) {
-                $tokeep = $histkeep;
-            } else {
-                $tokeep = $keepcounter;
+            // Read the information contained in the backup itself.
+            try {
+                $bcinfo = backup_general_helper::get_backup_information_from_mbz($dir . '/' . $backupfile);
+            } catch (backup_helper_exception $e) {
+                mtrace('Error: ' . $backupfile . ' does not appear to be a valid backup (' . $e->errorcode . ')');
+                continue;
+            }
+
+            // Make sure this backup concerns the course and site we are looking for.
+            if ($bcinfo->format === backup::FORMAT_MOODLE &&
+                    $bcinfo->type === backup::TYPE_1COURSE &&
+                    $bcinfo->original_course_id == $course->id &&
+                    backup_general_helper::backup_is_samesite($bcinfo)) {
+                $backupfiles[$bcinfo->backup_date] = $backupfile;
             }
         }
 
-        if (count($files) <= $tokeep) {
-            // There are less matching files than the desired number to keep there is nothing to clean up.
+        $backupstodelete = self::get_backups_to_delete($backupfiles, $now);
+        if ($backupstodelete) {
+            foreach ($backupstodelete as $backuptodelete) {
+                unlink($dir . '/' . $backuptodelete);
+            }
+            mtrace('Deleted ' . count($backupstodelete) . ' old backup file(s) from external directory');
+            return true;
+        } else {
             return false;
         }
+    }
 
-        $remove = array_splice($files, $tokeep);
-        if ($timeinkey) {
-            foreach ($remove as $file) {
-                $file->delete();
+    /**
+     * Get the list of backup files to delete depending on the automated backup settings.
+     *
+     * @param array $backupfiles Existing backup files
+     * @param int $now Starting time of the process
+     * @return array Backup files to delete
+     */
+    protected static function get_backups_to_delete($backupfiles, $now) {
+        $config = get_config('backup');
+        $maxkept = (int)$config->backup_auto_max_kept;
+        $deletedays = (int)$config->backup_auto_delete_days;
+        $minkept = (int)$config->backup_auto_min_kept;
+
+        // Sort by keys descending (newer to older filemodified).
+        krsort($backupfiles);
+        $tokeep = $maxkept;
+        if ($deletedays > 0) {
+            $deletedayssecs = $deletedays * DAYSECS;
+            $tokeep = 0;
+            $backupfileskeys = array_keys($backupfiles);
+            foreach ($backupfileskeys as $timemodified) {
+                $mustdeletebackup = $timemodified < ($now - $deletedayssecs);
+                if ($mustdeletebackup || $tokeep >= $maxkept) {
+                    break;
+                }
+                $tokeep++;
             }
-        } else {
-            foreach (array_keys($remove) as $file) {
-                unlink($dir . '/' . $file);
+
+            if ($tokeep < $minkept) {
+                $tokeep = $minkept;
             }
         }
 
-        if ($oldbackup) {
-            mtrace('Course '. $courseidnumber. ' removed '.count($remove).' old backup file(s) from the automated filearea');
+        if (count($backupfiles) <= $tokeep) {
+            // There are less or equal matching files than the desired number to keep, there is nothing to clean up.
+            return false;
+        } else {
+            $backupstodelete = array_splice($backupfiles, $tokeep);
+            return $backupstodelete;
         }
+    }
 
-        return true;
+    /**
+     * Check logs to find out if a course was modified since the given time.
+     *
+     * @param int $courseid course id to check
+     * @param int $since timestamp, from which to check
+     *
+     * @return bool true if the course was modified, false otherwise. This also returns false if no readers are enabled. This is
+     * intentional, since we cannot reliably determine if any modification was made or not.
+     */
+    protected static function is_course_modified($courseid, $since) {
+        $logmang = get_log_manager();
+        $readers = $logmang->get_readers('core\log\sql_reader');
+        $where = "courseid = :courseid and timecreated > :since and crud <> 'r'";
+        $params = array('courseid' => $courseid, 'since' => $since);
+        foreach ($readers as $reader) {
+            if ($reader->get_events_select_count($where, $params)) {
+                return true;
+            }
+        }
+        return false;
     }
 }
index cb88d53..320daac 100644 (file)
@@ -244,4 +244,100 @@ class backup_cron_helper_testcase extends advanced_testcase {
         $next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
         $this->assertEquals(date('w-20:00'), date('w-H:i', $next));
     }
+
+    /**
+     * Test {@link backup_cron_automated_helper::get_backups_to_delete}.
+     */
+    public function test_get_backups_to_delete() {
+        $this->resetAfterTest();
+        // Active only backup_auto_max_kept config to 2 days.
+        set_config('backup_auto_max_kept', '2', 'backup');
+        set_config('backup_auto_delete_days', '0', 'backup');
+        set_config('backup_auto_min_kept', '0', 'backup');
+
+        // No backups to delete.
+        $backupfiles = array(
+            '1000000000' => 'file1.mbz',
+            '1000432000' => 'file3.mbz'
+        );
+        $deletedbackups = testable_backup_cron_automated_helper::testable_get_backups_to_delete($backupfiles, 1000432000);
+        $this->assertFalse($deletedbackups);
+
+        // Older backup to delete.
+        $backupfiles['1000172800'] = 'file2.mbz';
+        $deletedbackups = testable_backup_cron_automated_helper::testable_get_backups_to_delete($backupfiles, 1000432000);
+        $this->assertEquals(1, count($deletedbackups));
+        $this->assertArrayHasKey('1000000000', $backupfiles);
+        $this->assertEquals('file1.mbz', $backupfiles['1000000000']);
+
+        // Activate backup_auto_max_kept to 5 days and backup_auto_delete_days to 10 days.
+        set_config('backup_auto_max_kept', '5', 'backup');
+        set_config('backup_auto_delete_days', '10', 'backup');
+        set_config('backup_auto_min_kept', '0', 'backup');
+
+        // No backups to delete. Timestamp is 1000000000 + 10 days.
+        $backupfiles['1000432001'] = 'file4.mbz';
+        $backupfiles['1000864000'] = 'file5.mbz';
+        $deletedbackups = testable_backup_cron_automated_helper::testable_get_backups_to_delete($backupfiles, 1000864000);
+        $this->assertFalse($deletedbackups);
+
+        // One old backup to delete. Timestamp is 1000000000 + 10 days + 1 second.
+        $deletedbackups = testable_backup_cron_automated_helper::testable_get_backups_to_delete($backupfiles, 1000864001);
+        $this->assertEquals(1, count($deletedbackups));
+        $this->assertArrayHasKey('1000000000', $backupfiles);
+        $this->assertEquals('file1.mbz', $backupfiles['1000000000']);
+
+        // Two old backups to delete. Timestamp is 1000000000 + 12 days + 1 second.
+        $deletedbackups = testable_backup_cron_automated_helper::testable_get_backups_to_delete($backupfiles, 1001036801);
+        $this->assertEquals(2, count($deletedbackups));
+        $this->assertArrayHasKey('1000000000', $backupfiles);
+        $this->assertEquals('file1.mbz', $backupfiles['1000000000']);
+        $this->assertArrayHasKey('1000172800', $backupfiles);
+        $this->assertEquals('file2.mbz', $backupfiles['1000172800']);
+
+        // Activate backup_auto_max_kept to 5 days, backup_auto_delete_days to 10 days and backup_auto_min_kept to 2.
+        set_config('backup_auto_max_kept', '5', 'backup');
+        set_config('backup_auto_delete_days', '10', 'backup');
+        set_config('backup_auto_min_kept', '2', 'backup');
+
+        // Three instead of four old backups are deleted. Timestamp is 1000000000 + 16 days.
+        $deletedbackups = testable_backup_cron_automated_helper::testable_get_backups_to_delete($backupfiles, 1001382400);
+        $this->assertEquals(3, count($deletedbackups));
+        $this->assertArrayHasKey('1000000000', $backupfiles);
+        $this->assertEquals('file1.mbz', $backupfiles['1000000000']);
+        $this->assertArrayHasKey('1000172800', $backupfiles);
+        $this->assertEquals('file2.mbz', $backupfiles['1000172800']);
+        $this->assertArrayHasKey('1000432000', $backupfiles);
+        $this->assertEquals('file3.mbz', $backupfiles['1000432000']);
+
+        // Three instead of all five backups are deleted. Timestamp is 1000000000 + 60 days.
+        $deletedbackups = testable_backup_cron_automated_helper::testable_get_backups_to_delete($backupfiles, 1005184000);
+        $this->assertEquals(3, count($deletedbackups));
+        $this->assertArrayHasKey('1000000000', $backupfiles);
+        $this->assertEquals('file1.mbz', $backupfiles['1000000000']);
+        $this->assertArrayHasKey('1000172800', $backupfiles);
+        $this->assertEquals('file2.mbz', $backupfiles['1000172800']);
+        $this->assertArrayHasKey('1000432000', $backupfiles);
+        $this->assertEquals('file3.mbz', $backupfiles['1000432000']);
+    }
+}
+
+/**
+ * Provides access to protected methods we want to explicitly test
+ *
+ * @copyright 2015 Jean-Philippe Gaudreau <jp.gaudreau@umontreal.ca>
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class testable_backup_cron_automated_helper extends backup_cron_automated_helper {
+
+    /**
+     * Provides access to protected method get_backups_to_remove.
+     *
+     * @param array $backupfiles Existing backup files
+     * @param int $now Starting time of the process
+     * @return array Backup files to remove
+     */
+    public static function testable_get_backups_to_delete($backupfiles, $now) {
+        return parent::get_backups_to_delete($backupfiles, $now);
+    }
 }
index eedd273..c0c0078 100644 (file)
@@ -26,15 +26,15 @@ $string['autoactivedisabled'] = 'Disabled';
 $string['autoactiveenabled'] = 'Enabled';
 $string['autoactivemanual'] = 'Manual';
 $string['autoactivedescription'] = 'Choose whether or not to do automated backups. If manual is selected automated backups will be possible only by through the automated backups CLI script. This can be done either manually on the command line or through cron.';
-$string['automatedbackupkeepcopies'] = 'Number of backups to keep';
-$string['automatedbackupkeepcopieshelp'] = 'Choose the minimum number of automated backups to keep.';
-$string['automatedbackupkeepdays'] = 'Remove backups after';
-$string['automatedbackupkeepdayshelp'] = 'Indicate after how many days you want to remove automated backups.';
-$string['automatedbackupremovalsettings'] = 'Automated backup removal settings';
 $string['automatedbackupschedule'] = 'Schedule';
 $string['automatedbackupschedulehelp'] = 'Choose which days of the week to perform automated backups.';
 $string['automatedbackupsinactive'] = 'Automated backups haven\'t been enabled by the site admin';
 $string['automatedbackupstatus'] = 'Automated backup status';
+$string['automateddeletedays'] = 'Delete backups older than';
+$string['automatedmaxkept'] = 'Maximum number of backups kept';
+$string['automatedmaxkepthelp'] = 'This specifies the maximum number of recent automated backups to be kept for each course. Older backups will be deleted automatically.';
+$string['automatedminkept'] = 'Minimum number of backups kept';
+$string['automatedminkepthelp'] = 'If backups older than a specified number of days are deleted, it can happen that an inactive course ends up with no backup. To prevent this, a minimum number of backups kept should be specified.';
 $string['automatedsetup'] = 'Automated backup setup';
 $string['automatedsettings'] = 'Automated backup settings';
 $string['automatedstorage'] = 'Automated backup storage';
index 4a6e3fc..2085e55 100644 (file)
@@ -186,7 +186,6 @@ $string['backupfromthissite'] = 'Backup was made on this site?';
 $string['backupgradebookhistoryhelp'] = 'If enabled then gradebook history will be included in automated backups. Note that grade history must not be disabled in server settings (disablegradehistory) in order for this to work';
 $string['backupincludemoduleshelp'] = 'Choose whether you want to include course modules, with or without user data, in automated backups';
 $string['backupincludemoduleuserdatahelp'] = 'Choose whether you want to include module user data in automated backups.';
-$string['backupkeephelp'] = 'How many recent backups for each course do you want to keep? (older ones will be deleted automatically)';
 $string['backuplogdetailed'] = 'Detailed execution log';
 $string['backuploglaststatus'] = 'Last execution log';
 $string['backupmissinguserinfoperms'] = 'Note: This backup contains no user data. Exercise and Workshop activities will not be included in the backup, since these modules are not compatible with this type of backup.';
index fd98221..20b5aaa 100644 (file)
@@ -4572,5 +4572,17 @@ function xmldb_main_upgrade($oldversion) {
         upgrade_main_savepoint(true, 2015092200.00);
     }
 
+    if ($oldversion < 2015092400.01) {
+        // Rename backup_auto_keep setting to backup_auto_max_kept.
+        $keep = get_config('backup', 'backup_auto_keep');
+        if ($keep !== false) {
+            set_config('backup_auto_max_kept', $keep, 'backup');
+            unset_config('backup_auto_keep', 'backup');
+        }
+
+        // Main savepoint reached.
+        upgrade_main_savepoint(true, 2015092400.01);
+    }
+
     return true;
 }
index 19abbe4..4622b2a 100644 (file)
@@ -29,7 +29,7 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-$version  = 2015092400.00;              // YYYYMMDD      = weekly release date of this DEV branch.
+$version  = 2015092400.01;              // YYYYMMDD      = weekly release date of this DEV branch.
                                         //         RR    = release increments - 00 in DEV branches.
                                         //           .XX = incremental changes.