From 42f4b0a99100e9c050666d3ee3dd739663336c7e Mon Sep 17 00:00:00 2001 From: Anupama Sarjoshi Date: Wed, 1 Mar 2023 14:39:27 +0000 Subject: [PATCH] MDL-77406 core: Logs to record user file uploaded in draft area --- lang/en/files.php | 1 + lib/classes/event/draft_file_added.php | 81 +++++++++++++++++ lib/tests/behat/behat_navigation.php | 6 ++ lib/tests/event/draft_file_added_test.php | 86 +++++++++++++++++++ repository/upload/lib.php | 20 ++++- .../upload/tests/behat/upload_file.feature | 10 +++ webservice/upload.php | 17 ++++ 7 files changed, 219 insertions(+), 2 deletions(-) create mode 100644 lib/classes/event/draft_file_added.php create mode 100644 lib/tests/event/draft_file_added_test.php diff --git a/lang/en/files.php b/lang/en/files.php index b371e15daf182..133b5e596d31d 100644 --- a/lang/en/files.php +++ b/lang/en/files.php @@ -25,6 +25,7 @@ defined('MOODLE_INTERNAL') || die(); +$string['eventfileaddedtodraftarea'] = 'File added to draft area'; $string['privacy:metadata:file_conversions'] = 'A record of the file conversions performed by a user.'; $string['privacy:metadata:file_conversion:usermodified'] = 'The user who started the file conversion.'; $string['privacy:metadata:files'] = 'A record of the files uploaded or shared by users'; diff --git a/lib/classes/event/draft_file_added.php b/lib/classes/event/draft_file_added.php new file mode 100644 index 0000000000000..0d2ce1528342b --- /dev/null +++ b/lib/classes/event/draft_file_added.php @@ -0,0 +1,81 @@ +. + +namespace core\event; + +/** + * draft_file_added + * + * Event fired when a file is added to the draft area. + * + * @property-read array $other { + * Extra information about the event. + * + * - string itemid: itemid of the file + * - string filename: Name of the file added to the draft area. + * - string filesize: The file size. + * - string filepath: The filepath. + * - string contenthash: The file contenthash. + * } + * + * @package core + * @since Moodle 4.2 + * @copyright 2023 The Open University. + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class draft_file_added extends base { + + protected function init() { + $this->data['objecttable'] = 'files'; + $this->data['crud'] = 'c'; + $this->data['edulevel'] = self::LEVEL_OTHER; + } + + public static function get_name() { + return get_string('eventfileaddedtodraftarea', 'files'); + } + + public function get_description() { + $humansize = display_size($this->other['filesize']); + return "The user with id '{$this->userid}' has uploaded file '{$this->other['filepath']}{$this->other['filename']}' " . + "to the draft file area with item id {$this->other['itemid']}. Size: {$humansize}. ". + "Content hash: {$this->other['contenthash']}."; + } + + protected function validate_data() { + parent::validate_data(); + + if (!isset($this->other['itemid'])) { + throw new \coding_exception('The \'itemid\' must be set in other.'); + } + + if (!isset($this->other['filename'])) { + throw new \coding_exception('The \'filename\' value must be set in other.'); + } + + if (!isset($this->other['filesize'])) { + throw new \coding_exception('The \'filesize\' value must be set in other.'); + } + + if (!isset($this->other['filepath'])) { + throw new \coding_exception('The \'filepath\' value must be set in other.'); + } + + if (!isset($this->other['contenthash'])) { + throw new \coding_exception('The \'contenthash\' value must be set in other.'); + } + } +} diff --git a/lib/tests/behat/behat_navigation.php b/lib/tests/behat/behat_navigation.php index e2af345c53037..2e9bd61da2b12 100644 --- a/lib/tests/behat/behat_navigation.php +++ b/lib/tests/behat/behat_navigation.php @@ -725,6 +725,12 @@ protected function resolve_core_page_url(string $name): moodle_url { case 'Admin notifications': return new moodle_url('/admin/'); + case 'My private files': + return new moodle_url('/user/files.php'); + + case 'System logs report': + return new moodle_url('/report/log/index.php'); + default: throw new Exception('Unrecognised core page type "' . $name . '."'); } diff --git a/lib/tests/event/draft_file_added_test.php b/lib/tests/event/draft_file_added_test.php new file mode 100644 index 0000000000000..e5c9eab0d3070 --- /dev/null +++ b/lib/tests/event/draft_file_added_test.php @@ -0,0 +1,86 @@ +. + +/** + * File added to draft area test events. + * + * @package core + * @category test + * @copyright 2023 The Open University. + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace core\event; + +/** + * Test for draft file added event. + * + * @package core + * @category test + * @copyright 2023 The Open University. + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @covers \core\event\draft_file_added + */ +class draft_file_added_test extends \advanced_testcase { + /** + * Test draft file added event. + */ + public function test_event() { + $this->resetAfterTest(); + $user = $this->getDataGenerator()->create_user(); + $this->setUser($user); + $usercontext = \context_user::instance($user->id); + + $sink = $this->redirectEvents(); + $fs = get_file_storage(); + + $filerecord = [ + 'contextid' => $usercontext->id, + 'component' => 'core', + 'filearea' => 'unittest', + 'itemid' => 0, + 'filepath' => '/', + 'filename' => 'test.txt', + 'source' => 'Copyright stuff', + ]; + $originalfile = $fs->create_file_from_string($filerecord, 'Test content'); + $nbsp = "\xc2\xa0"; + + // Event data for logging. + $eventdata = [ + 'objectid' => $originalfile->get_id(), + 'context' => $usercontext, + 'other' => [ + 'itemid' => $originalfile->get_itemid(), + 'filename' => $originalfile->get_filename(), + 'filesize' => $originalfile->get_filesize(), + 'filepath' => $originalfile->get_filepath(), + 'contenthash' => $originalfile->get_contenthash(), + ] + ]; + $event = \core\event\draft_file_added::create($eventdata); + $event->trigger(); + + $events = $sink->get_events(); + $this->assertCount(1, $events); + $event = reset($events); + + $this->assertEquals($usercontext, $event->get_context()); + $expected = "The user with id '{$user->id}' has uploaded file '/test.txt' to the draft file area with item id 0. ". + "Size: 12{$nbsp}bytes. Content hash: {$originalfile->get_contenthash()}."; + $this->assertSame($expected, $event->get_description()); + } +} diff --git a/repository/upload/lib.php b/repository/upload/lib.php index 13de7b3b35d39..125c918cfb88a 100644 --- a/repository/upload/lib.php +++ b/repository/upload/lib.php @@ -23,6 +23,8 @@ * @copyright 2010 Dongsheng Cai {@link http://dongsheng.org} * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ + +defined('MOODLE_INTERNAL') || die(); require_once($CFG->dirroot . '/repository/lib.php'); /** @@ -190,13 +192,14 @@ public function process_upload($saveas_filename, $maxbytes, $types = '*', $savep $record->itemid = 0; } - if (($maxbytes!==-1) && (filesize($_FILES[$elname]['tmp_name']) > $maxbytes)) { + $filesize = filesize($_FILES[$elname]['tmp_name']); + if (($maxbytes !== -1) && ($filesize > $maxbytes)) { $maxbytesdisplay = display_size($maxbytes, 0); throw new file_exception('maxbytesfile', (object) array('file' => $record->filename, 'size' => $maxbytesdisplay)); } - if (file_is_draft_area_limit_reached($record->itemid, $areamaxbytes, filesize($_FILES[$elname]['tmp_name']))) { + if (file_is_draft_area_limit_reached($record->itemid, $areamaxbytes, $filesize)) { throw new file_exception('maxareabytes'); } // Ensure the user does not upload too many draft files in a short period. @@ -233,6 +236,19 @@ public function process_upload($saveas_filename, $maxbytes, $types = '*', $savep $stored_file = $fs->create_file_from_pathname($record, $_FILES[$elname]['tmp_name']); } + $logevent = \core\event\draft_file_added::create([ + 'objectid' => $stored_file->get_id(), + 'context' => $context, + 'other' => [ + 'itemid' => $record->itemid, + 'filename' => $record->filename, + 'filesize' => $filesize, + 'filepath' => $record->filepath, + 'contenthash' => $stored_file->get_contenthash(), + ], + ]); + $logevent->trigger(); + return array( 'url'=>moodle_url::make_draftfile_url($record->itemid, $record->filepath, $record->filename)->out(false), 'id'=>$record->itemid, diff --git a/repository/upload/tests/behat/upload_file.feature b/repository/upload/tests/behat/upload_file.feature index c03fe802396c5..bb9a1529c7376 100644 --- a/repository/upload/tests/behat/upload_file.feature +++ b/repository/upload/tests/behat/upload_file.feature @@ -21,3 +21,13 @@ Feature: Upload files Then I should see "2" elements in "Files" filemanager And I should see "empty.txt" And I should see "empty_copy.txt" + + @javascript + Scenario: Verify logs for file upload + Given I am on the "My private files" page logged in as "admin" + And I upload "lib/tests/fixtures/empty.txt" file to "Files" filemanager + And I click on "Save changes" "button" + And I am on the "System logs report" page + And I click on "Get these logs" "button" + Then I should see "The user with id '2' has uploaded file '/empty.txt' to the draft file area with item id" in the "File added to draft area" "table_row" + And I should see "Size: 32 bytes. Content hash: " in the "File added to draft area" "table_row" diff --git a/webservice/upload.php b/webservice/upload.php index 9f34565672d6d..9a49d98b298b7 100644 --- a/webservice/upload.php +++ b/webservice/upload.php @@ -112,6 +112,8 @@ $file->filepath = $_FILES[$fieldname]['tmp_name']; // calculate total size of upload $totalsize += $_FILES[$fieldname]['size']; + // Size of individual file. + $file->size = $_FILES[$fieldname]['size']; } $files[] = $file; } @@ -148,6 +150,7 @@ $file_record->license = $CFG->sitedefaultlicense; $file_record->author = fullname($authenticationinfo['user']); $file_record->source = serialize((object)array('source' => $file->filename)); + $file_record->filesize = $file->size; //Check if the file already exist $existingfile = $fs->file_exists($file_record->contextid, $file_record->component, $file_record->filearea, @@ -159,6 +162,20 @@ } else { $stored_file = $fs->create_file_from_pathname($file_record, $file->filepath); $results[] = $file_record; + + // Log the event when a file is uploaded to the draft area. + $logevent = \core\event\draft_file_added::create([ + 'objectid' => $stored_file->get_id(), + 'context' => $context, + 'other' => [ + 'itemid' => $file_record->itemid, + 'filename' => $file_record->filename, + 'filesize' => $file_record->filesize, + 'filepath' => $file_record->filepath, + 'contenthash' => $stored_file->get_contenthash(), + ], + ]); + $logevent->trigger(); } } echo json_encode($results);