diff --git a/files/classes/reportbuilder/local/entities/file.php b/files/classes/reportbuilder/local/entities/file.php index 1795ef5b4e3..0b2dbc0616a 100644 --- a/files/classes/reportbuilder/local/entities/file.php +++ b/files/classes/reportbuilder/local/entities/file.php @@ -232,6 +232,17 @@ class file extends base { return html_writer::link($context->get_url(), $context->get_context_name()); }); + // Content hash. + $columns[] = (new column( + 'contenthash', + new lang_string('contenthash', 'core_files'), + $this->get_entity_name() + )) + ->add_joins($this->get_joins()) + ->set_type(column::TYPE_TEXT) + ->add_field("{$filesalias}.contenthash") + ->set_is_sortable(true); + // Component. $columns[] = (new column( 'component', @@ -355,6 +366,16 @@ class file extends base { }, $licenses); }); + // Content hash. + $filters[] = (new filter( + text::class, + 'contenthash', + new lang_string('contenthash', 'core_files'), + $this->get_entity_name(), + "{$filesalias}.contenthash" + )) + ->add_joins($this->get_joins()); + // Time created. $filters[] = (new filter( date::class, diff --git a/files/tests/reportbuilder/datasource/files_test.php b/files/tests/reportbuilder/datasource/files_test.php index 512ad59f970..e990a55e434 100644 --- a/files/tests/reportbuilder/datasource/files_test.php +++ b/files/tests/reportbuilder/datasource/files_test.php @@ -46,14 +46,14 @@ class files_test extends core_reportbuilder_testcase { public function test_datasource_default(): void { $this->resetAfterTest(); + $course = $this->getDataGenerator()->create_course(); + $coursecontext = context_course::instance($course->id); + $user = $this->getDataGenerator()->create_user(); $usercontext = context_user::instance($user->id); $this->setUser($user); - $course = $this->getDataGenerator()->create_course(); - $coursecontext = context_course::instance($course->id); - $this->generate_test_files($coursecontext); /** @var core_reportbuilder_generator $generator */ @@ -63,10 +63,14 @@ class files_test extends core_reportbuilder_testcase { $content = $this->get_custom_report_content($report->get('id')); $content = $this->filter_custom_report_content($content, static function(array $row): bool { return $row['c0_contextid'] !== 'System'; - }, 'c0_contextid'); + }); $this->assertCount(2, $content); + // Consistent order (course, user), just in case. + core_collator::asort_array_of_arrays_by_key($content, 'c0_contextid'); + $content = array_values($content); + // First row (course summary file). [$contextname, $userfullname, $filename, $mimetype, $filesize, $timecreated] = array_values($content[0]); @@ -95,24 +99,28 @@ class files_test extends core_reportbuilder_testcase { $this->resetAfterTest(); $this->setAdminUser(); + $course = $this->getDataGenerator()->create_course(); + $coursecontext = context_course::instance($course->id); + $user = $this->getDataGenerator()->create_user(); $usercontext = context_user::instance($user->id); $this->setUser($user); - $course = $this->getDataGenerator()->create_course(); - $coursecontext = context_course::instance($course->id); - $draftitemid = $this->generate_test_files($coursecontext); /** @var core_reportbuilder_generator $generator */ $generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder'); $report = $generator->create_report(['name' => 'Files', 'source' => files::class, 'default' => 0]); - $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'file:contexturl']); + // Consistent order, sorted by context and content hash. + $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'file:contexturl', + 'sortenabled' => 1, 'sortorder' => 1]); $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'file:path']); $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'file:author']); $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'file:license']); + $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'file:contenthash', + 'sortenabled' => 1, 'sortorder' => 2]); $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'file:component']); $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'file:area']); $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'file:itemid']); @@ -120,7 +128,7 @@ class files_test extends core_reportbuilder_testcase { $content = $this->get_custom_report_content($report->get('id')); $content = $this->filter_custom_report_content($content, static function(array $row): bool { return stripos($row['c0_contextid'], 'System') === false; - }, 'c0_contextid'); + }); // There should be two entries (directory & file) for each context. $this->assertEquals([ @@ -129,6 +137,7 @@ class files_test extends core_reportbuilder_testcase { '/', null, '', + 'da39a3ee5e6b4b0d3255bfef95601890afd80709', 'course', 'summary', 0, @@ -138,6 +147,7 @@ class files_test extends core_reportbuilder_testcase { '/', null, '', + 'f7ff9e8b7bb2e09b70935a5d785e0cc5d9d0abf0', 'course', 'summary', 0, @@ -147,6 +157,7 @@ class files_test extends core_reportbuilder_testcase { '/', null, '', + 'da39a3ee5e6b4b0d3255bfef95601890afd80709', 'user', 'draft', $draftitemid, @@ -156,6 +167,7 @@ class files_test extends core_reportbuilder_testcase { '/', null, '', + 'f7ff9e8b7bb2e09b70935a5d785e0cc5d9d0abf0', 'user', 'draft', $draftitemid, @@ -193,6 +205,14 @@ class files_test extends core_reportbuilder_testcase { 'file:license_operator' => select::EQUAL_TO, 'file:license_value' => 'public', ], 0], + 'Filter content hash' => ['file:contenthash', [ + 'file:contenthash_operator' => text::IS_EQUAL_TO, + 'file:contenthash_value' => 'f7ff9e8b7bb2e09b70935a5d785e0cc5d9d0abf0', + ], 2], + 'Filter content hash (no match)' => ['file:contenthash', [ + 'file:contenthash_operator' => text::IS_EQUAL_TO, + 'file:contenthash_value' => 'f00f', + ], 0], 'Filter time created' => ['file:timecreated', [ 'file:timecreated_operator' => date::DATE_RANGE, 'file:timecreated_from' => 1622502000, @@ -251,7 +271,7 @@ class files_test extends core_reportbuilder_testcase { $content = $this->get_custom_report_content($report->get('id'), 0, $filtervalues); $content = $this->filter_custom_report_content($content, static function(array $row): bool { return stripos($row['c0_contextid'], 'System') === false; - }, 'c0_contextid'); + }); $this->assertCount($expectmatchcount, $content); } @@ -280,16 +300,14 @@ class files_test extends core_reportbuilder_testcase { } /** - * Ensuring report content only includes files we have explicitly created within the test, and ordering them + * Ensuring report content only includes files we have explicitly created within the test * * @param array $content * @param callable $callback - * @param string $sortfield * @return array */ - protected function filter_custom_report_content(array $content, callable $callback, string $sortfield): array { + protected function filter_custom_report_content(array $content, callable $callback): array { $content = array_filter($content, $callback); - core_collator::asort_array_of_arrays_by_key($content, $sortfield); return array_values($content); } diff --git a/lang/en/files.php b/lang/en/files.php index 4ccdfb68e7d..8cc465821f7 100644 --- a/lang/en/files.php +++ b/lang/en/files.php @@ -25,6 +25,7 @@ defined('MOODLE_INTERNAL') || die(); +$string['contenthash'] = 'Content hash'; $string['eventfileaddedtodraftarea'] = 'File added to draft area'; $string['eventfiledeletedfromdraftarea'] = 'File deleted from draft area'; $string['privacy:metadata:file_conversions'] = 'A record of the file conversions performed by a user.';