diff --git a/mod/wiki/classes/courseformat/overview.php b/mod/wiki/classes/courseformat/overview.php new file mode 100644 index 00000000000..fcda9c41ad4 --- /dev/null +++ b/mod/wiki/classes/courseformat/overview.php @@ -0,0 +1,148 @@ +. + +namespace mod_wiki\courseformat; + +use core\output\renderer_helper; +use core\url; +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 mod_wiki\manager; + +/** + * Wiki overview integration. + * + * @package mod_wiki + * @copyright 2025 Laurent David + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class overview extends \core_courseformat\activityoverviewbase { + /** + * @var manager the wiki manager. + */ + private manager $manager; + + /** + * Constructor. + * + * @param cm_info $cm the course module instance. + * @param renderer_helper $rendererhelper the renderer helper. + * @param \core_string_manager $stringmanager + */ + public function __construct( + cm_info $cm, + /** @var renderer_helper $rendererhelper the renderer helper */ + protected readonly 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); + } + + #[\Override] + public function get_actions_overview(): ?overviewitem { + if (!has_capability('mod/wiki:managewiki', $this->cm->context)) { + return null; // If the user cannot manage the wiki, we don't show the actions. + } + // If a wiki does not have a main page means it is not used yet, so we do not show the action link. + $pageid = $this->manager->get_main_wiki_pageid(); + if (!$pageid) { + return null; + } + + $text = $this->stringmanager->get_string('view'); + + $content = new action_link( + url: new url( + '/mod/wiki/map.php', + ['pageid' => $pageid], + ), + text: $text, + attributes: ['class' => button::SECONDARY_OUTLINE->classes()], + ); + return new overviewitem( + name: $this->stringmanager->get_string('actions'), + value: $text, + content: $content, + textalign: text_align::CENTER, + ); + } + + #[\Override] + public function get_extra_overview_items(): array { + return [ + 'wiki_type' => $this->get_extra_wiki_type(), + 'totalentries' => $this->get_total_entries(), + 'my_entries' => $this->get_extra_my_entries(), + ]; + } + /** + * Get the overview item for wiki type. + * + * @return overviewitem An overview item for the wiki type. + */ + private function get_extra_wiki_type(): overviewitem { + return new overviewitem( + name: $this->stringmanager->get_string('wikimode', 'wiki'), + value: $this->manager->get_wiki_mode()->value, + content: $this->manager->get_wiki_mode()->to_string(), + textalign: text_align::CENTER, + ); + } + + /** + * Get the entries for a user + * + * @return overviewitem|null An overview item, or null if the user lacks the required capability. + */ + private function get_extra_my_entries(): ?overviewitem { + global $USER; + if (has_capability('mod/wiki:managewiki', $this->cm->context)) { + return null; // If the user manage the wiki, we don't show the my entries. + } + $entriescount = $this->manager->get_user_entries_count($USER->id); + return new overviewitem( + name: $this->stringmanager->get_string('myentries', 'wiki'), + value: $entriescount, + content: $entriescount, + textalign: text_align::CENTER, + ); + } + + /** + * Get the overview item for total entries. + * + * @return overviewitem An overview item for total entries. + */ + private function get_total_entries(): overviewitem { + global $USER; + $entriescount = $this->manager->get_all_entries_count($USER->id); + $label = $this->stringmanager->get_string('totalentries', 'wiki'); + if (has_capability('mod/wiki:managewiki', $this->cm->context)) { + $label = $this->stringmanager->get_string('entries', 'wiki'); + } + return new overviewitem( + name: $label, + value: $entriescount, + content: $entriescount, + textalign: text_align::CENTER, + ); + } +} diff --git a/mod/wiki/lang/en/wiki.php b/mod/wiki/lang/en/wiki.php index b5d47baa117..68c6f957ba0 100644 --- a/mod/wiki/lang/en/wiki.php +++ b/mod/wiki/lang/en/wiki.php @@ -64,6 +64,7 @@ $string['editing'] = 'Editing wiki page'; $string['editingcomment'] = 'Editing comment'; $string['editingpage'] = 'Editing this page \'{$a}\''; $string['editsection'] = 'edit'; +$string['entries'] = 'Entries'; $string['eventdiffviewed'] = 'Wiki diff viewed'; $string['eventhistoryviewed'] = 'Wiki history viewed'; $string['eventmapviewed'] = 'Wiki page map viewed'; @@ -151,6 +152,7 @@ Wikis have many uses, such as * As a personal journal for examination notes or revision (using an individual wiki)'; $string['modulename_link'] = 'mod/wiki/view'; $string['modulenameplural'] = 'Wikis'; +$string['myentries'] = 'My entries'; $string['navigation'] = 'Navigation'; $string['navigationfrom'] = 'This page comes from'; $string['navigationfrom_help'] = 'The wiki pages linking to this page'; @@ -259,6 +261,7 @@ $string['tableofcontents'] = 'Table of contents'; $string['tagarea_wiki_pages'] = 'Wiki pages'; $string['teacherrating'] = 'Teacher rating'; $string['timesrating'] = 'This page has been rated {$a->c} times with an average of: {$a->s}'; +$string['totalentries'] = 'Total entries'; $string['updatedpages'] = "Updated pages"; $string['updatedpages_help'] = "Recently updated wiki pages"; $string['updatedwikipages'] = "Updated wiki pages"; @@ -292,6 +295,7 @@ $string['wikimode'] = 'Wiki mode'; $string['wikimode_help'] = 'The wiki mode determines whether everyone can edit the wiki - a collaborative wiki - or whether everyone has their own wiki which only they can edit - an individual wiki.'; $string['wikimodecollaborative'] = 'Collaborative wiki'; $string['wikimodeindividual'] = 'Individual wiki'; +$string['wikimodeundefined'] = 'Wikimode undefined'; $string['wikiname'] = 'Wiki name'; $string['wikinowikitext'] = 'No wiki text'; $string['wikiorderedlist'] = 'Ordered list'; diff --git a/mod/wiki/tests/behat/overview_report.feature b/mod/wiki/tests/behat/overview_report.feature new file mode 100644 index 00000000000..a1f6016e9af --- /dev/null +++ b/mod/wiki/tests/behat/overview_report.feature @@ -0,0 +1,79 @@ +@mod @mod_wiki +Feature: Testing overview integration in mod_wiki + In order to summarize the wikis + As a user + I need to be able to see the wiki overview + + Background: + Given the following "users" exist: + | username | firstname | lastname | + | student1 | Student | 1 | + | student2 | Student | 2 | + | student3 | Student | 3 | + | teacher1 | Teacher | T | + And the following "courses" exist: + | fullname | shortname | groupmode | + | Course 1 | C1 | 1 | + And the following "course enrolments" exist: + | user | course | role | + | student1 | C1 | student | + | student2 | C1 | student | + | student3 | C1 | student | + | teacher1 | C1 | editingteacher | + Given the following "groups" exist: + | name | course | idnumber | participation | + | Group 1 | C1 | G1 | 1 | + | Group 2 | C1 | G2 | 1 | + | Group 3 | C1 | G3 | 0 | + And the following "group members" exist: + | user | group | + | student1 | G1 | + | student2 | G2 | + | student3 | G3 | + And the following "activities" exist: + | activity | course | name | idnumber | wikimode | firstpagetitle | groupmode | + | wiki | C1 | Separate wiki | wiki1 | collaborative | Separate page 1 | 1 | + | wiki | C1 | Visible wiki | wiki2 | collaborative | Visible page 1 | 2 | + And the following wiki pages exist: + | wiki | title | content | group | + | wiki1 | Separate page 1 | Group 1 page | G1 | + | wiki1 | Separate page 1 | Group 2 page | G2 | + | wiki2 | Visible page 1 | Group 1 page | G1 | + | wiki2 | Visible page 1 | Group 2 page | G2 | + And the following wiki pages exist: + | wiki | title | content | + | wiki1 | Separate page 1 | No group page | + | wiki2 | Visible page 1 | No group page | + + Scenario: The wiki overview report should generate log events + Given I am on the "Course 1" "course > activities > wiki" 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 'wiki'" + + Scenario: Students can see relevant columns in the wiki overview + Given I am on the "Course 1" "course > activities > wiki" page logged in as "student1" + Then the following should exist in the "Table listing all Wiki activities" table: + | Name | My entries | Total entries | + | Separate wiki | 0 | 0 | + | Visible wiki | 0 | 3 | + + Scenario: Teachers can see relevant columns in the wiki overview + Given I am on the "Course 1" "course > activities > wiki" page logged in as "teacher1" + Then the following should exist in the "Table listing all Wiki activities" table: + | Name | Wiki mode | Entries | Actions | + | Separate wiki | Collaborative wiki | 3 | View | + | Visible wiki | Collaborative wiki | 3 | View | + + Scenario: The wiki index redirect to the activities overview + When I log in as "admin" + And I am on "Course 1" course homepage with editing mode on + And I add the "Activities" block + And I click on "Wikis" "link" in the "Activities" "block" + Then I should see "An overview of all activities in the course" + And I should see "Name" in the "wiki_overview_collapsible" "region" + And I should see "Wiki mode" in the "wiki_overview_collapsible" "region" + And I should see "Entries" in the "wiki_overview_collapsible" "region" diff --git a/mod/wiki/tests/courseformat/overview_test.php b/mod/wiki/tests/courseformat/overview_test.php new file mode 100644 index 00000000000..1875a727465 --- /dev/null +++ b/mod/wiki/tests/courseformat/overview_test.php @@ -0,0 +1,279 @@ +. + +namespace mod_wiki\courseformat; + +use core_courseformat\local\overview\overviewfactory; +use mod_wiki\wiki_mode; +use ReflectionClass; + +/** + * Tests for Wiki integration. + * + * @covers \mod_wiki\courseformat\overview + * @package mod_wiki + * @category test + * @copyright 2025 Laurent David + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +final class overview_test extends \advanced_testcase { + + + /** + * Data provider for wiki modes. + * + * @return array + */ + public static function get_wiki_mode_provider(): array { + return [ + 'collaborative' => ['mode' => wiki_mode::COLLABORATIVE], + 'individual' => ['mode' => wiki_mode::INDIVIDUAL], + ]; + } + + /** + * Test get_extra_my_entries method. + * + * @param string $username + * @param int|null $expectedcount + * @return void + * + * @covers ::get_extra_my_entries + * @dataProvider get_extra_my_entries_provider + */ + public function test_get_extra_my_entries(string $username, ?int $expectedcount = null): void { + $this->resetAfterTest(); + ['users' => $users, 'instance' => $instance, 'course' => $course] = $this->setup_users_and_activity(); + $cm = get_fast_modinfo($course)->get_cm($instance->cmid); + $overview = overviewfactory::create($cm); + $this->setUser($users[$username]->id); + $items = $overview->get_extra_overview_items(); + $item = $items['my_entries'] ?? null; + + $this->assertEquals( + $expectedcount, + $item?->get_value() + ); + } + + /** + * Data provider for get_extra_my_entries. + * + * @return array + */ + public static function get_extra_my_entries_provider(): array { + return [ + 'student 1' => ['s1', 1], + 'student 2' => ['s2', 1], + 'teacher 1' => ['t1', null], // Teacher 1 does not have any entries. + ]; + } + + /** + * Test the wiki mode of the wiki instance. + * + * @param wiki_mode $mode the expected wiki mode. + * + * @covers ::get_extra_wiki_type + * @dataProvider get_wiki_mode_provider + */ + public function test_wiki_mode(wiki_mode $mode): void { + $this->resetAfterTest(); + ['users' => $users, 'instance' => $instance, 'course' => $course] = + $this->setup_users_and_activity(NOGROUPS, $mode->value); + $cm = get_fast_modinfo($course)->get_cm($instance->cmid); + $overview = overviewfactory::create($cm); + $this->setUser($users['t1']->id); + $items = $overview->get_extra_overview_items(); + $item = $items['wiki_type'] ?? null; + + $this->assertEquals( + $mode->value, + $item->get_value(), + ); + } + + /** + * Setup users and activity for testing answers retrieval. + * + * @param int $groupmode the group mode to use for the course. + * @param string $mode the mode of the wiki instance. + * @return array indexed array with 'users', 'course' and 'instance'. + */ + private function setup_users_and_activity(int $groupmode = NOGROUPS, string $mode = 'collaborative'): array { + global $CFG; + require_once($CFG->dirroot . '/mod/wiki/locallib.php'); + $users = []; + $generator = $this->getDataGenerator(); + $courseparams = []; + if ($groupmode !== NOGROUPS) { + // Set the group mode for the course. + $courseparams['groupmode'] = $groupmode; + } + $course = $generator->create_course($courseparams); + foreach (['s1' => 'student', 's2' => 'student', 't1' => 'teacher', 't2' => 'teacher'] as $username => $role) { + $users[$username] = $generator->create_and_enrol($course, $role, ['username' => $username]); + } + + $groups = []; + if ($groupmode !== NOGROUPS) { + // Create a group if the group mode is not NOGROUPS. + $groups[] = $generator->create_group(['courseid' => $course->id]); + $groups[] = $generator->create_group(['courseid' => $course->id]); + groups_add_member($groups[0], $users['s1']->id); + groups_add_member($groups[1], $users['s2']->id); + groups_add_member($groups[0], $users['t1']->id); + } + $instance = $generator->create_module( + 'wiki', + [ + 'course' => $course, + 'wikimode' => $mode, + 'groupmode' => $groupmode, + 'firstpagetitle' => 'Wiki first page title', + ], + ); + + $wikigenerator = $generator->get_plugin_generator('mod_wiki'); + + $pages = []; + foreach (['s1', 's2'] as $username) { + $user = $users[$username]; + // Create a first page for each user. + $this->setUser($user->id); + $groups = groups_get_my_groups(); + foreach ($groups as $group) { + $authorid = ($mode === wiki_mode::INDIVIDUAL->value) ? $user->id : 0; + + // Ensure the user is in the group. + $pages[] = $wikigenerator->create_first_page( + $instance, + [ + 'wikiid' => $instance->id, + 'userid' => $authorid, + 'group' => $group->id, + 'content' => "Wiki first page content for $username", + 'title' => "Wiki first page title", + ], + ); + } + if (empty($groups)) { + $pages[] = $wikigenerator->create_page( + $instance, + [ + 'wikiid' => $instance->id, + 'userid' => $user->id, + 'content' => "Wiki first page content for $username", + 'title' => "Wiki first page title", + ], + ); + } + } + return [ + 'users' => $users, + 'course' => $course, + 'instance' => $instance, + 'pages' => $pages, + + ]; + } + + /** + * Test get_extra_entries method. + * + * @param string $username + * @param int $coursegroupmode + * @param int $expectedcount + * + * @covers ::get_extra_entries + * @dataProvider data_provider_get_extra_entries + */ + public function test_get_extra_entries( + string $username, + int $coursegroupmode, + int $expectedcount + ): void { + $this->resetAfterTest(); + [ + 'users' => $users, + 'instance' => $instance, + 'course' => $course + ] = $this->setup_users_and_activity($coursegroupmode); + + $cm = get_fast_modinfo($course)->get_cm($instance->cmid); + $this->setUser($users[$username]->id); + + $overview = overviewfactory::create($cm); + $items = $overview->get_extra_overview_items(); + $item = $items['totalentries'] ?? null; + $this->assertEquals($expectedcount, $item->get_value()); + } + + /** + * Data provider for get_extra_entries. + * + * @return array + */ + public static function data_provider_get_extra_entries(): array { + return [ + 'teacher 1 (no group mode)' => ['t1', NOGROUPS, 2], + 'teacher 1 (separate group mode)' => ['t1', SEPARATEGROUPS, 1], // Teacher 1 belongs to group 1, so should see s1. + 'teacher 1 (visible group mode)' => ['t1', VISIBLEGROUPS, 2], + // Teacher 2 does not belong to any group. + 'teacher 2 (no group mode)' => ['t2', NOGROUPS, 2], + 'teacher 2 (separate group mode)' => ['t2', SEPARATEGROUPS, 0], // Teacher 2 does not belong to any group. + 'teacher 2 (visible group mode)' => ['t2', VISIBLEGROUPS, 2], + ]; + } + + /** + * Test get_extra_entries method. + * + * @covers ::get_actions_overview + */ + public function test_get_actions_overview(): void { + $this->resetAfterTest(); + + [ + 'users' => $users, + 'instance' => $instance, + 'course' => $course + ] = $this->setup_users_and_activity(SEPARATEGROUPS); + + $notinitinstance = $this->getDataGenerator()->create_module( + 'wiki', + [ + 'course' => $course, + 'wikimode' => wiki_mode::INDIVIDUAL->value, + ] + ); + + $this->setUser($users['t1']->id); + + $cm = get_fast_modinfo($course)->get_cm($instance->cmid); + $emptycm = get_fast_modinfo($course)->get_cm($notinitinstance->cmid); + + $overview = overviewfactory::create($cm); + $item = $overview->get_actions_overview(); + + $this->assertNotNull($item); + + $overview = overviewfactory::create($emptycm); + $item = $overview->get_actions_overview(); + + $this->assertNull($item); + } +}