From 2553e9db8e8d74eec16d7f03b69ddda446d168bd Mon Sep 17 00:00:00 2001 From: Jake Dallimore Date: Mon, 5 Nov 2018 14:28:00 +0800 Subject: [PATCH] MDL-63724 core_message: add send_messages_to_conversation() web service --- lib/db/services.php | 10 ++ message/externallib.php | 70 ++++++++++++ message/tests/externallib_test.php | 167 +++++++++++++++++++++++++++++ 3 files changed, 247 insertions(+) diff --git a/lib/db/services.php b/lib/db/services.php index 346ea3c811f..5c2759be1e4 100644 --- a/lib/db/services.php +++ b/lib/db/services.php @@ -1191,6 +1191,16 @@ $functions = array( 'ajax' => true, 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), + 'core_message_send_messages_to_conversation' => array( + 'classname' => 'core_message_external', + 'methodname' => 'send_messages_to_conversation', + 'classpath' => 'message/externallib.php', + 'description' => 'Send messages to an existing conversation between users', + 'type' => 'write', + 'capabilities' => 'moodle/site:sendmessage', + 'ajax' => true, + 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), + ), 'core_message_get_conversation_messages' => array( 'classname' => 'core_message_external', 'methodname' => 'get_conversation_messages', diff --git a/message/externallib.php b/message/externallib.php index af3740a426d..59950cef455 100644 --- a/message/externallib.php +++ b/message/externallib.php @@ -39,6 +39,76 @@ require_once($CFG->dirroot . "/message/lib.php"); * @since Moodle 2.2 */ class core_message_external extends external_api { + /** + * Returns description of method parameters + * + * @return external_function_parameters + * @since Moodle 3.6 + */ + public static function send_messages_to_conversation_parameters() { + return new external_function_parameters( + array( + 'conversationid' => new external_value(PARAM_INT, 'id of the conversation'), + 'messages' => new external_multiple_structure( + new external_single_structure( + array( + 'text' => new external_value(PARAM_RAW, 'the text of the message'), + 'textformat' => new external_format_value('text', VALUE_DEFAULT, FORMAT_MOODLE), + ) + ) + ) + ) + ); + } + + /** + * Send messages from the current USER to a conversation. + * + * This conversation may be any type of conversation, individual or group. + * + * @param int $conversationid the id of the conversation to which the messages will be sent. + * @param array $messages An array of message to send. + * @return array the array of messages which were sent (created). + * @since Moodle 3.6 + */ + public static function send_messages_to_conversation(int $conversationid, array $messages = []) { + global $CFG, $USER; + + // Check if messaging is enabled. + if (empty($CFG->messaging)) { + throw new moodle_exception('disabled', 'message'); + } + + // Ensure the current user is allowed to run this function. + $context = context_system::instance(); + self::validate_context($context); + + $params = self::validate_parameters(self::send_messages_to_conversation_parameters(), [ + 'conversationid' => $conversationid, + 'messages' => $messages + ]); + + $messages = []; + foreach ($params['messages'] as $message) { + $messages[] = \core_message\api::send_message_to_conversation($USER->id, $params['conversationid'], $message['text'], + $message['textformat']); + } + + return $messages; + } + + /** + * Returns description of method result value. + * + * @return external_description + * @since Moodle 3.6 + */ + public static function send_messages_to_conversation_returns() { + return new external_multiple_structure( + self::get_conversation_message_structure() + ); + } + /** * Returns description of method parameters diff --git a/message/tests/externallib_test.php b/message/tests/externallib_test.php index 0781adc416f..a5a54b4e096 100644 --- a/message/tests/externallib_test.php +++ b/message/tests/externallib_test.php @@ -5313,4 +5313,171 @@ class core_message_externallib_testcase extends externallib_advanced_testcase { $this->expectException('moodle_exception'); core_message_external::get_conversation_members($user3->id, $conversationid); } + + /** + * Test verifying multiple messages can be sent to an individual conversation. + */ + public function test_send_messages_to_conversation_individual() { + $this->resetAfterTest(true); + + // Get a bunch of conversations, some group, some individual and in different states. + list($user1, $user2, $user3, $user4, $ic1, $ic2, $ic3, + $gc1, $gc2, $gc3, $gc4, $gc5, $gc6) = $this->create_conversation_test_data(); + + // Enrol the users in the same course, so the default privacy controls (course + contacts) can be used. + $course1 = $this->getDataGenerator()->create_course(); + $this->getDataGenerator()->enrol_user($user1->id, $course1->id); + $this->getDataGenerator()->enrol_user($user2->id, $course1->id); + $this->getDataGenerator()->enrol_user($user3->id, $course1->id); + $this->getDataGenerator()->enrol_user($user4->id, $course1->id); + + // The user making the request. + $this->setUser($user1); + + // Try to send a message as user1 to a conversation user1 is a a part of. + $messages = [ + [ + 'text' => 'a message from user 1', + 'textformat' => FORMAT_MOODLE + ], + [ + 'text' => 'another message from user 1', + 'textformat' => FORMAT_MOODLE + ], + ]; + // Redirect messages. + // This marks messages as read, but we can still observe and verify the number of conversation recipients, + // based on the message_viewed events generated as part of marking the message as read for each user. + $this->preventResetByRollback(); + $sink = $this->redirectMessages(); + $writtenmessages = core_message_external::send_messages_to_conversation($ic1->id, $messages); + + external_api::clean_returnvalue(core_message_external::send_messages_to_conversation_returns(), $writtenmessages); + + $this->assertCount(2, $writtenmessages); + $this->assertObjectHasAttribute('id', $writtenmessages[0]); + $this->assertEquals($user1->id, $writtenmessages[0]->useridfrom); + $this->assertEquals($messages[0]['text'], $writtenmessages[0]->text); + $this->assertNotEmpty($writtenmessages[0]->timecreated); + + $this->assertObjectHasAttribute('id', $writtenmessages[1]); + $this->assertEquals($user1->id, $writtenmessages[1]->useridfrom); + $this->assertEquals($messages[1]['text'], $writtenmessages[1]->text); + $this->assertNotEmpty($writtenmessages[1]->timecreated); + } + + /** + * Test verifying multiple messages can be sent to an group conversation. + */ + public function test_send_messages_to_conversation_group() { + $this->resetAfterTest(true); + + // Get a bunch of conversations, some group, some individual and in different states. + list($user1, $user2, $user3, $user4, $ic1, $ic2, $ic3, + $gc1, $gc2, $gc3, $gc4, $gc5, $gc6) = $this->create_conversation_test_data(); + + // Enrol the users in the same course, so the default privacy controls (course + contacts) can be used. + $course1 = $this->getDataGenerator()->create_course(); + $this->getDataGenerator()->enrol_user($user1->id, $course1->id); + $this->getDataGenerator()->enrol_user($user2->id, $course1->id); + $this->getDataGenerator()->enrol_user($user3->id, $course1->id); + $this->getDataGenerator()->enrol_user($user4->id, $course1->id); + + // The user making the request. + $this->setUser($user1); + + // Try to send a message as user1 to a conversation user1 is a a part of. + $messages = [ + [ + 'text' => 'a message from user 1 to group conv', + 'textformat' => FORMAT_MOODLE + ], + [ + 'text' => 'another message from user 1 to group conv', + 'textformat' => FORMAT_MOODLE + ], + ]; + // Redirect messages. + // This marks messages as read, but we can still observe and verify the number of conversation recipients, + // based on the message_viewed events generated as part of marking the message as read for each user. + $this->preventResetByRollback(); + $sink = $this->redirectMessages(); + $writtenmessages = core_message_external::send_messages_to_conversation($gc2->id, $messages); + + external_api::clean_returnvalue(core_message_external::send_messages_to_conversation_returns(), $writtenmessages); + + $this->assertCount(2, $writtenmessages); + $this->assertObjectHasAttribute('id', $writtenmessages[0]); + $this->assertEquals($user1->id, $writtenmessages[0]->useridfrom); + $this->assertEquals($messages[0]['text'], $writtenmessages[0]->text); + $this->assertNotEmpty($writtenmessages[0]->timecreated); + + $this->assertObjectHasAttribute('id', $writtenmessages[1]); + $this->assertEquals($user1->id, $writtenmessages[1]->useridfrom); + $this->assertEquals($messages[1]['text'], $writtenmessages[1]->text); + $this->assertNotEmpty($writtenmessages[1]->timecreated); + } + + /** + * Test verifying multiple messages can not be sent to a non existent conversation. + */ + public function test_send_messages_to_conversation_non_existent_conversation() { + $this->resetAfterTest(true); + + // Get a bunch of conversations, some group, some individual and in different states. + list($user1, $user2, $user3, $user4, $ic1, $ic2, $ic3, + $gc1, $gc2, $gc3, $gc4, $gc5, $gc6) = $this->create_conversation_test_data(); + + // The user making the request. + $this->setUser($user1); + + // Try to send a message as user1 to a conversation user1 is a a part of. + $messages = [ + [ + 'text' => 'a message from user 1', + 'textformat' => FORMAT_MOODLE + ], + [ + 'text' => 'another message from user 1', + 'textformat' => FORMAT_MOODLE + ], + ]; + $this->expectException(\moodle_exception::class); + $writtenmessages = core_message_external::send_messages_to_conversation(0, $messages); + } + + /** + * Test verifying multiple messages can not be sent to a conversation by a non-member. + */ + public function test_send_messages_to_conversation_non_member() { + $this->resetAfterTest(true); + + // Get a bunch of conversations, some group, some individual and in different states. + list($user1, $user2, $user3, $user4, $ic1, $ic2, $ic3, + $gc1, $gc2, $gc3, $gc4, $gc5, $gc6) = $this->create_conversation_test_data(); + + // Enrol the users in the same course, so the default privacy controls (course + contacts) can be used. + $course1 = $this->getDataGenerator()->create_course(); + $this->getDataGenerator()->enrol_user($user1->id, $course1->id); + $this->getDataGenerator()->enrol_user($user2->id, $course1->id); + $this->getDataGenerator()->enrol_user($user3->id, $course1->id); + $this->getDataGenerator()->enrol_user($user4->id, $course1->id); + + // The user making the request. This user is not a member of group conversation 1 (gc1). + $this->setUser($user1); + + // Try to send a message as user1 to a conversation user1 is a a part of. + $messages = [ + [ + 'text' => 'a message from user 1 to group conv', + 'textformat' => FORMAT_MOODLE + ], + [ + 'text' => 'another message from user 1 to group conv', + 'textformat' => FORMAT_MOODLE + ], + ]; + $this->expectException(\moodle_exception::class); + $writtenmessages = core_message_external::send_messages_to_conversation($gc1->id, $messages); + } }