'capabilities'=> '',
),
- 'description' => 'Retrieve a list of messages send or received by a user (conversations, notifications or both)',
+ 'core_message_get_messages' => array(
+ 'classname' => 'core_message_external',
+ 'methodname' => 'get_messages',
+ 'classpath' => 'message/externallib.php',
++ 'description' => 'Retrieve a list of messages sent and received by a user (conversations, notifications or both)',
+ 'type' => 'read',
+ 'capabilities' => '',
+ ),
+
// === notes related functions ===
'moodle_notes_create_notes' => array(
'List of contacts'
);
}
- 'useridfrom' => new external_value(PARAM_INT,
- 'the user id who send the message, 0 for any user. -10 or -20 for no-reply or support user',
- VALUE_DEFAULT, 0),
- 'type' => new external_value(PARAM_ALPHA,
- 'type of message to return, expected values are: notifications, conversations and both',
- VALUE_DEFAULT, 'both'),
+
+ /**
+ * Get messages parameters description.
+ *
+ * @return external_function_parameters
+ * @since 2.8
+ */
+ public static function get_messages_parameters() {
+ return new external_function_parameters(
+ array(
+ 'useridto' => new external_value(PARAM_INT, 'the user id who received the message, 0 for any user', VALUE_REQUIRED),
- 'newestfirst' => new external_value(PARAM_BOOL,
- 'true for ordering by newest first, false for oldest first', VALUE_DEFAULT, true),
++ 'useridfrom' => new external_value(
++ PARAM_INT, 'the user id who send the message, 0 for any user. -10 or -20 for no-reply or support user',
++ VALUE_DEFAULT, 0),
++ 'type' => new external_value(
++ PARAM_ALPHA, 'type of message to return, expected values are: notifications, conversations and both',
++ VALUE_DEFAULT, 'both'),
+ 'read' => new external_value(PARAM_BOOL, 'true for getting read messages, false for unread', VALUE_DEFAULT, true),
- 'limitnum' => new external_value(PARAM_INT, 'limit number', VALUE_DEFAULT, 0) )
++ 'newestfirst' => new external_value(
++ PARAM_BOOL, 'true for ordering by newest first, false for oldest first',
++ VALUE_DEFAULT, true),
+ 'limitfrom' => new external_value(PARAM_INT, 'limit from', VALUE_DEFAULT, 0),
- * @since 2.8
++ 'limitnum' => new external_value(PARAM_INT, 'limit number', VALUE_DEFAULT, 0)
++ )
+ );
+ }
+
+ /**
+ * Get messages function implementation.
++ *
++ * @since 2.8
++ * @throws invalid_parameter_exception
++ * @throws moodle_exception
+ * @param int $useridto the user id who received the message
+ * @param int $useridfrom the user id who send the message. -10 or -20 for no-reply or support user
+ * @param string $type type of message to return, expected values: notifications, conversations and both
+ * @param bool $read true for retreiving read messages, false for unread
+ * @param bool $newestfirst true for ordering by newest first, false for oldest first
+ * @param int $limitfrom limit from
+ * @param int $limitnum limit num
+ * @return external_description
- global $CFG, $DB, $USER;
+ */
+ public static function get_messages($useridto, $useridfrom = 0, $type = 'both', $read = true,
+ $newestfirst = true, $limitfrom = 0, $limitnum = 0) {
- // Which type of messages retrieve.
++ global $CFG, $USER;
+ require_once($CFG->dirroot . "/message/lib.php");
+
+ $warnings = array();
+
+ $params = array(
+ 'useridto' => $useridto,
+ 'useridfrom' => $useridfrom,
+ 'type' => $type,
+ 'read' => $read,
+ 'newestfirst' => $newestfirst,
+ 'limitfrom' => $limitfrom,
+ 'limitnum' => $limitnum
+ );
+
+ $params = self::validate_parameters(self::get_messages_parameters(), $params);
+
+ $context = context_system::instance();
+ self::validate_context($context);
+
+ $useridto = $params['useridto'];
+ $useridfrom = $params['useridfrom'];
+ $type = $params['type'];
+ $read = $params['read'];
+ $newestfirst = $params['newestfirst'];
+ $limitfrom = $params['limitfrom'];
+ $limitnum = $params['limitnum'];
+
+ $allowedvalues = array('notifications', 'conversations', 'both');
+ if (!in_array($type, $allowedvalues)) {
+ throw new invalid_parameter_exception('Invalid value for type parameter (value: ' . $type . '),' .
+ 'allowed values are: ' . implode(',', $allowedvalues));
+ }
+
+ // Check if private messaging between users is allowed.
+ if (empty($CFG->messaging)) {
+ // If we are retreiving only conversations, and messaging is disabled, throw an exception.
+ if ($type == "conversations") {
+ throw new moodle_exception('disabled', 'message');
+ }
+ if ($type == "both") {
+ $warning = array();
+ $warning['item'] = 'message';
+ $warning['itemid'] = $USER->id;
+ $warning['warningcode'] = '1';
+ $warning['message'] = 'Private messages (conversations) are not enabled in this site.
+ Only notifications will be returned';
+ $warnings[] = $warning;
+ }
+ }
+
+ if (!empty($useridto)) {
+ if (core_user::is_real_user($useridto)) {
+ $userto = core_user::get_user($useridto, '*', MUST_EXIST);
+ } else {
+ throw new moodle_exception('invaliduser');
+ }
+ }
+
+ if (!empty($useridfrom)) {
+ // We use get_user here because the from user can be the noreply or support user.
+ $userfrom = core_user::get_user($useridfrom, '*', MUST_EXIST);
+ }
+
+ // Check if the current user is the sender/receiver or just a privileged user.
+ if ($useridto != $USER->id and $useridfrom != $USER->id and
+ !has_capability('moodle/site:readallmessages', $context)) {
+ throw new moodle_exception('accessdenied', 'admin');
+ }
+
- $user = new stdclass();
++ // Which type of messages to retrieve.
+ $notifications = -1;
+ if ($type != 'both') {
+ $notifications = ($type == 'notifications') ? 1 : 0;
+ }
+
+ $orderdirection = $newestfirst ? 'DESC' : 'ASC';
+ $sort = "mr.timecreated $orderdirection";
+
+ if ($messages = message_get_messages($useridto, $useridfrom, $notifications, $read, $sort, $limitfrom, $limitnum)) {
+ $canviewfullname = has_capability('moodle/site:viewfullnames', $context);
+
+ // In some cases, we don't need to get the to/from user objects from the sql query.
+ $userfromfullname = '';
+ $usertofullname = '';
+
+ // In this case, the useridto field is not empty, so we can get the user destinatary fullname from there.
+ if (!empty($useridto)) {
+ $usertofullname = fullname($userto, $canviewfullname);
+ // The user from may or may not be filled.
+ if (!empty($useridfrom)) {
+ $userfromfullname = fullname($userfrom, $canviewfullname);
+ }
+ } else {
+ // If the useridto field is empty, the useridfrom must be filled.
+ $userfromfullname = fullname($userfrom, $canviewfullname);
+ }
+ foreach ($messages as $mid => $message) {
+
+ // We need to get the user from the query.
+ if (empty($userfromfullname)) {
+ // Check for non-reply and support users.
+ if (core_user::is_real_user($message->useridfrom)) {
- $user = new stdclass();
++ $user = new stdClass();
+ $user = username_load_fields_from_object($user, $message, 'userfrom');
+ $message->userfromfullname = fullname($user, $canviewfullname);
+ } else {
+ $user = core_user::get_user($message->useridfrom);
+ $message->userfromfullname = fullname($user, $canviewfullname);
+ }
+ } else {
+ $message->userfromfullname = $userfromfullname;
+ }
+
+ // We need to get the user from the query.
+ if (empty($usertofullname)) {
++ $user = new stdClass();
+ $user = username_load_fields_from_object($user, $message, 'userto');
+ $message->usertofullname = fullname($user, $canviewfullname);
+ } else {
+ $message->usertofullname = $usertofullname;
+ }
+
+ // This field is only available in the message_read table.
+ if (!isset($message->timeread)) {
+ $message->timeread = 0;
+ }
+
+ $message->text = message_format_message_text($message);
+ $messages[$mid] = (array) $message;
+ }
+ }
+
+ $results = array(
+ 'messages' => $messages,
+ 'warnings' => $warnings
+ );
+
+ return $results;
+ }
+
+ /**
+ * Get messages return description.
+ *
+ * @return external_single_structure
+ * @since 2.8
+ */
+ public static function get_messages_returns() {
+ return new external_single_structure(
+ array(
+ 'messages' => new external_multiple_structure(
+ new external_single_structure(
+ array(
+ 'id' => new external_value(PARAM_INT, 'Message id'),
+ 'useridfrom' => new external_value(PARAM_INT, 'User from id'),
+ 'useridto' => new external_value(PARAM_INT, 'User to id'),
+ 'subject' => new external_value(PARAM_TEXT, 'The message subject'),
+ 'text' => new external_value(PARAM_RAW, 'The message text formated'),
+ 'fullmessage' => new external_value(PARAM_RAW, 'The message'),
+ 'fullmessageformat' => new external_format_value('fullmessage'),
+ 'fullmessagehtml' => new external_value(PARAM_RAW, 'The message in html'),
+ 'smallmessage' => new external_value(PARAM_RAW, 'The shorten message'),
+ 'notification' => new external_value(PARAM_INT, 'Is a notification?'),
+ 'contexturl' => new external_value(PARAM_RAW, 'Context URL'),
+ 'contexturlname' => new external_value(PARAM_TEXT, 'Context URL link name'),
+ 'timecreated' => new external_value(PARAM_INT, 'Time created'),
+ 'timeread' => new external_value(PARAM_INT, 'Time read'),
+ 'usertofullname' => new external_value(PARAM_TEXT, 'User to full name'),
+ 'userfromfullname' => new external_value(PARAM_TEXT, 'User from full name')
+ ), 'message'
+ )
+ ),
+ 'warnings' => new external_warnings()
+ )
+ );
+ }
+
}
/**
}
return true;
}
- * Get messages send or/and received by the specified users.
+
+ /**
- * @param int $useridfrom the user id who send the message. -10 or -20 for no-reply or support user
++ * Get messages sent or/and received by the specified users.
+ *
+ * @param int $useridto the user id who received the message
- * @param bool $read true for retreiving read messages, false for unread
++ * @param int $useridfrom the user id who sent the message. -10 or -20 for no-reply or support user
+ * @param int $notifications 1 for retrieving notifications, 0 for messages, -1 for both
- $usersql = "";
- $joinsql = "";
++ * @param bool $read true for retrieving read messages, false for unread
+ * @param string $sort the column name to order by including optionally direction
+ * @param int $limitfrom limit from
+ * @param int $limitnum limit num
+ * @return external_description
+ * @since 2.8
+ */
+ function message_get_messages($useridto, $useridfrom = 0, $notifications = -1, $read = true,
+ $sort = 'mr.timecreated DESC', $limitfrom = 0, $limitnum = 0) {
+ global $DB;
+
+ $messagetable = $read ? '{message_read}' : '{message}';
- $joinsql
- WHERE $usersql
- $typesql
+ $params = array('deleted' => 0);
+
+ // Empty useridto means that we are going to retrieve messages send by the useridfrom to any user.
+ if (empty($useridto)) {
+ $userfields = get_all_user_name_fields(true, 'u', '', 'userto');
+ $joinsql = "JOIN {user} u ON u.id = mr.useridto";
+ $usersql = "mr.useridfrom = :useridfrom AND u.deleted = :deleted";
+ $params['useridfrom'] = $useridfrom;
+ } else {
+ $userfields = get_all_user_name_fields(true, 'u', '', 'userfrom');
+ // Left join because useridfrom may be -10 or -20 (no-reply and support users).
+ $joinsql = "LEFT JOIN {user} u ON u.id = mr.useridfrom";
+ $usersql = "mr.useridto = :useridto AND (u.deleted IS NULL OR u.deleted = :deleted)";
+ $params['useridto'] = $useridto;
+ if (!empty($useridfrom)) {
+ $usersql .= " AND mr.useridfrom = :useridfrom";
+ $params['useridfrom'] = $useridfrom;
+ }
+ }
+
+ // Now, if retrieve notifications, conversations or both.
+ $typesql = "";
+ if ($notifications !== -1) {
+ $typesql = "AND mr.notification = :notification";
+ $params['notification'] = ($notifications) ? 1 : 0;
+ }
+
+ $sql = "SELECT mr.*, $userfields
+ FROM $messagetable mr
++ $joinsql
++ WHERE $usersql
++ $typesql
+ ORDER BY $sort";
+
+ $messages = $DB->get_records_sql($sql, $params, $limitfrom, $limitnum);
+ return $messages;
+ }
$this->setExpectedException('moodle_exception');
$results = core_message_external::search_contacts('');
}
- // This ones ommits notification = 1.
+
+ /**
+ * Test get_messages.
+ */
+ public function test_get_messages() {
+ global $CFG;
+ $this->resetAfterTest(true);
+
+ $this->preventResetByRollback();
+ // This mark the messages as read!.
+ $sink = $this->redirectMessages();
+
+ $user1 = self::getDataGenerator()->create_user();
+ $user2 = self::getDataGenerator()->create_user();
+ $user3 = self::getDataGenerator()->create_user();
+
+ $course = self::getDataGenerator()->create_course();
+
+ // Send a message from one user to another.
+ message_post_message($user1, $user2, 'some random text 1', FORMAT_MOODLE);
+ message_post_message($user1, $user3, 'some random text 2', FORMAT_MOODLE);
+ message_post_message($user2, $user3, 'some random text 3', FORMAT_MOODLE);
+ message_post_message($user3, $user2, 'some random text 4', FORMAT_MOODLE);
+ message_post_message($user3, $user1, 'some random text 5', FORMAT_MOODLE);
+
+ $this->setUser($user1);
+ // Get read conversations from user1 to user2.
+ $messages = core_message_external::get_messages($user2->id, $user1->id, 'conversations', true, true, 0, 0);
+ $messages = external_api::clean_returnvalue(core_message_external::get_messages_returns(), $messages);
+ $this->assertCount(1, $messages['messages']);
+
+ // Get unread conversations from user1 to user2.
+ $messages = core_message_external::get_messages($user2->id, $user1->id, 'conversations', false, true, 0, 0);
+ $messages = external_api::clean_returnvalue(core_message_external::get_messages_returns(), $messages);
+ $this->assertCount(0, $messages['messages']);
+
+ // Get read messages send from user1.
+ $messages = core_message_external::get_messages(0, $user1->id, 'conversations', true, true, 0, 0);
+ $messages = external_api::clean_returnvalue(core_message_external::get_messages_returns(), $messages);
+ $this->assertCount(2, $messages['messages']);
+
+ $this->setUser($user2);
+ // Get read conversations from any user to user2.
+ $messages = core_message_external::get_messages($user2->id, 0, 'conversations', true, true, 0, 0);
+ $messages = external_api::clean_returnvalue(core_message_external::get_messages_returns(), $messages);
+ $this->assertCount(2, $messages['messages']);
+
+ $this->setUser($user3);
+ // Get read notifications received by user3.
+ $messages = core_message_external::get_messages($user3->id, 0, 'notifications', true, true, 0, 0);
+ $messages = external_api::clean_returnvalue(core_message_external::get_messages_returns(), $messages);
+ $this->assertCount(0, $messages['messages']);
+
+ // Now, create some notifications...
+ // We are creating fake notifications but based on real ones.
+
++ // This one omits notification = 1.
+ $eventdata = new stdClass();
+ $eventdata->modulename = 'moodle';
+ $eventdata->component = 'enrol_paypal';
+ $eventdata->name = 'paypal_enrolment';
+ $eventdata->userfrom = get_admin();
+ $eventdata->userto = $user1;
+ $eventdata->subject = "Moodle: PayPal payment";
+ $eventdata->fullmessage = "Your PayPal payment is pending.";
+ $eventdata->fullmessageformat = FORMAT_PLAIN;
+ $eventdata->fullmessagehtml = '';
+ $eventdata->smallmessage = '';
+ message_send($eventdata);
+
+ $message = new stdClass();
+ $message->notification = 1;
+ $message->component = 'enrol_manual';
+ $message->name = 'expiry_notification';
+ $message->userfrom = $user2;
+ $message->userto = $user1;
+ $message->subject = 'Enrolment expired';
+ $message->fullmessage = 'Enrolment expired blah blah blah';
+ $message->fullmessageformat = FORMAT_MARKDOWN;
+ $message->fullmessagehtml = markdown_to_html($message->fullmessage);
+ $message->smallmessage = $message->subject;
+ $message->contexturlname = $course->fullname;
+ $message->contexturl = (string)new moodle_url('/course/view.php', array('id' => $course->id));
+ message_send($message);
+
+ $userfrom = core_user::get_noreply_user();
+ $userfrom->maildisplay = true;
+ $eventdata = new stdClass();
+ $eventdata->component = 'moodle';
+ $eventdata->name = 'badgecreatornotice';
+ $eventdata->userfrom = $userfrom;
+ $eventdata->userto = $user1;
+ $eventdata->notification = 1;
+ $eventdata->subject = 'New badge';
+ $eventdata->fullmessage = format_text_email($eventdata->subject, FORMAT_HTML);
+ $eventdata->fullmessageformat = FORMAT_PLAIN;
+ $eventdata->fullmessagehtml = $eventdata->subject;
+ $eventdata->smallmessage = $eventdata->subject;
+ message_send($eventdata);
+
+ $eventdata = new stdClass();
+ $eventdata->name = 'submission';
+ $eventdata->component = 'mod_feedback';
+ $eventdata->userfrom = $user1;
+ $eventdata->userto = $user2;
+ $eventdata->subject = 'Feedback submitted';
+ $eventdata->fullmessage = 'Feedback submitted from an user';
+ $eventdata->fullmessageformat = FORMAT_PLAIN;
+ $eventdata->fullmessagehtml = '<strong>Feedback submitted</strong>';
+ $eventdata->smallmessage = '';
+ message_send($eventdata);
+
+ $this->setUser($user1);
+ // Get read notifications from any user to user1.
+ $messages = core_message_external::get_messages($user1->id, 0, 'notifications', true, true, 0, 0);
+ $messages = external_api::clean_returnvalue(core_message_external::get_messages_returns(), $messages);
+ $this->assertCount(3, $messages['messages']);
+
+ // Get one read notifications from any user to user1.
+ $messages = core_message_external::get_messages($user1->id, 0, 'notifications', true, true, 0, 1);
+ $messages = external_api::clean_returnvalue(core_message_external::get_messages_returns(), $messages);
+ $this->assertCount(1, $messages['messages']);
+
+ // Get unread notifications from any user to user1.
+ $messages = core_message_external::get_messages($user1->id, 0, 'notifications', false, true, 0, 0);
+ $messages = external_api::clean_returnvalue(core_message_external::get_messages_returns(), $messages);
+ $this->assertCount(0, $messages['messages']);
+
+ // Get read both type of messages from any user to user1.
+ $messages = core_message_external::get_messages($user1->id, 0, 'both', true, true, 0, 0);
+ $messages = external_api::clean_returnvalue(core_message_external::get_messages_returns(), $messages);
+ $this->assertCount(4, $messages['messages']);
+
+ // Get read notifications from no-reply-user to user1.
+ $messages = core_message_external::get_messages($user1->id, $userfrom->id, 'notifications', true, true, 0, 0);
+ $messages = external_api::clean_returnvalue(core_message_external::get_messages_returns(), $messages);
+ $this->assertCount(1, $messages['messages']);
+
+ // Get notifications send by user1 to any user.
+ $messages = core_message_external::get_messages(0, $user1->id, 'notifications', true, true, 0, 0);
+ $messages = external_api::clean_returnvalue(core_message_external::get_messages_returns(), $messages);
+ $this->assertCount(1, $messages['messages']);
+
+ // Test warnings.
+ $CFG->messaging = 0;
+
+ $messages = core_message_external::get_messages(0, $user1->id, 'both', true, true, 0, 0);
+ $messages = external_api::clean_returnvalue(core_message_external::get_messages_returns(), $messages);
+ $this->assertCount(1, $messages['warnings']);
+
+ // Test exceptions.
+
+ // Messaging disabled.
+ try {
+ $messages = core_message_external::get_messages(0, $user1->id, 'conversations', true, true, 0, 0);
+ $this->fail('Exception expected due messaging disabled.');
+ } catch (moodle_exception $e) {
+ $this->assertEquals('disabled', $e->errorcode);
+ }
+
+ $CFG->messaging = 1;
+
+ // Invalid users.
+ try {
+ $messages = core_message_external::get_messages(0, 0, 'conversations', true, true, 0, 0);
+ $this->fail('Exception expected due invalid users.');
+ } catch (moodle_exception $e) {
+ $this->assertEquals('accessdenied', $e->errorcode);
+ }
+
+ // Invalid user ids.
+ try {
+ $messages = core_message_external::get_messages(2500, 0, 'conversations', true, true, 0, 0);
+ $this->fail('Exception expected due invalid users.');
+ } catch (moodle_exception $e) {
+ $this->assertEquals('invaliduser', $e->errorcode);
+ }
+
+ // Invalid users (permissions).
+ $this->setUser($user2);
+ try {
+ $messages = core_message_external::get_messages(0, $user1->id, 'conversations', true, true, 0, 0);
+ $this->fail('Exception expected due invalid user.');
+ } catch (moodle_exception $e) {
+ $this->assertEquals('accessdenied', $e->errorcode);
+ }
+
+ }
}