From aff9da176a50e00104ba3aca92ddf5b884c132b0 Mon Sep 17 00:00:00 2001 From: Juan Leyva Date: Mon, 5 May 2014 14:34:17 +0200 Subject: [PATCH 1/4] MDL-45374 messages: get_messages ws definition --- lib/db/services.php | 12 +- message/externallib.php | 240 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 251 insertions(+), 1 deletion(-) diff --git a/lib/db/services.php b/lib/db/services.php index 6b7130b1f22..941a2a7a2bb 100644 --- a/lib/db/services.php +++ b/lib/db/services.php @@ -761,6 +761,15 @@ $functions = array( 'capabilities'=> '', ), + 'core_message_get_messages' => array( + 'classname' => 'core_message_external', + 'methodname' => 'get_messages', + 'classpath' => 'message/externallib.php', + 'description' => 'Retrieve a message list (conversations, notifications or boths)', + 'type' => 'read', + 'capabilities'=> '', + ), + // === notes related functions === 'moodle_notes_create_notes' => array( @@ -958,7 +967,8 @@ $services = array( 'mod_forum_get_forums_by_courses', 'mod_forum_get_forum_discussions', 'mod_forum_get_forum_discussion_posts', - 'core_files_get_files'), + 'core_files_get_files', + 'core_message_get_messages'), 'enabled' => 0, 'restrictedusers' => 0, 'shortname' => MOODLE_OFFICIAL_MOBILE_SERVICE, diff --git a/message/externallib.php b/message/externallib.php index 1ea4932bc46..6406ba84811 100644 --- a/message/externallib.php +++ b/message/externallib.php @@ -568,6 +568,246 @@ class core_message_external extends external_api { 'List of contacts' ); } + + /** + * Get messages parameters description. + * + * @return external_function_parameters + * @since 2.7 + */ + 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'), + 'type' => new external_value(PARAM_ALPHA, + 'type of message to return, expected values are: notifications, conversations, both'), + 'read' => new external_value(PARAM_BOOL, 'true for retreiving read messages, false for unread'), + 'useridfrom' => new external_value(PARAM_INT, 'user id who send the message, 0 for any user', VALUE_DEFAULT, 0), + 'newestfirst' => new external_value(PARAM_BOOL, + 'true order by newest first, false for oldest first', VALUE_DEFAULT, true), + 'limitfrom' => new external_value(PARAM_INT, 'limit from', VALUE_DEFAULT, 0), + 'limitnum' => new external_value(PARAM_INT, 'limit number', VALUE_DEFAULT, 0) ) + ); + } + + /** + * Get messages function implementation. + * @param int $useridto the user id who received the messages + * @param string $type type of message tu return, notifications, conversations or both + * @param bool $read true for retreiving read messages, false for unread + * @param int $useridfrom user id from for filtering + * @param bool $newestfirst true order by newest first, false for oldest first + * @param int $limitfrom limit from + * @param int $limitnum limit num + * @return external_description + * @since 2.7 + */ + public static function get_messages($useridto, $type, $read, $useridfrom = 0, + $newestfirst = true, $limitfrom = 0, $limitnum = 0) { + global $CFG, $DB, $USER; + require_once($CFG->dirroot . "/message/lib.php"); + + $warnings = array(); + + $params = array( + 'useridto' => $useridto, + 'type' => $type, + 'read' => $read, + 'useridfrom' => $useridfrom, + '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']; + $type = $params['type']; + $read = $params['read']; + $useridfrom = $params['useridfrom']; + $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. + // DOUBT -- check user is admin? + 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)) { + $userto = $DB->get_record('user', array('id' => $useridto), '*', MUST_EXIST); + } + + 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'); + } + + if ($useridto == $useridfrom) { + throw new moodle_exception('invaliduserid'); + } + + // Get messages. + $messagetable = $read ? '{message_read}' : '{message}'; + $usersql = ""; + $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', 'userto'); + $joinuserfield = "mr.useridto"; + $usersql = "mr.useridfrom = :useridfrom"; + $params['useridfrom'] = $useridfrom; + } else { + $userfields = get_all_user_name_fields(true, 'u', 'userfrom', 'userfrom'); + $joinuserfield = "mr.useridfrom"; + $usersql = "mr.useridto = :useridto"; + $params['useridto'] = $useridto; + if (!empty($useridfrom)) { + $usersql .= " AND mr.useridfrom = :useridfrom"; + $params['useridfrom'] = $useridfrom; + } + } + + // Now, if retrieve notifications, conversations or both. + $typesql = ""; + if ($type != 'both') { + $typesql = "AND mr.notification = :notification"; + $params['notification'] = ($type == 'notification') ? 1 : 0; + } + + // Finally the sort direction. + $orderdirection = $newestfirst ? 'DESC' : 'ASC'; + + $sql = "SELECT mr.*, $userfields + FROM $messagetable mr + JOIN {user} u ON u.id = $joinuserfield + WHERE $usersql AND u.deleted = :deleted + $typesql + ORDER BY mr.timecreated $orderdirection"; + + if ($messages = $DB->get_records_sql($sql, $params, $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)) { + $user = new stdclass(); + $user = username_load_fields_from_object($user, $message, 'userfrom'); + $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; + } + + if (!isset($message->timeread)) { + $message->timeread = 0; + } + + // DOUBT, getting crazy with formats... + $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_description + * @since 2.7 + */ + 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, 'mMssage 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_value(PARAM_INT, 'The message message format'), + '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() + ) + ); + } + } /** From 6ff4464b430cdc7ecd3851774fb928f71ddc1c60 Mon Sep 17 00:00:00 2001 From: Juan Leyva Date: Tue, 6 May 2014 18:01:41 +0200 Subject: [PATCH 2/4] MDL-45374 messages: get_messages ws unit tests --- lib/db/services.php | 12 +- message/externallib.php | 73 ++++++----- message/tests/externallib_test.php | 197 +++++++++++++++++++++++++++++ 3 files changed, 244 insertions(+), 38 deletions(-) diff --git a/lib/db/services.php b/lib/db/services.php index 941a2a7a2bb..6db105f4cdf 100644 --- a/lib/db/services.php +++ b/lib/db/services.php @@ -762,12 +762,12 @@ $functions = array( ), 'core_message_get_messages' => array( - 'classname' => 'core_message_external', - 'methodname' => 'get_messages', - 'classpath' => 'message/externallib.php', - 'description' => 'Retrieve a message list (conversations, notifications or boths)', - 'type' => 'read', - 'capabilities'=> '', + 'classname' => 'core_message_external', + 'methodname' => 'get_messages', + 'classpath' => 'message/externallib.php', + 'description' => 'Retrieve a list of messages send or received by a user (conversations, notifications or both)', + 'type' => 'read', + 'capabilities' => '', ), // === notes related functions === diff --git a/message/externallib.php b/message/externallib.php index 6406ba84811..488dcc8c882 100644 --- a/message/externallib.php +++ b/message/externallib.php @@ -578,13 +578,16 @@ class core_message_external extends external_api { 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'), + 'useridto' => new external_value(PARAM_INT, 'the user id who received the message, 0 for any user', VALUE_REQUIRED), + '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, both'), - 'read' => new external_value(PARAM_BOOL, 'true for retreiving read messages, false for unread'), - 'useridfrom' => new external_value(PARAM_INT, 'user id who send the message, 0 for any user', VALUE_DEFAULT, 0), + '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), 'newestfirst' => new external_value(PARAM_BOOL, - 'true order by newest first, false for oldest first', VALUE_DEFAULT, true), + 'true for ordering by newest first, false for oldest first', VALUE_DEFAULT, true), 'limitfrom' => new external_value(PARAM_INT, 'limit from', VALUE_DEFAULT, 0), 'limitnum' => new external_value(PARAM_INT, 'limit number', VALUE_DEFAULT, 0) ) ); @@ -592,17 +595,17 @@ class core_message_external extends external_api { /** * Get messages function implementation. - * @param int $useridto the user id who received the messages - * @param string $type type of message tu return, notifications, conversations or both + * @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 tu return, expected values: notifications, conversations and both * @param bool $read true for retreiving read messages, false for unread - * @param int $useridfrom user id from for filtering - * @param bool $newestfirst true order by newest first, false for oldest first + * @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 * @since 2.7 */ - public static function get_messages($useridto, $type, $read, $useridfrom = 0, + public static function get_messages($useridto, $useridfrom = 0, $type = 'both' , $read = true, $newestfirst = true, $limitfrom = 0, $limitnum = 0) { global $CFG, $DB, $USER; require_once($CFG->dirroot . "/message/lib.php"); @@ -611,9 +614,9 @@ class core_message_external extends external_api { $params = array( 'useridto' => $useridto, + 'useridfrom' => $useridfrom, 'type' => $type, 'read' => $read, - 'useridfrom' => $useridfrom, 'newestfirst' => $newestfirst, 'limitfrom' => $limitfrom, 'limitnum' => $limitnum @@ -625,9 +628,9 @@ class core_message_external extends external_api { self::validate_context($context); $useridto = $params['useridto']; + $useridfrom = $params['useridfrom']; $type = $params['type']; $read = $params['read']; - $useridfrom = $params['useridfrom']; $newestfirst = $params['newestfirst']; $limitfrom = $params['limitfrom']; $limitnum = $params['limitnum']; @@ -641,7 +644,6 @@ class core_message_external extends external_api { // 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. - // DOUBT -- check user is admin? if ($type == "conversations") { throw new moodle_exception('disabled', 'message'); } @@ -657,7 +659,11 @@ class core_message_external extends external_api { } if (!empty($useridto)) { - $userto = $DB->get_record('user', array('id' => $useridto), '*', MUST_EXIST); + if (core_user::is_real_user($useridto)) { + $userto = core_user::get_user($useridto, '*', MUST_EXIST); + } else { + throw new moodle_exception('invaliduser'); + } } if (!empty($useridfrom)) { @@ -671,25 +677,23 @@ class core_message_external extends external_api { throw new moodle_exception('accessdenied', 'admin'); } - if ($useridto == $useridfrom) { - throw new moodle_exception('invaliduserid'); - } - // Get messages. $messagetable = $read ? '{message_read}' : '{message}'; $usersql = ""; + $joinsql = ""; $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', 'userto'); - $joinuserfield = "mr.useridto"; - $usersql = "mr.useridfrom = :useridfrom"; + $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', 'userfrom'); - $joinuserfield = "mr.useridfrom"; - $usersql = "mr.useridto = :useridto"; + $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"; @@ -701,7 +705,7 @@ class core_message_external extends external_api { $typesql = ""; if ($type != 'both') { $typesql = "AND mr.notification = :notification"; - $params['notification'] = ($type == 'notification') ? 1 : 0; + $params['notification'] = ($type == 'notifications') ? 1 : 0; } // Finally the sort direction. @@ -709,8 +713,8 @@ class core_message_external extends external_api { $sql = "SELECT mr.*, $userfields FROM $messagetable mr - JOIN {user} u ON u.id = $joinuserfield - WHERE $usersql AND u.deleted = :deleted + $joinsql + WHERE $usersql $typesql ORDER BY mr.timecreated $orderdirection"; @@ -737,9 +741,15 @@ class core_message_external extends external_api { // We need to get the user from the query. if (empty($userfromfullname)) { - $user = new stdclass(); - $user = username_load_fields_from_object($user, $message, 'userfrom'); - $message->userfromfullname = fullname($user, $canviewfullname); + // Check for non-reply and support users. + if (core_user::is_real_user($message->useridfrom)) { + $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; } @@ -757,7 +767,6 @@ class core_message_external extends external_api { $message->timeread = 0; } - // DOUBT, getting crazy with formats... $message->text = message_format_message_text($message); $messages[$mid] = (array) $message; @@ -775,7 +784,7 @@ class core_message_external extends external_api { /** * Get messages return description. * - * @return external_description + * @return external_single_structure * @since 2.7 */ public static function get_messages_returns() { diff --git a/message/tests/externallib_test.php b/message/tests/externallib_test.php index cddd3e38e69..2f0f385329b 100644 --- a/message/tests/externallib_test.php +++ b/message/tests/externallib_test.php @@ -32,6 +32,15 @@ require_once($CFG->dirroot . '/message/externallib.php'); class core_message_externallib_testcase extends externallib_advanced_testcase { + /** + * Tests set up + */ + protected function setUp() { + global $CFG; + + require_once($CFG->dirroot . '/message/lib.php'); + } + /** * Send a fake message. * @@ -381,4 +390,192 @@ class core_message_externallib_testcase extends externallib_advanced_testcase { $this->setExpectedException('moodle_exception'); $results = core_message_external::search_contacts(''); } + + /** + * 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 ones ommits 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 = 'Feedback submitted'; + $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); + } + + } } From 193edf7f775259415aa96ac0b9211e6c0025a698 Mon Sep 17 00:00:00 2001 From: Juan Leyva Date: Wed, 1 Oct 2014 11:10:42 +0200 Subject: [PATCH 3/4] MDL-45374 messages: Created new API function message_get_messages --- message/externallib.php | 60 ++++++++++------------------------------- message/lib.php | 58 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+), 46 deletions(-) diff --git a/message/externallib.php b/message/externallib.php index 488dcc8c882..187034c9926 100644 --- a/message/externallib.php +++ b/message/externallib.php @@ -71,6 +71,7 @@ class core_message_external extends external_api { global $CFG, $USER, $DB; require_once($CFG->dirroot . "/message/lib.php"); + //check if messaging is enabled if (!$CFG->messaging) { throw new moodle_exception('disabled', 'message'); @@ -573,7 +574,7 @@ class core_message_external extends external_api { * Get messages parameters description. * * @return external_function_parameters - * @since 2.7 + * @since 2.8 */ public static function get_messages_parameters() { return new external_function_parameters( @@ -597,15 +598,15 @@ class core_message_external extends external_api { * Get messages function implementation. * @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 tu return, expected values: notifications, conversations and both + * @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 - * @since 2.7 + * @since 2.8 */ - public static function get_messages($useridto, $useridfrom = 0, $type = 'both' , $read = true, + public static function get_messages($useridto, $useridfrom = 0, $type = 'both', $read = true, $newestfirst = true, $limitfrom = 0, $limitnum = 0) { global $CFG, $DB, $USER; require_once($CFG->dirroot . "/message/lib.php"); @@ -677,48 +678,16 @@ class core_message_external extends external_api { throw new moodle_exception('accessdenied', 'admin'); } - // Get messages. - $messagetable = $read ? '{message_read}' : '{message}'; - $usersql = ""; - $joinsql = ""; - $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 = ""; + // Which type of messages retrieve. + $notifications = -1; if ($type != 'both') { - $typesql = "AND mr.notification = :notification"; - $params['notification'] = ($type == 'notifications') ? 1 : 0; + $notifications = ($type == 'notifications') ? 1 : 0; } - // Finally the sort direction. $orderdirection = $newestfirst ? 'DESC' : 'ASC'; + $sort = "mr.timecreated $orderdirection"; - $sql = "SELECT mr.*, $userfields - FROM $messagetable mr - $joinsql - WHERE $usersql - $typesql - ORDER BY mr.timecreated $orderdirection"; - - if ($messages = $DB->get_records_sql($sql, $params, $limitfrom, $limitnum)) { + 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. @@ -736,7 +705,6 @@ class core_message_external extends external_api { // 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. @@ -763,12 +731,12 @@ class core_message_external extends external_api { $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; } } @@ -785,7 +753,7 @@ class core_message_external extends external_api { * Get messages return description. * * @return external_single_structure - * @since 2.7 + * @since 2.8 */ public static function get_messages_returns() { return new external_single_structure( @@ -793,13 +761,13 @@ class core_message_external extends external_api { 'messages' => new external_multiple_structure( new external_single_structure( array( - 'id' => new external_value(PARAM_INT, 'mMssage id'), + '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_value(PARAM_INT, 'The message message format'), + '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?'), diff --git a/message/lib.php b/message/lib.php index c7b9e7b7ab4..ac8839119d8 100644 --- a/message/lib.php +++ b/message/lib.php @@ -2614,3 +2614,61 @@ function message_current_user_is_involved($user1, $user2) { } return true; } + +/** + * Get messages send or/and received by the specified users. + * + * @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 int $notifications 1 for retrieving notifications, 0 for messages, -1 for both + * @param bool $read true for retreiving 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}'; + $usersql = ""; + $joinsql = ""; + $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; +} From ffb8abc29054665dd5cf83a003e9c92be194abc4 Mon Sep 17 00:00:00 2001 From: Juan Leyva Date: Wed, 1 Oct 2014 11:19:12 +0200 Subject: [PATCH 4/4] MDL-45374 messages: Bump version number --- version.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.php b/version.php index b1c47a58d63..ac061cf0288 100644 --- a/version.php +++ b/version.php @@ -29,7 +29,7 @@ defined('MOODLE_INTERNAL') || die(); -$version = 2014100200.00; // YYYYMMDD = weekly release date of this DEV branch. +$version = 2014100200.01; // YYYYMMDD = weekly release date of this DEV branch. // RR = release increments - 00 in DEV branches. // .XX = incremental changes.