Merge branch 'MDL-45374' of git://github.com/jleyva/moodle
authorSam Hemelryk <sam@moodle.com>
Sun, 5 Oct 2014 21:44:30 +0000 (10:44 +1300)
committerSam Hemelryk <sam@moodle.com>
Sun, 5 Oct 2014 21:44:30 +0000 (10:44 +1300)
1  2 
lib/db/services.php
message/externallib.php
message/lib.php
message/tests/externallib_test.php

@@@ -761,6 -761,15 +761,15 @@@ $functions = array
          '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(
@@@ -568,6 -569,222 +568,227 @@@ class core_message_external extends ext
              '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()
+             )
+         );
+     }
  }
  
  /**
diff --cc message/lib.php
@@@ -2614,3 -2614,61 +2614,59 @@@ function message_current_user_is_involv
      }
      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;
+ }
@@@ -381,4 -390,192 +390,192 @@@ class core_message_externallib_testcas
          $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);
+         }
+     }
  }