MDL-83891 mod_data: Add overview page

This commit is contained in:
Amaia Anabitarte
2025-06-26 14:10:56 +02:00
parent 82a1547ccd
commit 9d0c655576
10 changed files with 1291 additions and 8 deletions
@@ -0,0 +1,8 @@
issueNumber: MDL-83891
notes:
mod_data:
- message: >-
New get_approval_requested(), get_all_entries(),
filter_entries_by_user(), filter_entries_by_approval() and
get_comments() functions have been added to mod_data manager class.
type: improved
@@ -101,7 +101,7 @@ final class overviewfactory_test extends \advanced_testcase {
],
'data' => [
'resourcetype' => 'data',
'expected' => resourceoverview::class,
'expected' => \mod_data\courseformat\overview::class,
],
'feedback' => [
'resourcetype' => 'feedback',
@@ -67,7 +67,7 @@ final class missingoverviewnotice_test extends \advanced_testcase {
'bigbluebuttonbn' => ['modname' => 'bigbluebuttonbn', 'expectempty' => false],
'book' => ['modname' => 'book', 'expectempty' => false],
'choice' => ['modname' => 'choice', 'expectempty' => false],
'data' => ['modname' => 'data', 'expectempty' => false],
'data' => ['modname' => 'data', 'expectempty' => true],
'feedback' => ['modname' => 'feedback', 'expectempty' => true],
'folder' => ['modname' => 'folder', 'expectempty' => false],
'forum' => ['modname' => 'forum', 'expectempty' => false],
+233
View File
@@ -0,0 +1,233 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
namespace mod_data\courseformat;
use core_calendar\output\humandate;
use cm_info;
use core_courseformat\local\overview\overviewitem;
use core\output\action_link;
use core\output\local\properties\text_align;
use core\output\local\properties\button;
use core\url;
use mod_data\dates;
use mod_data\manager;
/**
* Database activity overview integration.
*
* @package mod_data
* @copyright 2025 Amaia Anabitarte <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class overview extends \core_courseformat\activityoverviewbase {
/** @var manager database activity manager. */
private $manager;
/** @var bool whether the user can see pendent entries or not. */
private $canviewall;
/**
* Constructor.
*
* @param cm_info $cm the course module instance.
* @param \core\output\renderer_helper $rendererhelper the renderer helper.
* @param \core_string_manager $stringmanager the string manager.
*/
public function __construct(
cm_info $cm,
/** @var \core\output\renderer_helper $rendererhelper the renderer helper */
protected readonly \core\output\renderer_helper $rendererhelper,
/** @var \core_string_manager $stringmanager the string manager */
protected readonly \core_string_manager $stringmanager,
) {
parent::__construct($cm);
$this->manager = manager::create_from_coursemodule($cm);
$this->canviewall = has_capability('mod/data:approve', $cm->context);
}
#[\Override]
public function get_due_date_overview(): ?overviewitem {
global $USER;
$dates = new dates($this->cm, $USER->id);
$duedate = $dates->get_due_date();
$name = $this->stringmanager->get_string('duedate', 'data');
if (empty($duedate)) {
return new overviewitem(
name: $name,
value: null,
content: '-',
);
}
$content = humandate::create_from_timestamp($duedate);
return new overviewitem(
name: $name,
value: $duedate,
content: $content,
);
}
#[\Override]
public function get_actions_overview(): ?overviewitem {
if (!$this->canviewall) {
return null;
}
$text = $this->stringmanager->get_string('view', 'moodle');
$toapprove = 0;
$alertlabel = $this->stringmanager->get_string('numberofentriestoapprove', 'data');
if ($this->manager->get_approval_requested()) {
// Let's calculate how many entries need to be approved.
$entries = $this->manager->filter_entries_by_approval($this->manager->get_all_entries(), 0);
$toapprove = count($entries);
$name = $this->stringmanager->get_string('approve', 'data');
if ($toapprove > 0) {
$renderer = $this->rendererhelper->get_core_renderer();
$badge = $renderer->notice_badge(
contents: $toapprove,
title: $alertlabel,
);
$text = $name . $badge;
}
}
$content = new action_link(
url: new url('/mod/data/view.php', ['id' => $this->cm->id]),
text: $text,
attributes: ['class' => button::SECONDARY_OUTLINE->classes()],
);
return new overviewitem(
name: $this->stringmanager->get_string('actions'),
value: $toapprove,
content: $content,
textalign: text_align::CENTER,
alertcount: $toapprove,
alertlabel: $alertlabel,
);
}
#[\Override]
public function get_extra_overview_items(): array {
$columns = [];
// Add entry columns for each view.
if ($this->canviewall) {
$columns['totalentries'] = $this->get_extra_entries_overview();
} else {
$columns['totalentries'] = $this->get_extra_totalentries_overview();
$columns['myentries'] = $this->get_extra_myentries_overview();
}
// Add comments column for all views.
$columns['comments'] = $this->get_extra_comments_overview();
return $columns;
}
/**
* Get the "Total entries" overview item.
*
* @return overviewitem The overview item.
*/
private function get_extra_totalentries_overview(): overviewitem {
$allentries = $this->manager->get_all_entries();
if ($this->manager->get_approval_requested()) {
$allentries = $this->manager->filter_entries_by_approval($allentries, 1);
}
$totalentries = count($allentries);
// Add total entries.
return new overviewitem(
name: $this->stringmanager->get_string('totalentries', 'data'),
value: $totalentries,
content: $totalentries,
textalign: text_align::CENTER,
);
}
/**
* Get the "My entries" overview item.
*
* @return overviewitem The overview item.
*/
private function get_extra_myentries_overview(): overviewitem {
global $USER;
$myentries = $this->manager->filter_entries_by_user($this->manager->get_all_entries(), $USER->id);
$totalmyentries = count($myentries);
return new overviewitem(
name: $this->stringmanager->get_string('myentries', 'data'),
value: $totalmyentries,
content: $totalmyentries,
textalign: text_align::CENTER,
);
}
/**
* Get the "Entries" overview item for teachers.
*
* @return overviewitem The overview item.
*/
private function get_extra_entries_overview(): overviewitem {
$allentries = $this->manager->get_all_entries();
$totalentries = count($allentries);
// Add total entries.
return new overviewitem(
name: $this->stringmanager->get_string('entries', 'data'),
value: $totalentries,
content: $totalentries,
textalign: text_align::CENTER,
);
}
/**
* Get the "Comments" overview item.
*
* @return overviewitem The overview item.
*/
private function get_extra_comments_overview(): overviewitem {
global $CFG;
// Add comments column for all views.
if (empty($CFG->usecomments) || (empty($this->manager->get_instance()->comments))) {
return new overviewitem(
name: $this->stringmanager->get_string('comments', 'data'),
value: 0,
content: '-',
textalign: text_align::CENTER,
);
}
$approved = ($this->canviewall) ? null : 1;
$comments = $this->manager->get_comments(approved: $approved);
$totalcomments = ($comments) ? count($comments) : 0;
return new overviewitem(
name: $this->stringmanager->get_string('comments', 'data'),
value: $totalcomments,
content: $totalcomments,
textalign: text_align::CENTER,
);
}
}
+27 -6
View File
@@ -36,6 +36,12 @@ use core\activity_dates;
*/
class dates extends activity_dates {
/** @var int|null timeopen the activity opening date */
private ?int $timeopen;
/** @var int|null timeclose the activity closing date */
private ?int $timeclose;
/**
* Returns a list of important dates in mod_data
*
@@ -44,27 +50,42 @@ class dates extends activity_dates {
protected function get_dates(): array {
$timeopen = $this->cm->customdata['timeavailablefrom'] ?? null;
$timeclose = $this->cm->customdata['timeavailableto'] ?? null;
$this->timeopen = $timeopen ? (int) $timeopen : null;
$this->timeclose = $timeclose ? (int) $timeclose : null;
$now = time();
$dates = [];
if ($timeopen) {
$openlabelid = $timeopen > $now ? 'activitydate:opens' : 'activitydate:opened';
if ($this->timeopen) {
$openlabelid = $this->timeopen > $now ? 'activitydate:opens' : 'activitydate:opened';
$dates[] = [
'dataid' => 'timeavailablefrom',
'label' => get_string($openlabelid, 'course'),
'timestamp' => (int) $timeopen,
'timestamp' => (int) $this->timeopen,
];
}
if ($timeclose) {
$closelabelid = $timeclose > $now ? 'activitydate:closes' : 'activitydate:closed';
if ($this->timeclose) {
$closelabelid = $this->timeclose > $now ? 'activitydate:closes' : 'activitydate:closed';
$dates[] = [
'dataid' => 'timeavailableto',
'label' => get_string($closelabelid, 'course'),
'timestamp' => (int) $timeclose,
'timestamp' => (int) $this->timeclose,
];
}
return $dates;
}
/**
* Returns the dues date data, if any.
* @return int|null the close timestamp or null if not set.
*/
public function get_due_date(): ?int {
if (!isset($this->timeclose)) {
$this->get_dates();
}
return $this->timeclose;
}
}
+105
View File
@@ -69,6 +69,16 @@ class manager {
/** @var cm_info course_modules record. */
private $cm;
/** @var array the current data records.
* Do not access this attribute directly, use $this->get_all_entries instead
*/
private $_entries = null;
/** @var array the current data comments.
* Do not access this attribute directly, use $this->get_all_comments instead
*/
private $_comments = null;
/** @var array the current data_fields records.
* Do not access this attribute directly, use $this->get_field_records instead
*/
@@ -233,6 +243,92 @@ class manager {
return !empty($this->_fieldrecords);
}
/**
* Return the database entries.
*
* @return [] the data records array.
*/
public function get_all_entries(): array {
global $DB;
if (empty($this->_entries)) {
$this->_entries = $DB->get_records('data_records', ['dataid' => $this->instance->id]);
}
return $this->_entries;
}
/**
* Return the database given entries filtered by userid.
*
* @param array $entries Entries to filter from.
* @param int $userid User to filter by. Zero for non-filtering.
*
* @return [] the filtered data records array.
*/
public function filter_entries_by_user(array $entries, int $userid = 0): array {
if ($userid > 0) {
$entries = array_filter($entries, function($entry) use ($userid) {
return $entry->userid == $userid;
});
}
return $entries;
}
/**
* Return the database entries filtered by approved and/or userid.
*
* @param array $entries Entries to filter from.
* @param int $approved Approved value to filter by.
*
* @return [] the filtered data records array.
*/
public function filter_entries_by_approval(array $entries, int $approved): array {
return array_filter($entries, function($entry) use ($approved) {
return $entry->approved == $approved;
});
}
/**
* Return the database comments filtered by approved entries.
*
* @param ?int $approved Approved value to filter by. Null for not filtering.
*
* @return [] the filtered data comments array or null if there is no comment.
*/
public function get_comments(?int $approved = null): ?array {
if ($this->_comments) {
return $this->_comments;
}
$entries = $this->get_all_entries();
if (!is_null($approved)) {
$entries = $this->filter_entries_by_approval($entries, $approved);
}
$this->_comments = [];
// Initilising comment object.
$args = new stdClass;
$args->context = $this->get_context();
$args->course = $this->cm->get_course();
$args->cm = $this->cm;
$args->area = 'database_entry';
$args->component = 'mod_data';
foreach ($entries as $entry) {
$args->itemid = $entry->id;
$comment = new \comment($args);
$morecomments = $comment->get_comments();
if ($morecomments) {
$this->_comments = array_merge($this->_comments, $morecomments);
}
}
return $this->_comments;
}
/**
* Return the database fields.
*
@@ -312,6 +408,15 @@ class manager {
return new template($this, $templatecontent, $options);
}
/**
* Return whether the database module requests entries approval or not.
*
* @return int whether the approval is requested or not
*/
public function get_approval_requested(): int {
return $this->get_instance()->approval;
}
/** Check if the user can manage templates on the current context.
*
* @param int $userid the user id to check ($USER->id if null).
+5
View File
@@ -124,6 +124,7 @@ $string['descending'] = 'Descending';
$string['directorynotapreset'] = '{$a->directory} Not a preset: missing files: {$a->missing_files}';
$string['disapprove'] = 'Undo approval';
$string['download'] = 'Download';
$string['duedate'] = 'Due date';
$string['edit'] = 'Edit';
$string['editcomment'] = 'Edit comment';
$string['editentry'] = 'Edit entry';
@@ -309,6 +310,7 @@ $string['movezipfailed'] = 'Can\'t move zip';
$string['multientry'] = 'Repeated entry';
$string['multimenu'] = 'Menu (Multi-select)';
$string['multipletags'] = 'Multiple tags found! Template not saved';
$string['myentries'] = 'My entries';
$string['newentry'] = 'New entry';
$string['newfield'] = 'Create a field';
$string['newfield_help'] = 'A field allows the input of data. Each entry in a database activity can have multiple fields of multiple types such as a date field, which allows participants to select a day, month and year from a drop-down menu, a picture field, which allows participants to upload an image file, or a checkbox field, which allows participants to select one or more options.
@@ -330,6 +332,7 @@ $string['notinjectivemap'] = 'Not an injective map';
$string['notemplates'] = 'No templates yet';
$string['notopenyet'] = 'Sorry, this activity is not available until {$a}';
$string['number'] = 'Number';
$string['numberofentriestoapprove'] = 'Entries to approve: {$a}';
$string['numberrssarticles'] = 'Entries in the RSS feed';
$string['numnotapproved'] = 'Pending';
$string['numrecords'] = '{$a} entries';
@@ -447,6 +450,7 @@ $string['text'] = 'Text';
$string['textarea'] = 'Text area';
$string['timeadded'] = 'Time added';
$string['timemodified'] = 'Time modified';
$string['totalentries'] = 'Total entries';
$string['type'] = 'Field type';
$string['undefinedprocessactionmethod'] = 'No action method defined in Data_Preset to handle action "{$a}".';
$string['unsupportedfields'] = 'Unsupported fields';
@@ -464,6 +468,7 @@ $string['usepredefinedset'] = 'Use predefined set';
$string['usepreset'] = 'Use this preset';
$string['usestandard'] = 'Use a preset';
$string['usestandard_help'] = 'To use a preset available to the whole site, select it from the list. (If you have added a preset to the list using the save as preset feature then you have the option of deleting it.)';
$string['viewentries'] = 'View entries';
$string['viewfromdate'] = 'Read only from';
$string['viewnavigation'] = 'View mode tertiary navigation';
$string['viewtemplates'] = 'View templates';
@@ -0,0 +1,123 @@
@mod @mod_data
Feature: Testing overview integration in database activity
In order to summarize the database activity
As a user
I need to be able to see the database activity overview
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| student1 | Student | 1 | student1@example.com |
| teacher1 | Teacher | 1 | teacher1@example.com |
And the following "courses" exist:
| fullname | shortname | category | enablecompletion |
| Course 1 | C1 | 0 | 1 |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| student1 | C1 | student |
And the following "activity" exists:
| course | C1 |
| activity | data |
| name | Database activity |
| intro | description |
| idnumber | data1 |
| approval | 1 |
| completion | 1 |
| comments | 1 |
| timeavailableto | ##1 Jan 2040 08:00## |
And the following "activity" exists:
| course | C1 |
| activity | data |
| name | Without comments |
| intro | description |
| idnumber | data2 |
| approval | 1 |
| completion | 1 |
| comments | 1 |
| timeavailableto | ##1 Jan 2040 08:00## |
And the following "activity" exists:
| course | C1 |
| activity | data |
| name | Empty database |
| intro | empty database |
| idnumber | data3 |
| approval | 0 |
| completion | 0 |
| comments | 0 |
And the following "mod_data > fields" exist:
| database | type | name | description |
| data1 | text | Title field | Title field description |
| data1 | text | Short text field | Short text field description |
| data2 | text | Title field | Title field description |
| data2 | text | Short text field | Short text field description |
And the following "mod_data > templates" exist:
| database | name |
| data1 | singletemplate |
| data1 | listtemplate |
| data1 | addtemplate |
| data1 | asearchtemplate |
| data1 | rsstemplate |
| data2 | singletemplate |
| data2 | listtemplate |
| data2 | addtemplate |
| data2 | asearchtemplate |
| data2 | rsstemplate |
And the following "mod_data > entries" exist:
| database | user | Title field | Short text field | approved |
| data1 | student1 | Student entry | Approved | 1 |
| data1 | student1 | Student second entry | Pending | 0 |
| data1 | teacher1 | Teacher entry | Approved | 1 |
| data2 | teacher1 | Entry no comments | Approved | 1 |
Scenario: The database activity overview report should generate log events
Given I am on the "Course 1" "course > activities > data" page logged in as "teacher1"
When I am on the "Course 1" "course" page logged in as "teacher1"
And I navigate to "Reports" in current page administration
And I click on "Logs" "link"
And I click on "Get these logs" "button"
Then I should see "Course activities overview page viewed"
And I should see "viewed the instance list for the module 'data'"
@javascript
Scenario: Students can see relevant columns in the database activity overview
# Add a comment to test the values.
Given I am on the "Database activity" "data activity" page logged in as student1
And I select "Single view" from the "jump" singleselect
And I click on "Comments (0)" "link"
And I set the following fields to these values:
| Comment | Commenting the entry |
And I click on "Save comment" "link"
When I am on the "Course 1" "course > activities > data" page
# Check columns.
Then I should see "Name" in the "data_overview_collapsible" "region"
And I should see "Status" in the "data_overview_collapsible" "region"
# Check column values.
And the following should exist in the "Table listing all Database activities" table:
| Name | Due date | Total entries | My entries | Comments |
| Database activity | 1 January 2040 | 2 | 2 | 1 |
| Without comments | 1 January 2040 | 1 | 0 | 0 |
| Empty database | - | 0 | 0 | - |
@javascript
Scenario: Teachers can see relevant columns in the database activity overview
# Add a comment to test the values.
Given I am on the "Database activity" "data activity" page logged in as teacher1
And I select "Single view" from the "jump" singleselect
And I click on "Comments (0)" "link"
And I set the following fields to these values:
| Comment | Commenting the entry |
And I click on "Save comment" "link"
When I am on the "Course 1" "course > activities > data" page
# Check columns.
And I should not see "My entries" in the "data_overview_collapsible" "region"
And I should not see "Total entries" in the "data_overview_collapsible" "region"
# Check column values.
Then the following should exist in the "Table listing all Database activities" table:
| Name | Due date | Entries | Comments | Actions |
| Database activity | 1 January 2040 | 3 | 1 | Approve (1) |
| Without comments | 1 January 2040 | 1 | 0 | View |
| Empty database | - | 0 | - | View |
# Check the Approve link.
And I click on "Approve" "link" in the "data_overview_collapsible" "region"
And I should see "Pending approval"
+344
View File
@@ -770,4 +770,348 @@ final class manager_test extends \advanced_testcase {
],
];
}
/**
* Test for get_all_entries().
*
* @covers ::get_all_entries
*/
public function test_get_all_entries(): void {
$this->resetAfterTest();
$course = $this->getDataGenerator()->create_course();
$data = $this->getDataGenerator()->create_module(manager::MODULE, ['course' => $course]);
$manager = manager::create_from_instance($data);
// Empty database should return empty array.
$this->assertEmpty($manager->get_all_entries());
// Create data record.
$datarecords = new \stdClass();
$datarecords->userid = '2';
$datarecords->dataid = $data->id;
// Add a field.
/** @var \mod_data_generator $generator */
$generator = $this->getDataGenerator()->get_plugin_generator('mod_data');
$fieldrecord = (object)[
'name' => 'myfield',
'type' => 'text',
];
$field = $generator->create_field($fieldrecord, $data);
$generator->create_entry(
$data,
[$field->field->id => 'Example entry'],
);
$this->assertCount(1, $manager->get_all_entries());
}
/**
* Test filter_entries_by_user.
*
* @covers ::filter_entries_by_user
* @dataProvider provider_test_filter_entries_by_user
*
* @param array $entries
* @param array $myentries
* @param int $expected
* @return void
*/
public function test_filter_entries_by_user(
array $entries,
array $myentries,
int $expected,
): void {
$this->resetAfterTest();
$course = $this->getDataGenerator()->create_course();
$this->setAdminUser();
$activity = $this->getDataGenerator()->create_module(
manager::MODULE,
['course' => $course, 'approval' => 1],
);
// Add a field.
/** @var \mod_data_generator $generator */
$generator = $this->getDataGenerator()->get_plugin_generator('mod_data');
$fieldrecord = (object)[
'name' => 'myfield',
'type' => 'text',
];
$field = $generator->create_field($fieldrecord, $activity);
foreach ($entries as $entry => $approved) {
$generator->create_entry(
$activity,
[$field->field->id => 'Example entry: '.$entry],
0,
[],
['approved' => $approved],
);
}
$currentuser = $this->getDataGenerator()->create_and_enrol($course, 'student');
$this->setUser($currentuser);
foreach ($myentries as $entry => $approved) {
$generator->create_entry(
$activity,
[$field->field->id => 'Example entry: '.$entry],
0,
[],
['approved' => $approved],
);
}
$manager = manager::create_from_instance($activity);
$allentries = $manager->get_all_entries();
$this->assertCount($expected, $manager->filter_entries_by_user($allentries, $currentuser->id));
}
/**
* Data provider for test_filter_entries_by_user.
*
* @return array
*/
public static function provider_test_filter_entries_by_user(): array {
return [
'Empty database filtered by current user' => [
'entries' => [],
'myentries' => [],
'expected' => 0,
],
'User without own entries filtered by current user' => [
'entries' => [1, 0],
'myentries' => [],
'expected' => 0,
],
'User with own entries filtered by current user' => [
'entries' => [1, 0],
'myentries' => [1, 0],
'expected' => 2,
],
];
}
/**
* Test filter_entries_by_approval.
*
* @covers ::filter_entries_by_approval
* @dataProvider provider_test_filter_entries_by_approval
*
* @param array $entries
* @param int $approvalfilter
* @param int $expected
* @return void
*/
public function test_filter_entries_by_approval(
array $entries,
int $approvalfilter,
int $expected,
): void {
$this->resetAfterTest();
$course = $this->getDataGenerator()->create_course();
$this->setAdminUser();
$activity = $this->getDataGenerator()->create_module(
manager::MODULE,
['course' => $course, 'approval' => 1],
);
// Add a field.
/** @var \mod_data_generator $generator */
$generator = $this->getDataGenerator()->get_plugin_generator('mod_data');
$fieldrecord = (object)[
'name' => 'myfield',
'type' => 'text',
];
$field = $generator->create_field($fieldrecord, $activity);
foreach ($entries as $entry => $approved) {
$generator->create_entry(
$activity,
[$field->field->id => 'Example entry: '.$entry],
0,
[],
['approved' => $approved],
);
}
$manager = manager::create_from_instance($activity);
$allentries = $manager->get_all_entries();
$this->assertCount($expected, $manager->filter_entries_by_approval($allentries, $approvalfilter));
}
/**
* Data provider for test_filter_entries_by_approval.
*
* @return array
*/
public static function provider_test_filter_entries_by_approval(): array {
return [
'Empty database filtered by approved' => [
'entries' => [],
'approvalfilter' => 1,
'expected' => 0,
],
'Database with entries filtered by approved' => [
'entries' => [1, 0, 1, 1],
'approvalfilter' => 1,
'expected' => 3,
],
'Database with entries filtered by non approved' => [
'entries' => [1, 0, 1, 1],
'approvalfilter' => 0,
'expected' => 1,
],
];
}
/**
* Test get_comments.
*
* @covers ::get_comments
* @dataProvider provider_test_get_comments
*
* @param array $entries
* @param int $expected
* @param ?int $approvalfilter
* @return void
*/
public function test_get_comments(
array $entries,
int $expected,
?int $approvalfilter = null,
): void {
global $CFG;
$this->resetAfterTest();
$CFG->usecomments = true;
$course = $this->getDataGenerator()->create_course(['enablecomment' => 1]);
$this->setAdminUser();
$activity = $this->getDataGenerator()->create_module(
manager::MODULE,
['course' => $course, 'approval' => 1, 'comments' => 1],
);
// Add a field.
/** @var \mod_data_generator $generator */
$generator = $this->getDataGenerator()->get_plugin_generator('mod_data');
$fieldrecord = (object)[
'name' => 'myfield',
'type' => 'text',
];
$field = $generator->create_field($fieldrecord, $activity);
foreach ($entries as $entry) {
$entryid = $generator->create_entry(
$activity,
[$field->field->id => 'Example entry'],
0,
[],
['approved' => $entry['approved']],
);
if ($entry['comments']) {
$commentdata = [
[
'contextlevel' => 'module',
'instanceid' => $activity->cmid,
'component' => 'mod_data',
'content' => 'abc',
'itemid' => $entryid,
'area' => 'database_entry',
],
];
\core_comment_external::add_comments($commentdata);
}
}
$manager = manager::create_from_instance($activity);
$this->assertCount($expected, $manager->get_comments(approved: $approvalfilter));
}
/**
* Data provider for test comments extras.
*
* @return array
*/
public static function provider_test_get_comments(): array {
return [
'No comments - no filter' => [
'entries' => [
['approved' => 1, 'comments' => false],
['approved' => 0, 'comments' => false],
],
'expected' => 0,
'approvalfilter' => null,
],
'No comments - filtered by approved' => [
'entries' => [
['approved' => 1, 'comments' => false],
['approved' => 0, 'comments' => false],
],
'expected' => 0,
'approvalfilter' => 1,
],
'No comments - filtered by pending' => [
'entries' => [
['approved' => 1, 'comments' => false],
['approved' => 0, 'comments' => false],
],
'expected' => 0,
'approvalfilter' => 0,
],
'With comments - no filter' => [
'entries' => [
['approved' => 1, 'comments' => true],
['approved' => 0, 'comments' => true],
],
'expected' => 2,
'approvalfilter' => null,
],
'With comments - filtered by approved' => [
'entries' => [
['approved' => 1, 'comments' => true],
['approved' => 0, 'comments' => true],
],
'expected' => 1,
'approvalfilter' => 1,
],
'With comments - filtered by pending' => [
'entries' => [
['approved' => 1, 'comments' => true],
['approved' => 0, 'comments' => true],
],
'expected' => 1,
'approvalfilter' => 0,
],
];
}
/**
* Test get_approval_requested.
*
* @covers ::get_approval_requested
*/
public function test_get_approval_requested(): void {
$this->resetAfterTest();
$course = $this->getDataGenerator()->create_course();
$this->setAdminUser();
$activity = $this->getDataGenerator()->create_module(
manager::MODULE,
['course' => $course, 'approval' => 1],
);
$manager = manager::create_from_instance($activity);
$this->assertEquals(1, $manager->get_approval_requested());
$activity = $this->getDataGenerator()->create_module(
manager::MODULE,
['course' => $course, 'approval' => 0],
);
$manager = manager::create_from_instance($activity);
$this->assertEquals(0, $manager->get_approval_requested());
}
}
+444
View File
@@ -0,0 +1,444 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
namespace mod_data;
use core_courseformat\local\overview\overviewfactory;
/**
* Tests for Database activity overview
*
* @covers \mod_data\courseformat\overview
* @package mod_data
* @category test
* @copyright 2025 Amaia Anabitarte <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
final class overview_test extends \advanced_testcase {
/**
* Test get_actions_overview.
*
* @covers ::get_actions_overview
* @dataProvider provider_test_get_actions_overview
*
* @param string $role
* @param bool $needsapproval
* @param array $entries
* @param array|null $expected
* @return void
*/
public function test_get_actions_overview(
string $role,
bool $needsapproval,
array $entries,
?array $expected
): void {
$this->resetAfterTest();
$course = $this->getDataGenerator()->create_course();
$currentuser = $this->getDataGenerator()->create_and_enrol($course, $role);
$this->setUser($currentuser);
$activity = $this->getDataGenerator()->create_module(
manager::MODULE,
['course' => $course, 'approval' => $needsapproval],
);
// Add a field.
/** @var \mod_data_generator $generator */
$generator = $this->getDataGenerator()->get_plugin_generator('mod_data');
$fieldrecord = (object)[
'name' => 'myfield',
'type' => 'text',
];
$field = $generator->create_field($fieldrecord, $activity);
foreach ($entries as $entry => $approved) {
$generator->create_entry(
$activity,
[$field->field->id => 'Example entry: '.$entry],
0,
[],
['approved' => $approved],
);
}
$cm = get_fast_modinfo($course)->get_cm($activity->cmid);
$item = overviewfactory::create($cm)->get_actions_overview();
if ($expected === null) {
$this->assertNull($item);
return;
}
$this->assertEquals($expected['value'], $item->get_value());
$content = $item->get_content();
$this->assertInstanceOf(\action_link::class, $content);
$this->assertStringContainsString($expected['link'], $content->text);
}
/**
* Data provider for test_get_actions_overview.
*
* @return array
*/
public static function provider_test_get_actions_overview(): array {
return [
'Student' => [
'role' => 'student',
'needsapproval' => false,
'entries' => [1, 0],
'expected' => null,
],
'Teacher with entries (non-require approval)' => [
'role' => 'editingteacher',
'needsapproval' => false,
'entries' => [1, 0],
'expected' => [
'link' => get_string('view', 'moodle'),
'value' => 0,
],
],
'Teacher without entries (require approval)' => [
'role' => 'editingteacher',
'needsapproval' => true,
'entries' => [],
'expected' => [
'link' => get_string('view', 'moodle'),
'value' => 0,
],
],
'Teacher with entries (require approval)' => [
'role' => 'editingteacher',
'needsapproval' => true,
'entries' => [1, 0],
'expected' => [
'link' => get_string('approve', 'data'),
'value' => 1,
],
],
'Teacher with approved entries (require approval)' => [
'role' => 'editingteacher',
'needsapproval' => true,
'entries' => [1, 1],
'expected' => [
'link' => get_string('view', 'moodle'),
'value' => 0,
],
],
];
}
/**
* Test get_extra_overview_items.
*
* @covers ::get_extra_overview_items
* @dataProvider provider_test_get_entries_overview
*
* @param string $role
* @param bool $needsapproval
* @param array $entries
* @param array $myentries
* @param array $expected
* @return void
*/
public function test_get_extra_entries_overview(
string $role,
bool $needsapproval,
array $entries,
array $myentries,
array $expected
): void {
$this->resetAfterTest();
$course = $this->getDataGenerator()->create_course();
$this->setAdminUser();
$activity = $this->getDataGenerator()->create_module(
manager::MODULE,
['course' => $course, 'approval' => $needsapproval],
);
// Add a field.
/** @var \mod_data_generator $generator */
$generator = $this->getDataGenerator()->get_plugin_generator('mod_data');
$fieldrecord = (object)[
'name' => 'myfield',
'type' => 'text',
];
$field = $generator->create_field($fieldrecord, $activity);
foreach ($entries as $entry => $approved) {
$generator->create_entry(
$activity,
[$field->field->id => 'Example entry: '.$entry],
0,
[],
['approved' => $approved],
);
}
$currentuser = $this->getDataGenerator()->create_and_enrol($course, $role);
$this->setUser($currentuser);
foreach ($myentries as $entry => $approved) {
$generator->create_entry(
$activity,
[$field->field->id => 'Example entry: '.$entry],
0,
[],
['approved' => $approved],
);
}
$cm = get_fast_modinfo($course)->get_cm($activity->cmid);
$items = overviewfactory::create($cm)->get_extra_overview_items();
if (is_null($expected['totalentries'])) {
$this->assertArrayNotHasKey('totalentries', $items);
} else {
$this->assertEquals($expected['totalentries'], $items['totalentries']->get_value());
}
if (is_null($expected['myentries'])) {
$this->assertArrayNotHasKey('myentries', $items);
} else {
$this->assertEquals($expected['myentries'], $items['myentries']->get_value());
}
}
/**
* Data provider for test entry related extras.
*
* @return array
*/
public static function provider_test_get_entries_overview(): array {
return [
'Student not needing approval' => [
'role' => 'student',
'needsapproval' => false,
'entries' => [1, 0],
'myentries' => [1, 0],
'expected' => [
'myentries' => 2,
'totalentries' => 4,
],
],
'Student needing approval' => [
'role' => 'student',
'needsapproval' => true,
'entries' => [1, 0],
'myentries' => [1, 0],
'expected' => [
'myentries' => 2,
'totalentries' => 2,
],
],
'Teacher with entries (non-require approval)' => [
'role' => 'editingteacher',
'needsapproval' => false,
'entries' => [1, 0],
'myentries' => [1, 0],
'expected' => [
'myentries' => null,
'totalentries' => 4,
],
],
'Teacher without entries (require approval)' => [
'role' => 'editingteacher',
'needsapproval' => true,
'entries' => [],
'myentries' => [],
'expected' => [
'myentries' => null,
'totalentries' => 0,
],
],
'Teacher with entries (require approval)' => [
'role' => 'editingteacher',
'needsapproval' => true,
'entries' => [1, 0],
'myentries' => [1, 0],
'expected' => [
'myentries' => null,
'totalentries' => 4,
],
],
'Teacher with approved entries (require approval)' => [
'role' => 'editingteacher',
'needsapproval' => true,
'entries' => [1, 1],
'myentries' => [1, 1],
'expected' => [
'myentries' => null,
'totalentries' => 4,
],
],
];
}
/**
* Test get_extra_comments_overview.
*
* @covers ::get_extra_comments_overview
* @dataProvider provider_test_get_comments_overview
*
* @param string $role
* @param bool $needsapproval
* @param array $entries
* @param int $expected
* @return void
*/
public function test_get_extra_comments_overview(
string $role,
bool $needsapproval,
array $entries,
int $expected
): void {
global $CFG;
$this->resetAfterTest();
$CFG->usecomments = true;
$course = $this->getDataGenerator()->create_course(['enablecomment' => 1]);
$this->setAdminUser();
$activity = $this->getDataGenerator()->create_module(
manager::MODULE,
['course' => $course, 'approval' => $needsapproval, 'comments' => 1],
);
// Add a field.
/** @var \mod_data_generator $generator */
$generator = $this->getDataGenerator()->get_plugin_generator('mod_data');
$fieldrecord = (object)[
'name' => 'myfield',
'type' => 'text',
];
$field = $generator->create_field($fieldrecord, $activity);
foreach ($entries as $entry) {
$entryid = $generator->create_entry(
$activity,
[$field->field->id => 'Example entry'],
0,
[],
['approved' => $entry['approved']],
);
if ($entry['comments']) {
$commentdata = [
[
'contextlevel' => 'module',
'instanceid' => $activity->cmid,
'component' => 'mod_data',
'content' => 'abc',
'itemid' => $entryid,
'area' => 'database_entry',
],
];
\core_comment_external::add_comments($commentdata);
}
}
$currentuser = $this->getDataGenerator()->create_and_enrol($course, $role);
$this->setUser($currentuser);
$cm = get_fast_modinfo($course)->get_cm($activity->cmid);
$items = overviewfactory::create($cm)->get_extra_overview_items();
$this->assertEquals($expected, $items['comments']->get_value());
}
/**
* Data provider for test comments extras.
*
* @return array
*/
public static function provider_test_get_comments_overview(): array {
return [
'Student not needing approval with no comments' => [
'role' => 'student',
'needsapproval' => false,
'entries' => [
['approved' => 1, 'comments' => false],
['approved' => 0, 'comments' => false],
],
'expected' => 0,
],
'Student not needing approval with comments' => [
'role' => 'student',
'needsapproval' => false,
'entries' => [
['approved' => 1, 'comments' => true],
['approved' => 0, 'comments' => true],
],
'expected' => 1,
],
'Student needing approval with no comments' => [
'role' => 'student',
'needsapproval' => true,
'entries' => [
['approved' => 1, 'comments' => false],
['approved' => 0, 'comments' => false],
],
'expected' => 0,
],
'Student needing approval with comments' => [
'role' => 'student',
'needsapproval' => true,
'entries' => [
['approved' => 1, 'comments' => true],
['approved' => 0, 'comments' => true],
],
'expected' => 1,
],
'Teacher not needing approval with no comments' => [
'role' => 'editingteacher',
'needsapproval' => false,
'entries' => [
['approved' => 1, 'comments' => false],
['approved' => 0, 'comments' => false],
],
'expected' => 0,
],
'Teacher not needing approval with comments' => [
'role' => 'editingteacher',
'needsapproval' => false,
'entries' => [
['approved' => 1, 'comments' => true],
['approved' => 0, 'comments' => true],
],
'expected' => 2,
],
'Teacher needing approval with no comments' => [
'role' => 'editingteacher',
'needsapproval' => true,
'entries' => [
['approved' => 1, 'comments' => false],
['approved' => 0, 'comments' => false],
],
'expected' => 0,
],
'Teacher needing approval with comments' => [
'role' => 'editingteacher',
'needsapproval' => true,
'entries' => [
['approved' => 1, 'comments' => true],
['approved' => 0, 'comments' => true],
],
'expected' => 2,
],
];
}
}