mirror of
https://github.com/moodle/moodle.git
synced 2025-04-21 00:12:56 +02:00
MDL-63724 core_message: add conversation support to message_send()
Added a test confirming that processors are called for each member and that a single event is fired.
This commit is contained in:
parent
9581bc3c00
commit
0eddf920be
@ -55,7 +55,7 @@ require_once(__DIR__ . '/../message/lib.php');
|
||||
* @return mixed the integer ID of the new message or false if there was a problem with submitted data
|
||||
*/
|
||||
function message_send(\core\message\message $eventdata) {
|
||||
global $CFG, $DB;
|
||||
global $CFG, $DB, $SITE;
|
||||
|
||||
//new message ID to return
|
||||
$messageid = false;
|
||||
@ -74,6 +74,89 @@ function message_send(\core\message\message $eventdata) {
|
||||
$eventdata->notification = 1;
|
||||
}
|
||||
|
||||
// This is a message directed to a conversation, not a specific user as was the way in legacy messaging .
|
||||
// We must call send_message_to_conversation(), which handles per-member processor iteration and triggers
|
||||
// a per-conversation event.
|
||||
if (!$eventdata->notification && $eventdata->convid) {
|
||||
if (!is_object($eventdata->userfrom)) {
|
||||
$eventdata->userfrom = core_user::get_user($eventdata->userfrom);
|
||||
}
|
||||
if (!$eventdata->userfrom) {
|
||||
debugging('Attempt to send msg from unknown user', DEBUG_NORMAL);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Only one message will be saved to the DB.
|
||||
$conversationid = $eventdata->convid;
|
||||
$table = 'messages';
|
||||
$tabledata = new stdClass();
|
||||
$tabledata->courseid = $eventdata->courseid;
|
||||
$tabledata->useridfrom = $eventdata->userfrom->id;
|
||||
$tabledata->conversationid = $conversationid;
|
||||
$tabledata->subject = $eventdata->subject;
|
||||
$tabledata->fullmessage = $eventdata->fullmessage;
|
||||
$tabledata->fullmessageformat = $eventdata->fullmessageformat;
|
||||
$tabledata->fullmessagehtml = $eventdata->fullmessagehtml;
|
||||
$tabledata->smallmessage = $eventdata->smallmessage;
|
||||
$tabledata->timecreated = time();
|
||||
|
||||
if (PHPUNIT_TEST and class_exists('phpunit_util')) {
|
||||
// Add some more tests to make sure the normal code can actually work.
|
||||
$componentdir = core_component::get_component_directory($eventdata->component);
|
||||
if (!$componentdir or !is_dir($componentdir)) {
|
||||
throw new coding_exception('Invalid component specified in message-send(): '.$eventdata->component);
|
||||
}
|
||||
if (!file_exists("$componentdir/db/messages.php")) {
|
||||
throw new coding_exception("$eventdata->component does not contain db/messages.php necessary for message_send()");
|
||||
}
|
||||
$messageproviders = null;
|
||||
include("$componentdir/db/messages.php");
|
||||
if (!isset($messageproviders[$eventdata->name])) {
|
||||
$errormsg = "Missing messaging defaults for event '$eventdata->name' in '$eventdata->component' messages.php file";
|
||||
throw new coding_exception($errormsg);
|
||||
}
|
||||
unset($componentdir);
|
||||
unset($messageproviders);
|
||||
// Now ask phpunit if it wants to catch this message.
|
||||
if (phpunit_util::is_redirecting_messages()) {
|
||||
$messageid = $DB->insert_record($table, $tabledata);
|
||||
$message = $DB->get_record($table, array('id' => $messageid));
|
||||
|
||||
// Mark the message as read for each of the other users.
|
||||
$sql = "SELECT u.*
|
||||
FROM {message_conversation_members} mcm
|
||||
JOIN {user} u
|
||||
ON (mcm.conversationid = :convid AND u.id = mcm.userid AND u.id != :userid)";
|
||||
$otherusers = $DB->get_records_sql($sql, ['convid' => $eventdata->convid, 'userid' => $eventdata->userfrom->id]);
|
||||
foreach ($otherusers as $othermember) {
|
||||
\core_message\api::mark_message_as_read($othermember->id, $message);
|
||||
}
|
||||
|
||||
// Unit tests need this detail.
|
||||
$message->notification = $eventdata->notification;
|
||||
phpunit_util::message_sent($message);
|
||||
return $messageid;
|
||||
}
|
||||
}
|
||||
|
||||
// Cache messages.
|
||||
if (!empty($eventdata->convid)) {
|
||||
// Cache the timecreated value of the last message in this conversation.
|
||||
$cache = cache::make('core', 'message_time_last_message_between_users');
|
||||
$key = \core_message\helper::get_last_message_time_created_cache_key($eventdata->convid);
|
||||
$cache->set($key, $tabledata->timecreated);
|
||||
}
|
||||
|
||||
// Store unread message just in case we get a fatal error any time later.
|
||||
$tabledata->id = $DB->insert_record($table, $tabledata);
|
||||
$eventdata->savedmessageid = $tabledata->id;
|
||||
|
||||
return \core\message\manager::send_message_to_conversation($eventdata, $tabledata);
|
||||
}
|
||||
|
||||
// Notifications and legacy messaging code:
|
||||
// Most of the next steps are shared by both the legacy message code (those being sent to a single 'userto', not to a
|
||||
// conversation), and for notifications. Any message-specific or notification-specific steps are clearly marked.
|
||||
if (!is_object($eventdata->userto)) {
|
||||
$eventdata->userto = core_user::get_user($eventdata->userto);
|
||||
}
|
||||
@ -290,14 +373,13 @@ function message_send(\core\message\message $eventdata) {
|
||||
}
|
||||
}
|
||||
|
||||
// Only cache messages, not notifications.
|
||||
if (!$eventdata->notification) {
|
||||
if (!empty($eventdata->convid)) {
|
||||
// Cache the timecreated value of the last message in this conversation.
|
||||
$cache = cache::make('core', 'message_time_last_message_between_users');
|
||||
$key = \core_message\helper::get_last_message_time_created_cache_key($eventdata->convid);
|
||||
$cache->set($key, $tabledata->timecreated);
|
||||
}
|
||||
// We have either created or derived a conversationid, which we can use to
|
||||
// update the 'message_time_last_message_between_users' cache.
|
||||
if (!empty($tabledata->conversationid)) {
|
||||
// Cache the timecreated value of the last message in this conversation.
|
||||
$cache = cache::make('core', 'message_time_last_message_between_users');
|
||||
$key = \core_message\helper::get_last_message_time_created_cache_key($tabledata->conversationid);
|
||||
$cache->set($key, $tabledata->timecreated);
|
||||
}
|
||||
|
||||
// Store unread message just in case we get a fatal error any time later.
|
||||
|
@ -732,6 +732,190 @@ class core_messagelib_testcase extends advanced_testcase {
|
||||
$sink->clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests calling message_send() with $eventdata representing a message to an individual conversation.
|
||||
*
|
||||
* This test will verify:
|
||||
* - that the 'messages' record is created.
|
||||
* - that the processors will be called for each conversation member, except the sender.
|
||||
* - the a single event will be generated - 'message_sent'
|
||||
*
|
||||
* Note: We won't redirect/capture messages in this test because doing so causes message_send() to return early, before
|
||||
* processors and events code is called. We need to test this code here, as we generally redirect messages elsewhere and we
|
||||
* need to be sure this is covered.
|
||||
*/
|
||||
public function test_message_send_to_conversation_individual() {
|
||||
global $DB;
|
||||
$this->preventResetByRollback();
|
||||
$this->resetAfterTest();
|
||||
|
||||
// Create some users and a conversation between them.
|
||||
$user1 = $this->getDataGenerator()->create_user(array('maildisplay' => 1));
|
||||
$user2 = $this->getDataGenerator()->create_user();
|
||||
set_config('allowedemaildomains', 'example.com');
|
||||
$conversation = \core_message\api::create_conversation(\core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
|
||||
[$user1->id, $user2->id], '1:1 project discussion');
|
||||
|
||||
// Generate the message.
|
||||
$message = new \core\message\message();
|
||||
$message->courseid = 1;
|
||||
$message->component = 'moodle';
|
||||
$message->name = 'instantmessage';
|
||||
$message->userfrom = $user1;
|
||||
$message->convid = $conversation->id;
|
||||
$message->subject = 'message subject 1';
|
||||
$message->fullmessage = 'message body';
|
||||
$message->fullmessageformat = FORMAT_MARKDOWN;
|
||||
$message->fullmessagehtml = '<p>message body</p>';
|
||||
$message->smallmessage = 'small message';
|
||||
$message->notification = '0';
|
||||
|
||||
// Content specific to the email processor.
|
||||
$content = array('*' => array('header' => ' test ', 'footer' => ' test '));
|
||||
$message->set_additional_content('email', $content);
|
||||
|
||||
// Ensure we're going to hit the email processor for this user.
|
||||
$DB->set_field_select('message_processors', 'enabled', 0, "name <> 'email'");
|
||||
set_user_preference('message_provider_moodle_instantmessage_loggedoff', 'email', $user2);
|
||||
|
||||
// Now, send a message and verify the message processors (in this case, email) are hit.
|
||||
$sink = $this->redirectEmails();
|
||||
$messageid = message_send($message);
|
||||
$emails = $sink->get_messages();
|
||||
$this->assertCount(1, $emails);
|
||||
$email = reset($emails);
|
||||
|
||||
// Verify the record was created in 'messages'.
|
||||
$recordexists = $DB->record_exists('messages', ['id' => $messageid]);
|
||||
$this->assertTrue($recordexists);
|
||||
|
||||
// Verify the email information.
|
||||
$this->assertSame($user1->email, $email->from);
|
||||
$this->assertSame($user2->email, $email->to);
|
||||
|
||||
// The message subject is generated during the call for conversation messages,
|
||||
// as the conversation may have many members having different lang preferences.
|
||||
$this->assertSame(get_string('unreadnewmessage', 'message', fullname($user1)), $email->subject);
|
||||
|
||||
// The email content will have had an emailtagline appended to it, based on lang prefs,
|
||||
// so verify the expected beginning and ends.
|
||||
$this->assertNotEmpty($email->header);
|
||||
$this->assertNotEmpty($email->body);
|
||||
$this->assertRegExp('/test message body.*test/s', $email->body);
|
||||
$sink->clear();
|
||||
|
||||
// Now, send the message again, and verify that the event fired includes the courseid and conversationid.
|
||||
$eventsink = $this->redirectEvents();
|
||||
$messageid = message_send($message);
|
||||
$events = $eventsink->get_events();
|
||||
$this->assertCount(1, $events);
|
||||
$event = reset($events);
|
||||
$this->assertInstanceOf(\core\event\message_sent::class, $event);
|
||||
$this->assertEquals($user1->id, $event->userid);
|
||||
$this->assertEquals($user2->id, $event->relateduserid);
|
||||
$this->assertEquals($message->courseid, $event->other['courseid']);
|
||||
|
||||
$eventsink->clear();
|
||||
$sink->clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests calling message_send() with $eventdata representing a message to an group conversation.
|
||||
*
|
||||
* This test will verify:
|
||||
* - that the 'messages' record is created.
|
||||
* - that the processors will be called for each conversation member, except the sender.
|
||||
* - the a single event will be generated - 'group_message_sent'
|
||||
*
|
||||
* Note: We won't redirect/capture messages in this test because doing so causes message_send() to return early, before
|
||||
* processors and events code is called. We need to test this code here, as we generally redirect messages elsewhere and we
|
||||
* need to be sure this is covered.
|
||||
*/
|
||||
public function test_message_send_to_conversation_group() {
|
||||
global $DB, $CFG, $SITE;
|
||||
$this->preventResetByRollback();
|
||||
$this->resetAfterTest();
|
||||
|
||||
// Create some users and a conversation between them.
|
||||
$user1 = $this->getDataGenerator()->create_user(array('maildisplay' => 1));
|
||||
$user2 = $this->getDataGenerator()->create_user();
|
||||
$user3 = $this->getDataGenerator()->create_user();
|
||||
set_config('allowedemaildomains', 'example.com');
|
||||
$conversation = \core_message\api::create_conversation(\core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP,
|
||||
[$user1->id, $user2->id, $user3->id], 'Group project discussion');
|
||||
|
||||
// Generate the message.
|
||||
$message = new \core\message\message();
|
||||
$message->courseid = 1;
|
||||
$message->component = 'moodle';
|
||||
$message->name = 'instantmessage';
|
||||
$message->userfrom = $user1;
|
||||
$message->convid = $conversation->id;
|
||||
$message->subject = 'message subject 1';
|
||||
$message->fullmessage = 'message body';
|
||||
$message->fullmessageformat = FORMAT_MARKDOWN;
|
||||
$message->fullmessagehtml = '<p>message body</p>';
|
||||
$message->smallmessage = 'small message';
|
||||
$message->notification = '0';
|
||||
|
||||
// Content specific to the email processor.
|
||||
$content = array('*' => array('header' => ' test ', 'footer' => ' test '));
|
||||
$message->set_additional_content('email', $content);
|
||||
|
||||
// Ensure we're going to hit the email processor for the recipient users.
|
||||
$DB->set_field_select('message_processors', 'enabled', 0, "name <> 'email'");
|
||||
set_user_preference('message_provider_moodle_instantmessage_loggedoff', 'email', $user2);
|
||||
set_user_preference('message_provider_moodle_instantmessage_loggedoff', 'email', $user3);
|
||||
|
||||
// Now, send a message and verify the message processors (in this case, email) are hit.
|
||||
$sink = $this->redirectEmails();
|
||||
$messageid = message_send($message);
|
||||
$emails = $sink->get_messages();
|
||||
$this->assertCount(2, $emails);
|
||||
|
||||
// Verify the record was created in 'messages'.
|
||||
$recordexists = $DB->record_exists('messages', ['id' => $messageid]);
|
||||
$this->assertTrue($recordexists);
|
||||
|
||||
// Verify the email information. Ordering is not guaranteed.
|
||||
$members = [$user2->email => '', $user3->email => ''];
|
||||
$email = $emails[0];
|
||||
$this->assertSame($user1->email, $email->from);
|
||||
$this->assertArrayHasKey($email->to, $members);
|
||||
unset($members[$email->to]);
|
||||
|
||||
$email = $emails[1];
|
||||
$this->assertSame($user1->email, $email->from);
|
||||
$this->assertArrayHasKey($email->to, $members);
|
||||
unset($members[$email->to]);
|
||||
|
||||
// The message subject is generated during the call for conversation messages,
|
||||
// as the conversation may have many members having different lang preferences.
|
||||
$tmp = (object) ['name' => fullname($user1), 'conversationname' => $conversation->name];
|
||||
$this->assertSame(get_string('unreadnewgroupconversationmessage', 'message', $tmp), $email->subject);
|
||||
|
||||
// The email content will have had an emailtagline appended to it, based on lang prefs,
|
||||
// so verify the expected beginning and ends.
|
||||
$this->assertNotEmpty($email->header);
|
||||
$this->assertNotEmpty($email->body);
|
||||
$this->assertRegExp('/test message body.*test/s', $email->body);
|
||||
$sink->clear();
|
||||
|
||||
// Now, send the message again, and verify that the event fired includes the courseid and conversationid.
|
||||
$eventsink = $this->redirectEvents();
|
||||
$messageid = message_send($message);
|
||||
$events = $eventsink->get_events();
|
||||
$this->assertCount(1, $events);
|
||||
$event = reset($events);
|
||||
$this->assertInstanceOf(\core\event\group_message_sent::class, $event);
|
||||
$this->assertEquals($user1->id, $event->userid);
|
||||
$this->assertNull($event->relateduserid);
|
||||
$this->assertEquals($message->courseid, $event->other['courseid']);
|
||||
$this->assertEquals($message->convid, $event->other['conversationid']);
|
||||
$eventsink->clear();
|
||||
$sink->clear();
|
||||
}
|
||||
|
||||
public function test_rollback() {
|
||||
global $DB;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user