diff --git a/lib/db/services.php b/lib/db/services.php index beb8dd01eaa..712c46c8c39 100644 --- a/lib/db/services.php +++ b/lib/db/services.php @@ -1492,6 +1492,12 @@ $functions = array( 'ajax' => true, 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), + 'core_message_get_unread_notification_count' => [ + 'classname' => '\core_message\external\get_unread_notification_count', + 'description' => 'Get number of unread notifications.', + 'type' => 'read', + 'services' => [MOODLE_OFFICIAL_MOBILE_SERVICE], + ], 'core_notes_create_notes' => array( 'classname' => 'core_notes_external', 'methodname' => 'create_notes', diff --git a/message/classes/external/get_unread_notification_count.php b/message/classes/external/get_unread_notification_count.php new file mode 100644 index 00000000000..402c70e13f9 --- /dev/null +++ b/message/classes/external/get_unread_notification_count.php @@ -0,0 +1,99 @@ +. + +namespace core_message\external; + +defined('MOODLE_INTERNAL') || die(); + +global $CFG; +require_once($CFG->libdir . '/externallib.php'); +require_once($CFG->dirroot . '/message/lib.php'); + +use external_api; +use external_function_parameters; +use external_value; +use context_system; +use core_user; +use moodle_exception; + +/** + * External service to get number of unread notifications + * + * @package core_message + * @category external + * @copyright 2021 Dani Palou + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @since Moodle 4.0 + */ +class get_unread_notification_count extends external_api { + /** + * Returns description of method parameters + * + * @return external_function_parameters + */ + public static function execute_parameters(): external_function_parameters { + return new external_function_parameters([ + 'useridto' => new external_value(PARAM_INT, 'user id who received the notification, 0 for any user', VALUE_REQUIRED), + ]); + } + + /** + * Get number of unread notifications. + * + * @param int $useridto the user id who received the notification, 0 for any user + * @return int number of unread notifications + * @throws \moodle_exception + */ + public static function execute(int $useridto): int { + global $USER; + + $params = self::validate_parameters( + self::execute_parameters(), + ['useridto' => $useridto], + ); + + $context = context_system::instance(); + self::validate_context($context); + + $useridto = $params['useridto']; + + if (!empty($useridto)) { + if (core_user::is_real_user($useridto)) { + $userto = core_user::get_user($useridto, '*', MUST_EXIST); + } else { + throw new moodle_exception('invaliduser'); + } + } + + // Check if the current user is the sender/receiver or just a privileged user. + if ($useridto != $USER->id and !has_capability('moodle/site:readallmessages', $context)) { + throw new moodle_exception('accessdenied', 'admin'); + } + + $messages = message_get_messages($useridto, 0, 1, MESSAGE_GET_UNREAD); + + return count($messages); + } + + /** + * Describe the return structure of the external service. + * + * @return external_value + */ + public static function execute_returns(): external_value { + return new external_value(PARAM_INT, 'The count of unread notifications.'); + } +} diff --git a/message/classes/tests/helper.php b/message/classes/tests/helper.php index 986df914c16..a0c1d645a10 100644 --- a/message/classes/tests/helper.php +++ b/message/classes/tests/helper.php @@ -58,4 +58,70 @@ class helper { $record->timecreated = $time; return $DB->insert_record('messages', $record); } + + /** + * Send a fake unread notification. + * + * message_send() does not support transaction, this function will simulate a message + * sent from a user to another. We should stop using it once message_send() will support + * transactions. This is not clean at all, this is just used to add rows to the table. + * + * @param stdClass $userfrom user object of the one sending the message. + * @param stdClass $userto user object of the one receiving the message. + * @param string $message message to send. + * @param int $timecreated time the message was created. + * @return int the id of the message + */ + public static function send_fake_unread_notification(\stdClass $userfrom, \stdClass $userto, string $message = 'Hello world!', + int $timecreated = 0): int { + global $DB; + + $record = new \stdClass(); + $record->useridfrom = $userfrom->id; + $record->useridto = $userto->id; + $record->notification = 1; + $record->subject = 'No subject'; + $record->fullmessage = $message; + $record->smallmessage = $message; + $record->timecreated = $timecreated ? $timecreated : time(); + $record->customdata = json_encode(['datakey' => 'data']); + + return $DB->insert_record('notifications', $record); + } + + /** + * Send a fake read notification. + * + * message_send() does not support transaction, this function will simulate a message + * sent from a user to another. We should stop using it once message_send() will support + * transactions. This is not clean at all, this is just used to add rows to the table. + * + * @param stdClass $userfrom user object of the one sending the message. + * @param stdClass $userto user object of the one receiving the message. + * @param string $message message to send. + * @param int $timecreated time the message was created. + * @param int $timeread the the message was read + * @return int the id of the message + */ + public static function send_fake_read_notification(\stdClass $userfrom, \stdClass $userto, string $message = 'Hello world!', + int $timecreated = 0, int $timeread = 0): int { + global $DB; + + $record = new \stdClass(); + $record->useridfrom = $userfrom->id; + $record->useridto = $userto->id; + $record->notification = 1; + $record->subject = 'No subject'; + $record->fullmessage = $message; + $record->smallmessage = $message; + $record->timecreated = $timecreated ? $timecreated : time(); + $record->timeread = $timeread ? $timeread : time(); + + $record->id = $DB->insert_record('notifications', $record); + + // Mark it as read. + \core_message\api::mark_notification_as_read($record); + + return $record->id; + } } diff --git a/message/output/popup/tests/base.php b/message/output/popup/tests/base.php index 840a68fdf7e..d3d2b96f710 100644 --- a/message/output/popup/tests/base.php +++ b/message/output/popup/tests/base.php @@ -24,7 +24,10 @@ defined('MOODLE_INTERNAL') || die(); +use \core_message\tests\helper as testhelper; + trait message_popup_test_helper { + /** * Send a fake unread popup notification. * @@ -38,20 +41,11 @@ trait message_popup_test_helper { * @param int $timecreated time the message was created. * @return int the id of the message */ - protected function send_fake_unread_popup_notification($userfrom, $userto, $message = 'Hello world!', $timecreated = 0) { + protected function send_fake_unread_popup_notification(\stdClass $userfrom, \stdClass $userto, + string $message = 'Hello world!', int $timecreated = 0): int { global $DB; - $record = new stdClass(); - $record->useridfrom = $userfrom->id; - $record->useridto = $userto->id; - $record->notification = 1; - $record->subject = 'No subject'; - $record->fullmessage = $message; - $record->smallmessage = $message; - $record->timecreated = $timecreated ? $timecreated : time(); - $record->customdata = json_encode(['datakey' => 'data']); - - $id = $DB->insert_record('notifications', $record); + $id = testhelper::send_fake_unread_notification($userfrom, $userto, $message, $timecreated); $popup = new stdClass(); $popup->notificationid = $id; @@ -75,29 +69,16 @@ trait message_popup_test_helper { * @param int $timeread the the message was read * @return int the id of the message */ - protected function send_fake_read_popup_notification($userfrom, $userto, $message = 'Hello world!', - $timecreated = 0, $timeread = 0) { + protected function send_fake_read_popup_notification(\stdClass $userfrom, \stdClass $userto, string $message = 'Hello world!', + int $timecreated = 0, int $timeread = 0): int { global $DB; - $record = new stdClass(); - $record->useridfrom = $userfrom->id; - $record->useridto = $userto->id; - $record->notification = 1; - $record->subject = 'No subject'; - $record->fullmessage = $message; - $record->smallmessage = $message; - $record->timecreated = $timecreated ? $timecreated : time(); - $record->timeread = $timeread ? $timeread : time(); - - $record->id = $DB->insert_record('notifications', $record); - - // Mark it as read. - \core_message\api::mark_notification_as_read($record); + $id = testhelper::send_fake_read_notification($userfrom, $userto, $message, $timecreated, $timeread); $popup = new stdClass(); - $popup->notificationid = $record->id; + $popup->notificationid = $id; $DB->insert_record('message_popup_notifications', $popup); - return $record->id; + return $id; } } diff --git a/message/tests/external/get_unread_notification_count_test.php b/message/tests/external/get_unread_notification_count_test.php new file mode 100644 index 00000000000..feef3c20f04 --- /dev/null +++ b/message/tests/external/get_unread_notification_count_test.php @@ -0,0 +1,90 @@ +. + +namespace core_message\external; + +defined('MOODLE_INTERNAL') || die(); + +global $CFG; +require_once($CFG->dirroot . '/webservice/tests/helpers.php'); + +use external_api; +use externallib_advanced_testcase; +use \core_message\tests\helper as testhelper; + +/** + * External function test for get_unread_notification_count. + * + * @package core_message + * @category test + * @copyright 2021 Dani Palou , based on Ryan Wyllie code + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @since Moodle 4.0 + */ +class get_unread_notification_count_test extends externallib_advanced_testcase { + + /** + * get_unread_notification should throw an exception for an invalid user. + */ + public function test_get_unread_notification_count_invalid_user_exception(): void { + $this->resetAfterTest(true); + + $this->expectException('moodle_exception'); + $result = get_unread_notification_count::execute(-2132131); + } + + /** + * get_unread_notification_count should throw exception if being requested for non-logged in user. + */ + public function test_get_unread_notification_count_access_denied_exception(): void { + $this->resetAfterTest(true); + + $sender = $this->getDataGenerator()->create_user(); + $user = $this->getDataGenerator()->create_user(); + + $this->setUser($user); + $this->expectException('moodle_exception'); + $result = get_unread_notification_count::execute($sender->id); + } + + /** + * Test get_unread_notification_count. + */ + public function test_get_unread_notification_count(): void { + $this->resetAfterTest(true); + + $sender1 = $this->getDataGenerator()->create_user(); + $sender2 = $this->getDataGenerator()->create_user(); + $sender3 = $this->getDataGenerator()->create_user(); + $recipient = $this->getDataGenerator()->create_user(); + + $this->setUser($recipient); + + $notificationids = [ + testhelper::send_fake_unread_notification($sender1, $recipient, 'Notification', 1), + testhelper::send_fake_unread_notification($sender1, $recipient, 'Notification', 2), + testhelper::send_fake_unread_notification($sender2, $recipient, 'Notification', 3), + testhelper::send_fake_unread_notification($sender2, $recipient, 'Notification', 4), + testhelper::send_fake_unread_notification($sender3, $recipient, 'Notification', 5), + testhelper::send_fake_unread_notification($sender3, $recipient, 'Notification', 6), + ]; + + $count = get_unread_notification_count::execute($recipient->id); + $this->assertEquals($count, 6); + + } + +} diff --git a/version.php b/version.php index e59cc0b730b..6b98bb9c1af 100644 --- a/version.php +++ b/version.php @@ -29,7 +29,7 @@ defined('MOODLE_INTERNAL') || die(); -$version = 2021100300.00; // YYYYMMDD = weekly release date of this DEV branch. +$version = 2021100300.01; // YYYYMMDD = weekly release date of this DEV branch. // RR = release increments - 00 in DEV branches. // .XX = incremental changes. $release = '4.0dev (Build: 20211003)'; // Human-friendly version name