From 179961e8a46d886de6ab865c2ef8cea26f619ef1 Mon Sep 17 00:00:00 2001 From: Tim Gus Date: Tue, 30 Jul 2013 14:44:19 -0400 Subject: [PATCH] MDL-38570 administration: Temporary file deletion Added new function "cron_delete_from_temp" to delete old files and directories under $CFG->tempdir during cron run. Unit test included. --- lib/cronlib.php | 48 ++++++++++++ lib/tests/cronlib_test.php | 155 +++++++++++++++++++++++++++++++++++++ 2 files changed, 203 insertions(+) create mode 100644 lib/tests/cronlib_test.php diff --git a/lib/cronlib.php b/lib/cronlib.php index 41c7bb2c604..2cce5c2099a 100644 --- a/lib/cronlib.php +++ b/lib/cronlib.php @@ -170,6 +170,9 @@ function cron_run() { mtrace(' Cleaned up read notifications'); } + mtrace(' Deleting temporary files...'); + cron_delete_from_temp(); + mtrace("...finished clean-up tasks"); } // End of occasional clean-up tasks @@ -767,3 +770,48 @@ function notify_login_failures() { return true; } + +/** + * Delete files and directories older than one week from directory provided by $CFG->tempdir. + * + * @exception Exception Failed reading/accessing file or directory + * @return bool True on successful file and directory deletion; otherwise, false on failure + */ +function cron_delete_from_temp() { + global $CFG; + + $tmpdir = $CFG->tempdir; + // Default to last weeks time. + $time = strtotime('-1 week'); + + try { + $dir = new RecursiveDirectoryIterator($tmpdir); + // Show all child nodes prior to their parent. + $iter = new RecursiveIteratorIterator($dir, RecursiveIteratorIterator::CHILD_FIRST); + + for ($iter->rewind(); $iter->valid(); $iter->next()) { + $node = $iter->getPathname(); + if (!is_readable($node)) { + continue; + } + // Check if file or directory is older than the given time. + if ($iter->getMTime() < $time) { + if ($iter->isDir() && !$iter->isDot()) { + if (@rmdir($node) === false) { + mtrace("Failed removing directory '$node'."); + } + } + if ($iter->isFile()) { + if (@unlink($node) === false) { + mtrace("Failed removing file '$node'."); + } + } + } + } + } catch (Exception $e) { + mtrace('Failed reading/accessing file or directory.'); + return false; + } + + return true; +} diff --git a/lib/tests/cronlib_test.php b/lib/tests/cronlib_test.php new file mode 100644 index 00000000000..fe60c889994 --- /dev/null +++ b/lib/tests/cronlib_test.php @@ -0,0 +1,155 @@ +. + +/** + * Unit tests for the cron. + * + * @package core + * @category test + * @copyright 2013 Tim Gusak + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +global $CFG; +require_once($CFG->libdir.'/cronlib.php'); + +class cronlib_testcase extends basic_testcase { + + /** + * Data provider for cron_delete_from_temp. + * + * @return array Provider data + */ + public function cron_delete_from_temp_provider() { + global $CFG; + $time = time(); + + $weekstime = $time - strtotime('1 week'); + $beforeweekstime = $time - strtotime('1 week') - 1; + $afterweekstime = $time + strtotime('1 week') + 1; + + $node1 = new stdClass(); + $node1->path = '/dir1/dir1_1/dir1_2/dir1_3/'; + $node1->time = 1; + $node1->isdir = true; + + $node2 = new stdClass(); + $node2->path = '/dir1/dir1_4/'; + $node2->time = $time; + $node2->isdir = true; + + $node3 = new stdClass(); + $node3->path = '/dir2/'; + $node3->isdir = true; + $node3->time = $time - $weekstime; + + $node4 = new stdClass(); + $node4->path = '/dir3/'; + $node4->isdir = true; + $node4->time = $time - $afterweekstime; + + $node5 = new stdClass(); + $node5->path = '/dir1/dir1_1/dir1_2/file1'; + $node5->isdir = false; + $node5->time = $beforeweekstime; + + $node6 = new stdClass(); + $node6->path = '/dir1/dir1_1/dir1_2/file2'; + $node6->isdir = false; + $node6->time = $time; + + $node7 = new stdClass(); + $node7->path = '/dir1/dir1_4/file1'; + $node7->isdir = false; + $node7->time = $time - $afterweekstime; + + $node8 = new stdClass(); + $node8->path = '/dir1/dir1_4/file2'; + $node8->isdir = false; + $node8->time = $time; + + $node9 = new stdClass(); + $node9->path = '/file1'; + $node9->isdir = false; + $node9->time = $time; + + $node10 = new stdClass(); + $node10->path = '/file2'; + $node10->isdir = false; + $node10->time = $time - $afterweekstime; + + $data = array( + array( + array($node1, $node2, $node3, $node4, $node5, $node6, $node7, $node8, $node9, $node10), + array( + "$CFG->tempdir/dir1", + "$CFG->tempdir/dir1/dir1_1", + "$CFG->tempdir/dir1/dir1_1/dir1_2", + "$CFG->tempdir/dir1/dir1_1/dir1_2/file2", + "$CFG->tempdir/dir1/dir1_4", + "$CFG->tempdir/dir1/dir1_4/file2", + "$CFG->tempdir/dir2", + "$CFG->tempdir/file1", + ) + ), + array( + array(), + array() + ) + ); + + return $data; + } + + /** + * Test removing files and directories from tempdir. + * + * @dataProvider cron_delete_from_temp_provider + * @param array $nodes List of files and directories + * @param array $expected The expected results + */ + public function test_cron_delete_from_temp($nodes, $expected) { + global $CFG; + + $tmpdir = $CFG->tempdir; + + foreach ($nodes as $data) { + if ($data->isdir) { + mkdir($tmpdir.$data->path, $CFG->directorypermissions, true); + } + touch($tmpdir.$data->path, $data->time); + } + + cron_delete_from_temp(); + + $dir = new RecursiveDirectoryIterator($tmpdir); + $iter = new RecursiveIteratorIterator($dir, RecursiveIteratorIterator::CHILD_FIRST); + + $actual = array(); + for ($iter->rewind(); $iter->valid(); $iter->next()) { + if (!$iter->isDot()) { + $actual[] = $iter->getPathname(); + } + } + + // Sort results to guarantee actual order. + sort($actual); + + $this->assertEquals($expected, $actual); + } +}