a message from user 1
', $writtenmessages[0]->text); $this->assertNotEmpty($writtenmessages[0]->timecreated); $this->assertObjectHasProperty('id', $writtenmessages[1]); $this->assertEquals($user1->id, $writtenmessages[1]->useridfrom); $this->assertEquals('another message from user 1
', $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->assertObjectHasProperty('id', $writtenmessages[0]); $this->assertEquals($user1->id, $writtenmessages[0]->useridfrom); $this->assertEquals('a message from user 1 to group conv
', $writtenmessages[0]->text); $this->assertNotEmpty($writtenmessages[0]->timecreated); $this->assertObjectHasProperty('id', $writtenmessages[1]); $this->assertEquals($user1->id, $writtenmessages[1]->useridfrom); $this->assertEquals('another message from user 1 to group conv
', $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); } /** * Test verifying a to long message can not be sent to a conversation. */ public function test_send_messages_to_conversation_long_text() { $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' => str_repeat("M", \core_message\api::MESSAGE_MAX_LENGTH + 100), 'textformat' => FORMAT_MOODLE ], ]; $this->expectException(\moodle_exception::class); $writtenmessages = core_message_external::send_messages_to_conversation($gc2->id, $messages); } /** * Test getting a conversation that doesn't exist. */ public function test_get_conversation_no_conversation() { $this->resetAfterTest(); $user1 = self::getDataGenerator()->create_user(); $user2 = self::getDataGenerator()->create_user(); $name = 'lol conversation'; $conversation = \core_message\api::create_conversation( \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL, [ $user1->id, $user2->id, ], $name ); $conversationid = $conversation->id; $this->setUser($user1); $this->expectException('moodle_exception'); $conv = core_message_external::get_conversation($user1->id, $conversationid + 1); external_api::clean_returnvalue(core_message_external::get_conversation_returns(), $conv); } /** * Test verifying that the correct favourite information is returned for a non-linked converastion at user context. */ public function test_get_conversation_favourited() { $this->resetAfterTest(); $user1 = self::getDataGenerator()->create_user(); $user2 = self::getDataGenerator()->create_user(); // Create a conversation between the 2 users. $conversation = \core_message\api::create_conversation( \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL, [ $user1->id, $user2->id, ], 'An individual conversation' ); // Favourite the conversation as user 1 only. \core_message\api::set_favourite_conversation($conversation->id, $user1->id); // Get the conversation for user1 and confirm it's favourited. $this->setUser($user1); $conv = core_message_external::get_conversation($user1->id, $conversation->id); $conv = external_api::clean_returnvalue(core_message_external::get_conversation_returns(), $conv); $this->assertTrue($conv['isfavourite']); // Get the conversation for user2 and confirm it's NOT favourited. $this->setUser($user2); $conv = core_message_external::get_conversation($user2->id, $conversation->id); $conv = external_api::clean_returnvalue(core_message_external::get_conversation_returns(), $conv); $this->assertFalse($conv['isfavourite']); } /** * Test verifying that the correct favourite information is returned for a group-linked conversation at course context. */ public function test_get_conversation_favourited_group_linked() { $this->resetAfterTest(); global $DB; $user1 = self::getDataGenerator()->create_user(); $user2 = self::getDataGenerator()->create_user(); $user3 = self::getDataGenerator()->create_user(); $course1 = $this->getDataGenerator()->create_course(); $course1context = \context_course::instance($course1->id); // Create a group with a linked conversation and a valid image. $this->setAdminUser(); $this->getDataGenerator()->enrol_user($user1->id, $course1->id); $this->getDataGenerator()->enrol_user($user2->id, $course1->id); $this->getDataGenerator()->enrol_user($user3->id, $course1->id); $group1 = $this->getDataGenerator()->create_group([ 'courseid' => $course1->id, 'enablemessaging' => 1 ]); // Add users to group1. $this->getDataGenerator()->create_group_member(array('groupid' => $group1->id, 'userid' => $user1->id)); $this->getDataGenerator()->create_group_member(array('groupid' => $group1->id, 'userid' => $user2->id)); // Verify that the conversation is a group linked conversation in the course context. $conversationrecord = $DB->get_record('message_conversations', ['component' => 'core_group', 'itemtype' => 'groups']); $this->assertEquals($course1context->id, $conversationrecord->contextid); // Favourite the conversation as user 1 only. \core_message\api::set_favourite_conversation($conversationrecord->id, $user1->id); // Get the conversation for user1 and confirm it's favourited. $this->setUser($user1); $conv = core_message_external::get_conversation($user1->id, $conversationrecord->id); $conv = external_api::clean_returnvalue(core_message_external::get_conversation_returns(), $conv); $this->assertTrue($conv['isfavourite']); // Get the conversation for user2 and confirm it's NOT favourited. $this->setUser($user2); $conv = core_message_external::get_conversation($user2->id, $conversationrecord->id); $conv = external_api::clean_returnvalue(core_message_external::get_conversation_returns(), $conv); $this->assertFalse($conv['isfavourite']); } /** * Test getting a conversation with no messages. */ public function test_get_conversation_no_messages() { $this->resetAfterTest(); $user1 = self::getDataGenerator()->create_user(); $user2 = self::getDataGenerator()->create_user(); $name = 'lol conversation'; $conversation = \core_message\api::create_conversation( \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL, [ $user1->id, $user2->id, ], $name ); $conversationid = $conversation->id; $this->setUser($user1); $conv = core_message_external::get_conversation($user1->id, $conversationid); external_api::clean_returnvalue(core_message_external::get_conversation_returns(), $conv); $conv = (array) $conv; $this->assertEquals($conversationid, $conv['id']); $this->assertEquals($name, $conv['name']); $this->assertArrayHasKey('subname', $conv); $this->assertEquals(\core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL, $conv['type']); $this->assertEquals(2, $conv['membercount']); $this->assertEquals(false, $conv['isfavourite']); $this->assertEquals(true, $conv['isread']); $this->assertEquals(0, $conv['unreadcount']); $this->assertCount(1, $conv['members']); foreach ($conv['members'] as $member) { $member = (array) $member; $this->assertArrayHasKey('id', $member); $this->assertArrayHasKey('fullname', $member); $this->assertArrayHasKey('profileimageurl', $member); $this->assertArrayHasKey('profileimageurlsmall', $member); $this->assertArrayHasKey('isonline', $member); $this->assertArrayHasKey('showonlinestatus', $member); $this->assertArrayHasKey('isblocked', $member); $this->assertArrayHasKey('iscontact', $member); } $this->assertEmpty($conv['messages']); } /** * Test getting a conversation with messages. */ public function test_get_conversation_with_messages() { $this->resetAfterTest(); $user1 = self::getDataGenerator()->create_user(); $user2 = self::getDataGenerator()->create_user(); $user3 = self::getDataGenerator()->create_user(); // Some random conversation. $otherconversation = \core_message\api::create_conversation( \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL, [ $user1->id, $user3->id, ] ); $conversation = \core_message\api::create_conversation( \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL, [ $user1->id, $user2->id, ] ); $conversationid = $conversation->id; $time = time(); $message1id = testhelper::send_fake_message_to_conversation($user1, $conversation->id, 'A', $time - 10); $message2id = testhelper::send_fake_message_to_conversation($user2, $conversation->id, 'B', $time - 5); $message3id = testhelper::send_fake_message_to_conversation($user1, $conversation->id, 'C', $time); // Add some messages to the other convo to make sure they aren't included. testhelper::send_fake_message_to_conversation($user1, $otherconversation->id, 'foo'); $this->setUser($user1); // Test newest first. $conv = core_message_external::get_conversation( $user1->id, $conversationid, false, false, 0, 0, 0, 0, true ); external_api::clean_returnvalue(core_message_external::get_conversation_returns(), $conv); $conv = (array) $conv; $this->assertEquals(false, $conv['isread']); $this->assertEquals(1, $conv['unreadcount']); $this->assertCount(3, $conv['messages']); $this->assertEquals($message3id, $conv['messages'][0]->id); $this->assertEquals($user1->id, $conv['messages'][0]->useridfrom); $this->assertEquals($message2id, $conv['messages'][1]->id); $this->assertEquals($user2->id, $conv['messages'][1]->useridfrom); $this->assertEquals($message1id, $conv['messages'][2]->id); $this->assertEquals($user1->id, $conv['messages'][2]->useridfrom); // Test newest last. $conv = core_message_external::get_conversation( $user1->id, $conversationid, false, false, 0, 0, 0, 0, false ); external_api::clean_returnvalue(core_message_external::get_conversation_returns(), $conv); $conv = (array) $conv; $this->assertCount(3, $conv['messages']); $this->assertEquals($message3id, $conv['messages'][2]->id); $this->assertEquals($user1->id, $conv['messages'][2]->useridfrom); $this->assertEquals($message2id, $conv['messages'][1]->id); $this->assertEquals($user2->id, $conv['messages'][1]->useridfrom); $this->assertEquals($message1id, $conv['messages'][0]->id); $this->assertEquals($user1->id, $conv['messages'][0]->useridfrom); // Test message offest and limit. $conv = core_message_external::get_conversation( $user1->id, $conversationid, false, false, 0, 0, 1, 1, true ); external_api::clean_returnvalue(core_message_external::get_conversation_returns(), $conv); $conv = (array) $conv; $this->assertCount(1, $conv['messages']); $this->assertEquals($message2id, $conv['messages'][0]->id); $this->assertEquals($user2->id, $conv['messages'][0]->useridfrom); } /** * Data provider for test_get_conversation_counts(). */ public function get_conversation_counts_test_cases() { $typeindividual = \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL; $typegroup = \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP; $typeself = \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF; list($user1, $user2, $user3, $user4, $user5, $user6, $user7, $user8) = [0, 1, 2, 3, 4, 5, 6, 7]; $conversations = [ [ 'type' => $typeindividual, 'users' => [$user1, $user2], 'messages' => [$user1, $user2, $user2], 'favourites' => [$user1], 'enabled' => null // Individual conversations cannot be disabled. ], [ 'type' => $typeindividual, 'users' => [$user1, $user3], 'messages' => [$user1, $user3, $user1], 'favourites' => [], 'enabled' => null // Individual conversations cannot be disabled. ], [ 'type' => $typegroup, 'users' => [$user1, $user2, $user3, $user4], 'messages' => [$user1, $user2, $user3, $user4], 'favourites' => [], 'enabled' => true ], [ 'type' => $typegroup, 'users' => [$user2, $user3, $user4], 'messages' => [$user2, $user3, $user4], 'favourites' => [], 'enabled' => true ], [ 'type' => $typegroup, 'users' => [$user6, $user7], 'messages' => [$user6, $user7, $user7], 'favourites' => [$user6], 'enabled' => false ], [ 'type' => $typeself, 'users' => [$user8], 'messages' => [$user8], 'favourites' => [], 'enabled' => null // Individual conversations cannot be disabled. ], ]; return [ 'No conversations' => [ 'conversationConfigs' => $conversations, 'deletemessagesuser' => null, 'deletemessages' => [], 'arguments' => [$user5], 'expectedcounts' => ['favourites' => 1, 'types' => [ \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 0, \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 0, \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => 0 ]], 'expectedunreadcounts' => ['favourites' => 0, 'types' => [ \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 0, \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 0, \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => 0 ]], 'deletedusers' => [] ], 'No individual conversations, 2 group conversations' => [ 'conversationConfigs' => $conversations, 'deletemessagesuser' => null, 'deletemessages' => [], 'arguments' => [$user4], 'expectedcounts' => ['favourites' => 1, 'types' => [ \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 0, \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 2, \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => 0 ]], 'expectedunreadcounts' => ['favourites' => 0, 'types' => [ \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 0, \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 2, \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => 0 ]], 'deletedusers' => [] ], '2 individual conversations (one favourited), 1 group conversation' => [ 'conversationConfigs' => $conversations, 'deletemessagesuser' => null, 'deletemessages' => [], 'arguments' => [$user1], 'expectedcounts' => ['favourites' => 2, 'types' => [ \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 1, \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 1, \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => 0 ]], 'expectedunreadcounts' => ['favourites' => 1, 'types' => [ \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 1, \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 1, \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => 0 ]], 'deletedusers' => [] ], '1 individual conversation, 2 group conversations' => [ 'conversationConfigs' => $conversations, 'deletemessagesuser' => null, 'deletemessages' => [], 'arguments' => [$user2], 'expectedcounts' => ['favourites' => 1, 'types' => [ \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 1, \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 2, \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => 0 ]], 'expectedunreadcounts' => ['favourites' => 0, 'types' => [ \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 1, \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 2, \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => 0 ]], 'deletedusers' => [] ], '2 group conversations only' => [ 'conversationConfigs' => $conversations, 'deletemessagesuser' => null, 'deletemessages' => [], 'arguments' => [$user4], 'expectedcounts' => ['favourites' => 1, 'types' => [ \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 0, \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 2, \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => 0 ]], 'expectedunreadcounts' => ['favourites' => 0, 'types' => [ \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 0, \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 2, \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => 0 ]], 'deletedusers' => [] ], 'All conversation types, delete a message from individual favourited, messages remaining' => [ 'conversationConfigs' => $conversations, 'deletemessagesuser' => $user1, 'deletemessages' => [0], 'arguments' => [$user1], 'expectedcounts' => ['favourites' => 2, 'types' => [ \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 1, \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 1, \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => 0 ]], 'expectedunreadcounts' => ['favourites' => 1, 'types' => [ \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 1, \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 1, \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => 0 ]], 'deletedusers' => [] ], 'All conversation types, delete a message from individual non-favourited, messages remaining' => [ 'conversationConfigs' => $conversations, 'deletemessagesuser' => $user1, 'deletemessages' => [3], 'arguments' => [$user1], 'expectedcounts' => ['favourites' => 2, 'types' => [ \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 1, \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 1, \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => 0 ]], 'expectedunreadcounts' => ['favourites' => 1, 'types' => [ \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 1, \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 1, \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => 0 ]], 'deletedusers' => [] ], 'All conversation types, delete all messages from individual favourited, no messages remaining' => [ 'conversationConfigs' => $conversations, 'deletemessagesuser' => $user1, 'deletemessages' => [0, 1, 2], 'arguments' => [$user1], 'expectedcounts' => ['favourites' => 1, 'types' => [ \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 1, \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 1, \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => 0 ]], 'expectedunreadcounts' => ['favourites' => 0, 'types' => [ \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 1, \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 1, \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => 0 ]], 'deletedusers' => [] ], 'All conversation types, delete all messages from individual non-favourited, no messages remaining' => [ 'conversationConfigs' => $conversations, 'deletemessagesuser' => $user1, 'deletemessages' => [3, 4, 5], 'arguments' => [$user1], 'expectedcounts' => ['favourites' => 2, 'types' => [ \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 0, \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 1, \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => 0 ]], 'expectedunreadcounts' => ['favourites' => 1, 'types' => [ \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 0, \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 1, \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => 0 ]], 'deletedusers' => [] ], 'All conversation types, delete all messages from individual favourited, no messages remaining, different user' => [ 'conversationConfigs' => $conversations, 'deletemessagesuser' => $user1, 'deletemessages' => [0, 1, 2], 'arguments' => [$user2], 'expectedcounts' => ['favourites' => 1, 'types' => [ \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 1, \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 2, \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => 0 ]], 'expectedunreadcounts' => ['favourites' => 0, 'types' => [ \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 1, \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 2, \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => 0 ]], 'deletedusers' => [] ], 'All conversation types, delete all messages from individual non-favourited, no messages remaining, different user' => [ 'conversationConfigs' => $conversations, 'deletemessagesuser' => $user1, 'deletemessages' => [3, 4, 5], 'arguments' => [$user3], 'expectedcounts' => ['favourites' => 1, 'types' => [ \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 1, \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 2, \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => 0 ]], 'expectedunreadcounts' => ['favourites' => 0, 'types' => [ \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 1, \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 2, \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => 0 ]], 'deletedusers' => [] ], 'All conversation types, delete some messages from group non-favourited, messages remaining,' => [ 'conversationConfigs' => $conversations, 'deletemessagesuser' => $user1, 'deletemessages' => [6, 7], 'arguments' => [$user1], 'expectedcounts' => ['favourites' => 2, 'types' => [ \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 1, \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 1, \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => 0 ]], 'expectedunreadcounts' => ['favourites' => 1, 'types' => [ \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 1, \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 1, \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => 0 ]], 'deletedusers' => [] ], 'All conversation types, delete all messages from group non-favourited, no messages remaining,' => [ 'conversationConfigs' => $conversations, 'deletemessagesuser' => $user1, 'deletemessages' => [6, 7, 8, 9], 'arguments' => [$user1], 'expectedcounts' => ['favourites' => 2, 'types' => [ \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 1, \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 1, \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => 0 ]], 'expectedunreadcounts' => ['favourites' => 1, 'types' => [ \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 1, \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 0, \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => 0 ]], 'deletedusers' => [] ], 'All conversation types, another user soft deleted' => [ 'conversationConfigs' => $conversations, 'deletemessagesuser' => null, 'deletemessages' => [], 'arguments' => [$user1], 'expectedcounts' => ['favourites' => 2, 'types' => [ \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 1, \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 1, \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => 0 ]], 'expectedunreadcounts' => ['favourites' => 1, 'types' => [ \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 1, \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 1, \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => 0 ]], 'deletedusers' => [$user2] ], 'All conversation types, all group users soft deleted' => [ 'conversationConfigs' => $conversations, 'deletemessagesuser' => null, 'deletemessages' => [], 'arguments' => [$user1], 'expectedcounts' => ['favourites' => 2, 'types' => [ \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 1, \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 1, \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => 0 ]], 'expectedunreadcounts' => ['favourites' => 1, 'types' => [ \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 1, \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 1, \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => 0 ]], 'deletedusers' => [$user2, $user3, $user4] ], 'Group conversation which is disabled, favourited' => [ 'conversationConfigs' => $conversations, 'deletemessagesuser' => null, 'deletemessages' => [], 'arguments' => [$user6], 'expectedcounts' => ['favourites' => 1, 'types' => [ \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 0, \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 0, \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => 0 ]], 'expectedunreadcounts' => ['favourites' => 0, 'types' => [ \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 0, \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 0, \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => 0 ]], 'deletedusers' => [] ], 'Group conversation which is disabled, non-favourited' => [ 'conversationConfigs' => $conversations, 'deletemessagesuser' => null, 'deletemessages' => [], 'arguments' => [$user7], 'expectedcounts' => ['favourites' => 1, 'types' => [ \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 0, \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 0, \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => 0 ]], 'expectedunreadcounts' => ['favourites' => 0, 'types' => [ \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 0, \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 0, \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => 0 ]], 'deletedusers' => [] ], 'Conversation with self' => [ 'conversationConfigs' => $conversations, 'deletemessagesuser' => null, 'deletemessages' => [], 'arguments' => [$user8], 'expectedcounts' => ['favourites' => 0, 'types' => [ \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 0, \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 0, \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => 1 ]], 'expectedunreadcounts' => ['favourites' => 0, 'types' => [ \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => 0, \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP => 0, \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF => 0 ]], 'deletedusers' => [] ], ]; } /** * Test the get_conversation_counts() function. * * @dataProvider get_conversation_counts_test_cases() * @param array $conversationconfigs Conversations to create * @param int $deletemessagesuser The user who is deleting the messages * @param array $deletemessages The list of messages to delete (by index) * @param array $arguments Arguments for the count conversations function * @param array $expectedcounts the expected conversation counts * @param array $expectedunreadcounts the expected unread conversation counts * @param array $deletedusers the array of users to soft delete. */ public function test_get_conversation_counts( $conversationconfigs, $deletemessagesuser, $deletemessages, $arguments, $expectedcounts, $expectedunreadcounts, $deletedusers ) { $this->resetAfterTest(); $generator = $this->getDataGenerator(); $users = [ $generator->create_user(), $generator->create_user(), $generator->create_user(), $generator->create_user(), $generator->create_user(), $generator->create_user(), $generator->create_user(), $generator->create_user() ]; $deleteuser = !is_null($deletemessagesuser) ? $users[$deletemessagesuser] : null; $this->setUser($users[$arguments[0]]); $arguments[0] = $users[$arguments[0]]->id; $systemcontext = \context_system::instance(); $conversations = []; $messageids = []; foreach ($conversationconfigs as $config) { $conversation = \core_message\api::create_conversation( $config['type'], array_map(function($userindex) use ($users) { return $users[$userindex]->id; }, $config['users']), null, ($config['enabled'] ?? true) ); foreach ($config['messages'] as $userfromindex) { $userfrom = $users[$userfromindex]; $messageids[] = testhelper::send_fake_message_to_conversation($userfrom, $conversation->id); } // Remove the self conversations created by the generator, // so we can choose to set that ourself and honour the original intention of the test. $userids = array_map(function($userindex) use ($users) { return $users[$userindex]->id; }, $config['users']); foreach ($userids as $userid) { if ($conversation->type == \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF) { \core_message\api::unset_favourite_conversation($conversation->id, $userid); } } foreach ($config['favourites'] as $userfromindex) { $userfrom = $users[$userfromindex]; $usercontext = \context_user::instance($userfrom->id); $ufservice = \core_favourites\service_factory::get_service_for_user_context($usercontext); $ufservice->create_favourite('core_message', 'message_conversations', $conversation->id, $systemcontext); } $conversations[] = $conversation; } foreach ($deletemessages as $messageindex) { \core_message\api::delete_message($deleteuser->id, $messageids[$messageindex]); } foreach ($deletedusers as $deleteduser) { delete_user($users[$deleteduser]); } $counts = core_message_external::get_conversation_counts(...$arguments); $counts = external_api::clean_returnvalue(core_message_external::get_conversation_counts_returns(), $counts); $this->assertEquals($expectedcounts['favourites'], $counts['favourites']); $this->assertEquals($expectedcounts['types'][\core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL], $counts['types'][\core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL]); $this->assertEquals($expectedcounts['types'][\core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP], $counts['types'][\core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP]); $this->assertEquals($expectedcounts['types'][\core_message\api::MESSAGE_CONVERSATION_TYPE_SELF], $counts['types'][\core_message\api::MESSAGE_CONVERSATION_TYPE_SELF]); } /** * Test the get_unread_conversation_counts() function. * * @dataProvider get_conversation_counts_test_cases * @param array $conversationconfigs Conversations to create * @param int $deletemessagesuser The user who is deleting the messages * @param array $deletemessages The list of messages to delete (by index) * @param array $arguments Arguments for the count conversations function * @param array $expectedcounts the expected conversation counts * @param array $expectedunreadcounts the expected unread conversation counts * @param array $deletedusers the list of users to soft-delete. */ public function test_get_unread_conversation_counts( $conversationconfigs, $deletemessagesuser, $deletemessages, $arguments, $expectedcounts, $expectedunreadcounts, $deletedusers ) { $this->resetAfterTest(); $generator = $this->getDataGenerator(); $users = [ $generator->create_user(), $generator->create_user(), $generator->create_user(), $generator->create_user(), $generator->create_user(), $generator->create_user(), $generator->create_user(), $generator->create_user() ]; $deleteuser = !is_null($deletemessagesuser) ? $users[$deletemessagesuser] : null; $this->setUser($users[$arguments[0]]); $arguments[0] = $users[$arguments[0]]->id; $systemcontext = \context_system::instance(); $conversations = []; $messageids = []; foreach ($conversationconfigs as $config) { $conversation = \core_message\api::create_conversation( $config['type'], array_map(function($userindex) use ($users) { return $users[$userindex]->id; }, $config['users']), null, ($config['enabled'] ?? true) ); foreach ($config['messages'] as $userfromindex) { $userfrom = $users[$userfromindex]; $messageids[] = testhelper::send_fake_message_to_conversation($userfrom, $conversation->id); } foreach ($config['favourites'] as $userfromindex) { $userfrom = $users[$userfromindex]; $usercontext = \context_user::instance($userfrom->id); $ufservice = \core_favourites\service_factory::get_service_for_user_context($usercontext); $ufservice->create_favourite('core_message', 'message_conversations', $conversation->id, $systemcontext); } $conversations[] = $conversation; } foreach ($deletemessages as $messageindex) { \core_message\api::delete_message($deleteuser->id, $messageids[$messageindex]); } foreach ($deletedusers as $deleteduser) { delete_user($users[$deleteduser]); } $counts = core_message_external::get_unread_conversation_counts(...$arguments); $counts = external_api::clean_returnvalue(core_message_external::get_unread_conversation_counts_returns(), $counts); $this->assertEquals($expectedunreadcounts['favourites'], $counts['favourites']); $this->assertEquals($expectedunreadcounts['types'][\core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL], $counts['types'][\core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL]); $this->assertEquals($expectedunreadcounts['types'][\core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP], $counts['types'][\core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP]); $this->assertEquals($expectedunreadcounts['types'][\core_message\api::MESSAGE_CONVERSATION_TYPE_SELF], $counts['types'][\core_message\api::MESSAGE_CONVERSATION_TYPE_SELF]); } /** * Test delete_message for all users. */ public function test_delete_message_for_all_users() { global $DB; $this->resetAfterTest(true); // Create fake data to test it. list($user1, $user2, $user3, $convgroup, $convindividual) = $this->create_delete_message_test_data(); // Send message as user1 to group conversation. $messageid1 = testhelper::send_fake_message_to_conversation($user1, $convgroup->id); $messageid2 = testhelper::send_fake_message_to_conversation($user2, $convgroup->id); // User1 deletes the first message for all users of group conversation. // First, we have to allow user1 (Teacher) can delete messages for all users. $editingteacher = $DB->get_record('role', ['shortname' => 'editingteacher']); assign_capability('moodle/site:deleteanymessage', CAP_ALLOW, $editingteacher->id, \context_system::instance()); $this->setUser($user1); // Now, user1 deletes message for all users. $return = core_message_external::delete_message_for_all_users($messageid1, $user1->id); $return = external_api::clean_returnvalue(core_message_external::delete_message_for_all_users_returns(), $return); // Check if everything is ok. $this->assertEquals(array(), $return); // Check we have 3 records on message_user_actions with the mark MESSAGE_ACTION_DELETED. $muas = $DB->get_records('message_user_actions', array('messageid' => $messageid1), 'userid ASC'); $this->assertCount(3, $muas); $mua1 = array_shift($muas); $mua2 = array_shift($muas); $mua3 = array_shift($muas); $this->assertEquals($user1->id, $mua1->userid); $this->assertEquals($messageid1, $mua1->messageid); $this->assertEquals(\core_message\api::MESSAGE_ACTION_DELETED, $mua1->action); $this->assertEquals($user2->id, $mua2->userid); $this->assertEquals($messageid1, $mua2->messageid); $this->assertEquals(\core_message\api::MESSAGE_ACTION_DELETED, $mua2->action); $this->assertEquals($user3->id, $mua3->userid); $this->assertEquals($messageid1, $mua3->messageid); $this->assertEquals(\core_message\api::MESSAGE_ACTION_DELETED, $mua3->action); } /** * Test delete_message for all users with messaging disabled. */ public function test_delete_message_for_all_users_messaging_disabled() { global $CFG; $this->resetAfterTest(); // Create fake data to test it. list($user1, $user2, $user3, $convgroup, $convindividual) = $this->create_delete_message_test_data(); // Send message as user1 to group conversation. $messageid = testhelper::send_fake_message_to_conversation($user1, $convgroup->id); $this->setUser($user1); // Disable messaging. $CFG->messaging = 0; // Ensure an exception is thrown. $this->expectException('moodle_exception'); core_message_external::delete_message_for_all_users($messageid, $user1->id); } /** * Test delete_message for all users with no permission. */ public function test_delete_message_for_all_users_no_permission() { $this->resetAfterTest(); // Create fake data to test it. list($user1, $user2, $user3, $convgroup, $convindividual) = $this->create_delete_message_test_data(); // Send message as user1 to group conversation. $messageid = testhelper::send_fake_message_to_conversation($user1, $convgroup->id); $this->setUser($user2); // Try as user2 to delete a message for all users without permission to do it. $this->expectException('moodle_exception'); $this->expectExceptionMessage('You do not have permission to delete this message for everyone.'); core_message_external::delete_message_for_all_users($messageid, $user2->id); } /** * Test delete_message for all users in a private conversation. */ public function test_delete_message_for_all_users_private_conversation() { global $DB; $this->resetAfterTest(); // Create fake data to test it. list($user1, $user2, $user3, $convgroup, $convindividual) = $this->create_delete_message_test_data(); // Send message as user1 to private conversation. $messageid = testhelper::send_fake_message_to_conversation($user1, $convindividual->id); // First, we have to allow user1 (Teacher) can delete messages for all users. $editingteacher = $DB->get_record('role', ['shortname' => 'editingteacher']); assign_capability('moodle/site:deleteanymessage', CAP_ALLOW, $editingteacher->id, \context_system::instance()); $this->setUser($user1); // Try as user1 to delete a private message for all users on individual conversation. // User1 should not delete message for all users in a private conversations despite being a teacher. // Because is a teacher in a course and not in a system context. $this->expectException('moodle_exception'); $this->expectExceptionMessage('You do not have permission to delete this message for everyone.'); core_message_external::delete_message_for_all_users($messageid, $user1->id); } /** * Test retrieving conversation messages by providing a timefrom higher than last message timecreated. It should return no * messages but keep the return structure to not break when called from the ws. */ public function test_get_conversation_messages_timefrom_higher_than_last_timecreated() { $this->resetAfterTest(true); // Create some users. $user1 = self::getDataGenerator()->create_user(); $user2 = self::getDataGenerator()->create_user(); $user3 = self::getDataGenerator()->create_user(); $user4 = self::getDataGenerator()->create_user(); // Create group conversation. $conversation = \core_message\api::create_conversation( \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP, [$user1->id, $user2->id, $user3->id, $user4->id] ); // The person asking for the messages for another user. $this->setUser($user1); // Send some messages back and forth. $time = 1; testhelper::send_fake_message_to_conversation($user1, $conversation->id, 'Message 1', $time + 1); testhelper::send_fake_message_to_conversation($user2, $conversation->id, 'Message 2', $time + 2); testhelper::send_fake_message_to_conversation($user1, $conversation->id, 'Message 3', $time + 3); testhelper::send_fake_message_to_conversation($user3, $conversation->id, 'Message 4', $time + 4); // Retrieve the messages. $result = core_message_external::get_conversation_messages($user1->id, $conversation->id, 0, 0, '', $time + 5); // We need to execute the return values cleaning process to simulate the web service server. $result = external_api::clean_returnvalue(core_message_external::get_conversation_messages_returns(), $result); // Check the results are correct. $this->assertEquals($conversation->id, $result['id']); // Confirm the message data is correct. $messages = $result['messages']; $this->assertEquals(0, count($messages)); // Confirm that members key is present. $this->assertArrayHasKey('members', $result); } /** * Helper to seed the database with initial state with data. */ protected function create_delete_message_test_data() { // Create some users. $user1 = self::getDataGenerator()->create_user(); $user2 = self::getDataGenerator()->create_user(); $user3 = self::getDataGenerator()->create_user(); // Create a course and enrol the users. $course = $this->getDataGenerator()->create_course(); $coursecontext = \context_course::instance($course->id); $this->getDataGenerator()->enrol_user($user1->id, $course->id, 'editingteacher'); $this->getDataGenerator()->enrol_user($user2->id, $course->id, 'student'); $this->getDataGenerator()->enrol_user($user3->id, $course->id, 'student'); // Create a group and added the users into. $group1 = $this->getDataGenerator()->create_group(array('courseid' => $course->id)); groups_add_member($group1->id, $user1->id); groups_add_member($group1->id, $user2->id); groups_add_member($group1->id, $user3->id); // Create a group conversation linked with the course. $convgroup = \core_message\api::create_conversation( \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP, [$user1->id, $user2->id, $user3->id], 'Group test delete for everyone', \core_message\api::MESSAGE_CONVERSATION_ENABLED, 'core_group', 'groups', $group1->id, \context_course::instance($course->id)->id ); // Create and individual conversation. $convindividual = \core_message\api::create_conversation( \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL, [$user1->id, $user2->id] ); return [$user1, $user2, $user3, $convgroup, $convindividual]; } }