From b927b0eac3c39474b47c048cfffaaa506aac5735 Mon Sep 17 00:00:00 2001 From: Dongsheng Cai <d@tux.im> Date: Tue, 20 Apr 2021 14:29:22 +1000 Subject: [PATCH] MDL-71330 mod_chat: Implement the activity dates functionality --- mod/chat/classes/dates.php | 57 +++++++++++ mod/chat/lang/en/chat.php | 5 +- mod/chat/lang/en/deprecated.txt | 1 + mod/chat/lib.php | 105 ++++++++++++++++---- mod/chat/tests/behat/behat_mod_chat.php | 76 ++++++++++++++ mod/chat/tests/dates_test.php | 127 ++++++++++++++++++++++++ mod/chat/view.php | 12 +-- 7 files changed, 356 insertions(+), 27 deletions(-) create mode 100644 mod/chat/classes/dates.php create mode 100644 mod/chat/lang/en/deprecated.txt create mode 100644 mod/chat/tests/behat/behat_mod_chat.php create mode 100644 mod/chat/tests/dates_test.php diff --git a/mod/chat/classes/dates.php b/mod/chat/classes/dates.php new file mode 100644 index 00000000000..21f6058f91c --- /dev/null +++ b/mod/chat/classes/dates.php @@ -0,0 +1,57 @@ +<?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/>. + +/** + * Contains the class for fetching the important dates in mod_chat for a given module instance and a user. + * + * @package mod_chat + * @copyright 2021 Dongsheng Cai + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +declare(strict_types=1); + +namespace mod_chat; + +use core\activity_dates; + +/** + * Class for fetching the important dates in mod_chat for a given module instance and a user. + * + */ +class dates extends activity_dates { + /** + * Returns a list of important dates in mod_chat. + * + * @return array + */ + protected function get_dates(): array { + $customdata = $this->cm->customdata; + $chat = (object) $customdata; + $chattime = $chat->chattime ?? 0; + $now = time(); + if (!empty($chat->schedule) && $chattime > $now) { + return [ + [ + 'label' => get_string('nextchattime', 'mod_chat'), + 'timestamp' => (int) $chattime + ] + ]; + } + + return []; + } +} diff --git a/mod/chat/lang/en/chat.php b/mod/chat/lang/en/chat.php index 63b55b6a07d..08a0fe926e2 100644 --- a/mod/chat/lang/en/chat.php +++ b/mod/chat/lang/en/chat.php @@ -63,6 +63,7 @@ $string['chat:readlog'] = 'View chat logs'; $string['chatreport'] = 'Chat sessions'; $string['chat:talk'] = 'Talk in a chat'; $string['chattime'] = 'Next chat time'; +$string['nextchattime'] = 'Next chat time:'; $string['chat:view'] = 'View chat activity'; $string['entermessage'] = "Enter your message"; $string['eventmessagesent'] = 'Message sent'; @@ -157,7 +158,7 @@ $string['serverip'] = 'Server ip'; $string['servermax'] = 'Max users'; $string['serverport'] = 'Server port'; $string['sessions'] = 'Chat sessions'; -$string['sessionstart'] = 'The next chat session will start on {$a->date}, ({$a->fromnow} from now)'; +$string['sessionstartsin'] = 'The next chat session will start {$a} from now.'; $string['strftimemessage'] = '%H:%M'; $string['studentseereports'] = 'Everyone can view past sessions'; $string['studentseereports_help'] = 'If set to No, only users have mod/chat:readlog capability are able to see the chat logs'; @@ -174,3 +175,5 @@ $string['usingchat_help'] = 'The chat module contains some features to make chat * Beeps - You can send a sound to other participants by clicking the "beep" link next to their name. A useful shortcut to beep all the people in the chat at once is to type "beep all". * HTML - If you know some HTML code, you can use it in your text to do things like insert images, play sounds or create different coloured text'; $string['viewreport'] = 'View past chat sessions'; +// Deprecated since Moodle 3.11. +$string['sessionstart'] = 'The next chat session will start on {$a->date}, ({$a->fromnow} from now)'; diff --git a/mod/chat/lang/en/deprecated.txt b/mod/chat/lang/en/deprecated.txt new file mode 100644 index 00000000000..5262248af39 --- /dev/null +++ b/mod/chat/lang/en/deprecated.txt @@ -0,0 +1 @@ +sessionstart,mod_chat diff --git a/mod/chat/lib.php b/mod/chat/lib.php index f7431cad047..2df0acde47a 100644 --- a/mod/chat/lib.php +++ b/mod/chat/lib.php @@ -31,6 +31,14 @@ define('CHAT_EVENT_TYPE_CHATTIME', 'chattime'); // Gap between sessions. 5 minutes or more of idleness between messages in a chat means the messages belong in different sessions. define('CHAT_SESSION_GAP', 300); +// Don't publish next chat time +define('CHAT_SCHEDULE_NONE', 0); +// Publish the specified time only. +define('CHAT_SCHEDULE_SINGLE', 1); +// Repeat chat session at the same time daily. +define('CHAT_SCHEDULE_DAILY', 2); +// Repeat chat session at the same time weekly. +define('CHAT_SCHEDULE_WEEKLY', 3); // The HTML head for the message window to start with (<!-- nix --> is used to get some browsers starting with output. global $CHAT_HTMLHEAD; @@ -116,6 +124,7 @@ function chat_add_instance($chat) { global $DB; $chat->timemodified = time(); + $chat->chattime = chat_calculate_next_chat_time($chat->schedule, $chat->chattime); $returnid = $DB->insert_record("chat", $chat); @@ -159,6 +168,7 @@ function chat_update_instance($chat) { $chat->timemodified = time(); $chat->id = $chat->instance; + $chat->chattime = chat_calculate_next_chat_time($chat->schedule, $chat->chattime); $DB->update_record("chat", $chat); @@ -636,6 +646,35 @@ function chat_delete_old_users() { } } +/** + * Calculate next chat session time based on schedule. + * + * @param int $schedule + * @param int $chattime + * + * @return int timestamp + */ +function chat_calculate_next_chat_time(int $schedule, int $chattime): int { + $timenow = time(); + + switch ($schedule) { + case CHAT_SCHEDULE_DAILY: { // Repeat daily. + while ($chattime <= $timenow) { + $chattime += DAYSECS; + } + break; + } + case CHAT_SCHEDULE_WEEKLY: { // Repeat weekly. + while ($chattime <= $timenow) { + $chattime += WEEKSECS; + } + break; + } + } + + return $chattime; +} + /** * Updates chat records so that the next chat time is correct * @@ -661,29 +700,18 @@ function chat_update_chat_times($chatid=0) { } } + $courseids = []; foreach ($chats as $chat) { - switch ($chat->schedule) { - case 1: // Single event - turn off schedule and disable. - $chat->chattime = 0; - $chat->schedule = 0; - break; - case 2: // Repeat daily. - while ($chat->chattime <= $timenow) { - $chat->chattime += 24 * 3600; - } - break; - case 3: // Repeat weekly. - while ($chat->chattime <= $timenow) { - $chat->chattime += 7 * 24 * 3600; - } - break; + $originalchattime = $chat->chattime; + $chat->chattime = chat_calculate_next_chat_time($chat->schedule, $chat->chattime); + if ($originalchattime != $chat->chattime) { + $courseids[] = $chat->course; } $DB->update_record("chat", $chat); - $event = new stdClass(); // Update calendar too. - $cond = "modulename='chat' AND instance = :chatid AND timestart <> :chattime"; - $params = array('chattime' => $chat->chattime, 'chatid' => $chat->id); + $cond = "modulename='chat' AND eventtype = :eventtype AND instance = :chatid AND timestart <> :chattime"; + $params = ['chattime' => $chat->chattime, 'eventtype' => CHAT_EVENT_TYPE_CHATTIME, 'chatid' => $chat->id]; if ($event->id = $DB->get_field_select('event', 'id', $cond, $params)) { $event->timestart = $chat->chattime; @@ -692,6 +720,11 @@ function chat_update_chat_times($chatid=0) { $calendarevent->update($event, false); } } + + $courseids = array_unique($courseids); + foreach ($courseids as $courseid) { + rebuild_course_cache($courseid, true); + } } /** @@ -1536,3 +1569,39 @@ function chat_get_session_messages($chatid, $group = false, $start = 0, $end = 0 return $DB->get_records_select('chat_messages', $select, $params, $sort); } + +/** + * Add a get_coursemodule_info function in case chat instance wants to add 'extra' information + * for the course (see resource). + * + * Given a course_module object, this function returns any "extra" information that may be needed + * when printing this activity in a course listing. See get_array_of_activities() in course/lib.php. + * + * @param stdClass $coursemodule The coursemodule object (record). + * @return cached_cm_info An object on information that the courses + * will know about (most noticeably, an icon). + */ +function chat_get_coursemodule_info($coursemodule) { + global $DB; + + $dbparams = ['id' => $coursemodule->instance]; + $fields = 'id, name, intro, introformat, chattime, schedule'; + if (!$chat = $DB->get_record('chat', $dbparams, $fields)) { + return false; + } + + $result = new cached_cm_info(); + $result->name = $chat->name; + if ($coursemodule->showdescription) { + // Convert intro to html. Do not filter cached version, filters run at display time. + $result->content = format_module_intro('chat', $chat, $coursemodule->id, false); + } + + // Populate some other values that can be used in calendar or on dashboard. + if ($chat->chattime) { + $result->customdata['chattime'] = $chat->chattime; + $result->customdata['schedule'] = $chat->schedule; + } + + return $result; +} diff --git a/mod/chat/tests/behat/behat_mod_chat.php b/mod/chat/tests/behat/behat_mod_chat.php new file mode 100644 index 00000000000..53c9eead539 --- /dev/null +++ b/mod/chat/tests/behat/behat_mod_chat.php @@ -0,0 +1,76 @@ +<?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/>. + +/** + * Steps definitions related to mod_chat. + * + * @package mod_chat + * @category test + * @copyright 2021 Dongsheng Cai + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +require_once(__DIR__ . '/../../../../lib/behat/behat_base.php'); + +/** + * Steps definitions related to mod_chat. + * + */ +class behat_mod_chat extends behat_base { + /** + * Convert page names to URLs for steps like 'When I am on the "[identifier]" "[page type]" page'. + * + * Recognised page names are: + * | pagetype | name meaning | description | + * | View | Chat name | The chat info page (view.php) | + * + * @param string $type identifies which type of page this is, e.g. 'View'. + * @param string $name chat instance name + * @return moodle_url the corresponding URL. + * @throws Exception with a meaningful error message if the specified page cannot be found. + */ + protected function resolve_page_instance_url(string $type, string $name): moodle_url { + switch ($type) { + case 'View': + $cm = $this->get_cm_by_chat_name($name); + return new moodle_url('/mod/chat/view.php', ['id' => $cm->id]); + default: + throw new Exception('Unrecognised chat page type "' . $type . '."'); + } + } + + /** + * Get a chat by name. + * + * @param string $name chat name. + * @return stdClass the corresponding DB row. + */ + protected function get_chat_by_name(string $name): stdClass { + global $DB; + return $DB->get_record('chat', ['name' => $name], '*', MUST_EXIST); + } + + /** + * Get a chat coursemodule object from the name. + * + * @param string $name chat name. + * @return stdClass cm from get_coursemodule_from_instance. + */ + protected function get_cm_by_chat_name(string $name): stdClass { + $chat = $this->get_chat_by_name($name); + return get_coursemodule_from_instance('chat', $chat->id, $chat->course); + } +} diff --git a/mod/chat/tests/dates_test.php b/mod/chat/tests/dates_test.php new file mode 100644 index 00000000000..dd8d3407612 --- /dev/null +++ b/mod/chat/tests/dates_test.php @@ -0,0 +1,127 @@ +<?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/>. + +/** + * Contains unit tests for mod_chat\dates. + * + * @package mod_chat + * @category test + * @copyright 2021 Dongsheng Cai + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +declare(strict_types=1); + +namespace mod_chat; + +use advanced_testcase; +use cm_info; +use core\activity_dates; + +/** + * Class for unit testing mod_chat\dates. + */ +class dates_test extends advanced_testcase { + + /** + * Data provider for get_dates_for_module(). + * @return array[] + */ + public function get_dates_for_module_provider(): array { + global $CFG; + require_once($CFG->dirroot . '/mod/chat/lib.php'); + + $now = time(); + $past = $now - DAYSECS; + $future = $now + DAYSECS; + + $dailynextchattime = $past + 2 * DAYSECS; + $weeklynextchattime = $past + 7 * DAYSECS; + $label = get_string('nextchattime', 'mod_chat'); + return [ + 'chattime in the past' => [ + $past, CHAT_SCHEDULE_NONE, [] + ], + 'chattime in the past' => [ + $past, CHAT_SCHEDULE_SINGLE, [] + ], + 'chattime in the future' => [ + $future, CHAT_SCHEDULE_SINGLE, [ + [ + 'label' => $label, + 'timestamp' => $future + ], + ] + ], + 'future chattime weekly' => [ + $future, CHAT_SCHEDULE_WEEKLY, [ + [ + 'label' => $label, + 'timestamp' => $future + ] + ] + ], + 'future chattime daily' => [ + $future, CHAT_SCHEDULE_DAILY, [ + [ + 'label' => $label, + 'timestamp' => $future + ] + ] + ], + 'past chattime daily' => [ + $past, CHAT_SCHEDULE_DAILY, [ + [ + 'label' => $label, + 'timestamp' => $dailynextchattime + ], + ] + ], + 'past chattime weekly' => [ + $past, CHAT_SCHEDULE_WEEKLY, [ + [ + 'label' => $label, + 'timestamp' => $weeklynextchattime + ], + ] + ], + ]; + } + + /** + * Test for get_dates_for_module(). + * + * @dataProvider get_dates_for_module_provider + * @param int|null $chattime + * @param int|null $schedule + * @param array $expected The expected value of calling get_dates_for_module() + */ + public function test_get_dates_for_module(?int $chattime, ?int $schedule, array $expected) { + $this->resetAfterTest(); + $course = $this->getDataGenerator()->create_course(); + $user = $this->getDataGenerator()->create_user(); + $this->getDataGenerator()->enrol_user($user->id, $course->id); + $chat = ['course' => $course->id]; + $chat['chattime'] = $chattime; + $chat['schedule'] = $schedule; + $modchat = $this->getDataGenerator()->create_module('chat', $chat); + $this->setUser($user); + $cm = get_coursemodule_from_instance('chat', $modchat->id); + $cminfo = cm_info::create($cm); + $dates = activity_dates::get_dates_for_module($cminfo, (int) $user->id); + $this->assertEquals($expected, $dates); + } +} diff --git a/mod/chat/view.php b/mod/chat/view.php index 6daf4d0b7ef..ad98f546fdc 100644 --- a/mod/chat/view.php +++ b/mod/chat/view.php @@ -122,14 +122,10 @@ if (has_capability('mod/chat:chat', $context)) { echo $OUTPUT->box_start('generalbox', 'enterlink'); $now = time(); - $span = $chat->chattime - $now; - if ($chat->chattime and $chat->schedule and ($span > 0)) { // A chat is scheduled. - echo '<p>'; - $chatinfo = new stdClass(); - $chatinfo->date = userdate($chat->chattime); - $chatinfo->fromnow = format_time($span); - echo get_string('sessionstart', 'chat', $chatinfo); - echo '</p>'; + $chattime = $chat->chattime ?? 0; + $span = $chattime - $now; + if (!empty($chat->schedule) && $span > 0) { + echo html_writer::tag('p', get_string('sessionstartsin', 'chat', format_time($span))); } $params['id'] = $chat->id;